Keybinds and custom views modules overhaul (#28)

This commit is contained in:
Azumgi 2018-11-13 20:47:39 +03:00 committed by GitHub
commit 70e726f090
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 1039 additions and 817 deletions

View file

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

View file

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

View file

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

View file

@ -1,358 +1,382 @@
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
vmf.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"},
}]]
}
vmf.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, ""}
}
-- ####################################################################################################################
-- ##### Script #######################################################################################################
-- ####################################################################################################################
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
for _, controller_keys in pairs(vmf.keys) do
for _, key_info in pairs(controller_keys) do
vmf.readable_key_names[key_info[2]] = key_info[1]
-- 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 ERRORS = {
PREFIX = {
function_call = "[Keybindings] function_call 'mod.%s'"
},
REGULAR = {
function_not_found = "[Keybindings] function_call 'mod.%s': function was not found."
}
}
-- #####################################################################################################################
-- ##### 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
-- Executes function for 'function_call' keybinds.
local function call_function(mod, function_name, keybind_is_pressed)
if type(mod[function_name]) == "function" then
vmf.safe_call_nr(mod, {ERRORS.PREFIX["function_call"], function_name}, mod[function_name], keybind_is_pressed)
else
mod:error(ERRORS.PREFIX["function_not_found"], function_name)
end
end
vmf.readable_key_names["ctrl"] = "Ctrl"
vmf.readable_key_names["alt"] = "Alt"
vmf.readable_key_names["shift"] = "Shift"
for _, key_info in pairs(vmf.keys.keyboard) do
VMFModsKeyMap.win32[key_info[2]] = {"keyboard", key_info[3], "held"}
-- 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.keybind_toggle_view(data.mod, data.view_name, can_perform_action, is_pressed)
return true
end
end
for i = 0, 4 do
local key_info = vmf.keys.mouse[i]
VMFModsKeyMap.win32[key_info[2]] = {"mouse", key_info[3], "held"}
-- #####################################################################################################################
-- ##### 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
for i = 10, 13 do
local key_info = vmf.keys.mouse[i]
VMFModsKeyMap.win32[key_info[2]] = {"mouse", key_info[3], "pressed"}
-- 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
-- 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 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
-- #####################################################################################################################
-- ##### Script ########################################################################################################
-- #####################################################################################################################
-- In case mods reloading was performed right at the moment of entering 'StateInGame'.
vmf.create_keybinds_input_service()

View file

@ -27,7 +27,3 @@ function vmf.check_old_vmf()
"Either remove old mods or disable workshop mods.")
end
end
function vmf.throw_error(error_message, ...)
error(string.format(error_message, ...), 0)
end

View file

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

View file

@ -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.readable_key_names[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

View file

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

View file

@ -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("VMFMods")
local original_is_blocked = input_service:is_blocked()
if original_is_blocked then
Managers.input:device_unblock_service("keyboard", 1, "VMFMods")
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, "VMFMods")
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

View file

@ -0,0 +1,409 @@
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 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 = "view initialization failed due to error during 'init_view_function' execution.",
-- validate_view_data:
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_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'",
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'"
}
}
-- #####################################################################################################################
-- ##### 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
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
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
vmf.throw_error(ERRORS.THROWABLE["transition_already_exists"], transition_name)
end
end
-- Initialize and inject view.
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
vmf.throw_error(ERRORS.THROWABLE["view_initializing_failed"], view_name)
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_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.
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.safe_call_nr(view_data.mod, {ERRORS.PREFIX["view_destroying"], view_name}, view.destroy)
end
_ingame_ui.views[view_name] = nil
end
end
end
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_ERRORS
local function validate_view_data(view_data)
-- Basic checks.
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 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
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
-- #####################################################################################################################
-- ##### 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
end
if _ingame_ui
and not _ingame_ui:pending_transition()
and not _ingame_ui:end_screen_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
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
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.PREFIX["handle_transition_no_fade"], _ingame_ui.handle_transition, _ingame_ui,
transition_name, transition_params)
end
return true
end
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)
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
if not vmf.safe_call_nrc(self, {ERRORS.PREFIX["register_view_validating"], view_name}, validate_view_data,
view_data) then
return
end
_views_data[view_name] = {
mod = self,
view_settings = view_data.view_settings,
view_transitions = view_data.view_transitions
}
if _ingame_ui 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
return true
end
-- #####################################################################################################################
-- ##### Hooks #########################################################################################################
-- #####################################################################################################################
vmf:hook_safe(IngameUI, "init", function(self)
_ingame_ui = self
for view_name, _ in pairs(_views_data) do
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
end)
vmf:hook_safe(IngameUI, "destroy", function()
remove_injected_views(false)
_ingame_ui = nil
end)
-- #####################################################################################################################
-- ##### VMF internal functions and variables ##########################################################################
-- #####################################################################################################################
function vmf.remove_custom_views()
remove_injected_views(true)
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
mod:error(ERRORS.REGULAR["view_not_registered"], view_name)
return
end
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
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

View file

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

View file

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

View file

@ -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.readable_key_names[key]
else
keybind_string = keybind_string .. " + " .. vmf.readable_key_names[key]
end
end
return keybind_string
end
local function create_keybind_widget(widget_definition, scenegraph_id)
local widget_size = SETTINGS_LIST_REGULAR_WIDGET_SIZE
@ -2654,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,
@ -3302,95 +3291,74 @@ 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 and (Keyboard.any_pressed() or Mouse.any_pressed()) then
local first_pressed_button_info = nil
local first_pressed_button_index = nil
local first_pressed_button_type = nil
if not widget_content.first_pressed_button_id then
if Keyboard.any_pressed() then
first_pressed_button_info = vmf.keys.keyboard[Keyboard.any_pressed()]
first_pressed_button_index = Keyboard.any_pressed()
first_pressed_button_type = "keyboard"
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
first_pressed_button_info = vmf.keys.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]
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_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 = {}
local preview_string = ""
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]
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
preview_string = preview_string .. " + Ctrl"
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
preview_string = preview_string .. " + Alt"
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
preview_string = preview_string .. " + Shift"
if Keyboard.button(Keyboard.button_index("left shift")) + Keyboard.button(Keyboard.button_index("right shift")) > 0 then
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 = vmf.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_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_id = 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
set_new_keybind(widget_content)
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 = {}
widget_content.keys = {}
set_new_keybind(widget_content)
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
@ -3867,7 +3835,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
@ -4309,7 +4277,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)
@ -4319,160 +4287,15 @@ local view_data = {
inn = true,
ingame = true
},
blocked_transitions = {
inn = {},
ingame = {
--vmf_options_view = true,
--vmf_options_view_force = true
}
},
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
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_new_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

View file

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

View file

@ -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"
@ -239,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

View file

@ -31,10 +31,10 @@ 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")
dofile("scripts/mods/vmf/modules/ui/options/mod_options")
dofile("scripts/mods/vmf/modules/vmf_options")
if VT1 then
@ -56,14 +56,13 @@ end
function vmf_mod_object:update(dt)
vmf.mods_update_event(dt)
vmf.check_pressed_keybinds()
vmf.check_custom_menus_close_keybinds(dt)
vmf.check_keybinds()
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()
@ -88,10 +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.delete_keybinds()
vmf.mods_unload_event(false)
vmf.remove_custom_views()
vmf.hooks_unload()
vmf.reset_guis()
end
@ -105,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