From 388574ba1ef225afabfb8e6de3f5f88bdb224187 Mon Sep 17 00:00:00 2001 From: UnShame Date: Mon, 19 Feb 2018 21:07:01 +0300 Subject: [PATCH 01/32] mutators: enable/disable sequence --- .../mods/vmf/modules/core/toggling.lua | 10 +- .../scripts/mods/vmf/modules/mutators.lua | 181 ++++++++++++++++++ vmf_source/scripts/mods/vmf/vmf_loader.lua | 2 + 3 files changed, 187 insertions(+), 6 deletions(-) create mode 100644 vmf_source/scripts/mods/vmf/modules/mutators.lua diff --git a/vmf_source/scripts/mods/vmf/modules/core/toggling.lua b/vmf_source/scripts/mods/vmf/modules/core/toggling.lua index ae40f99..2ab124c 100644 --- a/vmf_source/scripts/mods/vmf/modules/core/toggling.lua +++ b/vmf_source/scripts/mods/vmf/modules/core/toggling.lua @@ -52,13 +52,11 @@ VMFMod.enable = function (self) end end -VMFMod.init_state = function (self) - - if _DISABLED_MODS_LIST[self:get_name()] then - change_mod_state(self, false, true) - else - change_mod_state(self, true, true) +VMFMod.init_state = function (self, state) + if type(state) ~= "boolean" then + state = not _DISABLED_MODS_LIST[self:get_name()] end + change_mod_state(self, state, true) end -- #################################################################################################################### diff --git a/vmf_source/scripts/mods/vmf/modules/mutators.lua b/vmf_source/scripts/mods/vmf/modules/mutators.lua new file mode 100644 index 0000000..dcf0083 --- /dev/null +++ b/vmf_source/scripts/mods/vmf/modules/mutators.lua @@ -0,0 +1,181 @@ +local vmf = get_mod("VMF") + +local mutators = {} +local mutators_sequence = {} +local mutators_sorted = false + +local function update_mutators_sequence(mod_name, load_these_after) + if not mutators_sequence[mod_name] then + mutators_sequence[mod_name] = {} + end + for _, mutator_name in ipairs(load_these_after) do + if not table.has_item(mutators_sequence[mod_name], mutator_name) then + table.insert(mutators_sequence[mod_name], mutator_name) + end + end + table.combine(mutators_sequence[mod_name], load_these_after) +end + +local function sort_mutators() + + vmf:dump(mutators_sequence, "seq", 5) + for i, v in ipairs(mutators) do + print(i, v:get_name()) + end + print("-----------") + + local i = 2 + while i <= #mutators do + local mutator = mutators[i] + local mutator_name = mutator:get_name() + local load_these_after = mutators_sequence[mutator_name] or {} + + local j = i - 1 + while j > 0 do + local other_mutator = mutators[j] + if table.has_item(load_these_after, other_mutator:get_name()) then + table.remove(mutators, j) + table.insert(mutators, i, other_mutator) + i = i - 1 + end + j = j - 1 + end + + i = i + 1 + end + mutators_sorted = true + + for i, v in ipairs(mutators) do + print(i, v:get_name()) + end + print("-----------") +end + +local function set_mutator_state(self, state) + local i = table.index_of(mutators, self) + if i == nil then + self:error("Mutator isn't in the list") + return + end + + if not mutators_sorted then + sort_mutators() + end + + local disabled_mutators = {} + local load_these_after = mutators_sequence[self:get_name()] + + if load_these_after and #mutators > i then + for j = #mutators, i + 1, -1 do + if mutators[j]:is_enabled() and table.has_item(load_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 + + if state then + print("Enabled ", self:get_name(), "!") + VMFMod.enable(self) + else + print("Disabled ", self:get_name(), "!") + VMFMod.disable(self) + end + + 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 + print("---------") +end + +local function enable_mutator(self) + vmf:pcall(function() set_mutator_state(self, true) end) +end + +local function disable_mutator(self) + vmf:pcall(function() set_mutator_state(self, false) end) +end + +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) + + 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 + + mutators_sorted = false + + -- Always init in the off state + self:init_state(false) +end + +-- Testing +local mutator1 = new_mod("mutator1") +local mutator2 = new_mod("mutator2") +local mutator3 = new_mod("mutator3") +local mutator555 = new_mod("mutator555") +local mutator_whatever = new_mod("mutator_whatever") + +mutator555:register_as_mutator({ + enable_after_these = { + "mutator1" + } +}) +mutator555:create_options({}, true, "mutator555", "mutator555 description") +mutator555.on_enabled = function() end +mutator555.on_disabled = function() end + +mutator2:register_as_mutator({ + enable_before_these = { + "mutator555", + "mutator3" + } +}) +mutator2:create_options({}, true, "mutator2", "mutator2 description") +mutator2.on_enabled = function() end +mutator2.on_disabled = function() end + +mutator1:register_as_mutator({ + enable_before_these = { + "mutator2" + } +}) +mutator1:create_options({}, true, "mutator1", "mutator1 description") +mutator1.on_enabled = function() end +mutator1.on_disabled = function() end + +mutator3:register_as_mutator({ + enable_before_these = { + "mutator555" + } +}) +mutator3:create_options({}, true, "mutator3", "mutator3 description") +mutator3.on_enabled = function() end +mutator3.on_disabled = function() end + +mutator_whatever:register_as_mutator() +mutator_whatever:create_options({}, true, "mutator_whatever", "mutator_whatever description") +mutator_whatever.on_enabled = function() end +mutator_whatever.on_disabled = function() end diff --git a/vmf_source/scripts/mods/vmf/vmf_loader.lua b/vmf_source/scripts/mods/vmf/vmf_loader.lua index 8f46e0a..80e33c2 100644 --- a/vmf_source/scripts/mods/vmf/vmf_loader.lua +++ b/vmf_source/scripts/mods/vmf/vmf_loader.lua @@ -21,6 +21,8 @@ return { dofile("scripts/mods/vmf/modules/options_menu/vmf_options_view") dofile("scripts/mods/vmf/modules/vmf_options") + dofile("scripts/mods/vmf/modules/mutators") + object.vmf = get_mod("VMF") -- temporary solution: From 4d5a0e64e2986d6424db9c066c5025ae2802cd64 Mon Sep 17 00:00:00 2001 From: UnShame Date: Mon, 19 Feb 2018 21:45:55 +0300 Subject: [PATCH 02/32] mutators: fool proof 'enable_before_these'/'enable_after_these' --- .../scripts/mods/vmf/modules/mutators.lua | 81 ++++++++++++++----- 1 file changed, 60 insertions(+), 21 deletions(-) diff --git a/vmf_source/scripts/mods/vmf/modules/mutators.lua b/vmf_source/scripts/mods/vmf/modules/mutators.lua index dcf0083..8f2e719 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators.lua @@ -4,16 +4,24 @@ local mutators = {} local mutators_sequence = {} local mutators_sorted = false -local function update_mutators_sequence(mod_name, load_these_after) - if not mutators_sequence[mod_name] then - mutators_sequence[mod_name] = {} +local function update_mutators_sequence(mutator_name, load_these_after) + if not mutators_sequence[mutator_name] then + mutators_sequence[mutator_name] = {} end - for _, mutator_name in ipairs(load_these_after) do - if not table.has_item(mutators_sequence[mod_name], mutator_name) then - table.insert(mutators_sequence[mod_name], mutator_name) + for _, other_mutator_name in ipairs(load_these_after) do + + if mutators_sequence[other_mutator_name] and table.has_item(mutators_sequence[other_mutator_name], mutator_name) then + vmf: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[mod_name], load_these_after) + table.combine(mutators_sequence[mutator_name], load_these_after) +end + +local function error_endless_loop() + vmf:error("Mutators: too many iterations. Check for loops in 'enable_before_these'/'enable_after_these'.") end local function sort_mutators() @@ -24,6 +32,9 @@ local function sort_mutators() end print("-----------") + local maxIter = #mutators * #mutators * #mutators + local numIter = 0 + local i = 2 while i <= #mutators do local mutator = mutators[i] @@ -39,9 +50,15 @@ local function sort_mutators() i = i - 1 end j = j - 1 + + numIter = numIter + 1 + if numIter > maxIter then return error_endless_loop() end end i = i + 1 + + numIter = numIter + 1 + if numIter > maxIter then return error_endless_loop() end end mutators_sorted = true @@ -132,39 +149,61 @@ VMFMod.register_as_mutator = function(self, config) end -- Testing -local mutator1 = new_mod("mutator1") -local mutator2 = new_mod("mutator2") +local mutation = new_mod("mutation") +local deathwish = new_mod("deathwish") local mutator3 = new_mod("mutator3") local mutator555 = new_mod("mutator555") local mutator_whatever = new_mod("mutator_whatever") mutator555:register_as_mutator({ enable_after_these = { - "mutator1" + "mutation" } }) mutator555:create_options({}, true, "mutator555", "mutator555 description") mutator555.on_enabled = function() end mutator555.on_disabled = function() end -mutator2:register_as_mutator({ + +deathwish:register_as_mutator({ + enable_after_these = { + "mutation" + }, enable_before_these = { "mutator555", - "mutator3" + "mutator3", + "mutation" } }) -mutator2:create_options({}, true, "mutator2", "mutator2 description") -mutator2.on_enabled = function() end -mutator2.on_disabled = function() end +deathwish:create_options({}, true, "deathwish", "deathwish description") +deathwish.on_enabled = function() + print(tostring(Breeds.skaven_gutter_runner == Breeds.skaven_pack_master)) +end +deathwish.on_disabled = function() end -mutator1:register_as_mutator({ - enable_before_these = { - "mutator2" + +------------------------------- +local breeds +mutation:register_as_mutator({ + enable_after_these = { + "deathwish" } }) -mutator1:create_options({}, true, "mutator1", "mutator1 description") -mutator1.on_enabled = function() end -mutator1.on_disabled = function() end +mutation:create_options({}, true, "mutation", "mutation description") +mutation.on_enabled = function() + breeds = table.clone(Breeds) + Breeds.skaven_slave = Breeds.skaven_clan_rat + Breeds.skaven_clan_rat = Breeds.skaven_storm_vermin_commander + + Breeds.skaven_gutter_runner = Breeds.skaven_rat_ogre + Breeds.skaven_pack_master = Breeds.skaven_rat_ogre + Breeds.skaven_poison_wind_globadier = Breeds.skaven_rat_ogre + Breeds.skaven_ratling_gunner = Breeds.skaven_rat_ogre +end +mutation.on_disabled = function(initial) + if not initial then Breeds = breeds end +end +------------------------------- mutator3:register_as_mutator({ enable_before_these = { From ec95192907034115d6114e1e1ba5e5f7b9c3d9db Mon Sep 17 00:00:00 2001 From: UnShame Date: Mon, 19 Feb 2018 22:34:28 +0300 Subject: [PATCH 03/32] mutators: comments --- .../scripts/mods/vmf/modules/mutators.lua | 80 +++++++++++++++---- 1 file changed, 66 insertions(+), 14 deletions(-) diff --git a/vmf_source/scripts/mods/vmf/modules/mutators.lua b/vmf_source/scripts/mods/vmf/modules/mutators.lua index 8f2e719..d5f1d73 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators.lua @@ -1,14 +1,34 @@ local vmf = get_mod("VMF") +-- List of mods that are also mutators in order in which they should be enabled +-- This is populated via VMFMod.register_as_mutator local mutators = {} -local mutators_sequence = {} + +-- 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 -local function update_mutators_sequence(mutator_name, load_these_after) + +--[[ + PRIVATE METHODS +]]-- + +-- 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(load_these_after) do + 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 vmf:error("Mutators '" .. mutator_name .. "' and '" .. other_mutator_name .. "' are both set to load after the other one.") @@ -17,36 +37,48 @@ local function update_mutators_sequence(mutator_name, load_these_after) end end - table.combine(mutators_sequence[mutator_name], load_these_after) + table.combine(mutators_sequence[mutator_name], enable_these_after) end +-- This shouldn't happen local function error_endless_loop() vmf:error("Mutators: too many iterations. Check for loops in 'enable_before_these'/'enable_after_these'.") end +-- Sorts mutators in order they should be enabled local function sort_mutators() + -- LOG -- vmf:dump(mutators_sequence, "seq", 5) for i, v in ipairs(mutators) do print(i, v:get_name()) end print("-----------") + -- /LOG -- - local maxIter = #mutators * #mutators * #mutators + -- Preventing endless loops (worst case is n*n*(n+1)/2 I believe) + local maxIter = #mutators * #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 load_these_after = mutators_sequence[mutator_name] or {} + local enable_these_after = mutators_sequence[mutator_name] or {} + -- Going back from the previous mutator local j = i - 1 while j > 0 do local other_mutator = mutators[j] - if table.has_item(load_these_after, other_mutator:get_name()) then + + -- 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 @@ -62,29 +94,37 @@ local function sort_mutators() end mutators_sorted = true + -- LOG -- for i, v in ipairs(mutators) do print(i, v:get_name()) end print("-----------") + -- /LOG -- end +-- Enables/disables mutator while preserving the sequence in which they were enabled local function set_mutator_state(self, state) + local i = table.index_of(mutators, self) if i == nil then self:error("Mutator isn't in the list") return end + -- Sort mutators if this is the first call if not mutators_sorted then sort_mutators() end local disabled_mutators = {} - local load_these_after = mutators_sequence[self:get_name()] + local enable_these_after = mutators_sequence[self:get_name()] - if load_these_after and #mutators > i then + -- 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(load_these_after, mutators[j]:get_name()) then + 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]) @@ -92,6 +132,7 @@ local function set_mutator_state(self, state) end end + -- Enable/disable current mutator if state then print("Enabled ", self:get_name(), "!") VMFMod.enable(self) @@ -100,6 +141,8 @@ local function set_mutator_state(self, state) VMFMod.disable(self) 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()) @@ -109,14 +152,23 @@ local function set_mutator_state(self, state) print("---------") end +-- Enables mutator (pcall for now) local function enable_mutator(self) vmf:pcall(function() set_mutator_state(self, true) end) end +-- Disables mutator (pcall for now) local function disable_mutator(self) vmf:pcall(function() set_mutator_state(self, false) end) end + +--[[ + PUBLIC METHODS +]]-- + +-- Turns a mod into a mutator +-- For now all it does is creates a sequence in which they need to be enabled/disabled VMFMod.register_as_mutator = function(self, config) if not config then config = {} end @@ -148,7 +200,10 @@ VMFMod.register_as_mutator = function(self, config) self:init_state(false) end --- Testing + +--[[ + Testing +--]] local mutation = new_mod("mutation") local deathwish = new_mod("deathwish") local mutator3 = new_mod("mutator3") @@ -166,9 +221,6 @@ mutator555.on_disabled = function() end deathwish:register_as_mutator({ - enable_after_these = { - "mutation" - }, enable_before_these = { "mutator555", "mutator3", From b7f47297b2689cf9fe528f4b7e0d181ce5837a1a Mon Sep 17 00:00:00 2001 From: UnShame Date: Mon, 19 Feb 2018 22:58:07 +0300 Subject: [PATCH 04/32] mutators: minor fixes, move to folder --- .../vmf/modules/{ => mutators}/mutators.lua | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) rename vmf_source/scripts/mods/vmf/modules/{ => mutators}/mutators.lua (92%) diff --git a/vmf_source/scripts/mods/vmf/modules/mutators.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutators.lua similarity index 92% rename from vmf_source/scripts/mods/vmf/modules/mutators.lua rename to vmf_source/scripts/mods/vmf/modules/mutators/mutators.lua index d5f1d73..a981424 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutators.lua @@ -40,11 +40,6 @@ local function update_mutators_sequence(mutator_name, enable_these_after) table.combine(mutators_sequence[mutator_name], enable_these_after) end --- This shouldn't happen -local function error_endless_loop() - vmf:error("Mutators: too many iterations. Check for loops in 'enable_before_these'/'enable_after_these'.") -end - -- Sorts mutators in order they should be enabled local function sort_mutators() @@ -56,8 +51,8 @@ local function sort_mutators() print("-----------") -- /LOG -- - -- Preventing endless loops (worst case is n*n*(n+1)/2 I believe) - local maxIter = #mutators * #mutators * (#mutators + 1)/2 + -- 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 @@ -68,7 +63,7 @@ local function sort_mutators() local mutator_name = mutator:get_name() local enable_these_after = mutators_sequence[mutator_name] or {} - -- Going back from the previous mutator + -- 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] @@ -82,21 +77,21 @@ local function sort_mutators() i = i - 1 end j = j - 1 - - numIter = numIter + 1 - if numIter > maxIter then return error_endless_loop() end end i = i + 1 numIter = numIter + 1 - if numIter > maxIter then return error_endless_loop() end + if numIter > maxIter then + vmf:error("Mutators: too many iterations. Check for loops in 'enable_before_these'/'enable_after_these'.") + return + end end mutators_sorted = true -- LOG -- - for i, v in ipairs(mutators) do - print(i, v:get_name()) + for k, v in ipairs(mutators) do + print(k, v:get_name()) end print("-----------") -- /LOG -- @@ -133,6 +128,7 @@ local function set_mutator_state(self, state) 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 ", self:get_name(), "!") VMFMod.enable(self) From 8f3a801e3cde485c648687027c6e8566145cceb9 Mon Sep 17 00:00:00 2001 From: UnShame Date: Tue, 20 Feb 2018 10:44:38 +0300 Subject: [PATCH 05/32] mutators: dice, info, default_config --- vmf_source/resource_packages/vmf.package | 1 + .../vmf/modules/mutators/default_config.lua | 21 ++++ .../mods/vmf/modules/mutators/dice.lua | 66 ++++++++++++ .../mods/vmf/modules/mutators/info.lua | 79 ++++++++++++++ .../mods/vmf/modules/mutators/mutators.lua | 101 +++++++++++++++--- vmf_source/scripts/mods/vmf/vmf_loader.lua | 2 +- 6 files changed, 257 insertions(+), 13 deletions(-) create mode 100644 vmf_source/scripts/mods/vmf/modules/mutators/default_config.lua create mode 100644 vmf_source/scripts/mods/vmf/modules/mutators/dice.lua create mode 100644 vmf_source/scripts/mods/vmf/modules/mutators/info.lua diff --git a/vmf_source/resource_packages/vmf.package b/vmf_source/resource_packages/vmf.package index 88810b0..bee278a 100644 --- a/vmf_source/resource_packages/vmf.package +++ b/vmf_source/resource_packages/vmf.package @@ -20,4 +20,5 @@ lua = [ "scripts/mods/vmf/modules/debug/*" "scripts/mods/vmf/modules/gui/*" "scripts/mods/vmf/modules/options_menu/*" + "scripts/mods/vmf/modules/mutators/*" ] \ No newline at end of file diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/default_config.lua b/vmf_source/scripts/mods/vmf/modules/mutators/default_config.lua new file mode 100644 index 0000000..6f07938 --- /dev/null +++ b/vmf_source/scripts/mods/vmf/modules/mutators/default_config.lua @@ -0,0 +1,21 @@ +return { + dice = { + grims = 0, + tomes = 0, + bonus = 0 + }, + server_name = "", + title = "default_title", + --short_title = "", + difficulties = { + "easy", + "normal", + "hard", + "harder", + "hardest", + + "survival_hard", + "survival_harder", + "survival_hardest" + } +} diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/dice.lua b/vmf_source/scripts/mods/vmf/modules/mutators/dice.lua new file mode 100644 index 0000000..ebda96e --- /dev/null +++ b/vmf_source/scripts/mods/vmf/modules/mutators/dice.lua @@ -0,0 +1,66 @@ +local vmf = get_mod("VMF") + +local missions = { + "bonus_dice_hidden_mission", + "tome_bonus_mission", + "grimoire_hidden_mission" +} + +local num_dice_per_mission = { + bonus_dice_hidden_mission = 0, + tome_bonus_mission = 0, + grimoire_hidden_mission = 0 +} + +vmf: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 + + 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 + + for name, obj in pairs(active_mission) do + if table.has_item(missions, name) then + num_dice = num_dice + obj.current_amount + end + end + + 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) + +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, removeDice diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/info.lua b/vmf_source/scripts/mods/vmf/modules/mutators/info.lua new file mode 100644 index 0000000..77c657d --- /dev/null +++ b/vmf_source/scripts/mods/vmf/modules/mutators/info.lua @@ -0,0 +1,79 @@ +local vmf = get_mod("VMF") +local mutators = vmf.mutators + +local function get_enabled_mutators_names(short) + local name = "" + for _, mutator in ipairs(mutators) do + local config = mutator:get_config() + if mutator:is_enabled() then + name = name .. " " .. (short and config.short_title or config.title) + end + end + return name +end + +local function set_lobby_data() + + if not Managers.matchmaking then return end + + local name = get_enabled_mutators_names(true) + + local default_name = LobbyAux.get_unique_server_name() + if name 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 + +local function get_member_func( 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 function() + for _, v in ipairs(Managers.matchmaking.lobby:members():get_members()) do + if v == peer_id then + return {v} + end + end + return Managers.matchmaking.lobby:members():get_members() + end +end + + +vmf:hook("IngamePlayerListUI.set_difficulty_name", function(func, self, name) + local mutators_name = get_enabled_mutators_names(true) + if mutators_name then + name = name .. " " .. mutators_name + end + self.headers.content.game_difficulty = name +end) + +vmf:hook("MatchmakingStateHostGame.host_game", function(func, self, ...) + func(self, ...) + set_lobby_data() +end) + +vmf:hook("MatchmakingManager.rpc_matchmaking_request_join_lobby", function(func, self, sender, client_cookie, host_cookie, lobby_id, friend_join) + local name = get_enabled_mutators_names(false) + if name then + local message = "[Automated message] This lobby has the following difficulty mod active : " .. name + vmf:hook("Managers.chat.channels[1].members_func", get_member_func(client_cookie)) + Managers.chat:send_system_chat_message(1, message, 0, true) + vmf:hook_remove("Managers.chat.channels[1].members_func") + end + func(self, sender, client_cookie, host_cookie, lobby_id, friend_join) +end) + +return set_lobby_data \ No newline at end of file diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutators.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutators.lua index a981424..e0ad34a 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutators.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutators.lua @@ -1,8 +1,13 @@ local vmf = get_mod("VMF") + -- List of mods that are also mutators in order in which they should be enabled -- This is populated via VMFMod.register_as_mutator local mutators = {} +vmf.mutators = mutators + +local mutators_config = {} +local default_config = dofile("scripts/mods/vmf/modules/mutators/default_config") -- This lists mutators and which ones should be enabled after them -- This is populated via VMFMod.register_as_mutator @@ -23,6 +28,9 @@ local mutators_sorted = false PRIVATE METHODS ]]-- +local addDice, removeDice = dofile("scripts/mods/vmf/modules/mutators/dice") +local set_lobby_data = dofile("scripts/mods/vmf/modules/mutators/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 @@ -82,7 +90,7 @@ local function sort_mutators() i = i + 1 numIter = numIter + 1 - if numIter > maxIter then + if numIter > maxIter then vmf:error("Mutators: too many iterations. Check for loops in 'enable_before_these'/'enable_after_these'.") return end @@ -97,12 +105,33 @@ local function sort_mutators() -- /LOG -- end --- Enables/disables mutator while preserving the sequence in which they were enabled -local function set_mutator_state(self, state) +local function mutator_can_be_enabled(mutator) + return (not Managers.state or not Managers.state.difficulty:get_difficulty()) or table.has_item(mutator:get_config().difficulties, Managers.state.difficulty:get_difficulty()) +end - local i = table.index_of(mutators, self) +local function on_enabled(mutator) + local config = mutator:get_config() + addDice(config.dice) + set_lobby_data() +end + +local function on_disabled(mutator) + local config = mutator:get_config() + 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 - self:error("Mutator isn't in the list") + mutator:error("Mutator isn't in the list") + return + end + + if state and not mutator_can_be_enabled(mutator) then + mutator:error("Can't enable mutator - incorrect difficulty") return end @@ -112,7 +141,7 @@ local function set_mutator_state(self, state) end local disabled_mutators = {} - local enable_these_after = mutators_sequence[self:get_name()] + 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 @@ -130,11 +159,13 @@ local function set_mutator_state(self, state) -- 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 ", self:get_name(), "!") - VMFMod.enable(self) + print("Enabled ", mutator:get_name(), "!") + VMFMod.enable(mutator) + on_enabled(mutator) else - print("Disabled ", self:get_name(), "!") - VMFMod.disable(self) + print("Disabled ", mutator:get_name(), "!") + VMFMod.disable(mutator) + on_disabled(mutator) end -- Re-enable disabled mutators @@ -158,13 +189,16 @@ local function disable_mutator(self) vmf:pcall(function() set_mutator_state(self, false) end) end +local function get_config(self) + return mutators_config[self:get_name()] +end + --[[ PUBLIC METHODS ]]-- -- Turns a mod into a mutator --- For now all it does is creates a sequence in which they need to be enabled/disabled VMFMod.register_as_mutator = function(self, config) if not config then config = {} end @@ -177,6 +211,16 @@ VMFMod.register_as_mutator = function(self, config) 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] then + _config[k] = config[k] + end + end + if _config.short_title == "" then _config.short_title = nil end + if config.enable_before_these then update_mutators_sequence(mod_name, config.enable_before_these) end @@ -190,12 +234,42 @@ VMFMod.register_as_mutator = function(self, config) self.enable = enable_mutator self.disable = disable_mutator + self.get_config = get_config + mutators_sorted = false -- Always init in the off state self:init_state(false) end +--[[ + HOOKS +]]-- +vmf:hook("DifficultyManager.set_difficulty", function(func, self, difficulty) + for _, mutator in ipairs(mutators) do + if mutator:is_enabled() and not mutator_can_be_enabled(mutator:get_config()) then + mutator:disable() + end + end + return func(self, difficulty) +end) + + + + + + + + + + + + + + + + + --[[ Testing @@ -221,10 +295,13 @@ deathwish:register_as_mutator({ "mutator555", "mutator3", "mutation" + }, + difficulties = { + "hardest" } }) deathwish:create_options({}, true, "deathwish", "deathwish description") -deathwish.on_enabled = function() +deathwish.on_enabled = function() print(tostring(Breeds.skaven_gutter_runner == Breeds.skaven_pack_master)) end deathwish.on_disabled = function() end diff --git a/vmf_source/scripts/mods/vmf/vmf_loader.lua b/vmf_source/scripts/mods/vmf/vmf_loader.lua index 80e33c2..d5e393c 100644 --- a/vmf_source/scripts/mods/vmf/vmf_loader.lua +++ b/vmf_source/scripts/mods/vmf/vmf_loader.lua @@ -21,7 +21,7 @@ return { dofile("scripts/mods/vmf/modules/options_menu/vmf_options_view") dofile("scripts/mods/vmf/modules/vmf_options") - dofile("scripts/mods/vmf/modules/mutators") + dofile("scripts/mods/vmf/modules/mutators/mutators") object.vmf = get_mod("VMF") From 81a195ec308b1dd16f4c1f9816a4d4f977a966a7 Mon Sep 17 00:00:00 2001 From: UnShame Date: Tue, 20 Feb 2018 16:44:06 +0300 Subject: [PATCH 06/32] mutators: temporally add gui_mod to framework --- vmf_source/resource_packages/vmf.package | 1 + .../mods/vmf/modules/mod_gui/basic_gui.lua | 301 ++ .../scripts/mods/vmf/modules/mod_gui/gui.lua | 2529 +++++++++++++++++ vmf_source/scripts/mods/vmf/vmf_loader.lua | 3 + 4 files changed, 2834 insertions(+) create mode 100644 vmf_source/scripts/mods/vmf/modules/mod_gui/basic_gui.lua create mode 100644 vmf_source/scripts/mods/vmf/modules/mod_gui/gui.lua diff --git a/vmf_source/resource_packages/vmf.package b/vmf_source/resource_packages/vmf.package index bee278a..1bac16d 100644 --- a/vmf_source/resource_packages/vmf.package +++ b/vmf_source/resource_packages/vmf.package @@ -21,4 +21,5 @@ lua = [ "scripts/mods/vmf/modules/gui/*" "scripts/mods/vmf/modules/options_menu/*" "scripts/mods/vmf/modules/mutators/*" + "scripts/mods/vmf/modules/mod_gui/*" ] \ No newline at end of file diff --git a/vmf_source/scripts/mods/vmf/modules/mod_gui/basic_gui.lua b/vmf_source/scripts/mods/vmf/modules/mod_gui/basic_gui.lua new file mode 100644 index 0000000..83f65a8 --- /dev/null +++ b/vmf_source/scripts/mods/vmf/modules/mod_gui/basic_gui.lua @@ -0,0 +1,301 @@ +--[[ + author: grasmann + + Basic GUI + - Provides a screen gui and functionality to draw to it +--]] +local basic_gui = new_mod("basic_gui") + +basic_gui.gui = nil +basic_gui.default_world = "top_ingame_view" +basic_gui.default_font = "hell_shark" +basic_gui.default_font_size = 22 +basic_gui.default_font_material = "materials/fonts/gw_body_32" + +-- #################################################################################################################### +-- ##### Init ######################################################################################################### +-- #################################################################################################################### +--[[ + Init draw +--]] +basic_gui.init = function() + if not basic_gui.gui then + basic_gui.create_screen_ui() + end +end +--[[ + Create screen gui +--]] +basic_gui.create_screen_ui = function() + local world = Managers.world:world(basic_gui.default_world) + basic_gui.gui = World.create_screen_gui(world, "immediate", + "material", "materials/fonts/gw_fonts", + --"material", "materials/ui/ui_1080p_ingame", + "material", "materials/ui/ui_1080p_ingame_common") + -- "material", "materials/ui/ui_1080p_popup" + -- "material", "materials/ui/ui_1080p_ingame_inn" +end + +-- #################################################################################################################### +-- ##### Overloaded draw functions #################################################################################### +-- #################################################################################################################### +--[[ + Draw rect with vectors +--]] +basic_gui.rect_vectors = function(position, size, color) + + Gui.rect(basic_gui.gui, position, size, color) +end +--[[ + Draw text with vectors +--]] +basic_gui.text_vectors = function(text, position, font_size, color, font) + local font_type = font or "hell_shark" + local font_by_resolution = UIFontByResolution({ + dynamic_font = true, + font_type = font_type, + font_size = font_size + }) + local font, result_size, material = unpack(font_by_resolution) + Gui.text(basic_gui.gui, text, font, font_size, material, position, color) +end +--[[ + Draw bitmap with vectors +--]] +basic_gui.bitmap_uv = function(atlas, uv00, uv11, position, size, color) + local atlas = atlas or "gui_hud_atlas" + local uv00 = uv00 or Vector2(0.222656, 0.584961) + local uv11 = uv11 or Vector2(0.25293, 0.615234) + local position = position or Vector3(1, 1, 1) + local size = size or Vector2(62, 62) + local color = color or Color(255, 255, 255, 255) + return Gui.bitmap_uv(basic_gui.gui, atlas, uv00, uv11, position, size, color) +end + + +-- #################################################################################################################### +-- ##### Draw functions ############################################################################################### +-- #################################################################################################################### +--[[ + Draw rect + Parameters: + 1) int:X, int:Y, int:Z, int:Width, int:Height, Color:color + 2) vector3:Position, vector2:size, Color:color +--]] +basic_gui.rect = function(arg1, arg2, arg3, arg4, arg5, arg6) + local color = Color(255, 255, 255, 255) + if type(arg1) == "number" then + basic_gui.rect_vectors(Vector3(arg1, arg2, arg3 or 1), Vector2(arg4, arg5), arg6 or color) + else + basic_gui.rect_vectors(arg1, arg2, arg3 or color) + end +end +--[[ + Draw text + Parameters: + 1) string:Text, int:X, int:Y, int:Z, int:font_size, Color:color, string:font + 2) string:Text, vector3:position, int:font_size, Color:color, string:font +--]] +basic_gui.text = function(arg1, arg2, arg3, arg4, arg5, arg6, arg7) + local color = Color(255, 255, 255, 255) + local font_size = basic_gui.default_font_size + local font = basic_gui.default_font + if type(arg2) == "number" then + basic_gui.text_vectors(arg1, Vector3(arg2, arg3, arg4 or 1), arg5 or font_size, arg6 or color, arg7 or font) + else + basic_gui.text_vectors(arg1, arg2, arg3 or font_size, arg4 or color, arg5 or font) + end +end +--[[ + Create lines from string with \n +--]] +basic_gui.to_lines = function(text, font_size, font_material, colors) + local get_color = function(index) + if #colors >= index then + if colors[index] then + return colors[index] + end + else + return colors[#colors] + end + return {255, 255, 255, 255} + end + local _lines = {} + local width = 0 + local height = 0 + font_size = font_size or basic_gui.default_font_size + local index = 1 + font_material = font_material or basic_gui.default_font_material + for line in string.gmatch(text.."\n", "([^\n]*)\n") do + local w = basic_gui.text_width(line, font_material, font_size) + if line == "" then line = "#" end + if w > width then width = w end + --local h = basic_gui.text_height(line, font_material, font_size) + height = height + font_size --h + _lines[#_lines+1] = { + text = line, + width = w, + height = font_size, --h, + color = get_color(index), + } + index = index + 1 + end + if #_lines == 0 and #text > 0 then + _lines[#_lines+1] = text + end + local result = { + size = {width, height}, + ["lines"] = _lines, + } + return result +end +--[[ + Draw tooltip + Parameters: + 1) string:Text [, table:colors, int:font_size, int:line_padding, table:offset, table:padding, string:font_material] +--]] +basic_gui.tooltip = function(str, colors, font_size, line_padding, offset, padding, font_material, size) + basic_gui:pcall(function() + -- Create default colors if nil + colors = colors or + {Colors.get_color_table_with_alpha("cheeseburger", 255), + Colors.get_color_table_with_alpha("white", 255),} + -- Get mouse position + local cursor_axis_id = stingray.Mouse.axis_id("cursor") + local mouse = stingray.Mouse.axis(cursor_axis_id) + -- UI + local scale = UIResolutionScale() + local screen_w, screen_h = UIResolution() + -- Font + font_size = font_size or screen_w / 100 + font_material = font_material or basic_gui.default_font_material --"materials/fonts/gw_body_32" + -- Offset / Padding + offset = offset or {20*scale, -20*scale} + padding = padding or {5*scale, 5*scale, 5*scale, 5*scale} + line_padding = line_padding or 0*scale + -- Transform string + local text = basic_gui.to_lines(str, font_size, font_material, colors) + -- Transform simple text size + if #text.lines > 0 then + text.size[2] = text.size[2] + (#text.lines-1 * line_padding) + padding[4] + padding[1] + text.size[1] = text.size[1] + padding[1] + padding[3] + end + -- Render background + local x = mouse[1] + offset[1] + local y = mouse[2] + offset[2] + size = size or Vector2(text.size[1], text.size[2]) + basic_gui.rect(Vector3(x, y, 999), size, Color(200, 0, 0, 0)) + -- Render lines + --basic_gui:echo("rofl") + local text_x = x + padding[1] + local text_y = y + text.size[2] - padding[2] - font_size + --local index = 1 + local icon_size = Vector2(50*scale, 50*scale) + local icon_x = text_x + local icon_y = text_y - icon_size[2] + for _, line in pairs(text.lines) do + if line.text ~= "#" then + local color = Color(line.color[1], line.color[2], line.color[3], line.color[4]) + basic_gui.text(line.text, Vector3(text_x, text_y, 999), font_size, color) + end + text_y = text_y - line.height - line_padding + end + end) +end + +--[[ + Draw mission icon + Parameters: + 1) vector3:position, string:text, vector2:text_offset, color:color, [vector2:size, int:font_size, string:font] + + text_offset: + By default the text will be placed to the right of the icon up half its size + The offset if set will be calculated from the middle of the icon +--]] +basic_gui.side_mission_icon = function(position, text, text_offset, color, size, font_size, font, text_color) + local atlas = "gui_hud_atlas" + local uv00 = Vector2(0.222656, 0.584961) + local uv11 = Vector2(0.25293, 0.615234) + local position = position or Vector3(1, 1, 1) + local size = size or Vector2(62, 62) + local color = color or Color(255, 255, 255, 255) + local id = basic_gui.bitmap_uv(atlas, uv00, uv11, position, size, color) + if text ~= nil then + if text_offset ~= nil then + position = position + Vector3(text_offset[1], text_offset[2], 0) + else + position = position + Vector3(size[1], size[2] / 2, 0) + end + local font_size = font_size or basic_gui.default_font_size + local font = basic_gui.default_font + local text_color = text_color or color + basic_gui.text_vectors(text, position, font_size, text_color, font) + end + return id, size, color +end + +basic_gui.tome_icon = function(position, color, size) + local icon_texture = "consumables_book_lit" + local icon_settings = UIAtlasHelper.get_atlas_settings_by_texture_name(icon_texture) + + local atlas = "gui_generic_icons_atlas" + local size = size or Vector2(icon_settings.size[1], icon_settings.size[2]) + local color = color or Color(255, 255, 255, 255) + local uv00 = Vector2(icon_settings.uv00[1], icon_settings.uv00[2]) + local uv11 = Vector2(icon_settings.uv11[1], icon_settings.uv11[2]) + local position = position or Vector3(1, 1, 1) + return basic_gui.bitmap_uv(atlas, uv00, uv11, position, size, color) +end + +basic_gui.grim_icon = function(position, color, size) + local icon_texture = "consumables_grimoire_lit" + local icon_settings = UIAtlasHelper.get_atlas_settings_by_texture_name(icon_texture) + + local atlas = "gui_generic_icons_atlas" + local size = size or Vector2(icon_settings.size[1], icon_settings.size[2]) + local color = color or Color(255, 255, 255, 255) + local uv00 = Vector2(icon_settings.uv00[1], icon_settings.uv00[2]) + local uv11 = Vector2(icon_settings.uv11[1], icon_settings.uv11[2]) + local position = position or Vector3(1, 1, 1) + return basic_gui.bitmap_uv(atlas, uv00, uv11, position, size, color) +end + +basic_gui.draw_icon = function(icon_texture, position, color, size, atlas) + local icon_settings = UIAtlasHelper.get_atlas_settings_by_texture_name(icon_texture) + + local atlas = atlas or "gui_generic_icons_atlas" + local size = size or Vector2(icon_settings.size[1], icon_settings.size[2]) + local color = color or Color(255, 255, 255, 255) + local uv00 = Vector2(icon_settings.uv00[1], icon_settings.uv00[2]) + local uv11 = Vector2(icon_settings.uv11[1], icon_settings.uv11[2]) + local position = position or Vector3(1, 1, 1) + return basic_gui.bitmap_uv(atlas, uv00, uv11, position, size, color) +end + +-- #################################################################################################################### +-- ##### Get information ############################################################################################## +-- #################################################################################################################### +--[[ + Get width of a text with the given font and font size +--]] +basic_gui.text_width = function(text, font, font_size) + local text_extent_min, text_extent_max = Gui.text_extents(basic_gui.gui, text, font or basic_gui.default_font, font_size or basic_gui.default_font_size) + local text_width = text_extent_max[1] - text_extent_min[1] + return text_width +end +--[[ + Get height of a text with the given font and font size +--]] +basic_gui.text_height = function(text, font, font_size) + local text_extent_min, text_extent_max = Gui.text_extents(basic_gui.gui, text, font or basic_gui.default_font, font_size or basic_gui.default_font_size) + local text_height = text_extent_max[2] - text_extent_min[2] + return text_height +end + +basic_gui.on_game_state_changed = function(status, state) + if status == "enter" and state == "StateIngame" then + basic_gui.init() + end +end + +basic_gui:pcall(basic_gui.init) \ No newline at end of file diff --git a/vmf_source/scripts/mods/vmf/modules/mod_gui/gui.lua b/vmf_source/scripts/mods/vmf/modules/mod_gui/gui.lua new file mode 100644 index 0000000..107c51e --- /dev/null +++ b/vmf_source/scripts/mods/vmf/modules/mod_gui/gui.lua @@ -0,0 +1,2529 @@ +--[[ + Author: +--]] + +local gui = new_mod("gui") +local basic_gui = get_mod("basic_gui") + +-- ################################################################################################################ +-- ##### UTF8 ##################################################################################################### +-- ################################################################################################################ +local UTF8 = { + -- UTF-8 Reference: + -- 0xxxxxxx - 1 byte UTF-8 codepoint (ASCII character) + -- 110yyyxx - First byte of a 2 byte UTF-8 codepoint + -- 1110yyyy - First byte of a 3 byte UTF-8 codepoint + -- 11110zzz - First byte of a 4 byte UTF-8 codepoint + -- 10xxxxxx - Inner byte of a multi-byte UTF-8 codepoint + + chsize = function(self, char) + if not char then + return 0 + elseif char > 240 then + return 4 + elseif char > 225 then + return 3 + elseif char > 192 then + return 2 + else + return 1 + end + end, + + -- This function can return a substring of a UTF-8 string, properly handling + -- UTF-8 codepoints. Rather than taking a start index and optionally an end + -- index, it takes the string, the starting character, and the number of + -- characters to select from the string. + + utf8sub = function(self, str, startChar, numChars) + local startIndex = 1 + while startChar > 1 do + local char = string.byte(str, startIndex) + startIndex = startIndex + self:chsize(char) + startChar = startChar - 1 + end + + local currentIndex = startIndex + + while numChars > 0 and currentIndex <= #str do + local char = string.byte(str, currentIndex) + currentIndex = currentIndex + self:chsize(char) + numChars = numChars -1 + end + return str:sub(startIndex, currentIndex - 1) + end, +} + + +-- ################################################################################################################ +-- ##### Input keymap ############################################################################################# +-- ################################################################################################################ +MOD_GUI_KEY_MAP = { + win32 = { + ["backspace"] = {"keyboard", "backspace", "held"}, + ["enter"] = {"keyboard", "enter", "pressed"}, + ["esc"] = {"keyboard", "esc", "pressed"}, + }, +} +MOD_GUI_KEY_MAP.xb1 = MOD_GUI_KEY_MAP.win32 +local ui_special_keys = {"space", "<", ">"} + +-- ################################################################################################################ +-- ##### Color helper ############################################################################################# +-- ################################################################################################################ +local ColorHelper = { + --[[ + Transform color values to table + ]]-- + box = function(a, r, g, b) + return {a, r, g, b} + end, + --[[ + Transform color table to color + ]]-- + unbox = function(box) + return Color(box[1], box[2], box[3], box[4]) + end, +} + +-- ################################################################################################################ +-- ##### Main Object ############################################################################################## +-- ################################################################################################################ +gui.theme = "default" + +gui.width = 1920 +gui.height = 1080 + +gui.adjust_to_fit_position_and_scale = function(position) + position = gui.adjust_to_fit_scale(position) + + local screen_w, screen_h = UIResolution() + local scale = UIResolutionScale() + local ui_w = 1920*scale + local ui_h = 1080*scale + + position = { + position[1] + (screen_w - ui_w)/2, + position[2] + (screen_h - ui_h)/2 + } + + --gui:echo(position[1], position[2]) + + return position +end + +gui.adjust_to_fit_scale = function(position) + if not position then return {0, 0} end + + local scale = UIResolutionScale() + + position = { + position[1] * scale, + position[2] * scale + } + + --gui:echo(position[1], position[2]) + + return position +end + +-- ################################################################################################################ +-- ##### Create containers ######################################################################################## +-- ################################################################################################################ +--[[ + Create window +]]-- +gui.create_window = function(name, position, size) + + -- Create window + position = gui.adjust_to_fit_position_and_scale(position) + size = gui.adjust_to_fit_scale(size) + + local window = table.clone(gui.widgets.window) + window:set("name", name or "name") + window:set("position", position) + window:set("size", size or {0, 0}) + window:set("original_size", size or {0, 0}) + + -- Add window to list + gui.windows:add_window(window) + + return window +end + +-- ################################################################################################################ +-- ##### Cycle #################################################################################################### +-- ################################################################################################################ +--[[ + Update +]]-- +gui._update = function(dt) + -- Update timers + gui.timers:update(dt) + + gui.input:check() + + -- Click + local position = gui.mouse:cursor() + if stingray.Mouse.pressed(stingray.Mouse.button_id("left")) then + gui.mouse:click(position, gui.windows.list) + elseif stingray.Mouse.released(stingray.Mouse.button_id("left")) then + gui.mouse:release(position, gui.windows.list) + end + + -- Hover + gui.mouse:hover(position, gui.windows.list) + + -- Update windows + gui.windows:update() +end + +gui:hook("MatchmakingManager.update", function(func, self, dt, t) + func(self, dt, t) + gui._update(dt) +end) + +-- ################################################################################################################ +-- ##### Common functions ######################################################################################### +-- ################################################################################################################ +--[[ + Transform position and size to bounds +]]-- +gui.to_bounds = function(position, size) + return {position[1], position[1] + size[1], position[2], position[2] + size[2]} +end +--[[ + Check if position is in bounds +]]-- +gui.point_in_bounds = function(position, bounds) + if position[1] >= bounds[1] and position[1] <= bounds[2] and position[2] >= bounds[3] and position[2] <= bounds[4] then + return true, {position[1] - bounds[1], position[2] - bounds[3]} + end + return false, {0, 0} +end + +-- ################################################################################################################ +-- ##### Window system ############################################################################################ +-- ################################################################################################################ +gui.windows = { + list = {}, + --[[ + Add window to list + ]]-- + add_window = function(self, window) + self:inc_z_orders(#self.list) + window.z_order = 1 + self.list[#self.list+1] = window + end, + --[[ + Shift z orders of windows + ]]-- + inc_z_orders = function(self, changed_z) + for z=changed_z, 1, -1 do + for _, window in pairs(self.list) do + if window.z_order == z then + window.z_order = window.z_order + 1 + end + end + end + end, + --[[ + Shift z orders of windows + ]]-- + dec_z_orders = function(self, changed_z) + --for z=changed_z, 1, -1 do + for z=changed_z+1, #self.list do + for _, window in pairs(self.list) do + if window.z_order == z then + window.z_order = window.z_order - 1 + end + end + end + end, + --[[ + Shift z orders of windows + ]]-- + unfocus = function(self) + for _, window in pairs(self.list) do + if window.z_order == 1 then + window:unfocus() + end + end + end, + --[[ + Update windows + ]]-- + update = function(self) + if #self.list > 0 then + for z=#self.list, 1, -1 do + for _, window in pairs(self.list) do + if window.visible then + if window.z_order == z then + window:update() + window:render() + end + end + end + end + end + end, +} + +-- ################################################################################################################ +-- ##### Mouse system ############################################################################################# +-- ################################################################################################################ +gui.mouse = { + --[[ + Process click + ]]-- + click = function(self, position, windows) + for z=1, #windows do + for _, window in pairs(windows) do + if window.z_order == z then + if gui.point_in_bounds(position, window:extended_bounds()) then + window:click(position) + else + window:unfocus() + end + end + end + end + end, + --[[ + Process release + ]]-- + release = function(self, position, windows) + for z=1, #windows do + for _, window in pairs(windows) do + if window.z_order == z then + if gui.point_in_bounds(position, window:extended_bounds()) then + window:release(position) + else + window:unfocus() + end + end + end + end + end, + --[[ + Process hover + ]]-- + hover = function(self, position, windows) + self:un_hover_all(windows) + for z=1, #windows do + for _, window in pairs(windows) do + if window.z_order == z then + local hovered, cursor = gui.point_in_bounds(position, window:extended_bounds()) + if hovered then + window:hover(cursor) + return + end + end + end + end + end, + --[[ + Unhover all + ]]-- + un_hover_all = function(self, windows) + for _, window in pairs(windows) do + if window.hovered then + window:hover_exit() + end + end + end, + --[[ + Get mouse position + ]]-- + cursor = function(self) + local cursor_axis_id = stingray.Mouse.axis_id("cursor") -- retrieve the axis ID + local value = stingray.Mouse.axis(cursor_axis_id) -- use the ID to access to value + return {value[1], value[2]} + end, +} + +-- ################################################################################################################ +-- ##### Timer system ############################################################################################# +-- ################################################################################################################ +gui.timers = { + -- Timer list + items = {}, + -- Timer template + template = { + name = nil, + rate = 100, + enabled = false, + time_passed = 0, + params = nil, + + -- ##### Methods ############################################################################## + --[[ + Enable timer + ]]-- + enable = function(self) + self.enabled = true + end, + --[[ + Disable timer + ]]-- + disable = function(self) + self.enabled = false + end, + + -- ##### Cycle ################################################################################ + --[[ + Process tick + ]]-- + tick = function(self) + --if self.on_tick and type(self.on_tick) == "function" then + self:on_tick(self.params) + --end + end, + --[[ + Update + ]]-- + update = function(self, dt) + if self.enabled then + self.time_passed = self.time_passed + dt + if self.time_passed >= self.rate / 1000 then + self:tick() + self.time_passed = 0 + end + else + self.time_passed = 0 + end + end, + + -- ##### Events ################################################################################ + --[[ + On click event + ]]-- + on_tick = function(self, ...) + end, + }, + --[[ + Create timer + ]]-- + create_timer = function(self, name, rate, enabled, on_tick, ...) --, wait) + if not table.has_item(self.items, name) then + local new_timer = table.clone(self.template) + new_timer.name = name or "timer_" .. tostring(#self.items+1) + new_timer.rate = rate or new_timer.rate + new_timer.enabled = enabled or new_timer.enabled + new_timer.on_tick = on_tick or new_timer.on_tick + new_timer.params = ... + --new_timer.wait = wait or new_timer.wait + self.items[name] = new_timer + return new_timer + end + return nil + end, + --[[ + Update timers + ]]-- + update = function(self, dt) + for name, timer in pairs(self.items) do + timer:update(dt) + end + end, +} + +-- ################################################################################################################ +-- ##### Input system ############################################################################################# +-- ################################################################################################################ +gui.input = { + blocked_services = nil, + --[[ + Check and create input system + ]]-- + check = function(self) + if not Managers.input:get_input_service("mod_gui") then + Managers.input:create_input_service("mod_gui", "MOD_GUI_KEY_MAP") + Managers.input:map_device_to_service("mod_gui", "keyboard") + Managers.input:map_device_to_service("mod_gui", "mouse") + Managers.input:map_device_to_service("mod_gui", "gamepad") + end + end, + --[[ + Get list of unblocked input services and block them + ]]-- + block = function(self) + if not self.blocked_services then + self.blocked_services = {} + Managers.input:get_unblocked_services("keyboard", 1, self.blocked_services) + for _, s in pairs(self.blocked_services) do + Managers.input:device_block_service("keyboard", 1, s) + Managers.input:device_block_service("mouse", 1, s) + Managers.input:device_block_service("gamepad", 1, s) + end + end + end, + --[[ + Unblock previously blocked services + ]]-- + unblock = function(self) + if self.blocked_services then + for _, s in pairs(self.blocked_services) do + Managers.input:device_unblock_service("keyboard", 1, s) + Managers.input:device_unblock_service("mouse", 1, s) + Managers.input:device_unblock_service("gamepad", 1, s) + end + self.blocked_services = nil + end + end, +} + +-- ################################################################################################################ +-- ##### Font system ############################################################################################## +-- ################################################################################################################ +gui.fonts = { + fonts = {}, + --[[ + Font template + ]]-- + template = { + font = "hell_shark", + material = "materials/fonts/gw_body_32", + size = 22, + font_size = function(self) + if not self.dynamic_size then + return self.size + else + local screen_w, screen_h = UIResolution() + local size = screen_w / 100 + return size + end + end, + dynamic_size = false, + }, + --[[ + Create font + ]]-- + create = function(self, name, font, size, material, dynamic_size) + if not table.has_item(self.fonts, name) then + local new_font = table.clone(self.template) + new_font.font = font or new_font.font + new_font.material = material or new_font.material + new_font.size = size or new_font.size + new_font.dynamic_size = dynamic_size or new_font.dynamic_size + self.fonts[name] = new_font + end + end, + --[[ + Get font by name + ]]-- + get = function(self, name) + for k, font in pairs(self.fonts) do + if k == name then + return font + end + end + return gui.fonts.default or nil + end, +} + +-- ################################################################################################################ +-- ##### Anchor system ############################################################################################ +-- ################################################################################################################ +gui.anchor = { + styles = { + "bottom_left", + "center_left", + "top_left", + "middle_top", + "top_right", + "center_right", + "bottom_right", + "middle_bottom", + "fill", + }, + bottom_left = { + position = function(window, widget) + local x = window.position[1] + widget.offset[1] + local y = window.position[2] + widget.offset[2] + return {x, y}, widget.size + end, + }, + center_left = { + position = function(window, widget) + local x = window.position[1] + widget.offset[1] + local y = window.position[2] + window.size[2]/2 - widget.size[2]/2 + return {x, y}, widget.size + end, + }, + top_left = { + position = function(window, widget) + local x = window.position[1] + widget.offset[1] + local y = window.position[2] + window.size[2] - widget.offset[2] - widget.size[2] + return {x, y}, widget.size + end, + }, + middle_top = { + position = function(window, widget) + local x = window.position[1] + window.size[1]/2 - widget.size[1]/2 + local y = window.position[2] + window.size[2] - widget.offset[2] - widget.size[2] + return {x, y}, widget.size + end, + }, + top_right = { + position = function(window, widget) + local x = window.position[1] + window.size[1] - widget.offset[1] - widget.size[1] + local y = window.position[2] + window.size[2] - widget.offset[2] - widget.size[2] + return {x, y}, widget.size + end, + }, + center_right = { + position = function(window, widget) + local x = window.position[1] + window.size[1] - widget.offset[1] - widget.size[1] + local y = window.position[2] + window.size[2]/2 - widget.size[2]/2 + return {x, y}, widget.size + end, + }, + bottom_right = { + position = function(window, widget) + local x = window.position[1] + window.size[1] - widget.offset[1] - widget.size[1] + local y = window.position[2] + widget.offset[2] + return {x, y}, widget.size + end, + }, + middle_bottom = { + position = function(window, widget) + local x = window.position[1] + window.size[1]/2 - widget.size[1]/2 + local y = window.position[2] + widget.offset[2] + return {x, y}, widget.size + end, + }, + fill = { + position = function(window, widget) + return {window.position[1], window.position[2]}, {window.size[1], window.size[2]} + end, + }, +} + +-- ################################################################################################################ +-- ##### Textalignment system ##################################################################################### +-- ################################################################################################################ +gui.text_alignment = { + bottom_left = { + position = function(text, font, bounds) + local scale = UIResolutionScale() + local text_width = basic_gui.text_width(text, font.material, font:font_size()) + local text_height = font:font_size() --basic_gui.text_height(text, font.material, font:font_size()) + local frame_width = bounds[2] - bounds[1] + local frame_height = bounds[3] - bounds[4] + local left = bounds[1] + local bottom = bounds[3] + local fix = 2*scale + return {left + fix, bottom + fix} + end, + }, + bottom_center = { + position = function(text, font, bounds) + local scale = UIResolutionScale() + local text_width = basic_gui.text_width(text, font.material, font:font_size()) + local text_height = font:font_size() --basic_gui.text_height(text, font.material, font:font_size()) + local frame_width = bounds[2] - bounds[1] + local frame_height = bounds[3] - bounds[4] + local left = bounds[1] + local bottom = bounds[3] + local fix = 2*scale + return {left + frame_width/2 - text_width/2, bottom + fix} + end, + }, + bottom_right = { + position = function(text, font, bounds) + local scale = UIResolutionScale() + local text_width = basic_gui.text_width(text, font.material, font:font_size()) + local text_height = font:font_size() --basic_gui.text_height(text, font.material, font:font_size()) + local frame_width = bounds[2] - bounds[1] + local frame_height = bounds[3] - bounds[4] + local right = bounds[2] + local bottom = bounds[3] + local fix = 2*scale + return {right - text_width, bottom + fix} + end, + }, + middle_left = { + position = function(text, font, bounds, padding) + local scale = UIResolutionScale() + local text_height = font:font_size() + local frame_height = bounds[4] - bounds[3] + local left = bounds[1] + local bottom = bounds[3] + --local border = 5*scale + local fix = 2*scale + return {left + fix, bottom + fix + (frame_height/2) - (text_height/2)} + end, + }, + middle_center = { + position = function(text, font, bounds) + local scale = UIResolutionScale() + local text_width = basic_gui.text_width(text, font.material, font:font_size()) + local text_height = font:font_size() --basic_gui.text_height(text, font.material, font:font_size()) + local frame_width = bounds[2] - bounds[1] + local frame_height = bounds[4] - bounds[3] + local left = bounds[1] + local bottom = bounds[3] + local fix = 2*scale + return {left + fix + frame_width/2 - text_width/2, bottom + fix + (frame_height/2) - (text_height/2)} + end, + }, + middle_right = { + position = function(text, font, bounds) + local scale = UIResolutionScale() + local text_width = basic_gui.text_width(text, font.material, font:font_size()) + local text_height = font:font_size() --basic_gui.text_height(text, font.material, font:font_size()) + local frame_width = bounds[2] - bounds[1] + local frame_height = bounds[4] - bounds[3] + local right = bounds[2] + local bottom = bounds[3] + local fix = 2*scale + return {right - text_width - fix, bottom + fix + (frame_height/2) - (text_height/2)} + end, + }, + top_left = { + position = function(text, font, bounds) + local scale = UIResolutionScale() + local text_width = basic_gui.text_width(text, font.material, font:font_size()) + local text_height = font:font_size() --basic_gui.text_height(text, font.material, font:font_size()) + local frame_width = bounds[2] - bounds[1] + local frame_height = bounds[4] - bounds[3] + local left = bounds[1] + local top = bounds[4] + local fix = 2*scale + return {left + fix, top - fix - text_height/2} + end, + }, + top_center = { + position = function(text, font, bounds) + local scale = UIResolutionScale() + local text_width = basic_gui.text_width(text, font.material, font:font_size()) + local text_height = font:font_size() --basic_gui.text_height(text, font.material, font:font_size()) + local frame_width = bounds[2] - bounds[1] + local frame_height = bounds[4] - bounds[3] + local left = bounds[1] + local top = bounds[4] + local fix = 2*scale + return {left + frame_width/2 - text_width/2, top - fix - text_height/2} + end, + }, + top_right = { + position = function(text, font, bounds) + local scale = UIResolutionScale() + local text_width = basic_gui.text_width(text, font.material, font:font_size()) + local text_height = font:font_size() --basic_gui.text_height(text, font.material, font:font_size()) + local frame_width = bounds[2] - bounds[1] + local frame_height = bounds[4] - bounds[3] + local right = bounds[2] + local bottom = bounds[3] + local fix = 2*scale + return {right - fix - text_width, bottom - fix + frame_height - (text_height/2)} + end, + }, +} + +-- ################################################################################################################ +-- ##### widgets ################################################################################################# +-- ################################################################################################################ +gui.widgets = { + + window = { + name = "", + position = {0, 0}, + size = {0, 0}, + original_size = {0, 0}, + initialized = false, + hovered = false, + cursor = {0, 0}, + dragging = false, + drag_offset = {0, 0}, + resizing = false, + resize_offset = {0, 0}, + resize_origin = {0, 0}, + z_order = 0, + widgets = {}, + visible = true, + transparent = false, + + -- ################################################################################################################ + -- ##### Init ##################################################################################################### + -- ################################################################################################################ + --[[ + Set value + --]] + set = function(self, attribute, value) + self[attribute] = value + end, + --[[ + Refresh theme + --]] + refresh_theme = function(self) + self.theme = {} + -- Default + local theme_element = gui.themes[gui.theme].default + if theme_element then self:copy_theme_element(theme_element) end + -- Specific + local theme_element = gui.themes[gui.theme]["window"] + if theme_element then self:copy_theme_element(theme_element) end + end, + --[[ + Copy theme element + --]] + copy_theme_element = function(self, theme_element) + -- Go through elements + for key, element in pairs(theme_element) do + -- Set element + self.theme[key] = element + end + end, + + -- ################################################################################################################ + -- ##### Create widgets ########################################################################################## + -- ################################################################################################################ + --[[ + Create title bar + --]] + create_title = function(self, name, text, height) + -- Base widget + local widget = self:create_widget(name, nil, nil, "title") + -- Set attributes + widget:set("text", text or "") + widget:set("height", height or gui.themes[gui.theme].title.height) + -- Add widget + self:add_widget(widget) + return widget + end, + --[[ + Create button + --]] + create_button = function(self, name, position, size, text, on_click, anchor) + -- Base widget + local widget = self:create_widget(name, position, size, "button", anchor) + -- Set attributes + widget:set("text", text or "") + if on_click then + widget:set("on_click", on_click) + end + -- Add widget + self:add_widget(widget) + return widget + end, + --[[ + Create resizer + --]] + --[[create_resizer = function(self, name, size) + -- Base widget + local widget = self:create_widget(name, nil, size, "resizer") + -- Set attributes + widget:set("size", size or gui.themes[gui.theme].resizer.size) + -- Add widget + self:add_widget(widget) + return widget + end,--]] + --[[ + Create close button + --]] + create_close_button = function(self, name) + local widget = self:create_widget(name, {5, 0}, {25, 25}, "close_button", gui.anchor.styles.top_right) + widget:set("text", "X") + self:add_widget(widget) + return widget + end, + --[[ + Create textbox + --]] + create_textbox = function(self, name, position, size, text, placeholder, on_text_changed) + local widget = self:create_widget(name, position, size, "textbox") + widget:set("text", text or "") + widget:set("placeholder", placeholder or "") + if on_text_changed then + widget:set("on_text_changed", on_text_changed) + end + self:add_widget(widget) + return widget + end, + --[[ + Create checkbox + --]] + create_checkbox = function(self, name, position, size, text, value, on_value_changed) + local widget = self:create_widget(name, position, size, "checkbox") + widget:set("text", text or "") + widget:set("value", value or false) + if on_value_changed then + widget:set("on_value_changed", on_value_changed) + end + self:add_widget(widget) + return widget + end, + --[[ + Create label + --]] + create_label = function(self, name, position, size, text) + local widget = self:create_widget(name, position, size, "label") + widget:set("text", text or "") + self:add_widget(widget) + return widget + end, + --[[ + Create widget + --]] + create_dropdown = function(self, name, position, size, options, selected_index, on_index_changed, show_items_num) + local widget = self:create_widget(name, position, size, "dropdown") + --local widget.widgets = {} + widget:set("text", "") + widget:set("options", {}) + widget:set("index", selected_index) + --table.sort(options) + gui:pcall(function() + for text, index in pairs(options) do + local sub_widget = self:create_dropdown_item(name, index, widget, text) + -- gui:echo("--") + -- gui:echo(tostring(index)) + widget.options[#widget.options+1] = sub_widget + end + end) + widget:set("show_items_num", show_items_num or 2) + if on_index_changed then + widget:set("on_index_changed", on_index_changed) + end + self:add_widget(widget) + return widget + end, + create_dropdown_item = function(self, name, index, parent, text) + local widget = self:create_widget(name.."_option_"..text, {0, 0}, {0, 0}, "dropdown_item") + widget:set("text", text or "") + widget:set("index", index) + widget:set("parent", parent) + widget:set("anchor", nil) + widget:set("z_order", 1) + return widget + end, + --[[ + Create widget + --]] + create_widget = function(self, name, position, size, _type, anchor) + + + position = gui.adjust_to_fit_scale(position) + size = gui.adjust_to_fit_scale(size) + + -- Create widget + local widget = table.clone(gui.widgets.widget) + widget.name = name or "name" + widget.position = position or {0, 0} + widget.offset = widget.position + widget.size = size or {0, 0} + widget._type = _type or "button" + widget.window = self + -- Anchor + widget.anchor = anchor or "bottom_left" + -- Setup functions and theme + widget:setup() + + return widget + end, + --[[ + Add widget to list + --]] + add_widget = function(self, widget) + self:inc_z_orders(#self.widgets) + widget.z_order = 1 + self.widgets[#self.widgets+1] = widget + end, + + -- ################################################################################################################ + -- ##### Methods ################################################################################################## + -- ################################################################################################################ + --[[ + Initialize window + --]] + init = function(self) + -- Event + self:on_init() + + -- Theme + self:refresh_theme() + + -- Init widgets + if #self.widgets > 0 then + for _, widget in pairs(self.widgets) do + widget:init() + end + end + + self:update() + + self.initialized = true + end, + --[[ + Bring window to front + --]] + bring_to_front = function(self) + if not self:has_focus() then + gui.windows:unfocus() + gui.windows:inc_z_orders(self.z_order) + self.z_order = 1 + end + end, + --[[ + Destroy window + --]] + destroy = function(self) + self:before_destroy() + gui.windows:dec_z_orders(self.z_order) + table.remove(gui.windows.list, self:window_index()) + end, + --[[ + Increase z orders + --]] + inc_z_orders = function(self, changed_z) + for z=changed_z, 1, -1 do + for _, widget in pairs(self.widgets) do + if widget.z_order == z then + widget.z_order = widget.z_order + 1 + end + end + end + end, + --[[ + Decrease z orders + --]] + dec_z_orders = function(self, changed_z) + --for z=changed_z, #self.widgets do + for z=changed_z+1, #self.widgets do + for _, widget in pairs(self.widgets) do + if widget.z_order == z then + widget.z_order = widget.z_order - 1 + end + end + end + end, + --[[ + Focus window + --]] + focus = function(self) + self:bring_to_front() + self:on_focus() + end, + --[[ + Unfocus window + --]] + unfocus = function(self) + for _, widget in pairs(self.widgets) do + widget:unfocus() + end + self:on_unfocus() + end, + --[[ + Hover window + --]] + hover = function(self, cursor) + if not self.hovered then self:hover_enter() end + self.cursor = cursor + self:on_hover(cursor) + end, + --[[ + Start hover window + --]] + hover_enter = function(self) + --gui.mouse:un_hover_all(gui.windows.list) + self.hovered = true + self:on_hover_enter() + end, + --[[ + End hover window + --]] + hover_exit = function(self) + self.hovered = false + self:on_hover_exit() + end, + --[[ + Click window + --]] + click = function(self, position) + --self:focus() + local clicked = false + for z=1, #self.widgets do + for _, widget in pairs(self.widgets) do + if widget.z_order == z then + if not gui.point_in_bounds(position, widget:extended_bounds()) then + widget:unfocus() + end + if not clicked and gui.point_in_bounds(position, widget:extended_bounds()) then + widget:click() + clicked = true + end + end + end + end + self:on_click(position) + end, + --[[ + Release click window + --]] + release = function(self, position) + self:focus() + local released = false + for z=1, #self.widgets do + for _, widget in pairs(self.widgets) do + if widget.z_order == z then + if not gui.point_in_bounds(position, widget:extended_bounds()) then + widget:unfocus() + end + if not released and gui.point_in_bounds(position, widget:extended_bounds()) then + widget:release() + released = true + end + end + end + end + self:on_release(position) + end, + + -- ################################################################################################################ + -- ##### Events ################################################################################################### + -- ################################################################################################################ + --[[ + Window is initialized + --]] + on_init = function(self) + end, + --[[ + Window gets focus + --]] + on_focus = function(self) + end, + --[[ + Window loses focus + --]] + on_unfocus = function(self) + end, + --[[ + Window is hovered + --]] + on_hover = function(self, cursor) + end, + --[[ + Window starts being hovered + --]] + on_hover_enter = function(self) + end, + --[[ + Window ends being hovered + --]] + on_hover_exit = function(self) + end, + --[[ + Window is clicked + --]] + on_click = function(self, position) + end, + --[[ + Window click released + --]] + on_release = function(self, position) + end, + --[[ + Before window is updated + --]] + before_update = function(self) + end, + --[[ + Window was updated + --]] + after_update = function(self) + end, + --[[ + Window is dragged + --]] + on_dragged = function(self) + end, + --[[ + Window is resized + --]] + on_resize = function(self) + end, + --[[ + Before window is destroyed + --]] + before_destroy = function(self) + end, + + -- ################################################################################################################ + -- ##### Attributes ############################################################################################### + -- ################################################################################################################ + --[[ + Check if window has focus + --]] + has_focus = function(self) + return self.z_order == 1 + end, + --[[ + Get window index + --]] + window_index = function(self) + for i=1, #gui.windows.list do + if gui.windows.list[i] == self then return i end + end + return 0 + end, + --[[ + Window bounds + --]] + bounds = function(self) + return gui.to_bounds(self.position, self.size) + end, + extended_bounds = function(self) + local bounds = self:bounds() + for _, widget in pairs(self.widgets) do + local cbounds = widget:extended_bounds() + if cbounds[1] < bounds[1] then bounds[1] = cbounds[1] end + if cbounds[2] > bounds[2] then bounds[2] = cbounds[2] end + if cbounds[3] < bounds[3] then bounds[3] = cbounds[3] end + if cbounds[4] > bounds[4] then bounds[4] = cbounds[4] end + end + return bounds + end, + widget_bounds = function(self, exclude_resizer) + local bounds = {} + + for _, widget in pairs(self.widgets) do + if exclude_resizer and widget._type == "resizer" then + return {0, 0, 0, 0} + else + local cbounds = widget:extended_bounds() + if not bounds[1] or cbounds[1] < bounds[1] then bounds[1] = cbounds[1] end + if not bounds[2] or cbounds[2] > bounds[2] then bounds[2] = cbounds[2] end + if not bounds[3] or cbounds[3] < bounds[3] then bounds[3] = cbounds[3] end + if not bounds[4] or cbounds[4] > bounds[4] then bounds[4] = cbounds[4] end + end + end + return bounds + end, + --[[ + Z position + --]] + position_z = function(self) + return 800 + (#gui.windows.list - self.z_order) + end, + --[[ + Get widget by name + --]] + get_widget = function(self, name) + for _, widget in pairs(self.widgets) do + if widget.name == name then + return widget + end + end + return nil + end, + + -- ################################################################################################################ + -- ##### Cycle #################################################################################################### + -- ################################################################################################################ + --[[ + Update window + --]] + update = function(self) + if self.initialized then + self:before_update() + if self:has_focus() then + -- Get cursor position + local cursor = gui.mouse.cursor() + -- Drag + self:drag(cursor) + -- Resize + self:resize(cursor) + -- Update widgets + self:update_widgets() + end + self:after_update() + end + end, + --[[ + Resize window + --]] + drag = function(self, cursor) + if self.dragging then + self.position = {cursor[1] - self.drag_offset[1], cursor[2] - self.drag_offset[2]} + self:on_dragged() + end + end, + --[[ + Resize window + --]] + resize = function(self, cursor) + if self.resizing then + local new_size = { + cursor[1] - self.resize_origin[1] + self.resize_offset[1], + self.resize_origin[2] - cursor[2] + self.resize_offset[2], + } + if new_size[1] < self.original_size[1] then new_size[1] = self.original_size[1] end + if new_size[2] < self.original_size[2] then new_size[2] = self.original_size[2] end + self.size = new_size + local widget_bounds = self:widget_bounds(true) + if self.size[1] < widget_bounds[2] - widget_bounds[1] then + self.size[1] = widget_bounds[2] - widget_bounds[1] + end + if self.size[2] < widget_bounds[4] - widget_bounds[3] then + self.size[2] = widget_bounds[4] - widget_bounds[3] + end + self.position = {self.position[1], self.resize_origin[2] - new_size[2]} + self:on_resize() + end + end, + --[[ + Update widgets + --]] + update_widgets = function(self) + if #self.widgets > 0 then + local catched = false + for z=1, #self.widgets do + for _, widget in pairs(self.widgets) do + if widget.z_order == z and not catched then + catched = widget:update() + end + end + end + end + end, + + -- ################################################################################################################ + -- ##### Render ################################################################################################### + -- ################################################################################################################ + render = function(self) + if not self.visible then return end + self:render_shadow() + self:render_background() + self:render_widgets() + end, + --[[ + Render window + --]] + render_background = function(self) + if not self.visible or self.transparent then return end + local color = ColorHelper.unbox(self.theme.color) + if self.hovered then + color = ColorHelper.unbox(self.theme.color_hover) + end + basic_gui.rect(self.position[1], self.position[2], self:position_z(), self.size[1], self.size[2], color) + end, + --[[ + Render shadow + --]] + render_shadow = function(self) + if not self.visible then return end + -- Theme + local layers = self.theme.shadow.layers + local border = self.theme.shadow.border + local cv = self.theme.shadow.color + -- Render + for i=1, layers do + local color = Color((cv[1]/layers)*i, cv[2], cv[3], cv[4]) + local layer = layers-i + basic_gui.rect(self.position[1]+layer-border, self.position[2]-layer-border, self:position_z(), + self.size[1]-layer*2+border*2, self.size[2]+layer*2+border*2, color) + end + for i=1, layers do + local color = Color((cv[1]/layers)*i, cv[2], cv[3], cv[4]) + local layer = layers-i + basic_gui.rect(self.position[1]-layer-border, self.position[2]+layer-border, self:position_z(), + self.size[1]+layer*2+border*2, self.size[2]-layer*2+border*2, color) + end + end, + --[[ + Render widgets + --]] + render_widgets = function(self) + if not self.visible then return end + if #self.widgets > 0 then + for z=#self.widgets, 1, -1 do + for _, widget in pairs(self.widgets) do + if widget.z_order == z and widget.visible then + widget:render() + end + end + end + end + end, + + }, + + widget = { + name = "", + position = {0, 0}, + size = {0, 0}, + _type = "", + anchor = "", + hovered = false, + cursor = {0, 0}, + --colors = {}, + z_order = 0, + visible = true, + theme = {}, + + -- ################################################################################################################ + -- ##### widget Methods ########################################################################################## + -- ################################################################################################################ + --[[ + Initialize + --]] + init = function(self) + -- Trigger update + self:update() + -- Trigger event + self:on_init() + end, + --[[ + Click + --]] + click = function(self) + -- Disabled + if self.disabled then return end + -- Click + self.clicked = true + end, + --[[ + Release + --]] + release = function(self) + -- Disabled + if self.disabled then return end + -- Clicked + if not self.clicked then return end + -- Release + self.clicked = false + -- Trigger event + self:on_click() + end, + --[[ + Focus + --]] + focus = function(self) + -- Disabled + if self.disabled then return end + -- Focus + self.has_focus = true + end, + --[[ + Unfocus + --]] + unfocus = function(self) + -- Unfocus + self.hovered = false + self.clicked = false + self.has_focus = false + end, + -- ################################################################################################################ + -- ##### Init ##################################################################################################### + -- ################################################################################################################ + --[[ + Set value + --]] + set = function(self, attribute, value) + self[attribute] = value + end, + --[[ + Refresh theme + --]] + refresh_theme = function(self) + self.theme = {} + -- Default + local theme_element = gui.themes[gui.theme].default + if theme_element then self:copy_theme_element(theme_element) end + -- Specific + local theme_element = gui.themes[gui.theme][self._type] + if theme_element then self:copy_theme_element(theme_element) end + end, + --[[ + Copy theme element + --]] + copy_theme_element = function(self, theme_element) + -- Go through elements + for key, element in pairs(theme_element) do + -- Set element + self.theme[key] = element + end + end, + --[[ + Setup widget + --]] + setup = function(self) + -- Copy widget specific functions + local widget_element = gui.widgets[self._type] + if widget_element then self:copy_widget_element(widget_element) end + -- Refresh theme + self:refresh_theme() + end, + --[[ + Copy widget element + --]] + copy_widget_element = function(self, widget_element) + -- Go through elements + for key, element in pairs(widget_element) do + if type(element) == "function" then + -- If function save callback to original function + if self[key] then self[key.."_base"] = self[key] end + self[key] = element + else + -- Set element + self[key] = element + end + end + end, + -- ################################################################################################################ + -- ##### Cycle #################################################################################################### + -- ################################################################################################################ + --[[ + Update + --]] + update = function(self) + -- Trigger event + self:before_update() + -- Disabled + if self.disabled or not self.visible then return end + -- Mouse position + local cursor = gui.mouse.cursor() + -- Set widget position via anchor + if self.anchor then + self.position, self.size = gui.anchor[self.anchor].position(self.window, self) + end + -- Check hovered + self.hovered, self.cursor = gui.point_in_bounds(cursor, self:extended_bounds()) + if self.hovered then + if self.tooltip then basic_gui.tooltip(self.tooltip) end + self:on_hover() + end + -- Clicked + if self.clicked then + self.clicked = self.hovered + end + -- Trigger event + self:after_update() + -- Return + return self.clicked + end, + -- ################################################################################################################ + -- ##### Render ################################################################################################### + -- ################################################################################################################ + --[[ + Main Render + --]] + render = function(self) + -- Visible + if not self.visible then return end + -- Render shadow + self:render_shadow() + -- Render background + self:render_background() + -- Render text + self:render_text() + end, + --[[ + Render shadow + --]] + render_shadow = function(self) + -- Visible + if not self.visible then return end + -- Shadow set + if self.theme.shadow then + -- Get theme value + local layers = self.theme.shadow.layers + local border = self.theme.shadow.border + local cv = self.theme.shadow.color + -- Render + for i=1, layers do + local layer = layers-i + local color = Color((cv[1]/layers)*i, cv[2], cv[3], cv[4]) + basic_gui.rect(self.position[1]+layer-border, self.position[2]-layer-border, self:position_z(), + self.size[1]-layer*2+border*2, self.size[2]+layer*2+border*2, color) + end + for i=1, layers do + local layer = layers-i + local color = Color((cv[1]/layers)*i, cv[2], cv[3], cv[4]) + basic_gui.rect(self.position[1]-layer-border, self.position[2]+layer-border, self:position_z(), + self.size[1]+layer*2+border*2, self.size[2]-layer*2+border*2, color) + end + end + end, + --[[ + Render background + --]] + render_background = function(self) + -- Visible + if not self.visible then return end + -- Get current theme color + local color = ColorHelper.unbox(self.theme.color) + if self.clicked then + color = ColorHelper.unbox(self.theme.color_clicked) + elseif self.hovered then + color = ColorHelper.unbox(self.theme.color_hover) + end + -- Get bounds + local bounds = self:extended_bounds() + -- Render background rectangle + basic_gui.rect(bounds[1], bounds[4], self:position_z(), bounds[2]-bounds[1], bounds[3]-bounds[4], color) + end, + --[[ + Render text + --]] + render_text = function(self) + -- Visible + if not self.visible then return end + -- Get current theme color + local color = ColorHelper.unbox(self.theme.color_text) + if self.clicked then + color = ColorHelper.unbox(self.theme.color_text_clicked) + elseif self.hovered then + color = ColorHelper.unbox(self.theme.color_text_hover) + end + -- Get text info + local text = self.text or "" + --local font = self.theme.font + local font = gui.fonts:get(self.theme.font) + -- Get text alignment + local position = {self.position[1] + self.size[2]*0.2, self.position[2] + self.size[2]*0.2} + --local align = self.theme.text_alignment + local align = gui.text_alignment[self.theme.text_alignment] + if align then + position = align.position(text, font, self:bounds()) + end + -- Render text + basic_gui.text(text, position[1], position[2], self:position_z()+1, font:font_size(), color, font.font) + end, + -- ################################################################################################################ + -- ##### Attributes ############################################################################################### + -- ################################################################################################################ + --[[ + Bounds + --]] + bounds = function(self) + return gui.to_bounds(self.position, self.size) + end, + extended_bounds = function(self) + return self:bounds() + end, + --[[ + Position Z + --]] + position_z = function(self) + return self.window:position_z() + (#self.window.widgets - self.z_order) + end, + -- ################################################################################################################ + -- ##### Events ################################################################################################### + -- ################################################################################################################ + --[[ + On init + --]] + on_init = function(self) + end, + --[[ + On click + --]] + on_click = function(self) + end, + --[[ + On hover + --]] + on_hover = function(self) + end, + --[[ + Before update + --]] + before_update = function(self) + end, + --[[ + After update + --]] + after_update = function(self) + end, + }, + + title = { + -- ################################################################################################################ + -- ##### widget overrides ######################################################################################## + -- ################################################################################################################ + --[[ + Init override + --]] + init = function(self) + -- Original function + self:init_base() + -- Change + self.height = self.height or gui.themes[gui.theme].title.height + end, + --[[ + Click override + --]] + click = function(self) + -- Disabled + if self.disabled then return end + -- Original function + self:click_base() + -- Drag + self:drag() + end, + -- ################################################################################################################ + -- ##### Cycle overrides ########################################################################################## + -- ################################################################################################################ + --[[ + Update override + --]] + update = function(self) + -- Set bounds + self.size = {self.window.size[1], self.height or gui.themes[gui.theme].title.height} + self.position = {self.window.position[1], self.window.position[2] + self.window.size[2] - self.size[2]} + -- Disabled + if self.disabled then return end + -- Hover + local cursor = gui.mouse.cursor() + self.hovered, self.cursor = gui.point_in_bounds(cursor, self:bounds()) + -- Drag + if self.window.dragging then self:drag() end + -- Return + return self.clicked or self.window.dragging + end, + -- ################################################################################################################ + -- ##### Drag ##################################################################################################### + -- ################################################################################################################ + --[[ + Drag start + --]] + drag_start = function(self) + -- Set offset + self.window.drag_offset = self.window.cursor + -- Dragging + self.window.dragging = true + -- Block input + gui.input:block() + -- Trigger event + self:before_drag() + end, + --[[ + Drag + --]] + drag = function(self) + -- Catch start event + if not self.window.dragging then self:drag_start() end + -- Check mouse button + self.window.dragging = not stingray.Mouse.released(stingray.Mouse.button_id("left")) + -- Drag + self:on_drag() + -- Catch end event + if not self.window.dragging then self:drag_end() end + end, + --[[ + Drag end + --]] + drag_end = function(self) + -- Unblock input + gui.input:unblock() + -- Trigger event + self:after_drag() + end, + -- ################################################################################################################ + -- ##### Events ################################################################################################### + -- ################################################################################################################ + --[[ + On drag start + --]] + before_drag = function(self) + end, + --[[ + On drag + --]] + on_drag = function(self) + end, + --[[ + On drag end + --]] + after_drag = function(self) + end, + }, + + button = {}, + + resizer = { + -- ################################################################################################################ + -- ##### widget overrides ######################################################################################## + -- ################################################################################################################ + --[[ + Init override + --]] + init = function(self) + -- Original function + self:init_base() + -- Change + self.size = self.theme.size + end, + --[[ + Click override + --]] + click = function(self) + -- Disabled + if self.disabled then return end + -- Original function + self:click_base() + -- Resize + self:resize() + end, + -- ################################################################################################################ + -- ##### Resize ################################################################################################### + -- ################################################################################################################ + --[[ + Start resize + --]] + resize_start = function(self) + -- Save offset + self.window.resize_offset = {self.window.size[1] - self.window.cursor[1], self.window.cursor[2]} + self.window.resize_origin = {self.window.position[1], self.window.position[2] + self.window.size[2]} + -- Set resizing + self.window.resizing = true + gui.input:block() + -- Trigger event + self:before_resize() + end, + --[[ + Resize + --]] + resize = function(self) + -- Catch start event + if not self.window.resizing then self:resize_start() end + -- Check mouse button + self.window.resizing = not stingray.Mouse.released(stingray.Mouse.button_id("left")) + -- Trigger event + self:on_resize() + -- Catch end event + if not self.window.resizing then self:resize_end() end + end, + --[[ + Resize end + --]] + resize_end = function(self) + -- Block input + gui.input:unblock() + -- Trigger event + self:after_resize() + end, + -- ################################################################################################################ + -- ##### Cycle overrides ########################################################################################## + -- ################################################################################################################ + --[[ + Update override + --]] + update = function(self) + -- Update position + self.position = {self.window.position[1] + self.window.size[1] - self.size[1] - 5, self.window.position[2] + 5} + -- Disabled + if self.disabled then return end + -- Hover + local cursor = gui.mouse.cursor() + --local bounds = gui.to_bounds({self.position[1], self.position[2]}, self.size) + self.hovered, self.cursor = gui.point_in_bounds(cursor, self:bounds()) + -- Resize + if self.window.resizing then self:resize() end + -- Return + return self.clicked or self.window.resizing + end, + -- ################################################################################################################ + -- ##### Render overrides ######################################################################################### + -- ################################################################################################################ + --[[ + render_background override + --]] + render_background_bak = function(self) + -- if self.window.resizing then + -- local color = ColorHelper.unbox(self.theme.color_clicked) + -- local bounds = self:bounds() + -- basic_gui.rect(bounds[1], bounds[4], self:position_z(), bounds[2]-bounds[1], bounds[3]-bounds[4], color) + -- else + -- self:render_background_base() + -- end + end, + -- ################################################################################################################ + -- ##### Events ################################################################################################### + -- ################################################################################################################ + --[[ + Before resize + --]] + before_resize = function(self) + end, + --[[ + On resize + --]] + on_resize = function(self) + end, + --[[ + After resize + --]] + after_resize = function(self) + end, + }, + + close_button = { + -- ################################################################################################################ + -- ##### widget overrides ######################################################################################## + -- ################################################################################################################ + --[[ + Init override + --]] + init = function(self) + -- Original function + self:init_base() + -- Change + self.size = self.theme.size + end, + -- ################################################################################################################ + -- ##### Events ################################################################################################### + -- ################################################################################################################ + --[[ + OnClick override + --]] + on_click = function(self) + -- Destroy window + self.window:destroy() + end, + }, + + textbox = { + -- ################################################################################################################ + -- ##### widget overrides ######################################################################################## + -- ################################################################################################################ + --[[ + Init override + --]] + init = function(self) + -- Original function + self:init_base() + -- Input cursor timer + self.input_cursor = { + timer = gui.timers:create_timer(self.name .. "_input_cursor_timer", 500, true, self.on_input_cursor_timer, self), + state = true, + position = 0, + } + -- Input timer + self.input = { + --[[timer = gui.timers:create_timer(self.name .. "_input_timer", 100, true, self.on_input_timer, self),--]] + ready = true, + } + end, + --[[ + Release override + --]] + release = function(self) + -- Disabled + if self.disabled then return end + -- Clicked + if self.clicked then self:focus() end + -- Original function + self:release_base() + end, + --[[ + Text changed + --]] + text_changed = function(self) + -- Disable input + --self.input.ready = false + -- Enable input timer + --self.input.timer.enabled = true + -- Trigger event + self:on_text_changed() + end, + --[[ + Focus override + --]] + focus = function(self) + -- Disabled + if self.disabled then return end + -- Block input + if not self.has_focus then + gui.input:block() + end + -- Original function + self:focus_base() + end, + --[[ + Unfocus override + --]] + unfocus = function(self) + -- Disabled + if self.disabled then return end + -- Unblock input + if self.has_focus then + gui.input:unblock() + end + -- Original function + self:unfocus_base() + end, + -- ################################################################################################################ + -- ##### Events ################################################################################################### + -- ################################################################################################################ + --[[ + Cursor timer + --]] + on_input_cursor_timer = function(self, textbox) + -- Toggle cursor state + textbox.input_cursor.state = not textbox.input_cursor.state + end, + --[[ + Input timer + --]] + --[[on_input_timer = function(self, textbox) + -- Disable timer + self.enabled = false + -- Accept input + textbox.input.ready = true + end,--]] + -- ################################################################################################################ + -- ##### Cycle overrides ########################################################################################## + -- ################################################################################################################ + --[[ + Update override + --]] + update = function(self) + -- Disabled + if self.disabled then return end + -- Original function + self:update_base() + -- Input + if self.has_focus then + -- Get input service + Managers.input:device_unblock_service("keyboard", 1, "mod_gui") + local input_service = Managers.input:get_service("mod_gui") + -- Check input and timer + if input_service and self.input.ready then + -- Get keystrokes + local keystrokes = stingray.Keyboard.keystrokes() + -- Check keystrokes + for _, key in pairs(keystrokes) do + print(key) + if type(key) == "string" then + -- If string check if special key + if not table.has_item(ui_special_keys, key) then + -- Oridinary printable character + self.text = self.text .. key + -- Trigger changed + self:text_changed() + elseif key == "space" then + -- Add space + self.text = self.text .. " " + -- Trigger changed + self:text_changed() + end + else + -- If not string it's widget key + if input_service:get("backspace") then + -- Handle backspace - remove last character + if string.len(self.text) >= 1 then + local _, count = string.gsub(self.text, "[^\128-\193]", "") + self.text = UTF8:utf8sub(self.text, 1, count-1) + -- Trigger changed + self:text_changed() + end + elseif input_service:get("esc") or input_service:get("enter") then + -- Unfocus + self:unfocus() + end + end + end + end + end + -- Return + return self.clicked or self.has_focus + end, + -- ################################################################################################################ + -- ##### Render overrides ######################################################################################### + -- ################################################################################################################ + --[[ + Render main override + --]] + render = function(self) + -- Visible + if not self.visible then return end + -- Original function + self:render_base() + -- Cursor + self:render_cursor() + end, + --[[ + Render text override + --]] + render_text = function(self) + -- Visible + if not self.visible then return end + -- Get current theme color + local color = ColorHelper.unbox(self.theme.color_placeholder) + if self.clicked then + color = ColorHelper.unbox(self.theme.color_text_clicked) + elseif self.hovered and #self.text > 0 then + color = ColorHelper.unbox(self.theme.color_text_hover) + elseif #self.text > 0 then + color = ColorHelper.unbox(self.theme.color_text) + end + -- Get text + local text = self.text or "" + if not self.has_focus then + text = #self.text > 0 and self.text or self.placeholder or "" + end + -- Get font + --local font = self.theme.font + local font = gui.fonts:get(self.theme.font) + -- Get text alignment + local position = {self.position[1] + self.size[2]*0.2, self.position[2] + self.size[2]*0.2} + --local align = self.theme.text_alignment + local align = gui.text_alignment[self.theme.text_alignment] + if align then + position = align.position(text, font, self:bounds()) + end + -- Render text + basic_gui.text(text, position[1], position[2], self:position_z()+1, font:font_size(), color, font.font) + end, + --[[ + Render cursor + --]] + render_cursor = function(self) + -- Visible + if not self.visible then return end + -- Render + if self.has_focus and self.input_cursor.state then + -- Get current theme color + local color = ColorHelper.unbox(self.theme.color_input_cursor) + -- Get data + local width = 0 + --local font = self.theme.font + local font = gui.fonts:get(self.theme.font) + if self.text and #self.text > 0 then + width = basic_gui.text_width(self.text, font.material, font:font_size()) + end + -- Render cursor + basic_gui.rect(self.position[1]+2+width, self.position[2]+2, self:position_z(), 2, self.size[2]-4, color) + end + end, + -- ################################################################################################################ + -- ##### Events ################################################################################################### + -- ################################################################################################################ + --[[ + On text changed + --]] + on_text_changed = function(self) + end, + }, + + checkbox = { + -- ################################################################################################################ + -- ##### Methods ################################################################################################## + -- ################################################################################################################ + --[[ + Toggle state + --]] + toggle = function(self) + -- Change + self.value = not self.value + -- Trigger event + self:on_value_changed() + end, + -- ################################################################################################################ + -- ##### widget overrides ######################################################################################## + -- ################################################################################################################ + --[[ + Release override + --]] + release = function(self) + -- Disabled + if self.disabled then return end + -- Original function + self:release_base() + -- Toggle + self:toggle() + end, + -- ################################################################################################################ + -- ##### Render overrides ######################################################################################### + -- ################################################################################################################ + --[[ + Render override + --]] + render = function(self) + -- Visible + if not self.visible then return end + -- Original function + self:render_base() + -- Render box + self:render_box() + end, + --[[ + Render text override + --]] + render_text = function(self) + -- Visible + if not self.visible then return end + -- Get current theme color + local color = ColorHelper.unbox(self.theme.color_text) + if self.clicked then + color = ColorHelper.unbox(self.theme.color_text_clicked) + elseif self.hovered then + color = ColorHelper.unbox(self.theme.color_text_hover) + end + -- Get font + --local font = self.theme.font + local font = gui.fonts:get(self.theme.font) + -- Get text alignment + local position = {self.position[1] + self.size[2] + 5, self.position[2] + self.size[2]*0.2} + -- local align = self.theme.text_alignment + -- if align then + -- position = align.position(self.text, font, self:bounds()) + -- end + -- Render text + basic_gui.text(self.text, position[1], position[2], self:position_z()+1, font:font_size(), color, font.font) + end, + --[[ + Render box + --]] + render_box = function(self) + -- Visible + if not self.visible then return end + -- Check value + if self.value then + -- Get current theme color + local color = ColorHelper.unbox(self.theme.color_text) + if self.clicked then + color = ColorHelper.unbox(self.theme.color_text_clicked) + elseif self.hovered then + color = ColorHelper.unbox(self.theme.color_text_hover) + end + local text = "X" + -- Get font + --local font = self.theme.font + local font = gui.fonts:get(self.theme.font) + -- Get text alignment + local position = {self.position[1] + 5, self.position[2] + self.size[2]*0.2} + --local align = self.theme.text_alignment + local align = gui.text_alignment[self.theme.text_alignment] + if align then + position = align.position(text, font, self:bounds()) + end + -- Render text + basic_gui.text(text, position[1], position[2], self:position_z()+1, font:font_size(), color, font.font) + end + end, + -- ################################################################################################################ + -- ##### Events ################################################################################################### + -- ################################################################################################################ + --[[ + Text changed + --]] + on_value_changed = function(self) + end, + }, + + label = {}, + + dropdown = { + -- ################################################################################################################ + -- ##### Methods ################################################################################################## + -- ################################################################################################################ + --[[ + Select index + --]] + select_index = function(self, index) + -- Check options ( options are dropdown_item widgets ) + if self.options and #self.options >= index then + -- Set index + self.index = index + -- Set text + self:update_text() + -- Trigger event + self:on_index_changed() + end + end, + update_text = function(self) + for _, option in pairs(self.options) do + if option.index == self.index then + self.text = option.text + return + end + end + end, + --[[ + Wrap function calls to options + --]] + wrap_options = function(self, function_name) + local results = {} + -- Go through options + for key, option in pairs(self.options) do + -- Check for function and execute it + if option[function_name] and type(option[function_name]) == "function" then + local result = option[function_name](option) + results[#results+1] = result + end + end + -- Return + return results + end, + -- ################################################################################################################ + -- ##### widget overrides ######################################################################################## + -- ################################################################################################################ + --[[ + Init override + --]] + init = function(self) + -- Original function + self:init_base() + -- Wrap init + self:wrap_options("init") + -- If options select first + if #self.options > 0 and not self.index then self.index = 1 end + self:select_index(self.index) + end, + --[[ + Click override + --]] + click = function(self) + -- Disabled + if self.disabled then return end + -- Original function + self:click_base() + -- Go through options + for key, option in pairs(self.options) do + -- If option hovered + if gui.point_in_bounds(gui.mouse.cursor(), option:extended_bounds()) then + -- Click option + option:click() + end + end + end, + --[[ + Release override + --]] + release = function(self) + -- Disabled + if self.disabled then return end + -- Drop + if self.clicked then self.dropped = not self.dropped end + -- Original function + self:release_base() + -- Go through options + for key, option in pairs(self.options) do + -- If option hovered + if gui.point_in_bounds(gui.mouse.cursor(), option:extended_bounds()) then + -- Release + option:release() + end + end + end, + --[[ + Unfocus override + --]] + unfocus = function(self) + -- Original function + self:unfocus_base() + -- Drop off + self.dropped = false + -- Wrap + self:wrap_options("unfocus") + end, + -- ################################################################################################################ + -- ##### Cycle overrides ########################################################################################## + -- ################################################################################################################ + --[[ + Update override + --]] + update = function(self) + -- Disabled + if self.disabled then return end + -- Original function + self:update_base() + -- If dropped + if self.dropped then + -- Get some shit + local scale = UIResolutionScale() + local border = 2*scale + local x = self.position[1] --+ border + local y = self.position[2] - border -- self.size[2] - border + -- Go through options + --table.sort(self.options) + --for i = 1, #self.options do + --for i = #self.options, 1, -1 do + -- gui:echo("--") + for _, option in pairs(self.options) do + --if option.param == i then + -- Update position + -- gui:echo(tostring(option.param)) + option:set("position", {x, y - (self.size[2] * option.index)}) + option:set("size", self.size) + option:set("visible", true) + --y = y - self.size[2] --- border + --break + --end + end + --end + -- Wrap + self:wrap_options("update") + end + -- Return + return self.clicked or self.dropped + end, + -- ################################################################################################################ + -- ##### Render overrides ######################################################################################### + -- ################################################################################################################ + --[[ + Render main override + --]] + render = function(self) + -- Visible + if not self.visible then return end + -- Original function + self:render_base() + -- If dropped + if self.dropped then + -- Wrap + self:wrap_options("render") + end + end, + -- ################################################################################################################ + -- ##### Attribute overrides ###################################################################################### + -- ################################################################################################################ + --[[ + Bounds override + --]] + extended_bounds = function(self) + local bounds = self:bounds() + -- If dropped + if self.dropped then + -- Change bounds to reflect dropped size + bounds[3] = bounds[3] - (self.size[2]*#self.options) + --bounds[4] = bounds[4] --+ self.size[2] + (self.size[2]*#self.options) + end + -- Return + return bounds + end, + -- ################################################################################################################ + -- ##### Events ################################################################################################### + -- ################################################################################################################ + --[[ + Index changed + --]] + on_index_changed = function(self) + end, + }, + + dropdown_item = { + -- ################################################################################################################ + -- ##### widget overrides ######################################################################################## + -- ################################################################################################################ + --[[ + Release override + --]] + release = function(self) + -- Disabled + if self.disabled then return end + -- Original function + self:release_base() + -- Change + if self.parent and self.index then + self.parent:select_index(self.index) + end + end, + -- ################################################################################################################ + -- ##### Attribute overrides ###################################################################################### + -- ################################################################################################################ + --[[ + Position Z override + --]] + position_z = function(self) + -- Return parent position z + 1 + return self.parent:position_z()+1 + end, + }, + +} + +-- ################################################################################################################ +-- ##### Themes ################################################################################################### +-- ################################################################################################################ +gui.themes = { + -- Define a "default" theme element with common values for every widget + -- Define specific elements with a widget name to overwrite default settings + -- Default theme + default = { + -- default theme element + default = { + color = ColorHelper.box(200, 50, 50, 50), + color_hover = ColorHelper.box(200, 60, 60, 60), + color_clicked = ColorHelper.box(200, 90, 90, 90), + color_text = ColorHelper.box(100, 255, 255, 255), + color_text_hover = ColorHelper.box(200, 255, 168, 0), + color_text_clicked = ColorHelper.box(255, 255, 180, 0), + text_alignment = "middle_center", + shadow = { + layers = 5, + border = 0, + color = {20, 10, 10, 10}, + }, + font = "hell_shark", + }, + -- Overwrites and additions + window = { + color = ColorHelper.box(200, 30, 30, 30), -- + color_hover = ColorHelper.box(255, 35, 35, 35), -- + shadow = { + layers = 0, + border = 0, + color = {0, 255, 255, 255}, + }, + }, + title = { + height = 20, + color = ColorHelper.box(255, 40, 40, 40), + color_hover = ColorHelper.box(255, 50, 50, 50), + color_clicked = ColorHelper.box(255, 60, 60, 60), + color_text = ColorHelper.box(200, 255, 255, 255), + color_text_hover = ColorHelper.box(200, 255, 168, 0), + color_text_clicked = ColorHelper.box(255, 255, 180, 0), + shadow = { + layers = 5, + border = 0, + color = {20, 10, 10, 10}, + }, + }, + button = {}, + resizer = { + size = {20, 20}, + }, + close_button = { + size = {25, 25}, + }, + textbox = { + color_placeholder = ColorHelper.box(50, 255, 255, 255), + color_input_cursor = ColorHelper.box(100, 255, 255, 255), + text_alignment = "middle_left", + }, + checkbox = {}, + dropdown = { + draw_items_num = 5, + }, + dropdown_item = { + color_hover = ColorHelper.box(255, 60, 60, 60), + color_clicked = ColorHelper.box(255, 90, 90, 90), + }, + label = { + color = ColorHelper.box(0, 0, 0, 0), + color_hover = ColorHelper.box(0, 0, 0, 0), + color_clicked = ColorHelper.box(0, 0, 0, 0), + color_text = ColorHelper.box(200, 255, 255, 255), + color_text_hover = ColorHelper.box(200, 255, 255, 255), + color_text_clicked = ColorHelper.box(200, 255, 255, 255), + shadow = { + layers = 0, + border = 0, + color = {20, 10, 10, 10}, + }, + } + }, +} + +-- ################################################################################################################ +-- ##### Default stuff ############################################################################################ +-- ################################################################################################################ +gui.fonts:create("default", "hell_shark", 22) +gui.fonts:create("hell_shark", "hell_shark", 22, nil, true) + diff --git a/vmf_source/scripts/mods/vmf/vmf_loader.lua b/vmf_source/scripts/mods/vmf/vmf_loader.lua index d5e393c..4d0fc66 100644 --- a/vmf_source/scripts/mods/vmf/vmf_loader.lua +++ b/vmf_source/scripts/mods/vmf/vmf_loader.lua @@ -21,6 +21,9 @@ return { dofile("scripts/mods/vmf/modules/options_menu/vmf_options_view") dofile("scripts/mods/vmf/modules/vmf_options") + dofile("scripts/mods/vmf/modules/mod_gui/basic_gui") + dofile("scripts/mods/vmf/modules/mod_gui/gui") + dofile("scripts/mods/vmf/modules/mutators/mutators") object.vmf = get_mod("VMF") From 0d417798d8fcf0ee9f53b7eec27e218a81acd3eb Mon Sep 17 00:00:00 2001 From: UnShame Date: Tue, 20 Feb 2018 16:45:31 +0300 Subject: [PATCH 07/32] mutators: add prototype gui, add auto disabling of mutators --- .../vmf/modules/mutators/default_config.lua | 4 +- .../scripts/mods/vmf/modules/mutators/gui.lua | 176 ++++++++++++++++++ .../mods/vmf/modules/mutators/info.lua | 19 +- .../mods/vmf/modules/mutators/mutators.lua | 145 ++++++++++----- 4 files changed, 298 insertions(+), 46 deletions(-) create mode 100644 vmf_source/scripts/mods/vmf/modules/mutators/gui.lua diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/default_config.lua b/vmf_source/scripts/mods/vmf/modules/mutators/default_config.lua index 6f07938..4c4d85c 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/default_config.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/default_config.lua @@ -5,8 +5,8 @@ return { bonus = 0 }, server_name = "", - title = "default_title", - --short_title = "", + title = "", + short_title = "", difficulties = { "easy", "normal", diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/gui.lua b/vmf_source/scripts/mods/vmf/modules/mutators/gui.lua new file mode 100644 index 0000000..9bfe2cd --- /dev/null +++ b/vmf_source/scripts/mods/vmf/modules/mutators/gui.lua @@ -0,0 +1,176 @@ +local vmf = get_mod("VMF") +local mutators = vmf.mutators + + +local window = nil +local button = nil +local window_opened = false + +local function update_window_visibility(map_view) + if not window then return end + button.visible = map_view and map_view.friends and not map_view.friends:is_active() + window.visible = window_opened and button.visible +end + +local function destroy_window(map_view) + if window then + window:destroy() + window = nil + button:destroy() + map_view.ui_scenegraph.settings_button.position[1] = map_view.ui_scenegraph.settings_button.position[1] - 50 + map_view.ui_scenegraph.friends_button.position[1] = map_view.ui_scenegraph.friends_button.position[1] - 50 + map_view.ui_scenegraph.lobby_button.position[1] = map_view.ui_scenegraph.lobby_button.position[1] - 50 + end +end + +local function create_window(map_view) + destroy_window(map_view) + + vmf.sort_mutators() + vmf.disable_impossible_mutators() + + local window_size = {0, 0} + local window_position = {50, 500} + + window = get_mod("gui").create_window("mutators_window", window_position, window_size) + + for i, mutator in ipairs(mutators) do + window:create_checkbox("label" .. mutator:get_name(), {10, 40 * i}, {30, 30}, mutator:get_config().title, mutator:is_enabled(), function() + if not mutator:is_enabled() and mutator:can_be_enabled() then + mutator:enable() + elseif mutator:is_enabled() then + mutator:disable() + else + create_window(map_view) + end + end) + end + + window:init() + + button = get_mod("gui").create_window("mutators_button", window_position, window_size) + button:create_button("mutators", {55, -75}, {65, 65}, "Mut", function() + window_opened = not window_opened + end) + button:init() + + map_view.ui_scenegraph.settings_button.position[1] = map_view.ui_scenegraph.settings_button.position[1] + 50 + map_view.ui_scenegraph.friends_button.position[1] = map_view.ui_scenegraph.friends_button.position[1] + 50 + map_view.ui_scenegraph.lobby_button.position[1] = map_view.ui_scenegraph.lobby_button.position[1] + 50 + + update_window_visibility(map_view) +end + +vmf:hook("MapView.on_enter", function(func, self, ...) + func(self, ...) + print("on_enter") + vmf:pcall(function() create_window(self) end) +end) + +vmf:hook("MapView.on_level_index_changed", function(func, self, ...) + func(self, ...) + print("on_level_index_changed") + vmf:pcall(function() create_window(self) end) +end) + +vmf:hook("MapView.on_difficulty_index_changed", function(func, self, ...) + func(self, ...) + print("on_difficulty_index_changed") + vmf:pcall(function() create_window(self) end) +end) + +vmf:hook("MapView.set_difficulty_stepper_index", function(func, self, ...) + func(self, ...) + print("set_difficulty_stepper_index") + vmf:pcall(function() create_window(self) end) +end) + +vmf:hook("MapView.on_exit", function(func, self, ...) + func(self, ...) + print("on_exit") + vmf:pcall(function() destroy_window(self) end) + window_opened = false +end) + +vmf:hook("MapView.suspend", function(func, self, ...) + func(self, ...) + print("suspend") + vmf:pcall(function() destroy_window(self) end) +end) + +vmf:hook("MapView.update", function(func, self, dt, t) + func(self, dt, t) + vmf:pcall(function() update_window_visibility(self) end) +end) + +vmf:hook("MapView.draw", function(func, self, input_service, gamepad_active, dt) + local ui_renderer = self.ui_renderer + local ui_scenegraph = self.ui_scenegraph + + UIRenderer.begin_pass(ui_renderer, ui_scenegraph, input_service, dt, nil, self.render_settings) + + for _, widget in ipairs(self.background_widgets) do + UIRenderer.draw_widget(ui_renderer, widget) + end + + local number_of_player = self.number_of_player or 0 + + for i = 1, number_of_player, 1 do + local widget = self.player_list_widgets[i] + + UIRenderer.draw_widget(ui_renderer, widget) + end + + if not window_opened then + if self.settings_button_widget.content.toggled then + for widget_name, widget in pairs(self.advanced_settings_widgets) do + UIRenderer.draw_widget(ui_renderer, widget) + end + else + for widget_name, widget in pairs(self.normal_settings_widgets) do + UIRenderer.draw_widget(ui_renderer, widget) + end + end + end + + UIRenderer.draw_widget(ui_renderer, self.player_list_conuter_text_widget) + UIRenderer.draw_widget(ui_renderer, self.description_field_widget) + UIRenderer.draw_widget(ui_renderer, self.title_text_widget) + UIRenderer.draw_widget(ui_renderer, self.game_mode_selection_bar_widget) + UIRenderer.draw_widget(ui_renderer, self.game_mode_selection_bar_bg_widget) + UIRenderer.draw_widget(ui_renderer, self.private_checkbox_widget) + + if not gamepad_active then + UIRenderer.draw_widget(ui_renderer, self.friends_button_widget) + UIRenderer.draw_widget(ui_renderer, self.settings_button_widget) + UIRenderer.draw_widget(ui_renderer, self.confirm_button_widget) + UIRenderer.draw_widget(ui_renderer, self.cancel_button_widget) + UIRenderer.draw_widget(ui_renderer, self.lobby_button_widget) + + if not self.confirm_button_widget.content.button_hotspot.disabled then + UIRenderer.draw_widget(ui_renderer, self.button_eye_glow_widget) + else + UIRenderer.draw_widget(ui_renderer, self.confirm_button_disabled_tooltip_widget) + end + else + UIRenderer.draw_widget(ui_renderer, self.background_overlay_console_widget) + UIRenderer.draw_widget(ui_renderer, self.gamepad_button_selection_widget) + end + + local draw_intro_description = self.draw_intro_description + + if draw_intro_description then + for key, text_widget in pairs(self.description_text_widgets) do + UIRenderer.draw_widget(ui_renderer, text_widget) + end + end + + UIRenderer.end_pass(ui_renderer) + + local friends_menu_active = self.friends:is_active() + + if gamepad_active and not friends_menu_active and not self.popup_id and not draw_intro_description then + self.menu_input_description:draw(ui_renderer, dt) + end + +end) diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/info.lua b/vmf_source/scripts/mods/vmf/modules/mutators/info.lua index 77c657d..c938fb0 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/info.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/info.lua @@ -1,12 +1,19 @@ local vmf = get_mod("VMF") local mutators = vmf.mutators +local were_enabled_before = false + local function get_enabled_mutators_names(short) - local name = "" + local name = nil for _, mutator in ipairs(mutators) do local config = mutator:get_config() if mutator:is_enabled() then - name = name .. " " .. (short and config.short_title or config.title) + local added_name = (short and config.short_title or config.title or mutator:get_name()) + if name then + name = name .. " " .. added_name + else + name = added_name + end end end return name @@ -63,6 +70,14 @@ end) vmf:hook("MatchmakingStateHostGame.host_game", function(func, self, ...) func(self, ...) set_lobby_data() + local names = get_enabled_mutators_names() + if names then + Managers.chat:send_system_chat_message(1, "ENABLED MUTATORS: " .. names, 0, true) + were_enabled_before = true + elseif were_enabled_before then + Managers.chat:send_system_chat_message(1, "ALL MUTATORS DISABLED", 0, true) + were_enabled_before = false + end end) vmf:hook("MatchmakingManager.rpc_matchmaking_request_join_lobby", function(func, self, sender, client_cookie, host_cookie, lobby_id, friend_join) diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutators.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutators.lua index e0ad34a..d9b03fd 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutators.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutators.lua @@ -3,8 +3,8 @@ local vmf = get_mod("VMF") -- List of mods that are also mutators in order in which they should be enabled -- This is populated via VMFMod.register_as_mutator -local mutators = {} -vmf.mutators = mutators +vmf.mutators = {} +local mutators = vmf.mutators local mutators_config = {} local default_config = dofile("scripts/mods/vmf/modules/mutators/default_config") @@ -25,31 +25,13 @@ local mutators_sorted = false --[[ - PRIVATE METHODS + PUBLIC METHODS ]]-- -local addDice, removeDice = dofile("scripts/mods/vmf/modules/mutators/dice") -local set_lobby_data = dofile("scripts/mods/vmf/modules/mutators/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 - vmf: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 - -- Sorts mutators in order they should be enabled -local function sort_mutators() +vmf.sort_mutators = function() + + if mutators_sorted then return end -- LOG -- vmf:dump(mutators_sequence, "seq", 5) @@ -105,16 +87,48 @@ local function sort_mutators() -- /LOG -- end -local function mutator_can_be_enabled(mutator) - return (not Managers.state or not Managers.state.difficulty:get_difficulty()) or table.has_item(mutator:get_config().difficulties, Managers.state.difficulty:get_difficulty()) +-- Disables mutators that cannot be enabled right now +vmf.disable_impossible_mutators = function() + for _, mutator in pairs(mutators) do + if mutator:is_enabled() and not mutator:can_be_enabled() then + mutator:disable() + end + end end + +--[[ + PRIVATE METHODS +]]-- + +local addDice, removeDice = dofile("scripts/mods/vmf/modules/mutators/dice") +local set_lobby_data = dofile("scripts/mods/vmf/modules/mutators/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 + vmf: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 + +-- Called after mutator is enabled local function on_enabled(mutator) local config = mutator:get_config() addDice(config.dice) set_lobby_data() end +-- Called after mutator is disabled local function on_disabled(mutator) local config = mutator:get_config() removeDice(config.dice) @@ -130,14 +144,17 @@ local function set_mutator_state(mutator, state) return end - if state and not mutator_can_be_enabled(mutator) then - mutator:error("Can't enable mutator - incorrect difficulty") + 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 - sort_mutators() + vmf.sort_mutators() end local disabled_mutators = {} @@ -179,6 +196,11 @@ local function set_mutator_state(mutator, state) print("---------") end + +--[[ + MUTATOR'S OWN METHODS +]]-- + -- Enables mutator (pcall for now) local function enable_mutator(self) vmf:pcall(function() set_mutator_state(self, true) end) @@ -189,15 +211,35 @@ local function disable_mutator(self) vmf:pcall(function() set_mutator_state(self, false) end) end +-- Checks current difficulty and map selection screen settings to determine if a mutator can be enabled +local function can_be_enabled(self) + + local mutator_difficulties = self:get_config().difficulties + + local actual_difficulty = Managers.state and Managers.state.difficulty:get_difficulty() + local right_difficulty = not actual_difficulty or table.has_item(mutator_difficulties, actual_difficulty) + + local ingame_ui = Managers.matchmaking and Managers.matchmaking.ingame_ui + local map_view = ingame_ui and ingame_ui.views and ingame_ui.views.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_difficulties, 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 - ---[[ - PUBLIC METHODS -]]-- - -- Turns a mod into a mutator VMFMod.register_as_mutator = function(self, config) if not config then config = {} end @@ -220,6 +262,7 @@ VMFMod.register_as_mutator = function(self, config) 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) @@ -233,6 +276,7 @@ VMFMod.register_as_mutator = function(self, config) self.enable = enable_mutator self.disable = disable_mutator + self.can_be_enabled = can_be_enabled self.get_config = get_config @@ -242,19 +286,21 @@ VMFMod.register_as_mutator = function(self, config) self:init_state(false) end + --[[ HOOKS ]]-- vmf:hook("DifficultyManager.set_difficulty", function(func, self, difficulty) - for _, mutator in ipairs(mutators) do - if mutator:is_enabled() and not mutator_can_be_enabled(mutator:get_config()) then - mutator:disable() - end - end + vmf.disable_impossible_mutators() return func(self, difficulty) end) +--[[ + GUI +]]-- +dofile("scripts/mods/vmf/modules/mutators/gui") + @@ -283,7 +329,8 @@ local mutator_whatever = new_mod("mutator_whatever") mutator555:register_as_mutator({ enable_after_these = { "mutation" - } + }, + title = "mutator555" }) mutator555:create_options({}, true, "mutator555", "mutator555 description") mutator555.on_enabled = function() end @@ -298,7 +345,8 @@ deathwish:register_as_mutator({ }, difficulties = { "hardest" - } + }, + title = "deathwish" }) deathwish:create_options({}, true, "deathwish", "deathwish description") deathwish.on_enabled = function() @@ -312,6 +360,11 @@ local breeds mutation:register_as_mutator({ enable_after_these = { "deathwish" + }, + title = "mutation", + dice = { + grims = 5, + tomes = 1 } }) mutation:create_options({}, true, "mutation", "mutation description") @@ -333,13 +386,21 @@ end mutator3:register_as_mutator({ enable_before_these = { "mutator555" + }, + title = "mutator3", + dice = { + grims = 5, + tomes = 1, + bonus = 22 } }) mutator3:create_options({}, true, "mutator3", "mutator3 description") mutator3.on_enabled = function() end mutator3.on_disabled = function() end -mutator_whatever:register_as_mutator() +mutator_whatever:register_as_mutator({ + title = "mutator_whatever" +}) mutator_whatever:create_options({}, true, "mutator_whatever", "mutator_whatever description") mutator_whatever.on_enabled = function() end mutator_whatever.on_disabled = function() end From 512b69cb9e7e4606882d97826f926fe96992d336 Mon Sep 17 00:00:00 2001 From: UnShame Date: Wed, 21 Feb 2018 01:18:15 +0300 Subject: [PATCH 08/32] mutators: compatibility settings, some gui testing, fixes --- .../vmf/modules/mutators/default_config.lua | 7 +- .../scripts/mods/vmf/modules/mutators/gui.lua | 74 ++++++-- .../mods/vmf/modules/mutators/info.lua | 2 +- .../mods/vmf/modules/mutators/mutators.lua | 175 ++++++++++-------- 4 files changed, 166 insertions(+), 92 deletions(-) diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/default_config.lua b/vmf_source/scripts/mods/vmf/modules/mutators/default_config.lua index 4c4d85c..b36d451 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/default_config.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/default_config.lua @@ -4,7 +4,6 @@ return { tomes = 0, bonus = 0 }, - server_name = "", title = "", short_title = "", difficulties = { @@ -17,5 +16,9 @@ return { "survival_hard", "survival_harder", "survival_hardest" - } + }, + incompatible_with_all = false, + compatible_with_all = false, + incompatible_with = {}, + compatible_with = {} } diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/gui.lua b/vmf_source/scripts/mods/vmf/modules/mutators/gui.lua index 9bfe2cd..e0f4b70 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/gui.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/gui.lua @@ -2,10 +2,26 @@ local vmf = get_mod("VMF") local mutators = vmf.mutators +local banner_level_widget = UIWidgets.create_texture_with_text_and_tooltip("title_bar", "Mutators", "map_level_setting_tooltip", "banner_level", "banner_level_text", { + vertical_alignment = "center", + scenegraph_id = "banner_level_text", + localize = false, + font_size = 28, + horizontal_alignment = "center", + font_type = "hell_shark", + text_color = Colors.get_color_table_with_alpha("cheeseburger", 255) +}) +local banner_level = UIWidget.init(banner_level_widget) + local window = nil local button = nil local window_opened = false +local function get_map_view() + local ingame_ui = Managers.matchmaking and Managers.matchmaking.ingame_ui + return ingame_ui and ingame_ui.views and ingame_ui.views.map_view +end + local function update_window_visibility(map_view) if not window then return end button.visible = map_view and map_view.friends and not map_view.friends:is_active() @@ -24,10 +40,8 @@ local function destroy_window(map_view) end local function create_window(map_view) - destroy_window(map_view) - vmf.sort_mutators() - vmf.disable_impossible_mutators() + destroy_window(map_view) local window_size = {0, 0} local window_position = {50, 500} @@ -35,13 +49,16 @@ local function create_window(map_view) window = get_mod("gui").create_window("mutators_window", window_position, window_size) for i, mutator in ipairs(mutators) do - window:create_checkbox("label" .. mutator:get_name(), {10, 40 * i}, {30, 30}, mutator:get_config().title, mutator:is_enabled(), function() - if not mutator:is_enabled() and mutator:can_be_enabled() then - mutator:enable() + local title = mutator:get_config().title or mutator:get_name() + window:create_checkbox("checkbox_" .. mutator:get_name(), {30, 360 - 40 * (i - 1)}, {30, 30}, title, mutator:is_enabled(), function(self) + if self.value then + if not mutator:is_enabled() and mutator:can_be_enabled() then + mutator:enable() + elseif not mutator:is_enabled() then + create_window(map_view) + end elseif mutator:is_enabled() then mutator:disable() - else - create_window(map_view) end end) end @@ -61,27 +78,43 @@ local function create_window(map_view) update_window_visibility(map_view) end +local function reload_window() + local map_view = get_map_view() + if map_view and map_view.active then + create_window(map_view) + end +end + vmf:hook("MapView.on_enter", function(func, self, ...) func(self, ...) print("on_enter") + + vmf.sort_mutators() + vmf.disable_impossible_mutators() vmf:pcall(function() create_window(self) end) end) vmf:hook("MapView.on_level_index_changed", function(func, self, ...) func(self, ...) print("on_level_index_changed") + + vmf.disable_impossible_mutators() vmf:pcall(function() create_window(self) end) end) vmf:hook("MapView.on_difficulty_index_changed", function(func, self, ...) func(self, ...) print("on_difficulty_index_changed") + + vmf.disable_impossible_mutators() vmf:pcall(function() create_window(self) end) end) vmf:hook("MapView.set_difficulty_stepper_index", function(func, self, ...) func(self, ...) print("set_difficulty_stepper_index") + + vmf.disable_impossible_mutators() vmf:pcall(function() create_window(self) end) end) @@ -121,16 +154,25 @@ vmf:hook("MapView.draw", function(func, self, input_service, gamepad_active, dt) UIRenderer.draw_widget(ui_renderer, widget) end - if not window_opened then - if self.settings_button_widget.content.toggled then - for widget_name, widget in pairs(self.advanced_settings_widgets) do - UIRenderer.draw_widget(ui_renderer, widget) - end - else - for widget_name, widget in pairs(self.normal_settings_widgets) do + if window_opened or not self.settings_button_widget.content.toggled then + for widget_name, widget in pairs(self.normal_settings_widgets) do + local skipped_widgets_keys = { + "stepper_level", + "level_preview", + "level_preview_text", + "banner_level" + } + if not window_opened or not table.has_item(skipped_widgets_keys, widget_name) then UIRenderer.draw_widget(ui_renderer, widget) end end + if window_opened then + vmf:pcall(function() UIRenderer.draw_widget(ui_renderer, banner_level) end) + end + else + for widget_name, widget in pairs(self.advanced_settings_widgets) do + UIRenderer.draw_widget(ui_renderer, widget) + end end UIRenderer.draw_widget(ui_renderer, self.player_list_conuter_text_widget) @@ -174,3 +216,5 @@ vmf:hook("MapView.draw", function(func, self, input_service, gamepad_active, dt) end end) + +return reload_window, get_map_view diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/info.lua b/vmf_source/scripts/mods/vmf/modules/mutators/info.lua index c938fb0..f335cc4 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/info.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/info.lua @@ -38,7 +38,7 @@ local function set_lobby_data() Managers.matchmaking.lobby:set_lobby_data(lobby_data) end -local function get_member_func( client_cookie) +local function get_member_func(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,"-")))) diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutators.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutators.lua index d9b03fd..fd182a7 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutators.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutators.lua @@ -89,11 +89,14 @@ end -- Disables mutators that cannot be enabled right now vmf.disable_impossible_mutators = function() + 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 + return disabled_mutators end @@ -101,6 +104,7 @@ end PRIVATE METHODS ]]-- +local update_mutators_view, get_map_view = dofile("scripts/mods/vmf/modules/mutators/gui") local addDice, removeDice = dofile("scripts/mods/vmf/modules/mutators/dice") local set_lobby_data = dofile("scripts/mods/vmf/modules/mutators/info") @@ -121,6 +125,71 @@ local function update_mutators_sequence(mutator_name, enable_these_after) 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 + +-- Disables enabled mutators that aren't compatible with the specified +local function disable_incompatible_with(mutator) + local names = nil + for _, other_mutator in ipairs(mutators) do + if ( + other_mutator ~= mutator and + other_mutator:is_enabled() and + not is_compatible(mutator, other_mutator) + ) then + other_mutator:disable() + local name = other_mutator:get_config().title or other_mutator:get_name() + if names then + names = names .. " " .. name + else + names = name + end + end + end + if names then + -- TODO: output this to the menu instead of chat + vmf:echo("These mutators are incompatible with " .. mutator:get_name() .. " and were disabled: " .. names) + end +end + -- Called after mutator is enabled local function on_enabled(mutator) local config = mutator:get_config() @@ -157,6 +226,11 @@ local function set_mutator_state(mutator, state) vmf.sort_mutators() end + -- Disable mutators that aren't compatible + if state then + disable_incompatible_with(mutator) + end + local disabled_mutators = {} local enable_these_after = mutators_sequence[mutator:get_name()] @@ -193,6 +267,9 @@ local function set_mutator_state(mutator, state) disabled_mutators[j]:enable() end end + + update_mutators_view() + print("---------") end @@ -219,8 +296,7 @@ local function can_be_enabled(self) local actual_difficulty = Managers.state and Managers.state.difficulty:get_difficulty() local right_difficulty = not actual_difficulty or table.has_item(mutator_difficulties, actual_difficulty) - local ingame_ui = Managers.matchmaking and Managers.matchmaking.ingame_ui - local map_view = ingame_ui and ingame_ui.views and ingame_ui.views.map_view + local map_view = get_map_view() local map_view_active = map_view and map_view.active local right_unapplied_difficulty = false @@ -257,7 +333,7 @@ VMFMod.register_as_mutator = function(self, config) mutators_config[mod_name] = table.clone(default_config) local _config = mutators_config[mod_name] for k, _ in pairs(_config) do - if config[k] then + if config[k] ~= nil then _config[k] = config[k] end end @@ -291,16 +367,18 @@ end HOOKS ]]-- vmf:hook("DifficultyManager.set_difficulty", function(func, self, difficulty) - vmf.disable_impossible_mutators() + local disabled_mutators = vmf.disable_impossible_mutators() + if #disabled_mutators > 0 then + local message = "MUTATORS DISABLED DUE TO DIFFICULTY CHANGE:" + for _, mutator in ipairs(disabled_mutators) do + message = message .. " " .. mutator:get_config().title or mutator:get_name() + end + Managers.chat:send_system_chat_message(1, message, 0, true) + end return func(self, difficulty) end) ---[[ - GUI -]]-- -dofile("scripts/mods/vmf/modules/mutators/gui") - @@ -320,87 +398,36 @@ dofile("scripts/mods/vmf/modules/mutators/gui") --[[ Testing --]] -local mutation = new_mod("mutation") -local deathwish = new_mod("deathwish") +local mutator2 = new_mod("mutator2") local mutator3 = new_mod("mutator3") local mutator555 = new_mod("mutator555") -local mutator_whatever = new_mod("mutator_whatever") mutator555:register_as_mutator({ - enable_after_these = { - "mutation" - }, - title = "mutator555" + compatible_with_all = true, + incompatible_with = { + "mutator2" + } }) mutator555:create_options({}, true, "mutator555", "mutator555 description") mutator555.on_enabled = function() end mutator555.on_disabled = function() end -deathwish:register_as_mutator({ - enable_before_these = { - "mutator555", - "mutator3", - "mutation" - }, - difficulties = { - "hardest" - }, - title = "deathwish" -}) -deathwish:create_options({}, true, "deathwish", "deathwish description") -deathwish.on_enabled = function() - print(tostring(Breeds.skaven_gutter_runner == Breeds.skaven_pack_master)) -end -deathwish.on_disabled = function() end - - -------------------------------- -local breeds -mutation:register_as_mutator({ - enable_after_these = { - "deathwish" - }, - title = "mutation", - dice = { - grims = 5, - tomes = 1 - } -}) -mutation:create_options({}, true, "mutation", "mutation description") -mutation.on_enabled = function() - breeds = table.clone(Breeds) - Breeds.skaven_slave = Breeds.skaven_clan_rat - Breeds.skaven_clan_rat = Breeds.skaven_storm_vermin_commander - - Breeds.skaven_gutter_runner = Breeds.skaven_rat_ogre - Breeds.skaven_pack_master = Breeds.skaven_rat_ogre - Breeds.skaven_poison_wind_globadier = Breeds.skaven_rat_ogre - Breeds.skaven_ratling_gunner = Breeds.skaven_rat_ogre -end -mutation.on_disabled = function(initial) - if not initial then Breeds = breeds end -end -------------------------------- - mutator3:register_as_mutator({ - enable_before_these = { + compatible_with_all = true, + incompatible_with = { "mutator555" - }, - title = "mutator3", - dice = { - grims = 5, - tomes = 1, - bonus = 22 } }) mutator3:create_options({}, true, "mutator3", "mutator3 description") mutator3.on_enabled = function() end mutator3.on_disabled = function() end -mutator_whatever:register_as_mutator({ - title = "mutator_whatever" +mutator2:register_as_mutator({ + difficulties = { + "hardest" + } }) -mutator_whatever:create_options({}, true, "mutator_whatever", "mutator_whatever description") -mutator_whatever.on_enabled = function() end -mutator_whatever.on_disabled = function() end +mutator2:create_options({}, true, "mutator2", "mutator2 description") +mutator2.on_enabled = function() end +mutator2.on_disabled = function() end \ No newline at end of file From a28a09f15c740e1b15d0a03ea2a2de1e7ca75412 Mon Sep 17 00:00:00 2001 From: UnShame Date: Wed, 21 Feb 2018 18:51:51 +0300 Subject: [PATCH 09/32] dumpfile: no longer crashes on error --- vmf_source/scripts/mods/vmf/modules/core/core_functions.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vmf_source/scripts/mods/vmf/modules/core/core_functions.lua b/vmf_source/scripts/mods/vmf/modules/core/core_functions.lua index c76b0f3..9fcddd5 100644 --- a/vmf_source/scripts/mods/vmf/modules/core/core_functions.lua +++ b/vmf_source/scripts/mods/vmf/modules/core/core_functions.lua @@ -155,7 +155,7 @@ VMFMod.dofile = function (self, script_path) if not success then 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 return value From 15b89cdd0a9b737a4db58faf7ea2d78c0a180ccd Mon Sep 17 00:00:00 2001 From: UnShame Date: Wed, 21 Feb 2018 18:55:20 +0300 Subject: [PATCH 10/32] mutators: gui rewrite part 1 (no checkboxes yet) gui_mod is no longer needed, will be removed next commit created a separate mod for mutators manager --- vmf_source/resource_packages/vmf.package | 1 - .../scripts/mods/vmf/modules/mutators/gui.lua | 49 ++-- ..._config.lua => mutator_default_config.lua} | 0 .../mutators/{dice.lua => mutator_dice.lua} | 4 +- .../mods/vmf/modules/mutators/mutator_gui.lua | 269 ++++++++++++++++++ .../mutators/mutator_gui_definitions.lua | 172 +++++++++++ .../mutators/{info.lua => mutator_info.lua} | 14 +- .../{mutators.lua => mutator_manager.lua} | 40 ++- .../scripts/mods/vmf/modules/vmf_options.lua | 15 +- vmf_source/scripts/mods/vmf/vmf_loader.lua | 5 +- 10 files changed, 507 insertions(+), 62 deletions(-) rename vmf_source/scripts/mods/vmf/modules/mutators/{default_config.lua => mutator_default_config.lua} (100%) rename vmf_source/scripts/mods/vmf/modules/mutators/{dice.lua => mutator_dice.lua} (94%) create mode 100644 vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua create mode 100644 vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui_definitions.lua rename vmf_source/scripts/mods/vmf/modules/mutators/{info.lua => mutator_info.lua} (80%) rename vmf_source/scripts/mods/vmf/modules/mutators/{mutators.lua => mutator_manager.lua} (88%) diff --git a/vmf_source/resource_packages/vmf.package b/vmf_source/resource_packages/vmf.package index 1bac16d..bee278a 100644 --- a/vmf_source/resource_packages/vmf.package +++ b/vmf_source/resource_packages/vmf.package @@ -21,5 +21,4 @@ lua = [ "scripts/mods/vmf/modules/gui/*" "scripts/mods/vmf/modules/options_menu/*" "scripts/mods/vmf/modules/mutators/*" - "scripts/mods/vmf/modules/mod_gui/*" ] \ No newline at end of file diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/gui.lua b/vmf_source/scripts/mods/vmf/modules/mutators/gui.lua index e0f4b70..ead3d1f 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/gui.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/gui.lua @@ -1,5 +1,5 @@ -local vmf = get_mod("VMF") -local mutators = vmf.mutators +local manager = get_mod("vmf_mutator_manager") +local mutators = manager.mutators local banner_level_widget = UIWidgets.create_texture_with_text_and_tooltip("title_bar", "Mutators", "map_level_setting_tooltip", "banner_level", "banner_level_text", { @@ -50,7 +50,7 @@ local function create_window(map_view) for i, mutator in ipairs(mutators) do local title = mutator:get_config().title or mutator:get_name() - window:create_checkbox("checkbox_" .. mutator:get_name(), {30, 360 - 40 * (i - 1)}, {30, 30}, title, mutator:is_enabled(), function(self) + window:create_checkbox("checkbox_" .. mutator:get_name(), {65, 360 - 40 * (i - 1)}, {30, 30}, title, mutator:is_enabled(), function(self) if self.value then if not mutator:is_enabled() and mutator:can_be_enabled() then mutator:enable() @@ -85,58 +85,58 @@ local function reload_window() end end -vmf:hook("MapView.on_enter", function(func, self, ...) +manager:hook("MapView.on_enter", function(func, self, ...) func(self, ...) print("on_enter") - vmf.sort_mutators() - vmf.disable_impossible_mutators() - vmf:pcall(function() create_window(self) end) + manager.sort_mutators() + manager.disable_impossible_mutators() + manager:pcall(function() create_window(self) end) end) -vmf:hook("MapView.on_level_index_changed", function(func, self, ...) +manager:hook("MapView.on_level_index_changed", function(func, self, ...) func(self, ...) print("on_level_index_changed") - vmf.disable_impossible_mutators() - vmf:pcall(function() create_window(self) end) + manager.disable_impossible_mutators() + manager:pcall(function() create_window(self) end) end) -vmf:hook("MapView.on_difficulty_index_changed", function(func, self, ...) +manager:hook("MapView.on_difficulty_index_changed", function(func, self, ...) func(self, ...) print("on_difficulty_index_changed") - vmf.disable_impossible_mutators() - vmf:pcall(function() create_window(self) end) + manager.disable_impossible_mutators() + manager:pcall(function() create_window(self) end) end) -vmf:hook("MapView.set_difficulty_stepper_index", function(func, self, ...) +manager:hook("MapView.set_difficulty_stepper_index", function(func, self, ...) func(self, ...) print("set_difficulty_stepper_index") - vmf.disable_impossible_mutators() - vmf:pcall(function() create_window(self) end) + manager.disable_impossible_mutators() + manager:pcall(function() create_window(self) end) end) -vmf:hook("MapView.on_exit", function(func, self, ...) +manager:hook("MapView.on_exit", function(func, self, ...) func(self, ...) print("on_exit") - vmf:pcall(function() destroy_window(self) end) + manager:pcall(function() destroy_window(self) end) window_opened = false end) -vmf:hook("MapView.suspend", function(func, self, ...) +manager:hook("MapView.suspend", function(func, self, ...) func(self, ...) print("suspend") - vmf:pcall(function() destroy_window(self) end) + manager:pcall(function() destroy_window(self) end) end) -vmf:hook("MapView.update", function(func, self, dt, t) +manager:hook("MapView.update", function(func, self, dt, t) func(self, dt, t) - vmf:pcall(function() update_window_visibility(self) end) + manager:pcall(function() update_window_visibility(self) end) end) -vmf:hook("MapView.draw", function(func, self, input_service, gamepad_active, dt) +manager:hook("MapView.draw", function(func, self, input_service, gamepad_active, dt) local ui_renderer = self.ui_renderer local ui_scenegraph = self.ui_scenegraph @@ -157,7 +157,6 @@ vmf:hook("MapView.draw", function(func, self, input_service, gamepad_active, dt) if window_opened or not self.settings_button_widget.content.toggled then for widget_name, widget in pairs(self.normal_settings_widgets) do local skipped_widgets_keys = { - "stepper_level", "level_preview", "level_preview_text", "banner_level" @@ -167,7 +166,7 @@ vmf:hook("MapView.draw", function(func, self, input_service, gamepad_active, dt) end end if window_opened then - vmf:pcall(function() UIRenderer.draw_widget(ui_renderer, banner_level) end) + manager:pcall(function() UIRenderer.draw_widget(ui_renderer, banner_level) end) end else for widget_name, widget in pairs(self.advanced_settings_widgets) do diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/default_config.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_default_config.lua similarity index 100% rename from vmf_source/scripts/mods/vmf/modules/mutators/default_config.lua rename to vmf_source/scripts/mods/vmf/modules/mutators/mutator_default_config.lua diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/dice.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_dice.lua similarity index 94% rename from vmf_source/scripts/mods/vmf/modules/mutators/dice.lua rename to vmf_source/scripts/mods/vmf/modules/mutators/mutator_dice.lua index ebda96e..d34dcb2 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/dice.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_dice.lua @@ -1,4 +1,4 @@ -local vmf = get_mod("VMF") +local manager = get_mod("vmf_mutator_manager") local missions = { "bonus_dice_hidden_mission", @@ -12,7 +12,7 @@ local num_dice_per_mission = { grimoire_hidden_mission = 0 } -vmf:hook("GameModeManager.complete_level", function(func, self) +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") diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua new file mode 100644 index 0000000..5826625 --- /dev/null +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua @@ -0,0 +1,269 @@ +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 = 5 + +local mutators_view = { + + initialized = false, + active = false, + was_active = false, + map_view = nil, + current_page = 1, + + init = function(self, map_view) + if self.initialized then return end + + self.map_view = map_view + if not self.map_view then return end + + -- 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), + } + + -- 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, + + 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 + end + end, + + 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 + 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 = function(self) + if not self.initialized or not self.active then return end + + -- 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:update_level_stepper() + + self.active = false + + print("DEACTIVE") + end, + + 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 + print("TEST", tostring(new_index)) + else + self.map_view:on_level_index_changed(index_change) + end + 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) + manager:pcall(function() self:update(dt, t) end) + 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 + manager:pcall(function() self:activate() end) + end + end) + + -- Deactivate the view on exit + manager:hook("MapView.on_exit", function(func, map_view) + func(map_view) + manager:pcall(function() self:deactivate() end) + 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) + 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") + end, +} + +-- Initialize mutators view after map view +manager:hook("MapView.init", function(func, self, ...) + func(self, ...) + manager:pcall(function() mutators_view:init(self) end) +end) + +-- Destroy mutators view after map view +manager:hook("MapView.destroy", function(func, ...) + mutators_view:deinitialize() + func(...) +end) + + +-- Initialize mutators view when map_view has been initialized already +local function get_map_view() + local ingame_ui = Managers.matchmaking and Managers.matchmaking.ingame_ui + return ingame_ui and ingame_ui.views and ingame_ui.views.map_view +end + +manager:pcall(function() mutators_view:init(get_map_view()) end) + +return mutators_view \ No newline at end of file diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui_definitions.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui_definitions.lua new file mode 100644 index 0000000..af3d4a4 --- /dev/null +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui_definitions.lua @@ -0,0 +1,172 @@ +local definitions = local_require("scripts/ui/views/map_view_definitions") + +definitions.scenegraph_definition.mutators_button = { + vertical_alignment = "bottom", + parent = "banner_party", + horizontal_alignment = "center", + size = { + 64, + 64 + }, + position = { + -150, + 90, + 1 + } +} + +definitions.scenegraph_definition.banner_mutators_text = { + vertical_alignment = "center", + parent = "banner_level", + horizontal_alignment = "center", + size = { + 300, + 40 + }, + position = { + 0, + 0, + 1 + } +} + +definitions.new_widgets = { + 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 + } + }), + 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 = "map_icon_browser_01", + icon_hover_texture = "map_icon_browser_01", + 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" + } +} + +return definitions \ No newline at end of file diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/info.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_info.lua similarity index 80% rename from vmf_source/scripts/mods/vmf/modules/mutators/info.lua rename to vmf_source/scripts/mods/vmf/modules/mutators/mutator_info.lua index f335cc4..b100d15 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/info.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_info.lua @@ -1,5 +1,5 @@ -local vmf = get_mod("VMF") -local mutators = vmf.mutators +local manager = get_mod("vmf_mutator_manager") +local mutators = manager.mutators local were_enabled_before = false @@ -59,7 +59,7 @@ local function get_member_func(client_cookie) end -vmf:hook("IngamePlayerListUI.set_difficulty_name", function(func, self, name) +manager:hook("IngamePlayerListUI.set_difficulty_name", function(func, self, name) local mutators_name = get_enabled_mutators_names(true) if mutators_name then name = name .. " " .. mutators_name @@ -67,7 +67,7 @@ vmf:hook("IngamePlayerListUI.set_difficulty_name", function(func, self, name) self.headers.content.game_difficulty = name end) -vmf:hook("MatchmakingStateHostGame.host_game", function(func, self, ...) +manager:hook("MatchmakingStateHostGame.host_game", function(func, self, ...) func(self, ...) set_lobby_data() local names = get_enabled_mutators_names() @@ -80,13 +80,13 @@ vmf:hook("MatchmakingStateHostGame.host_game", function(func, self, ...) end end) -vmf: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 = get_enabled_mutators_names(false) if name then local message = "[Automated message] This lobby has the following difficulty mod active : " .. name - vmf:hook("Managers.chat.channels[1].members_func", get_member_func(client_cookie)) + manager:hook("Managers.chat.channels[1].members_func", get_member_func(client_cookie)) Managers.chat:send_system_chat_message(1, message, 0, true) - vmf:hook_remove("Managers.chat.channels[1].members_func") + manager:hook_remove("Managers.chat.channels[1].members_func") end func(self, sender, client_cookie, host_cookie, lobby_id, friend_join) end) diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutators.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua similarity index 88% rename from vmf_source/scripts/mods/vmf/modules/mutators/mutators.lua rename to vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua index fd182a7..7020c50 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutators.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua @@ -1,13 +1,13 @@ -local vmf = get_mod("VMF") +local manager = new_mod("vmf_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 -vmf.mutators = {} -local mutators = vmf.mutators +manager.mutators = {} +local mutators = manager.mutators local mutators_config = {} -local default_config = dofile("scripts/mods/vmf/modules/mutators/default_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 @@ -29,12 +29,12 @@ local mutators_sorted = false ]]-- -- Sorts mutators in order they should be enabled -vmf.sort_mutators = function() +manager.sort_mutators = function() if mutators_sorted then return end -- LOG -- - vmf:dump(mutators_sequence, "seq", 5) + manager:dump(mutators_sequence, "seq", 5) for i, v in ipairs(mutators) do print(i, v:get_name()) end @@ -73,7 +73,7 @@ vmf.sort_mutators = function() numIter = numIter + 1 if numIter > maxIter then - vmf:error("Mutators: too many iterations. Check for loops in 'enable_before_these'/'enable_after_these'.") + manager:error("Mutators: too many iterations. Check for loops in 'enable_before_these'/'enable_after_these'.") return end end @@ -88,7 +88,7 @@ vmf.sort_mutators = function() end -- Disables mutators that cannot be enabled right now -vmf.disable_impossible_mutators = function() +manager.disable_impossible_mutators = function() local disabled_mutators = {} for _, mutator in pairs(mutators) do if mutator:is_enabled() and not mutator:can_be_enabled() then @@ -104,9 +104,9 @@ end PRIVATE METHODS ]]-- -local update_mutators_view, get_map_view = dofile("scripts/mods/vmf/modules/mutators/gui") -local addDice, removeDice = dofile("scripts/mods/vmf/modules/mutators/dice") -local set_lobby_data = dofile("scripts/mods/vmf/modules/mutators/info") +local mutators_view = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_gui") +local addDice, removeDice = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_dice") +local 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) @@ -116,7 +116,7 @@ local function update_mutators_sequence(mutator_name, enable_these_after) 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 - vmf:error("Mutators '" .. mutator_name .. "' and '" .. other_mutator_name .. "' are both set to load after the other one.") + 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 @@ -186,7 +186,7 @@ local function disable_incompatible_with(mutator) end if names then -- TODO: output this to the menu instead of chat - vmf:echo("These mutators are incompatible with " .. mutator:get_name() .. " and were disabled: " .. names) + manager:echo("These mutators are incompatible with " .. mutator:get_name() .. " and were disabled: " .. names) end end @@ -223,7 +223,7 @@ local function set_mutator_state(mutator, state) -- Sort mutators if this is the first call if not mutators_sorted then - vmf.sort_mutators() + manager.sort_mutators() end -- Disable mutators that aren't compatible @@ -268,8 +268,6 @@ local function set_mutator_state(mutator, state) end end - update_mutators_view() - print("---------") end @@ -280,12 +278,12 @@ end -- Enables mutator (pcall for now) local function enable_mutator(self) - vmf:pcall(function() set_mutator_state(self, true) end) + manager:pcall(function() set_mutator_state(self, true) end) end -- Disables mutator (pcall for now) local function disable_mutator(self) - vmf:pcall(function() set_mutator_state(self, false) end) + manager:pcall(function() set_mutator_state(self, false) end) end -- Checks current difficulty and map selection screen settings to determine if a mutator can be enabled @@ -296,7 +294,7 @@ local function can_be_enabled(self) local actual_difficulty = Managers.state and Managers.state.difficulty:get_difficulty() local right_difficulty = not actual_difficulty or table.has_item(mutator_difficulties, actual_difficulty) - local map_view = get_map_view() + local map_view = mutators_view.map_view local map_view_active = map_view and map_view.active local right_unapplied_difficulty = false @@ -366,8 +364,8 @@ end --[[ HOOKS ]]-- -vmf:hook("DifficultyManager.set_difficulty", function(func, self, difficulty) - local disabled_mutators = vmf.disable_impossible_mutators() +manager:hook("DifficultyManager.set_difficulty", function(func, self, difficulty) + local disabled_mutators = manager.disable_impossible_mutators() if #disabled_mutators > 0 then local message = "MUTATORS DISABLED DUE TO DIFFICULTY CHANGE:" for _, mutator in ipairs(disabled_mutators) do diff --git a/vmf_source/scripts/mods/vmf/modules/vmf_options.lua b/vmf_source/scripts/mods/vmf/modules/vmf_options.lua index 371881d..5cd0c9b 100644 --- a/vmf_source/scripts/mods/vmf/modules/vmf_options.lua +++ b/vmf_source/scripts/mods/vmf/modules/vmf_options.lua @@ -132,10 +132,21 @@ local options_widgets = { ["default_value"] = 0 } } - } + }, + { + ["setting_name"] = "win", + ["widget_type"] = "keybind", + ["text"] = "WIN", + ["default_value"] = {}, + ["action"] = "win" + }, } vmf:create_options(options_widgets, false, "Vermintide Mod Framework") +vmf.win = function() + Managers.state.game_mode:complete_level() +end + vmf.setting_changed = function (setting_name) if setting_name == "vmf_options_scrolling_speed" then @@ -196,4 +207,4 @@ local vmf_developer_mode = vmf:get("developer_mode") if mod_developer_mode ~= vmf_developer_mode then Managers.mod._settings.developer_mode = vmf_developer_mode Application.set_user_setting("mod_settings", Managers.mod._settings) -end \ No newline at end of file +end diff --git a/vmf_source/scripts/mods/vmf/vmf_loader.lua b/vmf_source/scripts/mods/vmf/vmf_loader.lua index 4d0fc66..b84d121 100644 --- a/vmf_source/scripts/mods/vmf/vmf_loader.lua +++ b/vmf_source/scripts/mods/vmf/vmf_loader.lua @@ -21,10 +21,7 @@ return { dofile("scripts/mods/vmf/modules/options_menu/vmf_options_view") dofile("scripts/mods/vmf/modules/vmf_options") - dofile("scripts/mods/vmf/modules/mod_gui/basic_gui") - dofile("scripts/mods/vmf/modules/mod_gui/gui") - - dofile("scripts/mods/vmf/modules/mutators/mutators") + dofile("scripts/mods/vmf/modules/mutators/mutator_manager") object.vmf = get_mod("VMF") From f7710d8f3c3f3ad5fa99d723834a2288a4a4a0d2 Mon Sep 17 00:00:00 2001 From: UnShame Date: Wed, 21 Feb 2018 19:04:23 +0300 Subject: [PATCH 11/32] merge with master; remove win mod --- .../scripts/mods/vmf/modules/vmf_options.lua | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/vmf_source/scripts/mods/vmf/modules/vmf_options.lua b/vmf_source/scripts/mods/vmf/modules/vmf_options.lua index 5cd0c9b..c8f86ff 100644 --- a/vmf_source/scripts/mods/vmf/modules/vmf_options.lua +++ b/vmf_source/scripts/mods/vmf/modules/vmf_options.lua @@ -132,22 +132,11 @@ local options_widgets = { ["default_value"] = 0 } } - }, - { - ["setting_name"] = "win", - ["widget_type"] = "keybind", - ["text"] = "WIN", - ["default_value"] = {}, - ["action"] = "win" - }, + } } vmf:create_options(options_widgets, false, "Vermintide Mod Framework") -vmf.win = function() - Managers.state.game_mode:complete_level() -end - -vmf.setting_changed = function (setting_name) +vmf.on_setting_changed = function (setting_name) if setting_name == "vmf_options_scrolling_speed" then From db2b52a2fd18eed6ed0d4e2d6649cd1ff383fff1 Mon Sep 17 00:00:00 2001 From: UnShame Date: Thu, 22 Feb 2018 00:21:19 +0300 Subject: [PATCH 12/32] mutators: finished gui Changing difficulty on map screen doesn't disable mutators. Can no longer enable mutators if they are incompatible with other already enabled. --- vmf_source/localization/mutator_manager.lua | 26 +++ vmf_source/resource_packages/vmf.package | 2 +- .../mutators/mutator_default_config.lua | 1 + .../vmf/modules/mutators/mutator_dice.lua | 5 +- .../mods/vmf/modules/mutators/mutator_gui.lua | 167 +++++++++++++++--- .../mutators/mutator_gui_definitions.lua | 156 +++++++++++++++- .../vmf/modules/mutators/mutator_manager.lua | 121 +++++++------ 7 files changed, 396 insertions(+), 82 deletions(-) create mode 100644 vmf_source/localization/mutator_manager.lua diff --git a/vmf_source/localization/mutator_manager.lua b/vmf_source/localization/mutator_manager.lua new file mode 100644 index 0000000..9c28c04 --- /dev/null +++ b/vmf_source/localization/mutator_manager.lua @@ -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" + } +} \ No newline at end of file diff --git a/vmf_source/resource_packages/vmf.package b/vmf_source/resource_packages/vmf.package index bee278a..1981927 100644 --- a/vmf_source/resource_packages/vmf.package +++ b/vmf_source/resource_packages/vmf.package @@ -11,7 +11,7 @@ material = [ ] lua = [ - "localization/vmf" + "localization/*" "scripts/mods/vmf/*" "scripts/mods/vmf/functions/*" diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_default_config.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_default_config.lua index b36d451..3a0c4f9 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_default_config.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_default_config.lua @@ -6,6 +6,7 @@ return { }, title = "", short_title = "", + description = "No description provided", difficulties = { "easy", "normal", diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_dice.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_dice.lua index d34dcb2..4f258f0 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_dice.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_dice.lua @@ -63,4 +63,7 @@ local removeDice = function(dice) adjustDice(dice.grims, dice.tomes, dice.bonus, -1) end -return addDice, removeDice +return { + addDice = addDice, + removeDice = removeDice +} diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua index 5826625..dca3245 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua @@ -3,7 +3,7 @@ local mutators = manager.mutators local definitions = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_gui_definitions") -local PER_PAGE = 5 +local PER_PAGE = definitions.PER_PAGE local mutators_view = { @@ -12,6 +12,8 @@ local mutators_view = { was_active = false, map_view = nil, current_page = 1, + mutators_sorted = {}, + mutator_checkboxes = {}, init = function(self, map_view) if self.initialized then return end @@ -19,15 +21,21 @@ local mutators_view = { 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), + 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 = { @@ -85,6 +93,15 @@ local mutators_view = { 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() @@ -127,6 +144,54 @@ local mutators_view = { 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, + + update_checkboxes = function(self) + + local widgets = self.map_view.normal_settings_widget_types + + for i = 1, PER_PAGE do + local current_index = PER_PAGE * (self.current_page - 1) + i + local checkbox = self.mutator_checkboxes[i] + local hotspot = checkbox.content.button_hotspot + if #self.mutators_sorted < current_index then + checkbox.content.setting_text = "" + checkbox.content.tooltip_text = "" + widgets.adventure["mutator_checkbox_" .. i] = nil + widgets.survival["mutator_checkbox_" .. i] = nil + else + local mutator_info = self.mutators_sorted[current_index] + local mutator = get_mod(mutator_info[1]) + + checkbox.content.setting_text = mutator_info[2] + checkbox.content.tooltip_text = self:generate_tooltip_for(mutator) + widgets.adventure["mutator_checkbox_" .. i] = checkbox + widgets.survival["mutator_checkbox_" .. i] = checkbox + + local active = mutator:can_be_enabled() + local color = active and "cheeseburger" or "slate_gray" + local color_hover = active and "white" or "slate_gray" + checkbox.style.setting_text.text_color = Colors.get_color_table_with_alpha(color, 255) + checkbox.style.setting_text_hover.text_color = Colors.get_color_table_with_alpha(color_hover, 255) + checkbox.style.checkbox_style.color = Colors.get_color_table_with_alpha(color_hover, 255) + + if hotspot.on_hover_enter then + self.map_view:play_sound("Play_hud_hover") + end + + if hotspot.on_release then + self.map_view:play_sound("Play_hud_hover") + if mutator:is_enabled() then + mutator:disable() + else + mutator:enable() + end + end + checkbox.content.selected = mutator:is_enabled() + end end end, @@ -149,6 +214,7 @@ local mutators_view = { -- 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 @@ -156,7 +222,7 @@ local mutators_view = { self.active = true - print("ACTIVE") + print("ACTIVE!") end, deactivate = function(self) @@ -178,8 +244,15 @@ local mutators_view = { -- 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 + self.active = false print("DEACTIVE") @@ -200,12 +273,49 @@ local mutators_view = { end self.current_page = new_index - print("TEST", tostring(new_index)) else self.map_view:on_level_index_changed(index_change) end end, + generate_tooltip_for = function(self, mutator) + local config = mutator:get_config() + local text = config.description + local supports_difficulty = mutator:supports_current_difficulty() + + if not supports_difficulty then + text = text .. "\nSupported difficulty levels:" + for i, difficulty in ipairs(config.difficulties) do + text = text .. (i == 1 and " " or ", ") .. manager:localize(difficulty) + end + end + + local incompatible_mutators = mutator:get_incompatible_mutators(true) + local currently_compatible = #incompatible_mutators == 0 + if supports_difficulty and #incompatible_mutators == 0 then + incompatible_mutators = mutator:get_incompatible_mutators() + end + if #incompatible_mutators > 0 then + if currently_compatible and config.incompatible_with_all or #incompatible_mutators == #mutators - 1 then + text = text .. "\nIncompatible with all other mutators" + else + text = text .. "\nIncompatible with:" + for i, other_mutator in ipairs(incompatible_mutators) do + local name = (other_mutator:get_config().title or other_mutator:get_name()) + text = text .. (i == 1 and " " or ", ") .. name + end + end + elseif config.compatible_with_all then + text = text .. "\nCompatible with all other mutators" + end + + if mutator:is_enabled() and not supports_difficulty then + text = text .. "\nWill be disabled when Play is pressed" + end + + return text + end, + setup_hooks = function(self) -- Update the view after map_view has updated @@ -235,6 +345,26 @@ local mutators_view = { 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) @@ -242,28 +372,15 @@ local mutators_view = { 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, ...) - manager:pcall(function() mutators_view:init(self) end) -end) - --- Destroy mutators view after map view -manager:hook("MapView.destroy", function(func, ...) - mutators_view:deinitialize() - func(...) -end) - - --- Initialize mutators view when map_view has been initialized already -local function get_map_view() - local ingame_ui = Managers.matchmaking and Managers.matchmaking.ingame_ui - return ingame_ui and ingame_ui.views and ingame_ui.views.map_view -end - -manager:pcall(function() mutators_view:init(get_map_view()) end) - return mutators_view \ No newline at end of file diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui_definitions.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui_definitions.lua index af3d4a4..7d620f0 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui_definitions.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui_definitions.lua @@ -1,6 +1,9 @@ local definitions = local_require("scripts/ui/views/map_view_definitions") +local scenegraph_definition = definitions.scenegraph_definition -definitions.scenegraph_definition.mutators_button = { +definitions.PER_PAGE = 6 + +scenegraph_definition.mutators_button = { vertical_alignment = "bottom", parent = "banner_party", horizontal_alignment = "center", @@ -14,8 +17,7 @@ definitions.scenegraph_definition.mutators_button = { 1 } } - -definitions.scenegraph_definition.banner_mutators_text = { +scenegraph_definition.banner_mutators_text = { vertical_alignment = "center", parent = "banner_level", horizontal_alignment = "center", @@ -30,7 +32,8 @@ definitions.scenegraph_definition.banner_mutators_text = { } } -definitions.new_widgets = { + +local new_widgets = { banner_mutators_widget = UIWidgets.create_texture_with_text_and_tooltip("title_bar", "Mutators", "Enable and disable mutators", "banner_level", "banner_mutators_text", { vertical_alignment = "center", scenegraph_id = "banner_mutators_text", @@ -169,4 +172,149 @@ definitions.new_widgets = { } } +for i = 1, definitions.PER_PAGE do + new_widgets["mutator_checkbox_" .. i] = { + scenegraph_id = "mutator_checkbox_" .. i, + element = { + passes = { + { + pass_type = "hotspot", + content_id = "button_hotspot" + }, + { + style_id = "tooltip_text", + pass_type = "tooltip_text", + text_id = "tooltip_text", + content_check_function = function (ui_content) + return ui_content.button_hotspot.is_hover + end + }, + { + style_id = "setting_text", + pass_type = "text", + text_id = "setting_text", + content_check_function = function (content) + return not content.button_hotspot.is_hover + end + }, + { + style_id = "setting_text_hover", + pass_type = "text", + text_id = "setting_text", + content_check_function = function (content) + return content.button_hotspot.is_hover + end + }, + { + pass_type = "texture", + style_id = "checkbox_style", + texture_id = "checkbox_unchecked_texture", + content_check_function = function (content) + return not content.selected + end + }, + { + pass_type = "texture", + style_id = "checkbox_style", + texture_id = "checkbox_checked_texture", + content_check_function = function (content) + return content.selected + end + } + } + }, + content = { + tooltip_text = "Mutator ajksad " .. i, + checkbox_unchecked_texture = "checkbox_unchecked", + checkbox_checked_texture = "checkbox_checked", + selected = false, + setting_text = "Mutator asdasasda " .. i * 3, + button_hotspot = {} + }, + style = { + checkbox_style = { + size = { + 20, + 20 + }, + offset = { + 0, + 6, + 1 + }, + color = { + 255, + 255, + 255, + 255 + } + }, + setting_text = { + vertical_alignment = "center", + font_size = 22, + localize = false, + horizontal_alignment = "left", + word_wrap = true, + font_type = "hell_shark", + text_color = Colors.get_color_table_with_alpha("cheeseburger", 255), + offset = { + 24, + 2, + 4 + } + }, + setting_text_hover = { + vertical_alignment = "center", + font_size = 22, + localize = false, + horizontal_alignment = "left", + word_wrap = true, + font_type = "hell_shark", + text_color = Colors.get_color_table_with_alpha("white", 255), + offset = { + 24, + 2, + 4 + } + }, + tooltip_text = { + font_size = 18, + max_width = 500, + localize = false, + cursor_side = "right", + horizontal_alignment = "left", + vertical_alignment = "top", + font_type = "hell_shark", + text_color = Colors.get_color_table_with_alpha("white", 255), + line_colors = {}, + offset = { + 0, + 0, + 50 + }, + cursor_offset = { + -10, + -27 + } + } + } + } + scenegraph_definition["mutator_checkbox_" .. i] = { + vertical_alignment = "center", + parent = "banner_party", + horizontal_alignment = "left", + size = { + 310, + 30 + }, + position = { + 30, + 520 - 40 * (i - 1), + 1 + } + } +end + +definitions.new_widgets = new_widgets + return definitions \ No newline at end of file diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua index 7020c50..ecdd0e5 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua @@ -1,5 +1,6 @@ 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 @@ -88,7 +89,7 @@ manager.sort_mutators = function() end -- Disables mutators that cannot be enabled right now -manager.disable_impossible_mutators = function() +manager.disable_impossible_mutators = function(notify, everybody) local disabled_mutators = {} for _, mutator in pairs(mutators) do if mutator:is_enabled() and not mutator:can_be_enabled() then @@ -96,6 +97,17 @@ manager.disable_impossible_mutators = function() table.insert(disabled_mutators, mutator) end end + if #disabled_mutators > 0 and notify then + local message = everybody and "MUTATORS DISABLED DUE TO DIFFICULTY CHANGE:" or "Mutators disabled due to difficulty change:" + for _, mutator in ipairs(disabled_mutators) do + message = message .. " " .. (mutator:get_config().title or mutator:get_name()) + end + if everybody then + Managers.chat:send_system_chat_message(1, message, 0, true) + else + manager:echo(message) + end + end return disabled_mutators end @@ -105,7 +117,7 @@ end ]]-- local mutators_view = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_gui") -local addDice, removeDice = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_dice") +local dice_manager = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_dice") local set_lobby_data = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_info") -- Adds mutator names from enable_these_after to the list of mutators that should be enabled after the mutator_name @@ -166,41 +178,17 @@ local function is_compatible(mutator, other_mutator) return compatible end --- Disables enabled mutators that aren't compatible with the specified -local function disable_incompatible_with(mutator) - local names = nil - for _, other_mutator in ipairs(mutators) do - if ( - other_mutator ~= mutator and - other_mutator:is_enabled() and - not is_compatible(mutator, other_mutator) - ) then - other_mutator:disable() - local name = other_mutator:get_config().title or other_mutator:get_name() - if names then - names = names .. " " .. name - else - names = name - end - end - end - if names then - -- TODO: output this to the menu instead of chat - manager:echo("These mutators are incompatible with " .. mutator:get_name() .. " and were disabled: " .. names) - end -end - -- Called after mutator is enabled local function on_enabled(mutator) local config = mutator:get_config() - addDice(config.dice) + dice_manager.addDice(config.dice) set_lobby_data() end -- Called after mutator is disabled local function on_disabled(mutator) local config = mutator:get_config() - removeDice(config.dice) + dice_manager.removeDice(config.dice) set_lobby_data() end @@ -221,16 +209,15 @@ local function set_mutator_state(mutator, state) return end + if state and #mutator:get_incompatible_mutators(true) > 0 then + return + end + -- Sort mutators if this is the first call if not mutators_sorted then manager.sort_mutators() end - -- Disable mutators that aren't compatible - if state then - disable_incompatible_with(mutator) - end - local disabled_mutators = {} local enable_these_after = mutators_sequence[mutator:get_name()] @@ -289,8 +276,13 @@ end -- Checks current difficulty and map selection screen settings to determine if a mutator can be enabled local function can_be_enabled(self) - local mutator_difficulties = self:get_config().difficulties + if #self:get_incompatible_mutators(true) > 0 then return false end + return self:supports_current_difficulty() +end + +local function supports_current_difficulty(self) + local mutator_difficulties = self:get_config().difficulties local actual_difficulty = Managers.state and Managers.state.difficulty:get_difficulty() local right_difficulty = not actual_difficulty or table.has_item(mutator_difficulties, actual_difficulty) @@ -314,6 +306,20 @@ local function get_config(self) return mutators_config[self:get_name()] end +local function get_incompatible_mutators(self, enabled_only) + local incompatible_mutators = {} + for _, other_mutator in ipairs(mutators) do + if ( + other_mutator ~= self and + (not enabled_only or other_mutator:is_enabled()) and + not is_compatible(self, other_mutator) + ) then + table.insert(incompatible_mutators, other_mutator) + end + end + return incompatible_mutators +end + -- Turns a mod into a mutator VMFMod.register_as_mutator = function(self, config) if not config then config = {} end @@ -351,13 +357,17 @@ VMFMod.register_as_mutator = function(self, config) 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 @@ -365,19 +375,26 @@ end HOOKS ]]-- manager:hook("DifficultyManager.set_difficulty", function(func, self, difficulty) - local disabled_mutators = manager.disable_impossible_mutators() - if #disabled_mutators > 0 then - local message = "MUTATORS DISABLED DUE TO DIFFICULTY CHANGE:" - for _, mutator in ipairs(disabled_mutators) do - message = message .. " " .. mutator:get_config().title or mutator:get_name() - end - Managers.chat:send_system_chat_message(1, message, 0, true) - end + manager.disable_impossible_mutators(true, true) return func(self, difficulty) end) +-- Initialize mutators view after map view +manager:hook("MapView.init", function(func, self, ...) + func(self, ...) + manager:pcall(function() mutators_view:init(self) end) +end) +-- Destroy mutators view after map view +manager:hook("MapView.destroy", function(func, ...) + mutators_view:deinitialize() + func(...) +end) + + +-- Initialize mutators view when map_view has been initialized already +manager:pcall(function() mutators_view:init(mutators_view:get_map_view()) end) @@ -401,10 +418,7 @@ local mutator3 = new_mod("mutator3") local mutator555 = new_mod("mutator555") mutator555:register_as_mutator({ - compatible_with_all = true, - incompatible_with = { - "mutator2" - } + incompatible_with_all = true }) mutator555:create_options({}, true, "mutator555", "mutator555 description") mutator555.on_enabled = function() end @@ -412,20 +426,25 @@ mutator555.on_disabled = function() end mutator3:register_as_mutator({ - compatible_with_all = true, incompatible_with = { - "mutator555" + "mutator4" } }) -mutator3:create_options({}, true, "mutator3", "mutator3 description") mutator3.on_enabled = function() end mutator3.on_disabled = function() end mutator2:register_as_mutator({ + compatible_with_all = true, difficulties = { "hardest" } }) -mutator2:create_options({}, true, "mutator2", "mutator2 description") mutator2.on_enabled = function() end -mutator2.on_disabled = function() end \ No newline at end of file +mutator2.on_disabled = function() end + +--[[for i=4,17 do + local mutator = new_mod("mutator" .. i) + mutator:register_as_mutator({}) + mutator.on_enabled = function() end + mutator.on_disabled = function() end +end--]] \ No newline at end of file From 5e43a58c121477ae62c95757323fad8ebcfcd261 Mon Sep 17 00:00:00 2001 From: UnShame Date: Thu, 22 Feb 2018 00:21:39 +0300 Subject: [PATCH 13/32] remove gui_mod completely --- .../mods/vmf/modules/mod_gui/basic_gui.lua | 301 -- .../scripts/mods/vmf/modules/mod_gui/gui.lua | 2529 ----------------- 2 files changed, 2830 deletions(-) delete mode 100644 vmf_source/scripts/mods/vmf/modules/mod_gui/basic_gui.lua delete mode 100644 vmf_source/scripts/mods/vmf/modules/mod_gui/gui.lua diff --git a/vmf_source/scripts/mods/vmf/modules/mod_gui/basic_gui.lua b/vmf_source/scripts/mods/vmf/modules/mod_gui/basic_gui.lua deleted file mode 100644 index 83f65a8..0000000 --- a/vmf_source/scripts/mods/vmf/modules/mod_gui/basic_gui.lua +++ /dev/null @@ -1,301 +0,0 @@ ---[[ - author: grasmann - - Basic GUI - - Provides a screen gui and functionality to draw to it ---]] -local basic_gui = new_mod("basic_gui") - -basic_gui.gui = nil -basic_gui.default_world = "top_ingame_view" -basic_gui.default_font = "hell_shark" -basic_gui.default_font_size = 22 -basic_gui.default_font_material = "materials/fonts/gw_body_32" - --- #################################################################################################################### --- ##### Init ######################################################################################################### --- #################################################################################################################### ---[[ - Init draw ---]] -basic_gui.init = function() - if not basic_gui.gui then - basic_gui.create_screen_ui() - end -end ---[[ - Create screen gui ---]] -basic_gui.create_screen_ui = function() - local world = Managers.world:world(basic_gui.default_world) - basic_gui.gui = World.create_screen_gui(world, "immediate", - "material", "materials/fonts/gw_fonts", - --"material", "materials/ui/ui_1080p_ingame", - "material", "materials/ui/ui_1080p_ingame_common") - -- "material", "materials/ui/ui_1080p_popup" - -- "material", "materials/ui/ui_1080p_ingame_inn" -end - --- #################################################################################################################### --- ##### Overloaded draw functions #################################################################################### --- #################################################################################################################### ---[[ - Draw rect with vectors ---]] -basic_gui.rect_vectors = function(position, size, color) - - Gui.rect(basic_gui.gui, position, size, color) -end ---[[ - Draw text with vectors ---]] -basic_gui.text_vectors = function(text, position, font_size, color, font) - local font_type = font or "hell_shark" - local font_by_resolution = UIFontByResolution({ - dynamic_font = true, - font_type = font_type, - font_size = font_size - }) - local font, result_size, material = unpack(font_by_resolution) - Gui.text(basic_gui.gui, text, font, font_size, material, position, color) -end ---[[ - Draw bitmap with vectors ---]] -basic_gui.bitmap_uv = function(atlas, uv00, uv11, position, size, color) - local atlas = atlas or "gui_hud_atlas" - local uv00 = uv00 or Vector2(0.222656, 0.584961) - local uv11 = uv11 or Vector2(0.25293, 0.615234) - local position = position or Vector3(1, 1, 1) - local size = size or Vector2(62, 62) - local color = color or Color(255, 255, 255, 255) - return Gui.bitmap_uv(basic_gui.gui, atlas, uv00, uv11, position, size, color) -end - - --- #################################################################################################################### --- ##### Draw functions ############################################################################################### --- #################################################################################################################### ---[[ - Draw rect - Parameters: - 1) int:X, int:Y, int:Z, int:Width, int:Height, Color:color - 2) vector3:Position, vector2:size, Color:color ---]] -basic_gui.rect = function(arg1, arg2, arg3, arg4, arg5, arg6) - local color = Color(255, 255, 255, 255) - if type(arg1) == "number" then - basic_gui.rect_vectors(Vector3(arg1, arg2, arg3 or 1), Vector2(arg4, arg5), arg6 or color) - else - basic_gui.rect_vectors(arg1, arg2, arg3 or color) - end -end ---[[ - Draw text - Parameters: - 1) string:Text, int:X, int:Y, int:Z, int:font_size, Color:color, string:font - 2) string:Text, vector3:position, int:font_size, Color:color, string:font ---]] -basic_gui.text = function(arg1, arg2, arg3, arg4, arg5, arg6, arg7) - local color = Color(255, 255, 255, 255) - local font_size = basic_gui.default_font_size - local font = basic_gui.default_font - if type(arg2) == "number" then - basic_gui.text_vectors(arg1, Vector3(arg2, arg3, arg4 or 1), arg5 or font_size, arg6 or color, arg7 or font) - else - basic_gui.text_vectors(arg1, arg2, arg3 or font_size, arg4 or color, arg5 or font) - end -end ---[[ - Create lines from string with \n ---]] -basic_gui.to_lines = function(text, font_size, font_material, colors) - local get_color = function(index) - if #colors >= index then - if colors[index] then - return colors[index] - end - else - return colors[#colors] - end - return {255, 255, 255, 255} - end - local _lines = {} - local width = 0 - local height = 0 - font_size = font_size or basic_gui.default_font_size - local index = 1 - font_material = font_material or basic_gui.default_font_material - for line in string.gmatch(text.."\n", "([^\n]*)\n") do - local w = basic_gui.text_width(line, font_material, font_size) - if line == "" then line = "#" end - if w > width then width = w end - --local h = basic_gui.text_height(line, font_material, font_size) - height = height + font_size --h - _lines[#_lines+1] = { - text = line, - width = w, - height = font_size, --h, - color = get_color(index), - } - index = index + 1 - end - if #_lines == 0 and #text > 0 then - _lines[#_lines+1] = text - end - local result = { - size = {width, height}, - ["lines"] = _lines, - } - return result -end ---[[ - Draw tooltip - Parameters: - 1) string:Text [, table:colors, int:font_size, int:line_padding, table:offset, table:padding, string:font_material] ---]] -basic_gui.tooltip = function(str, colors, font_size, line_padding, offset, padding, font_material, size) - basic_gui:pcall(function() - -- Create default colors if nil - colors = colors or - {Colors.get_color_table_with_alpha("cheeseburger", 255), - Colors.get_color_table_with_alpha("white", 255),} - -- Get mouse position - local cursor_axis_id = stingray.Mouse.axis_id("cursor") - local mouse = stingray.Mouse.axis(cursor_axis_id) - -- UI - local scale = UIResolutionScale() - local screen_w, screen_h = UIResolution() - -- Font - font_size = font_size or screen_w / 100 - font_material = font_material or basic_gui.default_font_material --"materials/fonts/gw_body_32" - -- Offset / Padding - offset = offset or {20*scale, -20*scale} - padding = padding or {5*scale, 5*scale, 5*scale, 5*scale} - line_padding = line_padding or 0*scale - -- Transform string - local text = basic_gui.to_lines(str, font_size, font_material, colors) - -- Transform simple text size - if #text.lines > 0 then - text.size[2] = text.size[2] + (#text.lines-1 * line_padding) + padding[4] + padding[1] - text.size[1] = text.size[1] + padding[1] + padding[3] - end - -- Render background - local x = mouse[1] + offset[1] - local y = mouse[2] + offset[2] - size = size or Vector2(text.size[1], text.size[2]) - basic_gui.rect(Vector3(x, y, 999), size, Color(200, 0, 0, 0)) - -- Render lines - --basic_gui:echo("rofl") - local text_x = x + padding[1] - local text_y = y + text.size[2] - padding[2] - font_size - --local index = 1 - local icon_size = Vector2(50*scale, 50*scale) - local icon_x = text_x - local icon_y = text_y - icon_size[2] - for _, line in pairs(text.lines) do - if line.text ~= "#" then - local color = Color(line.color[1], line.color[2], line.color[3], line.color[4]) - basic_gui.text(line.text, Vector3(text_x, text_y, 999), font_size, color) - end - text_y = text_y - line.height - line_padding - end - end) -end - ---[[ - Draw mission icon - Parameters: - 1) vector3:position, string:text, vector2:text_offset, color:color, [vector2:size, int:font_size, string:font] - - text_offset: - By default the text will be placed to the right of the icon up half its size - The offset if set will be calculated from the middle of the icon ---]] -basic_gui.side_mission_icon = function(position, text, text_offset, color, size, font_size, font, text_color) - local atlas = "gui_hud_atlas" - local uv00 = Vector2(0.222656, 0.584961) - local uv11 = Vector2(0.25293, 0.615234) - local position = position or Vector3(1, 1, 1) - local size = size or Vector2(62, 62) - local color = color or Color(255, 255, 255, 255) - local id = basic_gui.bitmap_uv(atlas, uv00, uv11, position, size, color) - if text ~= nil then - if text_offset ~= nil then - position = position + Vector3(text_offset[1], text_offset[2], 0) - else - position = position + Vector3(size[1], size[2] / 2, 0) - end - local font_size = font_size or basic_gui.default_font_size - local font = basic_gui.default_font - local text_color = text_color or color - basic_gui.text_vectors(text, position, font_size, text_color, font) - end - return id, size, color -end - -basic_gui.tome_icon = function(position, color, size) - local icon_texture = "consumables_book_lit" - local icon_settings = UIAtlasHelper.get_atlas_settings_by_texture_name(icon_texture) - - local atlas = "gui_generic_icons_atlas" - local size = size or Vector2(icon_settings.size[1], icon_settings.size[2]) - local color = color or Color(255, 255, 255, 255) - local uv00 = Vector2(icon_settings.uv00[1], icon_settings.uv00[2]) - local uv11 = Vector2(icon_settings.uv11[1], icon_settings.uv11[2]) - local position = position or Vector3(1, 1, 1) - return basic_gui.bitmap_uv(atlas, uv00, uv11, position, size, color) -end - -basic_gui.grim_icon = function(position, color, size) - local icon_texture = "consumables_grimoire_lit" - local icon_settings = UIAtlasHelper.get_atlas_settings_by_texture_name(icon_texture) - - local atlas = "gui_generic_icons_atlas" - local size = size or Vector2(icon_settings.size[1], icon_settings.size[2]) - local color = color or Color(255, 255, 255, 255) - local uv00 = Vector2(icon_settings.uv00[1], icon_settings.uv00[2]) - local uv11 = Vector2(icon_settings.uv11[1], icon_settings.uv11[2]) - local position = position or Vector3(1, 1, 1) - return basic_gui.bitmap_uv(atlas, uv00, uv11, position, size, color) -end - -basic_gui.draw_icon = function(icon_texture, position, color, size, atlas) - local icon_settings = UIAtlasHelper.get_atlas_settings_by_texture_name(icon_texture) - - local atlas = atlas or "gui_generic_icons_atlas" - local size = size or Vector2(icon_settings.size[1], icon_settings.size[2]) - local color = color or Color(255, 255, 255, 255) - local uv00 = Vector2(icon_settings.uv00[1], icon_settings.uv00[2]) - local uv11 = Vector2(icon_settings.uv11[1], icon_settings.uv11[2]) - local position = position or Vector3(1, 1, 1) - return basic_gui.bitmap_uv(atlas, uv00, uv11, position, size, color) -end - --- #################################################################################################################### --- ##### Get information ############################################################################################## --- #################################################################################################################### ---[[ - Get width of a text with the given font and font size ---]] -basic_gui.text_width = function(text, font, font_size) - local text_extent_min, text_extent_max = Gui.text_extents(basic_gui.gui, text, font or basic_gui.default_font, font_size or basic_gui.default_font_size) - local text_width = text_extent_max[1] - text_extent_min[1] - return text_width -end ---[[ - Get height of a text with the given font and font size ---]] -basic_gui.text_height = function(text, font, font_size) - local text_extent_min, text_extent_max = Gui.text_extents(basic_gui.gui, text, font or basic_gui.default_font, font_size or basic_gui.default_font_size) - local text_height = text_extent_max[2] - text_extent_min[2] - return text_height -end - -basic_gui.on_game_state_changed = function(status, state) - if status == "enter" and state == "StateIngame" then - basic_gui.init() - end -end - -basic_gui:pcall(basic_gui.init) \ No newline at end of file diff --git a/vmf_source/scripts/mods/vmf/modules/mod_gui/gui.lua b/vmf_source/scripts/mods/vmf/modules/mod_gui/gui.lua deleted file mode 100644 index 107c51e..0000000 --- a/vmf_source/scripts/mods/vmf/modules/mod_gui/gui.lua +++ /dev/null @@ -1,2529 +0,0 @@ ---[[ - Author: ---]] - -local gui = new_mod("gui") -local basic_gui = get_mod("basic_gui") - --- ################################################################################################################ --- ##### UTF8 ##################################################################################################### --- ################################################################################################################ -local UTF8 = { - -- UTF-8 Reference: - -- 0xxxxxxx - 1 byte UTF-8 codepoint (ASCII character) - -- 110yyyxx - First byte of a 2 byte UTF-8 codepoint - -- 1110yyyy - First byte of a 3 byte UTF-8 codepoint - -- 11110zzz - First byte of a 4 byte UTF-8 codepoint - -- 10xxxxxx - Inner byte of a multi-byte UTF-8 codepoint - - chsize = function(self, char) - if not char then - return 0 - elseif char > 240 then - return 4 - elseif char > 225 then - return 3 - elseif char > 192 then - return 2 - else - return 1 - end - end, - - -- This function can return a substring of a UTF-8 string, properly handling - -- UTF-8 codepoints. Rather than taking a start index and optionally an end - -- index, it takes the string, the starting character, and the number of - -- characters to select from the string. - - utf8sub = function(self, str, startChar, numChars) - local startIndex = 1 - while startChar > 1 do - local char = string.byte(str, startIndex) - startIndex = startIndex + self:chsize(char) - startChar = startChar - 1 - end - - local currentIndex = startIndex - - while numChars > 0 and currentIndex <= #str do - local char = string.byte(str, currentIndex) - currentIndex = currentIndex + self:chsize(char) - numChars = numChars -1 - end - return str:sub(startIndex, currentIndex - 1) - end, -} - - --- ################################################################################################################ --- ##### Input keymap ############################################################################################# --- ################################################################################################################ -MOD_GUI_KEY_MAP = { - win32 = { - ["backspace"] = {"keyboard", "backspace", "held"}, - ["enter"] = {"keyboard", "enter", "pressed"}, - ["esc"] = {"keyboard", "esc", "pressed"}, - }, -} -MOD_GUI_KEY_MAP.xb1 = MOD_GUI_KEY_MAP.win32 -local ui_special_keys = {"space", "<", ">"} - --- ################################################################################################################ --- ##### Color helper ############################################################################################# --- ################################################################################################################ -local ColorHelper = { - --[[ - Transform color values to table - ]]-- - box = function(a, r, g, b) - return {a, r, g, b} - end, - --[[ - Transform color table to color - ]]-- - unbox = function(box) - return Color(box[1], box[2], box[3], box[4]) - end, -} - --- ################################################################################################################ --- ##### Main Object ############################################################################################## --- ################################################################################################################ -gui.theme = "default" - -gui.width = 1920 -gui.height = 1080 - -gui.adjust_to_fit_position_and_scale = function(position) - position = gui.adjust_to_fit_scale(position) - - local screen_w, screen_h = UIResolution() - local scale = UIResolutionScale() - local ui_w = 1920*scale - local ui_h = 1080*scale - - position = { - position[1] + (screen_w - ui_w)/2, - position[2] + (screen_h - ui_h)/2 - } - - --gui:echo(position[1], position[2]) - - return position -end - -gui.adjust_to_fit_scale = function(position) - if not position then return {0, 0} end - - local scale = UIResolutionScale() - - position = { - position[1] * scale, - position[2] * scale - } - - --gui:echo(position[1], position[2]) - - return position -end - --- ################################################################################################################ --- ##### Create containers ######################################################################################## --- ################################################################################################################ ---[[ - Create window -]]-- -gui.create_window = function(name, position, size) - - -- Create window - position = gui.adjust_to_fit_position_and_scale(position) - size = gui.adjust_to_fit_scale(size) - - local window = table.clone(gui.widgets.window) - window:set("name", name or "name") - window:set("position", position) - window:set("size", size or {0, 0}) - window:set("original_size", size or {0, 0}) - - -- Add window to list - gui.windows:add_window(window) - - return window -end - --- ################################################################################################################ --- ##### Cycle #################################################################################################### --- ################################################################################################################ ---[[ - Update -]]-- -gui._update = function(dt) - -- Update timers - gui.timers:update(dt) - - gui.input:check() - - -- Click - local position = gui.mouse:cursor() - if stingray.Mouse.pressed(stingray.Mouse.button_id("left")) then - gui.mouse:click(position, gui.windows.list) - elseif stingray.Mouse.released(stingray.Mouse.button_id("left")) then - gui.mouse:release(position, gui.windows.list) - end - - -- Hover - gui.mouse:hover(position, gui.windows.list) - - -- Update windows - gui.windows:update() -end - -gui:hook("MatchmakingManager.update", function(func, self, dt, t) - func(self, dt, t) - gui._update(dt) -end) - --- ################################################################################################################ --- ##### Common functions ######################################################################################### --- ################################################################################################################ ---[[ - Transform position and size to bounds -]]-- -gui.to_bounds = function(position, size) - return {position[1], position[1] + size[1], position[2], position[2] + size[2]} -end ---[[ - Check if position is in bounds -]]-- -gui.point_in_bounds = function(position, bounds) - if position[1] >= bounds[1] and position[1] <= bounds[2] and position[2] >= bounds[3] and position[2] <= bounds[4] then - return true, {position[1] - bounds[1], position[2] - bounds[3]} - end - return false, {0, 0} -end - --- ################################################################################################################ --- ##### Window system ############################################################################################ --- ################################################################################################################ -gui.windows = { - list = {}, - --[[ - Add window to list - ]]-- - add_window = function(self, window) - self:inc_z_orders(#self.list) - window.z_order = 1 - self.list[#self.list+1] = window - end, - --[[ - Shift z orders of windows - ]]-- - inc_z_orders = function(self, changed_z) - for z=changed_z, 1, -1 do - for _, window in pairs(self.list) do - if window.z_order == z then - window.z_order = window.z_order + 1 - end - end - end - end, - --[[ - Shift z orders of windows - ]]-- - dec_z_orders = function(self, changed_z) - --for z=changed_z, 1, -1 do - for z=changed_z+1, #self.list do - for _, window in pairs(self.list) do - if window.z_order == z then - window.z_order = window.z_order - 1 - end - end - end - end, - --[[ - Shift z orders of windows - ]]-- - unfocus = function(self) - for _, window in pairs(self.list) do - if window.z_order == 1 then - window:unfocus() - end - end - end, - --[[ - Update windows - ]]-- - update = function(self) - if #self.list > 0 then - for z=#self.list, 1, -1 do - for _, window in pairs(self.list) do - if window.visible then - if window.z_order == z then - window:update() - window:render() - end - end - end - end - end - end, -} - --- ################################################################################################################ --- ##### Mouse system ############################################################################################# --- ################################################################################################################ -gui.mouse = { - --[[ - Process click - ]]-- - click = function(self, position, windows) - for z=1, #windows do - for _, window in pairs(windows) do - if window.z_order == z then - if gui.point_in_bounds(position, window:extended_bounds()) then - window:click(position) - else - window:unfocus() - end - end - end - end - end, - --[[ - Process release - ]]-- - release = function(self, position, windows) - for z=1, #windows do - for _, window in pairs(windows) do - if window.z_order == z then - if gui.point_in_bounds(position, window:extended_bounds()) then - window:release(position) - else - window:unfocus() - end - end - end - end - end, - --[[ - Process hover - ]]-- - hover = function(self, position, windows) - self:un_hover_all(windows) - for z=1, #windows do - for _, window in pairs(windows) do - if window.z_order == z then - local hovered, cursor = gui.point_in_bounds(position, window:extended_bounds()) - if hovered then - window:hover(cursor) - return - end - end - end - end - end, - --[[ - Unhover all - ]]-- - un_hover_all = function(self, windows) - for _, window in pairs(windows) do - if window.hovered then - window:hover_exit() - end - end - end, - --[[ - Get mouse position - ]]-- - cursor = function(self) - local cursor_axis_id = stingray.Mouse.axis_id("cursor") -- retrieve the axis ID - local value = stingray.Mouse.axis(cursor_axis_id) -- use the ID to access to value - return {value[1], value[2]} - end, -} - --- ################################################################################################################ --- ##### Timer system ############################################################################################# --- ################################################################################################################ -gui.timers = { - -- Timer list - items = {}, - -- Timer template - template = { - name = nil, - rate = 100, - enabled = false, - time_passed = 0, - params = nil, - - -- ##### Methods ############################################################################## - --[[ - Enable timer - ]]-- - enable = function(self) - self.enabled = true - end, - --[[ - Disable timer - ]]-- - disable = function(self) - self.enabled = false - end, - - -- ##### Cycle ################################################################################ - --[[ - Process tick - ]]-- - tick = function(self) - --if self.on_tick and type(self.on_tick) == "function" then - self:on_tick(self.params) - --end - end, - --[[ - Update - ]]-- - update = function(self, dt) - if self.enabled then - self.time_passed = self.time_passed + dt - if self.time_passed >= self.rate / 1000 then - self:tick() - self.time_passed = 0 - end - else - self.time_passed = 0 - end - end, - - -- ##### Events ################################################################################ - --[[ - On click event - ]]-- - on_tick = function(self, ...) - end, - }, - --[[ - Create timer - ]]-- - create_timer = function(self, name, rate, enabled, on_tick, ...) --, wait) - if not table.has_item(self.items, name) then - local new_timer = table.clone(self.template) - new_timer.name = name or "timer_" .. tostring(#self.items+1) - new_timer.rate = rate or new_timer.rate - new_timer.enabled = enabled or new_timer.enabled - new_timer.on_tick = on_tick or new_timer.on_tick - new_timer.params = ... - --new_timer.wait = wait or new_timer.wait - self.items[name] = new_timer - return new_timer - end - return nil - end, - --[[ - Update timers - ]]-- - update = function(self, dt) - for name, timer in pairs(self.items) do - timer:update(dt) - end - end, -} - --- ################################################################################################################ --- ##### Input system ############################################################################################# --- ################################################################################################################ -gui.input = { - blocked_services = nil, - --[[ - Check and create input system - ]]-- - check = function(self) - if not Managers.input:get_input_service("mod_gui") then - Managers.input:create_input_service("mod_gui", "MOD_GUI_KEY_MAP") - Managers.input:map_device_to_service("mod_gui", "keyboard") - Managers.input:map_device_to_service("mod_gui", "mouse") - Managers.input:map_device_to_service("mod_gui", "gamepad") - end - end, - --[[ - Get list of unblocked input services and block them - ]]-- - block = function(self) - if not self.blocked_services then - self.blocked_services = {} - Managers.input:get_unblocked_services("keyboard", 1, self.blocked_services) - for _, s in pairs(self.blocked_services) do - Managers.input:device_block_service("keyboard", 1, s) - Managers.input:device_block_service("mouse", 1, s) - Managers.input:device_block_service("gamepad", 1, s) - end - end - end, - --[[ - Unblock previously blocked services - ]]-- - unblock = function(self) - if self.blocked_services then - for _, s in pairs(self.blocked_services) do - Managers.input:device_unblock_service("keyboard", 1, s) - Managers.input:device_unblock_service("mouse", 1, s) - Managers.input:device_unblock_service("gamepad", 1, s) - end - self.blocked_services = nil - end - end, -} - --- ################################################################################################################ --- ##### Font system ############################################################################################## --- ################################################################################################################ -gui.fonts = { - fonts = {}, - --[[ - Font template - ]]-- - template = { - font = "hell_shark", - material = "materials/fonts/gw_body_32", - size = 22, - font_size = function(self) - if not self.dynamic_size then - return self.size - else - local screen_w, screen_h = UIResolution() - local size = screen_w / 100 - return size - end - end, - dynamic_size = false, - }, - --[[ - Create font - ]]-- - create = function(self, name, font, size, material, dynamic_size) - if not table.has_item(self.fonts, name) then - local new_font = table.clone(self.template) - new_font.font = font or new_font.font - new_font.material = material or new_font.material - new_font.size = size or new_font.size - new_font.dynamic_size = dynamic_size or new_font.dynamic_size - self.fonts[name] = new_font - end - end, - --[[ - Get font by name - ]]-- - get = function(self, name) - for k, font in pairs(self.fonts) do - if k == name then - return font - end - end - return gui.fonts.default or nil - end, -} - --- ################################################################################################################ --- ##### Anchor system ############################################################################################ --- ################################################################################################################ -gui.anchor = { - styles = { - "bottom_left", - "center_left", - "top_left", - "middle_top", - "top_right", - "center_right", - "bottom_right", - "middle_bottom", - "fill", - }, - bottom_left = { - position = function(window, widget) - local x = window.position[1] + widget.offset[1] - local y = window.position[2] + widget.offset[2] - return {x, y}, widget.size - end, - }, - center_left = { - position = function(window, widget) - local x = window.position[1] + widget.offset[1] - local y = window.position[2] + window.size[2]/2 - widget.size[2]/2 - return {x, y}, widget.size - end, - }, - top_left = { - position = function(window, widget) - local x = window.position[1] + widget.offset[1] - local y = window.position[2] + window.size[2] - widget.offset[2] - widget.size[2] - return {x, y}, widget.size - end, - }, - middle_top = { - position = function(window, widget) - local x = window.position[1] + window.size[1]/2 - widget.size[1]/2 - local y = window.position[2] + window.size[2] - widget.offset[2] - widget.size[2] - return {x, y}, widget.size - end, - }, - top_right = { - position = function(window, widget) - local x = window.position[1] + window.size[1] - widget.offset[1] - widget.size[1] - local y = window.position[2] + window.size[2] - widget.offset[2] - widget.size[2] - return {x, y}, widget.size - end, - }, - center_right = { - position = function(window, widget) - local x = window.position[1] + window.size[1] - widget.offset[1] - widget.size[1] - local y = window.position[2] + window.size[2]/2 - widget.size[2]/2 - return {x, y}, widget.size - end, - }, - bottom_right = { - position = function(window, widget) - local x = window.position[1] + window.size[1] - widget.offset[1] - widget.size[1] - local y = window.position[2] + widget.offset[2] - return {x, y}, widget.size - end, - }, - middle_bottom = { - position = function(window, widget) - local x = window.position[1] + window.size[1]/2 - widget.size[1]/2 - local y = window.position[2] + widget.offset[2] - return {x, y}, widget.size - end, - }, - fill = { - position = function(window, widget) - return {window.position[1], window.position[2]}, {window.size[1], window.size[2]} - end, - }, -} - --- ################################################################################################################ --- ##### Textalignment system ##################################################################################### --- ################################################################################################################ -gui.text_alignment = { - bottom_left = { - position = function(text, font, bounds) - local scale = UIResolutionScale() - local text_width = basic_gui.text_width(text, font.material, font:font_size()) - local text_height = font:font_size() --basic_gui.text_height(text, font.material, font:font_size()) - local frame_width = bounds[2] - bounds[1] - local frame_height = bounds[3] - bounds[4] - local left = bounds[1] - local bottom = bounds[3] - local fix = 2*scale - return {left + fix, bottom + fix} - end, - }, - bottom_center = { - position = function(text, font, bounds) - local scale = UIResolutionScale() - local text_width = basic_gui.text_width(text, font.material, font:font_size()) - local text_height = font:font_size() --basic_gui.text_height(text, font.material, font:font_size()) - local frame_width = bounds[2] - bounds[1] - local frame_height = bounds[3] - bounds[4] - local left = bounds[1] - local bottom = bounds[3] - local fix = 2*scale - return {left + frame_width/2 - text_width/2, bottom + fix} - end, - }, - bottom_right = { - position = function(text, font, bounds) - local scale = UIResolutionScale() - local text_width = basic_gui.text_width(text, font.material, font:font_size()) - local text_height = font:font_size() --basic_gui.text_height(text, font.material, font:font_size()) - local frame_width = bounds[2] - bounds[1] - local frame_height = bounds[3] - bounds[4] - local right = bounds[2] - local bottom = bounds[3] - local fix = 2*scale - return {right - text_width, bottom + fix} - end, - }, - middle_left = { - position = function(text, font, bounds, padding) - local scale = UIResolutionScale() - local text_height = font:font_size() - local frame_height = bounds[4] - bounds[3] - local left = bounds[1] - local bottom = bounds[3] - --local border = 5*scale - local fix = 2*scale - return {left + fix, bottom + fix + (frame_height/2) - (text_height/2)} - end, - }, - middle_center = { - position = function(text, font, bounds) - local scale = UIResolutionScale() - local text_width = basic_gui.text_width(text, font.material, font:font_size()) - local text_height = font:font_size() --basic_gui.text_height(text, font.material, font:font_size()) - local frame_width = bounds[2] - bounds[1] - local frame_height = bounds[4] - bounds[3] - local left = bounds[1] - local bottom = bounds[3] - local fix = 2*scale - return {left + fix + frame_width/2 - text_width/2, bottom + fix + (frame_height/2) - (text_height/2)} - end, - }, - middle_right = { - position = function(text, font, bounds) - local scale = UIResolutionScale() - local text_width = basic_gui.text_width(text, font.material, font:font_size()) - local text_height = font:font_size() --basic_gui.text_height(text, font.material, font:font_size()) - local frame_width = bounds[2] - bounds[1] - local frame_height = bounds[4] - bounds[3] - local right = bounds[2] - local bottom = bounds[3] - local fix = 2*scale - return {right - text_width - fix, bottom + fix + (frame_height/2) - (text_height/2)} - end, - }, - top_left = { - position = function(text, font, bounds) - local scale = UIResolutionScale() - local text_width = basic_gui.text_width(text, font.material, font:font_size()) - local text_height = font:font_size() --basic_gui.text_height(text, font.material, font:font_size()) - local frame_width = bounds[2] - bounds[1] - local frame_height = bounds[4] - bounds[3] - local left = bounds[1] - local top = bounds[4] - local fix = 2*scale - return {left + fix, top - fix - text_height/2} - end, - }, - top_center = { - position = function(text, font, bounds) - local scale = UIResolutionScale() - local text_width = basic_gui.text_width(text, font.material, font:font_size()) - local text_height = font:font_size() --basic_gui.text_height(text, font.material, font:font_size()) - local frame_width = bounds[2] - bounds[1] - local frame_height = bounds[4] - bounds[3] - local left = bounds[1] - local top = bounds[4] - local fix = 2*scale - return {left + frame_width/2 - text_width/2, top - fix - text_height/2} - end, - }, - top_right = { - position = function(text, font, bounds) - local scale = UIResolutionScale() - local text_width = basic_gui.text_width(text, font.material, font:font_size()) - local text_height = font:font_size() --basic_gui.text_height(text, font.material, font:font_size()) - local frame_width = bounds[2] - bounds[1] - local frame_height = bounds[4] - bounds[3] - local right = bounds[2] - local bottom = bounds[3] - local fix = 2*scale - return {right - fix - text_width, bottom - fix + frame_height - (text_height/2)} - end, - }, -} - --- ################################################################################################################ --- ##### widgets ################################################################################################# --- ################################################################################################################ -gui.widgets = { - - window = { - name = "", - position = {0, 0}, - size = {0, 0}, - original_size = {0, 0}, - initialized = false, - hovered = false, - cursor = {0, 0}, - dragging = false, - drag_offset = {0, 0}, - resizing = false, - resize_offset = {0, 0}, - resize_origin = {0, 0}, - z_order = 0, - widgets = {}, - visible = true, - transparent = false, - - -- ################################################################################################################ - -- ##### Init ##################################################################################################### - -- ################################################################################################################ - --[[ - Set value - --]] - set = function(self, attribute, value) - self[attribute] = value - end, - --[[ - Refresh theme - --]] - refresh_theme = function(self) - self.theme = {} - -- Default - local theme_element = gui.themes[gui.theme].default - if theme_element then self:copy_theme_element(theme_element) end - -- Specific - local theme_element = gui.themes[gui.theme]["window"] - if theme_element then self:copy_theme_element(theme_element) end - end, - --[[ - Copy theme element - --]] - copy_theme_element = function(self, theme_element) - -- Go through elements - for key, element in pairs(theme_element) do - -- Set element - self.theme[key] = element - end - end, - - -- ################################################################################################################ - -- ##### Create widgets ########################################################################################## - -- ################################################################################################################ - --[[ - Create title bar - --]] - create_title = function(self, name, text, height) - -- Base widget - local widget = self:create_widget(name, nil, nil, "title") - -- Set attributes - widget:set("text", text or "") - widget:set("height", height or gui.themes[gui.theme].title.height) - -- Add widget - self:add_widget(widget) - return widget - end, - --[[ - Create button - --]] - create_button = function(self, name, position, size, text, on_click, anchor) - -- Base widget - local widget = self:create_widget(name, position, size, "button", anchor) - -- Set attributes - widget:set("text", text or "") - if on_click then - widget:set("on_click", on_click) - end - -- Add widget - self:add_widget(widget) - return widget - end, - --[[ - Create resizer - --]] - --[[create_resizer = function(self, name, size) - -- Base widget - local widget = self:create_widget(name, nil, size, "resizer") - -- Set attributes - widget:set("size", size or gui.themes[gui.theme].resizer.size) - -- Add widget - self:add_widget(widget) - return widget - end,--]] - --[[ - Create close button - --]] - create_close_button = function(self, name) - local widget = self:create_widget(name, {5, 0}, {25, 25}, "close_button", gui.anchor.styles.top_right) - widget:set("text", "X") - self:add_widget(widget) - return widget - end, - --[[ - Create textbox - --]] - create_textbox = function(self, name, position, size, text, placeholder, on_text_changed) - local widget = self:create_widget(name, position, size, "textbox") - widget:set("text", text or "") - widget:set("placeholder", placeholder or "") - if on_text_changed then - widget:set("on_text_changed", on_text_changed) - end - self:add_widget(widget) - return widget - end, - --[[ - Create checkbox - --]] - create_checkbox = function(self, name, position, size, text, value, on_value_changed) - local widget = self:create_widget(name, position, size, "checkbox") - widget:set("text", text or "") - widget:set("value", value or false) - if on_value_changed then - widget:set("on_value_changed", on_value_changed) - end - self:add_widget(widget) - return widget - end, - --[[ - Create label - --]] - create_label = function(self, name, position, size, text) - local widget = self:create_widget(name, position, size, "label") - widget:set("text", text or "") - self:add_widget(widget) - return widget - end, - --[[ - Create widget - --]] - create_dropdown = function(self, name, position, size, options, selected_index, on_index_changed, show_items_num) - local widget = self:create_widget(name, position, size, "dropdown") - --local widget.widgets = {} - widget:set("text", "") - widget:set("options", {}) - widget:set("index", selected_index) - --table.sort(options) - gui:pcall(function() - for text, index in pairs(options) do - local sub_widget = self:create_dropdown_item(name, index, widget, text) - -- gui:echo("--") - -- gui:echo(tostring(index)) - widget.options[#widget.options+1] = sub_widget - end - end) - widget:set("show_items_num", show_items_num or 2) - if on_index_changed then - widget:set("on_index_changed", on_index_changed) - end - self:add_widget(widget) - return widget - end, - create_dropdown_item = function(self, name, index, parent, text) - local widget = self:create_widget(name.."_option_"..text, {0, 0}, {0, 0}, "dropdown_item") - widget:set("text", text or "") - widget:set("index", index) - widget:set("parent", parent) - widget:set("anchor", nil) - widget:set("z_order", 1) - return widget - end, - --[[ - Create widget - --]] - create_widget = function(self, name, position, size, _type, anchor) - - - position = gui.adjust_to_fit_scale(position) - size = gui.adjust_to_fit_scale(size) - - -- Create widget - local widget = table.clone(gui.widgets.widget) - widget.name = name or "name" - widget.position = position or {0, 0} - widget.offset = widget.position - widget.size = size or {0, 0} - widget._type = _type or "button" - widget.window = self - -- Anchor - widget.anchor = anchor or "bottom_left" - -- Setup functions and theme - widget:setup() - - return widget - end, - --[[ - Add widget to list - --]] - add_widget = function(self, widget) - self:inc_z_orders(#self.widgets) - widget.z_order = 1 - self.widgets[#self.widgets+1] = widget - end, - - -- ################################################################################################################ - -- ##### Methods ################################################################################################## - -- ################################################################################################################ - --[[ - Initialize window - --]] - init = function(self) - -- Event - self:on_init() - - -- Theme - self:refresh_theme() - - -- Init widgets - if #self.widgets > 0 then - for _, widget in pairs(self.widgets) do - widget:init() - end - end - - self:update() - - self.initialized = true - end, - --[[ - Bring window to front - --]] - bring_to_front = function(self) - if not self:has_focus() then - gui.windows:unfocus() - gui.windows:inc_z_orders(self.z_order) - self.z_order = 1 - end - end, - --[[ - Destroy window - --]] - destroy = function(self) - self:before_destroy() - gui.windows:dec_z_orders(self.z_order) - table.remove(gui.windows.list, self:window_index()) - end, - --[[ - Increase z orders - --]] - inc_z_orders = function(self, changed_z) - for z=changed_z, 1, -1 do - for _, widget in pairs(self.widgets) do - if widget.z_order == z then - widget.z_order = widget.z_order + 1 - end - end - end - end, - --[[ - Decrease z orders - --]] - dec_z_orders = function(self, changed_z) - --for z=changed_z, #self.widgets do - for z=changed_z+1, #self.widgets do - for _, widget in pairs(self.widgets) do - if widget.z_order == z then - widget.z_order = widget.z_order - 1 - end - end - end - end, - --[[ - Focus window - --]] - focus = function(self) - self:bring_to_front() - self:on_focus() - end, - --[[ - Unfocus window - --]] - unfocus = function(self) - for _, widget in pairs(self.widgets) do - widget:unfocus() - end - self:on_unfocus() - end, - --[[ - Hover window - --]] - hover = function(self, cursor) - if not self.hovered then self:hover_enter() end - self.cursor = cursor - self:on_hover(cursor) - end, - --[[ - Start hover window - --]] - hover_enter = function(self) - --gui.mouse:un_hover_all(gui.windows.list) - self.hovered = true - self:on_hover_enter() - end, - --[[ - End hover window - --]] - hover_exit = function(self) - self.hovered = false - self:on_hover_exit() - end, - --[[ - Click window - --]] - click = function(self, position) - --self:focus() - local clicked = false - for z=1, #self.widgets do - for _, widget in pairs(self.widgets) do - if widget.z_order == z then - if not gui.point_in_bounds(position, widget:extended_bounds()) then - widget:unfocus() - end - if not clicked and gui.point_in_bounds(position, widget:extended_bounds()) then - widget:click() - clicked = true - end - end - end - end - self:on_click(position) - end, - --[[ - Release click window - --]] - release = function(self, position) - self:focus() - local released = false - for z=1, #self.widgets do - for _, widget in pairs(self.widgets) do - if widget.z_order == z then - if not gui.point_in_bounds(position, widget:extended_bounds()) then - widget:unfocus() - end - if not released and gui.point_in_bounds(position, widget:extended_bounds()) then - widget:release() - released = true - end - end - end - end - self:on_release(position) - end, - - -- ################################################################################################################ - -- ##### Events ################################################################################################### - -- ################################################################################################################ - --[[ - Window is initialized - --]] - on_init = function(self) - end, - --[[ - Window gets focus - --]] - on_focus = function(self) - end, - --[[ - Window loses focus - --]] - on_unfocus = function(self) - end, - --[[ - Window is hovered - --]] - on_hover = function(self, cursor) - end, - --[[ - Window starts being hovered - --]] - on_hover_enter = function(self) - end, - --[[ - Window ends being hovered - --]] - on_hover_exit = function(self) - end, - --[[ - Window is clicked - --]] - on_click = function(self, position) - end, - --[[ - Window click released - --]] - on_release = function(self, position) - end, - --[[ - Before window is updated - --]] - before_update = function(self) - end, - --[[ - Window was updated - --]] - after_update = function(self) - end, - --[[ - Window is dragged - --]] - on_dragged = function(self) - end, - --[[ - Window is resized - --]] - on_resize = function(self) - end, - --[[ - Before window is destroyed - --]] - before_destroy = function(self) - end, - - -- ################################################################################################################ - -- ##### Attributes ############################################################################################### - -- ################################################################################################################ - --[[ - Check if window has focus - --]] - has_focus = function(self) - return self.z_order == 1 - end, - --[[ - Get window index - --]] - window_index = function(self) - for i=1, #gui.windows.list do - if gui.windows.list[i] == self then return i end - end - return 0 - end, - --[[ - Window bounds - --]] - bounds = function(self) - return gui.to_bounds(self.position, self.size) - end, - extended_bounds = function(self) - local bounds = self:bounds() - for _, widget in pairs(self.widgets) do - local cbounds = widget:extended_bounds() - if cbounds[1] < bounds[1] then bounds[1] = cbounds[1] end - if cbounds[2] > bounds[2] then bounds[2] = cbounds[2] end - if cbounds[3] < bounds[3] then bounds[3] = cbounds[3] end - if cbounds[4] > bounds[4] then bounds[4] = cbounds[4] end - end - return bounds - end, - widget_bounds = function(self, exclude_resizer) - local bounds = {} - - for _, widget in pairs(self.widgets) do - if exclude_resizer and widget._type == "resizer" then - return {0, 0, 0, 0} - else - local cbounds = widget:extended_bounds() - if not bounds[1] or cbounds[1] < bounds[1] then bounds[1] = cbounds[1] end - if not bounds[2] or cbounds[2] > bounds[2] then bounds[2] = cbounds[2] end - if not bounds[3] or cbounds[3] < bounds[3] then bounds[3] = cbounds[3] end - if not bounds[4] or cbounds[4] > bounds[4] then bounds[4] = cbounds[4] end - end - end - return bounds - end, - --[[ - Z position - --]] - position_z = function(self) - return 800 + (#gui.windows.list - self.z_order) - end, - --[[ - Get widget by name - --]] - get_widget = function(self, name) - for _, widget in pairs(self.widgets) do - if widget.name == name then - return widget - end - end - return nil - end, - - -- ################################################################################################################ - -- ##### Cycle #################################################################################################### - -- ################################################################################################################ - --[[ - Update window - --]] - update = function(self) - if self.initialized then - self:before_update() - if self:has_focus() then - -- Get cursor position - local cursor = gui.mouse.cursor() - -- Drag - self:drag(cursor) - -- Resize - self:resize(cursor) - -- Update widgets - self:update_widgets() - end - self:after_update() - end - end, - --[[ - Resize window - --]] - drag = function(self, cursor) - if self.dragging then - self.position = {cursor[1] - self.drag_offset[1], cursor[2] - self.drag_offset[2]} - self:on_dragged() - end - end, - --[[ - Resize window - --]] - resize = function(self, cursor) - if self.resizing then - local new_size = { - cursor[1] - self.resize_origin[1] + self.resize_offset[1], - self.resize_origin[2] - cursor[2] + self.resize_offset[2], - } - if new_size[1] < self.original_size[1] then new_size[1] = self.original_size[1] end - if new_size[2] < self.original_size[2] then new_size[2] = self.original_size[2] end - self.size = new_size - local widget_bounds = self:widget_bounds(true) - if self.size[1] < widget_bounds[2] - widget_bounds[1] then - self.size[1] = widget_bounds[2] - widget_bounds[1] - end - if self.size[2] < widget_bounds[4] - widget_bounds[3] then - self.size[2] = widget_bounds[4] - widget_bounds[3] - end - self.position = {self.position[1], self.resize_origin[2] - new_size[2]} - self:on_resize() - end - end, - --[[ - Update widgets - --]] - update_widgets = function(self) - if #self.widgets > 0 then - local catched = false - for z=1, #self.widgets do - for _, widget in pairs(self.widgets) do - if widget.z_order == z and not catched then - catched = widget:update() - end - end - end - end - end, - - -- ################################################################################################################ - -- ##### Render ################################################################################################### - -- ################################################################################################################ - render = function(self) - if not self.visible then return end - self:render_shadow() - self:render_background() - self:render_widgets() - end, - --[[ - Render window - --]] - render_background = function(self) - if not self.visible or self.transparent then return end - local color = ColorHelper.unbox(self.theme.color) - if self.hovered then - color = ColorHelper.unbox(self.theme.color_hover) - end - basic_gui.rect(self.position[1], self.position[2], self:position_z(), self.size[1], self.size[2], color) - end, - --[[ - Render shadow - --]] - render_shadow = function(self) - if not self.visible then return end - -- Theme - local layers = self.theme.shadow.layers - local border = self.theme.shadow.border - local cv = self.theme.shadow.color - -- Render - for i=1, layers do - local color = Color((cv[1]/layers)*i, cv[2], cv[3], cv[4]) - local layer = layers-i - basic_gui.rect(self.position[1]+layer-border, self.position[2]-layer-border, self:position_z(), - self.size[1]-layer*2+border*2, self.size[2]+layer*2+border*2, color) - end - for i=1, layers do - local color = Color((cv[1]/layers)*i, cv[2], cv[3], cv[4]) - local layer = layers-i - basic_gui.rect(self.position[1]-layer-border, self.position[2]+layer-border, self:position_z(), - self.size[1]+layer*2+border*2, self.size[2]-layer*2+border*2, color) - end - end, - --[[ - Render widgets - --]] - render_widgets = function(self) - if not self.visible then return end - if #self.widgets > 0 then - for z=#self.widgets, 1, -1 do - for _, widget in pairs(self.widgets) do - if widget.z_order == z and widget.visible then - widget:render() - end - end - end - end - end, - - }, - - widget = { - name = "", - position = {0, 0}, - size = {0, 0}, - _type = "", - anchor = "", - hovered = false, - cursor = {0, 0}, - --colors = {}, - z_order = 0, - visible = true, - theme = {}, - - -- ################################################################################################################ - -- ##### widget Methods ########################################################################################## - -- ################################################################################################################ - --[[ - Initialize - --]] - init = function(self) - -- Trigger update - self:update() - -- Trigger event - self:on_init() - end, - --[[ - Click - --]] - click = function(self) - -- Disabled - if self.disabled then return end - -- Click - self.clicked = true - end, - --[[ - Release - --]] - release = function(self) - -- Disabled - if self.disabled then return end - -- Clicked - if not self.clicked then return end - -- Release - self.clicked = false - -- Trigger event - self:on_click() - end, - --[[ - Focus - --]] - focus = function(self) - -- Disabled - if self.disabled then return end - -- Focus - self.has_focus = true - end, - --[[ - Unfocus - --]] - unfocus = function(self) - -- Unfocus - self.hovered = false - self.clicked = false - self.has_focus = false - end, - -- ################################################################################################################ - -- ##### Init ##################################################################################################### - -- ################################################################################################################ - --[[ - Set value - --]] - set = function(self, attribute, value) - self[attribute] = value - end, - --[[ - Refresh theme - --]] - refresh_theme = function(self) - self.theme = {} - -- Default - local theme_element = gui.themes[gui.theme].default - if theme_element then self:copy_theme_element(theme_element) end - -- Specific - local theme_element = gui.themes[gui.theme][self._type] - if theme_element then self:copy_theme_element(theme_element) end - end, - --[[ - Copy theme element - --]] - copy_theme_element = function(self, theme_element) - -- Go through elements - for key, element in pairs(theme_element) do - -- Set element - self.theme[key] = element - end - end, - --[[ - Setup widget - --]] - setup = function(self) - -- Copy widget specific functions - local widget_element = gui.widgets[self._type] - if widget_element then self:copy_widget_element(widget_element) end - -- Refresh theme - self:refresh_theme() - end, - --[[ - Copy widget element - --]] - copy_widget_element = function(self, widget_element) - -- Go through elements - for key, element in pairs(widget_element) do - if type(element) == "function" then - -- If function save callback to original function - if self[key] then self[key.."_base"] = self[key] end - self[key] = element - else - -- Set element - self[key] = element - end - end - end, - -- ################################################################################################################ - -- ##### Cycle #################################################################################################### - -- ################################################################################################################ - --[[ - Update - --]] - update = function(self) - -- Trigger event - self:before_update() - -- Disabled - if self.disabled or not self.visible then return end - -- Mouse position - local cursor = gui.mouse.cursor() - -- Set widget position via anchor - if self.anchor then - self.position, self.size = gui.anchor[self.anchor].position(self.window, self) - end - -- Check hovered - self.hovered, self.cursor = gui.point_in_bounds(cursor, self:extended_bounds()) - if self.hovered then - if self.tooltip then basic_gui.tooltip(self.tooltip) end - self:on_hover() - end - -- Clicked - if self.clicked then - self.clicked = self.hovered - end - -- Trigger event - self:after_update() - -- Return - return self.clicked - end, - -- ################################################################################################################ - -- ##### Render ################################################################################################### - -- ################################################################################################################ - --[[ - Main Render - --]] - render = function(self) - -- Visible - if not self.visible then return end - -- Render shadow - self:render_shadow() - -- Render background - self:render_background() - -- Render text - self:render_text() - end, - --[[ - Render shadow - --]] - render_shadow = function(self) - -- Visible - if not self.visible then return end - -- Shadow set - if self.theme.shadow then - -- Get theme value - local layers = self.theme.shadow.layers - local border = self.theme.shadow.border - local cv = self.theme.shadow.color - -- Render - for i=1, layers do - local layer = layers-i - local color = Color((cv[1]/layers)*i, cv[2], cv[3], cv[4]) - basic_gui.rect(self.position[1]+layer-border, self.position[2]-layer-border, self:position_z(), - self.size[1]-layer*2+border*2, self.size[2]+layer*2+border*2, color) - end - for i=1, layers do - local layer = layers-i - local color = Color((cv[1]/layers)*i, cv[2], cv[3], cv[4]) - basic_gui.rect(self.position[1]-layer-border, self.position[2]+layer-border, self:position_z(), - self.size[1]+layer*2+border*2, self.size[2]-layer*2+border*2, color) - end - end - end, - --[[ - Render background - --]] - render_background = function(self) - -- Visible - if not self.visible then return end - -- Get current theme color - local color = ColorHelper.unbox(self.theme.color) - if self.clicked then - color = ColorHelper.unbox(self.theme.color_clicked) - elseif self.hovered then - color = ColorHelper.unbox(self.theme.color_hover) - end - -- Get bounds - local bounds = self:extended_bounds() - -- Render background rectangle - basic_gui.rect(bounds[1], bounds[4], self:position_z(), bounds[2]-bounds[1], bounds[3]-bounds[4], color) - end, - --[[ - Render text - --]] - render_text = function(self) - -- Visible - if not self.visible then return end - -- Get current theme color - local color = ColorHelper.unbox(self.theme.color_text) - if self.clicked then - color = ColorHelper.unbox(self.theme.color_text_clicked) - elseif self.hovered then - color = ColorHelper.unbox(self.theme.color_text_hover) - end - -- Get text info - local text = self.text or "" - --local font = self.theme.font - local font = gui.fonts:get(self.theme.font) - -- Get text alignment - local position = {self.position[1] + self.size[2]*0.2, self.position[2] + self.size[2]*0.2} - --local align = self.theme.text_alignment - local align = gui.text_alignment[self.theme.text_alignment] - if align then - position = align.position(text, font, self:bounds()) - end - -- Render text - basic_gui.text(text, position[1], position[2], self:position_z()+1, font:font_size(), color, font.font) - end, - -- ################################################################################################################ - -- ##### Attributes ############################################################################################### - -- ################################################################################################################ - --[[ - Bounds - --]] - bounds = function(self) - return gui.to_bounds(self.position, self.size) - end, - extended_bounds = function(self) - return self:bounds() - end, - --[[ - Position Z - --]] - position_z = function(self) - return self.window:position_z() + (#self.window.widgets - self.z_order) - end, - -- ################################################################################################################ - -- ##### Events ################################################################################################### - -- ################################################################################################################ - --[[ - On init - --]] - on_init = function(self) - end, - --[[ - On click - --]] - on_click = function(self) - end, - --[[ - On hover - --]] - on_hover = function(self) - end, - --[[ - Before update - --]] - before_update = function(self) - end, - --[[ - After update - --]] - after_update = function(self) - end, - }, - - title = { - -- ################################################################################################################ - -- ##### widget overrides ######################################################################################## - -- ################################################################################################################ - --[[ - Init override - --]] - init = function(self) - -- Original function - self:init_base() - -- Change - self.height = self.height or gui.themes[gui.theme].title.height - end, - --[[ - Click override - --]] - click = function(self) - -- Disabled - if self.disabled then return end - -- Original function - self:click_base() - -- Drag - self:drag() - end, - -- ################################################################################################################ - -- ##### Cycle overrides ########################################################################################## - -- ################################################################################################################ - --[[ - Update override - --]] - update = function(self) - -- Set bounds - self.size = {self.window.size[1], self.height or gui.themes[gui.theme].title.height} - self.position = {self.window.position[1], self.window.position[2] + self.window.size[2] - self.size[2]} - -- Disabled - if self.disabled then return end - -- Hover - local cursor = gui.mouse.cursor() - self.hovered, self.cursor = gui.point_in_bounds(cursor, self:bounds()) - -- Drag - if self.window.dragging then self:drag() end - -- Return - return self.clicked or self.window.dragging - end, - -- ################################################################################################################ - -- ##### Drag ##################################################################################################### - -- ################################################################################################################ - --[[ - Drag start - --]] - drag_start = function(self) - -- Set offset - self.window.drag_offset = self.window.cursor - -- Dragging - self.window.dragging = true - -- Block input - gui.input:block() - -- Trigger event - self:before_drag() - end, - --[[ - Drag - --]] - drag = function(self) - -- Catch start event - if not self.window.dragging then self:drag_start() end - -- Check mouse button - self.window.dragging = not stingray.Mouse.released(stingray.Mouse.button_id("left")) - -- Drag - self:on_drag() - -- Catch end event - if not self.window.dragging then self:drag_end() end - end, - --[[ - Drag end - --]] - drag_end = function(self) - -- Unblock input - gui.input:unblock() - -- Trigger event - self:after_drag() - end, - -- ################################################################################################################ - -- ##### Events ################################################################################################### - -- ################################################################################################################ - --[[ - On drag start - --]] - before_drag = function(self) - end, - --[[ - On drag - --]] - on_drag = function(self) - end, - --[[ - On drag end - --]] - after_drag = function(self) - end, - }, - - button = {}, - - resizer = { - -- ################################################################################################################ - -- ##### widget overrides ######################################################################################## - -- ################################################################################################################ - --[[ - Init override - --]] - init = function(self) - -- Original function - self:init_base() - -- Change - self.size = self.theme.size - end, - --[[ - Click override - --]] - click = function(self) - -- Disabled - if self.disabled then return end - -- Original function - self:click_base() - -- Resize - self:resize() - end, - -- ################################################################################################################ - -- ##### Resize ################################################################################################### - -- ################################################################################################################ - --[[ - Start resize - --]] - resize_start = function(self) - -- Save offset - self.window.resize_offset = {self.window.size[1] - self.window.cursor[1], self.window.cursor[2]} - self.window.resize_origin = {self.window.position[1], self.window.position[2] + self.window.size[2]} - -- Set resizing - self.window.resizing = true - gui.input:block() - -- Trigger event - self:before_resize() - end, - --[[ - Resize - --]] - resize = function(self) - -- Catch start event - if not self.window.resizing then self:resize_start() end - -- Check mouse button - self.window.resizing = not stingray.Mouse.released(stingray.Mouse.button_id("left")) - -- Trigger event - self:on_resize() - -- Catch end event - if not self.window.resizing then self:resize_end() end - end, - --[[ - Resize end - --]] - resize_end = function(self) - -- Block input - gui.input:unblock() - -- Trigger event - self:after_resize() - end, - -- ################################################################################################################ - -- ##### Cycle overrides ########################################################################################## - -- ################################################################################################################ - --[[ - Update override - --]] - update = function(self) - -- Update position - self.position = {self.window.position[1] + self.window.size[1] - self.size[1] - 5, self.window.position[2] + 5} - -- Disabled - if self.disabled then return end - -- Hover - local cursor = gui.mouse.cursor() - --local bounds = gui.to_bounds({self.position[1], self.position[2]}, self.size) - self.hovered, self.cursor = gui.point_in_bounds(cursor, self:bounds()) - -- Resize - if self.window.resizing then self:resize() end - -- Return - return self.clicked or self.window.resizing - end, - -- ################################################################################################################ - -- ##### Render overrides ######################################################################################### - -- ################################################################################################################ - --[[ - render_background override - --]] - render_background_bak = function(self) - -- if self.window.resizing then - -- local color = ColorHelper.unbox(self.theme.color_clicked) - -- local bounds = self:bounds() - -- basic_gui.rect(bounds[1], bounds[4], self:position_z(), bounds[2]-bounds[1], bounds[3]-bounds[4], color) - -- else - -- self:render_background_base() - -- end - end, - -- ################################################################################################################ - -- ##### Events ################################################################################################### - -- ################################################################################################################ - --[[ - Before resize - --]] - before_resize = function(self) - end, - --[[ - On resize - --]] - on_resize = function(self) - end, - --[[ - After resize - --]] - after_resize = function(self) - end, - }, - - close_button = { - -- ################################################################################################################ - -- ##### widget overrides ######################################################################################## - -- ################################################################################################################ - --[[ - Init override - --]] - init = function(self) - -- Original function - self:init_base() - -- Change - self.size = self.theme.size - end, - -- ################################################################################################################ - -- ##### Events ################################################################################################### - -- ################################################################################################################ - --[[ - OnClick override - --]] - on_click = function(self) - -- Destroy window - self.window:destroy() - end, - }, - - textbox = { - -- ################################################################################################################ - -- ##### widget overrides ######################################################################################## - -- ################################################################################################################ - --[[ - Init override - --]] - init = function(self) - -- Original function - self:init_base() - -- Input cursor timer - self.input_cursor = { - timer = gui.timers:create_timer(self.name .. "_input_cursor_timer", 500, true, self.on_input_cursor_timer, self), - state = true, - position = 0, - } - -- Input timer - self.input = { - --[[timer = gui.timers:create_timer(self.name .. "_input_timer", 100, true, self.on_input_timer, self),--]] - ready = true, - } - end, - --[[ - Release override - --]] - release = function(self) - -- Disabled - if self.disabled then return end - -- Clicked - if self.clicked then self:focus() end - -- Original function - self:release_base() - end, - --[[ - Text changed - --]] - text_changed = function(self) - -- Disable input - --self.input.ready = false - -- Enable input timer - --self.input.timer.enabled = true - -- Trigger event - self:on_text_changed() - end, - --[[ - Focus override - --]] - focus = function(self) - -- Disabled - if self.disabled then return end - -- Block input - if not self.has_focus then - gui.input:block() - end - -- Original function - self:focus_base() - end, - --[[ - Unfocus override - --]] - unfocus = function(self) - -- Disabled - if self.disabled then return end - -- Unblock input - if self.has_focus then - gui.input:unblock() - end - -- Original function - self:unfocus_base() - end, - -- ################################################################################################################ - -- ##### Events ################################################################################################### - -- ################################################################################################################ - --[[ - Cursor timer - --]] - on_input_cursor_timer = function(self, textbox) - -- Toggle cursor state - textbox.input_cursor.state = not textbox.input_cursor.state - end, - --[[ - Input timer - --]] - --[[on_input_timer = function(self, textbox) - -- Disable timer - self.enabled = false - -- Accept input - textbox.input.ready = true - end,--]] - -- ################################################################################################################ - -- ##### Cycle overrides ########################################################################################## - -- ################################################################################################################ - --[[ - Update override - --]] - update = function(self) - -- Disabled - if self.disabled then return end - -- Original function - self:update_base() - -- Input - if self.has_focus then - -- Get input service - Managers.input:device_unblock_service("keyboard", 1, "mod_gui") - local input_service = Managers.input:get_service("mod_gui") - -- Check input and timer - if input_service and self.input.ready then - -- Get keystrokes - local keystrokes = stingray.Keyboard.keystrokes() - -- Check keystrokes - for _, key in pairs(keystrokes) do - print(key) - if type(key) == "string" then - -- If string check if special key - if not table.has_item(ui_special_keys, key) then - -- Oridinary printable character - self.text = self.text .. key - -- Trigger changed - self:text_changed() - elseif key == "space" then - -- Add space - self.text = self.text .. " " - -- Trigger changed - self:text_changed() - end - else - -- If not string it's widget key - if input_service:get("backspace") then - -- Handle backspace - remove last character - if string.len(self.text) >= 1 then - local _, count = string.gsub(self.text, "[^\128-\193]", "") - self.text = UTF8:utf8sub(self.text, 1, count-1) - -- Trigger changed - self:text_changed() - end - elseif input_service:get("esc") or input_service:get("enter") then - -- Unfocus - self:unfocus() - end - end - end - end - end - -- Return - return self.clicked or self.has_focus - end, - -- ################################################################################################################ - -- ##### Render overrides ######################################################################################### - -- ################################################################################################################ - --[[ - Render main override - --]] - render = function(self) - -- Visible - if not self.visible then return end - -- Original function - self:render_base() - -- Cursor - self:render_cursor() - end, - --[[ - Render text override - --]] - render_text = function(self) - -- Visible - if not self.visible then return end - -- Get current theme color - local color = ColorHelper.unbox(self.theme.color_placeholder) - if self.clicked then - color = ColorHelper.unbox(self.theme.color_text_clicked) - elseif self.hovered and #self.text > 0 then - color = ColorHelper.unbox(self.theme.color_text_hover) - elseif #self.text > 0 then - color = ColorHelper.unbox(self.theme.color_text) - end - -- Get text - local text = self.text or "" - if not self.has_focus then - text = #self.text > 0 and self.text or self.placeholder or "" - end - -- Get font - --local font = self.theme.font - local font = gui.fonts:get(self.theme.font) - -- Get text alignment - local position = {self.position[1] + self.size[2]*0.2, self.position[2] + self.size[2]*0.2} - --local align = self.theme.text_alignment - local align = gui.text_alignment[self.theme.text_alignment] - if align then - position = align.position(text, font, self:bounds()) - end - -- Render text - basic_gui.text(text, position[1], position[2], self:position_z()+1, font:font_size(), color, font.font) - end, - --[[ - Render cursor - --]] - render_cursor = function(self) - -- Visible - if not self.visible then return end - -- Render - if self.has_focus and self.input_cursor.state then - -- Get current theme color - local color = ColorHelper.unbox(self.theme.color_input_cursor) - -- Get data - local width = 0 - --local font = self.theme.font - local font = gui.fonts:get(self.theme.font) - if self.text and #self.text > 0 then - width = basic_gui.text_width(self.text, font.material, font:font_size()) - end - -- Render cursor - basic_gui.rect(self.position[1]+2+width, self.position[2]+2, self:position_z(), 2, self.size[2]-4, color) - end - end, - -- ################################################################################################################ - -- ##### Events ################################################################################################### - -- ################################################################################################################ - --[[ - On text changed - --]] - on_text_changed = function(self) - end, - }, - - checkbox = { - -- ################################################################################################################ - -- ##### Methods ################################################################################################## - -- ################################################################################################################ - --[[ - Toggle state - --]] - toggle = function(self) - -- Change - self.value = not self.value - -- Trigger event - self:on_value_changed() - end, - -- ################################################################################################################ - -- ##### widget overrides ######################################################################################## - -- ################################################################################################################ - --[[ - Release override - --]] - release = function(self) - -- Disabled - if self.disabled then return end - -- Original function - self:release_base() - -- Toggle - self:toggle() - end, - -- ################################################################################################################ - -- ##### Render overrides ######################################################################################### - -- ################################################################################################################ - --[[ - Render override - --]] - render = function(self) - -- Visible - if not self.visible then return end - -- Original function - self:render_base() - -- Render box - self:render_box() - end, - --[[ - Render text override - --]] - render_text = function(self) - -- Visible - if not self.visible then return end - -- Get current theme color - local color = ColorHelper.unbox(self.theme.color_text) - if self.clicked then - color = ColorHelper.unbox(self.theme.color_text_clicked) - elseif self.hovered then - color = ColorHelper.unbox(self.theme.color_text_hover) - end - -- Get font - --local font = self.theme.font - local font = gui.fonts:get(self.theme.font) - -- Get text alignment - local position = {self.position[1] + self.size[2] + 5, self.position[2] + self.size[2]*0.2} - -- local align = self.theme.text_alignment - -- if align then - -- position = align.position(self.text, font, self:bounds()) - -- end - -- Render text - basic_gui.text(self.text, position[1], position[2], self:position_z()+1, font:font_size(), color, font.font) - end, - --[[ - Render box - --]] - render_box = function(self) - -- Visible - if not self.visible then return end - -- Check value - if self.value then - -- Get current theme color - local color = ColorHelper.unbox(self.theme.color_text) - if self.clicked then - color = ColorHelper.unbox(self.theme.color_text_clicked) - elseif self.hovered then - color = ColorHelper.unbox(self.theme.color_text_hover) - end - local text = "X" - -- Get font - --local font = self.theme.font - local font = gui.fonts:get(self.theme.font) - -- Get text alignment - local position = {self.position[1] + 5, self.position[2] + self.size[2]*0.2} - --local align = self.theme.text_alignment - local align = gui.text_alignment[self.theme.text_alignment] - if align then - position = align.position(text, font, self:bounds()) - end - -- Render text - basic_gui.text(text, position[1], position[2], self:position_z()+1, font:font_size(), color, font.font) - end - end, - -- ################################################################################################################ - -- ##### Events ################################################################################################### - -- ################################################################################################################ - --[[ - Text changed - --]] - on_value_changed = function(self) - end, - }, - - label = {}, - - dropdown = { - -- ################################################################################################################ - -- ##### Methods ################################################################################################## - -- ################################################################################################################ - --[[ - Select index - --]] - select_index = function(self, index) - -- Check options ( options are dropdown_item widgets ) - if self.options and #self.options >= index then - -- Set index - self.index = index - -- Set text - self:update_text() - -- Trigger event - self:on_index_changed() - end - end, - update_text = function(self) - for _, option in pairs(self.options) do - if option.index == self.index then - self.text = option.text - return - end - end - end, - --[[ - Wrap function calls to options - --]] - wrap_options = function(self, function_name) - local results = {} - -- Go through options - for key, option in pairs(self.options) do - -- Check for function and execute it - if option[function_name] and type(option[function_name]) == "function" then - local result = option[function_name](option) - results[#results+1] = result - end - end - -- Return - return results - end, - -- ################################################################################################################ - -- ##### widget overrides ######################################################################################## - -- ################################################################################################################ - --[[ - Init override - --]] - init = function(self) - -- Original function - self:init_base() - -- Wrap init - self:wrap_options("init") - -- If options select first - if #self.options > 0 and not self.index then self.index = 1 end - self:select_index(self.index) - end, - --[[ - Click override - --]] - click = function(self) - -- Disabled - if self.disabled then return end - -- Original function - self:click_base() - -- Go through options - for key, option in pairs(self.options) do - -- If option hovered - if gui.point_in_bounds(gui.mouse.cursor(), option:extended_bounds()) then - -- Click option - option:click() - end - end - end, - --[[ - Release override - --]] - release = function(self) - -- Disabled - if self.disabled then return end - -- Drop - if self.clicked then self.dropped = not self.dropped end - -- Original function - self:release_base() - -- Go through options - for key, option in pairs(self.options) do - -- If option hovered - if gui.point_in_bounds(gui.mouse.cursor(), option:extended_bounds()) then - -- Release - option:release() - end - end - end, - --[[ - Unfocus override - --]] - unfocus = function(self) - -- Original function - self:unfocus_base() - -- Drop off - self.dropped = false - -- Wrap - self:wrap_options("unfocus") - end, - -- ################################################################################################################ - -- ##### Cycle overrides ########################################################################################## - -- ################################################################################################################ - --[[ - Update override - --]] - update = function(self) - -- Disabled - if self.disabled then return end - -- Original function - self:update_base() - -- If dropped - if self.dropped then - -- Get some shit - local scale = UIResolutionScale() - local border = 2*scale - local x = self.position[1] --+ border - local y = self.position[2] - border -- self.size[2] - border - -- Go through options - --table.sort(self.options) - --for i = 1, #self.options do - --for i = #self.options, 1, -1 do - -- gui:echo("--") - for _, option in pairs(self.options) do - --if option.param == i then - -- Update position - -- gui:echo(tostring(option.param)) - option:set("position", {x, y - (self.size[2] * option.index)}) - option:set("size", self.size) - option:set("visible", true) - --y = y - self.size[2] --- border - --break - --end - end - --end - -- Wrap - self:wrap_options("update") - end - -- Return - return self.clicked or self.dropped - end, - -- ################################################################################################################ - -- ##### Render overrides ######################################################################################### - -- ################################################################################################################ - --[[ - Render main override - --]] - render = function(self) - -- Visible - if not self.visible then return end - -- Original function - self:render_base() - -- If dropped - if self.dropped then - -- Wrap - self:wrap_options("render") - end - end, - -- ################################################################################################################ - -- ##### Attribute overrides ###################################################################################### - -- ################################################################################################################ - --[[ - Bounds override - --]] - extended_bounds = function(self) - local bounds = self:bounds() - -- If dropped - if self.dropped then - -- Change bounds to reflect dropped size - bounds[3] = bounds[3] - (self.size[2]*#self.options) - --bounds[4] = bounds[4] --+ self.size[2] + (self.size[2]*#self.options) - end - -- Return - return bounds - end, - -- ################################################################################################################ - -- ##### Events ################################################################################################### - -- ################################################################################################################ - --[[ - Index changed - --]] - on_index_changed = function(self) - end, - }, - - dropdown_item = { - -- ################################################################################################################ - -- ##### widget overrides ######################################################################################## - -- ################################################################################################################ - --[[ - Release override - --]] - release = function(self) - -- Disabled - if self.disabled then return end - -- Original function - self:release_base() - -- Change - if self.parent and self.index then - self.parent:select_index(self.index) - end - end, - -- ################################################################################################################ - -- ##### Attribute overrides ###################################################################################### - -- ################################################################################################################ - --[[ - Position Z override - --]] - position_z = function(self) - -- Return parent position z + 1 - return self.parent:position_z()+1 - end, - }, - -} - --- ################################################################################################################ --- ##### Themes ################################################################################################### --- ################################################################################################################ -gui.themes = { - -- Define a "default" theme element with common values for every widget - -- Define specific elements with a widget name to overwrite default settings - -- Default theme - default = { - -- default theme element - default = { - color = ColorHelper.box(200, 50, 50, 50), - color_hover = ColorHelper.box(200, 60, 60, 60), - color_clicked = ColorHelper.box(200, 90, 90, 90), - color_text = ColorHelper.box(100, 255, 255, 255), - color_text_hover = ColorHelper.box(200, 255, 168, 0), - color_text_clicked = ColorHelper.box(255, 255, 180, 0), - text_alignment = "middle_center", - shadow = { - layers = 5, - border = 0, - color = {20, 10, 10, 10}, - }, - font = "hell_shark", - }, - -- Overwrites and additions - window = { - color = ColorHelper.box(200, 30, 30, 30), -- - color_hover = ColorHelper.box(255, 35, 35, 35), -- - shadow = { - layers = 0, - border = 0, - color = {0, 255, 255, 255}, - }, - }, - title = { - height = 20, - color = ColorHelper.box(255, 40, 40, 40), - color_hover = ColorHelper.box(255, 50, 50, 50), - color_clicked = ColorHelper.box(255, 60, 60, 60), - color_text = ColorHelper.box(200, 255, 255, 255), - color_text_hover = ColorHelper.box(200, 255, 168, 0), - color_text_clicked = ColorHelper.box(255, 255, 180, 0), - shadow = { - layers = 5, - border = 0, - color = {20, 10, 10, 10}, - }, - }, - button = {}, - resizer = { - size = {20, 20}, - }, - close_button = { - size = {25, 25}, - }, - textbox = { - color_placeholder = ColorHelper.box(50, 255, 255, 255), - color_input_cursor = ColorHelper.box(100, 255, 255, 255), - text_alignment = "middle_left", - }, - checkbox = {}, - dropdown = { - draw_items_num = 5, - }, - dropdown_item = { - color_hover = ColorHelper.box(255, 60, 60, 60), - color_clicked = ColorHelper.box(255, 90, 90, 90), - }, - label = { - color = ColorHelper.box(0, 0, 0, 0), - color_hover = ColorHelper.box(0, 0, 0, 0), - color_clicked = ColorHelper.box(0, 0, 0, 0), - color_text = ColorHelper.box(200, 255, 255, 255), - color_text_hover = ColorHelper.box(200, 255, 255, 255), - color_text_clicked = ColorHelper.box(200, 255, 255, 255), - shadow = { - layers = 0, - border = 0, - color = {20, 10, 10, 10}, - }, - } - }, -} - --- ################################################################################################################ --- ##### Default stuff ############################################################################################ --- ################################################################################################################ -gui.fonts:create("default", "hell_shark", 22) -gui.fonts:create("hell_shark", "hell_shark", 22, nil, true) - From f79a9cf0e534e95c2afb20f9107ba23cb5da1ce2 Mon Sep 17 00:00:00 2001 From: UnShame Date: Thu, 22 Feb 2018 00:25:18 +0300 Subject: [PATCH 14/32] mutators: remove comments --- .../mods/vmf/modules/mutators/mutator_gui.lua | 8 ++++---- .../mods/vmf/modules/mutators/mutator_manager.lua | 14 ++++++++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua index dca3245..3484603 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua @@ -61,7 +61,7 @@ local mutators_view = { self:setup_hooks() self.initialized = true - print("INIT") + --print("INIT") end, deinitialize = function(self) @@ -90,7 +90,7 @@ local mutators_view = { self.map_view = nil self.initialized = false - print("DEINIT") + --print("DEINIT") end, -- Sorts mutators by title @@ -222,7 +222,7 @@ local mutators_view = { self.active = true - print("ACTIVE!") + --print("ACTIVE!") end, deactivate = function(self) @@ -255,7 +255,7 @@ local mutators_view = { self.active = false - print("DEACTIVE") + --print("DEACTIVE") end, on_mutators_page_change = function(self, index_change) diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua index ecdd0e5..b260293 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua @@ -34,6 +34,7 @@ manager.sort_mutators = function() if mutators_sorted then return end + --[[ -- LOG -- manager:dump(mutators_sequence, "seq", 5) for i, v in ipairs(mutators) do @@ -41,6 +42,7 @@ manager.sort_mutators = function() end print("-----------") -- /LOG -- + --]] -- Preventing endless loops (worst case is n*(n+1)/2 I believe) local maxIter = #mutators * (#mutators + 1)/2 @@ -80,12 +82,14 @@ manager.sort_mutators = function() 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 @@ -227,7 +231,7 @@ local function set_mutator_state(mutator, state) 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()) + --print("Disabled ", mutators[j]:get_name()) mutators[j]:disable() table.insert(disabled_mutators, 1, mutators[j]) end @@ -237,11 +241,11 @@ local function set_mutator_state(mutator, state) -- 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(), "!") + --print("Enabled ", mutator:get_name(), "!") VMFMod.enable(mutator) on_enabled(mutator) else - print("Disabled ", mutator:get_name(), "!") + --print("Disabled ", mutator:get_name(), "!") VMFMod.disable(mutator) on_disabled(mutator) end @@ -250,12 +254,10 @@ local function set_mutator_state(mutator, state) -- This will be recursive if #disabled_mutators > 0 then for j = #disabled_mutators, 1, -1 do - print("Enabled ", disabled_mutators[j]:get_name()) + --print("Enabled ", disabled_mutators[j]:get_name()) disabled_mutators[j]:enable() end end - - print("---------") end From a549af2847247933d0d5070429116285d0b89f0b Mon Sep 17 00:00:00 2001 From: UnShame Date: Thu, 22 Feb 2018 00:26:40 +0300 Subject: [PATCH 15/32] mutators: remove old gui script --- .../scripts/mods/vmf/modules/mutators/gui.lua | 219 ------------------ 1 file changed, 219 deletions(-) delete mode 100644 vmf_source/scripts/mods/vmf/modules/mutators/gui.lua diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/gui.lua b/vmf_source/scripts/mods/vmf/modules/mutators/gui.lua deleted file mode 100644 index ead3d1f..0000000 --- a/vmf_source/scripts/mods/vmf/modules/mutators/gui.lua +++ /dev/null @@ -1,219 +0,0 @@ -local manager = get_mod("vmf_mutator_manager") -local mutators = manager.mutators - - -local banner_level_widget = UIWidgets.create_texture_with_text_and_tooltip("title_bar", "Mutators", "map_level_setting_tooltip", "banner_level", "banner_level_text", { - vertical_alignment = "center", - scenegraph_id = "banner_level_text", - localize = false, - font_size = 28, - horizontal_alignment = "center", - font_type = "hell_shark", - text_color = Colors.get_color_table_with_alpha("cheeseburger", 255) -}) -local banner_level = UIWidget.init(banner_level_widget) - -local window = nil -local button = nil -local window_opened = false - -local function get_map_view() - local ingame_ui = Managers.matchmaking and Managers.matchmaking.ingame_ui - return ingame_ui and ingame_ui.views and ingame_ui.views.map_view -end - -local function update_window_visibility(map_view) - if not window then return end - button.visible = map_view and map_view.friends and not map_view.friends:is_active() - window.visible = window_opened and button.visible -end - -local function destroy_window(map_view) - if window then - window:destroy() - window = nil - button:destroy() - map_view.ui_scenegraph.settings_button.position[1] = map_view.ui_scenegraph.settings_button.position[1] - 50 - map_view.ui_scenegraph.friends_button.position[1] = map_view.ui_scenegraph.friends_button.position[1] - 50 - map_view.ui_scenegraph.lobby_button.position[1] = map_view.ui_scenegraph.lobby_button.position[1] - 50 - end -end - -local function create_window(map_view) - - destroy_window(map_view) - - local window_size = {0, 0} - local window_position = {50, 500} - - window = get_mod("gui").create_window("mutators_window", window_position, window_size) - - for i, mutator in ipairs(mutators) do - local title = mutator:get_config().title or mutator:get_name() - window:create_checkbox("checkbox_" .. mutator:get_name(), {65, 360 - 40 * (i - 1)}, {30, 30}, title, mutator:is_enabled(), function(self) - if self.value then - if not mutator:is_enabled() and mutator:can_be_enabled() then - mutator:enable() - elseif not mutator:is_enabled() then - create_window(map_view) - end - elseif mutator:is_enabled() then - mutator:disable() - end - end) - end - - window:init() - - button = get_mod("gui").create_window("mutators_button", window_position, window_size) - button:create_button("mutators", {55, -75}, {65, 65}, "Mut", function() - window_opened = not window_opened - end) - button:init() - - map_view.ui_scenegraph.settings_button.position[1] = map_view.ui_scenegraph.settings_button.position[1] + 50 - map_view.ui_scenegraph.friends_button.position[1] = map_view.ui_scenegraph.friends_button.position[1] + 50 - map_view.ui_scenegraph.lobby_button.position[1] = map_view.ui_scenegraph.lobby_button.position[1] + 50 - - update_window_visibility(map_view) -end - -local function reload_window() - local map_view = get_map_view() - if map_view and map_view.active then - create_window(map_view) - end -end - -manager:hook("MapView.on_enter", function(func, self, ...) - func(self, ...) - print("on_enter") - - manager.sort_mutators() - manager.disable_impossible_mutators() - manager:pcall(function() create_window(self) end) -end) - -manager:hook("MapView.on_level_index_changed", function(func, self, ...) - func(self, ...) - print("on_level_index_changed") - - manager.disable_impossible_mutators() - manager:pcall(function() create_window(self) end) -end) - -manager:hook("MapView.on_difficulty_index_changed", function(func, self, ...) - func(self, ...) - print("on_difficulty_index_changed") - - manager.disable_impossible_mutators() - manager:pcall(function() create_window(self) end) -end) - -manager:hook("MapView.set_difficulty_stepper_index", function(func, self, ...) - func(self, ...) - print("set_difficulty_stepper_index") - - manager.disable_impossible_mutators() - manager:pcall(function() create_window(self) end) -end) - -manager:hook("MapView.on_exit", function(func, self, ...) - func(self, ...) - print("on_exit") - manager:pcall(function() destroy_window(self) end) - window_opened = false -end) - -manager:hook("MapView.suspend", function(func, self, ...) - func(self, ...) - print("suspend") - manager:pcall(function() destroy_window(self) end) -end) - -manager:hook("MapView.update", function(func, self, dt, t) - func(self, dt, t) - manager:pcall(function() update_window_visibility(self) end) -end) - -manager:hook("MapView.draw", function(func, self, input_service, gamepad_active, dt) - local ui_renderer = self.ui_renderer - local ui_scenegraph = self.ui_scenegraph - - UIRenderer.begin_pass(ui_renderer, ui_scenegraph, input_service, dt, nil, self.render_settings) - - for _, widget in ipairs(self.background_widgets) do - UIRenderer.draw_widget(ui_renderer, widget) - end - - local number_of_player = self.number_of_player or 0 - - for i = 1, number_of_player, 1 do - local widget = self.player_list_widgets[i] - - UIRenderer.draw_widget(ui_renderer, widget) - end - - if window_opened or not self.settings_button_widget.content.toggled then - for widget_name, widget in pairs(self.normal_settings_widgets) do - local skipped_widgets_keys = { - "level_preview", - "level_preview_text", - "banner_level" - } - if not window_opened or not table.has_item(skipped_widgets_keys, widget_name) then - UIRenderer.draw_widget(ui_renderer, widget) - end - end - if window_opened then - manager:pcall(function() UIRenderer.draw_widget(ui_renderer, banner_level) end) - end - else - for widget_name, widget in pairs(self.advanced_settings_widgets) do - UIRenderer.draw_widget(ui_renderer, widget) - end - end - - UIRenderer.draw_widget(ui_renderer, self.player_list_conuter_text_widget) - UIRenderer.draw_widget(ui_renderer, self.description_field_widget) - UIRenderer.draw_widget(ui_renderer, self.title_text_widget) - UIRenderer.draw_widget(ui_renderer, self.game_mode_selection_bar_widget) - UIRenderer.draw_widget(ui_renderer, self.game_mode_selection_bar_bg_widget) - UIRenderer.draw_widget(ui_renderer, self.private_checkbox_widget) - - if not gamepad_active then - UIRenderer.draw_widget(ui_renderer, self.friends_button_widget) - UIRenderer.draw_widget(ui_renderer, self.settings_button_widget) - UIRenderer.draw_widget(ui_renderer, self.confirm_button_widget) - UIRenderer.draw_widget(ui_renderer, self.cancel_button_widget) - UIRenderer.draw_widget(ui_renderer, self.lobby_button_widget) - - if not self.confirm_button_widget.content.button_hotspot.disabled then - UIRenderer.draw_widget(ui_renderer, self.button_eye_glow_widget) - else - UIRenderer.draw_widget(ui_renderer, self.confirm_button_disabled_tooltip_widget) - end - else - UIRenderer.draw_widget(ui_renderer, self.background_overlay_console_widget) - UIRenderer.draw_widget(ui_renderer, self.gamepad_button_selection_widget) - end - - local draw_intro_description = self.draw_intro_description - - if draw_intro_description then - for key, text_widget in pairs(self.description_text_widgets) do - UIRenderer.draw_widget(ui_renderer, text_widget) - end - end - - UIRenderer.end_pass(ui_renderer) - - local friends_menu_active = self.friends:is_active() - - if gamepad_active and not friends_menu_active and not self.popup_id and not draw_intro_description then - self.menu_input_description:draw(ui_renderer, dt) - end - -end) - -return reload_window, get_map_view From 4a3d7f3fb23ee6ab588e9eba5a5c3e7c6a714189 Mon Sep 17 00:00:00 2001 From: UnShame Date: Thu, 22 Feb 2018 01:12:39 +0300 Subject: [PATCH 16/32] mutators: add README.md --- .../mods/vmf/modules/mutators/README.md | 72 +++++++++++++++++++ .../vmf/modules/mutators/mutator_info.lua | 2 +- 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 vmf_source/scripts/mods/vmf/modules/mutators/README.md diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/README.md b/vmf_source/scripts/mods/vmf/modules/mutators/README.md new file mode 100644 index 0000000..cd68b60 --- /dev/null +++ b/vmf_source/scripts/mods/vmf/modules/mutators/README.md @@ -0,0 +1,72 @@ +# Mutators +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. + +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", + dice = { + grims = 0, + tomes = 0, + bonus = 0 + }, + difficulties = { + "easy", + "normal", + "hard", + "harder", + "hardest", + + "survival_hard", + "survival_harder", + "survival_hardest" + }, + 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. + +``dice = { grims = 0, tomes = 0, bonus = 0 }`` +This determines how many additional dice the players will get for completing maps with your mutator enabled. + +``` +difficulties = { + "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. diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_info.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_info.lua index b100d15..477a606 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_info.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_info.lua @@ -81,7 +81,7 @@ manager:hook("MatchmakingStateHostGame.host_game", function(func, self, ...) end) manager:hook("MatchmakingManager.rpc_matchmaking_request_join_lobby", function(func, self, sender, client_cookie, host_cookie, lobby_id, friend_join) - local name = get_enabled_mutators_names(false) + local name = get_enabled_mutators_names() if name then local message = "[Automated message] This lobby has the following difficulty mod active : " .. name manager:hook("Managers.chat.channels[1].members_func", get_member_func(client_cookie)) From 69d2e49b4e9423adda05c35076640647913164b0 Mon Sep 17 00:00:00 2001 From: UnShame Date: Thu, 22 Feb 2018 01:35:52 +0300 Subject: [PATCH 17/32] mutators: expanded readme; add ignore_map to difficulty check --- .../mods/vmf/modules/mutators/README.md | 25 +++++++++++++++++++ .../vmf/modules/mutators/mutator_manager.lua | 8 +++--- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/README.md b/vmf_source/scripts/mods/vmf/modules/mutators/README.md index cd68b60..c86cb11 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/README.md +++ b/vmf_source/scripts/mods/vmf/modules/mutators/README.md @@ -1,6 +1,11 @@ # Mutators 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")`` + +## 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 @@ -24,6 +29,8 @@ The config object is optional but obviously you'd want to provide at least a rea "survival_harder", "survival_hardest" }, + load_before_these = {}, + load_after_these = {}, incompatible_with_all = false, compatible_with_all = false, incompatible_with = {}, @@ -70,3 +77,21 @@ Set this to true if you are sure this mutator won't cause any problems. This ove ``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 `load_before_these/load_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. + diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua index b260293..db821f5 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua @@ -276,19 +276,19 @@ local function disable_mutator(self) end -- Checks current difficulty and map selection screen settings to determine if a mutator can be enabled -local function can_be_enabled(self) +local function can_be_enabled(self, ignore_map) if #self:get_incompatible_mutators(true) > 0 then return false end - return self:supports_current_difficulty() + return self:supports_current_difficulty(ignore_map) end -local function supports_current_difficulty(self) +local function supports_current_difficulty(self, ignore_map) local mutator_difficulties = self:get_config().difficulties local actual_difficulty = Managers.state and Managers.state.difficulty:get_difficulty() local right_difficulty = not actual_difficulty or table.has_item(mutator_difficulties, actual_difficulty) - local map_view = mutators_view.map_view + 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 From 1ee688402d7af266a2aed721eaae577701a3e897 Mon Sep 17 00:00:00 2001 From: UnShame Date: Thu, 22 Feb 2018 01:49:41 +0300 Subject: [PATCH 18/32] mutators readme: features --- .../scripts/mods/vmf/modules/mutators/README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/README.md b/vmf_source/scripts/mods/vmf/modules/mutators/README.md index c86cb11..0ad3571 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/README.md +++ b/vmf_source/scripts/mods/vmf/modules/mutators/README.md @@ -1,9 +1,20 @@ # 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: From 271ddc1660000a02cf358d092e4a8f1a8654ed15 Mon Sep 17 00:00:00 2001 From: UnShame Date: Thu, 22 Feb 2018 01:53:33 +0300 Subject: [PATCH 19/32] mutators readme fix --- vmf_source/scripts/mods/vmf/modules/mutators/README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/README.md b/vmf_source/scripts/mods/vmf/modules/mutators/README.md index 0ad3571..7787745 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/README.md +++ b/vmf_source/scripts/mods/vmf/modules/mutators/README.md @@ -40,8 +40,8 @@ The config object is optional but obviously you'd want to provide at least a rea "survival_harder", "survival_hardest" }, - load_before_these = {}, - load_after_these = {}, + enable_before_these = {}, + enable_after_these = {}, incompatible_with_all = false, compatible_with_all = false, incompatible_with = {}, @@ -95,7 +95,6 @@ You can improve the compatibility of your mutator with other ones by specifiying # 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 `load_before_these/load_after_these` fields. This shouldn't be modified. From bc4377a4c3d1cb9c3f9e2b882435484c45b420e7 Mon Sep 17 00:00:00 2001 From: UnShame Date: Thu, 22 Feb 2018 01:56:20 +0300 Subject: [PATCH 20/32] mutators: difficulties -> difficulty_levels --- vmf_source/scripts/mods/vmf/modules/mutators/README.md | 4 ++-- .../mods/vmf/modules/mutators/mutator_default_config.lua | 2 +- .../scripts/mods/vmf/modules/mutators/mutator_gui.lua | 2 +- .../scripts/mods/vmf/modules/mutators/mutator_manager.lua | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/README.md b/vmf_source/scripts/mods/vmf/modules/mutators/README.md index 7787745..45d7b3f 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/README.md +++ b/vmf_source/scripts/mods/vmf/modules/mutators/README.md @@ -29,7 +29,7 @@ The config object is optional but obviously you'd want to provide at least a rea tomes = 0, bonus = 0 }, - difficulties = { + difficulty_levels = { "easy", "normal", "hard", @@ -62,7 +62,7 @@ The description will show up in the tooltip of your mutator on the map screen. This determines how many additional dice the players will get for completing maps with your mutator enabled. ``` -difficulties = { +difficulty_levels = { "easy", "normal", "hard", diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_default_config.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_default_config.lua index 3a0c4f9..bfa828d 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_default_config.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_default_config.lua @@ -7,7 +7,7 @@ return { title = "", short_title = "", description = "No description provided", - difficulties = { + difficulty_levels = { "easy", "normal", "hard", diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua index 3484603..5b2a8da 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua @@ -285,7 +285,7 @@ local mutators_view = { if not supports_difficulty then text = text .. "\nSupported difficulty levels:" - for i, difficulty in ipairs(config.difficulties) do + for i, difficulty in ipairs(config.difficulty_levels) do text = text .. (i == 1 and " " or ", ") .. manager:localize(difficulty) end end diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua index db821f5..2519fa3 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua @@ -284,9 +284,9 @@ local function can_be_enabled(self, ignore_map) end local function supports_current_difficulty(self, ignore_map) - local mutator_difficulties = self:get_config().difficulties + 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_difficulties, actual_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 @@ -297,7 +297,7 @@ local function supports_current_difficulty(self, ignore_map) 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_difficulties, difficulty_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) @@ -437,7 +437,7 @@ mutator3.on_disabled = function() end mutator2:register_as_mutator({ compatible_with_all = true, - difficulties = { + difficulty_levels = { "hardest" } }) From d581e0a9ced37bc876a17b3899269db74c6621c8 Mon Sep 17 00:00:00 2001 From: UnShame Date: Thu, 22 Feb 2018 02:25:31 +0300 Subject: [PATCH 21/32] mutators: comments; split tests --- .../vmf/modules/mutators/mutator_dice.lua | 8 ++ .../mods/vmf/modules/mutators/mutator_gui.lua | 46 ++++++++++- .../mutators/mutator_gui_definitions.lua | 54 +++++++------ .../vmf/modules/mutators/mutator_info.lua | 26 +++++-- .../vmf/modules/mutators/mutator_manager.lua | 76 +++---------------- .../vmf/modules/mutators/mutator_test.lua | 35 +++++++++ 6 files changed, 149 insertions(+), 96 deletions(-) create mode 100644 vmf_source/scripts/mods/vmf/modules/mutators/mutator_test.lua diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_dice.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_dice.lua index 4f258f0..4c912ce 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_dice.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_dice.lua @@ -1,11 +1,15 @@ +--[[ 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, @@ -18,6 +22,7 @@ manager:hook("GameModeManager.complete_level", function(func, self) 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()) @@ -25,12 +30,14 @@ manager:hook("GameModeManager.complete_level", function(func, self) 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 @@ -47,6 +54,7 @@ manager:hook("GameModeManager.complete_level", function(func, self) 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 diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua index 5b2a8da..6703c31 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua @@ -1,3 +1,5 @@ +--[[ Add mutators panel to the map view --]] + local manager = get_mod("vmf_mutator_manager") local mutators = manager.mutators @@ -149,28 +151,40 @@ local mutators_view = { 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" @@ -178,10 +192,12 @@ local mutators_view = { 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 @@ -190,11 +206,13 @@ local mutators_view = { mutator:enable() end end + checkbox.content.selected = mutator:is_enabled() end 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 @@ -225,6 +243,7 @@ local mutators_view = { --print("ACTIVE!") end, + -- Deactivate on button click or map close deactivate = function(self) if not self.initialized or not self.active then return end @@ -258,6 +277,7 @@ local mutators_view = { --print("DEACTIVE") end, + -- Changes which muttators are displayed on_mutators_page_change = function(self, index_change) if not self.initialized then return end @@ -278,11 +298,14 @@ local mutators_view = { end end, + -- Creates and return text for checkbox tooltip generate_tooltip_for = function(self, mutator) + -- Description local config = mutator:get_config() local text = config.description - local supports_difficulty = mutator:supports_current_difficulty() + -- 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 @@ -290,13 +313,19 @@ local mutators_view = { 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:" @@ -305,10 +334,13 @@ local mutators_view = { 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 @@ -383,4 +415,16 @@ local mutators_view = { end } +-- Initialize mutators view after map view +manager:hook("MapView.init", function(func, self, ...) + func(self, ...) + manager:pcall(function() mutators_view:init(self) end) +end) + +-- Destroy mutators view after map view +manager:hook("MapView.destroy", function(func, ...) + mutators_view:deinitialize() + func(...) +end) + return mutators_view \ No newline at end of file diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui_definitions.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui_definitions.lua index 7d620f0..6e98cc5 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui_definitions.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui_definitions.lua @@ -1,8 +1,10 @@ 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", @@ -17,6 +19,8 @@ scenegraph_definition.mutators_button = { 1 } } + +-- This will replace the Mission text scenegraph_definition.banner_mutators_text = { vertical_alignment = "center", parent = "banner_level", @@ -34,30 +38,35 @@ scenegraph_definition.banner_mutators_text = { 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 + 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 = { @@ -172,6 +181,7 @@ local new_widgets = { } } +-- Checkboxes for i = 1, definitions.PER_PAGE do new_widgets["mutator_checkbox_" .. i] = { scenegraph_id = "mutator_checkbox_" .. i, diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_info.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_info.lua index 477a606..b54de4e 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_info.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_info.lua @@ -1,16 +1,19 @@ +--[[ 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 -local function get_enabled_mutators_names(short) +-- Assembles a list of enabled mutators +local function get_enabled_mutators_names(separator, short) local name = nil for _, mutator in ipairs(mutators) do local config = mutator:get_config() if mutator:is_enabled() then local added_name = (short and config.short_title or config.title or mutator:get_name()) if name then - name = name .. " " .. added_name + name = name .. separator .. added_name else name = added_name end @@ -19,11 +22,12 @@ local function get_enabled_mutators_names(short) return name end +-- Sets the lobby name local function set_lobby_data() if not Managers.matchmaking then return end - local name = get_enabled_mutators_names(true) + local name = get_enabled_mutators_names(" ", true) local default_name = LobbyAux.get_unique_server_name() if name then @@ -38,6 +42,8 @@ local function set_lobby_data() Managers.matchmaking.lobby:set_lobby_data(lobby_data) end +-- Return a function for chat system to only send messages to specific client +-- TODO: test if this works local function get_member_func(client_cookie) local peer_id = tostring(client_cookie) for _ = 1, 3 do @@ -58,19 +64,21 @@ local function get_member_func(client_cookie) end end - +-- Set difficulty in the tab menu +-- TODO: see if this can be set every time a mutator is enabled/disable manager:hook("IngamePlayerListUI.set_difficulty_name", function(func, self, name) - local mutators_name = get_enabled_mutators_names(true) + local mutators_name = get_enabled_mutators_names(" ", true) if mutators_name then name = name .. " " .. mutators_name end self.headers.content.game_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 = get_enabled_mutators_names() + local names = get_enabled_mutators_names(", ") if names then Managers.chat:send_system_chat_message(1, "ENABLED MUTATORS: " .. names, 0, true) were_enabled_before = true @@ -80,8 +88,10 @@ manager:hook("MatchmakingStateHostGame.host_game", function(func, self, ...) end end) +-- Send special messages with enabled mutators list to players just joining the lobby +-- TODO: test if this works manager:hook("MatchmakingManager.rpc_matchmaking_request_join_lobby", function(func, self, sender, client_cookie, host_cookie, lobby_id, friend_join) - local name = get_enabled_mutators_names() + local name = get_enabled_mutators_names(", ") if name then local message = "[Automated message] This lobby has the following difficulty mod active : " .. name manager:hook("Managers.chat.channels[1].members_func", get_member_func(client_cookie)) @@ -91,4 +101,4 @@ manager:hook("MatchmakingManager.rpc_matchmaking_request_join_lobby", function(f func(self, sender, client_cookie, host_cookie, lobby_id, friend_join) end) -return set_lobby_data \ No newline at end of file +return set_lobby_data diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua index 2519fa3..2e1543f 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua @@ -1,3 +1,5 @@ +--[[ Add ability to turn mods into mutators --]] + local manager = new_mod("vmf_mutator_manager") manager:localization("localization/mutator_manager") @@ -103,8 +105,8 @@ manager.disable_impossible_mutators = function(notify, everybody) end if #disabled_mutators > 0 and notify then local message = everybody and "MUTATORS DISABLED DUE TO DIFFICULTY CHANGE:" or "Mutators disabled due to difficulty change:" - for _, mutator in ipairs(disabled_mutators) do - message = message .. " " .. (mutator:get_config().title or mutator:get_name()) + for i, mutator in ipairs(disabled_mutators) do + message = message .. (i == 1 and " " or ", ") .. (mutator:get_config().title or mutator:get_name()) end if everybody then Managers.chat:send_system_chat_message(1, message, 0, true) @@ -275,14 +277,14 @@ local function disable_mutator(self) manager:pcall(function() set_mutator_state(self, false) end) end --- Checks current difficulty and map selection screen settings to determine if a mutator can be enabled +-- Checks current difficulty, map selection screen settings (optionally) and incompatible mutators 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 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() @@ -308,6 +310,7 @@ 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 @@ -382,71 +385,14 @@ manager:hook("DifficultyManager.set_difficulty", function(func, self, difficulty end) --- Initialize mutators view after map view -manager:hook("MapView.init", function(func, self, ...) - func(self, ...) - manager:pcall(function() mutators_view:init(self) end) -end) - --- Destroy mutators view after map view -manager:hook("MapView.destroy", function(func, ...) - mutators_view:deinitialize() - func(...) -end) - +--[[ + INITIALIZE +--]] -- Initialize mutators view when map_view has been initialized already manager:pcall(function() mutators_view:init(mutators_view:get_map_view()) end) - - - - - - - - - - - - - - --[[ Testing --]] -local mutator2 = new_mod("mutator2") -local mutator3 = new_mod("mutator3") -local mutator555 = new_mod("mutator555") - -mutator555:register_as_mutator({ - incompatible_with_all = true -}) -mutator555:create_options({}, true, "mutator555", "mutator555 description") -mutator555.on_enabled = function() end -mutator555.on_disabled = function() end - - -mutator3:register_as_mutator({ - incompatible_with = { - "mutator4" - } -}) -mutator3.on_enabled = function() end -mutator3.on_disabled = function() end - -mutator2:register_as_mutator({ - compatible_with_all = true, - difficulty_levels = { - "hardest" - } -}) -mutator2.on_enabled = function() end -mutator2.on_disabled = function() end - ---[[for i=4,17 do - local mutator = new_mod("mutator" .. i) - mutator:register_as_mutator({}) - mutator.on_enabled = function() end - mutator.on_disabled = function() end -end--]] \ No newline at end of file +manager:dofile("scripts/mods/vmf/modules/mutators/mutator_test") diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_test.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_test.lua new file mode 100644 index 0000000..fe8f03b --- /dev/null +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_test.lua @@ -0,0 +1,35 @@ +local mutator2 = new_mod("mutator2") +local mutator3 = new_mod("mutator3") +local mutator555 = new_mod("mutator555") + +mutator555:register_as_mutator({ + incompatible_with_all = true +}) +mutator555:create_options({}, true, "mutator555", "mutator555 description") +mutator555.on_enabled = function() end +mutator555.on_disabled = function() end + + +mutator3:register_as_mutator({ + incompatible_with = { + "mutator4" + } +}) +mutator3.on_enabled = function() end +mutator3.on_disabled = function() end + +mutator2:register_as_mutator({ + compatible_with_all = true, + difficulty_levels = { + "hardest" + } +}) +mutator2.on_enabled = function() end +mutator2.on_disabled = function() end + +--[[for i=4,17 do + local mutator = new_mod("mutator" .. i) + mutator:register_as_mutator({}) + mutator.on_enabled = function() end + mutator.on_disabled = function() end +end--]] \ No newline at end of file From 162926abc79354e32e7cfa068d08851325286bb8 Mon Sep 17 00:00:00 2001 From: UnShame Date: Thu, 22 Feb 2018 13:36:07 +0300 Subject: [PATCH 22/32] mutators: button icon; fix notifications; disable tests --- vmf_source/gui/vmf/mutator_button.dds | Bin 0 -> 65664 bytes vmf_source/gui/vmf/mutator_button.texture | 17 +++++ vmf_source/gui/vmf/mutator_button_hover.dds | Bin 0 -> 65664 bytes .../gui/vmf/mutator_button_hover.texture | 17 +++++ .../materials/vmf/mutator_button.material | 14 ++++ .../vmf/mutator_button_hover.material | 14 ++++ .../mutators/mutator_gui_definitions.lua | 5 +- .../vmf/modules/mutators/mutator_info.lua | 42 ++++++------ .../vmf/modules/mutators/mutator_manager.lua | 4 +- .../vmf/modules/mutators/mutator_test.lua | 60 +++++++++++++++--- .../modules/options_menu/vmf_options_view.lua | 28 ++++++-- 11 files changed, 163 insertions(+), 38 deletions(-) create mode 100644 vmf_source/gui/vmf/mutator_button.dds create mode 100644 vmf_source/gui/vmf/mutator_button.texture create mode 100644 vmf_source/gui/vmf/mutator_button_hover.dds create mode 100644 vmf_source/gui/vmf/mutator_button_hover.texture create mode 100644 vmf_source/materials/vmf/mutator_button.material create mode 100644 vmf_source/materials/vmf/mutator_button_hover.material diff --git a/vmf_source/gui/vmf/mutator_button.dds b/vmf_source/gui/vmf/mutator_button.dds new file mode 100644 index 0000000000000000000000000000000000000000..311c80ec7c10f332a5da83abca178d3e26e27152 GIT binary patch literal 65664 zcmeI5Nsrt_6vs_K0VEJhkT`%KY+?Th91uBh;(~+{BvzpSLL_VfAw`fQhrm}r5t+8# z2Sf;p-~{4>0Ad@MBbz{($>Y85skXbOTrQW(ZjbA4rQ@y3u6n;;Jy*M(&cTC^?>~_w z$u-v|$w{8eE7>D|HJ|o#k%za7$#RmA`ZHhQbLbNhC3qnQZ~zX#0XP5$-~b$e18@Ki zzyUY_2jBo4fCF#<4!{9800-az9DoCG01m(bH~);<3Z@snP zuSX=V_2~E$U6QQPb+xZ4T{<`uq)sk%mo_?1(ea-?ERy(y74I!@qkbzv9|G+{4ZgMYRC?)_ioh}AmKls))YvzI;1`HKgualnT?_;bjWmhD{b`%d$Zt>XTlE$nh1HKQ|u;@$4EVaR@;QOZ6xx^Erb z{XgZ!5FA)BdkICf{H5`$bYKANqvyXW2L{3({3QZB^_d3-#9o>nb^ezCsOf9EjK_mn zkTId{bHRij<#TKWIM3-sKFhS6z`qp0zVbj$&$o1-@i@QK%B4l{UkXQm*8+bBg0YvD zgTKaKR)?Dhg0h#Ug1mE@`4~4k5qgUUw zj`5!e%|_b zzHUEY3+K|l;<=rRT<+DSeQITud0a1Ke z%{LNhyJd=kzqEfs_i(J%t?^Rhf%<;0$9jv$15X*l+{JjHuHVm{*x-AV4;t%K+87Og zX|u$`Ef0KU^~BugHaQO*E`_#O44DTqlDgIRdk6MX!KnC4gI)4~nXlgGP3xPS2cDGE z?3lvl0n@qFHm82C>K7eJ}w`X?S;e;M?RZ zT`i|DPTl2!#_Xkj(ejtZhjt*PZ#cKq|Mq5h2fWxv&p)gKUo!hE<&aKOI0wAhOFg6R z{|Zz_r#!~{#q~F*FT8#$ILvt9lxl|0JbL~az1Euj$8?2H>}O>{9*~@piMILXqd@hmv9cr3i3qEgAA8E%MO{)_n|BQ}ttlF?AzSn2| z&ZsE>^?tW|y7jI$ycR^qKcg30vsZZ{lmnsI7xi);4@AX3B>N)P?eaiq_C>v%9EgU$ zJhSDK^K<%$%l*JFTkc){?pWpYy1CnTxN`0fYtCO)x7zVQ6#P>U zpX-jLI^^`G_52wF*hdR1h{T=vyHha%^YbD1Xj zj910kzTNoD@@V-sgkW}Ia_mgE|h2W0Fpq5F;RtJ;{+3tZm)w?qxO zZM6K?=q`@UKa2RPIPaWGzc)X}Uln(?U3dw%CjDNORdXrp!(Nqlci=D1|F@}PYCc;2 z(m;7G+ulFqx!ofw7GCpDX`ffQa-p`zNa@*{c2+){I%@tB zX#*y|2!Ov|+{RK0{KrDluN`CIk9N>#?&X0cSi%7~00-az9DoCG01m(bH~^NBJG{k3~_#s&MhgvfF;wmVc|(us-?G5E?;a zXj+6OT>A;E1K#7k#q+DCIf0Lc6)gs@hxX^(yzz{8vA;${OuFwT`h8;>AWt-)@6&=-BuH+|Gseb#q1P>YH=P{_`@c0O0nprL*{ z4jdDX6XSXy^n~BQac}~h3@5?ya10y?hk=@Q2k&*f7+N#akt`GX6Px_{h`l{Xft_HSg+qrh<+gMjWmuu^~>6+)fXYT92nC7rHUsBB$G|@D zYf!(l;3BvRu7{gp0Q84`a2;F$YI-)Pr{nD~=mD;^9iZwt5VS6Q%#OWn+lPG}3ihoH z+O196rj6RF&DyRH`l3(zW?g;NXMIY4v2+oI&i7V?fW;Poi z-T(9P(PNv8izdLNpN)&A!W7CAHysy^+jM+Xw$5W|ebw_OaLlRaDR3mzRt{7ad)u}T z*B|?|Z*3T)P1>f7+N#akt`9IlpY%;1sc#PGv%afA$k|OOtC3peYFL*)W#Zo$pEiKj zupM*-^WXW?G1RZ+*y!G<3jbz*7`?aNsOVwtajdE5Rd62ERt{7p_V#JtLz<3?9(r+k zw5$rINBYP;&I9_a?`ohHYN9r3q*iq~;<>hG;@=ucJA z&^ohLMDMRVD)kum-2<+(wUq-^jlF#iCH9X#|3T`#s@koO+&3t;SKrk@E!0G9)JUyz zHLS~@GVyO3`{p{gBkTvxf6k-skA}7wlNw)D9pfDMfE*~}-rCB6>c;-D=Rb(59D8ln zM}3Wb)^{~f3pG(2S@Z!so%sygwnf0)Iv?vMvd0y__xPD_t6J|d*F+~xpepjk4KZM8~<9$fgdFH+NzKG8vCs8 zYM>TsqBd)L{LS&~_`4744>gYeYUF^=+CNzA^Wv}1`ko#CwSD}z#NROgA3*tca6a4w z_cH%aP@|guV^3J!{lG@_E50x40On=>GoJfO=O_1i)n4!OebGnS@)bv_2phHGI6+KkE9tGb`7kpms+bDJ2;YdrS)qL2Ct`mFD2pcZPP zHfp3+xqGO({3)~l)29t#3-J4zz2HbV11^W#tMmLem{CUXtg@W z7kDmXzAmHP{Aa^noO93ZxchC4WzTudXR@5w+t%vX67HS4EcGwm|7nkNzdq}`8mNVu zYzE;wfVw>5b8XLz|8?-K8T<@>0lUEdp!R2hnh)eX>WFFY)=myoG7nrsAD4jZuHVzy zuUPi|q1UW40BU8u53zsxz33f%quqg^ulB3&YM>TsqBd%zR=FD1V6pWzTud_zbI!yWML+x$Nodc);#3k*fCM{73+ zqS3TF9PWm}U@o{0-v-9qb6y)x>_=DkyZ%_w(aelh1jv*C&0`M}0j6^j!^FgPN#KX6&z>@i(ts2mFqEV`v9ELJ#ow z5GTR8utsv=rRC8I#`YxI9|w=as2I!Ri2bCQ??)fhB-Z)&6o|dP>7%~tv%af=TBwQI zs8NQxJm#})5B`)N-QTPa&UstG&ae+00TpuKCuPy2?4OqAYh07h&%Gb5Xi^qELqAhs za*XBa$GsTM|1MRNR&(!}yexW~XBp?2p%BJ>h`m1Qt3K;@O|ya%mdHU-*YiDIlc(9E_f~a_jjq9C&4PleTcoj>a)HZ zd$lMZ_n}<3qh4-(@TcPON6P`$_)nSdXQTTp@HhwN!NT+AM9XS257cVx)oN||S26C{ zx&EW&0BikcwP(YE zInZlXZVnLpW%iA2_dua%JZ;|w^hKY1#@9!EEj}mKnZQ=T(Bi)Ow=QbpyDww!JJ6P} z8MKG(;a9LP`1};!5BSdhc6fj|jGeM9dN=neyk?(@%mWM0d?otlH{V4mzMp#6KJ6O@ zg0^Ho-)pNjYr8(^i$3X_KI*GJ>wDomsDiX5U!|CTnQ_n<4Vr=PKOG}}2PT_CJJ=e0 zPv&=|AqP&Qd@=NcyWmfa%A$#UUr<}`?mlmvyB~0GI2UFe`dl=(!DG=x`-Hne8?>bt zn4j9Ht=g>Z`e6KnU;3!8`mFE8=YoM>$@7L#o9Fw6!RxD_nH5Wh1Q@(u9F?1b4(|W+hv{GnQ}Kc1dfHX z;VKvi&clzx6yCf3<;i!V4|AhgvrlmjET&!G3(ba^_5t=~pZ0wK2ze&~-r z=u7a+bNZ^!`mP3Q;d-$4+3enq}K@ANFOR_H7Ote{Iq>ZPeCaw?62L zKIxm+^mQjNH`G8a)TD3@6q#mA@>1pT53%<<5A)w~u{-Py2g0FnIP{E<2Xj=@&h4^!B7CA6~+}qJ7xzViIr}#bLY}zdpe{InwZPP|=)n;wihr=NFq>o zWpF(Vf(IZy|8sPXHdgbebs04Lk-lzc|dK{NUhYYJO@fL$VH4Qx4k}oG=>eJHMrjI4(7hG?+us2wQv*M z(*DWlPL4N2zxdn@9Q%TG#Lwz(z5C%Yf4gz=%h5v0vtS-9g2lpf!IFvZM4#4d_RqbG z{lE(QqTMpEZ*9;PZ3^~jtMh{X=)=viPq)P9tf$ZJVax%waG$UpsF7MV2DQuNKoO}+ z@=~z7e6Jto{hjb>Z#W7Z1L~kogJIZ%|A^*(`gQbWkEc_PG4&k=jx(=2-^v|u7dY=f zLOh=EcRPMDY&3ZO#M!j%Z!_v~SRcyFr_RecGzc+J4_2)1xt;tc<>R z^k30}_?$VT&-$(gYOxQfO?wFIz&e!ca{L=$e-mf{o5MD+8ypCZedj^Pf_jY@_P6Mb zFxt@TtFAL5=UVsL>O2C5$2`dKVJIW^6S9A=XAWF8H}x*pa^mmj+RTBMm-~Cd^xtwh z4`_=v1^cvBo6EXPjb?oGRrF=>Yvezow>Uo%%mIB@1LuH)KyB1Wt<+5I)UYIncu}J= zd29Zz2d$tDbcC*OD4YZrg5zLVcI<;zAFqs7?mj*B0@t5}u`nja@)LY#H<@u?;eC%e zaLL?gjpsn-_k=0;gU$u{_D$G%YV?;6zly#J{)A(k1I`Wlt_EtMCTgQbYNcjsmze{K zid}*iGx2YP{msGotQ~ZQZg4o90vEw85B_&FH)xZ8WbAzXt7*|I)SCusEmMj8vwwa+ zwIaWM_-qbb{Q8=o2VPj7`iSR(XSFHIzG*v6iC$juRkSkLlYflY0`~(KgIe?iwNWFr zQZuzv!x9YeDoiSm|N5~nA^zR@tgrffafhd(A)kGn`hI@Q*U8TxewF%~vGf}CXA1A; zpXcwyKB}y4_BnsMan7sJQtruL3l^~_Ts(1U?d%D)MVo?s+N#ajw&a)fS>L(G_@4@D zqBhy_SG&3#f9I}duraiSPOv99{!fNJ(2sR~4CB1QJejT8N9f5~x)7aTedfKYzt=Gb z&YGQ*1B>W$KKOoM{`j|R=RINkd%{;cPmUHW|1v5X`%hWd_IW1#4CynDbD&zNiQ1@< zTB(`Z)#drWA@(F-KiZ~^#p>!~_ZjK6Y$*NT0BWEXY7(9S)k@9O zt}e&FKKA<@`&0Nibbv0fKO6&R!BubvjM#Nr^fdYv&4FUt7Q1E+^qw94i+tVb3yDqq&bzbWX-JvI(2A4s9a6equb$aS~_WecY&zflOJ^ps%sOQsj{|x$?0+VB= zFb~YAt=|t+W}iOkn?8cR>a)K00=3u+)JBceO3gB3zi!9h9B>b_K5PnM4mgmq^PbN? z&VxhXVR(Z5)62{QwVng>K8`--zrWx*+I4`P=8mU!g-xtPF2@dh1CI!38kB#xkcN(o>d)OC_gLC0p7zD#% zEKKG3X%5d%wUz^OKT3VXe|u#jZI{7AU|;vgSawe4zb%^@&);uV>A&Tf9pge}V*d@# zudma#d8cpobqDCPzN>*+s7Y&3Beinelv7mf61$jfcX_!!{oI z9)Qu{y!_k_Q=$bcRI; znuWAE6I^ff&He|%)u8WcpcZQ4Gk;T1tMb@~a*2hxQgec>l^cPD5=8T)}r{H}Pl`qp;Z zng#k|PLBrLH|Om4YzXmJ6URpRnqQfQm5N>>Co8kJ{G88%&0rJg2z$dZpazZw$AojE z8aY;GUbi6npX_m5-SW&2qK|n;|151y0N35S;a2Dimx1pe#PW480GtP0AI7sEczSi8 z3;HifKX-=l?><8x_4OJ!2h?C6P!qLj4s|mJ%43~BcZJwHzqNxM;Q;V^k8?ro2g8Ff z7N%XlF!hh>#yh({@8mw=z3hv&6}LHpqkY^6m&5sRUW{dPz~!4%+|mpcXqpdr+g|IZz_Qy@DB)$3Mh=WBdxS--~ijI2A5}o4`Hs zBk<&ni=w}!e1TuXf6u-bea8I#Ds6>v=U(4&=Q?k$Ip)>g+!M>5b4*&tx{lQ;Fq5`s z^Um?J8k&>7t>2>P@3i$K{0W?6^!Z{q4b-9sI4(jCG^4x@)T=-R;Y;L1MO%&idJtm2 z17%}>7?}6Y{eOU4;XW9B)1v5&)d+oBYu>$oI0sIE!y)8=j&IZ`s6k0)B z=mgy$#NK_Mb8bIyJUnvEf@s0&jQi{u_LRUZ6i^akxZ&$$<|Ki4A1LCAsblski(Z3OFrT9)TP1>sBN zM7fRiu{Zm9-)EaW;9&S2^o9_7=lzlF_2#YaxX;(#9EiU=cHULO)z5?9O8>3#L-e8VAqN}}YI6v5g^th~HiRairkNZlk#JWq zBiNUDchwww-OqM_F0elw4QIlo5Z3&W{T4g!tEygkz5D*2TE6Uy;@=nWz2eF))6(C~ zI`6s$sCj=d-p&K!p5PX6esJv{?LFRG-dn|zec$`{ntg9DU`hJBsO%hYJp31^ky`Bv zYNv+lLnEjQ@prv)-fszQz`4vh%{|Vk;Fxp#InR!~c0n|^Cb1v*M*2J5SzAp^&0YFM z@$Zg#2e*>n;Qg8Y+(A0wy2F7sg?UAwNpd2R8zIBFzia^L?%DY(=hgbN!dK!AC3jrIrltjJ$yA{ z|8MS}3D0@#Ffp3F>_5fhWe#+kk>1aTcRHk@;9pX zPXhN`&iljqE{tBQ$+*8|ar!xG8f{F0$?fQ zuNBMzp1(k{h@Q-h$UN59^W#^qI zi2Wk&Rd4s^BfvP`56cieZO_?fkEKB?|2i-p7Ywn*3GxWHf@{h zW!3@vwC`YpdrNH_eZ#`k!s^a#L;jlnj?j5XjZOzO+Y8jN9jK|=Hq4j3WIk6h{_J~R z%h$KiXLaQHU?G01?R{_;3-%~9&bPkbYkS@MjCo(UCdRVo zyk?z;Ze5aE5Nz^q5%}HCRL+}sUU#oxpP_&6y92aA8z1KHQ0DU6!zw@3416>Fn>5?0 z%kgi3{e|ZL)3}zN|B27oufC1G4f+;38vE-PruW!BCmDC=NB0lcfqSK^Vy@+Q12~S2 z!`(fej>hEDXn&ABjd`21p@ zp?_`BrW-*UwRL1wbKpy~*l|j_5ARhYwdx7opcAx#R?rkmdH%1DeNA9h`@egxm>0cL zYbmxr z+qQiYxc}P&!v0T9)wX`g%=h_*RQ~+e1)KMWV?nLd@OF6M%K6cp%Cw2ceSYlKU7b$_ z-;*5!zlEdWI5-g;H^%WiFcx8aUqIP>G2SPD_ZZfnxe?^6 zUOJyM@o$KYO`#=h37x=wtXiJ}YS|Z@rypQ^ypp{ZI2T?vH~kyk;atBH)KNXv#n{=- z0kAjh1wCM2I1s$1cGhzY+26@=*?1iV2Z8r=h23K;d(LY|f_1&$du_wAZQ6F|e;>-; zqYc_(ybcF*NL#hpIb<*lCkI~o;+yi{hX3c=)Hi)zPd`T*`$5nb9Q*cXA8OVKwgk0Q zQ?)Ifc;{P^iGMw8G*8XfO~Cv$kM{$|${BDm=&w0{56@I%*mJ+i_vEj}>!5StK)4<* z1;?Gbs;_$Q1RY^paDLn#>_g3r!@+PESYHkS^TRk9b9L!9Uv+K= z=GI2AG1!M%bp~~_uRWn#d~|&83f^nX+JN_M5M$YMUbBvMy(ha3+q7-_Fn8?J`?Nt@ zv}p%0KU}+=L$uv79sKN1`L-C#`tCLBsEOJ*2bqUzW*;p;E$c&FiNAI>fTqw2)YkE$ zrsjY-ehN5G>ieZ|8C)KhJ?Ay+Sl4@u#j@vm zgLQrb2Z0)?mHlX^eU!((ZjAqW*ene|Z5<xan5z@ z84LAQXZ1F2p&!S``k-FMW(%-C>&up4Y`u2_P}A)DEql&u*0HYlg!^sFHf`HJ?8`nw z{~?FG*L$?JGCxn{e6JYG`gsiW1U2yy3P~Q3XHAy8n=p?X3lxd zI@a}`aKCMZxZ8()g?_`aGCy~rp7U0|pZcoL`tCLBSa(~{&k%dt4DFX6b5$>$PnFr4 zn*(at8p3|d*gCfLf3FzJVXf^%*}C3iJRA>|Ym({5x!4#wmW`vi&>Xz433$Celxvw= z_L_C9>pk9U8@6Sew(a#o_lF$F_S5mKpN?(U1#?;~d(OC9C-~{T=2&4rODE#FmK4@S zy&Q9D=(_2C$@x{E&3XOr9Anw>?ltRJH>|aljobWK7(=x)PL5NtT$Ou6Th`6L-!_Zo zPWHWFEeU>Z#l4mJ={c_jKb?2Hx45mjs+ZQM;yIvxj-lpYo~w^zyA8C9vFtgoS;xBG zQ(Tkc{f5~5P{(XPH^g>*344mRl*O0_Kdlq|^xndDmR7VYx2Bl=nK9(pGSBtdv2C1P z^Te{}yk;H8Z)s{$JwF}W`F?sX_^BqP>1X9pE`GMSHs-hUsj&^~wd<`IbI*CL@ERD} zFK)$}zZ&c)d<<(*@YA(jf}h5{^!==fl7%(Np5M%Q=XNy^%f-h*XtyRdt%-YrJ;jf~ zPyGwjEkD Date: Thu, 22 Feb 2018 13:54:45 +0300 Subject: [PATCH 23/32] mutators: readme load_after -> enable_after --- vmf_source/scripts/mods/vmf/modules/mutators/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/README.md b/vmf_source/scripts/mods/vmf/modules/mutators/README.md index 45d7b3f..65a9b4e 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/README.md +++ b/vmf_source/scripts/mods/vmf/modules/mutators/README.md @@ -97,7 +97,7 @@ You can improve the compatibility of your mutator with other ones by specifiying 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 `load_before_these/load_after_these` fields. This shouldn't be modified. +``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. From 42290cd54fde0dcac8c35b008f425094015c9bc2 Mon Sep 17 00:00:00 2001 From: UnShame Date: Thu, 22 Feb 2018 14:05:50 +0300 Subject: [PATCH 24/32] mutators: remove unnecessary check --- .../scripts/mods/vmf/modules/mutators/mutator_manager.lua | 4 ---- 1 file changed, 4 deletions(-) diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua index 8ec45bf..bffce39 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua @@ -215,10 +215,6 @@ local function set_mutator_state(mutator, state) return end - if state and #mutator:get_incompatible_mutators(true) > 0 then - return - end - -- Sort mutators if this is the first call if not mutators_sorted then manager.sort_mutators() From 0a9edff19a5fe3a29d99fc00aba2b0e0693580f4 Mon Sep 17 00:00:00 2001 From: UnShame Date: Thu, 22 Feb 2018 14:22:00 +0300 Subject: [PATCH 25/32] mutator: update readme --- vmf_source/scripts/mods/vmf/modules/mutators/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/README.md b/vmf_source/scripts/mods/vmf/modules/mutators/README.md index 65a9b4e..525ca18 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/README.md +++ b/vmf_source/scripts/mods/vmf/modules/mutators/README.md @@ -105,3 +105,10 @@ Mutators have the same methods and event handlers as other mods plus a few addit ``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. From 9e13a1a672b9f78bf5be876752e859391378ab68 Mon Sep 17 00:00:00 2001 From: UnShame Date: Thu, 22 Feb 2018 14:34:48 +0300 Subject: [PATCH 26/32] mutators: add "no mutators installed" text --- .../mods/vmf/modules/mutators/mutator_gui.lua | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua index 6703c31..fe694e1 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua @@ -210,6 +210,28 @@ local mutators_view = { 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.offset[1] = 0 + else + checkbox.style.checkbox_style.offset[1] = 0 + checkbox.style.setting_text.horizontal_alignment = "left" + checkbox.style.setting_text.offset[1] = 24 + end end, -- Activate on button click or map open From 91d347c1fb989dcb274aee2126f9f1f73bcb9e37 Mon Sep 17 00:00:00 2001 From: UnShame Date: Thu, 22 Feb 2018 14:39:46 +0300 Subject: [PATCH 27/32] mutator: no mutators message hover fix --- vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua index fe694e1..613556c 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua @@ -226,11 +226,15 @@ local mutators_view = { 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, From c1e5411040eff2a766ee91706a1d11411d9e7da1 Mon Sep 17 00:00:00 2001 From: UnShame Date: Thu, 22 Feb 2018 15:03:24 +0300 Subject: [PATCH 28/32] mutator: fix level stepper not showing back up after closing mutators --- vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua index 613556c..4d9fbcf 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua @@ -273,6 +273,8 @@ local mutators_view = { 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 @@ -298,8 +300,6 @@ local mutators_view = { widgets.survival["mutator_checkbox_" .. i] = nil end - self.active = false - --print("DEACTIVE") end, From 3e93de6ca785818434ff9c05ece8cd64c3ea2326 Mon Sep 17 00:00:00 2001 From: UnShame Date: Thu, 22 Feb 2018 17:19:06 +0300 Subject: [PATCH 29/32] mutators: auto disable when no longer hosting; remove test strings --- .../mutators/mutator_gui_definitions.lua | 4 +- .../vmf/modules/mutators/mutator_info.lua | 8 +- .../vmf/modules/mutators/mutator_manager.lua | 204 ++++++++++-------- 3 files changed, 118 insertions(+), 98 deletions(-) diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui_definitions.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui_definitions.lua index 77d8d14..f3a5b65 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui_definitions.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui_definitions.lua @@ -235,11 +235,11 @@ for i = 1, definitions.PER_PAGE do } }, content = { - tooltip_text = "Mutator ajksad " .. i, + tooltip_text = "", checkbox_unchecked_texture = "checkbox_unchecked", checkbox_checked_texture = "checkbox_checked", selected = false, - setting_text = "Mutator asdasasda " .. i * 3, + setting_text = "", button_hotspot = {} }, style = { diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_info.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_info.lua index a887c4b..20ef46e 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_info.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_info.lua @@ -25,7 +25,12 @@ end -- Sets the lobby name local function set_lobby_data() - if not Managers.matchmaking then return end + 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 = get_enabled_mutators_names(" ", true) @@ -79,7 +84,6 @@ manager:hook("MatchmakingStateHostGame.host_game", function(func, self, ...) func(self, ...) set_lobby_data() local names = get_enabled_mutators_names(", ") - manager:echo("TEST") if names then manager:chat_broadcast("ENABLED MUTATORS: " .. names) were_enabled_before = true diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua index bffce39..1727136 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua @@ -27,97 +27,6 @@ local mutators_sequence = { local mutators_sorted = false ---[[ - 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) - 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 - local message = everybody and "MUTATORS DISABLED DUE TO DIFFICULTY CHANGE:" or "Mutators disabled due to difficulty change:" - for i, mutator in ipairs(disabled_mutators) do - message = message .. (i == 1 and " " or ", ") .. (mutator:get_config().title or mutator:get_name()) - end - if everybody then - manager:chat_broadcast(message) - else - manager:echo(message) - end - end - return disabled_mutators -end - - --[[ PRIVATE METHODS ]]-- @@ -258,6 +167,111 @@ local function set_mutator_state(mutator, state) 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 .. ":" + for i, mutator in ipairs(disabled_mutators) do + message = message .. (i == 1 and " " or ", ") .. (mutator:get_config().title or mutator:get_name()) + end + 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 + --[[ MUTATOR'S OWN METHODS @@ -273,11 +287,12 @@ local function disable_mutator(self) manager:pcall(function() set_mutator_state(self, false) end) end --- Checks current difficulty, map selection screen settings (optionally) and incompatible mutators to determine if a mutator can be enabled +-- 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 self:supports_current_difficulty(ignore_map) + return player_is_server() and self:supports_current_difficulty(ignore_map) end -- Only checks difficulty @@ -376,7 +391,7 @@ end HOOKS ]]-- manager:hook("DifficultyManager.set_difficulty", function(func, self, difficulty) - manager.disable_impossible_mutators(true, true) + manager.disable_impossible_mutators(true, true, "DUE TO CHANGE IN DIFFICULTY") return func(self, difficulty) end) @@ -392,3 +407,4 @@ manager:pcall(function() mutators_view:init(mutators_view:get_map_view()) end) Testing --]] --manager:dofile("scripts/mods/vmf/modules/mutators/mutator_test") +--manager:dofile("scripts/mods/vmf/modules/mutators/mutators/mutation") From 5943a29623d95eb06fee0767fb8d9e181f4c0968 Mon Sep 17 00:00:00 2001 From: UnShame Date: Thu, 22 Feb 2018 17:24:05 +0300 Subject: [PATCH 30/32] mutators: remove pcalls --- .../scripts/mods/vmf/modules/mutators/mutator_gui.lua | 8 ++++---- .../mods/vmf/modules/mutators/mutator_manager.lua | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua index 4d9fbcf..14ef0ed 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua @@ -379,7 +379,7 @@ local mutators_view = { -- Update the view after map_view has updated manager:hook("MapView.update", function(func, map_view, dt, t) func(map_view, dt, t) - manager:pcall(function() self:update(dt, t) end) + self:update(dt, t) end) -- Activate the view on enter if it was active on exit @@ -387,14 +387,14 @@ local mutators_view = { func(map_view) if self.was_active then self.widgets.mutators_button.content.toggled = true - manager:pcall(function() self:activate() end) + self:activate() end end) -- Deactivate the view on exit manager:hook("MapView.on_exit", function(func, map_view) func(map_view) - manager:pcall(function() self:deactivate() end) + self:deactivate() end) -- We don't want to let the game disable steppers when mutators view is active @@ -444,7 +444,7 @@ local mutators_view = { -- Initialize mutators view after map view manager:hook("MapView.init", function(func, self, ...) func(self, ...) - manager:pcall(function() mutators_view:init(self) end) + mutators_view:init(self) end) -- Destroy mutators view after map view diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua index 1727136..11a17cf 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua @@ -277,14 +277,14 @@ end MUTATOR'S OWN METHODS ]]-- --- Enables mutator (pcall for now) +-- Enables mutator local function enable_mutator(self) - manager:pcall(function() set_mutator_state(self, true) end) + set_mutator_state(self, true) end --- Disables mutator (pcall for now) +-- Disables mutator local function disable_mutator(self) - manager:pcall(function() set_mutator_state(self, false) end) + set_mutator_state(self, false) end -- Checks current difficulty, map selection screen settings (optionally), incompatible mutators and whether player is server @@ -401,7 +401,7 @@ end) --]] -- Initialize mutators view when map_view has been initialized already -manager:pcall(function() mutators_view:init(mutators_view:get_map_view()) end) +mutators_view:init(mutators_view:get_map_view()) --[[ Testing From afb4b3fa5747d5b747a1f27334643b2e40055dac Mon Sep 17 00:00:00 2001 From: UnShame Date: Thu, 22 Feb 2018 18:21:20 +0300 Subject: [PATCH 31/32] mutators: tooltip adjustments --- .../scripts/mods/vmf/modules/mutators/mutator_gui.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua index 14ef0ed..1e08a11 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_gui.lua @@ -326,9 +326,8 @@ local mutators_view = { -- Creates and return text for checkbox tooltip generate_tooltip_for = function(self, mutator) - -- Description local config = mutator:get_config() - local text = config.description + local text = "" -- Show supported difficulty when can't be enabled due to difficulty level local supports_difficulty = mutator:supports_current_difficulty() @@ -371,6 +370,12 @@ local mutators_view = { 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, From 99093eddcfde1e825c5427799f8af86dc0bde589 Mon Sep 17 00:00:00 2001 From: UnShame Date: Thu, 22 Feb 2018 19:50:24 +0300 Subject: [PATCH 32/32] mutators: title placement --- .../mods/vmf/modules/mutators/README.md | 5 ++ .../mutators/mutator_default_config.lua | 1 + .../vmf/modules/mutators/mutator_info.lua | 37 +++++--------- .../vmf/modules/mutators/mutator_manager.lua | 51 +++++++++++++++++-- 4 files changed, 65 insertions(+), 29 deletions(-) diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/README.md b/vmf_source/scripts/mods/vmf/modules/mutators/README.md index 525ca18..40fa51c 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/README.md +++ b/vmf_source/scripts/mods/vmf/modules/mutators/README.md @@ -24,6 +24,7 @@ The config object is optional but obviously you'd want to provide at least a rea title = "", short_title = "", description = "No description provided", + title_placement = "after", dice = { grims = 0, tomes = 0, @@ -58,6 +59,10 @@ 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. diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_default_config.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_default_config.lua index bfa828d..744b8d9 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_default_config.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_default_config.lua @@ -6,6 +6,7 @@ return { }, title = "", short_title = "", + title_placement = "after", description = "No description provided", difficulty_levels = { "easy", diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_info.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_info.lua index 20ef46e..65c2aa2 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_info.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_info.lua @@ -6,20 +6,14 @@ local mutators = manager.mutators local were_enabled_before = false -- Assembles a list of enabled mutators -local function get_enabled_mutators_names(separator, short) - local name = nil +local function add_enabled_mutators_titles_to_string(str, separator, short) + local _mutators = {} for _, mutator in ipairs(mutators) do - local config = mutator:get_config() if mutator:is_enabled() then - local added_name = (short and config.short_title or config.title or mutator:get_name()) - if name then - name = name .. separator .. added_name - else - name = added_name - end + table.insert(_mutators, mutator) end end - return name + return manager.add_mutator_titles_to_string(_mutators, str, separator, short) end -- Sets the lobby name @@ -32,10 +26,10 @@ local function set_lobby_data() not Managers.matchmaking.lobby.get_stored_lobby_data ) then return end - local name = get_enabled_mutators_names(" ", true) + local name = add_enabled_mutators_titles_to_string("", " ", true) local default_name = LobbyAux.get_unique_server_name() - if name then + if string.len(name) > 0 then name = "||" .. name .. "|| " .. default_name else name = default_name @@ -66,14 +60,9 @@ 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 nil - local mutators_name = get_enabled_mutators_names(" ", true) - if mutators_name then - if name then name = name .. " " else name = "" end - name = name .. mutators_name - else - name = "" - end + 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 @@ -83,8 +72,8 @@ end) manager:hook("MatchmakingStateHostGame.host_game", function(func, self, ...) func(self, ...) set_lobby_data() - local names = get_enabled_mutators_names(", ") - if names then + 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 @@ -95,8 +84,8 @@ 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 = get_enabled_mutators_names(", ") - if name then + 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 diff --git a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua index 11a17cf..147a954 100644 --- a/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua +++ b/vmf_source/scripts/mods/vmf/modules/mutators/mutator_manager.lua @@ -253,9 +253,7 @@ manager.disable_impossible_mutators = function(notify, everybody, reason) if #disabled_mutators > 0 and notify then if not reason then reason = "" end local message = everybody and "MUTATORS DISABLED " .. reason .. ":" or "Mutators disabled " .. reason .. ":" - for i, mutator in ipairs(disabled_mutators) do - message = message .. (i == 1 and " " or ", ") .. (mutator:get_config().title or mutator:get_name()) - end + message = message .. " " .. manager.add_mutator_titles_to_string(disabled_mutators, "", ", ", false) if everybody then manager:chat_broadcast(message) else @@ -272,6 +270,48 @@ manager.update = function() 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 @@ -406,5 +446,6 @@ 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/mutator_test") +-- manager:dofile("scripts/mods/vmf/modules/mutators/mutators/mutation") +-- manager:dofile("scripts/mods/vmf/modules/mutators/mutators/deathwish")