Merge pull request #2 from Vermintide-Modders/mutators

Mutators
This commit is contained in:
UnShame 2018-02-23 18:11:55 +03:00 committed by GitHub
commit f4941d8bb1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 191 additions and 142 deletions

View file

@ -22,5 +22,57 @@ return {
}, },
survival_hardest = { survival_hardest = {
en = "Heroic" en = "Heroic"
},
broadcast_enabled_mutators = {
en = "ENABLED MUTATORS"
},
broadcast_all_disabled = {
en = "ALL MUTATORS DISABLED"
},
broadcast_disabled_mutators = {
en = "MUTATORS DISABLED"
},
local_disabled_mutators = {
en = "Mutators disabled"
},
whisper_enabled_mutators = {
en = "[Automated message] This lobby has the following mutators active"
},
disabled_reason_not_server = {
en = "because you're no longer the host"
},
disabled_reason_difficulty_change = {
en = "DUE TO CHANGE IN DIFFICULTY"
},
mutators_title = {
en = "Mutators"
},
mutators_banner_tooltip = {
en = "Enable and disable mutators"
},
no_mutators = {
en = "No mutators installed"
},
no_mutators_tooltip = {
en = "Subscribe to mods and mutators on the workshop"
},
tooltip_supported_difficulty = {
en = "Supported difficulty levels"
},
tooltip_incompatible_with_all = {
en = "Incompatible with all other mutators"
},
tooltip_incompatible_with = {
en = "Incompatible with"
},
tooltip_compatible_with_all = {
en = "Compatible with all other mutators"
},
tooltip_will_be_disabled = {
en = "Will be disabled when Play is pressed"
} }
} }

View file

