commit
7e713f677a
21 changed files with 1761 additions and 13 deletions
BIN
vmf_source/gui/vmf/mutator_button.dds
Normal file
BIN
vmf_source/gui/vmf/mutator_button.dds
Normal file
Binary file not shown.
17
vmf_source/gui/vmf/mutator_button.texture
Normal file
17
vmf_source/gui/vmf/mutator_button.texture
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
common = {
|
||||||
|
input = {
|
||||||
|
filename = "gui/vmf/mutator_button"
|
||||||
|
}
|
||||||
|
output = {
|
||||||
|
apply_processing = true
|
||||||
|
correct_gamma = true
|
||||||
|
cut_alpha_threshold = 0.5
|
||||||
|
enable_cut_alpha_threshold = false
|
||||||
|
format = "A8R8G8B8"
|
||||||
|
mipmap_filter = "kaiser"
|
||||||
|
mipmap_filter_wrap_mode = "mirror"
|
||||||
|
mipmap_keep_original = false
|
||||||
|
mipmap_num_largest_steps_to_discard = 0
|
||||||
|
mipmap_num_smallest_steps_to_discard = 0
|
||||||
|
}
|
||||||
|
}
|
BIN
vmf_source/gui/vmf/mutator_button_hover.dds
Normal file
BIN
vmf_source/gui/vmf/mutator_button_hover.dds
Normal file
Binary file not shown.
17
vmf_source/gui/vmf/mutator_button_hover.texture
Normal file
17
vmf_source/gui/vmf/mutator_button_hover.texture
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
common = {
|
||||||
|
input = {
|
||||||
|
filename = "gui/vmf/mutator_button_hover"
|
||||||
|
}
|
||||||
|
output = {
|
||||||
|
apply_processing = true
|
||||||
|
correct_gamma = true
|
||||||
|
cut_alpha_threshold = 0.5
|
||||||
|
enable_cut_alpha_threshold = false
|
||||||
|
format = "A8R8G8B8"
|
||||||
|
mipmap_filter = "kaiser"
|
||||||
|
mipmap_filter_wrap_mode = "mirror"
|
||||||
|
mipmap_keep_original = false
|
||||||
|
mipmap_num_largest_steps_to_discard = 0
|
||||||
|
mipmap_num_smallest_steps_to_discard = 0
|
||||||
|
}
|
||||||
|
}
|
26
vmf_source/localization/mutator_manager.lua
Normal file
26
vmf_source/localization/mutator_manager.lua
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
14
vmf_source/materials/vmf/mutator_button.material
Normal file
14
vmf_source/materials/vmf/mutator_button.material
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
mutator_button = {
|
||||||
|
material_contexts = {
|
||||||
|
surface_material = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
shader = "gui_gradient:DIFFUSE_MAP"
|
||||||
|
|
||||||
|
textures = {
|
||||||
|
diffuse_map = "gui/vmf/mutator_button"
|
||||||
|
}
|
||||||
|
|
||||||
|
variables = {
|
||||||
|
}
|
||||||
|
}
|
14
vmf_source/materials/vmf/mutator_button_hover.material
Normal file
14
vmf_source/materials/vmf/mutator_button_hover.material
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
mutator_button_hover = {
|
||||||
|
material_contexts = {
|
||||||
|
surface_material = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
shader = "gui_gradient:DIFFUSE_MAP"
|
||||||
|
|
||||||
|
textures = {
|
||||||
|
diffuse_map = "gui/vmf/mutator_button_hover"
|
||||||
|
}
|
||||||
|
|
||||||
|
variables = {
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ material = [
|
||||||
]
|
]
|
||||||
|
|
||||||
lua = [
|
lua = [
|
||||||
"localization/vmf"
|
"localization/*"
|
||||||
|
|
||||||
"scripts/mods/vmf/*"
|
"scripts/mods/vmf/*"
|
||||||
"scripts/mods/vmf/functions/*"
|
"scripts/mods/vmf/functions/*"
|
||||||
|
@ -20,4 +20,5 @@ lua = [
|
||||||
"scripts/mods/vmf/modules/debug/*"
|
"scripts/mods/vmf/modules/debug/*"
|
||||||
"scripts/mods/vmf/modules/gui/*"
|
"scripts/mods/vmf/modules/gui/*"
|
||||||
"scripts/mods/vmf/modules/options_menu/*"
|
"scripts/mods/vmf/modules/options_menu/*"
|
||||||
|
"scripts/mods/vmf/modules/mutators/*"
|
||||||
]
|
]
|
|
@ -155,7 +155,7 @@ VMFMod.dofile = function (self, script_path)
|
||||||
if not success then
|
if not success then
|
||||||
self:error("(loadfile): %s", value.error)
|
self:error("(loadfile): %s", value.error)
|
||||||
|
|
||||||
print("\nTRACEBACK:\n\n" .. value.traceback .. "\nLOCALS:\n\n" .. value.locals)
|
print("\nTRACEBACK:\n\n" .. tostring(value.traceback) .. "\nLOCALS:\n\n" .. tostring(value.locals))
|
||||||
end
|
end
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
|
@ -52,13 +52,11 @@ VMFMod.enable = function (self)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
VMFMod.init_state = function (self)
|
VMFMod.init_state = function (self, state)
|
||||||
|
if type(state) ~= "boolean" then
|
||||||
if _DISABLED_MODS_LIST[self:get_name()] then
|
state = not _DISABLED_MODS_LIST[self:get_name()]
|
||||||
change_mod_state(self, false, true)
|
|
||||||
else
|
|
||||||
change_mod_state(self, true, true)
|
|
||||||
end
|
end
|
||||||
|
change_mod_state(self, state, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- ####################################################################################################################
|
-- ####################################################################################################################
|
||||||
|
|
119
vmf_source/scripts/mods/vmf/modules/mutators/README.md
Normal file
119
vmf_source/scripts/mods/vmf/modules/mutators/README.md
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
# Mutators
|
||||||
|
A mutator is a mod that affects other players as well as you by modifying the game rules, enemies, weapon balance etc.
|
||||||
|
|
||||||
|
You can turn your mod into a mutator by calling `mod:register_as_mutator(config)` instead of `mod:init_state()`. This way it will show up on the map screen and have additional features and options to control its behavior.
|
||||||
|
|
||||||
|
Note that you can still have additional options for your mutator in the mod options menu:
|
||||||
|
``vmf:create_options(options_widgets, false, "Title", "Description")``
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
* Show the toggle for the mutator on the map screen
|
||||||
|
* Choose which game modes and difficulty levels the mutator can be played on
|
||||||
|
* Add additional dice to the end game roll
|
||||||
|
* Control the order in which mutators are enabled/disabled
|
||||||
|
* Control compatibility with other mutators
|
||||||
|
* Notify players already in the lobby and just joining about enabled mutators
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
The config object is optional but obviously you'd want to provide at least a readable title for your mutator. Here are the default values:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
{
|
||||||
|
title = "",
|
||||||
|
short_title = "",
|
||||||
|
description = "No description provided",
|
||||||
|
title_placement = "after",
|
||||||
|
dice = {
|
||||||
|
grims = 0,
|
||||||
|
tomes = 0,
|
||||||
|
bonus = 0
|
||||||
|
},
|
||||||
|
difficulty_levels = {
|
||||||
|
"easy",
|
||||||
|
"normal",
|
||||||
|
"hard",
|
||||||
|
"harder",
|
||||||
|
"hardest",
|
||||||
|
|
||||||
|
"survival_hard",
|
||||||
|
"survival_harder",
|
||||||
|
"survival_hardest"
|
||||||
|
},
|
||||||
|
enable_before_these = {},
|
||||||
|
enable_after_these = {},
|
||||||
|
incompatible_with_all = false,
|
||||||
|
compatible_with_all = false,
|
||||||
|
incompatible_with = {},
|
||||||
|
compatible_with = {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
``title = ""``
|
||||||
|
The full title will show up on the map screen as well as when notifying players of enabled mutators.
|
||||||
|
|
||||||
|
``short_title = ""``
|
||||||
|
The short title will be used in the lobby browser.
|
||||||
|
|
||||||
|
``description = "No description provided"``
|
||||||
|
The description will show up in the tooltip of your mutator on the map screen.
|
||||||
|
|
||||||
|
``title_placement = "after"``
|
||||||
|
The determines where the title of your mod will be placed in the tab menu, the lobby name and chat messages: before all other, after all other or in the middle instead of the regular difficulty name (if it is present).
|
||||||
|
Possible values: `"before", "after", "replace"`
|
||||||
|
|
||||||
|
``dice = { grims = 0, tomes = 0, bonus = 0 }``
|
||||||
|
This determines how many additional dice the players will get for completing maps with your mutator enabled.
|
||||||
|
|
||||||
|
```
|
||||||
|
difficulty_levels = {
|
||||||
|
"easy",
|
||||||
|
"normal",
|
||||||
|
"hard",
|
||||||
|
"harder",
|
||||||
|
"hardest",
|
||||||
|
|
||||||
|
"survival_hard",
|
||||||
|
"survival_harder",
|
||||||
|
"survival_hardest"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
This determines which difficulty levels your mutator will be available at. First five are for Adventure and the last three are for Last Stand game mode.
|
||||||
|
|
||||||
|
|
||||||
|
You have a few ways to set compatibility with other mutators. Note that this should be used in the cases where combining mutators somehow breaks the game, not for making your mutator special by prohibiting other to be enabled with it:
|
||||||
|
|
||||||
|
``incompatible_with_all = false,``
|
||||||
|
Set this to true if you are sure combining other mutators with yours will cause problems. Exceptions can be specified in `compatible_with` on this or other mutators.
|
||||||
|
|
||||||
|
``compatible_with_all = false``
|
||||||
|
Set this to true if you are sure this mutator won't cause any problems. This overwrites `incompatible_with_all` on other mutators. Exceptions can be specified in `incompatible_with` on this or other mutators.
|
||||||
|
|
||||||
|
``compatible_with = {}``
|
||||||
|
``incompatible_with = {}``
|
||||||
|
You can provide a list of names of mutators you know for sure yours does/doesn't work with respectively. `compatible_with` overwrites `incompatible_with_all` and `incompatible_with` overwrites `compatible_with_all` on other mutators. Use these to provide exceptions to `incompatible_with_all` and `compatible_with_all` on this mutator.
|
||||||
|
|
||||||
|
``enable_before_these = {},``
|
||||||
|
``enable_after_these = {},``
|
||||||
|
You can improve the compatibility of your mutator with other ones by specifiying which mutators should be enabled after or before this one. This can help with mutators that modify the same portions of the game.
|
||||||
|
|
||||||
|
# Methods
|
||||||
|
|
||||||
|
Mutators have the same methods and event handlers as other mods plus a few additional ones. These are mostly used behind the scenes.
|
||||||
|
|
||||||
|
``mutator:get_config()`` - returns the configuration object without `enable_before_these/enable_after_these` fields. This shouldn't be modified.
|
||||||
|
|
||||||
|
``mutator:can_be_enabled(ignore_map)`` - returns whether the difficulty is right for the mutator and that there are no incompatible mutators enabled. `ignore_map` only takes into account the set difficulty and ignores difficulty selection on the map screen before Play button is pressed.
|
||||||
|
|
||||||
|
``mutator:supports_current_difficulty(ignore_map)`` - same as the last one only doesn't check for incompatible mutators
|
||||||
|
|
||||||
|
``mutator:get_incompatible_mutators(enabled_only)`` - returns an array of incompatible mutators. `enabled_only` only checks for enabled ones.
|
||||||
|
|
||||||
|
The mutators module creates a new mod `vmf_mutator_manager` that has a few additional fields and methods:
|
||||||
|
|
||||||
|
`get_mod("vmf_mutator_manager").mutators` - array of all mutators. A mod can be checked for being a mutator: `table.has_item(get_mod("vmf_mutator_manager").mutators, mod)`
|
||||||
|
|
||||||
|
`get_mod("vmf_mutator_manager").sort_mutators()` - this sorts the mutators in order they should be enabled/disabled. As this only happens when mutators are first enabled and not when they are added, I decided to expose this method for possible future use.
|
||||||
|
|
||||||
|
`get_mod("vmf_mutator_manager").disable_impossible_mutators(notify, everybody)` - disables mutators that can't be enabled on current difficulty settings. This takes into account the difficulty set on the map screen, which is what this was used for at first, but that feature has been disabled due to being annoying. Again, this is exposed for potential future use.
|
|
@ -0,0 +1,26 @@
|
||||||
|
return {
|
||||||
|
dice = {
|
||||||
|
grims = 0,
|
||||||
|
tomes = 0,
|
||||||
|
bonus = 0
|
||||||
|
},
|
||||||
|
title = "",
|
||||||
|
short_title = "",
|
||||||
|
title_placement = "after",
|
||||||
|
description = "No description provided",
|
||||||
|
difficulty_levels = {
|
||||||
|
"easy",
|
||||||
|
"normal",
|
||||||
|
"hard",
|
||||||
|
"harder",
|
||||||
|
"hardest",
|
||||||
|
|
||||||
|
"survival_hard",
|
||||||
|
"survival_harder",
|
||||||
|
"survival_hardest"
|
||||||
|
},
|
||||||
|
incompatible_with_all = false,
|
||||||
|
compatible_with_all = false,
|
||||||
|
incompatible_with = {},
|
||||||
|
compatible_with = {}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
--[[ Add additional dice to end game roll --]]
|
||||||
|
|
||||||
|
local manager = get_mod("vmf_mutator_manager")
|
||||||
|
|
||||||
|
-- List of all die types
|
||||||
|
local missions = {
|
||||||
|
"bonus_dice_hidden_mission",
|
||||||
|
"tome_bonus_mission",
|
||||||
|
"grimoire_hidden_mission"
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Amounts of additional dice to be added at level completion
|
||||||
|
local num_dice_per_mission = {
|
||||||
|
bonus_dice_hidden_mission = 0,
|
||||||
|
tome_bonus_mission = 0,
|
||||||
|
grimoire_hidden_mission = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
manager:hook("GameModeManager.complete_level", function(func, self)
|
||||||
|
local num_dice = 0
|
||||||
|
local max_dice = 7
|
||||||
|
local mission_system = Managers.state.entity:system("mission_system")
|
||||||
|
local active_mission = mission_system.active_missions
|
||||||
|
|
||||||
|
-- Add additional dice
|
||||||
|
for _, mission in ipairs(missions) do
|
||||||
|
for _ = 1, num_dice_per_mission[mission] do
|
||||||
|
mission_system:request_mission(mission, nil, Network.peer_id())
|
||||||
|
mission_system:update_mission(mission, true, nil, Network.peer_id(), nil, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get total number of dice
|
||||||
|
for name, obj in pairs(active_mission) do
|
||||||
|
if table.has_item(missions, name) then
|
||||||
|
num_dice = num_dice + obj.current_amount
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Remove excess dice
|
||||||
|
for _, mission in ipairs(missions) do
|
||||||
|
if active_mission[mission] then
|
||||||
|
for _ = 1, active_mission[mission].current_amount do
|
||||||
|
if num_dice > max_dice then
|
||||||
|
mission_system:request_mission(mission, nil, Network.peer_id())
|
||||||
|
mission_system:update_mission(mission, false, nil, Network.peer_id(), nil, true)
|
||||||
|
num_dice = num_dice - 1
|
||||||
|
else break end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if num_dice <= max_dice then break end
|
||||||
|
end
|
||||||
|
|
||||||
|
return func(self)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Adds/remove dice
|
||||||
|
local function adjustDice(grims, tomes, bonus, multiplier)
|
||||||
|
if grims then num_dice_per_mission.grimoire_hidden_mission = num_dice_per_mission.grimoire_hidden_mission + grims * multiplier end
|
||||||
|
if tomes then num_dice_per_mission.tome_bonus_mission = num_dice_per_mission.tome_bonus_mission + tomes * multiplier end
|
||||||
|
if bonus then num_dice_per_mission.bonus_dice_hidden_mission = num_dice_per_mission.bonus_dice_hidden_mission + bonus * multiplier end
|
||||||
|
end
|
||||||
|
|
||||||
|
local addDice = function(dice)
|
||||||
|
dice = dice or {}
|
||||||
|
adjustDice(dice.grims, dice.tomes, dice.bonus, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local removeDice = function(dice)
|
||||||
|
dice = dice or {}
|
||||||
|
adjustDice(dice.grims, dice.tomes, dice.bonus, -1)
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
addDice = addDice,
|
||||||
|
removeDice = removeDice
|
||||||
|
}
|
461
vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua
Normal file
461
vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua
Normal file
|
@ -0,0 +1,461 @@
|
||||||
|
--[[ Add mutators panel to the map view --]]
|
||||||
|
|
||||||
|
local manager = get_mod("vmf_mutator_manager")
|
||||||
|
local mutators = manager.mutators
|
||||||
|
|
||||||
|
local definitions = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_gui_definitions")
|
||||||
|
|
||||||
|
local PER_PAGE = definitions.PER_PAGE
|
||||||
|
|
||||||
|
local mutators_view = {
|
||||||
|
|
||||||
|
initialized = false,
|
||||||
|
active = false,
|
||||||
|
was_active = false,
|
||||||
|
map_view = nil,
|
||||||
|
current_page = 1,
|
||||||
|
mutators_sorted = {},
|
||||||
|
mutator_checkboxes = {},
|
||||||
|
|
||||||
|
init = function(self, map_view)
|
||||||
|
if self.initialized then return end
|
||||||
|
|
||||||
|
self.map_view = map_view
|
||||||
|
if not self.map_view then return end
|
||||||
|
|
||||||
|
self:update_mutator_list()
|
||||||
|
|
||||||
|
-- Recreate the map_view scenegraph defs
|
||||||
|
self.map_view.scenegraph_definition = UISceneGraph.init_scenegraph(definitions.scenegraph_definition)
|
||||||
|
|
||||||
|
-- Setup custom widgets
|
||||||
|
self.widgets = {
|
||||||
|
banner_mutators = UIWidget.init(definitions.new_widgets.banner_mutators_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
|
||||||
|
local widgets = self.map_view.normal_settings_widget_types
|
||||||
|
self.saved_widgets = {
|
||||||
|
level_preview = widgets.adventure.level_preview,
|
||||||
|
level_preview_text = widgets.adventure.level_preview_text
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Add our button to render lists
|
||||||
|
widgets.adventure.mutators_button = self.widgets.mutators_button
|
||||||
|
widgets.survival.mutators_button = self.widgets.mutators_button
|
||||||
|
self.map_view.advanced_settings_widgets.mutators_button = self.widgets.mutators_button
|
||||||
|
|
||||||
|
-- Move other buttons over
|
||||||
|
self.map_view.ui_scenegraph.settings_button.position[1] = -50
|
||||||
|
self.map_view.ui_scenegraph.lobby_button.position[1] = 150
|
||||||
|
self.map_view.ui_scenegraph.friends_button.position[1] = 50
|
||||||
|
|
||||||
|
-- Alter level select stepper's callback
|
||||||
|
self.map_view.steppers.level.callback = function(index_change)
|
||||||
|
self:on_mutators_page_change(index_change)
|
||||||
|
end
|
||||||
|
|
||||||
|
self:setup_hooks()
|
||||||
|
|
||||||
|
self.initialized = true
|
||||||
|
--print("INIT")
|
||||||
|
end,
|
||||||
|
|
||||||
|
deinitialize = function(self)
|
||||||
|
if not self.initialized then return end
|
||||||
|
|
||||||
|
self:deactivate()
|
||||||
|
self.was_active = false
|
||||||
|
|
||||||
|
-- Reset the stepper callback
|
||||||
|
self.map_view.steppers.level.callback = callback(self.map_view, "on_level_index_changed")
|
||||||
|
|
||||||
|
-- Remove our button
|
||||||
|
self.map_view.normal_settings_widget_types.adventure.mutators_button = nil
|
||||||
|
self.map_view.normal_settings_widget_types.survival.mutators_button = nil
|
||||||
|
self.map_view.advanced_settings_widgets.mutators_button = nil
|
||||||
|
|
||||||
|
-- Move other buttons back
|
||||||
|
self.map_view.ui_scenegraph.settings_button.position[1] = -100
|
||||||
|
self.map_view.ui_scenegraph.lobby_button.position[1] = 100
|
||||||
|
self.map_view.ui_scenegraph.friends_button.position[1] = 0
|
||||||
|
|
||||||
|
self.saved_widgets = {}
|
||||||
|
|
||||||
|
self:reset_hooks()
|
||||||
|
|
||||||
|
self.map_view = nil
|
||||||
|
|
||||||
|
self.initialized = false
|
||||||
|
--print("DEINIT")
|
||||||
|
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)
|
||||||
|
if not self.initialized then
|
||||||
|
self:init()
|
||||||
|
end
|
||||||
|
|
||||||
|
if not self.initialized or not self.map_view.active then return end
|
||||||
|
|
||||||
|
local transitioning = self.map_view:transitioning()
|
||||||
|
local friends = self.map_view.friends
|
||||||
|
local friends_menu_active = friends:is_active()
|
||||||
|
|
||||||
|
local mutators_button = self.widgets.mutators_button
|
||||||
|
local mutators_button_hotspot = mutators_button.content.button_hotspot
|
||||||
|
local settings_button = self.map_view.settings_button_widget
|
||||||
|
|
||||||
|
-- Handle menu toggles
|
||||||
|
if not transitioning and not friends_menu_active then
|
||||||
|
if mutators_button_hotspot.on_release then
|
||||||
|
self.map_view:play_sound("Play_hud_select")
|
||||||
|
mutators_button.content.toggled = not mutators_button.content.toggled
|
||||||
|
if mutators_button.content.toggled then
|
||||||
|
settings_button.content.toggled = false
|
||||||
|
end
|
||||||
|
elseif settings_button.content.toggled then
|
||||||
|
mutators_button.content.toggled = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Open/close mutators view
|
||||||
|
if mutators_button.content.toggled then
|
||||||
|
self:activate()
|
||||||
|
self.was_active = true
|
||||||
|
else
|
||||||
|
self:deactivate()
|
||||||
|
self.was_active = false
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.active then
|
||||||
|
-- Disable the Mission banner tooltip
|
||||||
|
local widgets = self.map_view.normal_settings_widget_types
|
||||||
|
widgets.adventure.banner_level.content.tooltip_hotspot.disabled = true
|
||||||
|
widgets.survival.banner_level.content.tooltip_hotspot.disabled = true
|
||||||
|
|
||||||
|
self:update_checkboxes()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Sets appropriate text and style to checkboxes, hides/shows them as needed
|
||||||
|
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
|
||||||
|
|
||||||
|
-- Hide if fewer mutators shown than there are checkboxes
|
||||||
|
if #self.mutators_sorted < current_index then
|
||||||
|
|
||||||
|
checkbox.content.setting_text = ""
|
||||||
|
checkbox.content.tooltip_text = ""
|
||||||
|
|
||||||
|
-- Remove from render lists
|
||||||
|
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])
|
||||||
|
|
||||||
|
-- Set text and tooltip
|
||||||
|
checkbox.content.setting_text = mutator_info[2]
|
||||||
|
checkbox.content.tooltip_text = self:generate_tooltip_for(mutator)
|
||||||
|
|
||||||
|
-- Add to render lists
|
||||||
|
widgets.adventure["mutator_checkbox_" .. i] = checkbox
|
||||||
|
widgets.survival["mutator_checkbox_" .. i] = checkbox
|
||||||
|
|
||||||
|
-- Set colors based on whether mutator can be enabled
|
||||||
|
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)
|
||||||
|
|
||||||
|
-- Sound on hover
|
||||||
|
if hotspot.on_hover_enter then
|
||||||
|
self.map_view:play_sound("Play_hud_hover")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Click event
|
||||||
|
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
|
||||||
|
|
||||||
|
local checkbox = self.mutator_checkboxes[1]
|
||||||
|
if #mutators == 0 then
|
||||||
|
|
||||||
|
widgets.adventure["mutator_checkbox_" .. 1] = checkbox
|
||||||
|
widgets.survival["mutator_checkbox_" .. 1] = checkbox
|
||||||
|
|
||||||
|
checkbox.style.setting_text.text_color = Colors.get_color_table_with_alpha("slate_gray", 255)
|
||||||
|
checkbox.style.setting_text_hover.text_color = Colors.get_color_table_with_alpha("slate_gray", 255)
|
||||||
|
checkbox.style.checkbox_style.color = Colors.get_color_table_with_alpha("slate_gray", 255)
|
||||||
|
|
||||||
|
checkbox.content.setting_text = "No mutators installed"
|
||||||
|
checkbox.content.tooltip_text = "Subscribe to mods and mutators on the workshop"
|
||||||
|
|
||||||
|
checkbox.style.checkbox_style.offset[1] = -10000
|
||||||
|
checkbox.style.setting_text.horizontal_alignment = "center"
|
||||||
|
checkbox.style.setting_text_hover.horizontal_alignment = "center"
|
||||||
|
checkbox.style.setting_text.offset[1] = 0
|
||||||
|
checkbox.style.setting_text_hover.offset[1] = 0
|
||||||
|
else
|
||||||
|
checkbox.style.checkbox_style.offset[1] = 0
|
||||||
|
checkbox.style.setting_text.horizontal_alignment = "left"
|
||||||
|
checkbox.style.setting_text_hover.horizontal_alignment = "left"
|
||||||
|
checkbox.style.setting_text.offset[1] = 24
|
||||||
|
checkbox.style.setting_text_hover.offset[1] = 24
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Activate on button click or map open
|
||||||
|
activate = function(self)
|
||||||
|
if not self.initialized or not self.map_view.active or self.active then return end
|
||||||
|
|
||||||
|
-- Hiding widgets
|
||||||
|
local widgets = self.map_view.normal_settings_widget_types
|
||||||
|
|
||||||
|
widgets.adventure.level_preview = nil
|
||||||
|
widgets.adventure.level_preview_text = nil
|
||||||
|
widgets.adventure.banner_mutators = self.widgets.banner_mutators
|
||||||
|
|
||||||
|
widgets.survival.level_preview = nil
|
||||||
|
widgets.survival.level_preview_text = nil
|
||||||
|
widgets.survival.banner_mutators = self.widgets.banner_mutators
|
||||||
|
|
||||||
|
-- "Mission" banner position
|
||||||
|
self.map_view.ui_scenegraph.banner_level_text.position[2] = -10000
|
||||||
|
|
||||||
|
-- Update steppers
|
||||||
|
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 num_pages = math.ceil(#mutators/PER_PAGE)
|
||||||
|
level_stepper_widget.content.left_button_hotspot.disable_button = num_pages <= 1
|
||||||
|
level_stepper_widget.content.right_button_hotspot.disable_button = num_pages <= 1
|
||||||
|
|
||||||
|
self.active = true
|
||||||
|
|
||||||
|
--print("ACTIVE!")
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Deactivate on button click or map close
|
||||||
|
deactivate = function(self)
|
||||||
|
if not self.initialized or not self.active then return end
|
||||||
|
|
||||||
|
self.active = false
|
||||||
|
|
||||||
|
-- Showing widgets
|
||||||
|
local widgets = self.map_view.normal_settings_widget_types
|
||||||
|
|
||||||
|
widgets.adventure.level_preview = self.saved_widgets.level_preview
|
||||||
|
widgets.adventure.level_preview_text = self.saved_widgets.level_preview
|
||||||
|
widgets.adventure.banner_mutators = nil
|
||||||
|
|
||||||
|
widgets.survival.level_preview = self.saved_widgets.level_preview
|
||||||
|
widgets.survival.level_preview_text = self.saved_widgets.level_preview
|
||||||
|
widgets.survival.banner_mutators = nil
|
||||||
|
|
||||||
|
-- "Mission" banner position
|
||||||
|
self.map_view.ui_scenegraph.banner_level_text.position[2] = 0
|
||||||
|
|
||||||
|
-- Update steppers
|
||||||
|
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()
|
||||||
|
|
||||||
|
-- Mutator checkboxes
|
||||||
|
for i = 1, PER_PAGE do
|
||||||
|
widgets.adventure["mutator_checkbox_" .. i] = nil
|
||||||
|
widgets.survival["mutator_checkbox_" .. i] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--print("DEACTIVE")
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Changes which muttators are displayed
|
||||||
|
on_mutators_page_change = function(self, index_change)
|
||||||
|
if not self.initialized then return end
|
||||||
|
|
||||||
|
if self.active then
|
||||||
|
local current_index = self.current_page
|
||||||
|
local new_index = current_index + index_change
|
||||||
|
local num_pages = math.ceil(#mutators/PER_PAGE)
|
||||||
|
|
||||||
|
if new_index < 1 then
|
||||||
|
new_index = num_pages
|
||||||
|
elseif num_pages < new_index then
|
||||||
|
new_index = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
self.current_page = new_index
|
||||||
|
else
|
||||||
|
self.map_view:on_level_index_changed(index_change)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Creates and return text for checkbox tooltip
|
||||||
|
generate_tooltip_for = function(self, mutator)
|
||||||
|
local config = mutator:get_config()
|
||||||
|
local text = ""
|
||||||
|
|
||||||
|
-- Show supported difficulty when can't be enabled due to difficulty level
|
||||||
|
local supports_difficulty = mutator:supports_current_difficulty()
|
||||||
|
if not supports_difficulty then
|
||||||
|
text = text .. "\nSupported difficulty levels:"
|
||||||
|
for i, difficulty in ipairs(config.difficulty_levels) do
|
||||||
|
text = text .. (i == 1 and " " or ", ") .. manager:localize(difficulty)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Show enabled incompatible
|
||||||
|
local incompatible_mutators = mutator:get_incompatible_mutators(true)
|
||||||
|
local currently_compatible = #incompatible_mutators == 0
|
||||||
|
|
||||||
|
-- Or all incompatible if difficulty is compatible
|
||||||
|
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
|
||||||
|
-- Show special message when incompatible with all
|
||||||
|
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
|
||||||
|
-- Special message when compatible with all
|
||||||
|
text = text .. "\nCompatible with all other mutators"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Special message if switched to unsupported difficulty level
|
||||||
|
if mutator:is_enabled() and not supports_difficulty then
|
||||||
|
text = text .. "\nWill be disabled when Play is pressed"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Description
|
||||||
|
if string.len(text) > 0 then
|
||||||
|
text = "\n-------------" .. text
|
||||||
|
end
|
||||||
|
text = config.description .. text
|
||||||
|
|
||||||
|
return text
|
||||||
|
end,
|
||||||
|
|
||||||
|
setup_hooks = function(self)
|
||||||
|
|
||||||
|
-- Update the view after map_view has updated
|
||||||
|
manager:hook("MapView.update", function(func, map_view, dt, t)
|
||||||
|
func(map_view, dt, t)
|
||||||
|
self:update(dt, t)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Activate the view on enter if it was active on exit
|
||||||
|
manager:hook("MapView.on_enter", function(func, map_view)
|
||||||
|
func(map_view)
|
||||||
|
if self.was_active then
|
||||||
|
self.widgets.mutators_button.content.toggled = true
|
||||||
|
self:activate()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Deactivate the view on exit
|
||||||
|
manager:hook("MapView.on_exit", function(func, map_view)
|
||||||
|
func(map_view)
|
||||||
|
self:deactivate()
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- We don't want to let the game disable steppers when mutators view is active
|
||||||
|
manager:hook("MapView.update_level_stepper", function(func, map_view)
|
||||||
|
if not self.active then
|
||||||
|
func(map_view)
|
||||||
|
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,
|
||||||
|
|
||||||
|
reset_hooks = function(self)
|
||||||
|
manager:hook_remove("MapView.update")
|
||||||
|
manager:hook_remove("MapView.on_enter")
|
||||||
|
manager:hook_remove("MapView.on_exit")
|
||||||
|
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,
|
||||||
|
|
||||||
|
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, ...)
|
||||||
|
mutators_view:init(self)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Destroy mutators view after map view
|
||||||
|
manager:hook("MapView.destroy", function(func, ...)
|
||||||
|
mutators_view:deinitialize()
|
||||||
|
func(...)
|
||||||
|
end)
|
||||||
|
|
||||||
|
return mutators_view
|
|
@ -0,0 +1,331 @@
|
||||||
|
|
||||||
|
local definitions = local_require("scripts/ui/views/map_view_definitions")
|
||||||
|
local scenegraph_definition = definitions.scenegraph_definition
|
||||||
|
|
||||||
|
-- Mutators to show per page
|
||||||
|
definitions.PER_PAGE = 6
|
||||||
|
|
||||||
|
-- Button to toggle mutators view
|
||||||
|
scenegraph_definition.mutators_button = {
|
||||||
|
vertical_alignment = "bottom",
|
||||||
|
parent = "banner_party",
|
||||||
|
horizontal_alignment = "center",
|
||||||
|
size = {
|
||||||
|
64,
|
||||||
|
64
|
||||||
|
},
|
||||||
|
position = {
|
||||||
|
-150,
|
||||||
|
90,
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- This will replace the Mission text
|
||||||
|
scenegraph_definition.banner_mutators_text = {
|
||||||
|
vertical_alignment = "center",
|
||||||
|
parent = "banner_level",
|
||||||
|
horizontal_alignment = "center",
|
||||||
|
size = {
|
||||||
|
300,
|
||||||
|
40
|
||||||
|
},
|
||||||
|
position = {
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
local new_widgets = {
|
||||||
|
|
||||||
|
-- This will replace the banner behind the Mission 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",
|
||||||
|
scenegraph_id = "banner_mutators_text",
|
||||||
|
localize = false,
|
||||||
|
font_size = 28,
|
||||||
|
horizontal_alignment = "center",
|
||||||
|
font_type = "hell_shark",
|
||||||
|
text_color = Colors.get_color_table_with_alpha("cheeseburger", 255)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
font_size = 24,
|
||||||
|
max_width = 500,
|
||||||
|
localize = false,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|
||||||
|
-- Button to toggle mutators view
|
||||||
|
mutators_button_widget = {
|
||||||
|
element = UIElements.ToggleIconButton,
|
||||||
|
content = {
|
||||||
|
click_texture = "octagon_button_clicked",
|
||||||
|
toggle_hover_texture = "octagon_button_toggled_hover",
|
||||||
|
toggle_texture = "octagon_button_toggled",
|
||||||
|
hover_texture = "octagon_button_hover",
|
||||||
|
normal_texture = "octagon_button_normal",
|
||||||
|
icon_texture = "mutator_button",
|
||||||
|
icon_hover_texture = "mutator_button_hover",
|
||||||
|
tooltip_text = "Mutators",
|
||||||
|
toggled_tooltip_text = "Mutators",
|
||||||
|
button_hotspot = {}
|
||||||
|
},
|
||||||
|
style = {
|
||||||
|
normal_texture = {
|
||||||
|
color = {
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hover_texture = {
|
||||||
|
color = {
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
}
|
||||||
|
},
|
||||||
|
click_texture = {
|
||||||
|
color = {
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toggle_texture = {
|
||||||
|
color = {
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toggle_hover_texture = {
|
||||||
|
color = {
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon_texture = {
|
||||||
|
color = {
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
},
|
||||||
|
offset = {
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon_hover_texture = {
|
||||||
|
color = {
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
},
|
||||||
|
offset = {
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon_click_texture = {
|
||||||
|
color = {
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
},
|
||||||
|
offset = {
|
||||||
|
0,
|
||||||
|
-1,
|
||||||
|
1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip_text = {
|
||||||
|
font_size = 24,
|
||||||
|
max_width = 500,
|
||||||
|
localize = false,
|
||||||
|
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,
|
||||||
|
20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scenegraph_id = "mutators_button"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Checkboxes
|
||||||
|
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 = "",
|
||||||
|
checkbox_unchecked_texture = "checkbox_unchecked",
|
||||||
|
checkbox_checked_texture = "checkbox_checked",
|
||||||
|
selected = false,
|
||||||
|
setting_text = "",
|
||||||
|
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
|
|
@ -0,0 +1,95 @@
|
||||||
|
--[[ Notify players of enabled mutators via chat and tab menu --]]
|
||||||
|
|
||||||
|
local manager = get_mod("vmf_mutator_manager")
|
||||||
|
local mutators = manager.mutators
|
||||||
|
|
||||||
|
local were_enabled_before = false
|
||||||
|
|
||||||
|
-- Assembles a list of enabled mutators
|
||||||
|
local function add_enabled_mutators_titles_to_string(str, separator, short)
|
||||||
|
local _mutators = {}
|
||||||
|
for _, mutator in ipairs(mutators) do
|
||||||
|
if mutator:is_enabled() then
|
||||||
|
table.insert(_mutators, mutator)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return manager.add_mutator_titles_to_string(_mutators, str, separator, short)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Sets the lobby name
|
||||||
|
local function set_lobby_data()
|
||||||
|
|
||||||
|
if (
|
||||||
|
not Managers.matchmaking or
|
||||||
|
not Managers.matchmaking.lobby or
|
||||||
|
not Managers.matchmaking.lobby.set_lobby_data or
|
||||||
|
not Managers.matchmaking.lobby.get_stored_lobby_data
|
||||||
|
) then return end
|
||||||
|
|
||||||
|
local name = add_enabled_mutators_titles_to_string("", " ", true)
|
||||||
|
|
||||||
|
local default_name = LobbyAux.get_unique_server_name()
|
||||||
|
if string.len(name) > 0 then
|
||||||
|
name = "||" .. name .. "|| " .. default_name
|
||||||
|
else
|
||||||
|
name = default_name
|
||||||
|
end
|
||||||
|
|
||||||
|
local lobby_data = Managers.matchmaking.lobby:get_stored_lobby_data()
|
||||||
|
lobby_data.unique_server_name = name
|
||||||
|
|
||||||
|
Managers.matchmaking.lobby:set_lobby_data(lobby_data)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Return a function for chat system to only send messages to specific client
|
||||||
|
local function get_peer_id_from_cookie(client_cookie)
|
||||||
|
local peer_id = tostring(client_cookie)
|
||||||
|
for _ = 1, 3 do
|
||||||
|
peer_id = string.sub(peer_id, 1 + tonumber(tostring(string.find(peer_id,"-"))))
|
||||||
|
end
|
||||||
|
peer_id = string.sub(peer_id, 2)
|
||||||
|
peer_id = string.reverse(peer_id)
|
||||||
|
peer_id = string.sub(peer_id, 2)
|
||||||
|
peer_id = string.reverse(peer_id)
|
||||||
|
|
||||||
|
return peer_id
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Append difficulty name with enabled mutators' titles
|
||||||
|
manager:hook("IngamePlayerListUI.update_difficulty", function(func, self)
|
||||||
|
local difficulty_settings = Managers.state.difficulty:get_difficulty_settings()
|
||||||
|
local difficulty_name = difficulty_settings.display_name
|
||||||
|
|
||||||
|
local name = not self.is_in_inn and Localize(difficulty_name) or ""
|
||||||
|
name = add_enabled_mutators_titles_to_string(name, " ", true)
|
||||||
|
|
||||||
|
self.set_difficulty_name(self, name)
|
||||||
|
|
||||||
|
self.current_difficulty_name = difficulty_name
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Notify everybody about enabled/disabled mutators when Play button is pressed on the map screen
|
||||||
|
manager:hook("MatchmakingStateHostGame.host_game", function(func, self, ...)
|
||||||
|
func(self, ...)
|
||||||
|
set_lobby_data()
|
||||||
|
local names = add_enabled_mutators_titles_to_string("", ", ")
|
||||||
|
if string.len(names) > 0 then
|
||||||
|
manager:chat_broadcast("ENABLED MUTATORS: " .. names)
|
||||||
|
were_enabled_before = true
|
||||||
|
elseif were_enabled_before then
|
||||||
|
manager:chat_broadcast("ALL MUTATORS DISABLED")
|
||||||
|
were_enabled_before = false
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Send special messages with enabled mutators list to players just joining the lobby
|
||||||
|
manager:hook("MatchmakingManager.rpc_matchmaking_request_join_lobby", function(func, self, sender, client_cookie, host_cookie, lobby_id, friend_join)
|
||||||
|
local name = add_enabled_mutators_titles_to_string("", ", ")
|
||||||
|
if string.len(name) > 0 then
|
||||||
|
local message = "[Automated message] This lobby has the following difficulty mod active : " .. name
|
||||||
|
manager:chat_whisper(get_peer_id_from_cookie(client_cookie), message)
|
||||||
|
end
|
||||||
|
func(self, sender, client_cookie, host_cookie, lobby_id, friend_join)
|
||||||
|
end)
|
||||||
|
|
||||||
|
return set_lobby_data
|
451
vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua
Normal file
451
vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua
Normal file
|
@ -0,0 +1,451 @@
|
||||||
|
--[[ Add ability to turn mods into mutators --]]
|
||||||
|
|
||||||
|
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
|
||||||
|
-- This is populated via VMFMod.register_as_mutator
|
||||||
|
manager.mutators = {}
|
||||||
|
local mutators = manager.mutators
|
||||||
|
|
||||||
|
local mutators_config = {}
|
||||||
|
local default_config = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_default_config")
|
||||||
|
|
||||||
|
-- This lists mutators and which ones should be enabled after them
|
||||||
|
-- This is populated via VMFMod.register_as_mutator
|
||||||
|
local mutators_sequence = {
|
||||||
|
--[[
|
||||||
|
this_mutator = {
|
||||||
|
"will be enabled",
|
||||||
|
"before these ones"
|
||||||
|
}
|
||||||
|
]]--
|
||||||
|
}
|
||||||
|
|
||||||
|
-- So we don't sort after each one is added
|
||||||
|
local mutators_sorted = false
|
||||||
|
|
||||||
|
|
||||||
|
--[[
|
||||||
|
PRIVATE METHODS
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local mutators_view = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_gui")
|
||||||
|
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")
|
||||||
|
|
||||||
|
-- Adds mutator names from enable_these_after to the list of mutators that should be enabled after the mutator_name
|
||||||
|
local function update_mutators_sequence(mutator_name, enable_these_after)
|
||||||
|
if not mutators_sequence[mutator_name] then
|
||||||
|
mutators_sequence[mutator_name] = {}
|
||||||
|
end
|
||||||
|
for _, other_mutator_name in ipairs(enable_these_after) do
|
||||||
|
|
||||||
|
if mutators_sequence[other_mutator_name] and table.has_item(mutators_sequence[other_mutator_name], mutator_name) then
|
||||||
|
manager:error("Mutators '" .. mutator_name .. "' and '" .. other_mutator_name .. "' are both set to load after the other one.")
|
||||||
|
elseif not table.has_item(mutators_sequence[mutator_name], other_mutator_name) then
|
||||||
|
table.insert(mutators_sequence[mutator_name], other_mutator_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
table.combine(mutators_sequence[mutator_name], enable_these_after)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Checks if mutators are compatible both ways
|
||||||
|
local function is_compatible(mutator, other_mutator)
|
||||||
|
local config = mutator:get_config()
|
||||||
|
local name = mutator:get_name()
|
||||||
|
local other_config = other_mutator:get_config()
|
||||||
|
local other_name = other_mutator:get_name()
|
||||||
|
|
||||||
|
local incompatible_specifically = (
|
||||||
|
#config.incompatible_with > 0 and (
|
||||||
|
table.has_item(config.incompatible_with, other_name)
|
||||||
|
) or
|
||||||
|
#other_config.incompatible_with > 0 and (
|
||||||
|
table.has_item(other_config.incompatible_with, name)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
local compatible_specifically = (
|
||||||
|
#config.compatible_with > 0 and (
|
||||||
|
table.has_item(config.compatible_with, other_name)
|
||||||
|
) or
|
||||||
|
#other_config.compatible_with > 0 and (
|
||||||
|
table.has_item(other_config.compatible_with, name)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
local compatible
|
||||||
|
if incompatible_specifically then
|
||||||
|
compatible = false
|
||||||
|
elseif compatible_specifically then
|
||||||
|
compatible = true
|
||||||
|
elseif config.compatible_with_all or other_config.compatible_with_all then
|
||||||
|
compatible = true
|
||||||
|
elseif config.incompatible_with_all or other_config.incompatible_with_all then
|
||||||
|
compatible = false
|
||||||
|
else
|
||||||
|
compatible = true
|
||||||
|
end
|
||||||
|
|
||||||
|
return compatible
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Called after mutator is enabled
|
||||||
|
local function on_enabled(mutator)
|
||||||
|
local config = mutator:get_config()
|
||||||
|
dice_manager.addDice(config.dice)
|
||||||
|
set_lobby_data()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Called after mutator is disabled
|
||||||
|
local function on_disabled(mutator)
|
||||||
|
local config = mutator:get_config()
|
||||||
|
dice_manager.removeDice(config.dice)
|
||||||
|
set_lobby_data()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Enables/disables mutator while preserving the sequence in which they were enabled
|
||||||
|
local function set_mutator_state(mutator, state)
|
||||||
|
|
||||||
|
local i = table.index_of(mutators, mutator)
|
||||||
|
if i == nil then
|
||||||
|
mutator:error("Mutator isn't in the list")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if state == mutator:is_enabled() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if state and not mutator:can_be_enabled() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Sort mutators if this is the first call
|
||||||
|
if not mutators_sorted then
|
||||||
|
manager.sort_mutators()
|
||||||
|
end
|
||||||
|
|
||||||
|
local disabled_mutators = {}
|
||||||
|
local enable_these_after = mutators_sequence[mutator:get_name()]
|
||||||
|
|
||||||
|
-- Disable mutators that were and are required to be enabled after the current one
|
||||||
|
-- This will be recursive so that if mutator2 requires mutator3 to be enabled after it, mutator3 will be disabled before mutator2
|
||||||
|
-- Yeah this is super confusing
|
||||||
|
if enable_these_after and #mutators > i then
|
||||||
|
for j = #mutators, i + 1, -1 do
|
||||||
|
if mutators[j]:is_enabled() and table.has_item(enable_these_after, mutators[j]:get_name()) then
|
||||||
|
--print("Disabled ", mutators[j]:get_name())
|
||||||
|
mutators[j]:disable()
|
||||||
|
table.insert(disabled_mutators, 1, mutators[j])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Enable/disable current mutator
|
||||||
|
-- We're calling methods on the class object because we've overwritten them on the current one
|
||||||
|
if state then
|
||||||
|
--print("Enabled ", mutator:get_name(), "!")
|
||||||
|
VMFMod.enable(mutator)
|
||||||
|
on_enabled(mutator)
|
||||||
|
else
|
||||||
|
--print("Disabled ", mutator:get_name(), "!")
|
||||||
|
VMFMod.disable(mutator)
|
||||||
|
on_disabled(mutator)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Re-enable disabled mutators
|
||||||
|
-- This will be recursive
|
||||||
|
if #disabled_mutators > 0 then
|
||||||
|
for j = #disabled_mutators, 1, -1 do
|
||||||
|
--print("Enabled ", disabled_mutators[j]:get_name())
|
||||||
|
disabled_mutators[j]:enable()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Checks if the player is server in a way that doesn't incorrectly return false during loading screens
|
||||||
|
local function player_is_server()
|
||||||
|
local player = Managers.player
|
||||||
|
local state = Managers.state
|
||||||
|
return not player or player.is_server or not state or state.game_mode == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
PUBLIC METHODS
|
||||||
|
]]--
|
||||||
|
|
||||||
|
-- Sorts mutators in order they should be enabled
|
||||||
|
manager.sort_mutators = function()
|
||||||
|
|
||||||
|
if mutators_sorted then return end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
-- LOG --
|
||||||
|
manager:dump(mutators_sequence, "seq", 5)
|
||||||
|
for i, v in ipairs(mutators) do
|
||||||
|
print(i, v:get_name())
|
||||||
|
end
|
||||||
|
print("-----------")
|
||||||
|
-- /LOG --
|
||||||
|
--]]
|
||||||
|
|
||||||
|
-- Preventing endless loops (worst case is n*(n+1)/2 I believe)
|
||||||
|
local maxIter = #mutators * (#mutators + 1)/2
|
||||||
|
local numIter = 0
|
||||||
|
|
||||||
|
-- The idea is that all mutators before the current one are already in the right order
|
||||||
|
-- Starting from second mutator
|
||||||
|
local i = 2
|
||||||
|
while i <= #mutators do
|
||||||
|
local mutator = mutators[i]
|
||||||
|
local mutator_name = mutator:get_name()
|
||||||
|
local enable_these_after = mutators_sequence[mutator_name] or {}
|
||||||
|
|
||||||
|
-- Going back from the previous mutator to the start of the list
|
||||||
|
local j = i - 1
|
||||||
|
while j > 0 do
|
||||||
|
local other_mutator = mutators[j]
|
||||||
|
|
||||||
|
-- Moving it after the current one if it is to be enabled after it
|
||||||
|
if table.has_item(enable_these_after, other_mutator:get_name()) then
|
||||||
|
table.remove(mutators, j)
|
||||||
|
table.insert(mutators, i, other_mutator)
|
||||||
|
|
||||||
|
-- This will shift the current mutator back, so adjust the index
|
||||||
|
i = i - 1
|
||||||
|
end
|
||||||
|
j = j - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
i = i + 1
|
||||||
|
|
||||||
|
numIter = numIter + 1
|
||||||
|
if numIter > maxIter then
|
||||||
|
manager:error("Mutators: too many iterations. Check for loops in 'enable_before_these'/'enable_after_these'.")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
mutators_sorted = true
|
||||||
|
|
||||||
|
--[[
|
||||||
|
-- LOG --
|
||||||
|
for k, v in ipairs(mutators) do
|
||||||
|
print(k, v:get_name())
|
||||||
|
end
|
||||||
|
print("-----------")
|
||||||
|
-- /LOG --
|
||||||
|
--]]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Disables mutators that cannot be enabled right now
|
||||||
|
manager.disable_impossible_mutators = function(notify, everybody, reason)
|
||||||
|
local disabled_mutators = {}
|
||||||
|
for _, mutator in pairs(mutators) do
|
||||||
|
if mutator:is_enabled() and not mutator:can_be_enabled() then
|
||||||
|
mutator:disable()
|
||||||
|
table.insert(disabled_mutators, mutator)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #disabled_mutators > 0 and notify then
|
||||||
|
if not reason then reason = "" end
|
||||||
|
local message = everybody and "MUTATORS DISABLED " .. reason .. ":" or "Mutators disabled " .. reason .. ":"
|
||||||
|
message = message .. " " .. manager.add_mutator_titles_to_string(disabled_mutators, "", ", ", false)
|
||||||
|
if everybody then
|
||||||
|
manager:chat_broadcast(message)
|
||||||
|
else
|
||||||
|
manager:echo(message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return disabled_mutators
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if player is still hosting
|
||||||
|
manager.update = function()
|
||||||
|
if not player_is_server() then
|
||||||
|
manager.disable_impossible_mutators(true, false, "because you're no longer the host")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Appends, prepends and replaces the string with mutator titles
|
||||||
|
manager.add_mutator_titles_to_string = function(_mutators, str, separator, short)
|
||||||
|
|
||||||
|
if #_mutators == 0 then return str end
|
||||||
|
|
||||||
|
local before = nil
|
||||||
|
local after = nil
|
||||||
|
local replace = nil
|
||||||
|
|
||||||
|
for _, mutator in ipairs(_mutators) do
|
||||||
|
local config = mutator:get_config()
|
||||||
|
local added_name = (short and config.short_title or config.title or mutator:get_name())
|
||||||
|
if config.title_placement == "before" then
|
||||||
|
if before then
|
||||||
|
before = added_name .. separator .. before
|
||||||
|
else
|
||||||
|
before = added_name
|
||||||
|
end
|
||||||
|
elseif config.title_placement == "replace" then
|
||||||
|
if replace then
|
||||||
|
replace = replace .. separator .. added_name
|
||||||
|
else
|
||||||
|
replace = added_name
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if after then
|
||||||
|
after = after .. separator .. added_name
|
||||||
|
else
|
||||||
|
after = added_name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local new_str = replace or str
|
||||||
|
if before then
|
||||||
|
new_str = before .. (string.len(new_str) > 0 and separator or "") .. new_str
|
||||||
|
end
|
||||||
|
if after then
|
||||||
|
new_str = new_str .. (string.len(new_str) > 0 and separator or "") .. after
|
||||||
|
end
|
||||||
|
return new_str
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--[[
|
||||||
|
MUTATOR'S OWN METHODS
|
||||||
|
]]--
|
||||||
|
|
||||||
|
-- Enables mutator
|
||||||
|
local function enable_mutator(self)
|
||||||
|
set_mutator_state(self, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Disables mutator
|
||||||
|
local function disable_mutator(self)
|
||||||
|
set_mutator_state(self, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Checks current difficulty, map selection screen settings (optionally), incompatible mutators and whether player is server
|
||||||
|
-- to determine if a mutator can be enabled
|
||||||
|
local function can_be_enabled(self, ignore_map)
|
||||||
|
|
||||||
|
if #self:get_incompatible_mutators(true) > 0 then return false end
|
||||||
|
return player_is_server() and self:supports_current_difficulty(ignore_map)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Only checks difficulty
|
||||||
|
local function supports_current_difficulty(self, ignore_map)
|
||||||
|
local mutator_difficulty_levels = self:get_config().difficulty_levels
|
||||||
|
local actual_difficulty = Managers.state and Managers.state.difficulty:get_difficulty()
|
||||||
|
local right_difficulty = not actual_difficulty or table.has_item(mutator_difficulty_levels, actual_difficulty)
|
||||||
|
|
||||||
|
local map_view = not ignore_map and mutators_view.map_view
|
||||||
|
local map_view_active = map_view and map_view.active
|
||||||
|
local right_unapplied_difficulty = false
|
||||||
|
|
||||||
|
if map_view_active then
|
||||||
|
|
||||||
|
local difficulty_data = map_view.selected_level_index and map_view:get_difficulty_data(map_view.selected_level_index)
|
||||||
|
local difficulty_layout = difficulty_data and difficulty_data[map_view.selected_difficulty_stepper_index]
|
||||||
|
local difficulty_key = difficulty_layout and difficulty_layout.key
|
||||||
|
right_unapplied_difficulty = difficulty_key and table.has_item(mutator_difficulty_levels, difficulty_key)
|
||||||
|
end
|
||||||
|
|
||||||
|
return (map_view_active and right_unapplied_difficulty) or (not map_view_active and right_difficulty)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Returns the config object for mutator from mutators_config
|
||||||
|
local function get_config(self)
|
||||||
|
return mutators_config[self:get_name()]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Returns a list of incompatible with self mutators, all or only enabled ones
|
||||||
|
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
|
||||||
|
VMFMod.register_as_mutator = function(self, config)
|
||||||
|
if not config then config = {} end
|
||||||
|
|
||||||
|
local mod_name = self:get_name()
|
||||||
|
|
||||||
|
if table.has_item(mutators, self) then
|
||||||
|
self:error("Mod is already registered as mutator")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(mutators, self)
|
||||||
|
|
||||||
|
-- Save config
|
||||||
|
mutators_config[mod_name] = table.clone(default_config)
|
||||||
|
local _config = mutators_config[mod_name]
|
||||||
|
for k, _ in pairs(_config) do
|
||||||
|
if config[k] ~= nil then
|
||||||
|
_config[k] = config[k]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if _config.short_title == "" then _config.short_title = nil end
|
||||||
|
if _config.title == "" then _config.title = nil end
|
||||||
|
|
||||||
|
if config.enable_before_these then
|
||||||
|
update_mutators_sequence(mod_name, config.enable_before_these)
|
||||||
|
end
|
||||||
|
|
||||||
|
if config.enable_after_these then
|
||||||
|
for _, other_mod_name in ipairs(config.enable_after_these) do
|
||||||
|
update_mutators_sequence(other_mod_name, {mod_name})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.enable = enable_mutator
|
||||||
|
self.disable = disable_mutator
|
||||||
|
self.can_be_enabled = can_be_enabled
|
||||||
|
self.supports_current_difficulty = supports_current_difficulty
|
||||||
|
|
||||||
|
self.get_config = get_config
|
||||||
|
self.get_incompatible_mutators = get_incompatible_mutators
|
||||||
|
|
||||||
|
mutators_sorted = false
|
||||||
|
|
||||||
|
-- Always init in the off state
|
||||||
|
self:init_state(false)
|
||||||
|
|
||||||
|
mutators_view:update_mutator_list()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--[[
|
||||||
|
HOOKS
|
||||||
|
]]--
|
||||||
|
manager:hook("DifficultyManager.set_difficulty", function(func, self, difficulty)
|
||||||
|
manager.disable_impossible_mutators(true, true, "DUE TO CHANGE IN DIFFICULTY")
|
||||||
|
return func(self, difficulty)
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
--[[
|
||||||
|
INITIALIZE
|
||||||
|
--]]
|
||||||
|
|
||||||
|
-- Initialize mutators view when map_view has been initialized already
|
||||||
|
mutators_view:init(mutators_view:get_map_view())
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Testing
|
||||||
|
--]]
|
||||||
|
-- manager:dofile("scripts/mods/vmf/modules/mutators/mutator_test")
|
||||||
|
-- manager:dofile("scripts/mods/vmf/modules/mutators/mutators/mutation")
|
||||||
|
-- manager:dofile("scripts/mods/vmf/modules/mutators/mutators/deathwish")
|
|
@ -0,0 +1,79 @@
|
||||||
|
local mutator555 = new_mod("mutator555")
|
||||||
|
|
||||||
|
mutator555:register_as_mutator({
|
||||||
|
incompatible_with_all = true,
|
||||||
|
title = "Legendary"
|
||||||
|
})
|
||||||
|
mutator555.on_enabled = function() end
|
||||||
|
mutator555.on_disabled = function() end
|
||||||
|
|
||||||
|
|
||||||
|
local mutator3 = new_mod("mutator3")
|
||||||
|
mutator3:register_as_mutator({
|
||||||
|
incompatible_with = {
|
||||||
|
"mutator4"
|
||||||
|
},
|
||||||
|
title = "Stormvermin Mutation"
|
||||||
|
})
|
||||||
|
mutator3.on_enabled = function() end
|
||||||
|
mutator3.on_disabled = function() end
|
||||||
|
|
||||||
|
local mutator2 = new_mod("mutator2")
|
||||||
|
mutator2:register_as_mutator({
|
||||||
|
difficulty_levels = {
|
||||||
|
"hardest",
|
||||||
|
"survival_hardest"
|
||||||
|
},
|
||||||
|
title = "Deathwish"
|
||||||
|
})
|
||||||
|
mutator2.on_enabled = function() end
|
||||||
|
mutator2.on_disabled = function() end
|
||||||
|
|
||||||
|
local slayer = new_mod("slayer")
|
||||||
|
slayer:register_as_mutator({
|
||||||
|
difficulty_levels = {
|
||||||
|
"survival_hard",
|
||||||
|
"survival_harder",
|
||||||
|
"survival_hardest"
|
||||||
|
},
|
||||||
|
title = "Slayer's Oath"
|
||||||
|
})
|
||||||
|
slayer.on_enabled = function() end
|
||||||
|
slayer.on_disabled = function() end
|
||||||
|
|
||||||
|
local true_solo = new_mod("true_solo")
|
||||||
|
true_solo:register_as_mutator({
|
||||||
|
compatible_with_all = true,
|
||||||
|
title = "True Solo"
|
||||||
|
})
|
||||||
|
true_solo.on_enabled = function() end
|
||||||
|
true_solo.on_disabled = function() end
|
||||||
|
|
||||||
|
local onslaught = new_mod("onslaught")
|
||||||
|
onslaught:register_as_mutator({
|
||||||
|
title = "Onslaught"
|
||||||
|
})
|
||||||
|
onslaught.on_enabled = function() end
|
||||||
|
onslaught.on_disabled = function() end
|
||||||
|
|
||||||
|
local one_hit_one_kill = new_mod("one_hit_one_kill")
|
||||||
|
one_hit_one_kill:register_as_mutator({
|
||||||
|
title = "One Hit One Kill"
|
||||||
|
})
|
||||||
|
one_hit_one_kill.on_enabled = function() end
|
||||||
|
one_hit_one_kill.on_disabled = function() end
|
||||||
|
|
||||||
|
local more_rat_weapons = new_mod("more_rat_weapons")
|
||||||
|
more_rat_weapons:register_as_mutator({
|
||||||
|
compatible_with_all = true,
|
||||||
|
title = "More Rat Weapons"
|
||||||
|
})
|
||||||
|
more_rat_weapons.on_enabled = function() end
|
||||||
|
more_rat_weapons.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--]]
|
|
@ -19,6 +19,10 @@ inject_material("materials/vmf/header_fav_icon_lit", "header_fav_icon_lit", "ing
|
||||||
inject_material("materials/vmf/header_fav_arrow", "header_fav_arrow", "ingame_ui")
|
inject_material("materials/vmf/header_fav_arrow", "header_fav_arrow", "ingame_ui")
|
||||||
inject_material("materials/vmf/search_bar_icon", "search_bar_icon", "ingame_ui")
|
inject_material("materials/vmf/search_bar_icon", "search_bar_icon", "ingame_ui")
|
||||||
|
|
||||||
|
-- TODO: move to mutator_gui
|
||||||
|
inject_material("materials/vmf/mutator_button", "mutator_button", "ingame_ui")
|
||||||
|
inject_material("materials/vmf/mutator_button_hover", "mutator_button_hover", "ingame_ui")
|
||||||
|
|
||||||
|
|
||||||
-- ####################################################################################################################
|
-- ####################################################################################################################
|
||||||
-- ##### MENU WIDGETS DEFINITIONS #####################################################################################
|
-- ##### MENU WIDGETS DEFINITIONS #####################################################################################
|
||||||
|
@ -4215,7 +4219,11 @@ if ingame_ui_exists then
|
||||||
"material",
|
"material",
|
||||||
"materials/vmf/header_fav_arrow",
|
"materials/vmf/header_fav_arrow",
|
||||||
"material",
|
"material",
|
||||||
"materials/vmf/search_bar_icon")
|
"materials/vmf/search_bar_icon",
|
||||||
|
"material",
|
||||||
|
"materials/vmf/mutator_button",
|
||||||
|
"material",
|
||||||
|
"materials/vmf/mutator_button_hover")
|
||||||
|
|
||||||
local gui_retained = World.create_screen_gui(ingame_ui.ui_renderer.world,
|
local gui_retained = World.create_screen_gui(ingame_ui.ui_renderer.world,
|
||||||
"material",
|
"material",
|
||||||
|
@ -4237,7 +4245,11 @@ if ingame_ui_exists then
|
||||||
"material",
|
"material",
|
||||||
"materials/vmf/header_fav_arrow",
|
"materials/vmf/header_fav_arrow",
|
||||||
"material",
|
"material",
|
||||||
"materials/vmf/search_bar_icon")
|
"materials/vmf/search_bar_icon",
|
||||||
|
"material",
|
||||||
|
"materials/vmf/mutator_button",
|
||||||
|
"material",
|
||||||
|
"materials/vmf/mutator_button_hover")
|
||||||
|
|
||||||
World.destroy_gui(ingame_ui.ui_renderer.world, ingame_ui.ui_renderer.gui)
|
World.destroy_gui(ingame_ui.ui_renderer.world, ingame_ui.ui_renderer.gui)
|
||||||
World.destroy_gui(ingame_ui.ui_renderer.world, ingame_ui.ui_renderer.gui_retained)
|
World.destroy_gui(ingame_ui.ui_renderer.world, ingame_ui.ui_renderer.gui_retained)
|
||||||
|
@ -4265,7 +4277,11 @@ if ingame_ui_exists then
|
||||||
"material",
|
"material",
|
||||||
"materials/vmf/header_fav_arrow",
|
"materials/vmf/header_fav_arrow",
|
||||||
"material",
|
"material",
|
||||||
"materials/vmf/search_bar_icon")
|
"materials/vmf/search_bar_icon",
|
||||||
|
"material",
|
||||||
|
"materials/vmf/mutator_button",
|
||||||
|
"material",
|
||||||
|
"materials/vmf/mutator_button_hover")
|
||||||
|
|
||||||
gui_retained = World.create_screen_gui(ingame_ui.ui_top_renderer.world,
|
gui_retained = World.create_screen_gui(ingame_ui.ui_top_renderer.world,
|
||||||
"material",
|
"material",
|
||||||
|
@ -4287,7 +4303,11 @@ if ingame_ui_exists then
|
||||||
"material",
|
"material",
|
||||||
"materials/vmf/header_fav_arrow",
|
"materials/vmf/header_fav_arrow",
|
||||||
"material",
|
"material",
|
||||||
"materials/vmf/search_bar_icon")
|
"materials/vmf/search_bar_icon",
|
||||||
|
"material",
|
||||||
|
"materials/vmf/mutator_button",
|
||||||
|
"material",
|
||||||
|
"materials/vmf/mutator_button_hover")
|
||||||
|
|
||||||
World.destroy_gui(ingame_ui.ui_top_renderer.world, ingame_ui.ui_top_renderer.gui)
|
World.destroy_gui(ingame_ui.ui_top_renderer.world, ingame_ui.ui_top_renderer.gui)
|
||||||
World.destroy_gui(ingame_ui.ui_top_renderer.world, ingame_ui.ui_top_renderer.gui_retained)
|
World.destroy_gui(ingame_ui.ui_top_renderer.world, ingame_ui.ui_top_renderer.gui_retained)
|
||||||
|
|
|
@ -196,4 +196,4 @@ local vmf_developer_mode = vmf:get("developer_mode")
|
||||||
if mod_developer_mode ~= vmf_developer_mode then
|
if mod_developer_mode ~= vmf_developer_mode then
|
||||||
Managers.mod._settings.developer_mode = vmf_developer_mode
|
Managers.mod._settings.developer_mode = vmf_developer_mode
|
||||||
Application.set_user_setting("mod_settings", Managers.mod._settings)
|
Application.set_user_setting("mod_settings", Managers.mod._settings)
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,6 +21,8 @@ return {
|
||||||
dofile("scripts/mods/vmf/modules/options_menu/vmf_options_view")
|
dofile("scripts/mods/vmf/modules/options_menu/vmf_options_view")
|
||||||
dofile("scripts/mods/vmf/modules/vmf_options")
|
dofile("scripts/mods/vmf/modules/vmf_options")
|
||||||
|
|
||||||
|
dofile("scripts/mods/vmf/modules/mutators/mutator_manager")
|
||||||
|
|
||||||
object.vmf = get_mod("VMF")
|
object.vmf = get_mod("VMF")
|
||||||
|
|
||||||
-- temporary solution:
|
-- temporary solution:
|
||||||
|
|
Loading…
Add table
Reference in a new issue