diff --git a/vmf/localization/vmf.lua b/vmf/localization/vmf.lua index fa7ce87..36c470b 100644 --- a/vmf/localization/vmf.lua +++ b/vmf/localization/vmf.lua @@ -36,10 +36,12 @@ return { ru = "Консоль разработчика", }, show_developer_console_tooltip = { - en = "Opens up the new window showing game log in real time.\n\n" .. - "In order to safely close this window, disable it from the menu options first, and then close the window.", - ru = "Открывает новое окно, в которое в реальном времени выводится игровой лог.\n\n" .. - "Чтобы его закрыть, сначала выключите консоль из меню настроек, и потом закройте вручную.", + en = "Opens up the new window showing game log in real time.", + ru = "Открывает новое окно, в которое в реальном времени выводится игровой лог.", + }, + toggle_developer_console = { + en = "Toggle Developer Console", + ru = "Открыть/закрыть консоль разработчика", }, show_network_debug_info = { en = "Log Network Calls", @@ -109,4 +111,80 @@ return { en = "Log & Chat", ru = "Лог и чат", }, + chat_history_enable = { + en = "Chat Input History", + ru = "История ввода чата", + }, + chat_history_enable_tooltip = { + en = "Saves all the messages and commands you typed in the chat window.\n\n" .. + "You can browse your input history by opening the chat and pressing \"Arrow Up\" and \"Arrow Down\".", + ru = "Сохраняет все сообщения и команды, введённые в чате.\n\n" .. + "Чтобы пролистывать историю ввода, откройте чат и используйте клавиши \"стрелка вверх\" и \"стрелка вниз\".", + }, + chat_history_save = { + en = "Save Input History Between Game Sessions", + ru = "Сохранять историю ввода между сеансами игры", + }, + chat_history_save_tooltip = { + en = "Your chat input history will be saved even after reloading your game (or just VMF).", + ru = "Когда игрок выключает игру (или перезагружает VMF), VMF cохраняет историю ввода в файл настроек, чтобы загрузить её при следующем запуске игры.", + }, + chat_history_buffer_size = { + en = "Input History Buffer Size", + ru = "Размер буфера истории ввода", + }, + chat_history_buffer_size_tooltip = { + en = "Maximum number of saved entries.\n\n" .. + "WARNING: Changing this setting will erase your chat history.", + ru = "Максимальное количество сохраняемых записей.\n\n" .. + "ВНИМАНИЕ: изменение этой настройки очистит вашу историю ввода.", + }, + chat_history_remove_dups = { + en = "Remove Duplicate Entries", + ru = "Удалять повторяющиеся записи", + }, + chat_history_remove_dups_mode = { + en = "Removal Mode", + ru = "Режим удаления", + }, + chat_history_remove_dups_mode_tooltip = { + en = "Which duplicate entries should be removed.\n\n" .. + "-- LAST --\nRemoves previous entry if it matches the last one.\n\n" .. + "-- ALL --\nRemoves all entries if it matches the last one.", + ru = "Повторяющиеся записи, которые будут удалены.\n\n" .. + "-- ПОСЛЕДНИЕ --\nПредпоследняя запись будет удалена, если она совпадает с последней.\n\n" .. + "-- ВСЕ --\nВсе записи, совпадающие с последней записью, будут удалены.", + }, + settings_last = { + en = "Last", + ru = "Последние", + }, + settings_all = { + en = "All", + ru = "Все", + }, + chat_history_commands_only = { + en = "Save only executed commands", + ru = "Сохранять только выполненные команды", + }, + chat_history_commands_only_tooltip = { + en = "Only successfully executed commands will be saved in the chat history.\n\n" .. + "WARNING: Changing this setting will erase your chat history.", + ru = "Только успешно выполненные команды будут сохранены в истории ввода.\n\n" .. + "ВНИМАНИЕ: изменение этой настройки очистит вашу историю ввода.", + }, + + + clean_chat_history = { + en = "cleans chat input history", + ru = "очищает историю ввода", + }, + dev_console_opened = { + en = "Developer console opened.", + ru = "Консоль разработчика открыта.", + }, + dev_console_closed = { + en = "Developer console closed.", + ru = "Консоль разработчика закрыта.", + }, } \ No newline at end of file diff --git a/vmf/resource_packages/vmf.package b/vmf/resource_packages/vmf.package index 1981927..a45f905 100644 --- a/vmf/resource_packages/vmf.package +++ b/vmf/resource_packages/vmf.package @@ -19,6 +19,7 @@ lua = [ "scripts/mods/vmf/modules/core/*" "scripts/mods/vmf/modules/debug/*" "scripts/mods/vmf/modules/gui/*" - "scripts/mods/vmf/modules/options_menu/*" - "scripts/mods/vmf/modules/mutators/*" + "scripts/mods/vmf/modules/ui/options/*" + "scripts/mods/vmf/modules/ui/chat/*" + "scripts/mods/vmf/modules/ui/mutators/*" ] \ No newline at end of file diff --git a/vmf/scripts/mods/vmf/modules/core/commands.lua b/vmf/scripts/mods/vmf/modules/core/commands.lua new file mode 100644 index 0000000..8d38aa1 --- /dev/null +++ b/vmf/scripts/mods/vmf/modules/core/commands.lua @@ -0,0 +1,166 @@ +local vmf = get_mod("VMF") +--[[ + * letters' case doesn't matter + * ctrl+c & ctrl+v + * very flexible chat history without any glitches that occured in the past + * commands are not shown if the mod is disabled + + not sure about UI scaling +]] + +-- @TODO: move 'vmf.check_wrong_argument_type' to somewhere else + +local _COMMANDS = {} + +-- #################################################################################################################### +-- ##### Local functions ############################################################################################## +-- #################################################################################################################### + +vmf.check_wrong_argument_type = function(mod, vmf_function_name, argument_name, argument, ...) + + local allowed_types = {...} + local argument_type = type(argument) + + for _, allowed_type in ipairs(allowed_types) do + if allowed_type == argument_type then + return false + end + end + + 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 + +-- #################################################################################################################### +-- ##### VMFMod ####################################################################################################### +-- #################################################################################################################### + +VMFMod.command = function (self, command_name, command_description, command_function) + + if vmf.check_wrong_argument_type(self, "command", "command_name", command_name, "string") or + vmf.check_wrong_argument_type(self, "command", "command_description", command_description, "string", "nil") or + vmf.check_wrong_argument_type(self, "command", "command_function", command_function, "function") then + + return + end + + command_name = command_name:lower() + + if _COMMANDS[command_name] and _COMMANDS[command_name].mod ~= self then + self:error("(command): command name '%s' is already used by another mod '%s'", command_name, _COMMANDS[command_name].mod:get_name()) + return + end + + _COMMANDS[command_name] = { + mod = self, + exec_function = command_function, + description = command_description, + is_enabled = true + } +end + + +VMFMod.command_remove = function (self, command_name) + + if vmf.check_wrong_argument_type(self, "command_remove", "command_name", command_name, "string") then + return + end + + _COMMANDS[command_name] = nil +end + + +VMFMod.command_disable = function (self, command_name) + + if vmf.check_wrong_argument_type(self, "command_disable", "command_name", command_name, "string") then + return + end + + if _COMMANDS[command_name] then + _COMMANDS[command_name].is_enabled = false + end +end + + +VMFMod.command_enable = function (self, command_name) + + if vmf.check_wrong_argument_type(self, "command_enable", "command_name", command_name, "string") then + return + end + + if _COMMANDS[command_name] then + _COMMANDS[command_name].is_enabled = true + end +end + + +VMFMod.remove_all_commands = function (self) + + for command_name, command_entry in pairs(_COMMANDS) do + if command_entry.mod == self then + _COMMANDS[command_name] = nil + end + end +end + + +VMFMod.disable_all_commands = function (self) + for _, command_entry in pairs(_COMMANDS) do + if command_entry.mod == self then + command_entry.is_enabled = false + end + end +end + + +VMFMod.enable_all_commands = function (self) + for _, command_entry in pairs(_COMMANDS) do + if command_entry.mod == self then + command_entry.is_enabled = true + end + end +end + +-- #################################################################################################################### +-- ##### VMF internal functions and variables ######################################################################### +-- #################################################################################################################### + +vmf.get_commands_list = function(name_contains, exact_match) + + name_contains = name_contains:lower() + + local commands_list = {} + + for command_name, command_entry in pairs(_COMMANDS) do + + if exact_match then + if command_name == name_contains and command_entry.is_enabled then + table.insert(commands_list, {name = command_name, description = command_entry.description}) + break + end + else + if string.sub(command_name, 1, string.len(name_contains)) == name_contains and command_entry.is_enabled and command_entry.mod:is_enabled() then + table.insert(commands_list, {name = command_name, description = command_entry.description}) + end + end + end + + table.sort(commands_list, function(a, b) return a.name < b.name end) + + return commands_list +end + + +vmf.run_command = function(command_name, ...) + + local command_entry = _COMMANDS[command_name] + if command_entry then + local success, error_message = pcall(command_entry.exec_function, ...) + if not success then + command_entry.mod:error("(commands) in command '%s': %s", command_name, tostring(error_message)) + end + else + vmf:error("(commands): command '%s' wasn't found.", command_name) -- should never see this + end +end \ No newline at end of file diff --git a/vmf/scripts/mods/vmf/modules/core/core_functions.lua b/vmf/scripts/mods/vmf/modules/core/core_functions.lua index 43b5471..c5b2834 100644 --- a/vmf/scripts/mods/vmf/modules/core/core_functions.lua +++ b/vmf/scripts/mods/vmf/modules/core/core_functions.lua @@ -181,17 +181,17 @@ vmf.unsent_chat_messages = _UNSENT_CHAT_MESSAGES vmf.load_logging_settings = function () _LOGGING_SETTINGS = { - echo = vmf:get("logging_mode") == "custom" and vmf:get("output_mode_echo") or 3, - error = vmf:get("logging_mode") == "custom" and vmf:get("output_mode_error") or 3, - warning = vmf:get("logging_mode") == "custom" and vmf:get("output_mode_warning") or 3, - info = vmf:get("logging_mode") == "custom" and vmf:get("output_mode_info") or 1, - debug = vmf:get("logging_mode") == "custom" and vmf:get("output_mode_debug") or 0, + echo = vmf:get("logging_mode") == "custom" and vmf:get("output_mode_echo"),-- or 3, @TODO: clean up? + error = vmf:get("logging_mode") == "custom" and vmf:get("output_mode_error"),-- or 3, + warning = vmf:get("logging_mode") == "custom" and vmf:get("output_mode_warning"),-- or 3, + info = vmf:get("logging_mode") == "custom" and vmf:get("output_mode_info"),-- or 1, + debug = vmf:get("logging_mode") == "custom" and vmf:get("output_mode_debug"),-- or 0, } for method_name, logging_mode in pairs(_LOGGING_SETTINGS) do _LOGGING_SETTINGS[method_name] = { - send_to_chat = logging_mode >= 2, - send_to_log = logging_mode % 2 == 1 + send_to_chat = logging_mode and logging_mode >= 2, + send_to_log = logging_mode and logging_mode % 2 == 1 } end end diff --git a/vmf/scripts/mods/vmf/modules/core/events.lua b/vmf/scripts/mods/vmf/modules/core/events.lua index 22c1c7c..9677d7b 100644 --- a/vmf/scripts/mods/vmf/modules/core/events.lua +++ b/vmf/scripts/mods/vmf/modules/core/events.lua @@ -19,8 +19,8 @@ end -- ##### VMF internal functions and variables ######################################################################### -- #################################################################################################################### --- call 'unload' for every mod which defined it -vmf.mods_unload_event = function() +-- call 'on_unload' for every mod which defined it +vmf.mods_unload_event = function(exit_game) local event_name = "on_unload" @@ -29,7 +29,7 @@ vmf.mods_unload_event = function() local mod = _MODS[mod_name] local event = mod[event_name] if event then - run_event(mod, event_name, event) + run_event(mod, event_name, event, exit_game) end end end @@ -48,7 +48,7 @@ vmf.mods_update_event = function(dt) end end --- call 'game_state_changed' for every mod which defined it +-- call 'on_game_state_changed' for every mod which defined it vmf.mods_game_state_changed_event = function(status, state) local event_name = "on_game_state_changed" @@ -114,4 +114,18 @@ vmf.mod_user_left_the_game = function(mod, player) if event then run_event(mod, event_name, event, player) end +end + +vmf.all_mods_loaded_event = function() + + local event_name = "on_all_mods_loaded" + + for _, mod_name in ipairs(_MODS_UNLOADING_ORDER) do + + local mod = _MODS[mod_name] + local event = mod[event_name] + if event then + run_event(mod, event_name, event) + end + end end \ No newline at end of file diff --git a/vmf/scripts/mods/vmf/modules/core/hooks.lua b/vmf/scripts/mods/vmf/modules/core/hooks.lua index a31d2e6..69bb33b 100644 --- a/vmf/scripts/mods/vmf/modules/core/hooks.lua +++ b/vmf/scripts/mods/vmf/modules/core/hooks.lua @@ -67,8 +67,6 @@ local function create_hook_entry(mod, hooked_function_entry, hook_function) hook_entry.is_enabled = true table.insert(hooked_function_entry.hooks, hook_entry) - - --return hook_entry -- @TODO: do I need this return? end @@ -134,6 +132,7 @@ end local function modify_hook(mod, hooked_function_name, action) + -- @TODO: I guess I don't need this check? if not get_function_by_name(hooked_function_name) then mod:error("(hook_%s): function [%s] doesn't exist", action, hooked_function_name) return diff --git a/vmf/scripts/mods/vmf/modules/core/network.lua b/vmf/scripts/mods/vmf/modules/core/network.lua index 8390c3e..0a4a73d 100644 --- a/vmf/scripts/mods/vmf/modules/core/network.lua +++ b/vmf/scripts/mods/vmf/modules/core/network.lua @@ -11,6 +11,8 @@ local _SHARED_RPCS_MAP = "" local _NETWORK_MODULE_IS_INITIALIZED = false +local _NETWORK_DEBUG = false + -- #################################################################################################################### -- ##### Local functions ############################################################################################## -- #################################################################################################################### @@ -82,7 +84,7 @@ end local function network_debug(rpc_type, action_type, peer_id, mod_name, rpc_name, data) - if vmf.network_debug then + if _NETWORK_DEBUG then local debug_message = nil @@ -196,7 +198,7 @@ VMFMod.network_send = function (self, rpc_name, recipient, ...) if recipient == "all" then for peer_id, _ in pairs(_VMF_USERS) do - send_rpc_vmf_data(peer_id, self:get_name(), rpc_name, ...) + send_rpc_vmf_data(peer_id, self:get_name(), rpc_name, ...) end send_rpc_vmf_data_local(self:get_name(), rpc_name, ...) @@ -241,7 +243,7 @@ vmf:hook("ChatManager.rpc_chat_message", function(func, self, sender, channel_id elseif channel_id == 4 then -- rpc_vmf_responce (@TODO: maybe I should protect it from sending by the player who's not in the game?) network_debug("pong", "received", sender) - if vmf.network_debug then + if _NETWORK_DEBUG then vmf:info("[RECEIVED MODS TABLE]: " .. message) vmf:info("[RECEIVED RPCS TABLE]: " .. localization_param) end @@ -329,8 +331,6 @@ end) -- ##### VMF internal functions and variables ######################################################################### -- #################################################################################################################### -vmf.network_debug = vmf:get("developer_mode") and vmf:get("show_network_debug_info") - vmf.create_network_dictionary = function() _SHARED_MODS_MAP = {} @@ -374,4 +374,14 @@ vmf.ping_vmf_users = function() end end end -end \ No newline at end of file +end + +vmf.load_network_settings = function() + _NETWORK_DEBUG = vmf:get("developer_mode") and vmf:get("show_network_debug_info") +end + +-- #################################################################################################################### +-- ##### Script ####################################################################################################### +-- #################################################################################################################### + +vmf.load_network_settings() \ No newline at end of file diff --git a/vmf/scripts/mods/vmf/modules/core/settings.lua b/vmf/scripts/mods/vmf/modules/core/settings.lua index 88957ff..1385590 100644 --- a/vmf/scripts/mods/vmf/modules/core/settings.lua +++ b/vmf/scripts/mods/vmf/modules/core/settings.lua @@ -29,7 +29,7 @@ end -- #################################################################################################################### --[[ - * setting_name [string] : setting name, can contain any characters lua-string can @TODO: check this + * setting_name [string] : setting name, can contain any characters lua-string can * setting_value [anything]: setting value, will be serialized to SJSON format, so you can save whole tables * call_setting_changed_event [bool] : if 'true', when some setting will be changed, 'setting_changed' event will be called (if mod defined one) --]] @@ -53,7 +53,7 @@ VMFMod.set = function (self, setting_name, setting_value, call_setting_changed_e end --[[ - * setting_name [string]: setting name, can contain any characters lua-string can @TODO: check this + * setting_name [string]: setting name, can contain any characters lua-string can --]] VMFMod.get = function (self, setting_name) diff --git a/vmf/scripts/mods/vmf/modules/debug/dev_console.lua b/vmf/scripts/mods/vmf/modules/debug/dev_console.lua index 3e36c97..3ad7f9d 100644 --- a/vmf/scripts/mods/vmf/modules/debug/dev_console.lua +++ b/vmf/scripts/mods/vmf/modules/debug/dev_console.lua @@ -36,6 +36,19 @@ local function close_dev_console() print = PRINT_ORIGINAL_FUNCTION CommandWindow.close() + + -- CommandWindow won't close by itself, so it have to be closed manually + vmf:pcall(function() + local ffi = require("ffi") + ffi.cdef([[ + void* FindWindowA(const char* lpClassName, const char* lpWindowName); + int64_t SendMessageA(void* hWnd, unsigned int Msg, uint64_t wParam, int64_t lParam); + ]]) + local WM_CLOSE = 0x10; + local hwnd = ffi.C.FindWindowA("ConsoleWindowClass", "Developer console") + ffi.C.SendMessageA(hwnd, WM_CLOSE, 0, 0) + end) + DEV_CONSOLE_ENABLED = false end end @@ -44,9 +57,26 @@ end -- ##### VMF internal functions and variables ######################################################################### -- #################################################################################################################### -vmf.toggle_developer_console = function (open_console) +vmf.toggle_developer_console = function () - if open_console then + if vmf:get("developer_mode") then + + local show_console = not vmf:get("show_developer_console") + vmf:set("show_developer_console", show_console) + + vmf.load_dev_console_settings() + + if show_console then + vmf:echo(vmf:localize("dev_console_opened")) + else + vmf:echo(vmf:localize("dev_console_closed")) + end + end +end + +vmf.load_dev_console_settings = function() + + if vmf:get("developer_mode") and vmf:get("show_developer_console") then open_dev_console() else close_dev_console() @@ -57,7 +87,4 @@ end -- ##### Script ####################################################################################################### -- #################################################################################################################### -if vmf:get("developer_mode") and vmf:get("show_developer_console") then - open_dev_console() -end - +vmf.load_dev_console_settings() \ No newline at end of file diff --git a/vmf/scripts/mods/vmf/modules/gui/custom_textures.lua b/vmf/scripts/mods/vmf/modules/gui/custom_textures.lua index 0cc096f..287d3c8 100644 --- a/vmf/scripts/mods/vmf/modules/gui/custom_textures.lua +++ b/vmf/scripts/mods/vmf/modules/gui/custom_textures.lua @@ -7,6 +7,8 @@ local _CUSTOM_UI_ATLAS_SETTINGS = {} local _INJECTED_MATERIALS = {} +local _SHOW_DEBUG_INFO = false + -- #################################################################################################################### -- ##### Local functions ############################################################################################## -- #################################################################################################################### @@ -130,10 +132,15 @@ VMFMod.inject_materials = function (self, ui_renderer_creator, ...) _INJECTED_MATERIALS[ui_renderer_creator] = injected_materials_list -- recreate GUIs with injected materials for ui_renderers created by 'ui_renderer_creator' - for ui_renderer, _ in pairs(UI_RENDERERS) do - if ui_renderer.vmf_data.ui_renderer_creator == ui_renderer_creator then + local vmf_data - local new_materials_list = table.clone(ui_renderer.vmf_data.original_materials) + for ui_renderer, _ in pairs(UI_RENDERERS) do + + vmf_data = rawget(ui_renderer, "vmf_data") + + if vmf_data.ui_renderer_creator == ui_renderer_creator then + + local new_materials_list = table.clone(vmf_data.original_materials) for _, injected_material in ipairs(injected_materials_list) do table.insert(new_materials_list, "material") @@ -192,7 +199,7 @@ vmf:hook("UIRenderer.create", function(func, world, ...) -- DEBUG INFO - if vmf.custom_textures_debug then + if _SHOW_DEBUG_INFO then vmf:info("UI_RENDERER CREATED BY:") vmf:info(" %s", ui_renderer_creator) vmf:info("UI_RENDERER MATERIALS:") @@ -203,29 +210,19 @@ vmf:hook("UIRenderer.create", function(func, world, ...) -- CREATING THE NEW UI_RENDERER AND SAVING SOME DATA INSIDE OF IT - ui_renderer_creating = true local ui_renderer = func(world, unpack(ui_renderer_materials)) UI_RENDERERS[ui_renderer] = true - ui_renderer.vmf_data.original_materials = {...} - ui_renderer.vmf_data.ui_renderer_creator = ui_renderer_creator + local vmf_data = {} + vmf_data.original_materials = {...} + vmf_data.ui_renderer_creator = ui_renderer_creator + rawset(ui_renderer, "vmf_data", vmf_data) return ui_renderer end) -vmf:hook("MakeTableStrict", function(func, t) - - if ui_renderer_creating then - t.vmf_data = {} - ui_renderer_creating = false - end - - return func(t) -end) - - vmf:hook("UIRenderer.destroy", function(func, self, world) UI_RENDERERS[self] = nil @@ -261,4 +258,12 @@ end) -- ##### VMF internal functions and variables ######################################################################### -- #################################################################################################################### -vmf.custom_textures_debug = vmf:get("developer_mode") and vmf:get("log_ui_renderers_info") \ No newline at end of file +vmf.load_custom_textures_settings = function() + _SHOW_DEBUG_INFO = vmf:get("developer_mode") and vmf:get("log_ui_renderers_info") +end + +-- #################################################################################################################### +-- ##### Script ####################################################################################################### +-- #################################################################################################################### + +vmf.load_custom_textures_settings() \ No newline at end of file diff --git a/vmf/scripts/mods/vmf/modules/gui/ui_scaling.lua b/vmf/scripts/mods/vmf/modules/gui/ui_scaling.lua index f2dcd39..725170f 100644 --- a/vmf/scripts/mods/vmf/modules/gui/ui_scaling.lua +++ b/vmf/scripts/mods/vmf/modules/gui/ui_scaling.lua @@ -1,28 +1,49 @@ +-- If enabled, scale UI for resolutions greater than 1080p when necessary. Reports to a global when active, so that existing scaling can be disabled. local vmf = get_mod("VMF") --- If enabled, scale UI for resolutions greater than 1080p when necessary. Reports to a global when active, so that existing scaling can be disabled. -local ui_resolution = UIResolution -local ui_resolution_width_fragments = UIResolutionWidthFragments -local ui_resolution_height_fragments = UIResolutionHeightFragments -local math_min = math.min -local raw_set = rawset +local _UI_RESOLUTION = UIResolution +local _UI_RESOLUTION_WIDTH_FRAGMENTS = UIResolutionWidthFragments +local _UI_RESOLUTION_HEIGHT_FRAGMENTS = UIResolutionHeightFragments +local _MATH_MIN = math.min + +local _UI_SCALING_ENABLED + +-- #################################################################################################################### +-- ##### Hooks ######################################################################################################## +-- #################################################################################################################### vmf:hook("UIResolutionScale", function (func, ...) - local w, h = ui_resolution() + local w, h = _UI_RESOLUTION() - if (w > ui_resolution_width_fragments() and h > ui_resolution_height_fragments() and vmf:get("ui_scaling")) then + if (w > _UI_RESOLUTION_WIDTH_FRAGMENTS() and h > _UI_RESOLUTION_HEIGHT_FRAGMENTS() and _UI_SCALING_ENABLED) then local max_scaling_factor = 4 - local width_scale = math_min(w / ui_resolution_width_fragments(), max_scaling_factor) -- Changed to allow scaling up to quadruple the original max scale (1 -> 4) - local height_scale = math_min(h / ui_resolution_height_fragments(), max_scaling_factor) -- Changed to allow scaling up to quadruple the original max scale (1 -> 4) + local width_scale = _MATH_MIN(w / _UI_RESOLUTION_WIDTH_FRAGMENTS(), max_scaling_factor) -- Changed to allow scaling up to quadruple the original max scale (1 -> 4) + local height_scale = _MATH_MIN(h / _UI_RESOLUTION_HEIGHT_FRAGMENTS(), max_scaling_factor) -- Changed to allow scaling up to quadruple the original max scale (1 -> 4) - raw_set(_G, "vmf_hd_ui_scaling_enabled", true) - return math_min(width_scale, height_scale) + return _MATH_MIN(width_scale, height_scale) else - - raw_set(_G, "vmf_hd_ui_scaling_enabled", false) return func(...) end -end) \ No newline at end of file +end) + +-- #################################################################################################################### +-- ##### VMF internal functions and variables ######################################################################### +-- #################################################################################################################### + +vmf.load_ui_scaling_settings = function () + _UI_SCALING_ENABLED = vmf:get("ui_scaling") + if _UI_SCALING_ENABLED then + RESOLUTION_LOOKUP.ui_scaling = true + else + RESOLUTION_LOOKUP.ui_scaling = false + end +end + +-- #################################################################################################################### +-- ##### Script ####################################################################################################### +-- #################################################################################################################### + +vmf.load_ui_scaling_settings() \ No newline at end of file diff --git a/vmf/scripts/mods/vmf/modules/testing_stuff_here.lua b/vmf/scripts/mods/vmf/modules/testing_stuff_here.lua index 377aa2f..e4d444f 100644 --- a/vmf/scripts/mods/vmf/modules/testing_stuff_here.lua +++ b/vmf/scripts/mods/vmf/modules/testing_stuff_here.lua @@ -109,6 +109,12 @@ local options_widgets = { mod:create_options(options_widgets, true, "Test", "Mod description") +mod:command("whatever", "description whatever", function() mod:echo("whatever") end) +mod:command("what", "description what", function() mod:echo("what") end) +mod:command("wh", "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse tincidunt placerat nulla eget pharetra. Vivamus consequat tristique vestibulum. Nullam vitae feugiat arcu, non porta ante. Phasellus consequat facilisis quam quis dignissim.", function(a, b) mod:echo("wh, " .. tostring(a) .. ", " .. tostring(b)) end) +mod:command("whoa", "description whoa", function() mod:echo("whoa") end) +mod:command("wheat", "description wheat", function() mod:echo("wheat") end) +mod:command("test", "short command description\n params: [parameter1] [parameter2]\nparameter1 - string\nparameter2 - number", function(p1, p2) mod:echo("Test function executed.\nParameter1: " .. tostring(p1) .. "\nParameter2: " .. tostring(p2)) end) -- chat_broadcast mod.whatever = function () --mod:echo("whatever") diff --git a/vmf/scripts/mods/vmf/modules/ui/chat/chat_actions.lua b/vmf/scripts/mods/vmf/modules/ui/chat/chat_actions.lua new file mode 100644 index 0000000..7603939 --- /dev/null +++ b/vmf/scripts/mods/vmf/modules/ui/chat/chat_actions.lua @@ -0,0 +1,276 @@ +--[[ + * chat commands + * chat history + * ctrl + c, ctrl + v +]] +local vmf = get_mod("VMF") + +local _CHAT_OPENED = false + +local _COMMANDS_LIST = {} +local _COMMAND_INDEX = 0 -- 0 => nothing selected + +local _COMMANDS_LIST_GUI_DRAW = nil + +local _CHAT_HISTORY = {} +local _CHAT_HISTORY_INDEX = 0 +local _CHAT_HISTORY_ENABLED = true +local _CHAT_HISTORY_SAVE = true +local _CHAT_HISTORY_MAX = 50 +local _CHAT_HISTORY_REMOVE_DUPS_LAST = false +local _CHAT_HISTORY_REMOVE_DUPS_ALL = false +local _CHAT_HISTORY_SAVE_COMMANDS_ONLY = false + +-- #################################################################################################################### +-- ##### Local functions ############################################################################################## +-- #################################################################################################################### + +local function clean_chat_history() + _CHAT_HISTORY = {} + _CHAT_HISTORY_INDEX = 0 +end + +-- #################################################################################################################### +-- ##### Hooks ######################################################################################################## +-- #################################################################################################################### + +vmf:hook("WorldManager.create_world", function(func, self, name, ...) + local world = func(self, name, ...) + + if name == "top_ingame_view" then + _COMMANDS_LIST_GUI_DRAW = dofile("scripts/mods/vmf/modules/ui/chat/commands_list_gui") + end + + return world +end) + + +vmf:hook("ChatGui.block_input", function(func, ...) + func(...) + + _CHAT_OPENED = true +end) + + +vmf:hook("ChatGui._update_input", function(func, self, input_service, menu_input_service, dt, no_unblock, chat_enabled) + + local command_executed = false + + -- if ENTER was pressed + if Keyboard.pressed(Keyboard.button_index("enter")) then + + -- chat history + if _CHAT_HISTORY_ENABLED + and self.chat_message ~= "" + and not (_CHAT_HISTORY_REMOVE_DUPS_LAST and (self.chat_message == _CHAT_HISTORY[1])) + and (not _CHAT_HISTORY_SAVE_COMMANDS_ONLY or (_COMMAND_INDEX ~= 0)) then + table.insert(_CHAT_HISTORY, 1, self.chat_message) + + if #_CHAT_HISTORY == _CHAT_HISTORY_MAX + 1 then + table.remove(_CHAT_HISTORY, #_CHAT_HISTORY) + end + + if _CHAT_HISTORY_REMOVE_DUPS_ALL then + + for i = 2, #_CHAT_HISTORY do + if _CHAT_HISTORY[i] == self.chat_message then + table.remove(_CHAT_HISTORY, i) + break + end + end + end + end + + -- command execution + if _COMMAND_INDEX ~= 0 then + local args = {} + for arg in string.gmatch(self.chat_message, "%S+") do + table.insert(args, arg) + end + table.remove(args, 1) + + vmf.run_command(_COMMANDS_LIST[_COMMAND_INDEX].name, unpack(args)) + + _COMMANDS_LIST = {} + _COMMAND_INDEX = 0 + + self.chat_message = "" + command_executed = true + end + end + + local old_chat_message = self.chat_message + + local chat_focused, chat_closed, chat_close_time = func(self, input_service, menu_input_service, dt, no_unblock, chat_enabled) + + if chat_closed then + self.chat_message = "" + + _CHAT_OPENED = false + + _COMMANDS_LIST = {} + _COMMAND_INDEX = 0 + _CHAT_HISTORY_INDEX = 0 + + if command_executed then + chat_closed = false + chat_close_time = nil + end + end + + if _CHAT_OPENED then + + -- getting state of 'tab', 'arrow up' and 'arrow down' buttons + local tab_pressed = false + local arrow_up_pressed = false + local arrow_down_pressed = false + for _, stroke in ipairs(Keyboard.keystrokes()) do + if stroke == Keyboard.TAB then + tab_pressed = true + -- game considers some "ctrl + [something]" combinations as arrow buttons, so I have to check for ctrl not pressed + elseif stroke == Keyboard.UP and Keyboard.button(Keyboard.button_index("left ctrl")) == 0 then + arrow_up_pressed = true + elseif stroke == Keyboard.DOWN and Keyboard.button(Keyboard.button_index("left ctrl")) == 0 then + arrow_down_pressed = true + end + end + + -- chat history + if _CHAT_HISTORY_ENABLED then + -- message was modified by player + if self.chat_message ~= self.previous_chat_message then + _CHAT_HISTORY_INDEX = 0 + end + if arrow_up_pressed or arrow_down_pressed then + + local new_index = _CHAT_HISTORY_INDEX + (arrow_up_pressed and 1 or -1) + new_index = math.clamp(new_index, 0, #_CHAT_HISTORY) + + if _CHAT_HISTORY_INDEX ~= new_index then + if _CHAT_HISTORY[new_index] then + + self.chat_message = _CHAT_HISTORY[new_index] + self.chat_index = KeystrokeHelper.num_utf8chars(self.chat_message) + 1 + self.chat_input_widget.content.text_index = 1 + + self.previous_chat_message = self.chat_message + + _CHAT_HISTORY_INDEX = new_index + else -- new_index == 0 + self.chat_message = "" + self.chat_index = 1 + end + end + end + end + + -- ctrl + v + if Keyboard.pressed(Keyboard.button_index("v")) and Keyboard.button(Keyboard.button_index("left ctrl")) == 1 then + self.chat_message = self.chat_message .. tostring(Clipboard.get()):gsub(string.char(0x0D), "") -- remove CR characters + self.chat_index = KeystrokeHelper.num_utf8chars(self.chat_message) + 1 + end + + -- ctrl + c + if Keyboard.pressed(Keyboard.button_index("c")) and Keyboard.button(Keyboard.button_index("left ctrl")) == 1 then + Clipboard.put(self.chat_message) + end + + -- entered chat message starts with "/" + if string.sub(self.chat_message, 1, 1) == "/" then + + if not string.find(self.chat_message, " ") -- if there's no space after '/part_of_command_name' + and tab_pressed -- if TAB was pressed + and (string.len(self.chat_message) + 1) == self.chat_index -- if TAB was pressed with caret at the end of the string + and (#_COMMANDS_LIST > 0) then -- if there are any commands matching entered '/part_of_command_name' + + _COMMAND_INDEX = _COMMAND_INDEX % #_COMMANDS_LIST + 1 + + self.chat_message = "/" .. _COMMANDS_LIST[_COMMAND_INDEX].name + self.chat_index = KeystrokeHelper.num_utf8chars(self.chat_message) + 1 + + -- so the next block won't update the commands list + old_chat_message = self.chat_message + end + + + if self.chat_message ~= old_chat_message then + + -- get '/part_of_command_name' without '/' + local command_name_contains = self.chat_message:match("%S+"):sub(2, -1) + + if string.find(self.chat_message, " ") then + _COMMANDS_LIST = vmf.get_commands_list(command_name_contains, true) + else + _COMMANDS_LIST = vmf.get_commands_list(command_name_contains) + end + + _COMMAND_INDEX = 0 + + if #_COMMANDS_LIST > 0 and command_name_contains:lower() == _COMMANDS_LIST[1].name then + _COMMAND_INDEX = 1 + end + end + + + -- chat message was modified and doesn't start with '/' + elseif self.chat_message ~= old_chat_message and #_COMMANDS_LIST > 0 then + _COMMANDS_LIST = {} + _COMMAND_INDEX = 0 + end + + if #_COMMANDS_LIST > 0 then + _COMMANDS_LIST_GUI_DRAW(_COMMANDS_LIST, _COMMAND_INDEX) + end + end + + return chat_focused, chat_closed, chat_close_time +end) + +-- #################################################################################################################### +-- ##### VMF internal functions and variables ######################################################################### +-- #################################################################################################################### + +vmf.load_chat_history_settings = function(clean_chat_history_) + + _CHAT_HISTORY_ENABLED = vmf:get("chat_history_enable") + _CHAT_HISTORY_SAVE = vmf:get("chat_history_save") + _CHAT_HISTORY_MAX = vmf:get("chat_history_buffer_size") + _CHAT_HISTORY_REMOVE_DUPS_LAST = vmf:get("chat_history_remove_dups") + _CHAT_HISTORY_REMOVE_DUPS_ALL = vmf:get("chat_history_remove_dups") and (vmf:get("chat_history_remove_dups_mode") == "all") + _CHAT_HISTORY_SAVE_COMMANDS_ONLY = vmf:get("chat_history_commands_only") + + if _CHAT_HISTORY_ENABLED then + vmf:command("clean_chat_history", vmf:localize("clean_chat_history"), clean_chat_history) + else + vmf:command_remove("clean_chat_history") + end + + if not _CHAT_HISTORY_SAVE then + vmf:set("chat_history", nil) + end + + if clean_chat_history_ then + clean_chat_history() + end +end + +vmf.save_chat_history = function() + if _CHAT_HISTORY_SAVE then + vmf:set("chat_history", _CHAT_HISTORY) + end +end + +-- #################################################################################################################### +-- ##### Script ####################################################################################################### +-- #################################################################################################################### + +vmf.load_chat_history_settings() + +if _CHAT_HISTORY_SAVE then + _CHAT_HISTORY = vmf:get("chat_history") or _CHAT_HISTORY +end + +if Managers.world:has_world("top_ingame_view") then + -- @TODO: move this to local function? + _COMMANDS_LIST_GUI_DRAW = dofile("scripts/mods/vmf/modules/ui/chat/commands_list_gui") +end \ No newline at end of file diff --git a/vmf/scripts/mods/vmf/modules/ui/chat/commands_list_gui.lua b/vmf/scripts/mods/vmf/modules/ui/chat/commands_list_gui.lua new file mode 100644 index 0000000..8e4e8f0 --- /dev/null +++ b/vmf/scripts/mods/vmf/modules/ui/chat/commands_list_gui.lua @@ -0,0 +1,142 @@ +local vmf = get_mod("VMF") --@TODO: remove it? + +local _GUI = World.create_screen_gui(Managers.world:world("top_ingame_view"), "immediate", "material", "materials/fonts/gw_fonts", "material", "materials/ui/ui_1080p_ingame_common") + +local _FONT_TYPE = "hell_shark_arial" +local _FONT_SIZE = 22 + +local _MULTISTRING_INDICATOR_TEXT = "[...]" + +local _MAX_COMMANDS_VISIBLE = 5 + +local _STRING_HEIGHT = 25 +local _STRING_Y_OFFSET = 7 +local _STRING_X_MARGIN = 10 + +local _OFFSET_X = 0 +local _OFFSET_Y = 200 +local _OFFSET_Z = 880 +local _WIDTH = 550 + +-- #################################################################################################################### +-- ##### Local functions ############################################################################################## +-- #################################################################################################################### + +local function get_text_width(text, font_material, font_size) + local text_extent_min, text_extent_max = Gui.text_extents(_GUI, text, font_material, font_size) + local text_height = text_extent_max[1] - text_extent_min[1] + return text_height +end + +local function word_wrap(text, font_material, font_size, max_width) + local whitespace = " " + local soft_dividers = "-+&/*" + local return_dividers = "\n" + local reuse_global_table = true + local scale = RESOLUTION_LOOKUP.scale + + return Gui.word_wrap(_GUI, text, font_material, font_size, max_width * scale, whitespace, soft_dividers, return_dividers, reuse_global_table) +end + +local function draw(commands_list, selected_command_index) + --vmf:pcall(function() + + local selected_command_new_index = 0 + + -- pick displayed commands + local last_displayed_command = math.max(math.min(_MAX_COMMANDS_VISIBLE, #commands_list), selected_command_index) + local first_displayed_command = math.max(1, last_displayed_command - (_MAX_COMMANDS_VISIBLE - 1)) + local displayed_commands = {} + for i = first_displayed_command, last_displayed_command do + local new_entry = {} + new_entry.name = "/" .. commands_list[i].name + new_entry.description = commands_list[i].description + new_entry.full_text = new_entry.name .. " " .. new_entry.description + if i == selected_command_index then + new_entry.selected = true + selected_command_new_index = #displayed_commands + 1 + end + table.insert(displayed_commands, new_entry) + end + + local scale = RESOLUTION_LOOKUP.scale + + local selected_strings_number = 1 + + local font, font_size = UIFontByResolution({font_type = _FONT_TYPE, font_size = _FONT_SIZE}) + local font_material = font[1] + local font_name = font[3] + + for i, command in ipairs(displayed_commands) do + + -- draw "/command_name" text + local string_position = Vector3((_OFFSET_X + _STRING_X_MARGIN) * scale, (_OFFSET_Y - _STRING_HEIGHT * (i + selected_strings_number - 1) + _STRING_Y_OFFSET) * scale, _OFFSET_Z + 2) + Gui.text(_GUI, command.name, font_material, font_size, font_name, string_position, Color(255, 100, 255, 100)) + + local command_text_strings = word_wrap(command.full_text, font_material, font_size, _WIDTH - _STRING_X_MARGIN * 2) + local multistring = #command_text_strings > 1 + local first_description_string + if multistring then + if command.selected then + selected_strings_number = #command_text_strings + else + local multistring_indicator_width = get_text_width(_MULTISTRING_INDICATOR_TEXT, font_material, font_size) + command_text_strings = word_wrap(command.full_text, font_material, font_size, _WIDTH - _STRING_X_MARGIN * 2 - (multistring_indicator_width / scale)) + + -- draw that [...] thing + local multistring_indicator_position = Vector3((_OFFSET_X + _WIDTH) * scale - multistring_indicator_width, string_position.y, string_position.z) + Gui.text(_GUI, _MULTISTRING_INDICATOR_TEXT, font_material, font_size, font_name, multistring_indicator_position, Color(255, 100, 100, 100)) + end + first_description_string = string.sub(command_text_strings[1], #command.name + 2) + else + first_description_string = command.description + end + + -- draw command description text (1st string) + local first_description_string_width = get_text_width(command.name, font_material, font_size) + local first_description_string_position = Vector3(string_position.x + first_description_string_width, string_position.y, string_position.z) + Gui.text(_GUI, first_description_string, font_material, font_size, font_name, first_description_string_position, Color(255, 255, 255, 255)) + + -- draw command description text (2+ strings) + if command.selected and multistring then + for j = 2, selected_strings_number do + string_position.y = string_position.y - _STRING_HEIGHT * scale + Gui.text(_GUI, command_text_strings[j], font_material, font_size, font_name, string_position, Color(255, 255, 255, 255)) + end + end + end + + -- background rectangle + local bg_height = _STRING_HEIGHT * (#displayed_commands + selected_strings_number - 1) + local bg_pos_y = _OFFSET_Y - bg_height + + local bg_position = Vector3(_OFFSET_X * scale, bg_pos_y * scale, _OFFSET_Z) + local bg_size = Vector2(_WIDTH * scale, bg_height * scale) + local bg_color = Color(200, 10, 10, 10) + Gui.rect(_GUI, bg_position, bg_size, bg_color) + + -- selection rectangle + if selected_command_new_index > 0 then + local selection_height = _STRING_HEIGHT * selected_strings_number + local selection_pos_y = _OFFSET_Y - selection_height - _STRING_HEIGHT * (selected_command_new_index - 1) + + local selection_position = Vector3(_OFFSET_X * scale, selection_pos_y * scale, _OFFSET_Z + 1) + local selection_size = Vector2(_WIDTH * scale, selection_height * scale) + local selection_color = Color(100, 120, 120, 120) + Gui.rect(_GUI, selection_position, selection_size, selection_color) + end + + -- "selected command number / total commands number" indicator + if not ((#commands_list == 1) and (selected_command_index > 0)) then + local total_number_indicator = tostring(#commands_list) + if selected_command_index > 0 then + total_number_indicator = selected_command_index .. "/" .. total_number_indicator + end + local total_number_indicator_width = get_text_width(total_number_indicator, font_material, font_size) + local total_number_indicator_position = Vector3((_WIDTH) * scale - total_number_indicator_width, (_OFFSET_Y + _STRING_Y_OFFSET) * scale, _OFFSET_Z + 2) + Gui.text(_GUI, total_number_indicator, font_material, font_size, font_name, total_number_indicator_position, Color(255, 100, 100, 100)) + end + --end) +end + +return draw \ No newline at end of file diff --git a/vmf/scripts/mods/vmf/modules/mutators/README.md b/vmf/scripts/mods/vmf/modules/ui/mutators/README.md similarity index 100% rename from vmf/scripts/mods/vmf/modules/mutators/README.md rename to vmf/scripts/mods/vmf/modules/ui/mutators/README.md diff --git a/vmf/scripts/mods/vmf/modules/mutators/mutator_default_config.lua b/vmf/scripts/mods/vmf/modules/ui/mutators/mutator_default_config.lua similarity index 100% rename from vmf/scripts/mods/vmf/modules/mutators/mutator_default_config.lua rename to vmf/scripts/mods/vmf/modules/ui/mutators/mutator_default_config.lua diff --git a/vmf/scripts/mods/vmf/modules/mutators/mutator_dice.lua b/vmf/scripts/mods/vmf/modules/ui/mutators/mutator_dice.lua similarity index 100% rename from vmf/scripts/mods/vmf/modules/mutators/mutator_dice.lua rename to vmf/scripts/mods/vmf/modules/ui/mutators/mutator_dice.lua diff --git a/vmf/scripts/mods/vmf/modules/mutators/mutator_gui.lua b/vmf/scripts/mods/vmf/modules/ui/mutators/mutator_gui.lua similarity index 99% rename from vmf/scripts/mods/vmf/modules/mutators/mutator_gui.lua rename to vmf/scripts/mods/vmf/modules/ui/mutators/mutator_gui.lua index bf45802..12ab12c 100644 --- a/vmf/scripts/mods/vmf/modules/mutators/mutator_gui.lua +++ b/vmf/scripts/mods/vmf/modules/ui/mutators/mutator_gui.lua @@ -6,7 +6,7 @@ local mutators = manager.mutators manager:custom_textures("mutator_button", "mutator_button_hover") manager:inject_materials("ingame_ui", "materials/vmf/mutator_button", "materials/vmf/mutator_button_hover") -local definitions = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_gui_definitions") +local definitions = manager:dofile("scripts/mods/vmf/modules/ui/mutators/mutator_gui_definitions") local PER_PAGE = definitions.PER_PAGE diff --git a/vmf/scripts/mods/vmf/modules/mutators/mutator_gui_definitions.lua b/vmf/scripts/mods/vmf/modules/ui/mutators/mutator_gui_definitions.lua similarity index 100% rename from vmf/scripts/mods/vmf/modules/mutators/mutator_gui_definitions.lua rename to vmf/scripts/mods/vmf/modules/ui/mutators/mutator_gui_definitions.lua diff --git a/vmf/scripts/mods/vmf/modules/mutators/mutator_info.lua b/vmf/scripts/mods/vmf/modules/ui/mutators/mutator_info.lua similarity index 100% rename from vmf/scripts/mods/vmf/modules/mutators/mutator_info.lua rename to vmf/scripts/mods/vmf/modules/ui/mutators/mutator_info.lua diff --git a/vmf/scripts/mods/vmf/modules/mutators/mutator_manager.lua b/vmf/scripts/mods/vmf/modules/ui/mutators/mutator_manager.lua similarity index 96% rename from vmf/scripts/mods/vmf/modules/mutators/mutator_manager.lua rename to vmf/scripts/mods/vmf/modules/ui/mutators/mutator_manager.lua index d8f7678..a69aa5b 100644 --- a/vmf/scripts/mods/vmf/modules/mutators/mutator_manager.lua +++ b/vmf/scripts/mods/vmf/modules/ui/mutators/mutator_manager.lua @@ -11,7 +11,7 @@ local mutators = manager.mutators -- Table of mutators' configs by name local mutators_config = {} -local default_config = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_default_config") +local default_config = manager:dofile("scripts/mods/vmf/modules/ui/mutators/mutator_default_config") -- This lists mutators and which ones should be enabled after them -- This is populated via VMFMod.register_as_mutator @@ -35,9 +35,9 @@ local all_mutators_disabled = false PRIVATE METHODS ]]-- -local mutators_view = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_gui") -local dice_manager = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_dice") -local set_lobby_data = manager:dofile("scripts/mods/vmf/modules/mutators/mutator_info") +local mutators_view = manager:dofile("scripts/mods/vmf/modules/ui/mutators/mutator_gui") +local dice_manager = manager:dofile("scripts/mods/vmf/modules/ui/mutators/mutator_dice") +local set_lobby_data = manager:dofile("scripts/mods/vmf/modules/ui/mutators/mutator_info") -- Adds mutator names from enable_these_after to the list of mutators that should be enabled after the mutator_name local function update_mutators_sequence(mutator_name, enable_these_after) @@ -127,7 +127,7 @@ local function set_mutator_state(mutator, state) end if state and ( - not mutator:can_be_enabled() or + not mutator:can_be_enabled() or Managers.state and Managers.state.game_mode and Managers.state.game_mode._game_mode_key ~= "inn" ) then return @@ -177,7 +177,7 @@ end -- Checks if the player is server in a way that doesn't incorrectly return false during loading screens local function player_is_server() local player = Managers.player - local state = Managers.state + local state = Managers.state return not player or player.is_server or not state or state.game_mode == nil end @@ -272,7 +272,7 @@ end -- Appends, prepends and replaces the string with mutator titles manager.add_mutator_titles_to_string = function(_mutators, str, separator, short) - + if #_mutators == 0 then return str end local before = nil @@ -294,7 +294,7 @@ manager.add_mutator_titles_to_string = function(_mutators, str, separator, short else replace = added_name end - else + else if after then after = after .. separator .. added_name else @@ -454,6 +454,6 @@ mutators_view:init(mutators_view:get_map_view()) --[[ Testing --]] --- manager:dofile("scripts/mods/vmf/modules/mutators/mutator_test") --- manager:dofile("scripts/mods/vmf/modules/mutators/mutators/mutation") --- manager:dofile("scripts/mods/vmf/modules/mutators/mutators/deathwish") +-- manager:dofile("scripts/mods/vmf/modules/ui/mutators/mutator_test") +-- manager:dofile("scripts/mods/vmf/modules/ui/mutators/mutators/mutation") +-- manager:dofile("scripts/mods/vmf/modules/ui/mutators/mutators/deathwish") diff --git a/vmf/scripts/mods/vmf/modules/options_menu/vmf_options_view.lua b/vmf/scripts/mods/vmf/modules/ui/options/vmf_options_view.lua similarity index 99% rename from vmf/scripts/mods/vmf/modules/options_menu/vmf_options_view.lua rename to vmf/scripts/mods/vmf/modules/ui/options/vmf_options_view.lua index 53c75bb..e513374 100644 --- a/vmf/scripts/mods/vmf/modules/options_menu/vmf_options_view.lua +++ b/vmf/scripts/mods/vmf/modules/ui/options/vmf_options_view.lua @@ -2355,6 +2355,8 @@ end local SETTINGS_LIST_WIDGETS_DEFINITIONS = {} -- numerical sorting [ipairs] +local _DEFAULT_SCROLL_STEP = 40 +local _SCROLL_STEP -- #################################################################################################################### -- ##### INITIALIZATION ############################################################################################### @@ -2366,14 +2368,11 @@ VMFOptionsView.init = function (self, ingame_ui_context) self.current_setting_list_offset_y = 0 - self.default_scroll_step = 40 - self.scroll_step = self.default_scroll_step / 100 * (vmf:get("vmf_options_scrolling_speed") or 100) - self.is_setting_changes_applied_immidiately = true self.definitions = {} self.definitions.scenegraph = scenegraph_definition - self.definitions.scenegraph_2nd_layer = {} + self.definitions.scenegraph_2nd_layer = {} self.definitions.menu_widgets = menu_widgets_definition self.definitions.settings_list_widgets = SETTINGS_LIST_WIDGETS_DEFINITIONS @@ -3564,7 +3563,7 @@ VMFOptionsView.update_mousewheel_scroll_area_input = function (self) if mouse_scroll_value ~= 0 then - local new_offset = self.current_setting_list_offset_y + mouse_scroll_value * self.scroll_step + local new_offset = self.current_setting_list_offset_y + mouse_scroll_value * _SCROLL_STEP self.current_setting_list_offset_y = math.clamp(new_offset, 0, self.max_setting_list_offset_y) @@ -3833,6 +3832,33 @@ VMFOptionsView.unsuspend = function (self) self.input_manager:block_device_except_service("vmf_options_menu", "gamepad", 1) end +-- #################################################################################################################### +-- ##### VMF internal functions and variables ######################################################################### +-- #################################################################################################################### + +vmf.load_vmf_options_view_settings = function() + if vmf:get("vmf_options_scrolling_speed") then + _SCROLL_STEP = _DEFAULT_SCROLL_STEP / 100 * vmf:get("vmf_options_scrolling_speed") + end +end + +-- #################################################################################################################### +-- ##### Script ####################################################################################################### +-- #################################################################################################################### + +vmf.load_vmf_options_view_settings() + + + + + + + + + + + + @@ -3920,7 +3946,7 @@ VMFMod.create_options = function (self, widgets_definition, is_mod_toggable, rea new_widget_definition.widget_type = current_widget.widget_type -- all new_widget_definition.widget_index = new_widget_index -- all [gen] new_widget_definition.widget_level = level -- all [gen] - new_widget_definition.mod_name = self:get_name() -- all [gen] + new_widget_definition.mod_name = self:get_name() -- all [gen] new_widget_definition.setting_name = current_widget.setting_name -- all new_widget_definition.text = current_widget.text -- all new_widget_definition.tooltip = current_widget.tooltip -- all [optional] @@ -4011,43 +4037,6 @@ VMFMod.create_options = function (self, widgets_definition, is_mod_toggable, rea table.insert(SETTINGS_LIST_WIDGETS_DEFINITIONS, mod_settings_list_widgets_definitions) end - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if type(vmf:get("options_menu_favorite_mods")) ~= "table" then vmf:set("options_menu_favorite_mods", {}) end diff --git a/vmf/scripts/mods/vmf/modules/vmf_options.lua b/vmf/scripts/mods/vmf/modules/vmf_options.lua index 6ec82ef..ca662a2 100644 --- a/vmf/scripts/mods/vmf/modules/vmf_options.lua +++ b/vmf/scripts/mods/vmf/modules/vmf_options.lua @@ -42,6 +42,13 @@ local options_widgets = { vmf:localize("show_developer_console_tooltip"), ["default_value"] = false }, + { + ["setting_name"] = "toggle_developer_console", + ["widget_type"] = "keybind", + ["text"] = vmf:localize("toggle_developer_console"), + ["default_value"] = {}, + ["action"] = "toggle_developer_console" + }, { ["setting_name"] = "show_network_debug_info", ["widget_type"] = "checkbox", @@ -57,14 +64,7 @@ local options_widgets = { ["tooltip"] = vmf:localize("log_ui_renderers_info") .. "\n" .. vmf:localize("log_ui_renderers_info_tooltip"), ["default_value"] = false - }, --- { --- ["setting_name"] = "toggle_developer_console", --- ["widget_type"] = "keybind", --- ["text"] = "Toggle Developer Console", --- ["default_value"] = {}, --- ["action"] = "toggle_developer_console" --- } + } } }, { @@ -148,79 +148,139 @@ local options_widgets = { ["default_value"] = 0 } } + }, + { + ["setting_name"] = "chat_history_enable", + ["widget_type"] = "checkbox", + ["text"] = vmf:localize("chat_history_enable"), + ["tooltip"] = vmf:localize("chat_history_enable") .. "\n" .. + vmf:localize("chat_history_enable_tooltip"), + ["default_value"] = true, + ["sub_widgets"] = { + { + ["setting_name"] = "chat_history_save", + ["widget_type"] = "checkbox", + ["text"] = vmf:localize("chat_history_save"), + ["tooltip"] = vmf:localize("chat_history_save") .. "\n" .. + vmf:localize("chat_history_save_tooltip"), + ["default_value"] = true + }, + { + ["setting_name"] = "chat_history_buffer_size", + ["widget_type"] = "numeric", + ["text"] = vmf:localize("chat_history_buffer_size"), + ["tooltip"] = vmf:localize("chat_history_buffer_size") .. "\n" .. + vmf:localize("chat_history_buffer_size_tooltip"), + ["range"] = {10, 200}, + ["default_value"] = 50 + }, + { + ["setting_name"] = "chat_history_remove_dups", + ["widget_type"] = "checkbox", + ["text"] = vmf:localize("chat_history_remove_dups"), + ["default_value"] = false, + ["sub_widgets"] = { + { + ["setting_name"] = "chat_history_remove_dups_mode", + ["widget_type"] = "dropdown", + ["text"] = vmf:localize("chat_history_remove_dups_mode"), + ["tooltip"] = vmf:localize("chat_history_remove_dups_mode") .. "\n" .. + vmf:localize("chat_history_remove_dups_mode_tooltip"), + ["options"] = { + {text = vmf:localize("settings_last"), value = "last"}, + {text = vmf:localize("settings_all"), value = "all"}, + }, + ["default_value"] = "last" + } + } + }, + { + ["setting_name"] = "chat_history_commands_only", + ["widget_type"] = "checkbox", + ["text"] = vmf:localize("chat_history_commands_only"), + ["tooltip"] = vmf:localize("chat_history_commands_only") .. "\n" .. + vmf:localize("chat_history_commands_only_tooltip"), + ["default_value"] = false + } + } } } -vmf:create_options(options_widgets, false, "Vermintide Mod Framework") + +-- #################################################################################################################### +-- ##### VMF internal functions and variables ######################################################################### +-- #################################################################################################################### vmf.on_setting_changed = function (setting_name) if setting_name == "vmf_options_scrolling_speed" then - local ingame_ui_exists, ingame_ui = pcall(function () return Managers.player.network_manager.matchmaking_manager.matchmaking_ui.ingame_ui end) - if ingame_ui_exists then - local vmf_options_view = ingame_ui.views["vmf_options_view"] - if vmf_options_view then - vmf_options_view.scroll_step = vmf_options_view.default_scroll_step / 100 * vmf:get(setting_name) - end - end + vmf.load_vmf_options_view_settings() elseif setting_name == "developer_mode" then - Managers.mod._settings.developer_mode = vmf:get(setting_name) - Application.set_user_setting("mod_settings", Managers.mod._settings) - - vmf.network_debug = vmf:get(setting_name) and vmf:get("show_network_debug_info") - vmf.custom_textures_debug = vmf:get(setting_name) and vmf:get("log_ui_renderers_info") - - local show_developer_console = vmf:get(setting_name) and vmf:get("show_developer_console") - vmf.toggle_developer_console(show_developer_console) + vmf.load_developer_mode_settings() + vmf.load_network_settings() + vmf.load_custom_textures_settings() + vmf.load_dev_console_settings() elseif setting_name == "show_developer_console" then - vmf.toggle_developer_console(vmf:get(setting_name)) + vmf.load_dev_console_settings() elseif setting_name == "show_network_debug_info" then - vmf.network_debug = vmf:get("developer_mode") and vmf:get(setting_name) + vmf.load_network_settings() elseif setting_name == "log_ui_renderers_info" then - vmf.custom_textures_debug = vmf:get("developer_mode") and vmf:get(setting_name) + vmf.load_custom_textures_settings() - elseif setting_name == "logging_mode" then + elseif setting_name == "ui_scaling" then + + vmf.load_ui_scaling_settings() + + elseif setting_name == "logging_mode" + or setting_name == "output_mode_echo" + or setting_name == "output_mode_error" + or setting_name == "output_mode_warning" + or setting_name == "output_mode_info" + or setting_name == "output_mode_debug" then vmf.load_logging_settings() - elseif setting_name == "output_mode_echo" then + elseif setting_name == "chat_history_enable" + or setting_name == "chat_history_save" + or setting_name == "chat_history_buffer_size" + or setting_name == "chat_history_remove_dups" + or setting_name == "chat_history_remove_dups_mode" + or setting_name == "chat_history_commands_only" then - vmf.load_logging_settings() - - elseif setting_name == "output_mode_error" then - - vmf.load_logging_settings() - - elseif setting_name == "output_mode_warning" then - - vmf.load_logging_settings() - - elseif setting_name == "output_mode_info" then - - vmf.load_logging_settings() - - elseif setting_name == "output_mode_debug" then - - vmf.load_logging_settings() + vmf.load_chat_history_settings(setting_name == "chat_history_enable" or setting_name == "chat_history_buffer_size" or setting_name == "chat_history_commands_only") end end +vmf.load_developer_mode_settings = function () --@TODO: maybe move it to somewhere else? + Managers.mod._settings.developer_mode = vmf:get("developer_mode") + Application.set_user_setting("mod_settings", Managers.mod._settings) +end + -- #################################################################################################################### -- ##### Script ####################################################################################################### -- #################################################################################################################### -local mod_developer_mode = Managers.mod._settings.developer_mode -local vmf_developer_mode = vmf:get("developer_mode") +vmf:create_options(options_widgets, false, "Vermintide Mod Framework") -if mod_developer_mode ~= vmf_developer_mode then - Managers.mod._settings.developer_mode = vmf_developer_mode - Application.set_user_setting("mod_settings", Managers.mod._settings) +-- first VMF initialization +-- it will be run only 1 time, when the player launch the game with VMF for the first time +if not vmf:get("vmf_initialized") then + + vmf.load_logging_settings() + vmf.load_developer_mode_settings() + vmf.load_network_settings() + vmf.load_custom_textures_settings() + vmf.load_dev_console_settings() + vmf.load_chat_history_settings() + vmf.load_ui_scaling_settings() + + vmf:set("vmf_initialized", true) end diff --git a/vmf/scripts/mods/vmf/vmf_loader.lua b/vmf/scripts/mods/vmf/vmf_loader.lua index cc45db3..49c9a4f 100644 --- a/vmf/scripts/mods/vmf/vmf_loader.lua +++ b/vmf/scripts/mods/vmf/vmf_loader.lua @@ -16,16 +16,24 @@ return { dofile("scripts/mods/vmf/modules/core/chat") dofile("scripts/mods/vmf/modules/core/localization") 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/ui_scaling") - dofile("scripts/mods/vmf/modules/options_menu/vmf_options_view") + dofile("scripts/mods/vmf/modules/ui/chat/chat_actions") + dofile("scripts/mods/vmf/modules/ui/options/vmf_options_view") dofile("scripts/mods/vmf/modules/vmf_options") - dofile("scripts/mods/vmf/modules/mutators/mutator_manager") + dofile("scripts/mods/vmf/modules/ui/mutators/mutator_manager") object.vmf = get_mod("VMF") + object.vmf:hook("ModManager.destroy", function(func, self) + + object.vmf.mods_unload_event(true) + func(self) + end) + -- temporary solution: dofile("scripts/mods/vmf/modules/testing_stuff_here") end, @@ -43,12 +51,16 @@ return { object.vmf.create_network_dictionary() object.vmf.ping_vmf_users() + object.vmf.all_mods_loaded_event() + object.vmf.all_mods_were_loaded = true end end, on_unload = function(object) print("VMF:ON_UNLOAD()") + object.vmf.save_chat_history() + object.vmf.save_unsaved_settings_to_file() object.vmf = nil end, @@ -59,7 +71,6 @@ return { object.vmf.delete_keybinds() object.vmf.mods_unload_event() object.vmf.hooks_unload() - object.vmf.save_unsaved_settings_to_file() end, on_game_state_changed = function(object, status, state)