@ -31,7 +31,8 @@ local mutators_view = {
-- Setup custom widgets -- Setup custom widgets
self.widgets = { self.widgets = {
banner_mutators = UIWidget.init(definitions.new_widgets.banner_mutators_widget), banner_mutators = UIWidget.init(definitions.new_widgets.banner_mutators_widget),
mutators_button = UIWidget.init(definitions.new_widgets.mutators_button_widget) mutators_button = UIWidget.init(definitions.new_widgets.mutators_button_widget),
no_mutators_text = UIWidget.init(definitions.new_widgets.no_mutators_text_widget)
} }
for i = 1, PER_PAGE do for i = 1, PER_PAGE do
@ -63,7 +64,7 @@ local mutators_view = {
self:setup_hooks() self:setup_hooks()
self.initialized = true self.initialized = true
--print("INIT") print("[MUTATORS] GUI initialized")
end, end,
deinitialize = function(self) deinitialize = function(self)
@ -92,7 +93,7 @@ local mutators_view = {
self.map_view = nil self.map_view = nil
self.initialized = false self.initialized = false
--print("DEINIT") print("[MUTATORS] GUI deinitialized")
end, end,
-- Sorts mutators by title -- Sorts mutators by title
@ -199,7 +200,7 @@ local mutators_view = {
-- Click event -- Click event
if hotspot.on_release then if hotspot.on_release then
self.map_view:play_sound("Play_hud_hover") self.map_view:play_sound("Play_hud_select")
if mutator:is_enabled() then if mutator:is_enabled() then
mutator:disable() mutator:disable()
else else
@ -211,30 +212,13 @@ local mutators_view = {
end end
end end
local checkbox = self.mutator_checkboxes[1]
if #mutators == 0 then if #mutators == 0 then
widgets.adventure["mutator_checkbox_" .. 1] = checkbox widgets.adventure["no_mutators_text"] = self.widgets.no_mutators_text
widgets.survival["mutator_checkbox_" .. 1] = checkbox widgets.survival["no_mutators_text"] = self.widgets.no_mutators_text
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 else
checkbox.style.checkbox_style.offset[1] = 0 widgets.adventure["no_mutators_text"] = nil
checkbox.style.setting_text.horizontal_alignment = "left" widgets.survival["no_mutators_text"] = nil
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
end, end,
@ -242,7 +226,7 @@ local mutators_view = {
activate = function(self) activate = function(self)
if not self.initialized or not self.map_view.active or self.active then return end if not self.initialized or not self.map_view.active or self.active then return end
-- Hiding widgets -- Widgets
local widgets = self.map_view.normal_settings_widget_types local widgets = self.map_view.normal_settings_widget_types
widgets.adventure.level_preview = nil widgets.adventure.level_preview = nil
@ -257,16 +241,16 @@ local mutators_view = {
self.map_view.ui_scenegraph.banner_level_text.position[2] = -10000 self.map_view.ui_scenegraph.banner_level_text.position[2] = -10000
-- Update steppers -- 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 level_stepper_widget = self.map_view.steppers.level.widget
local num_pages = math.ceil(#mutators/PER_PAGE) local num_pages = math.ceil(#mutators/PER_PAGE)
level_stepper_widget.style.setting_text.offset[2] = -10000
level_stepper_widget.style.hover_texture.offset[2] = -10000
level_stepper_widget.content.left_button_hotspot.disable_button = num_pages <= 1 level_stepper_widget.content.left_button_hotspot.disable_button = num_pages <= 1
level_stepper_widget.content.right_button_hotspot.disable_button = num_pages <= 1 level_stepper_widget.content.right_button_hotspot.disable_button = num_pages <= 1
self.active = true self.active = true
--print("ACTIVE!") print("[MUTATORS] GUI activated")
end, end,
-- Deactivate on button click or map close -- Deactivate on button click or map close
@ -275,7 +259,7 @@ local mutators_view = {
self.active = false self.active = false
-- Showing widgets -- Widgets
local widgets = self.map_view.normal_settings_widget_types local widgets = self.map_view.normal_settings_widget_types
widgets.adventure.level_preview = self.saved_widgets.level_preview widgets.adventure.level_preview = self.saved_widgets.level_preview
@ -286,6 +270,9 @@ local mutators_view = {
widgets.survival.level_preview_text = self.saved_widgets.level_preview widgets.survival.level_preview_text = self.saved_widgets.level_preview
widgets.survival.banner_mutators = nil widgets.survival.banner_mutators = nil
widgets.adventure["no_mutators_text"] = nil
widgets.survival["no_mutators_text"] = nil
-- "Mission" banner position -- "Mission" banner position
self.map_view.ui_scenegraph.banner_level_text.position[2] = 0 self.map_view.ui_scenegraph.banner_level_text.position[2] = 0
@ -300,7 +287,7 @@ local mutators_view = {
widgets.survival["mutator_checkbox_" .. i] = nil widgets.survival["mutator_checkbox_" .. i] = nil
end end
--print("DEACTIVE") print("[MUTATORS] GUI deactivated")
end, end,
-- Changes which muttators are displayed -- Changes which muttators are displayed
@ -332,7 +319,7 @@ local mutators_view = {
-- Show supported difficulty when can't be enabled due to difficulty level -- Show supported difficulty when can't be enabled due to difficulty level
local supports_difficulty = mutator:supports_current_difficulty() local supports_difficulty = mutator:supports_current_difficulty()
if not supports_difficulty then if not supports_difficulty then
text = text .. "\nSupported difficulty levels:" text = text .. "\n" .. manager:localize("tooltip_supported_difficulty") .. ":"
for i, difficulty in ipairs(config.difficulty_levels) do for i, difficulty in ipairs(config.difficulty_levels) do
text = text .. (i == 1 and " " or ", ") .. manager:localize(difficulty) text = text .. (i == 1 and " " or ", ") .. manager:localize(difficulty)
end end
@ -351,9 +338,9 @@ local mutators_view = {
if currently_compatible and config.incompatible_with_all or #incompatible_mutators == #mutators - 1 then if currently_compatible and config.incompatible_with_all or #incompatible_mutators == #mutators - 1 then
-- Show special message when incompatible with all -- Show special message when incompatible with all
text = text .. "\nIncompatible with all other mutators" text = text .. "\n" .. manager:localize("tooltip_incompatible_with_all")
else else
text = text .. "\nIncompatible with:" text = text .. "\n" .. manager:localize("tooltip_incompatible_with") .. ":"
for i, other_mutator in ipairs(incompatible_mutators) do for i, other_mutator in ipairs(incompatible_mutators) do
local name = (other_mutator:get_config().title or other_mutator:get_name()) local name = (other_mutator:get_config().title or other_mutator:get_name())
text = text .. (i == 1 and " " or ", ") .. name text = text .. (i == 1 and " " or ", ") .. name
@ -362,12 +349,12 @@ local mutators_view = {
elseif config.compatible_with_all then elseif config.compatible_with_all then
-- Special message when compatible with all -- Special message when compatible with all
text = text .. "\nCompatible with all other mutators" text = text .. "\n" .. manager:localize("tooltip_compatible_with_all")
end end
-- Special message if switched to unsupported difficulty level -- Special message if switched to unsupported difficulty level
if mutator:is_enabled() and not supports_difficulty then if mutator:is_enabled() and not supports_difficulty then
text = text .. "\nWill be disabled when Play is pressed" text = text .. "\n" .. manager:localize("tooltip_will_be_disabled")
end end
-- Description -- Description

View file

@ -1,4 +1,4 @@
local manager = get_mod("vmf_mutator_manager")
local definitions = local_require("scripts/ui/views/map_view_definitions") local definitions = local_require("scripts/ui/views/map_view_definitions")
local scenegraph_definition = definitions.scenegraph_definition local scenegraph_definition = definitions.scenegraph_definition
@ -37,11 +37,31 @@ scenegraph_definition.banner_mutators_text = {
} }
} }
scenegraph_definition.no_mutators_text = {
vertical_alignment = "center",
parent = "banner_party",
horizontal_alignment = "center",
size = {
310,
30
},
position = {
0,
520,
1
}
}
local new_widgets = { local new_widgets = {
-- This will replace the banner behind the Mission text -- 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", { banner_mutators_widget = UIWidgets.create_texture_with_text_and_tooltip(
"title_bar",
manager:localize("mutators_title"),
manager:localize("mutators_banner_tooltip"),
"banner_level",
"banner_mutators_text", {
vertical_alignment = "center", vertical_alignment = "center",
scenegraph_id = "banner_mutators_text", scenegraph_id = "banner_mutators_text",
localize = false, localize = false,
@ -78,8 +98,8 @@ local new_widgets = {
normal_texture = "octagon_button_normal", normal_texture = "octagon_button_normal",
icon_texture = "mutator_button", icon_texture = "mutator_button",
icon_hover_texture = "mutator_button_hover", icon_hover_texture = "mutator_button_hover",
tooltip_text = "Mutators", tooltip_text = manager:localize("mutators_title"),
toggled_tooltip_text = "Mutators", toggled_tooltip_text = manager:localize("mutators_title"),
button_hotspot = {} button_hotspot = {}
}, },
style = { style = {
@ -179,6 +199,68 @@ local new_widgets = {
} }
}, },
scenegraph_id = "mutators_button" scenegraph_id = "mutators_button"
},
no_mutators_text_widget = {
element = {
passes = {
{
style_id = "text",
pass_type = "text",
text_id = "text"
},
{
pass_type = "hotspot",
content_id = "tooltip_hotspot"
},
{
style_id = "tooltip_text",
pass_type = "tooltip_text",
text_id = "tooltip_text",
content_check_function = function (ui_content)
return ui_content.tooltip_hotspot.is_hover
end
}
}
},
content = {
text = manager:localize("no_mutators"),
tooltip_text = manager:localize("no_mutators_tooltip"),
tooltip_hotspot = {},
color = Colors.get_color_table_with_alpha("slate_gray", 255)
},
style = {
text = {
vertical_alignment = "center",
font_size = 22,
localize = false,
horizontal_alignment = "center",
word_wrap = true,
font_type = "hell_shark",
text_color = Colors.get_color_table_with_alpha("slate_gray", 255),
offset = {
0,
2,
4
}
},
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,
50
}
}
},
scenegraph_id = "no_mutators_text"
} }
} }

View file

@ -41,7 +41,6 @@ local function set_lobby_data()
Managers.matchmaking.lobby:set_lobby_data(lobby_data) Managers.matchmaking.lobby:set_lobby_data(lobby_data)
end end
-- Return a function for chat system to only send messages to specific client
local function get_peer_id_from_cookie(client_cookie) local function get_peer_id_from_cookie(client_cookie)
local peer_id = tostring(client_cookie) local peer_id = tostring(client_cookie)
for _ = 1, 3 do for _ = 1, 3 do
@ -74,10 +73,10 @@ manager:hook("MatchmakingStateHostGame.host_game", function(func, self, ...)
set_lobby_data() set_lobby_data()
local names = add_enabled_mutators_titles_to_string("", ", ") local names = add_enabled_mutators_titles_to_string("", ", ")
if string.len(names) > 0 then if string.len(names) > 0 then
manager:chat_broadcast("ENABLED MUTATORS: " .. names) manager:chat_broadcast(manager:localize("broadcast_enabled_mutators") .. ": " .. names)
were_enabled_before = true were_enabled_before = true
elseif were_enabled_before then elseif were_enabled_before then
manager:chat_broadcast("ALL MUTATORS DISABLED") manager:chat_broadcast(manager:localize("broadcast_all_disabled"))
were_enabled_before = false were_enabled_before = false
end end
end) end)
@ -86,7 +85,7 @@ end)
manager:hook("MatchmakingManager.rpc_matchmaking_request_join_lobby", function(func, self, sender, client_cookie, host_cookie, lobby_id, friend_join) 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("", ", ") local name = add_enabled_mutators_titles_to_string("", ", ")
if string.len(name) > 0 then if string.len(name) > 0 then
local message = "[Automated message] This lobby has the following difficulty mod active : " .. name local message = manager:localize("whisper_enabled_mutators") .. ": " .. name
manager:chat_whisper(get_peer_id_from_cookie(client_cookie), message) manager:chat_whisper(get_peer_id_from_cookie(client_cookie), message)
end end
func(self, sender, client_cookie, host_cookie, lobby_id, friend_join) func(self, sender, client_cookie, host_cookie, lobby_id, friend_join)

View file

@ -9,6 +9,7 @@ manager:localization("localization/mutator_manager")
manager.mutators = {} manager.mutators = {}
local mutators = manager.mutators local mutators = manager.mutators
-- Table of mutators' configs by name
local mutators_config = {} local mutators_config = {}
local default_config = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_default_config") local default_config = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_default_config")
@ -26,6 +27,9 @@ local mutators_sequence = {
-- So we don't sort after each one is added -- So we don't sort after each one is added
local mutators_sorted = false local mutators_sorted = false
-- So we don't have to check when player isn't hosting
local all_mutators_disabled = false
--[[ --[[
PRIVATE METHODS PRIVATE METHODS
@ -98,6 +102,7 @@ local function on_enabled(mutator)
local config = mutator:get_config() local config = mutator:get_config()
dice_manager.addDice(config.dice) dice_manager.addDice(config.dice)
set_lobby_data() set_lobby_data()
print("[MUTATORS] Enabled " .. mutator:get_name() .. " (" .. tostring(table.index_of(mutators, mutator)) .. ")")
end end
-- Called after mutator is disabled -- Called after mutator is disabled
@ -105,6 +110,7 @@ local function on_disabled(mutator)
local config = mutator:get_config() local config = mutator:get_config()
dice_manager.removeDice(config.dice) dice_manager.removeDice(config.dice)
set_lobby_data() set_lobby_data()
print("[MUTATORS] Disabled " .. mutator:get_name() .. " (" .. tostring(table.index_of(mutators, mutator)) .. ")")
end end
-- Enables/disables mutator while preserving the sequence in which they were enabled -- Enables/disables mutator while preserving the sequence in which they were enabled
@ -120,7 +126,10 @@ local function set_mutator_state(mutator, state)
return return
end end
if state and not mutator:can_be_enabled() then if state and (
not mutator:can_be_enabled() or
Managers.state and Managers.state.game_mode and Managers.state.game_mode._game_mode_key ~= "inn"
) then
return return
end end
@ -148,11 +157,9 @@ local function set_mutator_state(mutator, state)
-- Enable/disable current mutator -- Enable/disable current mutator
-- We're calling methods on the class object because we've overwritten them on the current one -- We're calling methods on the class object because we've overwritten them on the current one
if state then if state then
--print("Enabled ", mutator:get_name(), "!")
VMFMod.enable(mutator) VMFMod.enable(mutator)
on_enabled(mutator) on_enabled(mutator)
else else
--print("Disabled ", mutator:get_name(), "!")
VMFMod.disable(mutator) VMFMod.disable(mutator)
on_disabled(mutator) on_disabled(mutator)
end end
@ -231,20 +238,19 @@ manager.sort_mutators = function()
end end
mutators_sorted = true mutators_sorted = true
--[[
-- LOG -- -- LOG --
print("[MUTATORS] Sorted")
for k, v in ipairs(mutators) do for k, v in ipairs(mutators) do
print(k, v:get_name()) print(" ", k, v:get_name())
end end
print("-----------")
-- /LOG -- -- /LOG --
--]]
end end
-- Disables mutators that cannot be enabled right now -- Disables mutators that cannot be enabled right now
manager.disable_impossible_mutators = function(notify, everybody, reason) manager.disable_impossible_mutators = function(notify, everybody, reason)
local disabled_mutators = {} local disabled_mutators = {}
for _, mutator in pairs(mutators) do for i = #mutators, 1, -1 do
local mutator = mutators[i]
if mutator:is_enabled() and not mutator:can_be_enabled() then if mutator:is_enabled() and not mutator:can_be_enabled() then
mutator:disable() mutator:disable()
table.insert(disabled_mutators, mutator) table.insert(disabled_mutators, mutator)
@ -252,7 +258,8 @@ manager.disable_impossible_mutators = function(notify, everybody, reason)
end end
if #disabled_mutators > 0 and notify then if #disabled_mutators > 0 and notify then
if not reason then reason = "" end if not reason then reason = "" end
local message = everybody and "MUTATORS DISABLED " .. reason .. ":" or "Mutators disabled " .. reason .. ":" local loc = everybody and "broadcast_disabled_mutators" or "local_disabled_mutators"
local message = manager:localize(loc) .. " " .. manager:localize(reason) .. ":"
message = message .. " " .. manager.add_mutator_titles_to_string(disabled_mutators, "", ", ", false) message = message .. " " .. manager.add_mutator_titles_to_string(disabled_mutators, "", ", ", false)
if everybody then if everybody then
manager:chat_broadcast(message) manager:chat_broadcast(message)
@ -263,13 +270,6 @@ manager.disable_impossible_mutators = function(notify, everybody, reason)
return disabled_mutators return disabled_mutators
end 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 -- Appends, prepends and replaces the string with mutator titles
manager.add_mutator_titles_to_string = function(_mutators, str, separator, short) manager.add_mutator_titles_to_string = function(_mutators, str, separator, short)
@ -312,6 +312,14 @@ manager.add_mutator_titles_to_string = function(_mutators, str, separator, short
return new_str return new_str
end end
-- Check if player is still hosting
manager.update = function()
if not all_mutators_disabled and not player_is_server() then
manager.disable_impossible_mutators(true, false, "disabled_reason_not_server")
all_mutators_disabled = true
end
end
--[[ --[[
MUTATOR'S OWN METHODS MUTATOR'S OWN METHODS
@ -320,6 +328,7 @@ end
-- Enables mutator -- Enables mutator
local function enable_mutator(self) local function enable_mutator(self)
set_mutator_state(self, true) set_mutator_state(self, true)
all_mutators_disabled = false
end end
-- Disables mutator -- Disables mutator
@ -327,10 +336,9 @@ local function disable_mutator(self)
set_mutator_state(self, false) set_mutator_state(self, false)
end end
-- Checks current difficulty, map selection screen settings (optionally), incompatible mutators and whether player is server -- Checks current difficulty, map selection screen settings (optionally), incompatible mutators and whether player is server
-- to determine if a mutator can be enabled -- to determine if a mutator can be enabled
local function can_be_enabled(self, ignore_map) local function can_be_enabled(self, ignore_map)
if #self:get_incompatible_mutators(true) > 0 then return false end if #self:get_incompatible_mutators(true) > 0 then return false end
return player_is_server() and self:supports_current_difficulty(ignore_map) return player_is_server() and self:supports_current_difficulty(ignore_map)
end end
@ -431,7 +439,7 @@ end
HOOKS HOOKS
]]-- ]]--
manager:hook("DifficultyManager.set_difficulty", function(func, self, difficulty) manager:hook("DifficultyManager.set_difficulty", function(func, self, difficulty)
manager.disable_impossible_mutators(true, true, "DUE TO CHANGE IN DIFFICULTY") manager.disable_impossible_mutators(true, true, "disabled_reason_difficulty_change")
return func(self, difficulty) return func(self, difficulty)
end) end)

View file

@ -1,79 +0,0 @@
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--]]