mutators: finished gui

Changing difficulty on map screen doesn't disable mutators.
Can no longer enable mutators if they are incompatible with other already enabled.
This commit is contained in:
UnShame 2018-02-22 00:21:19 +03:00
parent f7710d8f3c
commit db2b52a2fd
7 changed files with 396 additions and 82 deletions

View file

@ -0,0 +1,26 @@
return {
easy = {
en = "Easy"
},
normal = {
en = "Normal"
},
hard = {
en = "Hard"
},
harder = {
en = "Nightmare"
},
hardest = {
en = "Cataclysm"
},
survival_hard = {
en = "Veteran"
},
survival_harder = {
en = "Champion"
},
survival_hardest = {
en = "Heroic"
}
}

View file

@ -11,7 +11,7 @@ material = [
] ]
lua = [ lua = [
"localization/vmf" "localization/*"
"scripts/mods/vmf/*" "scripts/mods/vmf/*"
"scripts/mods/vmf/functions/*" "scripts/mods/vmf/functions/*"

View file

@ -6,6 +6,7 @@ return {
}, },
title = "", title = "",
short_title = "", short_title = "",
description = "No description provided",
difficulties = { difficulties = {
"easy", "easy",
"normal", "normal",

View file

@ -63,4 +63,7 @@ local removeDice = function(dice)
adjustDice(dice.grims, dice.tomes, dice.bonus, -1) adjustDice(dice.grims, dice.tomes, dice.bonus, -1)
end end
return addDice, removeDice return {
addDice = addDice,
removeDice = removeDice
}

View file

@ -3,7 +3,7 @@ local mutators = manager.mutators
local definitions = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_gui_definitions") local definitions = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_gui_definitions")
local PER_PAGE = 5 local PER_PAGE = definitions.PER_PAGE
local mutators_view = { local mutators_view = {
@ -12,6 +12,8 @@ local mutators_view = {
was_active = false, was_active = false,
map_view = nil, map_view = nil,
current_page = 1, current_page = 1,
mutators_sorted = {},
mutator_checkboxes = {},
init = function(self, map_view) init = function(self, map_view)
if self.initialized then return end if self.initialized then return end
@ -19,15 +21,21 @@ local mutators_view = {
self.map_view = map_view self.map_view = map_view
if not self.map_view then return end if not self.map_view then return end
self:update_mutator_list()
-- Recreate the map_view scenegraph defs -- Recreate the map_view scenegraph defs
self.map_view.scenegraph_definition = UISceneGraph.init_scenegraph(definitions.scenegraph_definition) self.map_view.scenegraph_definition = UISceneGraph.init_scenegraph(definitions.scenegraph_definition)
-- Setup custom widgets -- Setup custom widgets
self.widgets = { self.widgets = {
banner_mutators = UIWidget.init(definitions.new_widgets.banner_mutators_widget), banner_mutators = UIWidget.init(definitions.new_widgets.banner_mutators_widget),
mutators_button = UIWidget.init(definitions.new_widgets.mutators_button_widget), mutators_button = UIWidget.init(definitions.new_widgets.mutators_button_widget)
} }
for i = 1, PER_PAGE do
table.insert(self.mutator_checkboxes, UIWidget.init(definitions.new_widgets["mutator_checkbox_" .. i]))
end
-- Save widgets we're gonna mess with -- Save widgets we're gonna mess with
local widgets = self.map_view.normal_settings_widget_types local widgets = self.map_view.normal_settings_widget_types
self.saved_widgets = { self.saved_widgets = {
@ -85,6 +93,15 @@ local mutators_view = {
print("DEINIT") print("DEINIT")
end, end,
-- Sorts mutators by title
update_mutator_list = function(self)
self.mutators_sorted = {}
for _, mutator in ipairs(mutators) do
table.insert(self.mutators_sorted, {mutator:get_name(), mutator:get_config().title or mutator:get_name()})
end
table.sort(self.mutators_sorted, function(a, b) return string.lower(a[2]) < string.lower(b[2]) end)
end,
update = function(self) update = function(self)
if not self.initialized then if not self.initialized then
self:init() self:init()
@ -127,6 +144,54 @@ local mutators_view = {
local widgets = self.map_view.normal_settings_widget_types local widgets = self.map_view.normal_settings_widget_types
widgets.adventure.banner_level.content.tooltip_hotspot.disabled = true widgets.adventure.banner_level.content.tooltip_hotspot.disabled = true
widgets.survival.banner_level.content.tooltip_hotspot.disabled = true widgets.survival.banner_level.content.tooltip_hotspot.disabled = true
self:update_checkboxes()
end
end,
update_checkboxes = function(self)
local widgets = self.map_view.normal_settings_widget_types
for i = 1, PER_PAGE do
local current_index = PER_PAGE * (self.current_page - 1) + i
local checkbox = self.mutator_checkboxes[i]
local hotspot = checkbox.content.button_hotspot
if #self.mutators_sorted < current_index then
checkbox.content.setting_text = ""
checkbox.content.tooltip_text = ""
widgets.adventure["mutator_checkbox_" .. i] = nil
widgets.survival["mutator_checkbox_" .. i] = nil
else
local mutator_info = self.mutators_sorted[current_index]
local mutator = get_mod(mutator_info[1])
checkbox.content.setting_text = mutator_info[2]
checkbox.content.tooltip_text = self:generate_tooltip_for(mutator)
widgets.adventure["mutator_checkbox_" .. i] = checkbox
widgets.survival["mutator_checkbox_" .. i] = checkbox
local active = mutator:can_be_enabled()
local color = active and "cheeseburger" or "slate_gray"
local color_hover = active and "white" or "slate_gray"
checkbox.style.setting_text.text_color = Colors.get_color_table_with_alpha(color, 255)
checkbox.style.setting_text_hover.text_color = Colors.get_color_table_with_alpha(color_hover, 255)
checkbox.style.checkbox_style.color = Colors.get_color_table_with_alpha(color_hover, 255)
if hotspot.on_hover_enter then
self.map_view:play_sound("Play_hud_hover")
end
if hotspot.on_release then
self.map_view:play_sound("Play_hud_hover")
if mutator:is_enabled() then
mutator:disable()
else
mutator:enable()
end
end
checkbox.content.selected = mutator:is_enabled()
end
end end
end, end,
@ -149,6 +214,7 @@ local mutators_view = {
-- Update steppers -- Update steppers
self.map_view.steppers.level.widget.style.setting_text.offset[2] = -10000 self.map_view.steppers.level.widget.style.setting_text.offset[2] = -10000
self.map_view.steppers.level.widget.style.hover_texture.offset[2] = -10000
local level_stepper_widget = self.map_view.steppers.level.widget local level_stepper_widget = self.map_view.steppers.level.widget
local num_pages = math.ceil(#mutators/PER_PAGE) local num_pages = math.ceil(#mutators/PER_PAGE)
level_stepper_widget.content.left_button_hotspot.disable_button = num_pages <= 1 level_stepper_widget.content.left_button_hotspot.disable_button = num_pages <= 1
@ -156,7 +222,7 @@ local mutators_view = {
self.active = true self.active = true
print("ACTIVE") print("ACTIVE!")
end, end,
deactivate = function(self) deactivate = function(self)
@ -178,8 +244,15 @@ local mutators_view = {
-- Update steppers -- Update steppers
self.map_view.steppers.level.widget.style.setting_text.offset[2] = -120 self.map_view.steppers.level.widget.style.setting_text.offset[2] = -120
self.map_view.steppers.level.widget.style.hover_texture.offset[2] = -19.5
self.map_view:update_level_stepper() self.map_view:update_level_stepper()
-- Mutator checkboxes
for i = 1, PER_PAGE do
widgets.adventure["mutator_checkbox_" .. i] = nil
widgets.survival["mutator_checkbox_" .. i] = nil
end
self.active = false self.active = false
print("DEACTIVE") print("DEACTIVE")
@ -200,12 +273,49 @@ local mutators_view = {
end end
self.current_page = new_index self.current_page = new_index
print("TEST", tostring(new_index))
else else
self.map_view:on_level_index_changed(index_change) self.map_view:on_level_index_changed(index_change)
end end
end, end,
generate_tooltip_for = function(self, mutator)
local config = mutator:get_config()
local text = config.description
local supports_difficulty = mutator:supports_current_difficulty()
if not supports_difficulty then
text = text .. "\nSupported difficulty levels:"
for i, difficulty in ipairs(config.difficulties) do
text = text .. (i == 1 and " " or ", ") .. manager:localize(difficulty)
end
end
local incompatible_mutators = mutator:get_incompatible_mutators(true)
local currently_compatible = #incompatible_mutators == 0
if supports_difficulty and #incompatible_mutators == 0 then
incompatible_mutators = mutator:get_incompatible_mutators()
end
if #incompatible_mutators > 0 then
if currently_compatible and config.incompatible_with_all or #incompatible_mutators == #mutators - 1 then
text = text .. "\nIncompatible with all other mutators"
else
text = text .. "\nIncompatible with:"
for i, other_mutator in ipairs(incompatible_mutators) do
local name = (other_mutator:get_config().title or other_mutator:get_name())
text = text .. (i == 1 and " " or ", ") .. name
end
end
elseif config.compatible_with_all then
text = text .. "\nCompatible with all other mutators"
end
if mutator:is_enabled() and not supports_difficulty then
text = text .. "\nWill be disabled when Play is pressed"
end
return text
end,
setup_hooks = function(self) setup_hooks = function(self)
-- Update the view after map_view has updated -- Update the view after map_view has updated
@ -235,6 +345,26 @@ local mutators_view = {
func(map_view) func(map_view)
end end
end) end)
--[[
manager:hook("MapView.on_level_index_changed", function(func, map_view, ...)
func(map_view, ...)
print("on_level_index_changed")
manager.disable_impossible_mutators(true)
end)
manager:hook("MapView.on_difficulty_index_changed", function(func, map_view, ...)
func(map_view, ...)
print("on_difficulty_index_changed")
manager.disable_impossible_mutators(true)
end)
manager:hook("MapView.set_difficulty_stepper_index", function(func, map_view, ...)
func(map_view, ...)
print("set_difficulty_stepper_index")
manager.disable_impossible_mutators(true)
end)
--]]
end, end,
reset_hooks = function(self) reset_hooks = function(self)
@ -242,28 +372,15 @@ local mutators_view = {
manager:hook_remove("MapView.on_enter") manager:hook_remove("MapView.on_enter")
manager:hook_remove("MapView.on_exit") manager:hook_remove("MapView.on_exit")
manager:hook_remove("MapView.update_level_stepper") manager:hook_remove("MapView.update_level_stepper")
-- manager:hook_remove("MapView.on_level_index_changed")
-- manager:hook_remove("MapView.on_difficulty_index_changed")
-- manager:hook_remove("MapView.set_difficulty_stepper_index")
end, end,
get_map_view = function(self)
local ingame_ui = Managers.matchmaking and Managers.matchmaking.ingame_ui
return ingame_ui and ingame_ui.views and ingame_ui.views.map_view
end
} }
-- Initialize mutators view after map view
manager:hook("MapView.init", function(func, self, ...)
func(self, ...)
manager:pcall(function() mutators_view:init(self) end)
end)
-- Destroy mutators view after map view
manager:hook("MapView.destroy", function(func, ...)
mutators_view:deinitialize()
func(...)
end)
-- Initialize mutators view when map_view has been initialized already
local function get_map_view()
local ingame_ui = Managers.matchmaking and Managers.matchmaking.ingame_ui
return ingame_ui and ingame_ui.views and ingame_ui.views.map_view
end
manager:pcall(function() mutators_view:init(get_map_view()) end)
return mutators_view return mutators_view

View file

@ -1,6 +1,9 @@
local definitions = local_require("scripts/ui/views/map_view_definitions") local definitions = local_require("scripts/ui/views/map_view_definitions")
local scenegraph_definition = definitions.scenegraph_definition
definitions.scenegraph_definition.mutators_button = { definitions.PER_PAGE = 6
scenegraph_definition.mutators_button = {
vertical_alignment = "bottom", vertical_alignment = "bottom",
parent = "banner_party", parent = "banner_party",
horizontal_alignment = "center", horizontal_alignment = "center",
@ -14,8 +17,7 @@ definitions.scenegraph_definition.mutators_button = {
1 1
} }
} }
scenegraph_definition.banner_mutators_text = {
definitions.scenegraph_definition.banner_mutators_text = {
vertical_alignment = "center", vertical_alignment = "center",
parent = "banner_level", parent = "banner_level",
horizontal_alignment = "center", horizontal_alignment = "center",
@ -30,7 +32,8 @@ definitions.scenegraph_definition.banner_mutators_text = {
} }
} }
definitions.new_widgets = {
local new_widgets = {
banner_mutators_widget = UIWidgets.create_texture_with_text_and_tooltip("title_bar", "Mutators", "Enable and disable mutators", "banner_level", "banner_mutators_text", { banner_mutators_widget = UIWidgets.create_texture_with_text_and_tooltip("title_bar", "Mutators", "Enable and disable mutators", "banner_level", "banner_mutators_text", {
vertical_alignment = "center", vertical_alignment = "center",
scenegraph_id = "banner_mutators_text", scenegraph_id = "banner_mutators_text",
@ -169,4 +172,149 @@ definitions.new_widgets = {
} }
} }
for i = 1, definitions.PER_PAGE do
new_widgets["mutator_checkbox_" .. i] = {
scenegraph_id = "mutator_checkbox_" .. i,
element = {
passes = {
{
pass_type = "hotspot",
content_id = "button_hotspot"
},
{
style_id = "tooltip_text",
pass_type = "tooltip_text",
text_id = "tooltip_text",
content_check_function = function (ui_content)
return ui_content.button_hotspot.is_hover
end
},
{
style_id = "setting_text",
pass_type = "text",
text_id = "setting_text",
content_check_function = function (content)
return not content.button_hotspot.is_hover
end
},
{
style_id = "setting_text_hover",
pass_type = "text",
text_id = "setting_text",
content_check_function = function (content)
return content.button_hotspot.is_hover
end
},
{
pass_type = "texture",
style_id = "checkbox_style",
texture_id = "checkbox_unchecked_texture",
content_check_function = function (content)
return not content.selected
end
},
{
pass_type = "texture",
style_id = "checkbox_style",
texture_id = "checkbox_checked_texture",
content_check_function = function (content)
return content.selected
end
}
}
},
content = {
tooltip_text = "Mutator ajksad " .. i,
checkbox_unchecked_texture = "checkbox_unchecked",
checkbox_checked_texture = "checkbox_checked",
selected = false,
setting_text = "Mutator asdasasda " .. i * 3,
button_hotspot = {}
},
style = {
checkbox_style = {
size = {
20,
20
},
offset = {
0,
6,
1
},
color = {
255,
255,
255,
255
}
},
setting_text = {
vertical_alignment = "center",
font_size = 22,
localize = false,
horizontal_alignment = "left",
word_wrap = true,
font_type = "hell_shark",
text_color = Colors.get_color_table_with_alpha("cheeseburger", 255),
offset = {
24,
2,
4
}
},
setting_text_hover = {
vertical_alignment = "center",
font_size = 22,
localize = false,
horizontal_alignment = "left",
word_wrap = true,
font_type = "hell_shark",
text_color = Colors.get_color_table_with_alpha("white", 255),
offset = {
24,
2,
4
}
},
tooltip_text = {
font_size = 18,
max_width = 500,
localize = false,
cursor_side = "right",
horizontal_alignment = "left",
vertical_alignment = "top",
font_type = "hell_shark",
text_color = Colors.get_color_table_with_alpha("white", 255),
line_colors = {},
offset = {
0,
0,
50
},
cursor_offset = {
-10,
-27
}
}
}
}
scenegraph_definition["mutator_checkbox_" .. i] = {
vertical_alignment = "center",
parent = "banner_party",
horizontal_alignment = "left",
size = {
310,
30
},
position = {
30,
520 - 40 * (i - 1),
1
}
}
end
definitions.new_widgets = new_widgets
return definitions return definitions

View file

@ -1,5 +1,6 @@
local manager = new_mod("vmf_mutator_manager") local manager = new_mod("vmf_mutator_manager")
manager:localization("localization/mutator_manager")
-- List of mods that are also mutators in order in which they should be enabled -- List of mods that are also mutators in order in which they should be enabled
-- This is populated via VMFMod.register_as_mutator -- This is populated via VMFMod.register_as_mutator
@ -88,7 +89,7 @@ manager.sort_mutators = function()
end end
-- Disables mutators that cannot be enabled right now -- Disables mutators that cannot be enabled right now
manager.disable_impossible_mutators = function() manager.disable_impossible_mutators = function(notify, everybody)
local disabled_mutators = {} local disabled_mutators = {}
for _, mutator in pairs(mutators) do for _, mutator in pairs(mutators) do
if mutator:is_enabled() and not mutator:can_be_enabled() then if mutator:is_enabled() and not mutator:can_be_enabled() then
@ -96,6 +97,17 @@ manager.disable_impossible_mutators = function()
table.insert(disabled_mutators, mutator) table.insert(disabled_mutators, mutator)
end end
end end
if #disabled_mutators > 0 and notify then
local message = everybody and "MUTATORS DISABLED DUE TO DIFFICULTY CHANGE:" or "Mutators disabled due to difficulty change:"
for _, mutator in ipairs(disabled_mutators) do
message = message .. " " .. (mutator:get_config().title or mutator:get_name())
end
if everybody then
Managers.chat:send_system_chat_message(1, message, 0, true)
else
manager:echo(message)
end
end
return disabled_mutators return disabled_mutators
end end
@ -105,7 +117,7 @@ end
]]-- ]]--
local mutators_view = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_gui") local mutators_view = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_gui")
local addDice, removeDice = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_dice") local dice_manager = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_dice")
local set_lobby_data = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_info") local set_lobby_data = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_info")
-- Adds mutator names from enable_these_after to the list of mutators that should be enabled after the mutator_name -- Adds mutator names from enable_these_after to the list of mutators that should be enabled after the mutator_name
@ -166,41 +178,17 @@ local function is_compatible(mutator, other_mutator)
return compatible return compatible
end end
-- Disables enabled mutators that aren't compatible with the specified
local function disable_incompatible_with(mutator)
local names = nil
for _, other_mutator in ipairs(mutators) do
if (
other_mutator ~= mutator and
other_mutator:is_enabled() and
not is_compatible(mutator, other_mutator)
) then
other_mutator:disable()
local name = other_mutator:get_config().title or other_mutator:get_name()
if names then
names = names .. " " .. name
else
names = name
end
end
end
if names then
-- TODO: output this to the menu instead of chat
manager:echo("These mutators are incompatible with " .. mutator:get_name() .. " and were disabled: " .. names)
end
end
-- Called after mutator is enabled -- Called after mutator is enabled
local function on_enabled(mutator) local function on_enabled(mutator)
local config = mutator:get_config() local config = mutator:get_config()
addDice(config.dice) dice_manager.addDice(config.dice)
set_lobby_data() set_lobby_data()
end end
-- Called after mutator is disabled -- Called after mutator is disabled
local function on_disabled(mutator) local function on_disabled(mutator)
local config = mutator:get_config() local config = mutator:get_config()
removeDice(config.dice) dice_manager.removeDice(config.dice)
set_lobby_data() set_lobby_data()
end end
@ -221,16 +209,15 @@ local function set_mutator_state(mutator, state)
return return
end end
if state and #mutator:get_incompatible_mutators(true) > 0 then
return
end
-- Sort mutators if this is the first call -- Sort mutators if this is the first call
if not mutators_sorted then if not mutators_sorted then
manager.sort_mutators() manager.sort_mutators()
end end
-- Disable mutators that aren't compatible
if state then
disable_incompatible_with(mutator)
end
local disabled_mutators = {} local disabled_mutators = {}
local enable_these_after = mutators_sequence[mutator:get_name()] local enable_these_after = mutators_sequence[mutator:get_name()]
@ -289,8 +276,13 @@ end
-- Checks current difficulty and map selection screen settings to determine if a mutator can be enabled -- Checks current difficulty and map selection screen settings to determine if a mutator can be enabled
local function can_be_enabled(self) local function can_be_enabled(self)
local mutator_difficulties = self:get_config().difficulties if #self:get_incompatible_mutators(true) > 0 then return false end
return self:supports_current_difficulty()
end
local function supports_current_difficulty(self)
local mutator_difficulties = self:get_config().difficulties
local actual_difficulty = Managers.state and Managers.state.difficulty:get_difficulty() local actual_difficulty = Managers.state and Managers.state.difficulty:get_difficulty()
local right_difficulty = not actual_difficulty or table.has_item(mutator_difficulties, actual_difficulty) local right_difficulty = not actual_difficulty or table.has_item(mutator_difficulties, actual_difficulty)
@ -314,6 +306,20 @@ local function get_config(self)
return mutators_config[self:get_name()] return mutators_config[self:get_name()]
end end
local function get_incompatible_mutators(self, enabled_only)
local incompatible_mutators = {}
for _, other_mutator in ipairs(mutators) do
if (
other_mutator ~= self and
(not enabled_only or other_mutator:is_enabled()) and
not is_compatible(self, other_mutator)
) then
table.insert(incompatible_mutators, other_mutator)
end
end
return incompatible_mutators
end
-- Turns a mod into a mutator -- Turns a mod into a mutator
VMFMod.register_as_mutator = function(self, config) VMFMod.register_as_mutator = function(self, config)
if not config then config = {} end if not config then config = {} end
@ -351,13 +357,17 @@ VMFMod.register_as_mutator = function(self, config)
self.enable = enable_mutator self.enable = enable_mutator
self.disable = disable_mutator self.disable = disable_mutator
self.can_be_enabled = can_be_enabled self.can_be_enabled = can_be_enabled
self.supports_current_difficulty = supports_current_difficulty
self.get_config = get_config self.get_config = get_config
self.get_incompatible_mutators = get_incompatible_mutators
mutators_sorted = false mutators_sorted = false
-- Always init in the off state -- Always init in the off state
self:init_state(false) self:init_state(false)
mutators_view:update_mutator_list()
end end
@ -365,19 +375,26 @@ end
HOOKS HOOKS
]]-- ]]--
manager:hook("DifficultyManager.set_difficulty", function(func, self, difficulty) manager:hook("DifficultyManager.set_difficulty", function(func, self, difficulty)
local disabled_mutators = manager.disable_impossible_mutators() manager.disable_impossible_mutators(true, true)
if #disabled_mutators > 0 then
local message = "MUTATORS DISABLED DUE TO DIFFICULTY CHANGE:"
for _, mutator in ipairs(disabled_mutators) do
message = message .. " " .. mutator:get_config().title or mutator:get_name()
end
Managers.chat:send_system_chat_message(1, message, 0, true)
end
return func(self, difficulty) return func(self, difficulty)
end) end)
-- Initialize mutators view after map view
manager:hook("MapView.init", function(func, self, ...)
func(self, ...)
manager:pcall(function() mutators_view:init(self) end)
end)
-- Destroy mutators view after map view
manager:hook("MapView.destroy", function(func, ...)
mutators_view:deinitialize()
func(...)
end)
-- Initialize mutators view when map_view has been initialized already
manager:pcall(function() mutators_view:init(mutators_view:get_map_view()) end)
@ -401,10 +418,7 @@ local mutator3 = new_mod("mutator3")
local mutator555 = new_mod("mutator555") local mutator555 = new_mod("mutator555")
mutator555:register_as_mutator({ mutator555:register_as_mutator({
compatible_with_all = true, incompatible_with_all = true
incompatible_with = {
"mutator2"
}
}) })
mutator555:create_options({}, true, "mutator555", "mutator555 description") mutator555:create_options({}, true, "mutator555", "mutator555 description")
mutator555.on_enabled = function() end mutator555.on_enabled = function() end
@ -412,20 +426,25 @@ mutator555.on_disabled = function() end
mutator3:register_as_mutator({ mutator3:register_as_mutator({
compatible_with_all = true,
incompatible_with = { incompatible_with = {
"mutator555" "mutator4"
} }
}) })
mutator3:create_options({}, true, "mutator3", "mutator3 description")
mutator3.on_enabled = function() end mutator3.on_enabled = function() end
mutator3.on_disabled = function() end mutator3.on_disabled = function() end
mutator2:register_as_mutator({ mutator2:register_as_mutator({
compatible_with_all = true,
difficulties = { difficulties = {
"hardest" "hardest"
} }
}) })
mutator2:create_options({}, true, "mutator2", "mutator2 description")
mutator2.on_enabled = function() end mutator2.on_enabled = function() end
mutator2.on_disabled = function() end mutator2.on_disabled = function() end
--[[for i=4,17 do
local mutator = new_mod("mutator" .. i)
mutator:register_as_mutator({})
mutator.on_enabled = function() end
mutator.on_disabled = function() end
end--]]