Merge pull request #1 from Vermintide-Modders/mutators

Mutators
This commit is contained in:
Azumgi 2018-02-22 20:15:27 +03:00 committed by GitHub
commit 7e713f677a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 1761 additions and 13 deletions

Binary file not shown.

View 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
}
}

Binary file not shown.

View 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
}
}

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

@ -0,0 +1,14 @@
mutator_button = {
material_contexts = {
surface_material = ""
}
shader = "gui_gradient:DIFFUSE_MAP"
textures = {
diffuse_map = "gui/vmf/mutator_button"
}
variables = {
}
}

View 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 = {
}
}

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/*"
@ -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/*"
] ]

View file

@ -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

View file

@ -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
-- #################################################################################################################### -- ####################################################################################################################

View 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.

View file

@ -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 = {}
}

View file

@ -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
}

View 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

View file

@ -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

View file

@ -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

View 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")

View file

@ -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--]]

View file

@ -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)

View file

@ -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

View file

@ -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: