diff --git a/vmf/scripts/mods/vmf/modules/core/keybindings.lua b/vmf/scripts/mods/vmf/modules/core/keybindings.lua index a0daad7..ed3ffcd 100644 --- a/vmf/scripts/mods/vmf/modules/core/keybindings.lua +++ b/vmf/scripts/mods/vmf/modules/core/keybindings.lua @@ -215,7 +215,7 @@ local function perform_keybind_action(data, is_pressed) call_function(data.mod, data.function_name, is_pressed) return true elseif data.type == "view_toggle" and data.mod:is_enabled() then - vmf:echo("KEYBIND [VIEW_TOGGLE] " .. (is_pressed and "PRESSED" or "RELEASED") .. ": " .. data.view_name) + vmf.keybind_toggle_view(data.view_name, can_perform_action) return true end end diff --git a/vmf/scripts/mods/vmf/modules/gui/custom_menus.lua b/vmf/scripts/mods/vmf/modules/gui/custom_menus.lua deleted file mode 100644 index 1e46dde..0000000 --- a/vmf/scripts/mods/vmf/modules/gui/custom_menus.lua +++ /dev/null @@ -1,203 +0,0 @@ -local vmf = get_mod("VMF") - -local ingame_ui = nil - --- needed to protect opened menus from being closed right away and vice versa -local closing_keybind_is_pressed = false -local opening_keybind_is_pressed = true - -local views_settings = {} - --- #################################################################################################################### --- ##### VMFMod ####################################################################################################### --- #################################################################################################################### - -VMFMod.register_new_view = function (self, new_view_data) - - new_view_data.view_settings.mod_name = self:get_name() - - views_settings[new_view_data.view_name] = new_view_data.view_settings - - -- there's no direct access to local variable 'transitions' in ingame_ui - local transitions = require("scripts/ui/views/ingame_ui_settings").transitions - - for transition_name, transition_function in pairs(new_view_data.view_transitions) do - transitions[transition_name] = transition_function - end - - if new_view_data.view_settings.hotkey_action_name then - -- create function mod.hotkey_action_name() - -- so the menu will open when the keybind is pressed - self[new_view_data.view_settings.hotkey_action_name] = function() - - if not closing_keybind_is_pressed - and ingame_ui - and not ingame_ui:pending_transition() - and not ingame_ui:end_screen_active() - and not ingame_ui.menu_active - and not ingame_ui.leave_game - and not ingame_ui.return_to_title_screen - -- V2 doesn't have 'popup_join_lobby_handler' - and not (ingame_ui.popup_join_lobby_handler and ingame_ui.popup_join_lobby_handler.visible) - then - ingame_ui:handle_transition(new_view_data.view_settings.hotkey_transition_name) - end - - closing_keybind_is_pressed = false - end - end - - -- if reloading mods, ingame_ui exists and hook "IngameUI.setup_views" won't work - -- so set new variables and create new menu manually - if ingame_ui then - - -- set 'ingame_ui.views' - local new_view_name = new_view_data.view_name - local new_view_init_function = new_view_data.view_settings.init_view_function - - --if new_view_name ~= "vmf_options_view" then - ingame_ui.views[new_view_name] = new_view_init_function(ingame_ui.ingame_ui_context) - --end - -- set 'ingame_ui.blocked_transitions' - local blocked_transitions = new_view_data.view_settings.blocked_transitions - local current_blocked_transitions = ingame_ui.is_in_inn and blocked_transitions.inn or blocked_transitions.ingame - - for blocked_transition_name, _ in pairs(current_blocked_transitions) do - ingame_ui.blocked_transitions[blocked_transition_name] = true - end - end -end - --- #################################################################################################################### --- ##### Hooks ######################################################################################################## --- #################################################################################################################### - -vmf:hook_safe(IngameUI, "setup_views", function(self, ingame_ui_context) - - for view_name, view_settings in pairs(views_settings) do - - if self.is_in_inn then - if view_settings.active.inn then - self.views[view_name] = view_settings.init_view_function(ingame_ui_context) - end - - for blocked_transition_name, _ in pairs(view_settings.blocked_transitions.inn) do - self.blocked_transitions[blocked_transition_name] = true - end - else - if view_settings.active.ingame then - self.views[view_name] = view_settings.init_view_function(ingame_ui_context) - end - - for blocked_transition_name, _ in pairs(view_settings.blocked_transitions.ingame) do - self.blocked_transitions[blocked_transition_name] = true - end - end - end -end) - -vmf:hook_safe(IngameUI, "init", function(self) - ingame_ui = self -end) - -vmf:hook_safe(IngameUI, "destroy", function() - ingame_ui = nil -end) - --- #################################################################################################################### --- ##### VMF internal functions and variables ######################################################################### --- #################################################################################################################### - -vmf.check_custom_menus_close_keybinds = function() - if ingame_ui then - if views_settings[ingame_ui.current_view] then - local opened_view_settings = views_settings[ingame_ui.current_view] - local mod_name = opened_view_settings.mod_name - local hotkey_name = opened_view_settings.hotkey_name - - if not hotkey_name then - return - end - - local close_keybind = get_mod(mod_name):get(hotkey_name) - - -- vmf keybinds input service - local input_service = Managers.input:get_service("VMF") - local original_is_blocked = input_service:is_blocked() - - if original_is_blocked then - Managers.input:device_unblock_service("keyboard", 1, "VMF") - end - - if opening_keybind_is_pressed and not input_service:get(close_keybind[1]) then - opening_keybind_is_pressed = false - end - - local input_ctrl = input_service:get("ctrl") - local input_shift = input_service:get("shift") - local input_alt = input_service:get("alt") - - local close_menu = false - if not opening_keybind_is_pressed then - if input_service:get(close_keybind[1]) and - (not close_keybind[2] and not input_ctrl or close_keybind[2] and input_ctrl) and - (not close_keybind[3] and not input_alt or close_keybind[3] and input_alt) and - (not close_keybind[4] and not input_shift or close_keybind[4] and input_shift) then - - close_menu = not ingame_ui.views[ingame_ui.current_view]:input_service():is_blocked() - end - end - - if original_is_blocked then - Managers.input:device_block_service("keyboard", 1, "VMF") - end - - if close_menu then - ingame_ui:handle_transition("exit_menu") - - closing_keybind_is_pressed = true - end - else - opening_keybind_is_pressed = true - end - end -end - -vmf.close_opened_custom_menus = function() - if ingame_ui then - local current_view = ingame_ui.current_view - if views_settings[current_view] then - ingame_ui:handle_transition("exit_menu") - - if ingame_ui.views[current_view].destroy and get_mod(views_settings[ingame_ui.current_view].mod_name) then - - local mod = get_mod(views_settings[current_view].mod_name) - local destroy_method = ingame_ui.views[current_view].destroy - vmf.xpcall_no_return_values(mod, "(custom menus) destroy view", destroy_method) - end - - ingame_ui.views[current_view] = nil - end - end -end - --- #################################################################################################################### --- ##### Script ####################################################################################################### --- #################################################################################################################### - - -local ingame_ui_exists, ingame_ui_return -if VT1 then - ingame_ui_exists, ingame_ui_return = pcall(function() - return Managers.player.network_manager.matchmaking_manager.matchmaking_ui.ingame_ui - end) -else - ingame_ui_exists, ingame_ui_return = pcall(function() - return Managers.player.network_manager.matchmaking_manager._ingame_ui - end) -end - --- if VMF is reloaded mid-game -if ingame_ui_exists then - ingame_ui = ingame_ui_return -end \ No newline at end of file diff --git a/vmf/scripts/mods/vmf/modules/gui/custom_views.lua b/vmf/scripts/mods/vmf/modules/gui/custom_views.lua new file mode 100644 index 0000000..eb64089 --- /dev/null +++ b/vmf/scripts/mods/vmf/modules/gui/custom_views.lua @@ -0,0 +1,218 @@ +local vmf = get_mod("VMF") + +local _ingame_ui = nil +-- There's no direct access to local variable 'transitions' in ingame_ui. +local _ingame_ui_transitions = require("scripts/ui/views/ingame_ui_settings").transitions +local _views_data = {} + +-- ##################################################################################################################### +-- ##### Local functions ############################################################################################### +-- ##################################################################################################################### + +local function find_view_owner(view_name) +end + + +local function find_transition_owner(transition_name) +end + + +-- Throws error. +local function inject_elements(view_name) + local view_settings = _views_data[view_name].view_settings + + local mod = _views_data[view_name].mod + local init_view_function = view_settings.init_view_function + local transitions = _views_data[view_name].view_transitions + local blocked_transitions = view_settings.blocked_transitions[_ingame_ui.is_in_inn and "inn" or "ingame"] + + -- Check for collisions. + if _ingame_ui.views[view_name] then + -- @TODO: throw error + end + for transition_name, _ in pairs(transitions) do + if _ingame_ui_transitions[transition_name] then + -- @TODO: throw error + end + end + + -- Initialize and inject view. + local success, view = vmf.xpcall(mod, "calling init_view_function", init_view_function, _ingame_ui.ingame_ui_context) + if success then + _ingame_ui.views[view_name] = view + else + -- @TODO: throw error + end + + -- Inject view transitions. + for transition_name, transition_function in pairs(transitions) do + _ingame_ui_transitions[transition_name] = transition_function + end + + -- Inject view blocked transitions. + for blocked_transition_name, _ in pairs(blocked_transitions) do + _ingame_ui.blocked_transitions[blocked_transition_name] = true + end +end + + +local function remove_injected_elements(on_reload) + -- These elements should be removed only on_reload, because, otherwise, they will be deleted automatically. + if on_reload and _ingame_ui then + -- If some custom view is active, close it. + if _views_data[_ingame_ui.current_view] then + _ingame_ui:handle_transition("exit_menu") + end + + for view_name, view_data in pairs(_views_data) do + -- Remove injected views. + local view = _ingame_ui.views[view_name] + if view then + if type(view.destroy) == "function" then + vmf.xpcall_no_return_values(view_data.mod, "(custom menus) destroy view", view.destroy) + end + _ingame_ui.views[view_name] = nil + end + + -- Remove blocked transitions + local blocked_transitions = view_data.view_settings.blocked_transitions[_ingame_ui.is_in_inn and "inn" or + "ingame"] + for blocked_transition_name, _ in pairs(blocked_transitions) do + _ingame_ui.blocked_transitions[blocked_transition_name] = nil + end + end + end + + -- Remove injected transitions. + for _, view_data in pairs(_views_data) do + for transition_name, _ in pairs(view_data.view_transitions) do + _ingame_ui_transitions[transition_name] = nil + end + end +end + + +-- Throws error. +-- Make, so blocked transitions can be only the one from this view, so they won't need further checks +local function validate_view_data(view_data) +end + +-- ##################################################################################################################### +-- ##### VMFMod ######################################################################################################## +-- ##################################################################################################################### + +function VMFMod:handle_transition(transition_name, transition_params, fade) + if _ingame_ui + and not _ingame_ui:pending_transition() + and not _ingame_ui:end_screen_active() + and not _ingame_ui.menu_active + and not _ingame_ui.leave_game + and not _ingame_ui.return_to_title_screen + and ( + VT1 + and not _ingame_ui.popup_join_lobby_handler.visible + or not VT1 + and not _ingame_ui.ingame_hud.ingame_player_list_ui:is_active() + and not Managers.transition:in_fade_active() + and not _ingame_ui:cutscene_active() + and not _ingame_ui:unavailable_hero_popup_active() + ) + then + if fade then + _ingame_ui:transition_with_fade(transition_name, transition_params) + else + _ingame_ui:handle_transition(transition_name, transition_params) + end + return true + end +end + + +function VMFMod:register_view(view_data) + if vmf.check_wrong_argument_type(self, "register_view", "view_data", view_data, "table") then + return + end + + if vmf.catch_errors(self, "(register_view) view data validating: %s", validate_view_data, view_data) then + return + end + + _views_data[view_data.view_name] = { + mod = self, + view_settings = table.clone(view_data.view_settings), + view_transitions = table.clone(view_data.view_transitions) + } + + if _ingame_ui then + if vmf.catch_errors(self, "(custom views) view injection: %s", inject_elements, view_data.view_name) then + _views_data[view_data.view_name] = nil + end + end +end + +-- ##################################################################################################################### +-- ##### Hooks ######################################################################################################### +-- ##################################################################################################################### + +vmf:hook_safe(IngameUI, "init", function(self) + _ingame_ui = self + for view_name, _ in pairs(_views_data) do + if vmf.catch_errors(self, "(custom views) view injection: %s", inject_elements, view_name) then + _views_data[view_name] = nil + end + end +end) + + +vmf:hook_safe(IngameUI, "destroy", function() + _ingame_ui = nil + remove_injected_elements(false) +end) + +-- ##################################################################################################################### +-- ##### VMF internal functions and variables ########################################################################## +-- ##################################################################################################################### + +function vmf.remove_custom_views() + remove_injected_elements(true) +end + + +function vmf.keybind_toggle_view(view_name, can_be_opened) + --@TODO: check if there's the custom view at all. If not, show error. + + if _ingame_ui then + local mod = _views_data[view_name].mod + local keybind_transitions = _views_data[view_name].view_settings.keybind_transitions + if not _ingame_ui.menu_suspended then + if _ingame_ui.current_view == view_name then + if keybind_transitions.close_view_transition then + mod:handle_transition(keybind_transitions.close_view_transition, keybind_transitions.close_view_transition_params, keybind_transitions.transition_fade) + end + elseif can_be_opened then + if keybind_transitions.open_view_transition then + mod:handle_transition(keybind_transitions.open_view_transition, keybind_transitions.close_view_transition_params, keybind_transitions.transition_fade) + end + end + end + end +end + +-- ##################################################################################################################### +-- ##### Script ######################################################################################################## +-- ##################################################################################################################### + +-- If VMF is reloaded mid-game, get ingame_ui. +local ingame_ui_exists, ingame_ui_return +if VT1 then + ingame_ui_exists, ingame_ui_return = pcall(function() + return Managers.player.network_manager.matchmaking_manager.matchmaking_ui.ingame_ui + end) +else + ingame_ui_exists, ingame_ui_return = pcall(function() + return Managers.player.network_manager.matchmaking_manager._ingame_ui + end) +end +if ingame_ui_exists then + _ingame_ui = ingame_ui_return +end \ No newline at end of file diff --git a/vmf/scripts/mods/vmf/modules/ui/options/vmf_options_view.lua b/vmf/scripts/mods/vmf/modules/ui/options/vmf_options_view.lua index b86a331..02ab846 100644 --- a/vmf/scripts/mods/vmf/modules/ui/options/vmf_options_view.lua +++ b/vmf/scripts/mods/vmf/modules/ui/options/vmf_options_view.lua @@ -4288,32 +4288,16 @@ local view_data = { }, blocked_transitions = { inn = {}, - ingame = { - --vmf_options_view = true, - --vmf_options_view_force = true - } + ingame = {} }, - hotkey_name = "open_vmf_options", - hotkey_action_name = "open_vmf_options", - hotkey_transition_name = "vmf_options_view", - transition_fade = false + keybind_transitions = { + open_view_transition = "vmf_options_view", + close_view_transition = "exit_menu", + } }, view_transitions = { - vmf_options_view = function (self) self.current_view = "vmf_options_view" - - return - end, - - vmf_options_view_force = function (self) - - ShowCursorStack.push() - - self.current_view = "vmf_options_view" - - self.views[self.current_view].exit_to_game = true -- why? - return end } } @@ -4435,7 +4419,7 @@ end vmf.initialize_vmf_options_view = function () - vmf:register_new_view(view_data) + vmf:register_view(view_data) _button_injection_data.mod_options_button_disabled = false end diff --git a/vmf/scripts/mods/vmf/vmf_loader.lua b/vmf/scripts/mods/vmf/vmf_loader.lua index a0edf6a..0b95550 100644 --- a/vmf/scripts/mods/vmf/vmf_loader.lua +++ b/vmf/scripts/mods/vmf/vmf_loader.lua @@ -31,7 +31,7 @@ function vmf_mod_object:init() dofile("scripts/mods/vmf/modules/core/network") dofile("scripts/mods/vmf/modules/core/commands") dofile("scripts/mods/vmf/modules/gui/custom_textures") - dofile("scripts/mods/vmf/modules/gui/custom_menus") + dofile("scripts/mods/vmf/modules/gui/custom_views") dofile("scripts/mods/vmf/modules/gui/ui_scaling") dofile("scripts/mods/vmf/modules/ui/chat/chat_actions") dofile("scripts/mods/vmf/modules/ui/options/vmf_options_view") @@ -57,7 +57,6 @@ end function vmf_mod_object:update(dt) vmf.mods_update_event(dt) vmf.check_keybinds() - vmf.check_custom_menus_close_keybinds(dt) vmf.execute_queued_chat_command() if VT1 then vmf.check_mutators_state() end @@ -88,9 +87,9 @@ end function vmf_mod_object:on_reload() print("VMF:ON_RELOAD()") vmf.disable_mods_options_button() - vmf.close_opened_custom_menus() if VT1 then vmf.reset_map_view() end vmf.mods_unload_event(false) + vmf.remove_custom_views() vmf.hooks_unload() vmf.reset_guis() end