From 75fb26d3cf3c869707ccf88d7466bd3875db1182 Mon Sep 17 00:00:00 2001 From: Azumgi <4zumgi@gmail.com> Date: Thu, 27 Sep 2018 15:05:48 +0300 Subject: [PATCH 01/19] [Keybinds] Change way of getting key data --- .../mods/vmf/modules/core/keybindings.lua | 31 +++++++++++++------ vmf/scripts/mods/vmf/modules/core/options.lua | 2 +- .../modules/ui/options/vmf_options_view.lua | 16 +++++----- 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/vmf/scripts/mods/vmf/modules/core/keybindings.lua b/vmf/scripts/mods/vmf/modules/core/keybindings.lua index dcacf72..9406fbe 100644 --- a/vmf/scripts/mods/vmf/modules/core/keybindings.lua +++ b/vmf/scripts/mods/vmf/modules/core/keybindings.lua @@ -180,7 +180,7 @@ end local keyboard_buton_name = Keyboard.button_name local mouse_buton_name = Mouse.button_name -vmf.keys = { +local _keys = { keyboard = { [8] = {"Backspace", "backspace", keyboard_buton_name(8)}, [9] = {"Tab", "tab", keyboard_buton_name(9)}, @@ -327,32 +327,43 @@ vmf.keys = { }]] } -vmf.readable_key_names = {} +local _readable_key_names = {} + + +function vmf.get_key_name(device, key_index) + local key_info = _keys[device][key_index] + return key_info and key_info[2] +end + + +function vmf.get_readable_key_name(key_name) + return _readable_key_names[key_name] +end -- #################################################################################################################### -- ##### Script ####################################################################################################### -- #################################################################################################################### -for _, controller_keys in pairs(vmf.keys) do +for _, controller_keys in pairs(_keys) do for _, key_info in pairs(controller_keys) do - vmf.readable_key_names[key_info[2]] = key_info[1] + _readable_key_names[key_info[2]] = key_info[1] end end -vmf.readable_key_names["ctrl"] = "Ctrl" -vmf.readable_key_names["alt"] = "Alt" -vmf.readable_key_names["shift"] = "Shift" +_readable_key_names["ctrl"] = "Ctrl" +_readable_key_names["alt"] = "Alt" +_readable_key_names["shift"] = "Shift" -for _, key_info in pairs(vmf.keys.keyboard) do +for _, key_info in pairs(_keys.keyboard) do VMFModsKeyMap.win32[key_info[2]] = {"keyboard", key_info[3], "held"} end for i = 0, 4 do - local key_info = vmf.keys.mouse[i] + local key_info = _keys.mouse[i] VMFModsKeyMap.win32[key_info[2]] = {"mouse", key_info[3], "held"} end for i = 10, 13 do - local key_info = vmf.keys.mouse[i] + local key_info = _keys.mouse[i] VMFModsKeyMap.win32[key_info[2]] = {"mouse", key_info[3], "pressed"} end \ No newline at end of file diff --git a/vmf/scripts/mods/vmf/modules/core/options.lua b/vmf/scripts/mods/vmf/modules/core/options.lua index 9913318..ef255bc 100644 --- a/vmf/scripts/mods/vmf/modules/core/options.lua +++ b/vmf/scripts/mods/vmf/modules/core/options.lua @@ -339,7 +339,7 @@ local function validate_keybind_data(data) vmf.throw_error("[widget \"%s\" (keybind)]: table stored in 'default_value' field can't exceed 4 elements", data.setting_id) end - if default_value[1] and (not vmf.readable_key_names[default_value[1]] or allowed_special_keys[default_value[1]]) then + if default_value[1] and (not vmf.get_readable_key_name(default_value[1]) or allowed_special_keys[default_value[1]]) then vmf.throw_error("[widget \"%s\" (keybind)]: 'default_value[1]' must be a valid key name", data.setting_id) end if default_value[2] and not allowed_special_keys[default_value[2]] or 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 1865cd6..85174e6 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 @@ -2488,9 +2488,9 @@ local function build_keybind_string(keys) for i, key in ipairs(keys) do if i == 1 then - keybind_string = keybind_string .. vmf.readable_key_names[key] + keybind_string = keybind_string .. vmf.get_readable_key_name(key) else - keybind_string = keybind_string .. " + " .. vmf.readable_key_names[key] + keybind_string = keybind_string .. " + " .. vmf.get_readable_key_name(key) end end @@ -3307,25 +3307,25 @@ VMFOptionsView.callback_setting_keybind = function (self, widget_content) if not widget_content.first_pressed_button and (Keyboard.any_pressed() or Mouse.any_pressed()) then - local first_pressed_button_info = nil + local first_pressed_button_name = nil local first_pressed_button_index = nil local first_pressed_button_type = nil if Keyboard.any_pressed() then - first_pressed_button_info = vmf.keys.keyboard[Keyboard.any_pressed()] + first_pressed_button_name = vmf.get_key_name("keyboard", Keyboard.any_pressed()) first_pressed_button_index = Keyboard.any_pressed() first_pressed_button_type = "keyboard" elseif Mouse.any_pressed() then - first_pressed_button_info = vmf.keys.mouse[Mouse.any_pressed()] + first_pressed_button_name = vmf.get_key_name("mouse", Mouse.any_pressed()) first_pressed_button_index = Mouse.any_pressed() first_pressed_button_type = "mouse" end - if first_pressed_button_info then - widget_content.first_pressed_button = first_pressed_button_info[2] + if first_pressed_button_name then + widget_content.first_pressed_button = first_pressed_button_name widget_content.first_pressed_button_index = first_pressed_button_index widget_content.first_pressed_button_type = first_pressed_button_type end @@ -3336,7 +3336,7 @@ VMFOptionsView.callback_setting_keybind = function (self, widget_content) if widget_content.first_pressed_button then table.insert(pressed_buttons, widget_content.first_pressed_button) - preview_string = vmf.readable_key_names[widget_content.first_pressed_button] + preview_string = vmf.get_readable_key_name(widget_content.first_pressed_button) end if Keyboard.button(Keyboard.button_index("left ctrl")) == 1 then preview_string = preview_string .. " + Ctrl" From 1c8e1121fe6263e3ca383f5c70239adb296996b4 Mon Sep 17 00:00:00 2001 From: Azumgi <4zumgi@gmail.com> Date: Mon, 15 Oct 2018 14:16:09 +0300 Subject: [PATCH 02/19] [Mods Options] Refactor keybind widget's callback --- .../mods/vmf/modules/core/keybindings.lua | 1 + .../modules/ui/options/vmf_options_view.lua | 82 ++++++------------- 2 files changed, 26 insertions(+), 57 deletions(-) diff --git a/vmf/scripts/mods/vmf/modules/core/keybindings.lua b/vmf/scripts/mods/vmf/modules/core/keybindings.lua index 9406fbe..732ccc5 100644 --- a/vmf/scripts/mods/vmf/modules/core/keybindings.lua +++ b/vmf/scripts/mods/vmf/modules/core/keybindings.lua @@ -353,6 +353,7 @@ end _readable_key_names["ctrl"] = "Ctrl" _readable_key_names["alt"] = "Alt" _readable_key_names["shift"] = "Shift" +_readable_key_names["no_button"] = "" -- hack for build_keybind_string function for _, key_info in pairs(_keys.keyboard) do VMFModsKeyMap.win32[key_info[2]] = {"keyboard", key_info[3], "held"} 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 85174e6..ae4bbe8 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 @@ -3305,92 +3305,60 @@ end VMFOptionsView.callback_setting_keybind = function (self, widget_content) - if not widget_content.first_pressed_button and (Keyboard.any_pressed() or Mouse.any_pressed()) then - - local first_pressed_button_name = nil - local first_pressed_button_index = nil - local first_pressed_button_type = nil - + if not widget_content.first_pressed_button_name then if Keyboard.any_pressed() then - - first_pressed_button_name = vmf.get_key_name("keyboard", Keyboard.any_pressed()) - first_pressed_button_index = Keyboard.any_pressed() - first_pressed_button_type = "keyboard" - + widget_content.first_pressed_button_name = vmf.get_key_name("keyboard", Keyboard.any_pressed()) + widget_content.first_pressed_button_index = Keyboard.any_pressed() + widget_content.first_pressed_button_type = "keyboard" elseif Mouse.any_pressed() then - - first_pressed_button_name = vmf.get_key_name("mouse", Mouse.any_pressed()) - first_pressed_button_index = Mouse.any_pressed() - first_pressed_button_type = "mouse" - end - - if first_pressed_button_name then - widget_content.first_pressed_button = first_pressed_button_name - widget_content.first_pressed_button_index = first_pressed_button_index - widget_content.first_pressed_button_type = first_pressed_button_type + widget_content.first_pressed_button_name = vmf.get_key_name("mouse", Mouse.any_pressed()) + widget_content.first_pressed_button_index = Mouse.any_pressed() + widget_content.first_pressed_button_type = "mouse" end end local pressed_buttons = {} - local preview_string = "" - if widget_content.first_pressed_button then - table.insert(pressed_buttons, widget_content.first_pressed_button) - preview_string = vmf.get_readable_key_name(widget_content.first_pressed_button) + if widget_content.first_pressed_button_name then + table.insert(pressed_buttons, widget_content.first_pressed_button_name) + else + table.insert(pressed_buttons, "no_button") end if Keyboard.button(Keyboard.button_index("left ctrl")) == 1 then - preview_string = preview_string .. " + Ctrl" table.insert(pressed_buttons, "ctrl") end if Keyboard.button(Keyboard.button_index("left alt")) == 1 then - preview_string = preview_string .. " + Alt" table.insert(pressed_buttons, "alt") end if Keyboard.button(Keyboard.button_index("left shift")) == 1 then - preview_string = preview_string .. " + Shift" table.insert(pressed_buttons, "shift") end - if preview_string ~= "" then - widget_content.keys = pressed_buttons - widget_content.keybind_text = preview_string - else - widget_content.keybind_text = "_" - end + local preview_string = build_keybind_string(pressed_buttons) - if widget_content.first_pressed_button then - if (widget_content.first_pressed_button_type == "keyboard" and Keyboard.released(widget_content.first_pressed_button_index) or - widget_content.first_pressed_button_type == "mouse" and Mouse.released(widget_content.first_pressed_button_index)) then + widget_content.keybind_text = preview_string ~= "" and preview_string or "_" + widget_content.keys = pressed_buttons - widget_content.keybind_text = build_keybind_string(widget_content.keys) - - widget_content.first_pressed_button = nil + if widget_content.first_pressed_button_name then + if widget_content.first_pressed_button_type == "keyboard" and Keyboard.released(widget_content.first_pressed_button_index) or + widget_content.first_pressed_button_type == "mouse" and Mouse.released(widget_content.first_pressed_button_index) + then + widget_content.first_pressed_button_name = nil widget_content.first_pressed_button_index = nil widget_content.first_pressed_button_type = nil - if widget_content.action then - get_mod(widget_content.mod_name):keybind(widget_content.setting_id, widget_content.action, widget_content.keys) - end + get_mod(widget_content.mod_name):keybind(widget_content.setting_id, widget_content.action, widget_content.keys) self:callback_change_setting_keybind_state(widget_content) - return true end - else - if Keyboard.released(Keyboard.button_index("esc")) then + elseif Keyboard.released(Keyboard.button_index("esc")) then + widget_content.keybind_text = "" - widget_content.keys = {} + get_mod(widget_content.mod_name):keybind(widget_content.setting_id, widget_content.action, widget_content.keys) - widget_content.keybind_text = build_keybind_string(widget_content.keys) - - if widget_content.action then - get_mod(widget_content.mod_name):keybind(widget_content.setting_id, widget_content.action, widget_content.keys) - end - - self:callback_change_setting_keybind_state(widget_content) - - return true - end + self:callback_change_setting_keybind_state(widget_content) + return true end end From f5b65201e0b6603b2e2705c197a1b2b554e03ec7 Mon Sep 17 00:00:00 2001 From: Azumgi <4zumgi@gmail.com> Date: Mon, 15 Oct 2018 14:27:44 +0300 Subject: [PATCH 03/19] [Keybinds] Move "build_keybind_string" function --- .../mods/vmf/modules/core/keybindings.lua | 12 +++++++++++ .../modules/ui/options/vmf_options_view.lua | 20 ++----------------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/vmf/scripts/mods/vmf/modules/core/keybindings.lua b/vmf/scripts/mods/vmf/modules/core/keybindings.lua index 732ccc5..ba70d0e 100644 --- a/vmf/scripts/mods/vmf/modules/core/keybindings.lua +++ b/vmf/scripts/mods/vmf/modules/core/keybindings.lua @@ -340,6 +340,18 @@ function vmf.get_readable_key_name(key_name) return _readable_key_names[key_name] end + +function vmf.build_keybind_string(keys) + local keybind_string = "" + for i, key_name in ipairs(keys) do + if i == 1 then + keybind_string = keybind_string .. _readable_key_names[key_name] + else + keybind_string = keybind_string .. " + " .. _readable_key_names[key_name] + end + end + return keybind_string +end -- #################################################################################################################### -- ##### Script ####################################################################################################### -- #################################################################################################################### 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 ae4bbe8..329d70c 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 @@ -2482,22 +2482,6 @@ end -- ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═════╝ ╚═╝╚═╝ ╚═══╝╚═════╝ -local function build_keybind_string(keys) - - local keybind_string = "" - - for i, key in ipairs(keys) do - if i == 1 then - keybind_string = keybind_string .. vmf.get_readable_key_name(key) - else - keybind_string = keybind_string .. " + " .. vmf.get_readable_key_name(key) - end - end - - return keybind_string -end - - local function create_keybind_widget(widget_definition, scenegraph_id) local widget_size = SETTINGS_LIST_REGULAR_WIDGET_SIZE @@ -3334,7 +3318,7 @@ VMFOptionsView.callback_setting_keybind = function (self, widget_content) table.insert(pressed_buttons, "shift") end - local preview_string = build_keybind_string(pressed_buttons) + local preview_string = vmf.build_keybind_string(pressed_buttons) widget_content.keybind_text = preview_string ~= "" and preview_string or "_" widget_content.keys = pressed_buttons @@ -3834,7 +3818,7 @@ VMFOptionsView.update_picked_option_for_settings_list_widgets = function (self) widget_content.keys = widget_content.default_value end - widget_content.keybind_text = build_keybind_string(widget_content.keys) + widget_content.keybind_text = vmf.build_keybind_string(widget_content.keys) elseif widget_type == "numeric" then From e83c3d893be06be21f9cd69435fdb9f879ad9db8 Mon Sep 17 00:00:00 2001 From: Azumgi <4zumgi@gmail.com> Date: Mon, 29 Oct 2018 15:34:41 +0300 Subject: [PATCH 04/19] [Keybinds] Overhaul --- .../mods/vmf/modules/core/keybindings.lua | 714 +++++++++--------- vmf/scripts/mods/vmf/modules/core/options.lua | 13 +- .../mods/vmf/modules/gui/custom_menus.lua | 6 +- .../mods/vmf/modules/legacy/options.lua | 18 +- .../modules/ui/options/vmf_options_view.lua | 46 +- vmf/scripts/mods/vmf/modules/vmf_options.lua | 5 +- vmf/scripts/mods/vmf/vmf_loader.lua | 7 +- 7 files changed, 417 insertions(+), 392 deletions(-) diff --git a/vmf/scripts/mods/vmf/modules/core/keybindings.lua b/vmf/scripts/mods/vmf/modules/core/keybindings.lua index ba70d0e..a0daad7 100644 --- a/vmf/scripts/mods/vmf/modules/core/keybindings.lua +++ b/vmf/scripts/mods/vmf/modules/core/keybindings.lua @@ -1,382 +1,374 @@ local vmf = get_mod("VMF") -VMFModsKeyMap = { - win32 = { - ["ctrl"] = {"keyboard", "left ctrl", "held"}, - ["alt"] = {"keyboard", "left alt", "held"}, - ["shift"] = {"keyboard", "left shift", "held"} +local PRIMARY_BINDABLE_KEYS = { + KEYBOARD = { + [8] = {"Backspace", "backspace"}, + [9] = {"Tab", "tab"}, + [13] = {"Enter", "enter"}, + [20] = {"Caps Lock", "caps lock"}, + [32] = {"Space", "space"}, + [33] = {"Page Up", "page up"}, + [34] = {"Page Down", "page down"}, + [35] = {"End", "end"}, + [36] = {"Home", "home"}, + [37] = {"Left", "left"}, + [38] = {"Up", "up"}, + [39] = {"Right", "right"}, + [40] = {"Down", "down"}, + [45] = {"Insert", "insert"}, + [46] = {"Delete", "delete"}, + [48] = {"0", "0"}, + [49] = {"1", "1"}, + [50] = {"2", "2"}, + [51] = {"3", "3"}, + [52] = {"4", "4"}, + [53] = {"5", "5"}, + [54] = {"6", "6"}, + [55] = {"7", "7"}, + [56] = {"8", "8"}, + [57] = {"9", "9"}, + [65] = {"A", "a"}, + [66] = {"B", "b"}, + [67] = {"C", "c"}, + [68] = {"D", "d"}, + [69] = {"E", "e"}, + [70] = {"F", "f"}, + [71] = {"G", "g"}, + [72] = {"H", "h"}, + [73] = {"I", "i"}, + [74] = {"J", "j"}, + [75] = {"K", "k"}, + [76] = {"L", "l"}, + [77] = {"M", "m"}, + [78] = {"N", "n"}, + [79] = {"O", "o"}, + [80] = {"P", "p"}, + [81] = {"Q", "q"}, + [82] = {"R", "r"}, + [83] = {"S", "s"}, + [84] = {"T", "t"}, + [85] = {"U", "u"}, + [86] = {"V", "v"}, + [87] = {"W", "w"}, + [88] = {"X", "x"}, + [89] = {"Y", "y"}, + [90] = {"Z", "z"}, + [91] = {"Win", "win"}, + [92] = {"RWin", "right win"}, + [96] = {"Num 0", "numpad 0"}, + [97] = {"Num 1", "numpad 1"}, + [98] = {"Num 2", "numpad 2"}, + [99] = {"Num 3", "numpad 3"}, + [100] = {"Num 4", "numpad 4"}, + [101] = {"Num 5", "numpad 5"}, + [102] = {"Num 6", "numpad 6"}, + [103] = {"Num 7", "numpad 7"}, + [104] = {"Num 8", "numpad 8"}, + [105] = {"Num 9", "numpad 9"}, + [106] = {"Num *", "numpad *"}, + [107] = {"Num +", "numpad +"}, + [109] = {"Num -", "numpad -"}, + [110] = {"Num .", "numpad ."}, + [111] = {"Num /", "numpad /"}, + [112] = {"F1", "f1"}, + [113] = {"F2", "f2"}, + [114] = {"F3", "f3"}, + [115] = {"F4", "f4"}, + [116] = {"F5", "f5"}, + [117] = {"F6", "f6"}, + [118] = {"F7", "f7"}, + [119] = {"F8", "f8"}, + [120] = {"F9", "f9"}, + [121] = {"F10", "f10"}, + [122] = {"F11", "f11"}, + [123] = {"F12", "f12"}, + [144] = {"Num Lock", "num lock"}, + [145] = {"Scroll Lock", "scroll lock"}, + [166] = {"Browser Back", "browser back"}, + [167] = {"Browser Forward", "browser forward"}, + [168] = {"Browser Refresh", "browser refresh"}, + [169] = {"Browser Stop", "browser stop"}, + [170] = {"Browser Search", "browser search"}, + [171] = {"Browser Favorites", "browser favorites"}, + [172] = {"Browser Home", "browser home"}, + [173] = {"Volume Mute", "volume mute"}, + [174] = {"Volume Down", "volume down"}, + [175] = {"Volume Up", "volume up"}, + [176] = {"Next Track", "next track"}, + [177] = {"Previous Track", "previous track"}, + [178] = {"Stop", "stop"}, + [179] = {"Play/Pause", "play pause"}, + [180] = {"Mail", "mail"}, + [181] = {"Media", "media"}, + [182] = {"Start Application 1", "start app 1"}, + [183] = {"Start Application 2", "start app 2"}, + [186] = {";", ";"}, + [187] = {"=", "="}, + [188] = {",", ","}, + [189] = {"-", "-"}, + [190] = {".", "."}, + [191] = {"/", "/"}, + [192] = {"`", "`"}, + [219] = {"[", "["}, + [220] = {"\\", "\\"}, + [221] = {"]", "]"}, + [222] = {"'", "'"}, + --?[226] = {"\", "oem_102 (> <)"}, + [256] = {"Num Enter", "numpad enter"} }, - xb1 = {} -} - --- ["mod_name"]["setting_id"] = { --- "function_name", --- {"primary_key", "special_key", "special_key", "special_key"} --- } --- Special Keys: "ctrl" / "shift" / "alt" -local _raw_keybinds = {} - --- ["primary_key"] = { --- {"mod_name", "function_name", ctrl_used(bool), alt_used(bool), shift_used(bool)}, --- {}, --- {}, --- ... --- } -local _optimized_keybinds = {} - -local _activated_pressed_key - --- #################################################################################################################### --- ##### Local functions ############################################################################################## --- #################################################################################################################### - -local function apply_keybinds() - - _optimized_keybinds = {} - - for mod_name, mod_keybinds in pairs(_raw_keybinds) do - for _, keybind in pairs(mod_keybinds) do - local function_name = keybind[1] - local primary_key = keybind[2][1] - - local special_key1 = keybind[2][2] - local special_key2 = keybind[2][3] - local special_key3 = keybind[2][4] - - local special_keys = {} - - if special_key1 then - special_keys[special_key1] = true - end - if special_key2 then - special_keys[special_key2] = true - end - if special_key3 then - special_keys[special_key3] = true - end - - _optimized_keybinds[primary_key] = _optimized_keybinds[primary_key] or {} - table.insert(_optimized_keybinds[primary_key], { - mod_name, function_name, - special_keys["ctrl"], - special_keys["alt"], - special_keys["shift"] - }) - end - end -end - --- #################################################################################################################### --- ##### VMFMod ####################################################################################################### --- #################################################################################################################### - --- use it directly only for dedugging purposes, otherwise use keybind widget --- setting_id [string] - keybind identifyer for certain mod --- function_name [string] - name of some mod.function which will be called when keybind is pressed --- keys [table] = {"primary_key", "2nd_key" [optional], "3rd_key" [optional], "4th_key" [optional]} --- 2, 3, 4 keys can contain words "ctrl", "alt", "shift" (lowercase) -VMFMod.keybind = function (self, setting_id, function_name, keys) - - if keys[1] then - - local mod_keybinds = _raw_keybinds[self:get_name()] or {} - - mod_keybinds[setting_id] = {function_name, keys} - - _raw_keybinds[self:get_name()] = mod_keybinds - else - - local mod_keybinds = _raw_keybinds[self:get_name()] - - if mod_keybinds and mod_keybinds[setting_id] then - mod_keybinds[setting_id] = nil - end - end - - if vmf.keybind_input_service then - apply_keybinds() - end -end - --- #################################################################################################################### --- ##### VMF internal functions and variables ######################################################################### --- #################################################################################################################### - -vmf.initialize_keybinds = function() - Managers.input:create_input_service("VMFMods", "VMFModsKeyMap") - Managers.input:map_device_to_service("VMFMods", "keyboard") - Managers.input:map_device_to_service("VMFMods", "mouse") - - vmf.keybind_input_service = Managers.input:get_service("VMFMods") - - apply_keybinds() -end - -vmf.check_pressed_keybinds = function() - - local input_service = vmf.keybind_input_service - if input_service then - - -- don't check for the pressed keybindings until player will release already pressed keybind - if _activated_pressed_key then - if input_service:get(_activated_pressed_key) then - return - else - _activated_pressed_key = nil - end - end - - local key_has_active_keybind = false - local input_ctrl = input_service:get("ctrl") - local input_shift = input_service:get("shift") - local input_alt = input_service:get("alt") - - for key, key_bindings in pairs(_optimized_keybinds) do - if input_service:get(key) then - - for _, binding_info in ipairs(key_bindings) do - if (not binding_info[3] and not input_ctrl or binding_info[3] and input_ctrl) and - (not binding_info[4] and not input_alt or binding_info[4] and input_alt) and - (not binding_info[5] and not input_shift or binding_info[5] and input_shift) then - - local mod = get_mod(binding_info[1]) - - if binding_info[2] == "toggle_mod_state" and not mod:get_internal_data("is_mutator") then - - vmf.mod_state_changed(mod:get_name(), not mod:is_enabled()) - - key_has_active_keybind = true - _activated_pressed_key = key - - elseif mod:is_enabled() then - - local action_exists, action_function = pcall(function() return mod[binding_info[2]] end) - if action_exists then - local error_prefix = "(keybindings) " .. tostring(binding_info[2]) - vmf.xpcall_no_return_values(mod, error_prefix, action_function) - else - mod:error("(keybindings): function '%s' wasn't found.", tostring(binding_info[2])) - end - - key_has_active_keybind = true - _activated_pressed_key = key - end - end - end - - -- return here because some other mods can have the same keybind which also need to be executed - if key_has_active_keybind then - return - end - end - end - end -end - -vmf.delete_keybinds = function() - VMFModsKeyMap = {} -end - -local keyboard_buton_name = Keyboard.button_name -local mouse_buton_name = Mouse.button_name - -local _keys = { - keyboard = { - [8] = {"Backspace", "backspace", keyboard_buton_name(8)}, - [9] = {"Tab", "tab", keyboard_buton_name(9)}, - [13] = {"Enter", "enter", keyboard_buton_name(13)}, - [20] = {"Caps Lock", "caps lock", keyboard_buton_name(20)}, - [32] = {"Space", "space", keyboard_buton_name(32)}, - [33] = {"Page Up", "page up", keyboard_buton_name(33)}, - [34] = {"Page Down", "page down", keyboard_buton_name(34)}, - [35] = {"End", "end", keyboard_buton_name(35)}, - [36] = {"Home", "home", keyboard_buton_name(36)}, - [37] = {"Left", "left", keyboard_buton_name(37)}, - [38] = {"Up", "up", keyboard_buton_name(38)}, - [39] = {"Right", "right", keyboard_buton_name(39)}, - [40] = {"Down", "down", keyboard_buton_name(40)}, - [45] = {"Insert", "insert", keyboard_buton_name(45)}, - [46] = {"Delete", "delete", keyboard_buton_name(46)}, - [48] = {"0", "0", keyboard_buton_name(48)}, - [49] = {"1", "1", keyboard_buton_name(49)}, - [50] = {"2", "2", keyboard_buton_name(50)}, - [51] = {"3", "3", keyboard_buton_name(51)}, - [52] = {"4", "4", keyboard_buton_name(52)}, - [53] = {"5", "5", keyboard_buton_name(53)}, - [54] = {"6", "6", keyboard_buton_name(54)}, - [55] = {"7", "7", keyboard_buton_name(55)}, - [56] = {"8", "8", keyboard_buton_name(56)}, - [57] = {"9", "9", keyboard_buton_name(57)}, - [65] = {"A", "a", keyboard_buton_name(65)}, - [66] = {"B", "b", keyboard_buton_name(66)}, - [67] = {"C", "c", keyboard_buton_name(67)}, - [68] = {"D", "d", keyboard_buton_name(68)}, - [69] = {"E", "e", keyboard_buton_name(69)}, - [70] = {"F", "f", keyboard_buton_name(70)}, - [71] = {"G", "g", keyboard_buton_name(71)}, - [72] = {"H", "h", keyboard_buton_name(72)}, - [73] = {"I", "i", keyboard_buton_name(73)}, - [74] = {"J", "j", keyboard_buton_name(74)}, - [75] = {"K", "k", keyboard_buton_name(75)}, - [76] = {"L", "l", keyboard_buton_name(76)}, - [77] = {"M", "m", keyboard_buton_name(77)}, - [78] = {"N", "n", keyboard_buton_name(78)}, - [79] = {"O", "o", keyboard_buton_name(79)}, - [80] = {"P", "p", keyboard_buton_name(80)}, - [81] = {"Q", "q", keyboard_buton_name(81)}, - [82] = {"R", "r", keyboard_buton_name(82)}, - [83] = {"S", "s", keyboard_buton_name(83)}, - [84] = {"T", "t", keyboard_buton_name(84)}, - [85] = {"U", "u", keyboard_buton_name(85)}, - [86] = {"V", "v", keyboard_buton_name(86)}, - [87] = {"W", "w", keyboard_buton_name(87)}, - [88] = {"X", "x", keyboard_buton_name(88)}, - [89] = {"Y", "y", keyboard_buton_name(89)}, - [90] = {"Z", "z", keyboard_buton_name(90)}, - [91] = {"Win", "win", keyboard_buton_name(91)}, - [92] = {"RWin", "right win", keyboard_buton_name(92)}, - [96] = {"Num 0", "numpad 0", keyboard_buton_name(96)}, - [97] = {"Num 1", "numpad 1", keyboard_buton_name(97)}, - [98] = {"Num 2", "numpad 2", keyboard_buton_name(98)}, - [99] = {"Num 3", "numpad 3", keyboard_buton_name(99)}, - [100] = {"Num 4", "numpad 4", keyboard_buton_name(100)}, - [101] = {"Num 5", "numpad 5", keyboard_buton_name(101)}, - [102] = {"Num 6", "numpad 6", keyboard_buton_name(102)}, - [103] = {"Num 7", "numpad 7", keyboard_buton_name(103)}, - [104] = {"Num 8", "numpad 8", keyboard_buton_name(104)}, - [105] = {"Num 9", "numpad 9", keyboard_buton_name(105)}, - [106] = {"Num *", "numpad *", keyboard_buton_name(106)}, - [107] = {"Num +", "numpad +", keyboard_buton_name(107)}, - [109] = {"Num -", "numpad -", keyboard_buton_name(109)}, - [110] = {"Num .", "numpad .", keyboard_buton_name(110)}, - [111] = {"Num /", "numpad /", keyboard_buton_name(111)}, - [112] = {"F1", "f1", keyboard_buton_name(112)}, - [113] = {"F2", "f2", keyboard_buton_name(113)}, - [114] = {"F3", "f3", keyboard_buton_name(114)}, - [115] = {"F4", "f4", keyboard_buton_name(115)}, - [116] = {"F5", "f5", keyboard_buton_name(116)}, - [117] = {"F6", "f6", keyboard_buton_name(117)}, - [118] = {"F7", "f7", keyboard_buton_name(118)}, - [119] = {"F8", "f8", keyboard_buton_name(119)}, - [120] = {"F9", "f9", keyboard_buton_name(120)}, - [121] = {"F10", "f10", keyboard_buton_name(121)}, - [122] = {"F11", "f11", keyboard_buton_name(122)}, - [123] = {"F12", "f12", keyboard_buton_name(123)}, - [144] = {"Num Lock", "num lock", keyboard_buton_name(144)}, - [145] = {"Scroll Lock", "scroll lock", keyboard_buton_name(145)}, - [166] = {"Browser Back", "browser back", keyboard_buton_name(166)}, - [167] = {"Browser Forward", "browser forward", keyboard_buton_name(167)}, - [168] = {"Browser Refresh", "browser refresh", keyboard_buton_name(168)}, - [169] = {"Browser Stop", "browser stop", keyboard_buton_name(169)}, - [170] = {"Browser Search", "browser search", keyboard_buton_name(170)}, - [171] = {"Browser Favorites", "browser favorites", keyboard_buton_name(171)}, - [172] = {"Browser Home", "browser home", keyboard_buton_name(172)}, - [173] = {"Volume Mute", "volume mute", keyboard_buton_name(173)}, - [174] = {"Volume Down", "volume down", keyboard_buton_name(174)}, - [175] = {"Volume Up", "volume up", keyboard_buton_name(175)}, - [176] = {"Next Track", "next track", keyboard_buton_name(176)}, - [177] = {"Previous Track", "previous track", keyboard_buton_name(177)}, - [178] = {"Stop", "stop", keyboard_buton_name(178)}, - [179] = {"Play/Pause", "play pause", keyboard_buton_name(179)}, - [180] = {"Mail", "mail", keyboard_buton_name(180)}, - [181] = {"Media", "media", keyboard_buton_name(181)}, - [182] = {"Start Application 1", "start app 1", keyboard_buton_name(182)}, - [183] = {"Start Application 2", "start app 2", keyboard_buton_name(183)}, - [186] = {";", ";", keyboard_buton_name(186)}, - [187] = {"=", "=", keyboard_buton_name(187)}, - [188] = {",", ",", keyboard_buton_name(188)}, - [189] = {"-", "-", keyboard_buton_name(189)}, - [190] = {".", ".", keyboard_buton_name(190)}, - [191] = {"/", "/", keyboard_buton_name(191)}, - [192] = {"`", "`", keyboard_buton_name(192)}, - [219] = {"[", "[", keyboard_buton_name(219)}, - [220] = {"\\", "\\", keyboard_buton_name(220)}, - [221] = {"]", "]", keyboard_buton_name(221)}, - [222] = {"'", "'", keyboard_buton_name(222)}, - --?[226] = {"\", "oem_102 (> <)", keyboard_buton_name(226)}, - [256] = {"Num Enter", "numpad enter", keyboard_buton_name(256)} - }, - mouse = { - [0] = {"Mouse Left", "mouse left", mouse_buton_name(0)}, - [1] = {"Mouse Right", "mouse right", mouse_buton_name(1)}, - [2] = {"Mouse Middle", "mouse middle", mouse_buton_name(2)}, - [3] = {"Mouse Extra 1", "mouse extra 1", mouse_buton_name(3)}, - [4] = {"Mouse Extra 2", "mouse extra 2", mouse_buton_name(4)}, - [10] = {"Mouse Wheel Up", "mouse wheel up", mouse_buton_name(10)}, - [11] = {"Mouse Wheel Down", "mouse wheel down", mouse_buton_name(11)}, - [12] = {"Mouse Wheel Left", "mouse wheel left", mouse_buton_name(12)}, - [13] = {"Mouse Wheel Right", "mouse wheel right", mouse_buton_name(13)} + MOUSE = { + [0] = {"Mouse Left", "mouse left"}, + [1] = {"Mouse Right", "mouse right"}, + [2] = {"Mouse Middle", "mouse middle"}, + [3] = {"Mouse Extra 1", "mouse extra 1"}, + [4] = {"Mouse Extra 2", "mouse extra 2"}, + [10] = {"Mouse Wheel Up", "mouse wheel up"}, + [11] = {"Mouse Wheel Down", "mouse wheel down"}, + [12] = {"Mouse Wheel Left", "mouse wheel left"}, + [13] = {"Mouse Wheel Right", "mouse wheel right"} },--[[ -- will work on this if it will be needed - gamepad = { - [0] = {"", "d_up", gamepad_buton_name(0)}, - [1] = {"", "d_down", gamepad_buton_name(1)}, - [2] = {"", "d_left", gamepad_buton_name(2)}, - [3] = {"", "d_right", gamepad_buton_name(3)}, - [4] = {"", "start", gamepad_buton_name(4)}, - [5] = {"", "back", gamepad_buton_name(5)}, - [6] = {"", "left_thumb", gamepad_buton_name(6)}, - [7] = {"", "right_thumb", gamepad_buton_name(7)}, - [8] = {"", "left_shoulder", gamepad_buton_name(8)}, - [9] = {"", "right_shoulder", gamepad_buton_name(9)}, - [10] = {"", "left_trigger", gamepad_buton_name(10)}, - [11] = {"", "right_trigger", gamepad_buton_name(11)}, - [12] = {"", "a", gamepad_buton_name(12)}, - [13] = {"", "b", gamepad_buton_name(13)}, - [14] = {"", "x", gamepad_buton_name(14)}, - [15] = {"", "y", gamepad_buton_name(15)}, + GAMEPAD = { + [0] = {"", "d_up"}, + [1] = {"", "d_down"}, + [2] = {"", "d_left"}, + [3] = {"", "d_right"}, + [4] = {"", "start"}, + [5] = {"", "back"}, + [6] = {"", "left_thumb"}, + [7] = {"", "right_thumb"}, + [8] = {"", "left_shoulder"}, + [9] = {"", "right_shoulder"}, + [10] = {"", "left_trigger"}, + [11] = {"", "right_trigger"}, + [12] = {"", "a"}, + [13] = {"", "b"}, + [14] = {"", "x"}, + [15] = {"", "y"}, }]] } -local _readable_key_names = {} +local OTHER_KEYS = { + -- modifier keys + ["shift"] = {160, "Shift", "KEYBOARD", 161}, + ["ctrl"] = {162, "Ctrl", "KEYBOARD", 163}, + ["alt"] = {164, "Alt", "KEYBOARD", 165}, + -- hack for 'vmf.build_keybind_string' function + ["no_button"] = {-1, ""} +} + +local KEYS_INFO = {} +-- Populate KEYS_INFO: +for input_device_name, input_device_keys in pairs(PRIMARY_BINDABLE_KEYS) do + for key_index, key_info in pairs(input_device_keys) do + KEYS_INFO[key_info[2]] = {key_index, key_info[1], input_device_name} + end +end +for key_id, key_data in pairs(OTHER_KEYS) do + KEYS_INFO[key_id] = key_data +end + +-- Can't use 'Device.released' because it will break keybinds if button is released when game window is not active. +local CHECK_INPUT_FUNCTIONS = { + KEYBOARD = { + PRESSED = function(key_id) return Keyboard.pressed(KEYS_INFO[key_id][1]) end, + RELEASED = function(key_id) return Keyboard.button(KEYS_INFO[key_id][1]) == 0 end + }, + MOUSE = { + PRESSED = function(key_id) return Mouse.pressed(KEYS_INFO[key_id][1]) end, + RELEASED = function(key_id) return Mouse.button(KEYS_INFO[key_id][1]) == 0 end + } +} + +local _raw_keybinds_data = {} +local _keybinds = {} +local _pressed_key + +-- ##################################################################################################################### +-- ##### Local functions ############################################################################################### +-- ##################################################################################################################### + +local function is_vmf_input_service_active() + local input_service = Managers.input:get_service("VMF") + return input_service and not input_service:is_blocked() +end -function vmf.get_key_name(device, key_index) - local key_info = _keys[device][key_index] +-- Executes function for 'function_call' keybinds. +local function call_function(mod, function_name, keybind_is_pressed) + if type(mod[function_name]) == "function" then + local error_prefix = string.format("(keybindings) function_call 'mod.%s'", function_name) + vmf.xpcall_no_return_values(mod, error_prefix, mod[function_name], keybind_is_pressed) + else + mod:error("(keybindings) function_call 'mod.%s': function was not found.", tostring(function_name)) + end +end + + +-- If check of keybind's conditions is successful, performs keybind's action and returns 'true'. +local function perform_keybind_action(data, is_pressed) + local can_perform_action = is_vmf_input_service_active() or data.global or data.release_action + + if data.type == "mod_toggle" and can_perform_action and not data.mod:get_internal_data("is_mutator") then + vmf.mod_state_changed(data.mod:get_name(), not data.mod:is_enabled()) + return true + elseif data.type == "function_call" and can_perform_action and data.mod:is_enabled() then + 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) + return true + end +end + +-- ##################################################################################################################### +-- ##### VMF internal functions and variables ########################################################################## +-- ##################################################################################################################### + +-- Checks for pressed and released keybinds, performs keybind actions. +-- * Checks for both right and left key modifiers (ctrl, alt, shift). +-- * If some keybind is pressed, won't check for other keybinds until this keybing is released. +-- * If several mods bound the same keys, keybind action will be performed for all of them, when keybind is pressed. +-- * Keybind is considered released, when its primary key is released. +function vmf.check_keybinds() + local ctrl_pressed = (Keyboard.button(KEYS_INFO["ctrl"][1]) + Keyboard.button(KEYS_INFO["ctrl"][4])) > 0 + local alt_pressed = (Keyboard.button(KEYS_INFO["alt"][1]) + Keyboard.button(KEYS_INFO["alt"][4])) > 0 + local shift_pressed = (Keyboard.button(KEYS_INFO["shift"][1]) + Keyboard.button(KEYS_INFO["shift"][4])) > 0 + + if not _pressed_key then + for primary_key, keybinds_data in pairs(_keybinds) do + if keybinds_data.check_pressed(primary_key) then + for _, keybind_data in ipairs(keybinds_data) do + if (not keybind_data.ctrl and not ctrl_pressed or keybind_data.ctrl and ctrl_pressed) and + (not keybind_data.alt and not alt_pressed or keybind_data.alt and alt_pressed) and + (not keybind_data.shift and not shift_pressed or keybind_data.shift and shift_pressed) + then + if perform_keybind_action(keybind_data, true) then + if keybind_data.trigger == "held" then + keybind_data.release_action = true + end + _pressed_key = primary_key + end + end + end + if _pressed_key then + break + end + end + end + end + + if _pressed_key then + if _keybinds[_pressed_key].check_released(_pressed_key) then + for _, keybind_data in ipairs(_keybinds[_pressed_key]) do + if keybind_data.release_action then + perform_keybind_action(keybind_data, false) + keybind_data.release_action = nil + end + end + _pressed_key = nil + end + end +end + + +-- Converts managable (raw) table of keybinds data to the table designed for the function checking for pressed and +-- released keybinds. After initial call requires to be called every time some keybind is added/removed. +function vmf.generate_keybinds() + _keybinds = {} + + for mod, mod_keybinds in pairs(_raw_keybinds_data) do + for _, raw_keybind_data in pairs(mod_keybinds) do + + local keys = raw_keybind_data[4] + local primary_key = keys[1] + local modifier_keys = {} + for i = 2, #keys do + modifier_keys[keys[i]] = true + end + + local keybind_data = { + mod = mod, + global = raw_keybind_data[1], + trigger = raw_keybind_data[2], + type = raw_keybind_data[3], + ctrl = modifier_keys["ctrl"], + alt = modifier_keys["alt"], + shift = modifier_keys["shift"], + + function_name = raw_keybind_data[5], + view_name = raw_keybind_data[6] + } + + _keybinds[primary_key] = _keybinds[primary_key] or { + check_pressed = CHECK_INPUT_FUNCTIONS[KEYS_INFO[primary_key][3]].PRESSED, + check_released = CHECK_INPUT_FUNCTIONS[KEYS_INFO[primary_key][3]].RELEASED + } + table.insert(_keybinds[primary_key], keybind_data) + end + end +end + + +-- Adds/removes keybinds. +function vmf.add_mod_keybind(mod, setting_id, global, trigger, type, keys, function_name, view_name) + if #keys > 0 then + _raw_keybinds_data[mod] = _raw_keybinds_data[mod] or {} + _raw_keybinds_data[mod][setting_id] = {global, trigger, type, keys, function_name, view_name} + elseif _raw_keybinds_data[mod] and _raw_keybinds_data[mod][setting_id] then + _raw_keybinds_data[mod][setting_id] = nil + end + + -- Keybind is changed from Mod Options. + if vmf.all_mods_were_loaded then + vmf.generate_keybinds() + end +end + + +-- Creates VMF input service. It is required to know when non-global keybinds can be triggered. +-- (Called every time a level is loaded, or on mods reload) +function vmf.create_keybinds_input_service() + -- VMF input has to be created only during the actual game + if Managers.state.game_mode and not Managers.input:get_service("VMF") then + rawset(_G, "EmptyKeyMap", {win32 = {}, xb1 = {}}) + Managers.input:create_input_service("VMF", "EmptyKeyMap") + rawset(_G, "EmptyKeyMap", nil) + + -- Synchronize state of VMF input service with Player input service + local is_blocked = Managers.input:get_service("Player"):is_blocked() + Managers.input:get_service("VMF"):set_blocked(is_blocked) + end +end + + +-- Converts key_index to readable key_id, which is used by VMF to idenify keys. +-- (Used for capturing keybinds) +function vmf.get_key_id(device, key_index) + local key_info = PRIMARY_BINDABLE_KEYS[device][key_index] return key_info and key_info[2] end -function vmf.get_readable_key_name(key_name) - return _readable_key_names[key_name] +-- Simply tells if key with key_id can be binded as primary key. +-- (Used for verifying keybind widgets) +function vmf.can_bind_as_primary_key(key_id) + return KEYS_INFO[key_id] and not OTHER_KEYS[key_id] end +-- Builds string with readable keys' names to look like "Primary Key + Ctrl + Alt + Shift". +-- (Used in keybind widget) function vmf.build_keybind_string(keys) - local keybind_string = "" - for i, key_name in ipairs(keys) do - if i == 1 then - keybind_string = keybind_string .. _readable_key_names[key_name] - else - keybind_string = keybind_string .. " + " .. _readable_key_names[key_name] - end - end - return keybind_string -end --- #################################################################################################################### --- ##### Script ####################################################################################################### --- #################################################################################################################### - -for _, controller_keys in pairs(_keys) do - for _, key_info in pairs(controller_keys) do - _readable_key_names[key_info[2]] = key_info[1] + local readable_key_names = {} + for _, key_id in ipairs(keys) do + table.insert(readable_key_names, KEYS_INFO[key_id][2]) end + return table.concat(readable_key_names, " + ") end -_readable_key_names["ctrl"] = "Ctrl" -_readable_key_names["alt"] = "Alt" -_readable_key_names["shift"] = "Shift" -_readable_key_names["no_button"] = "" -- hack for build_keybind_string function +-- ##################################################################################################################### +-- ##### Script ######################################################################################################## +-- ##################################################################################################################### -for _, key_info in pairs(_keys.keyboard) do - VMFModsKeyMap.win32[key_info[2]] = {"keyboard", key_info[3], "held"} -end - -for i = 0, 4 do - local key_info = _keys.mouse[i] - VMFModsKeyMap.win32[key_info[2]] = {"mouse", key_info[3], "held"} -end - -for i = 10, 13 do - local key_info = _keys.mouse[i] - VMFModsKeyMap.win32[key_info[2]] = {"mouse", key_info[3], "pressed"} -end \ No newline at end of file +-- In case mods reloading was performed right at the moment of entering 'StateInGame'. +vmf.create_keybinds_input_service() \ No newline at end of file diff --git a/vmf/scripts/mods/vmf/modules/core/options.lua b/vmf/scripts/mods/vmf/modules/core/options.lua index ef255bc..f3cf266 100644 --- a/vmf/scripts/mods/vmf/modules/core/options.lua +++ b/vmf/scripts/mods/vmf/modules/core/options.lua @@ -301,7 +301,7 @@ local allowed_keybind_types = { view_toggle = true, mod_toggle = true } -local allowed_special_keys = { +local allowed_modifier_keys = { ctrl = true, alt = true, shift = true @@ -339,12 +339,12 @@ local function validate_keybind_data(data) vmf.throw_error("[widget \"%s\" (keybind)]: table stored in 'default_value' field can't exceed 4 elements", data.setting_id) end - if default_value[1] and (not vmf.get_readable_key_name(default_value[1]) or allowed_special_keys[default_value[1]]) then + if default_value[1] and not vmf.can_bind_as_primary_key(default_value[1]) then vmf.throw_error("[widget \"%s\" (keybind)]: 'default_value[1]' must be a valid key name", data.setting_id) end - if default_value[2] and not allowed_special_keys[default_value[2]] or - default_value[3] and not allowed_special_keys[default_value[3]] or - default_value[4] and not allowed_special_keys[default_value[4]] + if default_value[2] and not allowed_modifier_keys[default_value[2]] or + default_value[3] and not allowed_modifier_keys[default_value[3]] or + default_value[4] and not allowed_modifier_keys[default_value[4]] then vmf.throw_error("[widget \"%s\" (keybind)]: 'default_value [2], [3] and [4]' can be only strings: \"ctrl\", " .. "\"alt\" and \"shift\" (in no particular order)", data.setting_id) @@ -542,7 +542,8 @@ local function initialize_default_settings_and_keybinds(mod, initialized_widgets mod:set(data.setting_id, data.default_value) end if data.type == "keybind" then - mod:keybind(data.setting_id, data.function_name, mod:get(data.setting_id)) + vmf.add_mod_keybind(mod, data.setting_id, data.keybind_global, data.keybind_trigger, data.keybind_type, + mod:get(data.setting_id), data.function_name, data.view_name) end end end diff --git a/vmf/scripts/mods/vmf/modules/gui/custom_menus.lua b/vmf/scripts/mods/vmf/modules/gui/custom_menus.lua index a8afbb4..1e46dde 100644 --- a/vmf/scripts/mods/vmf/modules/gui/custom_menus.lua +++ b/vmf/scripts/mods/vmf/modules/gui/custom_menus.lua @@ -122,11 +122,11 @@ vmf.check_custom_menus_close_keybinds = function() local close_keybind = get_mod(mod_name):get(hotkey_name) -- vmf keybinds input service - local input_service = Managers.input:get_service("VMFMods") + 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, "VMFMods") + Managers.input:device_unblock_service("keyboard", 1, "VMF") end if opening_keybind_is_pressed and not input_service:get(close_keybind[1]) then @@ -149,7 +149,7 @@ vmf.check_custom_menus_close_keybinds = function() end if original_is_blocked then - Managers.input:device_block_service("keyboard", 1, "VMFMods") + Managers.input:device_block_service("keyboard", 1, "VMF") end if close_menu then diff --git a/vmf/scripts/mods/vmf/modules/legacy/options.lua b/vmf/scripts/mods/vmf/modules/legacy/options.lua index c38e13a..f99ecb6 100644 --- a/vmf/scripts/mods/vmf/modules/legacy/options.lua +++ b/vmf/scripts/mods/vmf/modules/legacy/options.lua @@ -94,9 +94,25 @@ vmf.initialize_mod_options_legacy = function (mod, widgets_definition) end if current_widget.widget_type == "keybind" then + new_widget_definition.keybind_trigger = "pressed" + if current_widget.action == "toggle_mod_state" then + new_widget_definition.keybind_type = "mod_toggle" + new_widget_definition.function_name = nil + else + new_widget_definition.keybind_type = "function_call" + end + local keybind = mod:get(current_widget.setting_name) if current_widget.action then - mod:keybind(current_widget.setting_name, current_widget.action, keybind) + vmf.add_mod_keybind( + mod, + new_widget_definition.setting_id, + nil, + new_widget_definition.keybind_trigger, + new_widget_definition.keybind_type, + keybind, + new_widget_definition.function_name + ) end end 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 329d70c..b86a331 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 @@ -2638,7 +2638,12 @@ local function create_keybind_widget(widget_definition, scenegraph_id) setting_id = widget_definition.setting_id, widget_type = widget_definition.type, - action = widget_definition.function_name, + keybind_global = widget_definition.keybind_global, + keybind_trigger = widget_definition.keybind_trigger, + keybind_type = widget_definition.keybind_type, + function_name = widget_definition.function_name, + view_name = widget_definition.view_name, + keybind_text = widget_definition.keybind_text, default_value = widget_definition.default_value, parent_widget_number = widget_definition.parent_index, @@ -3286,35 +3291,45 @@ VMFOptionsView.callback_change_setting_keybind_state = function (self, widget_co end end +local function set_new_keybind(keybind_widget_content) + vmf.add_mod_keybind( + get_mod(keybind_widget_content.mod_name), + keybind_widget_content.setting_id, + keybind_widget_content.keybind_global, + keybind_widget_content.keybind_trigger, + keybind_widget_content.keybind_type, + keybind_widget_content.keys, + keybind_widget_content.function_name, + keybind_widget_content.view_name + ) +end VMFOptionsView.callback_setting_keybind = function (self, widget_content) - - if not widget_content.first_pressed_button_name then + if not widget_content.first_pressed_button_id then if Keyboard.any_pressed() then - widget_content.first_pressed_button_name = vmf.get_key_name("keyboard", Keyboard.any_pressed()) + widget_content.first_pressed_button_id = vmf.get_key_id("KEYBOARD", Keyboard.any_pressed()) widget_content.first_pressed_button_index = Keyboard.any_pressed() widget_content.first_pressed_button_type = "keyboard" elseif Mouse.any_pressed() then - widget_content.first_pressed_button_name = vmf.get_key_name("mouse", Mouse.any_pressed()) + widget_content.first_pressed_button_id = vmf.get_key_id("MOUSE", Mouse.any_pressed()) widget_content.first_pressed_button_index = Mouse.any_pressed() widget_content.first_pressed_button_type = "mouse" end end local pressed_buttons = {} - - if widget_content.first_pressed_button_name then - table.insert(pressed_buttons, widget_content.first_pressed_button_name) + if widget_content.first_pressed_button_id then + table.insert(pressed_buttons, widget_content.first_pressed_button_id) else table.insert(pressed_buttons, "no_button") end - if Keyboard.button(Keyboard.button_index("left ctrl")) == 1 then + if Keyboard.button(Keyboard.button_index("left ctrl")) + Keyboard.button(Keyboard.button_index("right ctrl")) > 0 then table.insert(pressed_buttons, "ctrl") end - if Keyboard.button(Keyboard.button_index("left alt")) == 1 then + if Keyboard.button(Keyboard.button_index("left alt")) + Keyboard.button(Keyboard.button_index("right alt")) > 0 then table.insert(pressed_buttons, "alt") end - if Keyboard.button(Keyboard.button_index("left shift")) == 1 then + if Keyboard.button(Keyboard.button_index("left shift")) + Keyboard.button(Keyboard.button_index("right shift")) > 0 then table.insert(pressed_buttons, "shift") end @@ -3323,23 +3338,24 @@ VMFOptionsView.callback_setting_keybind = function (self, widget_content) widget_content.keybind_text = preview_string ~= "" and preview_string or "_" widget_content.keys = pressed_buttons - if widget_content.first_pressed_button_name then + if widget_content.first_pressed_button_id then if widget_content.first_pressed_button_type == "keyboard" and Keyboard.released(widget_content.first_pressed_button_index) or widget_content.first_pressed_button_type == "mouse" and Mouse.released(widget_content.first_pressed_button_index) then - widget_content.first_pressed_button_name = nil + widget_content.first_pressed_button_id = nil widget_content.first_pressed_button_index = nil widget_content.first_pressed_button_type = nil - get_mod(widget_content.mod_name):keybind(widget_content.setting_id, widget_content.action, widget_content.keys) + set_new_keybind(widget_content) self:callback_change_setting_keybind_state(widget_content) return true end elseif Keyboard.released(Keyboard.button_index("esc")) then widget_content.keybind_text = "" + widget_content.keys = {} - get_mod(widget_content.mod_name):keybind(widget_content.setting_id, widget_content.action, widget_content.keys) + set_new_keybind(widget_content) self:callback_change_setting_keybind_state(widget_content) return true diff --git a/vmf/scripts/mods/vmf/modules/vmf_options.lua b/vmf/scripts/mods/vmf/modules/vmf_options.lua index c166f69..fcca4df 100644 --- a/vmf/scripts/mods/vmf/modules/vmf_options.lua +++ b/vmf/scripts/mods/vmf/modules/vmf_options.lua @@ -9,8 +9,8 @@ vmf_mod_data.options = { type = "keybind", default_value = {"f4"}, keybind_trigger = "pressed", - keybind_type = "function_call", - function_name = "open_vmf_options" + keybind_type = "view_toggle", + view_name = "vmf_options_view" }, { setting_id = "vmf_options_scrolling_speed", @@ -38,6 +38,7 @@ vmf_mod_data.options = { setting_id = "toggle_developer_console", type = "keybind", default_value = {}, + keybind_global = true, keybind_trigger = "pressed", keybind_type = "function_call", function_name = "toggle_developer_console" diff --git a/vmf/scripts/mods/vmf/vmf_loader.lua b/vmf/scripts/mods/vmf/vmf_loader.lua index aaf1a82..a0edf6a 100644 --- a/vmf/scripts/mods/vmf/vmf_loader.lua +++ b/vmf/scripts/mods/vmf/vmf_loader.lua @@ -56,14 +56,14 @@ end function vmf_mod_object:update(dt) vmf.mods_update_event(dt) - vmf.check_pressed_keybinds() + vmf.check_keybinds() vmf.check_custom_menus_close_keybinds(dt) vmf.execute_queued_chat_command() if VT1 then vmf.check_mutators_state() end if not vmf.all_mods_were_loaded and Managers.mod._state == "done" then - vmf.initialize_keybinds() + vmf.generate_keybinds() vmf.initialize_vmf_options_view() vmf.create_network_dictionary() vmf.ping_vmf_users() @@ -90,7 +90,6 @@ function vmf_mod_object:on_reload() vmf.disable_mods_options_button() vmf.close_opened_custom_menus() if VT1 then vmf.reset_map_view() end - vmf.delete_keybinds() vmf.mods_unload_event(false) vmf.hooks_unload() vmf.reset_guis() @@ -104,7 +103,7 @@ function vmf_mod_object:on_game_state_changed(status, state) vmf.apply_delayed_hooks(status, state) if status == "enter" and state == "StateIngame" then - vmf.initialize_keybinds() + vmf.create_keybinds_input_service() end end From 0b7af2d48b8f75f0ff2f18fb828ec7f58216bd4c Mon Sep 17 00:00:00 2001 From: Azumgi <4zumgi@gmail.com> Date: Thu, 1 Nov 2018 10:29:59 +0300 Subject: [PATCH 05/19] [Misc] Add `vmf.catch_errors` --- vmf/scripts/mods/vmf/modules/core/misc.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/vmf/scripts/mods/vmf/modules/core/misc.lua b/vmf/scripts/mods/vmf/modules/core/misc.lua index a6107c7..7307926 100644 --- a/vmf/scripts/mods/vmf/modules/core/misc.lua +++ b/vmf/scripts/mods/vmf/modules/core/misc.lua @@ -19,6 +19,16 @@ function vmf.check_wrong_argument_type(mod, vmf_function_name, argument_name, ar return true end + function vmf.throw_error(error_message, ...) error(string.format(error_message, ...), 0) +end + + +function vmf.catch_errors(mod, error_format, exec_function, ...) + local success, error_message = pcall(exec_function, ...) + if not success then + mod:error(string.format(error_format, error_message)) + return true + end end \ No newline at end of file From 199a174a57c412ea5201fa9e6ee9b16f9c236345 Mon Sep 17 00:00:00 2001 From: Azumgi <4zumgi@gmail.com> Date: Thu, 1 Nov 2018 10:45:00 +0300 Subject: [PATCH 06/19] [Custom Views] Complete overhaul (basic) --- .../mods/vmf/modules/core/keybindings.lua | 2 +- .../mods/vmf/modules/gui/custom_menus.lua | 203 ---------------- .../mods/vmf/modules/gui/custom_views.lua | 218 ++++++++++++++++++ .../modules/ui/options/vmf_options_view.lua | 28 +-- vmf/scripts/mods/vmf/vmf_loader.lua | 5 +- 5 files changed, 227 insertions(+), 229 deletions(-) delete mode 100644 vmf/scripts/mods/vmf/modules/gui/custom_menus.lua create mode 100644 vmf/scripts/mods/vmf/modules/gui/custom_views.lua 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 From f3443dc32e1c9bb9eb14d12a3722aa1c7a117e9b Mon Sep 17 00:00:00 2001 From: Azumgi <4zumgi@gmail.com> Date: Thu, 1 Nov 2018 12:08:58 +0300 Subject: [PATCH 07/19] [Custom Views] More checks for transition handling --- .../mods/vmf/modules/core/keybindings.lua | 2 +- .../mods/vmf/modules/gui/custom_views.lua | 28 +++++++++++-------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/vmf/scripts/mods/vmf/modules/core/keybindings.lua b/vmf/scripts/mods/vmf/modules/core/keybindings.lua index ed3ffcd..9212bc7 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.keybind_toggle_view(data.view_name, can_perform_action) + vmf.keybind_toggle_view(data.view_name, can_perform_action, is_pressed) return true end end diff --git a/vmf/scripts/mods/vmf/modules/gui/custom_views.lua b/vmf/scripts/mods/vmf/modules/gui/custom_views.lua index eb64089..4e0d19c 100644 --- a/vmf/scripts/mods/vmf/modules/gui/custom_views.lua +++ b/vmf/scripts/mods/vmf/modules/gui/custom_views.lua @@ -101,12 +101,13 @@ end -- ##### VMFMod ######################################################################################################## -- ##################################################################################################################### -function VMFMod:handle_transition(transition_name, transition_params, fade) +function VMFMod:handle_transition(transition_name, transition_params, fade, ignore_active_menu) 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.menu_active or ignore_active_menu) and not _ingame_ui.leave_game + and not _ingame_ui.menu_suspended and not _ingame_ui.return_to_title_screen and ( VT1 @@ -178,21 +179,24 @@ function vmf.remove_custom_views() end -function vmf.keybind_toggle_view(view_name, can_be_opened) +function vmf.keybind_toggle_view(view_name, can_be_opened, is_keybind_pressed) --@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 + 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, true) + end + -- Can open views only when keybind is pressed. + elseif can_be_opened and is_keybind_pressed 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, true) end end end From 0e51011f6c1c82a401684c4cadec789770c42ba2 Mon Sep 17 00:00:00 2001 From: Azumgi <4zumgi@gmail.com> Date: Wed, 7 Nov 2018 13:49:30 +0300 Subject: [PATCH 08/19] [Custom Views] Add a lot of checks with feedback --- .../mods/vmf/modules/core/keybindings.lua | 2 +- vmf/scripts/mods/vmf/modules/core/misc.lua | 5 +- .../mods/vmf/modules/gui/custom_views.lua | 216 ++++++++++++++---- .../modules/ui/options/vmf_options_view.lua | 1 + 4 files changed, 181 insertions(+), 43 deletions(-) diff --git a/vmf/scripts/mods/vmf/modules/core/keybindings.lua b/vmf/scripts/mods/vmf/modules/core/keybindings.lua index 9212bc7..17ff342 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.keybind_toggle_view(data.view_name, can_perform_action, is_pressed) + vmf.keybind_toggle_view(data.mod, data.view_name, can_perform_action, is_pressed) return true end end diff --git a/vmf/scripts/mods/vmf/modules/core/misc.lua b/vmf/scripts/mods/vmf/modules/core/misc.lua index 7307926..8a6a95f 100644 --- a/vmf/scripts/mods/vmf/modules/core/misc.lua +++ b/vmf/scripts/mods/vmf/modules/core/misc.lua @@ -25,10 +25,11 @@ function vmf.throw_error(error_message, ...) end -function vmf.catch_errors(mod, error_format, exec_function, ...) +function vmf.catch_errors(mod, error_prefix, additional_error_prefix_info, exec_function, ...) local success, error_message = pcall(exec_function, ...) if not success then - mod:error(string.format(error_format, error_message)) + error_prefix = string.format(error_prefix, additional_error_prefix_info) + mod:error(string.format("%s: %s", error_prefix, error_message)) return true end 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 index 4e0d19c..806cf10 100644 --- a/vmf/scripts/mods/vmf/modules/gui/custom_views.lua +++ b/vmf/scripts/mods/vmf/modules/gui/custom_views.lua @@ -5,20 +5,56 @@ local _ingame_ui = nil local _ingame_ui_transitions = require("scripts/ui/views/ingame_ui_settings").transitions local _views_data = {} +local ERRORS = { + THROWABLE = { + -- inject_view: + view_already_exists = "view with name '%s' already persists in original game.", + transition_already_exists = "transition with name '%s' already persists in original game.", + view_initializing_failed = "was not able to initialize '%s' view: execution of 'init_view_function' failed.", + -- validate_view_data: + view_data_wrong_type = "view data must be a table, not %s.", + view_name_wrong_type = "'view_name' must be a string, not %s.", + view_transitions_wrong_type = "'view_transitions' must be a table, not %s.", + view_settings_wrong_type = "'view_settings' must be a table, not %s.", + transition_wrong_type = "all transitions inside 'view_transitions' must be functions, but '%s' transition is %s.", + transition_name_taken = "transition name '%s' is already used by '%s' mod for '%s' view.", + init_view_function_wrong_type = "'view_settings.init_view_function' must be a function, not %s.", + active_wrong_type = "'view_settings.active' must be a table, not %s.", + active_missing_element = "'view_settings.active' must contain 2 elements: 'inn' and 'ingame'.", + active_element_wrong_name = "the only allowed names for 'view_settings.active' elements are 'inn' and 'ingame'; " .. + "you can't name your element '%s'.", + active_element_wrong_type = "'view_settings.active.%s' must be boolean, not %s.", + blocked_transitions_wrong_type = "'view_settings.blocked_transitions' (optional) must be a table, not %s.", + blocked_transitions_missing_element = "'view_settings.blocked_transitions' must contain 2 table elements: " .. + "'inn' and 'ingame'.", + blocked_transitions_element_wrong_name = "the only allowed names for 'view_settings.active' elements are " .. + "'inn' and 'ingame'; you can't name your element '%s'.", + blocked_transitions_element_wrong_type = "'view_settings.blocked_transitions.%s' must be a table, not %s.", + blocked_transition_invalid = "you can't put transition '%s' into 'view_settings.blocked_transitions.%s', " .. + "because it's not listed in 'view_transitions'.", + blocked_transition_wrong_value = "invalid value for 'view_settings.blocked_transitions.%s.%s'; must be 'true'.", + keybind_transitions_wrong_type = "'view_settings.keybind_transitions' (optional) must be a table, not %s.", + open_view_transition_wrong_type = "'view_settings.keybind_transitions.open_view_transition' (optional) must be " .. + "a string, not %s.", + transition_fade_wrong_type = "'view_settings.keybind_transitions.transition_fade' (optional) must be a boolean, " .. + "not %s.", + }, + REGULAR = { + view_not_registered = "[Custom Views] Opening view with keybind: view '%s' wasn't registered for this mod." + }, + PREFIX = { + register_view_validating = "[Custom Views] (register_view) View data validating '%s'", + register_view_injection = "[Custom Views] (register_view) View injection '%s'", + ingameui_hook_injection = "[Custom Views] View injection '%s'" + } +} + -- ##################################################################################################################### -- ##### 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) +-- @THROWS_ERRORS +local function inject_view(view_name) local view_settings = _views_data[view_name].view_settings local mod = _views_data[view_name].mod @@ -28,11 +64,11 @@ local function inject_elements(view_name) -- Check for collisions. if _ingame_ui.views[view_name] then - -- @TODO: throw error + vmf.throw_error(ERRORS.THROWABLE["view_already_exists"], view_name) end for transition_name, _ in pairs(transitions) do if _ingame_ui_transitions[transition_name] then - -- @TODO: throw error + vmf.throw_error(ERRORS.THROWABLE["transition_already_exists"], transition_name) end end @@ -41,7 +77,7 @@ local function inject_elements(view_name) if success then _ingame_ui.views[view_name] = view else - -- @TODO: throw error + vmf.throw_error(ERRORS.THROWABLE["view_initializing_failed"], view_name) end -- Inject view transitions. @@ -56,7 +92,7 @@ local function inject_elements(view_name) end -local function remove_injected_elements(on_reload) +local function remove_injected_views(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. @@ -73,28 +109,125 @@ local function remove_injected_elements(on_reload) 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 + -- Remove injected transitions. for transition_name, _ in pairs(view_data.view_transitions) do _ingame_ui_transitions[transition_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 --- Throws error. --- Make, so blocked transitions can be only the one from this view, so they won't need further checks +-- @THROWS_ERRORS local function validate_view_data(view_data) + -- Basic checks. + if type(view_data) ~= "table" then + vmf.throw_error(ERRORS.THROWABLE["view_data_wrong_type"], type(view_data)) + end + if type(view_data.view_name) ~= "string" then + vmf.throw_error(ERRORS.THROWABLE["view_name_wrong_type"], type(view_data.view_name)) + end + if type(view_data.view_transitions) ~= "table" then + vmf.throw_error(ERRORS.THROWABLE["view_transitions_wrong_type"], type(view_data.view_transitions)) + end + if type(view_data.view_settings) ~= "table" then + vmf.throw_error(ERRORS.THROWABLE["view_settings_wrong_type"], type(view_data.view_settings)) + end + + -- VIEW TRANSITIONS + + local view_transitions = view_data.view_transitions + for transition_name, transition_function in pairs(view_transitions) do + if type(transition_function) ~= "function" then + vmf.throw_error(ERRORS.THROWABLE["transition_wrong_type"], transition_name, type(transition_function)) + end + for another_view_name, another_view_data in pairs(_views_data) do + for another_transition_name, _ in pairs(another_view_data.view_transitions) do + if transition_name == another_transition_name then + vmf.throw_error(ERRORS.THROWABLE["transition_name_taken"], transition_name, another_view_data.mod:get_name(), + another_view_name) + end + end + end + end + + -- VIEW SETTINGS + + local view_settings = view_data.view_settings + + -- Use default values for optional fields if they are not defined. + view_settings.blocked_transitions = view_settings.blocked_transitions or {inn = {}, ingame = {}} + view_settings.keybind_transitions = view_settings.keybind_transitions or {} + + -- Verify everything. + if type(view_settings.init_view_function) ~= "function" then + vmf.throw_error(ERRORS.THROWABLE["init_view_function_wrong_type"], type(view_settings.init_view_function)) + end + + local active = view_settings.active + if type(active) ~= "table" then + vmf.throw_error(ERRORS.THROWABLE["active_wrong_type"], type(active)) + end + if not active.inn or not active.ingame then + vmf.throw_error(ERRORS.THROWABLE["active_missing_element"]) + end + for level_name, value in pairs(active) do + if level_name ~= "inn" and level_name ~= "ingame" then + vmf.throw_error(ERRORS.THROWABLE["active_element_wrong_name"], level_name) + end + if type(value) ~= "boolean" then + vmf.throw_error(ERRORS.THROWABLE["active_element_wrong_type"], level_name, type(value)) + end + end + + local blocked_transitions = view_settings.blocked_transitions + if type(blocked_transitions) ~= "table" then + vmf.throw_error(ERRORS.THROWABLE["blocked_transitions_wrong_type"], type(blocked_transitions)) + end + if not blocked_transitions.inn or not blocked_transitions.ingame then + vmf.throw_error(ERRORS.THROWABLE["blocked_transitions_missing_element"]) + end + for level_name, level_blocked_transitions in pairs(blocked_transitions) do + if level_name ~= "inn" and level_name ~= "ingame" then + vmf.throw_error(ERRORS.THROWABLE["blocked_transitions_element_wrong_name"], level_name) + end + if type(level_blocked_transitions) ~= "table" then + vmf.throw_error(ERRORS.THROWABLE["blocked_transitions_element_wrong_type"], level_name, + type(level_blocked_transitions)) + end + for transition_name, value in pairs(level_blocked_transitions) do + if not view_transitions[transition_name] then + vmf.throw_error(ERRORS.THROWABLE["blocked_transition_invalid"], transition_name, level_name) + end + if value ~= true then + vmf.throw_error(ERRORS.THROWABLE["blocked_transition_wrong_value"], level_name, transition_name) + end + end + end + + local keybind_transitions = view_settings.keybind_transitions + if type(keybind_transitions) ~= "table" then + vmf.throw_error(ERRORS.THROWABLE["keybind_transitions_wrong_type"], type(keybind_transitions)) + end + if keybind_transitions.open_view_transition and type(keybind_transitions.open_view_transition) ~= "string" then + vmf.throw_error(ERRORS.THROWABLE["open_view_transition_wrong_type"], type(keybind_transitions.open_view_transition)) + end + if keybind_transitions.close_view_transition and type(keybind_transitions.close_view_transition) ~= "string" then + vmf.throw_error(ERRORS.THROWABLE["close_view_transition_wrong_type"], + type(keybind_transitions.close_view_transition)) + end + if keybind_transitions.transition_fade and type(keybind_transitions.transition_fade) ~= "boolean" then + vmf.throw_error(ERRORS.THROWABLE["transition_fade_wrong_type"], type(keybind_transitions.transition_fade)) + end end -- ##################################################################################################################### @@ -130,22 +263,23 @@ end function VMFMod:register_view(view_data) - if vmf.check_wrong_argument_type(self, "register_view", "view_data", view_data, "table") then + -- @TODO: load table from file, check if it's table + view_data = table.clone(view_data) + + local view_name = view_data.view_name + + if vmf.catch_errors(self, ERRORS.PREFIX["register_view_validating"], view_name, validate_view_data, view_data) 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] = { + _views_data[view_name] = { mod = self, - view_settings = table.clone(view_data.view_settings), - view_transitions = table.clone(view_data.view_transitions) + view_settings = view_data.view_settings, + view_transitions = 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 + if vmf.catch_errors(self, ERRORS.PREFIX["register_view_injection"], view_name, inject_view, view_name) then _views_data[view_data.view_name] = nil end end @@ -158,7 +292,7 @@ end 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 + if vmf.catch_errors(self, ERRORS.PREFIX["ingameui_hook_injection"], view_name, inject_view, view_name) then _views_data[view_name] = nil end end @@ -166,8 +300,8 @@ end) vmf:hook_safe(IngameUI, "destroy", function() + remove_injected_views(false) _ingame_ui = nil - remove_injected_elements(false) end) -- ##################################################################################################################### @@ -175,15 +309,17 @@ end) -- ##################################################################################################################### function vmf.remove_custom_views() - remove_injected_elements(true) + remove_injected_views(true) end -function vmf.keybind_toggle_view(view_name, can_be_opened, is_keybind_pressed) - --@TODO: check if there's the custom view at all. If not, show error. - +function vmf.keybind_toggle_view(mod, view_name, can_be_opened, is_keybind_pressed) if _ingame_ui then - local mod = _views_data[view_name].mod + if not _views_data[view_name] or (_views_data[view_name].mod ~= mod) then + mod:error(ERRORS.REGULAR["view_not_registered"], view_name) + return + end + local keybind_transitions = _views_data[view_name].view_settings.keybind_transitions if _ingame_ui.current_view == view_name then if keybind_transitions.close_view_transition then 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 02ab846..acedc4b 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 @@ -4298,6 +4298,7 @@ local view_data = { view_transitions = { vmf_options_view = function (self) self.current_view = "vmf_options_view" + self.menu_active = true end } } From 1de94497f900d134d978f28b519ffb246a6dc925 Mon Sep 17 00:00:00 2001 From: Azumgi <4zumgi@gmail.com> Date: Wed, 7 Nov 2018 16:33:54 +0300 Subject: [PATCH 09/19] [Custom Views] Fix not working 'active' option --- vmf/scripts/mods/vmf/modules/gui/custom_views.lua | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/vmf/scripts/mods/vmf/modules/gui/custom_views.lua b/vmf/scripts/mods/vmf/modules/gui/custom_views.lua index 806cf10..5007a77 100644 --- a/vmf/scripts/mods/vmf/modules/gui/custom_views.lua +++ b/vmf/scripts/mods/vmf/modules/gui/custom_views.lua @@ -53,8 +53,19 @@ local ERRORS = { -- ##### Local functions ############################################################################################### -- ##################################################################################################################### +local function is_view_active_for_current_level(view_name) + local active = _views_data[view_name].view_settings.active + if _ingame_ui.is_in_inn and active.inn or not _ingame_ui.is_in_inn and active.ingame then + return true + end +end + -- @THROWS_ERRORS local function inject_view(view_name) + if not is_view_active_for_current_level(view_name) then + return + end + local view_settings = _views_data[view_name].view_settings local mod = _views_data[view_name].mod @@ -177,7 +188,7 @@ local function validate_view_data(view_data) if type(active) ~= "table" then vmf.throw_error(ERRORS.THROWABLE["active_wrong_type"], type(active)) end - if not active.inn or not active.ingame then + if active.inn == nil or active.ingame == nil then vmf.throw_error(ERRORS.THROWABLE["active_missing_element"]) end for level_name, value in pairs(active) do From ad3d2a8767f51a76a1497ddeed59e7321cdb837b Mon Sep 17 00:00:00 2001 From: Azumgi <4zumgi@gmail.com> Date: Wed, 7 Nov 2018 16:42:52 +0300 Subject: [PATCH 10/19] [Custom Views] Add check if view is active --- .../mods/vmf/modules/gui/custom_views.lua | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/vmf/scripts/mods/vmf/modules/gui/custom_views.lua b/vmf/scripts/mods/vmf/modules/gui/custom_views.lua index 5007a77..72e0c9a 100644 --- a/vmf/scripts/mods/vmf/modules/gui/custom_views.lua +++ b/vmf/scripts/mods/vmf/modules/gui/custom_views.lua @@ -331,19 +331,21 @@ function vmf.keybind_toggle_view(mod, view_name, can_be_opened, is_keybind_press return end - local keybind_transitions = _views_data[view_name].view_settings.keybind_transitions - 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, true) - end - -- Can open views only when keybind is pressed. - elseif can_be_opened and is_keybind_pressed 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, true) + if is_view_active_for_current_level(view_name) then + local keybind_transitions = _views_data[view_name].view_settings.keybind_transitions + 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, true) + end + -- Can open views only when keybind is pressed. + elseif can_be_opened and is_keybind_pressed 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, true) + end end end end From da2cdc669d274291e720e7fff9ef322cbc930257 Mon Sep 17 00:00:00 2001 From: Azumgi <4zumgi@gmail.com> Date: Sat, 10 Nov 2018 15:53:40 +0300 Subject: [PATCH 11/19] [Safe Calls] methods migration and renaming --- .../mods/vmf/modules/core/commands.lua | 2 +- vmf/scripts/mods/vmf/modules/core/events.lua | 2 +- vmf/scripts/mods/vmf/modules/core/hooks.lua | 2 +- .../mods/vmf/modules/core/keybindings.lua | 2 +- vmf/scripts/mods/vmf/modules/core/misc.lua | 15 ------ vmf/scripts/mods/vmf/modules/core/network.lua | 4 +- .../mods/vmf/modules/core/safe_calls.lua | 49 +++++++++++++++---- .../mods/vmf/modules/gui/custom_views.lua | 12 +++-- .../mods/vmf/modules/vmf_mod_manager.lua | 10 ++-- 9 files changed, 58 insertions(+), 40 deletions(-) diff --git a/vmf/scripts/mods/vmf/modules/core/commands.lua b/vmf/scripts/mods/vmf/modules/core/commands.lua index 459e4fa..c3aa42d 100644 --- a/vmf/scripts/mods/vmf/modules/core/commands.lua +++ b/vmf/scripts/mods/vmf/modules/core/commands.lua @@ -154,7 +154,7 @@ function vmf.run_command(command_name, ...) local command_data = _commands[command_name] if command_data then local error_prefix = "(commands) " .. tostring(command_name) - vmf.xpcall_no_return_values(command_data.mod, error_prefix, command_data.exec_function, ...) + vmf.safe_call_nr(command_data.mod, error_prefix, command_data.exec_function, ...) else vmf:error("(commands): command '%s' wasn't found.", command_name) -- Should never see this end diff --git a/vmf/scripts/mods/vmf/modules/core/events.lua b/vmf/scripts/mods/vmf/modules/core/events.lua index 0ba7c2b..329d864 100644 --- a/vmf/scripts/mods/vmf/modules/core/events.lua +++ b/vmf/scripts/mods/vmf/modules/core/events.lua @@ -10,7 +10,7 @@ local _mods_unloading_order = vmf.mods_unloading_order local function run_event(mod, event_name, ...) local event = mod[event_name] if event then - vmf.xpcall_no_return_values(mod, "(event) " .. event_name, event, ...) + vmf.safe_call_nr(mod, "(event) " .. event_name, event, ...) end end diff --git a/vmf/scripts/mods/vmf/modules/core/hooks.lua b/vmf/scripts/mods/vmf/modules/core/hooks.lua index d083bc1..6233d96 100644 --- a/vmf/scripts/mods/vmf/modules/core/hooks.lua +++ b/vmf/scripts/mods/vmf/modules/core/hooks.lua @@ -164,7 +164,7 @@ local function create_specialized_hook(mod, orig, hook_type) elseif hook_type == HOOK_TYPE_SAFE then func = function(...) if hook_data.active then - vmf.xpcall_no_return_values(mod, "(safe_hook)", hook_data.handler, ...) + vmf.safe_call_nr(mod, "(safe_hook)", hook_data.handler, ...) end end end diff --git a/vmf/scripts/mods/vmf/modules/core/keybindings.lua b/vmf/scripts/mods/vmf/modules/core/keybindings.lua index 17ff342..82a7595 100644 --- a/vmf/scripts/mods/vmf/modules/core/keybindings.lua +++ b/vmf/scripts/mods/vmf/modules/core/keybindings.lua @@ -197,7 +197,7 @@ end local function call_function(mod, function_name, keybind_is_pressed) if type(mod[function_name]) == "function" then local error_prefix = string.format("(keybindings) function_call 'mod.%s'", function_name) - vmf.xpcall_no_return_values(mod, error_prefix, mod[function_name], keybind_is_pressed) + vmf.safe_call_nr(mod, error_prefix, mod[function_name], keybind_is_pressed) else mod:error("(keybindings) function_call 'mod.%s': function was not found.", tostring(function_name)) end diff --git a/vmf/scripts/mods/vmf/modules/core/misc.lua b/vmf/scripts/mods/vmf/modules/core/misc.lua index 8a6a95f..3d45c99 100644 --- a/vmf/scripts/mods/vmf/modules/core/misc.lua +++ b/vmf/scripts/mods/vmf/modules/core/misc.lua @@ -17,19 +17,4 @@ function vmf.check_wrong_argument_type(mod, vmf_function_name, argument_name, ar mod:error("(%s): argument '%s' should have the '%s' type, not '%s'", vmf_function_name, argument_name, table.concat(allowed_types, "/"), argument_type) return true -end - - -function vmf.throw_error(error_message, ...) - error(string.format(error_message, ...), 0) -end - - -function vmf.catch_errors(mod, error_prefix, additional_error_prefix_info, exec_function, ...) - local success, error_message = pcall(exec_function, ...) - if not success then - error_prefix = string.format(error_prefix, additional_error_prefix_info) - mod:error(string.format("%s: %s", error_prefix, error_message)) - return true - end end \ No newline at end of file diff --git a/vmf/scripts/mods/vmf/modules/core/network.lua b/vmf/scripts/mods/vmf/modules/core/network.lua index aa3fdd2..a57a7aa 100644 --- a/vmf/scripts/mods/vmf/modules/core/network.lua +++ b/vmf/scripts/mods/vmf/modules/core/network.lua @@ -176,7 +176,7 @@ local function send_rpc_vmf_data_local(mod_name, rpc_name, ...) network_debug("data", "local", nil, mod_name, rpc_name, {...}) local error_prefix = "(local rpc) " .. tostring(rpc_name) - vmf.xpcall_no_return_values(mod, error_prefix, _rpc_callbacks[mod_name][rpc_name], Network.peer_id(), ...) + vmf.safe_call_nr(mod, error_prefix, _rpc_callbacks[mod_name][rpc_name], Network.peer_id(), ...) end end @@ -307,7 +307,7 @@ vmf:hook("ChatManager", "rpc_chat_message", -- can be error in both callback_function() and deserialize_data() local error_prefix = "(network) " .. tostring(rpc_name) - vmf.xpcall_no_return_values( + vmf.safe_call_nr( get_mod(mod_name), error_prefix, function() _rpc_callbacks[mod_name][rpc_name](sender, deserialize_data(rpc_data2)) end diff --git a/vmf/scripts/mods/vmf/modules/core/safe_calls.lua b/vmf/scripts/mods/vmf/modules/core/safe_calls.lua index f07d52e..d2eaf00 100644 --- a/vmf/scripts/mods/vmf/modules/core/safe_calls.lua +++ b/vmf/scripts/mods/vmf/modules/core/safe_calls.lua @@ -17,17 +17,29 @@ local function print_error_callstack(error_message) return error_message end + +local function show_error(mod, error_prefix_data, error_message) + local error_prefix + if type(error_prefix_data) == "table" then + error_prefix = string.format(error_prefix_data[1], error_prefix_data[2], error_prefix_data[3], error_prefix_data[4]) + else + error_prefix = error_prefix_data + end + + mod:error("%s: %s", error_prefix, error_message) +end + -- ##################################################################################################################### -- ##### VMFMod ######################################################################################################## -- ##################################################################################################################### function VMFMod:pcall(...) - return vmf.xpcall(self, "(pcall)", ...) + return vmf.safe_call(self, "(pcall)", ...) end function VMFMod:dofile(file_path) - local _, return_values = pack_pcall(vmf.xpcall_dofile(self, "(dofile)", file_path)) + local _, return_values = pack_pcall(vmf.safe_call_dofile(self, "(dofile)", file_path)) return unpack(return_values, 1, return_values.n) end @@ -35,29 +47,48 @@ end -- ##### VMF internal functions and variables ########################################################################## -- ##################################################################################################################### -function vmf.xpcall(mod, error_prefix, func, ...) +-- Safe Call +function vmf.safe_call(mod, error_prefix_data, func, ...) local success, return_values = pack_pcall(xpcall(func, print_error_callstack, ...)) if not success then - mod:error("%s: %s", error_prefix, return_values[1]) + show_error(mod, error_prefix_data, return_values[1]) return success end return success, unpack(return_values, 1, return_values.n) end -function vmf.xpcall_no_return_values(mod, error_prefix, func, ...) +-- Safe Call [No return values] +function vmf.safe_call_nr(mod, error_prefix_data, func, ...) local success, error_message = xpcall(func, print_error_callstack, ...) if not success then - mod:error("%s: %s", error_prefix, error_message) + show_error(mod, error_prefix_data, error_message) end return success end -function vmf.xpcall_dofile(mod, error_prefix, file_path) +-- Safe Call [No return values and error callstack] +function vmf.safe_call_nrc(mod, error_prefix_data, func, ...) + local success, error_message = pcall(func, ...) + if not success then + show_error(mod, error_prefix_data, error_message) + end + return success +end + + +-- Safe Call [dofile] +function vmf.safe_call_dofile(mod, error_prefix_data, file_path) if type(file_path) ~= "string" then - mod:error("%s: file path should be a string.", error_prefix) + show_error(mod, error_prefix_data, "file path should be a string.") return false end - return vmf.xpcall(mod, error_prefix, dofile, file_path) + return vmf.safe_call(mod, error_prefix_data, dofile, file_path) +end + + +-- Format error message and throw error. +function vmf.throw_error(error_message, ...) + error(string.format(error_message, ...), 0) 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 index 72e0c9a..50faaec 100644 --- a/vmf/scripts/mods/vmf/modules/gui/custom_views.lua +++ b/vmf/scripts/mods/vmf/modules/gui/custom_views.lua @@ -84,7 +84,8 @@ local function inject_view(view_name) end -- Initialize and inject view. - local success, view = vmf.xpcall(mod, "calling init_view_function", init_view_function, _ingame_ui.ingame_ui_context) + local success, view = vmf.safe_call(mod, "calling init_view_function", init_view_function, + _ingame_ui.ingame_ui_context) if success then _ingame_ui.views[view_name] = view else @@ -116,7 +117,7 @@ local function remove_injected_views(on_reload) 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) + vmf.safe_call_nr(view_data.mod, "(custom menus) destroy view", view.destroy) end _ingame_ui.views[view_name] = nil end @@ -279,7 +280,8 @@ function VMFMod:register_view(view_data) local view_name = view_data.view_name - if vmf.catch_errors(self, ERRORS.PREFIX["register_view_validating"], view_name, validate_view_data, view_data) then + if not vmf.safe_call_nrc(self, {ERRORS.PREFIX["register_view_validating"], view_name}, validate_view_data, + view_data) then return end @@ -290,7 +292,7 @@ function VMFMod:register_view(view_data) } if _ingame_ui then - if vmf.catch_errors(self, ERRORS.PREFIX["register_view_injection"], view_name, inject_view, view_name) then + if not vmf.safe_call_nrc(self, {ERRORS.PREFIX["register_view_injection"], view_name}, inject_view, view_name) then _views_data[view_data.view_name] = nil end end @@ -303,7 +305,7 @@ end vmf:hook_safe(IngameUI, "init", function(self) _ingame_ui = self for view_name, _ in pairs(_views_data) do - if vmf.catch_errors(self, ERRORS.PREFIX["ingameui_hook_injection"], view_name, inject_view, view_name) then + if not vmf.safe_call_nrc(self, {ERRORS.PREFIX["ingameui_hook_injection"], view_name}, inject_view, view_name) then _views_data[view_name] = nil end end diff --git a/vmf/scripts/mods/vmf/modules/vmf_mod_manager.lua b/vmf/scripts/mods/vmf/modules/vmf_mod_manager.lua index 33fd992..a64a6fe 100644 --- a/vmf/scripts/mods/vmf/modules/vmf_mod_manager.lua +++ b/vmf/scripts/mods/vmf/modules/vmf_mod_manager.lua @@ -53,8 +53,8 @@ function new_mod(mod_name, mod_resources) -- Load localization data file if mod_resources.mod_localization then - local success, localization_table = vmf.xpcall_dofile(mod, "(new_mod)('mod_localization' initialization)", - mod_resources.mod_localization) + local success, localization_table = vmf.safe_call_dofile(mod, "(new_mod)('mod_localization' initialization)", + mod_resources.mod_localization) if success then vmf.load_mod_localization(mod, localization_table) -- @TODO: return here if not sucessful? rename to "initialize_" else @@ -64,15 +64,15 @@ function new_mod(mod_name, mod_resources) -- Load mod data file if mod_resources.mod_data then - local success, mod_data_table = vmf.xpcall_dofile(mod, "(new_mod)('mod_data' initialization)", - mod_resources.mod_data) + local success, mod_data_table = vmf.safe_call_dofile(mod, "(new_mod)('mod_data' initialization)", + mod_resources.mod_data) if success and not vmf.initialize_mod_data(mod, mod_data_table) then return end end -- Load mod @TODO: what will happen if mod_resources.mod_script == nil? - if not vmf.xpcall_dofile(mod, "(new_mod)('mod_script' initialization)", mod_resources.mod_script) then + if not vmf.safe_call_dofile(mod, "(new_mod)('mod_script' initialization)", mod_resources.mod_script) then return end From 180edd40752e66a4f0cc376776eb81adb188340d Mon Sep 17 00:00:00 2001 From: Azumgi <4zumgi@gmail.com> Date: Sat, 10 Nov 2018 18:46:32 +0300 Subject: [PATCH 12/19] [Custom Views] Wrap ingameui methods in safe calls --- .../mods/vmf/modules/gui/custom_views.lua | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/vmf/scripts/mods/vmf/modules/gui/custom_views.lua b/vmf/scripts/mods/vmf/modules/gui/custom_views.lua index 50faaec..1ef1687 100644 --- a/vmf/scripts/mods/vmf/modules/gui/custom_views.lua +++ b/vmf/scripts/mods/vmf/modules/gui/custom_views.lua @@ -45,7 +45,11 @@ local ERRORS = { PREFIX = { register_view_validating = "[Custom Views] (register_view) View data validating '%s'", register_view_injection = "[Custom Views] (register_view) View injection '%s'", - ingameui_hook_injection = "[Custom Views] View injection '%s'" + ingameui_hook_injection = "[Custom Views] View injection '%s'", + handle_transition_fade = "[Custom Views] (handle_transition) executing 'ingame_ui.transition_with_fade' for " .. + "transition '%s'", + handle_transition_no_fade = "[Custom Views] (handle_transition) executing 'ingame_ui.handle_transition' for " .. + "transition '%s'" } } @@ -265,9 +269,11 @@ function VMFMod:handle_transition(transition_name, transition_params, fade, igno ) then if fade then - _ingame_ui:transition_with_fade(transition_name, transition_params) + vmf.safe_call_nr(self, ERRORS.THROWABLE["handle_transition_fade"], _ingame_ui.transition_with_fade, _ingame_ui, + transition_name, transition_params) else - _ingame_ui:handle_transition(transition_name, transition_params) + vmf.safe_call_nr(self, ERRORS.THROWABLE["handle_transition_no_fade"], _ingame_ui.handle_transition, _ingame_ui, + transition_name, transition_params) end return true end @@ -338,14 +344,14 @@ function vmf.keybind_toggle_view(mod, view_name, can_be_opened, is_keybind_press 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.close_view_transition_params, keybind_transitions.transition_fade, true) end -- Can open views only when keybind is pressed. elseif can_be_opened and is_keybind_pressed then if keybind_transitions.open_view_transition then mod:handle_transition(keybind_transitions.open_view_transition, - keybind_transitions.close_view_transition_params, + keybind_transitions.close_view_transition_params, keybind_transitions.transition_fade, true) end end From 1b93ca23ab37d1f60dc14de49d21340bb61db65d Mon Sep 17 00:00:00 2001 From: Azumgi <4zumgi@gmail.com> Date: Sun, 11 Nov 2018 17:42:56 +0300 Subject: [PATCH 13/19] [Custom Views] Misc. changes for error-handling --- vmf/scripts/mods/vmf/modules/gui/custom_views.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/vmf/scripts/mods/vmf/modules/gui/custom_views.lua b/vmf/scripts/mods/vmf/modules/gui/custom_views.lua index 1ef1687..3aec34b 100644 --- a/vmf/scripts/mods/vmf/modules/gui/custom_views.lua +++ b/vmf/scripts/mods/vmf/modules/gui/custom_views.lua @@ -10,7 +10,7 @@ local ERRORS = { -- inject_view: view_already_exists = "view with name '%s' already persists in original game.", transition_already_exists = "transition with name '%s' already persists in original game.", - view_initializing_failed = "was not able to initialize '%s' view: execution of 'init_view_function' failed.", + view_initializing_failed = "view initialization failed due to error during 'init_view_function' execution.", -- validate_view_data: view_data_wrong_type = "view data must be a table, not %s.", view_name_wrong_type = "'view_name' must be a string, not %s.", @@ -43,6 +43,8 @@ local ERRORS = { view_not_registered = "[Custom Views] Opening view with keybind: view '%s' wasn't registered for this mod." }, PREFIX = { + view_initializing = "[Custom Views] Calling 'init_view_function'", + view_destroying = "[Custom Views] Destroying view '%s'", register_view_validating = "[Custom Views] (register_view) View data validating '%s'", register_view_injection = "[Custom Views] (register_view) View injection '%s'", ingameui_hook_injection = "[Custom Views] View injection '%s'", @@ -88,8 +90,8 @@ local function inject_view(view_name) end -- Initialize and inject view. - local success, view = vmf.safe_call(mod, "calling init_view_function", init_view_function, - _ingame_ui.ingame_ui_context) + local success, view = vmf.safe_call(mod, ERRORS.PREFIX["view_initializing"], init_view_function, + _ingame_ui.ingame_ui_context) if success then _ingame_ui.views[view_name] = view else @@ -121,7 +123,7 @@ local function remove_injected_views(on_reload) local view = _ingame_ui.views[view_name] if view then if type(view.destroy) == "function" then - vmf.safe_call_nr(view_data.mod, "(custom menus) destroy view", view.destroy) + vmf.safe_call_nr(view_data.mod, {ERRORS.PREFIX["view_destroying"], view_name}, view.destroy) end _ingame_ui.views[view_name] = nil end From 2decd8d2e060ea37bddb65bb18ea42aaa4a7445e Mon Sep 17 00:00:00 2001 From: Azumgi <4zumgi@gmail.com> Date: Mon, 12 Nov 2018 18:49:27 +0300 Subject: [PATCH 14/19] [Custom Views] Make register_view work with files --- .../mods/vmf/modules/gui/custom_views.lua | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/vmf/scripts/mods/vmf/modules/gui/custom_views.lua b/vmf/scripts/mods/vmf/modules/gui/custom_views.lua index 3aec34b..9867433 100644 --- a/vmf/scripts/mods/vmf/modules/gui/custom_views.lua +++ b/vmf/scripts/mods/vmf/modules/gui/custom_views.lua @@ -12,7 +12,6 @@ local ERRORS = { transition_already_exists = "transition with name '%s' already persists in original game.", view_initializing_failed = "view initialization failed due to error during 'init_view_function' execution.", -- validate_view_data: - view_data_wrong_type = "view data must be a table, not %s.", view_name_wrong_type = "'view_name' must be a string, not %s.", view_transitions_wrong_type = "'view_transitions' must be a table, not %s.", view_settings_wrong_type = "'view_settings' must be a table, not %s.", @@ -40,11 +39,14 @@ local ERRORS = { "not %s.", }, REGULAR = { + view_data_wrong_type = "[Custom Views] (register_view) Loading view data file '%s': returned view data must be " .. + "a table, not %s.", view_not_registered = "[Custom Views] Opening view with keybind: view '%s' wasn't registered for this mod." }, PREFIX = { view_initializing = "[Custom Views] Calling 'init_view_function'", view_destroying = "[Custom Views] Destroying view '%s'", + register_view_open_file = "[Custom Views] (register_view) Opening view data file '%s'", register_view_validating = "[Custom Views] (register_view) View data validating '%s'", register_view_injection = "[Custom Views] (register_view) View injection '%s'", ingameui_hook_injection = "[Custom Views] View injection '%s'", @@ -148,9 +150,6 @@ end -- @THROWS_ERRORS local function validate_view_data(view_data) -- Basic checks. - if type(view_data) ~= "table" then - vmf.throw_error(ERRORS.THROWABLE["view_data_wrong_type"], type(view_data)) - end if type(view_data.view_name) ~= "string" then vmf.throw_error(ERRORS.THROWABLE["view_name_wrong_type"], type(view_data.view_name)) end @@ -282,9 +281,18 @@ function VMFMod:handle_transition(transition_name, transition_params, fade, igno end -function VMFMod:register_view(view_data) - -- @TODO: load table from file, check if it's table - view_data = table.clone(view_data) +function VMFMod:register_view(view_data_file_path) + local success, view_data = vmf.safe_call_dofile(self, {ERRORS.PREFIX["register_view_open_file"], view_data_file_path}, + view_data_file_path) + if success then + if type(view_data) ~= "table" then + self:error(ERRORS.REGULAR["view_data_wrong_type"], view_data_file_path, type(view_data)) + return + end + view_data = table.clone(view_data) + else + return + end local view_name = view_data.view_name @@ -304,6 +312,8 @@ function VMFMod:register_view(view_data) _views_data[view_data.view_name] = nil end end + + return true end -- ##################################################################################################################### From e940f59fd38d81b05411aadd89a863b8e518b461 Mon Sep 17 00:00:00 2001 From: Azumgi <4zumgi@gmail.com> Date: Mon, 12 Nov 2018 18:52:05 +0300 Subject: [PATCH 15/19] [Mod Options] Move options view to separate file --- .../vmf/modules/ui/options/mod_options.lua | 126 ++++++++++++++++ .../modules/ui/options/vmf_options_view.lua | 134 +----------------- vmf/scripts/mods/vmf/modules/vmf_options.lua | 2 +- vmf/scripts/mods/vmf/vmf_loader.lua | 2 +- 4 files changed, 130 insertions(+), 134 deletions(-) create mode 100644 vmf/scripts/mods/vmf/modules/ui/options/mod_options.lua diff --git a/vmf/scripts/mods/vmf/modules/ui/options/mod_options.lua b/vmf/scripts/mods/vmf/modules/ui/options/mod_options.lua new file mode 100644 index 0000000..aa6c319 --- /dev/null +++ b/vmf/scripts/mods/vmf/modules/ui/options/mod_options.lua @@ -0,0 +1,126 @@ +local vmf = get_mod("VMF") + +local _button_injection_data = vmf:persistent_table("button_injection_data") + + +if VT1 then + + + -- Disable Mod Options button during mods reloading + vmf:hook_safe(IngameView, "update_menu_options", function (self) + for _, button_info in ipairs(self.active_button_data) do + if button_info.transition == "vmf_options_view" then + button_info.widget.content.disabled = _button_injection_data.mod_options_button_disabled + button_info.widget.content.button_hotspot.disabled = _button_injection_data.mod_options_button_disabled + end + end + end) + + + -- Inject Mod Options button in current ESC-menu layout + -- Disable localization for button widget + vmf:hook(IngameView, "setup_button_layout", function (func, self, layout_data, ...) + local mods_options_button = { + display_name = vmf:localize("mods_options"), + transition = "vmf_options_view", + fade = false + } + for i = 1, #layout_data do + if layout_data[i].transition == "options_menu" and layout_data[i + 1].transition ~= "vmf_options_view" then + table.insert(layout_data, i + 1, mods_options_button) + break + end + end + + func(self, layout_data, ...) + + for _, button_info in ipairs(self.active_button_data) do + if button_info.transition == "vmf_options_view" then + button_info.widget.style.text.localize = false + button_info.widget.style.text_disabled.localize = false + button_info.widget.style.text_click.localize = false + button_info.widget.style.text_hover.localize = false + button_info.widget.style.text_selected.localize = false + end + end + end) + + +else + + + local function get_mod_options_button_index(layout_logic) + for button_index, button_data in ipairs(layout_logic.active_button_data) do + if button_data.transition == "vmf_options_view" then + return button_index + end + end + end + + + -- Disable localization for Mod Options button widget for pc version of ESC-menu + -- Widget definition: ingame_view_definitions.lua -> UIWidgets.create_default_button + vmf:hook_safe(IngameView, "on_enter", function (self) + self.layout_logic._ingame_view = self + end) + vmf:hook_safe(IngameViewLayoutLogic, "setup_button_layout", function (self) + if self._ingame_view then + local mod_options_button_index = get_mod_options_button_index(self) + local button_widget = self._ingame_view.stored_buttons[mod_options_button_index] + button_widget.style.title_text.localize = false + button_widget.style.title_text_shadow.localize = false + button_widget.style.title_text_disabled.localize = false + end + end) + + + -- Disable localization for Mod Options button widget for console version of ESC-menu + -- Widget definition: hero_window_ingame_view_definitions.lua -> create_title_button + vmf:hook_safe(HeroWindowIngameView, "on_enter", function (self) + local button_widget = self._title_button_widgets[get_mod_options_button_index(self.layout_logic)] + button_widget.style.text.localize = false + button_widget.style.text_hover.localize = false + button_widget.style.text_shadow.localize = false + button_widget.style.text_disabled.localize = false + end) + + + -- Disable Mod Options button during mods reloading + vmf:hook_safe(IngameViewLayoutLogic, "_update_menu_options_enabled_states", function (self) + local mod_options_button_index = get_mod_options_button_index(self) + local mod_options_button_data = self.active_button_data[mod_options_button_index] + mod_options_button_data.disabled = _button_injection_data.mod_options_button_disabled + end) + + + -- Inject Mod Options button in all possible ESC-menu layouts (except for developer's one, because it will increase + -- the number of buttons to 10, when the hard limit is 9, which will crash the game) + vmf:hook_safe(IngameViewLayoutLogic, "init", function (self) + local mod_options_button = { + display_name = vmf:localize("mods_options"), + transition = "vmf_options_view", + fade = false + } + for _, layout in pairs(self.layout_list) do + for i = 1, #layout do + if layout[i].transition == "options_menu" and layout[i + 1].transition ~= "vmf_options_view" then + table.insert(layout, i + 1, mod_options_button) + break + end + end + end + end) + + +end + + +vmf.initialize_vmf_options_view = function () + vmf:register_view("scripts/mods/vmf/modules/ui/options/vmf_options_view") + _button_injection_data.mod_options_button_disabled = false +end + + +vmf.disable_mods_options_button = function () + _button_injection_data.mod_options_button_disabled = true +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 acedc4b..4e340ee 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 @@ -4276,7 +4276,7 @@ vmf.load_vmf_options_view_settings() -local view_data = { +return { view_name = "vmf_options_view", view_settings = { init_view_function = function (ingame_ui_context) @@ -4286,10 +4286,6 @@ local view_data = { inn = true, ingame = true }, - blocked_transitions = { - inn = {}, - ingame = {} - }, keybind_transitions = { open_view_transition = "vmf_options_view", close_view_transition = "exit_menu", @@ -4301,130 +4297,4 @@ local view_data = { self.menu_active = true end } -} - - -local _button_injection_data = vmf:persistent_table("button_injection_data") - - -if VT1 then - - - -- Disable Mod Options button during mods reloading - vmf:hook_safe(IngameView, "update_menu_options", function (self) - for _, button_info in ipairs(self.active_button_data) do - if button_info.transition == "vmf_options_view" then - button_info.widget.content.disabled = _button_injection_data.mod_options_button_disabled - button_info.widget.content.button_hotspot.disabled = _button_injection_data.mod_options_button_disabled - end - end - end) - - - -- Inject Mod Options button in current ESC-menu layout - -- Disable localization for button widget - vmf:hook(IngameView, "setup_button_layout", function (func, self, layout_data, ...) - local mods_options_button = { - display_name = vmf:localize("mods_options"), - transition = "vmf_options_view", - fade = false - } - for i = 1, #layout_data do - if layout_data[i].transition == "options_menu" and layout_data[i + 1].transition ~= "vmf_options_view" then - table.insert(layout_data, i + 1, mods_options_button) - break - end - end - - func(self, layout_data, ...) - - for _, button_info in ipairs(self.active_button_data) do - if button_info.transition == "vmf_options_view" then - button_info.widget.style.text.localize = false - button_info.widget.style.text_disabled.localize = false - button_info.widget.style.text_click.localize = false - button_info.widget.style.text_hover.localize = false - button_info.widget.style.text_selected.localize = false - end - end - end) - - -else - - - local function get_mod_options_button_index(layout_logic) - for button_index, button_data in ipairs(layout_logic.active_button_data) do - if button_data.transition == "vmf_options_view" then - return button_index - end - end - end - - - -- Disable localization for Mod Options button widget for pc version of ESC-menu - -- Widget definition: ingame_view_definitions.lua -> UIWidgets.create_default_button - vmf:hook_safe(IngameView, "on_enter", function (self) - self.layout_logic._ingame_view = self - end) - vmf:hook_safe(IngameViewLayoutLogic, "setup_button_layout", function (self) - if self._ingame_view then - local mod_options_button_index = get_mod_options_button_index(self) - local button_widget = self._ingame_view.stored_buttons[mod_options_button_index] - button_widget.style.title_text.localize = false - button_widget.style.title_text_shadow.localize = false - button_widget.style.title_text_disabled.localize = false - end - end) - - - -- Disable localization for Mod Options button widget for console version of ESC-menu - -- Widget definition: hero_window_ingame_view_definitions.lua -> create_title_button - vmf:hook_safe(HeroWindowIngameView, "on_enter", function (self) - local button_widget = self._title_button_widgets[get_mod_options_button_index(self.layout_logic)] - button_widget.style.text.localize = false - button_widget.style.text_hover.localize = false - button_widget.style.text_shadow.localize = false - button_widget.style.text_disabled.localize = false - end) - - - -- Disable Mod Options button during mods reloading - vmf:hook_safe(IngameViewLayoutLogic, "_update_menu_options_enabled_states", function (self) - local mod_options_button_index = get_mod_options_button_index(self) - local mod_options_button_data = self.active_button_data[mod_options_button_index] - mod_options_button_data.disabled = _button_injection_data.mod_options_button_disabled - end) - - - -- Inject Mod Options button in all possible ESC-menu layouts (except for developer's one, because it will increase - -- the number of buttons to 10, when the hard limit is 9, which will crash the game) - vmf:hook_safe(IngameViewLayoutLogic, "init", function (self) - local mod_options_button = { - display_name = vmf:localize("mods_options"), - transition = "vmf_options_view", - fade = false - } - for _, layout in pairs(self.layout_list) do - for i = 1, #layout do - if layout[i].transition == "options_menu" and layout[i + 1].transition ~= "vmf_options_view" then - table.insert(layout, i + 1, mod_options_button) - break - end - end - end - end) - - -end - - -vmf.initialize_vmf_options_view = function () - vmf:register_view(view_data) - _button_injection_data.mod_options_button_disabled = false -end - - -vmf.disable_mods_options_button = function () - _button_injection_data.mod_options_button_disabled = true -end \ No newline at end of file +} \ No newline at end of file diff --git a/vmf/scripts/mods/vmf/modules/vmf_options.lua b/vmf/scripts/mods/vmf/modules/vmf_options.lua index fcca4df..33f81d2 100644 --- a/vmf/scripts/mods/vmf/modules/vmf_options.lua +++ b/vmf/scripts/mods/vmf/modules/vmf_options.lua @@ -240,7 +240,7 @@ if not vmf:get("vmf_initialized") then vmf.load_dev_console_settings() vmf.load_chat_history_settings() vmf.load_ui_scaling_settings() - vmf.load_vmf_options_view_settings() + --vmf.load_vmf_options_view_settings() vmf:set("vmf_initialized", true) end \ No newline at end of file diff --git a/vmf/scripts/mods/vmf/vmf_loader.lua b/vmf/scripts/mods/vmf/vmf_loader.lua index 0b95550..083932b 100644 --- a/vmf/scripts/mods/vmf/vmf_loader.lua +++ b/vmf/scripts/mods/vmf/vmf_loader.lua @@ -34,7 +34,7 @@ function vmf_mod_object:init() 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") + dofile("scripts/mods/vmf/modules/ui/options/mod_options") dofile("scripts/mods/vmf/modules/vmf_options") if VT1 then From 42ff0bd6b238ea225d3e0ce40748cf631ca16ee3 Mon Sep 17 00:00:00 2001 From: Azumgi <4zumgi@gmail.com> Date: Tue, 13 Nov 2018 16:38:55 +0300 Subject: [PATCH 16/19] [Keybinds] Errors refactoring --- vmf/scripts/mods/vmf/modules/core/keybindings.lua | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/vmf/scripts/mods/vmf/modules/core/keybindings.lua b/vmf/scripts/mods/vmf/modules/core/keybindings.lua index 82a7595..84d9157 100644 --- a/vmf/scripts/mods/vmf/modules/core/keybindings.lua +++ b/vmf/scripts/mods/vmf/modules/core/keybindings.lua @@ -183,6 +183,15 @@ local _raw_keybinds_data = {} local _keybinds = {} local _pressed_key +local ERRORS = { + PREFIX = { + function_call = "[Keybindings] function_call 'mod.%s'" + }, + REGULAR = { + function_not_found = "[Keybindings] function_call 'mod.%s': function was not found." + } +} + -- ##################################################################################################################### -- ##### Local functions ############################################################################################### -- ##################################################################################################################### @@ -196,10 +205,9 @@ end -- Executes function for 'function_call' keybinds. local function call_function(mod, function_name, keybind_is_pressed) if type(mod[function_name]) == "function" then - local error_prefix = string.format("(keybindings) function_call 'mod.%s'", function_name) - vmf.safe_call_nr(mod, error_prefix, mod[function_name], keybind_is_pressed) + vmf.safe_call_nr(mod, {ERRORS.PREFIX["function_call"], function_name}, mod[function_name], keybind_is_pressed) else - mod:error("(keybindings) function_call 'mod.%s': function was not found.", tostring(function_name)) + mod:error(ERRORS.PREFIX["function_not_found"], function_name) end end From 2d288bb737ffc095359c2db740398a8793225e14 Mon Sep 17 00:00:00 2001 From: Azumgi <4zumgi@gmail.com> Date: Tue, 13 Nov 2018 16:40:32 +0300 Subject: [PATCH 17/19] [Custom Views] Fix nil instead of error message --- vmf/scripts/mods/vmf/modules/gui/custom_views.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vmf/scripts/mods/vmf/modules/gui/custom_views.lua b/vmf/scripts/mods/vmf/modules/gui/custom_views.lua index 9867433..901222e 100644 --- a/vmf/scripts/mods/vmf/modules/gui/custom_views.lua +++ b/vmf/scripts/mods/vmf/modules/gui/custom_views.lua @@ -270,11 +270,11 @@ function VMFMod:handle_transition(transition_name, transition_params, fade, igno ) then if fade then - vmf.safe_call_nr(self, ERRORS.THROWABLE["handle_transition_fade"], _ingame_ui.transition_with_fade, _ingame_ui, - transition_name, transition_params) + vmf.safe_call_nr(self, ERRORS.PREFIX["handle_transition_fade"], _ingame_ui.transition_with_fade, _ingame_ui, + transition_name, transition_params) else - vmf.safe_call_nr(self, ERRORS.THROWABLE["handle_transition_no_fade"], _ingame_ui.handle_transition, _ingame_ui, - transition_name, transition_params) + vmf.safe_call_nr(self, ERRORS.PREFIX["handle_transition_no_fade"], _ingame_ui.handle_transition, _ingame_ui, + transition_name, transition_params) end return true end From 2f0636f443e2af67a7d64a502240349395dbc894 Mon Sep 17 00:00:00 2001 From: Azumgi <4zumgi@gmail.com> Date: Tue, 13 Nov 2018 20:01:06 +0300 Subject: [PATCH 18/19] [Custom Views] Add handle_transition args check --- vmf/scripts/mods/vmf/modules/gui/custom_views.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/vmf/scripts/mods/vmf/modules/gui/custom_views.lua b/vmf/scripts/mods/vmf/modules/gui/custom_views.lua index 901222e..4304535 100644 --- a/vmf/scripts/mods/vmf/modules/gui/custom_views.lua +++ b/vmf/scripts/mods/vmf/modules/gui/custom_views.lua @@ -252,6 +252,10 @@ end -- ##################################################################################################################### function VMFMod:handle_transition(transition_name, transition_params, fade, ignore_active_menu) + if vmf.check_wrong_argument_type(self, "handle_transition", "transition_name", transition_name, "string") then + return + end + if _ingame_ui and not _ingame_ui:pending_transition() and not _ingame_ui:end_screen_active() From 87c4bdcf700c952040b131b077a08da3263168c1 Mon Sep 17 00:00:00 2001 From: Azumgi <4zumgi@gmail.com> Date: Tue, 13 Nov 2018 20:02:29 +0300 Subject: [PATCH 19/19] [Custom Views] Add description for some functions --- vmf/scripts/mods/vmf/modules/gui/custom_views.lua | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/vmf/scripts/mods/vmf/modules/gui/custom_views.lua b/vmf/scripts/mods/vmf/modules/gui/custom_views.lua index 4304535..33b4e49 100644 --- a/vmf/scripts/mods/vmf/modules/gui/custom_views.lua +++ b/vmf/scripts/mods/vmf/modules/gui/custom_views.lua @@ -251,6 +251,14 @@ end -- ##### VMFMod ######################################################################################################## -- ##################################################################################################################### +--[[ + Wraps ingame_ui transition handling calls in a lot of safety checks. Returns 'true', if call is successful. + * transition_name [string] : name of a transition that should be perfomed + * transition_params [anything]: parameter, which will be passed to callable transition function, 'on_exit' method of + the old view and 'on_enter' method of the new view + * fade [boolean] : if transition should be performed with fade + * ignore_active_menu [boolean] : if 'ingame_ui.menu_active' should be ignored +--]] function VMFMod:handle_transition(transition_name, transition_params, fade, ignore_active_menu) if vmf.check_wrong_argument_type(self, "handle_transition", "transition_name", transition_name, "string") then return @@ -285,6 +293,10 @@ function VMFMod:handle_transition(transition_name, transition_params, fade, igno end +--[[ + Opens a file with a view data and validates it. Registers the view and returns 'true' if everything is correct. + * view_data_file_path [string]: path to a file returning view_data table +--]] function VMFMod:register_view(view_data_file_path) local success, view_data = vmf.safe_call_dofile(self, {ERRORS.PREFIX["register_view_open_file"], view_data_file_path}, view_data_file_path) @@ -348,6 +360,8 @@ function vmf.remove_custom_views() end +-- Opens/closes a view if all conditions are met. Since keybinds module can't do UI-related checks, all the cheks are +-- done in this function. This function is called every time some view-toggling keybind is pressed. function vmf.keybind_toggle_view(mod, view_name, can_be_opened, is_keybind_pressed) if _ingame_ui then if not _views_data[view_name] or (_views_data[view_name].mod ~= mod) then