Initial rewrite for Darktide
This commit is contained in:
parent
279169243f
commit
988ec944bc
65 changed files with 3788 additions and 7291 deletions
|
@ -1,37 +0,0 @@
|
|||
formats = {
|
||||
R8G8B8A8 = {compressed=false, alpha=true}
|
||||
A8R8G8B8 = {compressed=false, alpha=true}
|
||||
|
||||
R16G16B16A16F = {compressed=false, alpha=true}
|
||||
R32G32B32A32F = {compressed=false, alpha=true, hidden=true}
|
||||
|
||||
R32F = {compressed=false, alpha=true, hidden=true}
|
||||
R16F = {compressed=false, alpha=true, hidden=true}
|
||||
R16UNORM = {compressed=false, alpha=true, hidden=true}
|
||||
R16G16F = {compressed=false, alpha=true, hidden=true}
|
||||
|
||||
BC1 = {min_width=4, min_height=4, alpha=false, hidden=true}
|
||||
BC2 = {min_width=4, min_height=4, alpha=false, hidden=true}
|
||||
BC3 = {min_width=4, min_height=4, alpha=false, hidden=true}
|
||||
BC4 = {min_width=4, min_height=4, alpha=false}
|
||||
BC5 = {min_width=4, min_height=4, alpha=false}
|
||||
BC7 = {min_width=4, min_height=4, alpha=true}
|
||||
|
||||
DXT1 = {min_width=4, min_height=4, alpha=true}
|
||||
DXT1a = {min_width=4, min_height=4, alpha=true}
|
||||
DXT3 = {min_width=4, min_height=4, alpha=false}
|
||||
DXT5 = {min_width=4, min_height=4, alpha=true}
|
||||
DXT5n = {min_width=4, min_height=4, alpha=false}
|
||||
}
|
||||
|
||||
platforms = {
|
||||
win32 = {
|
||||
formats = [
|
||||
"R8G8B8A8", "A8R8G8B8",
|
||||
"R16G16B16A16F", "R32G32B32A32F",
|
||||
"R32F", "R16F", "R16UNORM", "R16G16F",
|
||||
"BC1", "BC2", "BC3", "BC4", "BC5",
|
||||
"DXT1", "DXT1a", "DXT3", "DXT5", "DXT5n"
|
||||
]
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,18 +0,0 @@
|
|||
common = {
|
||||
input = {
|
||||
filename = "gui/vmf/vmf_atlas"
|
||||
}
|
||||
output = {
|
||||
apply_processing = true
|
||||
correct_gamma = true
|
||||
cut_alpha_threshold = 0.5
|
||||
enable_cut_alpha_threshold = false
|
||||
format = "A8R8G8B8"
|
||||
mipmap_filter = "kaiser"
|
||||
mipmap_filter_wrap_mode = "mirror"
|
||||
mipmap_keep_original = false
|
||||
mipmap_num_largest_steps_to_discard = 0
|
||||
mipmap_num_smallest_steps_to_discard = 0
|
||||
srgb = true
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
title = "[Beta] Vermintide Mod Framework";
|
||||
description = "The latest VMF version for testing purposes. It is intended for modders. Regular players should ingore it and subscribe to [url=https://steamcommunity.com/sharedfiles/filedetails/?id=1289946781]stable version[/url] instead.";
|
||||
preview = "previewV1.jpg";
|
||||
content = "bundleV1";
|
||||
language = "english";
|
||||
visibility = "public";
|
||||
published_id = 1500136933L;
|
|
@ -1,26 +0,0 @@
|
|||
title = "Vermintide Mod Framework";
|
||||
description = "The Vermintide Mod Framework (VMF) is an open-source, community-run framework of modules that provides enhanced modding capabilities and support. The framework is designed to be both independent and lightweight; making no changes to gameplay on its own.
|
||||
|
||||
Mods created for the project may utilize:
|
||||
[list]
|
||||
[*]Mod options
|
||||
[*]Shared function hooks
|
||||
[*]Chat commands
|
||||
[*]Keybinds
|
||||
[*]Mutator support
|
||||
[*]Network calls
|
||||
[*]QHD+ UI re-scaling
|
||||
[*]Rewritten, lightweight mod functions
|
||||
[*]An on-event call system
|
||||
[/list]
|
||||
|
||||
The Vermintide Mod Framework originally started in Warhammer End Times: Vermintide as an unofficial modding platform. In the time since, VMF has been rewritten and redesigned with contributions from many unique members of the community; culminating in this unified project made for the arrival of official mod support.
|
||||
|
||||
If you're interested in creating mods with VMF, please check out [url=https://vmf-docs.verminti.de/]the project's wiki[/url].
|
||||
|
||||
If you'd like to contribute to the code behind VMF, visit [url=https://github.com/Vermintide-Mod-Framework/Vermintide-Mod-Framework]the project's GitHub repository[/url].";
|
||||
preview = "previewV1_stable.jpg";
|
||||
content = "bundleV1";
|
||||
language = "english";
|
||||
visibility = "public";
|
||||
published_id = 1289946781L;
|
|
@ -1,9 +0,0 @@
|
|||
title = "[Beta] Vermintide Mod Framework";
|
||||
description = "The latest VMF version for testing purposes. It is intended for modders. Regular players should ingore it and subscribe to [url=https://steamcommunity.com/sharedfiles/filedetails/?id=1369573612]stable version[/url] instead.";
|
||||
preview = "previewV2.png";
|
||||
content = "bundleV2";
|
||||
language = "english";
|
||||
visibility = "public";
|
||||
published_id = 1500112422L;
|
||||
apply_for_sanctioned_status = false;
|
||||
tags = ["Tools"];
|
|
@ -1,26 +0,0 @@
|
|||
title = "Vermintide Mod Framework";
|
||||
description = "The Vermintide Mod Framework (VMF) is an open-source, community-run framework of modules that provides enhanced modding capabilities and support. The framework is designed to be both independent and lightweight; making no changes to gameplay on its own.
|
||||
|
||||
Mods created for the project may utilize:
|
||||
[list]
|
||||
[*]Mod options
|
||||
[*]Shared function hooks
|
||||
[*]Chat commands
|
||||
[*]Keybinds
|
||||
[*]Mutator support (only available in Vermintide 1 at this time)
|
||||
[*]Network calls
|
||||
[*]Rewritten, lightweight mod functions
|
||||
[*]An on-event call system
|
||||
[/list]
|
||||
|
||||
The Vermintide Mod Framework originally started in Warhammer End Times: Vermintide as an unofficial modding platform. In the time since, VMF has been rewritten and redesigned with contributions from many unique members of the community; culminating in this unified project made for the arrival of official mod support.
|
||||
|
||||
If you're interested in creating mods with VMF, please check out [url=https://vmf-docs.verminti.de/]the project's wiki[/url].
|
||||
|
||||
If you'd like to contribute to the code behind VMF, visit [url=https://github.com/Vermintide-Mod-Framework/Vermintide-Mod-Framework]the project's GitHub repository[/url].";
|
||||
preview = "previewV2_stable.png";
|
||||
content = "bundleV2";
|
||||
language = "english";
|
||||
visibility = "public";
|
||||
tags = ["Tools"];
|
||||
published_id = 1369573612L;
|
|
@ -98,6 +98,9 @@ return {
|
|||
es = "Personalizado",
|
||||
ru = "Пользовательские",
|
||||
},
|
||||
output_mode_notification = {
|
||||
en = "'Notification' Output",
|
||||
},
|
||||
output_mode_echo = {
|
||||
en = "'Echo' Output",
|
||||
es = "Mensajes de 'Echo'",
|
||||
|
@ -138,11 +141,23 @@ return {
|
|||
es = "Chat",
|
||||
ru = "Чат",
|
||||
},
|
||||
output_notification = {
|
||||
en = "Notification",
|
||||
},
|
||||
output_log_and_chat = {
|
||||
en = "Log & Chat",
|
||||
es = "Registro (log) y chat",
|
||||
ru = "Лог и чат",
|
||||
},
|
||||
output_all = {
|
||||
en = "All",
|
||||
},
|
||||
output_log_and_notification = {
|
||||
en = "Log & Notification",
|
||||
},
|
||||
output_chat_and_notification = {
|
||||
en = "Chat & Notification",
|
||||
},
|
||||
chat_history_enable = {
|
||||
en = "Chat Input History",
|
||||
es = "Historial de chat",
|
||||
|
@ -224,12 +239,17 @@ return {
|
|||
"ВНИМАНИЕ: изменение этой настройки очистит вашу историю ввода.",
|
||||
},
|
||||
|
||||
|
||||
chat_command_not_recognized = {
|
||||
en = "Command not recognized",
|
||||
},
|
||||
clean_chat_history = {
|
||||
en = "cleans chat input history",
|
||||
es = "Borra el historial de usuario",
|
||||
ru = "очищает историю ввода",
|
||||
},
|
||||
clean_chat_notifications = {
|
||||
en = "cleans chat notification alerts"
|
||||
},
|
||||
dev_console_opened = {
|
||||
en = "Developer console opened.",
|
||||
es = "Abierto la consola de desarrollo.",
|
||||
|
@ -249,30 +269,21 @@ return {
|
|||
es = "No se proporcionó una descripción.",
|
||||
},
|
||||
|
||||
-- Difficulties' names [V1]
|
||||
easy = {
|
||||
en = "Easy"
|
||||
-- Difficulties' names
|
||||
lowest = {
|
||||
en = "Sedition"
|
||||
},
|
||||
normal = {
|
||||
en = "Normal"
|
||||
low = {
|
||||
en = "Uprising"
|
||||
},
|
||||
hard = {
|
||||
en = "Hard"
|
||||
medium = {
|
||||
en = "Malice"
|
||||
},
|
||||
harder = {
|
||||
en = "Nightmare"
|
||||
high = {
|
||||
en = "Heresy"
|
||||
},
|
||||
hardest = {
|
||||
en = "Cataclysm"
|
||||
},
|
||||
survival_hard = {
|
||||
en = "Veteran"
|
||||
},
|
||||
survival_harder = {
|
||||
en = "Champion"
|
||||
},
|
||||
survival_hardest = {
|
||||
en = "Heroic"
|
||||
highest = {
|
||||
en = "Damnation"
|
||||
},
|
||||
|
||||
-- Chat messages
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
valid_tags = {}
|
|
@ -1,38 +0,0 @@
|
|||
-- Image Source:
|
||||
return {
|
||||
header_fav_arrow = {
|
||||
size = { 48, 48, },
|
||||
uv00 = { 0.181641, 0.880859, },
|
||||
uv11 = { 0.228516, 0.974609, },
|
||||
},
|
||||
header_fav_icon = {
|
||||
size = { 48, 48, },
|
||||
uv00 = { 0.130859, 0.880859, },
|
||||
uv11 = { 0.177734, 0.974609, },
|
||||
},
|
||||
header_fav_icon_lit = {
|
||||
size = { 48, 48, },
|
||||
uv00 = { 0.052734, 0.880859, },
|
||||
uv11 = { 0.099609, 0.974609, },
|
||||
},
|
||||
search_bar_icon = {
|
||||
size = { 48, 48, },
|
||||
uv00 = { 0.001953, 0.880859, },
|
||||
uv11 = { 0.048828, 0.974609, },
|
||||
},
|
||||
map_view_party_button = {
|
||||
size = { 128, 128, },
|
||||
uv00 = { 0.130859, 0.623047, },
|
||||
uv11 = { 0.255859, 0.873047, },
|
||||
},
|
||||
map_view_party_button_lit = {
|
||||
size = { 128, 128, },
|
||||
uv00 = { 0.001953, 0.623047, },
|
||||
uv11 = { 0.126953, 0.873047, },
|
||||
},
|
||||
map_view_mutators_area = {
|
||||
size = { 547, 313, },
|
||||
uv00 = { 0.001953, 0.003906, },
|
||||
uv11 = { 0.536133, 0.615234, },
|
||||
},
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
vmf_atlas = {
|
||||
material_contexts = {
|
||||
surface_material = ""
|
||||
}
|
||||
|
||||
shader = "gui:DIFFUSE_MAP"
|
||||
|
||||
textures = {
|
||||
diffuse_map = "gui/vmf/vmf_atlas"
|
||||
}
|
||||
|
||||
variables = {
|
||||
}
|
||||
}
|
||||
|
||||
vmf_atlas_masked = {
|
||||
material_contexts = {
|
||||
surface_material = ""
|
||||
}
|
||||
shader = "gui_gradient:DIFFUSE_MAP:MASKED"
|
||||
textures = {
|
||||
diffuse_map = "gui/vmf/vmf_atlas"
|
||||
}
|
||||
variables = {
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 124 KiB |
Binary file not shown.
Before Width: | Height: | Size: 133 KiB |
Binary file not shown.
Before Width: | Height: | Size: 114 KiB |
Binary file not shown.
Before Width: | Height: | Size: 94 KiB |
|
@ -1,28 +0,0 @@
|
|||
mod = [
|
||||
"vmf"
|
||||
]
|
||||
|
||||
package = [
|
||||
"resource_packages/vmf"
|
||||
]
|
||||
|
||||
material = [
|
||||
"materials/vmf/*"
|
||||
]
|
||||
|
||||
lua = [
|
||||
"localization/*"
|
||||
|
||||
"scripts/mods/vmf/*"
|
||||
"scripts/mods/vmf/modules/*"
|
||||
"scripts/mods/vmf/modules/core/*"
|
||||
"scripts/mods/vmf/modules/core/mutators/*"
|
||||
"scripts/mods/vmf/modules/core/mutators/test/*"
|
||||
"scripts/mods/vmf/modules/debug/*"
|
||||
"scripts/mods/vmf/modules/gui/*"
|
||||
"scripts/mods/vmf/modules/ui/options/*"
|
||||
"scripts/mods/vmf/modules/ui/chat/*"
|
||||
"scripts/mods/vmf/modules/ui/mutators/*"
|
||||
|
||||
"materials/vmf/vmf_atlas"
|
||||
]
|
|
@ -1,38 +1,18 @@
|
|||
local vmf = get_mod("VMF")
|
||||
|
||||
-- Constants used as parameters in some 'chat_manager's functions
|
||||
local CHANNEL_ID = 1
|
||||
local MESSAGE_SENDER = ""
|
||||
local LOCAL_PLAYER_ID = 0 -- VT2 only
|
||||
local LOCALIZATION_PARAMETERS = {} -- VT2 only
|
||||
local LOCALIZE = false -- VT2 only
|
||||
local LOCALIZE_PARAMETERS = false -- VT2 only
|
||||
local LOCALIZATION_PARAM = "" -- VT1 only
|
||||
local IS_SYSTEM_MESSAGE = false
|
||||
local POP_CHAT = true
|
||||
local IS_DEV = true
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### Local functions ###############################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
||||
local function send_system_message(peer_id, message)
|
||||
if VT1 then
|
||||
RPC.rpc_chat_message(peer_id, CHANNEL_ID, MESSAGE_SENDER, message, LOCALIZATION_PARAM, IS_SYSTEM_MESSAGE, POP_CHAT,
|
||||
IS_DEV)
|
||||
else
|
||||
RPC.rpc_chat_message(PEER_ID_TO_CHANNEL[peer_id], CHANNEL_ID, MESSAGE_SENDER, LOCAL_PLAYER_ID, message,
|
||||
LOCALIZATION_PARAMETERS, LOCALIZE, LOCALIZE_PARAMETERS, IS_SYSTEM_MESSAGE, POP_CHAT, IS_DEV,
|
||||
Irc.PARTY_MSG)
|
||||
end
|
||||
end
|
||||
local function broadcast_message(message, channel_tag)
|
||||
local chat_manager = Managers.chat
|
||||
|
||||
local function add_system_message_to_chat(chat_manager, message)
|
||||
if VT1 then
|
||||
chat_manager:_add_message_to_list(CHANNEL_ID, MESSAGE_SENDER, message, IS_SYSTEM_MESSAGE, POP_CHAT, IS_DEV)
|
||||
else
|
||||
chat_manager:_add_message_to_list(CHANNEL_ID, MESSAGE_SENDER, LOCAL_PLAYER_ID, message, IS_SYSTEM_MESSAGE, POP_CHAT,
|
||||
IS_DEV)
|
||||
if chat_manager and channel_tag then
|
||||
for channel_handle, channel in pairs(chat_manager:connected_chat_channels()) do
|
||||
if channel and channel.tag == channel_tag then
|
||||
chat_manager:send_channel_message(channel_handle, tostring(message))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -43,26 +23,10 @@ end
|
|||
--[[
|
||||
Broadcasts the message to all players in a lobby.
|
||||
* message [string]: message to broadcast
|
||||
* channel_tag [string]: tag of target chat channel
|
||||
--]]
|
||||
function VMFMod:chat_broadcast(message)
|
||||
local chat = Managers.chat
|
||||
if chat and chat:has_channel(1) then
|
||||
if chat.is_server then
|
||||
local members = chat:channel_members(CHANNEL_ID)
|
||||
local my_peer_id = chat.my_peer_id
|
||||
for _, member_peer_id in pairs(members) do
|
||||
if member_peer_id ~= my_peer_id then
|
||||
send_system_message(member_peer_id, message)
|
||||
end
|
||||
end
|
||||
else
|
||||
local host_peer_id = chat.host_peer_id
|
||||
if host_peer_id then
|
||||
send_system_message(host_peer_id, message)
|
||||
end
|
||||
end
|
||||
add_system_message_to_chat(chat, message)
|
||||
end
|
||||
function VMFMod:chat_broadcast(message, channel_tag)
|
||||
broadcast_message(message, channel_tag)
|
||||
end
|
||||
|
||||
--[[
|
||||
|
@ -71,8 +35,6 @@ end
|
|||
* message [string] : message to send
|
||||
--]]
|
||||
function VMFMod:chat_whisper(peer_id, message)
|
||||
local chat = Managers.chat
|
||||
if chat and chat:has_channel(1) and chat.is_server and peer_id ~= chat.host_peer_id then
|
||||
send_system_message(peer_id, message)
|
||||
end
|
||||
-- @TODO: Rewrite for Darktide
|
||||
vmf:notify("Chat whisper is not yet implemented!")
|
||||
end
|
||||
|
|
|
@ -8,7 +8,7 @@ local vmf = get_mod("VMF")
|
|||
local HOOK_TYPES = {
|
||||
hook = 1,
|
||||
hook_safe = 2,
|
||||
hook_origin = 3,
|
||||
hook_origin = 3
|
||||
}
|
||||
|
||||
-- Constants to ease on table lookups when not needed
|
||||
|
@ -36,6 +36,7 @@ local _hooks = {
|
|||
setmetatable({}, auto_table_meta), -- safe
|
||||
{}, -- origin
|
||||
}
|
||||
local _hooks_by_file = {}
|
||||
local _origs = {}
|
||||
|
||||
-- ####################################################################################################################
|
||||
|
@ -390,6 +391,33 @@ function VMFMod:hook_origin(obj, method, handler)
|
|||
return generic_hook(self, obj, method, handler, "hook_origin")
|
||||
end
|
||||
|
||||
-- :hook_file() allows you to hook a function across every past and future version of a game file,
|
||||
-- allowing your handler to replace the function in the stack,
|
||||
-- and control its execution. All hooks on the same function will be part of a chain, with the
|
||||
-- original function at the end. Your handler has to call the next function in the chain manually.
|
||||
-- The chain of event is determined by mod load order.
|
||||
function VMFMod:hook_file(obj_str, method_str, handler)
|
||||
-- Add hook create function to list for the file
|
||||
_hooks_by_file[obj_str] = _hooks_by_file[obj_str] or {}
|
||||
|
||||
local hook_create_func = function(this_filepath, this_index)
|
||||
local dynamic_obj =
|
||||
"vmf:get_require_store(\"" .. this_filepath .. "\")[" .. tostring(this_index) .. "]"
|
||||
return generic_hook(self, dynamic_obj, method_str, handler, "hook")
|
||||
end
|
||||
table.insert(_hooks_by_file[obj_str], hook_create_func)
|
||||
|
||||
-- Add the new hook to every instance of the file
|
||||
local all_file_instances = vmf:get_require_store(obj_str)
|
||||
if all_file_instances then
|
||||
for i, item in ipairs(all_file_instances) do
|
||||
if item then
|
||||
hook_create_func(obj_str, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Enable/disable functions for all hook types:
|
||||
function VMFMod:hook_enable(obj, method)
|
||||
generic_hook_toggle(self, obj, method, true)
|
||||
|
@ -439,3 +467,16 @@ vmf.apply_delayed_hooks = function(status, state)
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
vmf.apply_hooks_to_file = function(filepath, store_index)
|
||||
local all_file_instances = vmf:get_require_store(filepath)
|
||||
local file_instance = all_file_instances and all_file_instances[store_index]
|
||||
|
||||
local all_file_hooks = _hooks_by_file[filepath]
|
||||
|
||||
if all_file_hooks and file_instance then
|
||||
for _, hook_create_func in ipairs(all_file_hooks) do
|
||||
hook_create_func(filepath, store_index)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -197,8 +197,8 @@ local ERRORS = {
|
|||
-- #####################################################################################################################
|
||||
|
||||
local function is_vmf_input_service_active()
|
||||
local input_service = Managers.input:get_service("VMF")
|
||||
return input_service and not input_service:is_blocked()
|
||||
-- @TODO: Implement check for active VMF input service
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
|
@ -304,8 +304,7 @@ function vmf.generate_keybinds()
|
|||
shift = modifier_keys["shift"],
|
||||
|
||||
function_name = raw_keybind_data.function_name,
|
||||
view_name = raw_keybind_data.view_name,
|
||||
transition_data = raw_keybind_data.transition_data
|
||||
view_name = raw_keybind_data.view_name
|
||||
}
|
||||
|
||||
_keybinds[primary_key] = _keybinds[primary_key] or {
|
||||
|
@ -337,16 +336,14 @@ 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
|
||||
-- @TODO: Link this input service to the player's input service and find some way to see if it's blocked
|
||||
--[[
|
||||
-- To create the VMF input service in Darktide
|
||||
local input_manager = Managers.input
|
||||
local service_type = "VMF"
|
||||
input_manager:add_setting(service_type, aliases, raw_key_table, filter_table, default_devices)
|
||||
input_manager:get_input_service(service_type)
|
||||
--]]
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ Polish (pl)
|
|||
]]
|
||||
|
||||
local _language_id = Application.user_setting("language_id")
|
||||
|
||||
local _global_localization_database = {}
|
||||
local _localization_database = {}
|
||||
|
||||
-- ####################################################################################################################
|
||||
|
@ -25,8 +27,35 @@ local function safe_string_format(mod, str, ...)
|
|||
|
||||
if success then
|
||||
return message
|
||||
else
|
||||
elseif mod then
|
||||
mod:error("(localize) \"%s\": %s", tostring(str), tostring(message))
|
||||
else
|
||||
vmf:error("(localize) \"%s\": %s", tostring(str), tostring(message))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function get_translated_or_english_message(mod, text_translations, ...)
|
||||
|
||||
if text_translations then
|
||||
|
||||
local message
|
||||
|
||||
if text_translations[_language_id] then
|
||||
|
||||
message = safe_string_format(mod, text_translations[_language_id], ...)
|
||||
if message then
|
||||
return message
|
||||
end
|
||||
end
|
||||
|
||||
if text_translations["en"] then
|
||||
|
||||
message = safe_string_format(mod, text_translations["en"], ...)
|
||||
if message then
|
||||
return message
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -36,37 +65,41 @@ end
|
|||
|
||||
VMFMod.localize = function (self, text_id, ...)
|
||||
|
||||
local message
|
||||
local mod_localization_table = _localization_database[self:get_name()]
|
||||
if mod_localization_table then
|
||||
|
||||
local text_translations = mod_localization_table[text_id]
|
||||
if text_translations then
|
||||
|
||||
local message
|
||||
|
||||
if text_translations[_language_id] then
|
||||
|
||||
message = safe_string_format(self, text_translations[_language_id], ...)
|
||||
if message then
|
||||
return message
|
||||
end
|
||||
end
|
||||
|
||||
if text_translations["en"] then
|
||||
|
||||
message = safe_string_format(self, text_translations["en"], ...)
|
||||
if message then
|
||||
return message
|
||||
end
|
||||
end
|
||||
end
|
||||
message = get_translated_or_english_message(self, text_translations, ...)
|
||||
else
|
||||
self:error("(localize): localization file was not loaded for this mod")
|
||||
end
|
||||
|
||||
return "<" .. tostring(text_id) .. ">"
|
||||
return message or ("<" .. tostring(text_id) .. ">")
|
||||
end
|
||||
|
||||
|
||||
VMFMod.add_global_localize_strings = function (self, text_translations)
|
||||
for text_id, translations in ipairs(text_translations) do
|
||||
if not _global_localization_database[text_id] then
|
||||
_global_localization_database[text_id] = translations
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- ####################################################################################################################
|
||||
-- ##### VMF internal functions and variables #########################################################################
|
||||
-- ####################################################################################################################
|
||||
|
||||
-- Handles the return of global localize text_ids
|
||||
vmf:hook(_G, "Localize", function (func, text_id, ...)
|
||||
|
||||
local text_translations = text_id and _global_localization_database[text_id]
|
||||
local message = get_translated_or_english_message(nil, text_translations, ...)
|
||||
|
||||
return message or func(text_id, ...)
|
||||
end)
|
||||
|
||||
-- ####################################################################################################################
|
||||
-- ##### VMF internal functions and variables #########################################################################
|
||||
-- ####################################################################################################################
|
||||
|
@ -89,9 +122,13 @@ end
|
|||
|
||||
-- Localize without parameters and return nil instead of <text_id> if nothing found
|
||||
vmf.quick_localize = function (mod, text_id)
|
||||
|
||||
local mod_localization_table = _localization_database[mod:get_name()]
|
||||
|
||||
if mod_localization_table then
|
||||
|
||||
local text_translations = mod_localization_table[text_id]
|
||||
|
||||
if text_translations then
|
||||
return text_translations[_language_id] or text_translations["en"]
|
||||
end
|
||||
|
@ -102,5 +139,5 @@ end
|
|||
-- ##### Script #######################################################################################################
|
||||
-- ####################################################################################################################
|
||||
|
||||
local localization_table = vmf:dofile("localization/vmf")
|
||||
local localization_table = vmf:dofile("dmf/localization/vmf")
|
||||
vmf.initialize_mod_localization(vmf, localization_table)
|
||||
|
|
|
@ -2,26 +2,56 @@ local vmf = get_mod("VMF")
|
|||
|
||||
local _unsent_chat_messages = {}
|
||||
local _logging_settings
|
||||
local _logging_settings_lookup = {
|
||||
[0] = {[1] = false, [2] = false, [3] = false}, -- Disabled
|
||||
[1] = {[1] = true, [2] = false, [3] = false}, -- Log only
|
||||
[2] = {[1] = false, [2] = true, [3] = false}, -- Chat only
|
||||
[3] = {[1] = false, [2] = false, [3] = true}, -- Notification only
|
||||
[4] = {[1] = true, [2] = true, [3] = false}, -- Log and chat
|
||||
[5] = {[1] = true, [2] = false, [3] = true}, -- Log and Notification
|
||||
[6] = {[1] = false, [2] = true, [3] = true}, -- Chat and Notification
|
||||
[7] = {[1] = true, [2] = true, [3] = true}, -- All
|
||||
}
|
||||
local _notification_sound = "wwise/events/ui/play_ui_click"
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### Local functions ###############################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
||||
local function add_chat_message(message)
|
||||
local function add_chat_notification(message)
|
||||
local event_manager = Managers.event
|
||||
|
||||
if event_manager then
|
||||
event_manager:trigger("event_add_notification_message", "default", message, nil, _notification_sound)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function add_chat_message(message, sender)
|
||||
local chat_manager = Managers.chat
|
||||
local new_message = {
|
||||
channel_id = 1,
|
||||
message_sender = "System",
|
||||
message = message,
|
||||
is_system_message = VT1 and true,
|
||||
type = not VT1 and Irc.SYSTEM_MSG, -- luacheck: ignore Irc
|
||||
pop_chat = true,
|
||||
is_dev = false
|
||||
local event_manager = Managers.event
|
||||
|
||||
if chat_manager and event_manager then
|
||||
local message_obj = {
|
||||
message_body = message,
|
||||
is_current_user = false,
|
||||
}
|
||||
|
||||
table.insert(chat_manager.chat_messages, new_message)
|
||||
if not VT1 then
|
||||
table.insert(chat_manager.global_messages, new_message)
|
||||
local participant = {
|
||||
displayname = sender or "SYSTEM",
|
||||
}
|
||||
|
||||
local message_sent = false
|
||||
|
||||
local channel_handle, channel = next(chat_manager:connected_chat_channels())
|
||||
if channel then
|
||||
event_manager:trigger("chat_manager_message_recieved", channel_handle, participant, message_obj)
|
||||
message_sent = true
|
||||
end
|
||||
|
||||
if not message_sent then
|
||||
table.insert(_unsent_chat_messages, message)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -37,17 +67,18 @@ local function safe_format(mod, str, ...)
|
|||
end
|
||||
|
||||
|
||||
local function send_to_notifications(self, message)
|
||||
add_chat_notification(message)
|
||||
end
|
||||
|
||||
|
||||
local function send_to_chat(self, msg_type, message)
|
||||
|
||||
if msg_type ~= "echo" then
|
||||
message = string.format("[%s][%s] %s", self:get_name(), string.upper(msg_type), message)
|
||||
end
|
||||
|
||||
if Managers.chat and Managers.chat:has_channel(1) then
|
||||
add_chat_message(message)
|
||||
else
|
||||
table.insert(_unsent_chat_messages, message)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
@ -60,6 +91,9 @@ local function log_message(self, msg_type, message, ...)
|
|||
|
||||
message = safe_format(self, tostring(message), ...)
|
||||
if message then
|
||||
if _logging_settings[msg_type].send_to_notifications then
|
||||
send_to_notifications(self, message)
|
||||
end
|
||||
if _logging_settings[msg_type].send_to_chat then
|
||||
send_to_chat(self, msg_type, message)
|
||||
end
|
||||
|
@ -73,6 +107,13 @@ end
|
|||
-- ##### VMFMod ########################################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
||||
function VMFMod:notify(message, ...)
|
||||
if _logging_settings.notification.enabled then
|
||||
log_message(self, "notification", message, ...)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function VMFMod:echo(message, ...)
|
||||
if _logging_settings.echo.enabled then
|
||||
log_message(self, "echo", message, ...)
|
||||
|
@ -119,8 +160,8 @@ end
|
|||
-- Can't be hooked right away, since hooking module is not initialized yet
|
||||
-- Sends unsent messages to chat when chat channel is finally created
|
||||
function vmf.delayed_chat_messages_hook()
|
||||
vmf:hook_safe("ChatManager", "register_channel", function (self, channel_id)
|
||||
if (channel_id == 1) and (#_unsent_chat_messages > 0) then
|
||||
vmf:hook_safe("VivoxManager", "join_chat_channel", function (self)
|
||||
if #_unsent_chat_messages > 0 and #self:connected_chat_channels() > 0 then
|
||||
for _, message in ipairs(_unsent_chat_messages) do
|
||||
add_chat_message(message)
|
||||
end
|
||||
|
@ -135,17 +176,19 @@ end
|
|||
function vmf.load_logging_settings()
|
||||
|
||||
_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,
|
||||
notification = vmf:get("logging_mode") == "custom" and vmf:get("output_mode_notification") or 5,
|
||||
echo = vmf:get("logging_mode") == "custom" and vmf:get("output_mode_echo") or 4,
|
||||
error = vmf:get("logging_mode") == "custom" and vmf:get("output_mode_error") or 4,
|
||||
warning = vmf:get("logging_mode") == "custom" and vmf:get("output_mode_warning") or 4,
|
||||
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 and logging_mode >= 2,
|
||||
send_to_log = logging_mode and logging_mode % 2 == 1,
|
||||
send_to_notifications = logging_mode and _logging_settings_lookup[logging_mode][3],
|
||||
send_to_chat = logging_mode and _logging_settings_lookup[logging_mode][2],
|
||||
send_to_log = logging_mode and _logging_settings_lookup[logging_mode][1],
|
||||
enabled = logging_mode and logging_mode > 0
|
||||
}
|
||||
end
|
||||
|
|
|
@ -18,12 +18,3 @@ function vmf.check_wrong_argument_type(mod, vmf_function_name, argument_name, ar
|
|||
table.concat(allowed_types, "/"), argument_type)
|
||||
return true
|
||||
end
|
||||
|
||||
function vmf.check_old_vmf()
|
||||
local old_vmf_table = rawget(_G, "Mods")
|
||||
|
||||
if old_vmf_table and old_vmf_table.exec then
|
||||
error("Unfortunately, workshop mods and old-fashioned mods (VMF-pack or QoL) are incompatible. " ..
|
||||
"Either remove old mods or disable workshop mods.")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,21 +1,17 @@
|
|||
return {
|
||||
dice = {
|
||||
grims = 0,
|
||||
tomes = 0,
|
||||
bonus = 0
|
||||
reward = {
|
||||
credits = 0,
|
||||
plasteel = 0,
|
||||
diamantine = 0
|
||||
},
|
||||
short_title = "",
|
||||
title_placement = "after",
|
||||
difficulty_levels = {
|
||||
"easy",
|
||||
"normal",
|
||||
"hard",
|
||||
"harder",
|
||||
"hardest",
|
||||
|
||||
"survival_hard",
|
||||
"survival_harder",
|
||||
"survival_hardest"
|
||||
"lowest",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"highest",
|
||||
},
|
||||
incompatible_with_all = false,
|
||||
compatible_with_all = false,
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
--[[
|
||||
Add additional dice to end game roll
|
||||
--]]
|
||||
local vmf = get_mod("VMF")
|
||||
|
||||
-- List of all die types
|
||||
local MISSIONS = {
|
||||
"bonus_dice_hidden_mission",
|
||||
"tome_bonus_mission",
|
||||
"grimoire_hidden_mission"
|
||||
}
|
||||
|
||||
-- Amounts of additional dice to be added at level completion
|
||||
local _num_dice_per_mission = {
|
||||
bonus_dice_hidden_mission = 0,
|
||||
tome_bonus_mission = 0,
|
||||
grimoire_hidden_mission = 0
|
||||
}
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### Local functions ###############################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
||||
-- Adds/remove dice
|
||||
local function adjustDice(grims, tomes, bonus, multiplier)
|
||||
if grims then
|
||||
_num_dice_per_mission.grimoire_hidden_mission = _num_dice_per_mission.grimoire_hidden_mission + grims * multiplier
|
||||
end
|
||||
if tomes then
|
||||
_num_dice_per_mission.tome_bonus_mission = _num_dice_per_mission.tome_bonus_mission + tomes * multiplier
|
||||
end
|
||||
if bonus then
|
||||
_num_dice_per_mission.bonus_dice_hidden_mission = _num_dice_per_mission.bonus_dice_hidden_mission + bonus *
|
||||
multiplier
|
||||
end
|
||||
end
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### Hooks #########################################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
||||
vmf:hook(GameModeManager, "complete_level", function(func, ...)
|
||||
local num_dice = 0
|
||||
local max_dice = 7
|
||||
local mission_system = Managers.state.entity:system("mission_system")
|
||||
local active_mission = mission_system.active_missions
|
||||
|
||||
-- Add additional dice
|
||||
for _, mission in ipairs(MISSIONS) do
|
||||
for _ = 1, _num_dice_per_mission[mission] do
|
||||
mission_system:request_mission(mission, nil, Network.peer_id())
|
||||
mission_system:update_mission(mission, true, nil, Network.peer_id(), nil, true)
|
||||
end
|
||||
end
|
||||
|
||||
-- Get total number of dice
|
||||
for name, obj in pairs(active_mission) do
|
||||
if table.contains(MISSIONS, name) then
|
||||
num_dice = num_dice + obj.current_amount
|
||||
end
|
||||
end
|
||||
|
||||
-- Remove excess dice
|
||||
for _, mission in ipairs(MISSIONS) do
|
||||
if active_mission[mission] then
|
||||
for _ = 1, active_mission[mission].current_amount do
|
||||
if num_dice > max_dice then
|
||||
mission_system:request_mission(mission, nil, Network.peer_id())
|
||||
mission_system:update_mission(mission, false, nil, Network.peer_id(), nil, true)
|
||||
num_dice = num_dice - 1
|
||||
else break end
|
||||
end
|
||||
end
|
||||
if num_dice <= max_dice then break end
|
||||
end
|
||||
|
||||
func(...)
|
||||
end)
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### Return ########################################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
||||
return {
|
||||
addDice = function(dice)
|
||||
adjustDice(dice.grims, dice.tomes, dice.bonus, 1)
|
||||
end,
|
||||
|
||||
removeDice = function(dice)
|
||||
adjustDice(dice.grims, dice.tomes, dice.bonus, -1)
|
||||
end
|
||||
}
|
|
@ -3,8 +3,6 @@
|
|||
--]]
|
||||
local vmf = get_mod("VMF")
|
||||
|
||||
local _were_enabled_before = false
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### Local functions ###############################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
@ -23,28 +21,7 @@ end
|
|||
|
||||
-- Sets the lobby name
|
||||
local function set_lobby_data()
|
||||
|
||||
if not Managers.matchmaking or
|
||||
not Managers.matchmaking.lobby or
|
||||
not Managers.matchmaking.lobby.set_lobby_data or
|
||||
not Managers.matchmaking.lobby.get_stored_lobby_data
|
||||
then
|
||||
return
|
||||
end
|
||||
|
||||
local name = add_enabled_mutators_titles_to_string(" ", true) -- @TODO: change separator?
|
||||
|
||||
local default_name = LobbyAux.get_unique_server_name()
|
||||
if string.len(name) > 0 then
|
||||
name = "||" .. name .. "|| " .. default_name
|
||||
else
|
||||
name = default_name
|
||||
end
|
||||
|
||||
local lobby_data = Managers.matchmaking.lobby:get_stored_lobby_data()
|
||||
lobby_data.unique_server_name = name
|
||||
|
||||
Managers.matchmaking.lobby:set_lobby_data(lobby_data)
|
||||
-- @TODO: Add mutator titles to lobby name in matchmaking
|
||||
end
|
||||
|
||||
|
||||
|
@ -56,49 +33,11 @@ end
|
|||
-- ##### Hooks #########################################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
||||
-- Append difficulty name with enabled mutators' titles
|
||||
vmf:hook_origin(IngamePlayerListUI, "update_difficulty", function(self)
|
||||
local difficulty_settings = Managers.state.difficulty:get_difficulty_settings()
|
||||
local difficulty_name = difficulty_settings.display_name
|
||||
-- @TODO: Hook to update difficulty name
|
||||
|
||||
local name = add_enabled_mutators_titles_to_string(", ", true)
|
||||
local localized_difficulty_name = not self.is_in_inn and Localize(difficulty_name) or ""
|
||||
if name == "" then -- no mutators
|
||||
name = localized_difficulty_name
|
||||
elseif localized_difficulty_name ~= "" then -- it can be "" if player is in the inn with no selected level
|
||||
name = name .. " (" .. localized_difficulty_name .. ")"
|
||||
end
|
||||
-- @TODO: Hook to notify strike team of enabled mutators
|
||||
|
||||
self.set_difficulty_name(self, name)
|
||||
|
||||
self.current_difficulty_name = difficulty_name
|
||||
end)
|
||||
|
||||
|
||||
-- Notify everybody about enabled/disabled mutators when Play button is pressed on the map screen
|
||||
vmf:hook_safe(MatchmakingStateHostGame, "host_game", function()
|
||||
set_lobby_data()
|
||||
local names = add_enabled_mutators_titles_to_string(", ")
|
||||
if names ~= "" then
|
||||
vmf:chat_broadcast(vmf:localize("broadcast_enabled_mutators") .. ": " .. names)
|
||||
_were_enabled_before = true
|
||||
elseif _were_enabled_before then
|
||||
vmf:chat_broadcast(vmf:localize("broadcast_all_disabled"))
|
||||
_were_enabled_before = false
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
-- @TODO: can't I do it with hook_safe? Also can't I just use 'sender' intead of extracting peer_id form cookie?
|
||||
-- Send special messages with enabled mutators list to players just joining the lobby
|
||||
vmf:hook(MatchmakingManager, "rpc_matchmaking_request_join_lobby", function(func, self, sender, client_cookie, ...)
|
||||
local name = add_enabled_mutators_titles_to_string(", ")
|
||||
if name ~= "" then
|
||||
local message = vmf:localize("whisper_enabled_mutators") .. ": " .. name
|
||||
vmf:chat_whisper(get_peer_id_from_cookie(client_cookie), message)
|
||||
end
|
||||
func(self, sender, client_cookie, ...)
|
||||
end)
|
||||
-- @TODO: Hook to whisper incoming players about enabled mutators
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### Return ########################################################################################################
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
--[[
|
||||
Manages everything related to mutators: loading order, enabling/disabling process, giving extra dice etc.
|
||||
Manages everything related to mutators: loading order, enabling/disabling process, giving extra rewards etc.
|
||||
--]]
|
||||
local vmf = get_mod("VMF")
|
||||
|
||||
|
@ -23,11 +23,11 @@ local _mutators_sorted = false
|
|||
local _all_mutators_disabled = false
|
||||
|
||||
-- External modules
|
||||
local dice_manager = vmf:dofile("scripts/mods/vmf/modules/core/mutators/mutators_dice")
|
||||
local set_lobby_data = vmf:dofile("scripts/mods/vmf/modules/core/mutators/mutators_info")
|
||||
local reward_manager = vmf:dofile("dmf/scripts/mods/vmf/modules/core/mutators/mutators_reward")
|
||||
local set_lobby_data = vmf:dofile("dmf/scripts/mods/vmf/modules/core/mutators/mutators_info")
|
||||
|
||||
-- Get default configuration
|
||||
local _default_config = vmf:dofile("scripts/mods/vmf/modules/core/mutators/mutators_default_config")
|
||||
local _default_config = vmf:dofile("dmf/scripts/mods/vmf/modules/core/mutators/mutators_default_config")
|
||||
|
||||
-- List of enabled mutators in case VMF is reloaded in the middle of the game
|
||||
local _enabled_mutators = vmf:persistent_table("enabled_mutators")
|
||||
|
@ -49,7 +49,7 @@ end
|
|||
-- Called after mutator is enabled
|
||||
local function on_enabled(mutator)
|
||||
local config = mutator:get_internal_data("mutator_config")
|
||||
dice_manager.addDice(config.dice)
|
||||
reward_manager.addReward(config.reward)
|
||||
set_lobby_data()
|
||||
print("[MUTATORS] Enabled " .. mutator:get_name() .. " (" .. tostring(get_index(_mutators, mutator)) .. ")")
|
||||
|
||||
|
@ -61,9 +61,9 @@ end
|
|||
local function on_disabled(mutator, initial_call)
|
||||
local config = mutator:get_internal_data("mutator_config")
|
||||
|
||||
-- All mutators run on_disabled on initial call, so there's no need to remove dice and set lobby data
|
||||
-- All mutators run on_disabled on initial call, so there's no need to remove rewards and set lobby data
|
||||
if not initial_call then
|
||||
dice_manager.removeDice(config.dice)
|
||||
reward_manager.removeReward(config.reward)
|
||||
set_lobby_data()
|
||||
end
|
||||
print("[MUTATORS] Disabled " .. mutator:get_name() .. " (" .. tostring(get_index(_mutators, mutator)) .. ")")
|
||||
|
@ -74,9 +74,13 @@ end
|
|||
|
||||
-- Checks if the player is server in a way that doesn't incorrectly return false during loading screens
|
||||
local function player_is_server()
|
||||
local player = Managers.player
|
||||
local state = Managers.state
|
||||
return not player or player.is_server or not state or state.game_mode == nil
|
||||
return Managers and Managers.state and Managers.state.game_session and Managers.state.game_session:is_server()
|
||||
|
||||
--[[ -- Might not be necessary?
|
||||
return Managers and Managers.state and (
|
||||
(Managers.state.game_session and Managers.state.game_session:is_server()) or
|
||||
(Managers.state.game_mode and Managers.state.game_mode._game_mode == nil))
|
||||
--]]
|
||||
end
|
||||
|
||||
|
||||
|
@ -154,7 +158,7 @@ local function mutator_can_be_enabled(mutator)
|
|||
end
|
||||
|
||||
-- If conflicting difficulty is set (if no difficulty is set, all mutators are allowed)
|
||||
local actual_difficulty = Managers.state and Managers.state.difficulty:get_difficulty()
|
||||
local actual_difficulty = Managers.state and Managers.state.difficulty:get_difficulty() or 0
|
||||
local compatible_difficulties = mutator_compatibility_config.compatible_difficulties
|
||||
return not actual_difficulty or compatible_difficulties[actual_difficulty]
|
||||
end
|
||||
|
@ -302,14 +306,11 @@ local function update_compatibility(mutator)
|
|||
|
||||
-- Compatibility with current difficulty (This part works only for VT1. Will see what to do with VT2 later.)
|
||||
compatibility.compatible_difficulties = {
|
||||
easy = false,
|
||||
normal = false,
|
||||
hard = false,
|
||||
harder = false,
|
||||
hardest = false,
|
||||
survival_hard = false,
|
||||
survival_harder = false,
|
||||
survival_hardest = false,
|
||||
lowest = false,
|
||||
low = false,
|
||||
medium = false,
|
||||
high = false,
|
||||
highest = false,
|
||||
}
|
||||
local compatible_difficulties = compatibility.compatible_difficulties
|
||||
local compatible_difficulties_number = 0
|
||||
|
@ -342,7 +343,7 @@ local function initialize_mutator_config(mutator, _raw_config)
|
|||
|
||||
local config = mutator:get_internal_data("mutator_config")
|
||||
|
||||
config.dice = raw_config.dice
|
||||
config.reward = raw_config.reward
|
||||
config.short_title = raw_config.short_title
|
||||
config.title_placement = raw_config.title_placement
|
||||
|
||||
|
@ -493,13 +494,11 @@ end
|
|||
-- ##### Hooks #########################################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
||||
vmf:hook_safe(DifficultyManager, "set_difficulty", function()
|
||||
disable_impossible_mutators(true, "disabled_reason_difficulty_change")
|
||||
end)
|
||||
-- @TODO: Hook to disable impossible mutators when the difficulty changes
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### Script ########################################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
||||
-- Testing
|
||||
--vmf:dofile("scripts/mods/vmf/modules/core/mutators/test/mutators_test")
|
||||
--vmf:dofile("dmf/scripts/mods/vmf/modules/core/mutators/test/mutators_test")
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
--[[
|
||||
Add additional reward to end game results
|
||||
--]]
|
||||
local vmf = get_mod("VMF")
|
||||
|
||||
-- Amounts of additional rewards to be added at level completion
|
||||
local _num_reward = {
|
||||
credits = 0,
|
||||
plasteel = 0,
|
||||
diamantine = 0
|
||||
}
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### Local functions ###############################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
||||
-- Adds/removes reward modifiers
|
||||
local function adjustReward(credits, plasteel, diamantine, multiplier)
|
||||
if credits then
|
||||
_num_reward.credits = _num_reward.credits + credits * multiplier
|
||||
end
|
||||
if plasteel then
|
||||
_num_reward.plasteel = _num_reward.plasteel + plasteel * multiplier
|
||||
end
|
||||
if diamantine then
|
||||
_num_reward.diamantine = _num_reward.diamantine + diamantine * multiplier
|
||||
end
|
||||
end
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### Hooks #########################################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
||||
-- @TODO: Hook to increase mission's reward according to enabled mutators
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### Return ########################################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
||||
return {
|
||||
addReward = function(reward)
|
||||
adjustReward(reward.credits, reward.plasteel, reward.diamantine, 1)
|
||||
end,
|
||||
|
||||
removeReward = function(reward)
|
||||
adjustReward(reward.credits, reward.plasteel, reward.diamantine, -1)
|
||||
end
|
||||
}
|
|
@ -50,8 +50,7 @@ create_test_mutator("test_deathwish", {
|
|||
is_mutator = true,
|
||||
mutator_settings = {
|
||||
difficulty_levels = {
|
||||
"hardest",
|
||||
"survival_hardest",
|
||||
"highest",
|
||||
},
|
||||
title_placement = "after",
|
||||
},
|
||||
|
@ -64,9 +63,9 @@ create_test_mutator("test_slayer", {
|
|||
is_mutator = true,
|
||||
mutator_settings = {
|
||||
difficulty_levels = {
|
||||
"survival_hard",
|
||||
"survival_harder",
|
||||
"survival_hardest",
|
||||
"medium",
|
||||
"high",
|
||||
"highest",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -98,7 +97,7 @@ create_test_mutator("test_one_hit_one_kill", {
|
|||
" non porta ante. Phasellus consequat facilisis quam quis dignissim",
|
||||
is_mutator = true,
|
||||
mutator_settings = {
|
||||
difficulty_levels = {"hardest"},
|
||||
difficulty_levels = {"highest"},
|
||||
enable_after_these = {"test_more_rats_weapons"},
|
||||
},
|
||||
})
|
||||
|
@ -116,10 +115,10 @@ create_test_mutator("lmao", {
|
|||
name = "lmao",
|
||||
is_mutator = true,
|
||||
mutator_settings = {
|
||||
difficulty_levels = {"hardest"},
|
||||
difficulty_levels = {"highest"},
|
||||
enable_after_these = {"ayyyy"},
|
||||
dice = {
|
||||
bonus = 2,
|
||||
reward = {
|
||||
plasteel = 2,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -131,7 +130,7 @@ create_test_mutator("test_more_rats_weapons", {
|
|||
is_mutator = true,
|
||||
mutator_settings = {
|
||||
compatible_with_all = true,
|
||||
difficulty_levels = {"hardest"},
|
||||
difficulty_levels = {"highest"},
|
||||
},
|
||||
})
|
||||
|
||||
|
|
|
@ -1,419 +1,29 @@
|
|||
local vmf = get_mod("VMF")
|
||||
|
||||
local _vmf_users = {}
|
||||
local _rpc_callbacks = {}
|
||||
|
||||
local _local_mods_map = {}
|
||||
local _local_rpcs_map = {}
|
||||
|
||||
local _shared_mods_map = ""
|
||||
local _shared_rpcs_map = ""
|
||||
|
||||
local _network_module_is_initialized = false
|
||||
local _network_debug = false
|
||||
|
||||
local VT2_PORT_NUMBER = 0
|
||||
|
||||
local VERMINTIDE_CHANNEL_ID = 1
|
||||
local RPC_VMF_REQUEST_CHANNEL_ID = 3
|
||||
local RPC_VMF_RESPONCE_CHANNEL_ID = 4
|
||||
local RPC_VMF_UNKNOWN_CHANNEL_ID = 5 -- Note(Siku): No clue what 5 is supposed to mean.
|
||||
|
||||
-- ####################################################################################################################
|
||||
-- ##### Local functions ##############################################################################################
|
||||
-- ####################################################################################################################
|
||||
|
||||
local function is_rpc_registered(mod_name, rpc_name)
|
||||
|
||||
local success = pcall(function() return _rpc_callbacks[mod_name][rpc_name] end)
|
||||
return success
|
||||
end
|
||||
|
||||
-- CONVERTING
|
||||
|
||||
local function convert_names_to_numbers(peer_id, mod_name, rpc_name)
|
||||
|
||||
local user_rpcs_dictionary = _vmf_users[peer_id]
|
||||
if user_rpcs_dictionary then
|
||||
|
||||
local mod_number = user_rpcs_dictionary[1][mod_name]
|
||||
if mod_number then
|
||||
|
||||
local rpc_number = user_rpcs_dictionary[2][mod_number][rpc_name]
|
||||
if rpc_number then
|
||||
|
||||
return mod_number, rpc_number
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function convert_numbers_to_names(mod_number, rpc_number)
|
||||
|
||||
local mod_name = _local_mods_map[mod_number]
|
||||
if mod_name then
|
||||
|
||||
local rpc_name = _local_rpcs_map[mod_number][rpc_number]
|
||||
if rpc_name then
|
||||
|
||||
return mod_name, rpc_name
|
||||
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- SERIALIZATION
|
||||
|
||||
local function serialize_data(...)
|
||||
|
||||
return cjson.encode({...})
|
||||
end
|
||||
|
||||
local function deserialize_data(data)
|
||||
|
||||
data = cjson.decode(data)
|
||||
|
||||
local args_number = #data
|
||||
|
||||
for i, _ in ipairs(data) do
|
||||
if type(data[i]) == "userdata" then -- userdata [nullptr (deleted)] -> nil
|
||||
data[i] = nil
|
||||
end
|
||||
end
|
||||
|
||||
return unpack(data, 1, args_number)
|
||||
end
|
||||
|
||||
-- DEBUG
|
||||
|
||||
local function network_debug(rpc_type, action_type, peer_id, mod_name, rpc_name, data)
|
||||
|
||||
if _network_debug then
|
||||
|
||||
local debug_message
|
||||
|
||||
if action_type == "local" then
|
||||
debug_message = "[NETWORK][LOCAL]"
|
||||
else
|
||||
local msg_direction = (action_type == "sent" and "<-" or "->")
|
||||
local player_string = tostring(Managers.player:player_from_peer_id(peer_id))
|
||||
--NOTE (Siku): Multiple concatenation requires the creation of multiple strings, look into it.
|
||||
--debug_message = string.format("[NETWORK][%s (%s)] %s", peer_id, player_string, msg_direction)
|
||||
debug_message = "[NETWORK][" .. peer_id .. " (" .. player_string .. ")]" .. msg_direction
|
||||
end
|
||||
|
||||
if rpc_type == "ping" then
|
||||
|
||||
debug_message = debug_message .. "[PING]"
|
||||
|
||||
elseif rpc_type == "pong" then
|
||||
|
||||
debug_message = debug_message .. "[PONG]"
|
||||
|
||||
elseif rpc_type == "data" then
|
||||
|
||||
--debug_message = string.format("%s[DATA][%s][%s]: ", debug_message, mod_name, rpc_name)
|
||||
debug_message = debug_message .. "[DATA][" .. mod_name .. "][" .. rpc_name .. "]: "
|
||||
|
||||
if type(data) == "string" then
|
||||
debug_message = debug_message .. data
|
||||
else
|
||||
local success, serialized_data = pcall(serialize_data, unpack(data))
|
||||
if success then
|
||||
debug_message = debug_message .. serialized_data
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
vmf:info(debug_message)
|
||||
end
|
||||
end
|
||||
|
||||
-- NETWORK
|
||||
|
||||
local rpc_chat_message
|
||||
if VT1 then
|
||||
rpc_chat_message = function(member, channel_id, message_sender, message, localization_param,
|
||||
is_system_message, pop_chat, is_dev)
|
||||
RPC.rpc_chat_message(member, channel_id, message_sender, message, localization_param,
|
||||
is_system_message, pop_chat, is_dev)
|
||||
end
|
||||
else
|
||||
local _payload = {"","",""}
|
||||
rpc_chat_message = function(member, channel_id, _, rpc_data1, rpc_data2)
|
||||
_payload[1] = tostring(channel_id)
|
||||
_payload[2] = rpc_data1
|
||||
_payload[3] = rpc_data2
|
||||
Managers.mod:network_send(member, VT2_PORT_NUMBER, _payload)
|
||||
end
|
||||
end
|
||||
|
||||
local function send_rpc_vmf_ping(peer_id)
|
||||
|
||||
network_debug("ping", "sent", peer_id)
|
||||
rpc_chat_message(peer_id, 3, Network.peer_id(), "", "", false, true, false)
|
||||
end
|
||||
|
||||
local function send_rpc_vmf_pong(peer_id)
|
||||
|
||||
network_debug("pong", "sent", peer_id)
|
||||
rpc_chat_message(peer_id, 4, Network.peer_id(), _shared_mods_map, _shared_rpcs_map, false, true, false)
|
||||
end
|
||||
|
||||
local function send_rpc_vmf_data(peer_id, mod_name, rpc_name, ...)
|
||||
|
||||
local mod_number, rpc_number = convert_names_to_numbers(peer_id, mod_name, rpc_name)
|
||||
if mod_number then
|
||||
|
||||
local rpc_info = cjson.encode({mod_number, rpc_number})
|
||||
local success, data = pcall(serialize_data, ...)
|
||||
if success then
|
||||
network_debug("data", "sent", peer_id, mod_name, rpc_name, data)
|
||||
rpc_chat_message(peer_id, 5, Network.peer_id(), rpc_info, data, false, true, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function send_rpc_vmf_data_local(mod_name, rpc_name, ...)
|
||||
|
||||
local mod = get_mod(mod_name)
|
||||
|
||||
if mod:is_enabled() then
|
||||
network_debug("data", "local", nil, mod_name, rpc_name, {...})
|
||||
|
||||
local error_prefix = "(local rpc) " .. tostring(rpc_name)
|
||||
vmf.safe_call_nr(mod, error_prefix, _rpc_callbacks[mod_name][rpc_name], Network.peer_id(), ...)
|
||||
end
|
||||
end
|
||||
|
||||
-- ####################################################################################################################
|
||||
-- ##### VMFMod #######################################################################################################
|
||||
-- ####################################################################################################################
|
||||
|
||||
VMFMod.network_register = function (self, rpc_name, rpc_function)
|
||||
|
||||
if _network_module_is_initialized then
|
||||
self:error("(network_register): you can't register new rpc after mod initialization")
|
||||
return
|
||||
end
|
||||
|
||||
if vmf.check_wrong_argument_type(self, "network_register", "rpc_name", rpc_name, "string") or
|
||||
vmf.check_wrong_argument_type(self, "network_register", "rpc_function", rpc_function, "function") then
|
||||
return
|
||||
end
|
||||
|
||||
_rpc_callbacks[self:get_name()] = _rpc_callbacks[self:get_name()] or {}
|
||||
|
||||
_rpc_callbacks[self:get_name()][rpc_name] = rpc_function
|
||||
end
|
||||
|
||||
-- recipient = "all", "local", "others", peer_id
|
||||
VMFMod.network_send = function (self, rpc_name, recipient, ...)
|
||||
|
||||
if not is_rpc_registered(self:get_name(), rpc_name) then
|
||||
|
||||
self:error("(network_send): attempt to send non-registered rpc")
|
||||
return
|
||||
end
|
||||
|
||||
if recipient == "all" then
|
||||
|
||||
for peer_id, _ in pairs(_vmf_users) do
|
||||
send_rpc_vmf_data(peer_id, self:get_name(), rpc_name, ...)
|
||||
end
|
||||
|
||||
send_rpc_vmf_data_local(self:get_name(), rpc_name, ...)
|
||||
|
||||
elseif recipient == "others" then
|
||||
|
||||
for peer_id, _ in pairs(_vmf_users) do
|
||||
send_rpc_vmf_data(peer_id, self:get_name(), rpc_name, ...)
|
||||
end
|
||||
|
||||
elseif recipient == "local" or recipient == Network.peer_id() then
|
||||
|
||||
send_rpc_vmf_data_local(self:get_name(), rpc_name, ...)
|
||||
|
||||
else -- recipient == peer_id
|
||||
|
||||
send_rpc_vmf_data(recipient, self:get_name(), rpc_name, ...)
|
||||
end
|
||||
end
|
||||
|
||||
-- ####################################################################################################################
|
||||
-- ##### Hooks ########################################################################################################
|
||||
-- ####################################################################################################################
|
||||
|
||||
local function vmf_network_recv(sender, channel_id, rpc_data1, rpc_data2)
|
||||
if not _network_module_is_initialized then
|
||||
return
|
||||
end
|
||||
|
||||
if channel_id == RPC_VMF_REQUEST_CHANNEL_ID then -- rpc_vmf_request
|
||||
|
||||
network_debug("ping", "received", sender)
|
||||
|
||||
send_rpc_vmf_pong(sender)
|
||||
|
||||
elseif channel_id == RPC_VMF_RESPONCE_CHANNEL_ID 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 _network_debug then
|
||||
vmf:info("[RECEIVED MODS TABLE]: " .. rpc_data1)
|
||||
vmf:info("[RECEIVED RPCS TABLE]: " .. rpc_data2)
|
||||
end
|
||||
|
||||
pcall(function()
|
||||
|
||||
local user_rpcs_dictionary = {}
|
||||
|
||||
user_rpcs_dictionary[1] = cjson.decode(rpc_data1) -- mods
|
||||
user_rpcs_dictionary[2] = cjson.decode(rpc_data2) -- rpcs
|
||||
|
||||
_vmf_users[sender] = user_rpcs_dictionary
|
||||
|
||||
vmf:info("Added %s to the VMF users list.", sender)
|
||||
|
||||
-- event
|
||||
local player = Managers.player:player_from_peer_id(sender)
|
||||
if player then
|
||||
|
||||
for mod_name, _ in pairs(user_rpcs_dictionary[1]) do
|
||||
local mod = get_mod(mod_name)
|
||||
if mod then
|
||||
vmf.mod_user_joined_the_game(mod, player)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
elseif channel_id == RPC_VMF_UNKNOWN_CHANNEL_ID then
|
||||
local mod_number, rpc_number = unpack(cjson.decode(rpc_data1))
|
||||
|
||||
local mod_name, rpc_name = convert_numbers_to_names(mod_number, rpc_number)
|
||||
if mod_name and get_mod(mod_name):is_enabled() then
|
||||
|
||||
network_debug("data", "received", sender, mod_name, rpc_name, rpc_data2)
|
||||
|
||||
-- can be error in both callback_function() and deserialize_data()
|
||||
local error_prefix = "(network) " .. tostring(rpc_name)
|
||||
vmf.safe_call_nr(
|
||||
get_mod(mod_name),
|
||||
error_prefix,
|
||||
function() _rpc_callbacks[mod_name][rpc_name](sender, deserialize_data(rpc_data2)) end
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if VT1 then
|
||||
vmf:hook("ChatManager", "rpc_chat_message",
|
||||
function(func, self, sender, channel_id, message_sender, arg1, arg2, ...)
|
||||
if channel_id == VERMINTIDE_CHANNEL_ID then
|
||||
func(self, sender, channel_id, message_sender, arg1, arg2, ...)
|
||||
else
|
||||
vmf_network_recv(sender, channel_id, arg1, arg2)
|
||||
end
|
||||
end)
|
||||
end
|
||||
-- VT2 uses the networking API provided by the ModManager.
|
||||
|
||||
vmf:hook(PlayerManager, "add_remote_player", function (func, self, peer_id, player_controlled, ...)
|
||||
|
||||
if player_controlled then
|
||||
send_rpc_vmf_ping(peer_id)
|
||||
end
|
||||
|
||||
return func(self, peer_id, player_controlled, ...)
|
||||
end)
|
||||
|
||||
vmf:hook(PlayerManager, "remove_player", function (func, self, peer_id, ...)
|
||||
|
||||
if _vmf_users[peer_id] then
|
||||
|
||||
-- make sure it's not the bot
|
||||
for _, player in pairs(Managers.player:human_players()) do
|
||||
if player.peer_id == peer_id then
|
||||
|
||||
vmf:info("Removed %s from the VMF users list.", peer_id)
|
||||
|
||||
-- event
|
||||
for mod_name, _ in pairs(_vmf_users[peer_id][1]) do
|
||||
local mod = get_mod(mod_name)
|
||||
if mod then
|
||||
vmf.mod_user_left_the_game(mod, player)
|
||||
end
|
||||
end
|
||||
|
||||
_vmf_users[peer_id] = nil
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
func(self, peer_id, ...)
|
||||
end)
|
||||
|
||||
-- ####################################################################################################################
|
||||
-- ##### VMF internal functions and variables #########################################################################
|
||||
-- ####################################################################################################################
|
||||
|
||||
vmf.create_network_dictionary = function()
|
||||
|
||||
_shared_mods_map = {}
|
||||
_shared_rpcs_map = {}
|
||||
|
||||
local i = 0
|
||||
for mod_name, mod_rpcs in pairs(_rpc_callbacks) do
|
||||
i = i + 1
|
||||
|
||||
_shared_mods_map[mod_name] = i
|
||||
_local_mods_map[i] = mod_name
|
||||
|
||||
_shared_rpcs_map[i] = {}
|
||||
_local_rpcs_map[i] = {}
|
||||
|
||||
local j = 0
|
||||
for rpc_name, _ in pairs(mod_rpcs) do
|
||||
j = j + 1
|
||||
|
||||
_shared_rpcs_map[i][rpc_name] = j
|
||||
_local_rpcs_map[i][j] = rpc_name
|
||||
end
|
||||
end
|
||||
|
||||
_shared_mods_map = cjson.encode(_shared_mods_map)
|
||||
_shared_rpcs_map = cjson.encode(_shared_rpcs_map)
|
||||
|
||||
if not VT1 then
|
||||
Managers.mod:network_bind(VT2_PORT_NUMBER, function(sender, payload)
|
||||
vmf_network_recv(sender, tonumber(payload[1]), payload[2], payload[3])
|
||||
end)
|
||||
end
|
||||
|
||||
_network_module_is_initialized = true
|
||||
end
|
||||
|
||||
vmf.network_unload = function()
|
||||
if not VT1 then
|
||||
Managers.mod:network_unbind(VT2_PORT_NUMBER)
|
||||
end
|
||||
-- @TODO: Not implemented
|
||||
end
|
||||
|
||||
vmf.ping_vmf_users = function()
|
||||
|
||||
if Managers.player then
|
||||
for _, player in pairs(Managers.player:human_players()) do
|
||||
if player.peer_id ~= Network.peer_id() then
|
||||
|
||||
send_rpc_vmf_ping(player.peer_id)
|
||||
send_rpc_vmf_pong(player.peer_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- @TODO: Not implemented
|
||||
end
|
||||
|
||||
vmf.load_network_settings = function()
|
||||
|
|
|
@ -329,24 +329,6 @@ local function validate_keybind_data(data)
|
|||
vmf.throw_error("[widget \"%s\" (keybind)]: 'keybind_type' is set to \"view_toggle\" so 'view_name' " ..
|
||||
"field is required and must have 'string' type", data.setting_id)
|
||||
end
|
||||
|
||||
local transition_data = data.transition_data
|
||||
if type(transition_data) ~= "table" then
|
||||
vmf.throw_error("[widget \"%s\" (keybind)]: 'keybind_type' is set to \"view_toggle\" so 'transition_data' " ..
|
||||
"field is required and must have 'table' type", data.setting_id)
|
||||
end
|
||||
if transition_data.open_view_transition_name and type(transition_data.open_view_transition_name) ~= "string" then
|
||||
vmf.throw_error("[widget \"%s\" (keybind)]: 'transition_data.open_view_transition_name' must have "..
|
||||
"'string' type", data.setting_id)
|
||||
end
|
||||
if transition_data.close_view_transition_name and type(transition_data.close_view_transition_name) ~= "string" then
|
||||
vmf.throw_error("[widget \"%s\" (keybind)]: 'transition_data.close_view_transition_name' must have "..
|
||||
"'string' type", data.setting_id)
|
||||
end
|
||||
if transition_data.transition_fade and type(transition_data.transition_fade) ~= "boolean" then
|
||||
vmf.throw_error("[widget \"%s\" (keybind)]: 'transition_data.transition_fade' must have "..
|
||||
"'boolean' type", data.setting_id)
|
||||
end
|
||||
end
|
||||
|
||||
local default_value = data.default_value
|
||||
|
@ -388,7 +370,6 @@ local function initialize_keybind_data(mod, data, localize)
|
|||
new_data.keybind_type = data.keybind_type
|
||||
new_data.function_name = data.function_name -- required, if (keybind_type == "function_call")
|
||||
new_data.view_name = data.view_name -- required, if (keybind_type == "view_toggle")
|
||||
new_data.transition_data = data.transition_data -- required, if (keybind_type == "view_toggle")
|
||||
|
||||
validate_keybind_data(new_data)
|
||||
|
||||
|
@ -525,11 +506,12 @@ local function initialize_mod_options_widgets_data(mod, widgets_data, localize)
|
|||
end
|
||||
|
||||
local initialized_data = {}
|
||||
local base_depth = 0
|
||||
|
||||
-- Define widget data for header widget, because it's not up to modders to define it.
|
||||
local header_widget_data = {type = "header", sub_widgets = widgets_data}
|
||||
-- Put data of all widgets in one-dimensional array in order they will be displayed in mod options.
|
||||
_unfolded_raw_widgets_data = unfold_table({header_widget_data}, widgets_data, 1, 1)
|
||||
_unfolded_raw_widgets_data = unfold_table({header_widget_data}, widgets_data, 1, base_depth)
|
||||
-- Load info about widgets previously collapsed by user
|
||||
local collapsed_widgets = vmf:get("options_menu_collapsed_widgets")[mod:get_name()] or {}
|
||||
|
||||
|
@ -571,8 +553,7 @@ local function initialize_default_settings_and_keybinds(mod, initialized_widgets
|
|||
type = data.keybind_type,
|
||||
keys = mod:get(data.setting_id),
|
||||
function_name = data.function_name,
|
||||
view_name = data.view_name,
|
||||
transition_data = data.transition_data
|
||||
view_name = data.view_name
|
||||
}
|
||||
)
|
||||
end
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
local vmf = get_mod("VMF")
|
||||
|
||||
Managers.vmf = Managers.vmf or {} -- @TODO: move it to on_reload when it will be implemented in vt1
|
||||
-- @TODO: move it to on_reload when it will be implemented in vt1
|
||||
Managers.vmf = Managers.vmf or {
|
||||
delete = function()
|
||||
return
|
||||
end
|
||||
}
|
||||
Managers.vmf.persistent_tables = Managers.vmf.persistent_tables or {}
|
||||
|
||||
local _persistent_tables = Managers.vmf.persistent_tables
|
||||
|
|
80
vmf/scripts/mods/vmf/modules/core/require.lua
Normal file
80
vmf/scripts/mods/vmf/modules/core/require.lua
Normal file
|
@ -0,0 +1,80 @@
|
|||
local vmf = get_mod("VMF")
|
||||
|
||||
local _io_requires = {}
|
||||
|
||||
-- Global store of objects created through require()
|
||||
local _require_store = Mods.require_store
|
||||
|
||||
-- Global backup of the require() function
|
||||
local _original_require = Mods.original_require
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### Local functions ###############################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
||||
local function add_io_require_path(path)
|
||||
_io_requires[path] = true
|
||||
end
|
||||
|
||||
|
||||
local function remove_io_require_path(path)
|
||||
_io_requires[path] = nil
|
||||
end
|
||||
|
||||
|
||||
local function get_require_store(path)
|
||||
return _require_store[path]
|
||||
end
|
||||
|
||||
|
||||
local function original_require(path, ...)
|
||||
return _original_require(path, ...)
|
||||
end
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### VMFMod ########################################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
||||
-- Add a file path to be loaded through io instead of require()
|
||||
function VMFMod:add_require_path(path)
|
||||
add_io_require_path(path)
|
||||
end
|
||||
|
||||
|
||||
-- Remove a file path that was previously loaded through io instead of require()
|
||||
function VMFMod:remove_require_path(path)
|
||||
remove_io_require_path(path)
|
||||
end
|
||||
|
||||
|
||||
-- Get all instances of a file created through require()
|
||||
function VMFMod:get_require_store(path)
|
||||
return get_require_store(path)
|
||||
end
|
||||
|
||||
|
||||
-- Get a file through the original, unhooked require() function
|
||||
function VMFMod:original_require(path, ...)
|
||||
return original_require(path, ...)
|
||||
end
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### Hooks #########################################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
||||
-- Handles the swap to io for registered files
|
||||
vmf:hook(_G, "require", function (func, path, ...)
|
||||
if _io_requires[path] then
|
||||
return vmf:dofile(path)
|
||||
else
|
||||
return func(path, ...)
|
||||
end
|
||||
end)
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### VMF internal functions and variables ##########################################################################
|
||||
-- #####################################################################################################################
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### Script ########################################################################################################
|
||||
-- #####################################################################################################################
|
|
@ -1,5 +1,8 @@
|
|||
local vmf = get_mod("VMF")
|
||||
|
||||
-- Global method to load a file through io with a return
|
||||
local mod_dofile = Mods.file.dofile
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### Local functions ###############################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
@ -84,7 +87,7 @@ function vmf.safe_call_dofile(mod, error_prefix_data, file_path)
|
|||
show_error(mod, error_prefix_data, "file path should be a string.")
|
||||
return false
|
||||
end
|
||||
return vmf.safe_call(mod, error_prefix_data, dofile, file_path)
|
||||
return vmf.safe_call(mod, error_prefix_data, mod_dofile, file_path)
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -12,11 +12,9 @@ vmf.set_mod_state = function (mod, is_enabled, initial_call)
|
|||
|
||||
if is_enabled then
|
||||
mod:enable_all_hooks()
|
||||
vmf.inject_hud_components(mod)
|
||||
vmf.mod_enabled_event(mod, initial_call)
|
||||
else
|
||||
mod:disable_all_hooks()
|
||||
vmf.remove_injected_hud_components(mod)
|
||||
vmf.mod_disabled_event(mod, initial_call)
|
||||
end
|
||||
|
||||
|
|
|
@ -4,6 +4,9 @@ local vmf = get_mod("VMF")
|
|||
-- It would requires hooks to be pushed higher in the loading order, but then we lose hooks printing to console
|
||||
-- Unless we find a way to store our logging messages in memory before the console is loaded.
|
||||
|
||||
-- Global backup of the ffi library
|
||||
local _ffi = Mods.lua.ffi
|
||||
|
||||
local _console_data = vmf:persistent_table("dev_console_data")
|
||||
if not _console_data.enabled then _console_data.enabled = false end
|
||||
if not _console_data.original_print then _console_data.original_print = print end
|
||||
|
@ -14,12 +17,6 @@ if not _console_data.original_print then _console_data.original_print = print en
|
|||
|
||||
local function open_dev_console()
|
||||
|
||||
-- Forbid using dev console in official realm. Hopefully, temporarily restriction. So no localization.
|
||||
if not VT1 and not script_data["eac-untrusted"] then
|
||||
vmf:echo("You can't use developer console in official realm.")
|
||||
return
|
||||
end
|
||||
|
||||
if not _console_data.enabled then
|
||||
|
||||
local print_hook_function = function(func, ...)
|
||||
|
@ -50,14 +47,13 @@ local function close_dev_console()
|
|||
|
||||
-- CommandWindow won't close by itself, so it have to be closed manually
|
||||
vmf:pcall(function()
|
||||
local ffi = require("ffi")
|
||||
ffi.cdef([[
|
||||
_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)
|
||||
local hwnd = _ffi.C.FindWindowA("ConsoleWindowClass", "Developer console")
|
||||
_ffi.C.SendMessageA(hwnd, WM_CLOSE, 0, 0)
|
||||
end)
|
||||
|
||||
_console_data.enabled = false
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
local vmf = get_mod("VMF") -- @TODO: remove it?
|
||||
|
||||
|
||||
-- Global backup of the io library
|
||||
local _io = Mods.lua.io
|
||||
|
||||
-- Global backup of the os library
|
||||
local _os = Mods.lua.os
|
||||
|
||||
local function table_dump(key, value, depth, max_depth)
|
||||
if max_depth < depth then
|
||||
return
|
||||
|
@ -222,8 +228,8 @@ local function table_dump_to_file(dumped_table, dumped_table_name, max_depth)
|
|||
-- ## Saving to file ##
|
||||
-- ####################
|
||||
|
||||
os.execute("mkdir dump 2>nul")
|
||||
local file = assert(io.open("./dump/" .. dumped_table_name .. ".json", "w+"))
|
||||
_os.execute("mkdir dump 2>nul")
|
||||
local file = assert(_io.open("./dump/" .. dumped_table_name .. ".json", "w+"))
|
||||
|
||||
local function dump_to_file(table_entry, table_name, depth)
|
||||
|
||||
|
|
|
@ -1,255 +0,0 @@
|
|||
local vmf = get_mod("VMF")
|
||||
|
||||
local _ingame_hud
|
||||
local _components_data = {}
|
||||
|
||||
local COMPONENT_STATUS = table.enum("REGISTERED", "INJECTED")
|
||||
|
||||
local ERRORS = {
|
||||
THROWABLE = {
|
||||
-- inject_hud_component:
|
||||
component_already_exists = "hud component with class_name '%s' already exists.",
|
||||
-- validate_component_data:
|
||||
class_name_wrong_type = "'class_name' must be a string, not %s.",
|
||||
visibility_groups_wrong_type = "'visibility_groups' must be a table, not %s.",
|
||||
visibility_groups_key_wrong_type = "'visibility_groups' table keys must be a number, not %s.",
|
||||
visibility_groups_value_wrong_type = "'visibility_groups' table values must be a string, not %s.",
|
||||
use_hud_scale_wrong_type = "'use_hud_scale' must be a boolean or nil, not %s.",
|
||||
validation_func_wrong_type = "'validation_function' must be a function or nil, not %s."
|
||||
},
|
||||
PREFIX = {
|
||||
component_validation = "[Custom HUD Components] (register_hud_component) Hud component data validation '%s'",
|
||||
component_injection = "[Custom HUD Components] (inject_hud_component) Hud component injection '%s' ",
|
||||
ingamehud_hook_injection = "[Custom HUD Components] Hud component injection '%s'"
|
||||
}
|
||||
}
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### Local functions ###############################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
||||
local function reset_component_status()
|
||||
for _, component_data in pairs(_components_data) do
|
||||
component_data.status = COMPONENT_STATUS.REGISTERED
|
||||
end
|
||||
end
|
||||
|
||||
local function get_mod_hud_components(mod)
|
||||
return table.filter(_components_data, function(component_data)
|
||||
return component_data.mod == mod
|
||||
end)
|
||||
end
|
||||
|
||||
local function remove_injected_hud_components(mod)
|
||||
|
||||
local visibility_groups_lookup = _ingame_hud._definitions.visibility_groups_lookup
|
||||
|
||||
local components_to_remove = mod and get_mod_hud_components(mod) or _components_data
|
||||
for component_name, component_data in pairs(components_to_remove) do
|
||||
if component_data.status == COMPONENT_STATUS.INJECTED and _ingame_hud._component_list[component_name] then
|
||||
_ingame_hud:_remove_component(_ingame_hud._component_list,
|
||||
_ingame_hud._components,
|
||||
_ingame_hud._components_array,
|
||||
_ingame_hud._components_array_id_lookup,
|
||||
component_name)
|
||||
|
||||
local component_settings = component_data.component_settings
|
||||
for _, visibility_group in ipairs(component_settings.visibility_groups) do
|
||||
visibility_groups_lookup[visibility_group].visible_components[component_name] = nil
|
||||
end
|
||||
_ingame_hud._components_hud_scale_lookup[component_name] = nil
|
||||
_ingame_hud._component_list[component_name] = nil
|
||||
|
||||
component_data.status = COMPONENT_STATUS.REGISTERED
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- @ THROWS_ERRORS
|
||||
local function inject_hud_component(component_name)
|
||||
local component_data = _components_data[component_name]
|
||||
local component_settings = component_data.component_settings
|
||||
|
||||
if not component_data.mod:is_enabled() or component_data.status == COMPONENT_STATUS.INJECTED then
|
||||
return
|
||||
end
|
||||
|
||||
-- Check for collisions.
|
||||
if _ingame_hud._component_list[component_name] then
|
||||
vmf.throw_error(ERRORS.THROWABLE.component_already_exists, component_name)
|
||||
end
|
||||
|
||||
if component_settings.use_hud_scale then
|
||||
_ingame_hud._components_hud_scale_lookup[component_name] = true
|
||||
end
|
||||
|
||||
local visibility_groups_lookup = _ingame_hud._definitions.visibility_groups_lookup
|
||||
for _, visibility_group in ipairs(component_settings.visibility_groups) do
|
||||
visibility_groups_lookup[visibility_group].visible_components[component_name] = true
|
||||
end
|
||||
|
||||
if table.contains(component_settings.visibility_groups, _ingame_hud._current_group_name) then
|
||||
_ingame_hud._currently_visible_components[component_name] = true
|
||||
end
|
||||
|
||||
_ingame_hud._component_list[component_name] = component_settings
|
||||
_ingame_hud:_add_component(_ingame_hud._component_list,
|
||||
_ingame_hud._components,
|
||||
_ingame_hud._components_array,
|
||||
_ingame_hud._components_array_id_lookup,
|
||||
component_name)
|
||||
|
||||
component_data.status = COMPONENT_STATUS.INJECTED
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- @ THROWS_ERRORS
|
||||
local function validate_component_data(component_settings)
|
||||
if type(component_settings.class_name) ~= "string" then
|
||||
vmf.throw_error(ERRORS.THROWABLE.class_name_wrong_type, type(component_settings.class_name))
|
||||
end
|
||||
if component_settings.use_hud_scale and type(component_settings.use_hud_scale) ~= "boolean" then
|
||||
vmf.throw_error(ERRORS.THROWABLE.use_hud_scale_wrong_type, type(component_settings.use_hud_scale))
|
||||
end
|
||||
if type(component_settings.visibility_groups) ~= "table" then
|
||||
vmf.throw_error(ERRORS.THROWABLE.visibility_groups_wrong_type, type(component_settings.visibility_groups))
|
||||
end
|
||||
if component_settings.validation_function and type(component_settings.validation_function) ~= "function" then
|
||||
vmf.throw_error(ERRORS.THROWABLE.validation_func_wrong_type, type(component_settings.validation_function))
|
||||
end
|
||||
|
||||
local visibility_groups = component_settings.visibility_groups
|
||||
for key, visibility_group in pairs(visibility_groups) do
|
||||
if type(key) ~= "number" then
|
||||
vmf.throw_error(ERRORS.THROWABLE.visibility_groups_key_wrong_type, type(key))
|
||||
end
|
||||
if type(visibility_group) ~= "string" then
|
||||
vmf.throw_error(ERRORS.THROWABLE.visibility_groups_value_wrong_type, type(visibility_group))
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### VMFMod ########################################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
||||
--[[
|
||||
Validates provided component settings, injects the component, and returns 'true' if everything is correct.
|
||||
* component_settings [table] : Settings of the component to register
|
||||
** class_name [string] (required) : Name of the class containing the component logic.
|
||||
** visibility_groups [table<number,string] (required) : Array of visibility group names for the component to be
|
||||
included in. "alive" is most common.
|
||||
** use_hud_scale [boolean] (optional) : Set to 'true' if ingame_hud should scale the component.
|
||||
** validation_function [function] (optional) : Function called by ingame_hud to determine whether to
|
||||
create the component.
|
||||
Return 'true' to enable.
|
||||
Set to nil to always enable.
|
||||
--]]
|
||||
function VMFMod:register_hud_component(component_settings)
|
||||
if vmf.check_wrong_argument_type(self, "register_hud_component", "component_data", component_settings, "table") then
|
||||
return
|
||||
end
|
||||
|
||||
component_settings = table.clone(component_settings)
|
||||
|
||||
local component_name = component_settings.class_name
|
||||
|
||||
if not vmf.safe_call_nrc(self,
|
||||
{
|
||||
ERRORS.PREFIX.component_validation,
|
||||
component_name
|
||||
},
|
||||
validate_component_data,
|
||||
component_settings
|
||||
) then
|
||||
return
|
||||
end
|
||||
|
||||
_components_data[component_name] = {
|
||||
mod = self,
|
||||
component_settings = component_settings,
|
||||
status = COMPONENT_STATUS.REGISTERED
|
||||
}
|
||||
|
||||
if _ingame_hud then
|
||||
if not vmf.safe_call_nrc(self,
|
||||
{
|
||||
ERRORS.PREFIX.component_injection,
|
||||
component_name
|
||||
},
|
||||
inject_hud_component,
|
||||
component_name
|
||||
) then
|
||||
_components_data[component_name] = nil
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### Hooks #########################################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
||||
vmf:hook_safe(IngameHud, "_setup_components", function(self)
|
||||
_ingame_hud = self
|
||||
for component_name, _ in pairs(_components_data) do
|
||||
if not vmf.safe_call_nrc(self,
|
||||
{
|
||||
ERRORS.PREFIX.ingamehud_hook_injection,
|
||||
component_name
|
||||
},
|
||||
inject_hud_component,
|
||||
component_name
|
||||
) then
|
||||
_components_data[component_name] = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
vmf:hook_safe(IngameHud, "destroy", function()
|
||||
_ingame_hud = nil
|
||||
-- HUD components are reset every time the party is changed (including the initial local-player-only lobby)
|
||||
-- We need to reset injection status as well.
|
||||
reset_component_status()
|
||||
end)
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### VMF internal functions and variables ##########################################################################
|
||||
-- #####################################################################################################################
|
||||
function vmf.inject_hud_components(mod)
|
||||
if _ingame_hud then
|
||||
local components_to_inject = get_mod_hud_components(mod)
|
||||
|
||||
for component_name, component_data in pairs(components_to_inject) do
|
||||
if not vmf.safe_call_nrc(mod,
|
||||
{
|
||||
ERRORS.PREFIX.component_injection,
|
||||
component_name
|
||||
},
|
||||
inject_hud_component,
|
||||
component_name
|
||||
) then
|
||||
_components_data[component_name] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function vmf.remove_injected_hud_components(mod)
|
||||
if _ingame_hud then
|
||||
remove_injected_hud_components(mod)
|
||||
end
|
||||
end
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### Script ########################################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
||||
-- If VMF is reloaded mid-game, get ingame_hud.
|
||||
_ingame_hud = Managers.ui and Managers.ui._ingame_ui.ingame_hud
|
|
@ -1,49 +1,13 @@
|
|||
local vmf = get_mod("VMF")
|
||||
|
||||
local _ui_renderers = vmf:persistent_table("_ui_renderers")
|
||||
|
||||
local _custom_none_atlas_textures = {}
|
||||
local _custom_ui_atlas_settings = {}
|
||||
|
||||
local _injected_materials = {}
|
||||
|
||||
local _show_debug_info = false
|
||||
|
||||
-- ####################################################################################################################
|
||||
-- ##### Local functions ##############################################################################################
|
||||
-- ####################################################################################################################
|
||||
|
||||
local original_gasbtn_function = UIAtlasHelper.get_atlas_settings_by_texture_name
|
||||
local function check_texture_availability(mod, texture_name)
|
||||
|
||||
local texture_exists, texture_settings = pcall(original_gasbtn_function, texture_name)
|
||||
if texture_exists then
|
||||
|
||||
if type(texture_settings) == "nil" then
|
||||
mod:error("(custom texture/atlas): texture name '%s' is already used by Fatshark in 'none_atlas_textures'",
|
||||
texture_name)
|
||||
else
|
||||
mod:error("(custom texture/atlas): texture name '%s' is already used by Fatshark in atlas '%s'",
|
||||
texture_name, tostring(texture_settings.material_name))
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
if _custom_none_atlas_textures[texture_name] then
|
||||
mod:error("(custom texture/atlas): texture name '%s' is already used by the mod '%s' as none atlas texture",
|
||||
texture_name, _custom_none_atlas_textures[texture_name])
|
||||
return false
|
||||
end
|
||||
|
||||
if _custom_ui_atlas_settings[texture_name] then
|
||||
texture_settings = _custom_ui_atlas_settings[texture_name]
|
||||
mod:error("(custom texture/atlas): texture name '%s' is already used by the mod '%s' in atlas '%s'",
|
||||
texture_name, texture_settings.mod_name, tostring(texture_settings.material_name))
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
-- @TODO: Method for checking texture availability
|
||||
end
|
||||
|
||||
-- ####################################################################################################################
|
||||
|
@ -51,222 +15,21 @@ end
|
|||
-- ####################################################################################################################
|
||||
|
||||
vmf.custom_textures = function (mod, ...)
|
||||
|
||||
for i, texture_name in ipairs({...}) do
|
||||
if type(texture_name) == "string" then
|
||||
if check_texture_availability(mod, texture_name) then
|
||||
_custom_none_atlas_textures[texture_name] = mod:get_name()
|
||||
end
|
||||
else
|
||||
mod:error("(custom_textures): all arguments should have the string type, but the argument #%s is %s",
|
||||
i, type(texture_name))
|
||||
end
|
||||
end
|
||||
-- @TODO: Not implemented
|
||||
end
|
||||
|
||||
vmf.custom_atlas = function (mod, material_settings_file, material_name, masked_material_name,
|
||||
point_sample_material_name, masked_point_sample_material_name,
|
||||
saturated_material_name)
|
||||
|
||||
if vmf.check_wrong_argument_type(mod, "custom_atlas", "material_settings_file",
|
||||
material_settings_file, "string") or
|
||||
vmf.check_wrong_argument_type(mod, "custom_atlas", "material_name",
|
||||
material_name, "string", "nil") or
|
||||
vmf.check_wrong_argument_type(mod, "custom_atlas", "masked_material_name",
|
||||
masked_material_name, "string", "nil") or
|
||||
vmf.check_wrong_argument_type(mod, "custom_atlas", "point_sample_material_name",
|
||||
point_sample_material_name, "string", "nil") or
|
||||
vmf.check_wrong_argument_type(mod, "custom_atlas", "masked_point_sample_material_name",
|
||||
masked_point_sample_material_name, "string", "nil") or
|
||||
vmf.check_wrong_argument_type(mod, "custom_atlas", "saturated_material_name",
|
||||
saturated_material_name, "string", "nil") then
|
||||
return
|
||||
end
|
||||
|
||||
local material_settings = mod:dofile(material_settings_file)
|
||||
if material_settings then
|
||||
|
||||
local mod_name = mod:get_name()
|
||||
|
||||
for texture_name, texture_settings in pairs(material_settings) do
|
||||
if check_texture_availability(mod, texture_name) then
|
||||
texture_settings.mod_name = mod_name
|
||||
|
||||
texture_settings.material_name = material_name
|
||||
texture_settings.masked_material_name = masked_material_name
|
||||
texture_settings.point_sample_material_name = point_sample_material_name
|
||||
texture_settings.masked_point_sample_material_name = masked_point_sample_material_name
|
||||
texture_settings.saturated_material_name = saturated_material_name
|
||||
|
||||
_custom_ui_atlas_settings[texture_name] = texture_settings
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
mod:error("(custom_atlas): can't load 'material_settings'")
|
||||
end
|
||||
vmf.custom_atlas = function (mod, ...)
|
||||
-- @TODO: Not implemented
|
||||
end
|
||||
|
||||
vmf.inject_materials = function (mod, ui_renderer_creator, ...)
|
||||
|
||||
if vmf.check_wrong_argument_type(mod, "inject_materials", "ui_renderer_creator", ui_renderer_creator, "string") then
|
||||
return
|
||||
end
|
||||
|
||||
local injected_materials_list = _injected_materials[ui_renderer_creator] or {}
|
||||
|
||||
local can_inject
|
||||
for i, new_injected_material in ipairs({...}) do
|
||||
if type(new_injected_material) == "string" then
|
||||
|
||||
can_inject = true
|
||||
|
||||
-- check if injected_materials_list already contains current material
|
||||
for _, injected_material in ipairs(injected_materials_list) do
|
||||
if new_injected_material == injected_material then
|
||||
can_inject = false
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if can_inject then
|
||||
table.insert(injected_materials_list, new_injected_material)
|
||||
end
|
||||
|
||||
else
|
||||
mod:error("(inject_materials): all arguments should have the string type, but the argument #%s is %s",
|
||||
i + 1, type(new_injected_material) )
|
||||
end
|
||||
end
|
||||
|
||||
_injected_materials[ui_renderer_creator] = injected_materials_list
|
||||
|
||||
-- recreate GUIs with injected materials for ui_renderers created by 'ui_renderer_creator'
|
||||
local vmf_data
|
||||
|
||||
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")
|
||||
table.insert(new_materials_list, injected_material)
|
||||
end
|
||||
|
||||
World.destroy_gui(ui_renderer.world, ui_renderer.gui)
|
||||
World.destroy_gui(ui_renderer.world, ui_renderer.gui_retained)
|
||||
|
||||
ui_renderer.gui = World.create_screen_gui(ui_renderer.world, "immediate", unpack(new_materials_list))
|
||||
ui_renderer.gui_retained = World.create_screen_gui(ui_renderer.world, unpack(new_materials_list))
|
||||
|
||||
vmf_data.is_modified = true
|
||||
end
|
||||
end
|
||||
vmf.inject_materials = function (mod, ...)
|
||||
-- @TODO: Not implemented
|
||||
end
|
||||
|
||||
-- ####################################################################################################################
|
||||
-- ##### Hooks ########################################################################################################
|
||||
-- ####################################################################################################################
|
||||
|
||||
local LUA_SCRIPT_CALLER_POSITION = 4
|
||||
vmf:hook(UIRenderer, "create", function(func, world, ...)
|
||||
|
||||
local is_modified = false
|
||||
|
||||
-- FINDING OUT WHO CREATED UI_RENDERER
|
||||
|
||||
local ui_renderer_creator = nil
|
||||
|
||||
local callstack = debug.traceback()
|
||||
-- get the name of lua script which called 'UIRenderer.create'
|
||||
-- it's the 4th string of the 'debug.traceback()' output
|
||||
local i = 0
|
||||
for s in callstack:gmatch("(.-)\n") do
|
||||
i = i + 1
|
||||
if i == LUA_SCRIPT_CALLER_POSITION then
|
||||
ui_renderer_creator = s:match("([^%/]+)%.lua:")
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not ui_renderer_creator then
|
||||
print(Script.callstack())
|
||||
vmf:error("(UIRenderer.create): ui_renderer_creator not found. You're never supposed to see this message. " ..
|
||||
"If you see this, please, save the game log and report about this incident to the VMF team.")
|
||||
return func(world, ...)
|
||||
end
|
||||
|
||||
-- CREATING THE LIST OF TEXTURES FOR THE NEW UI_RENDERER
|
||||
|
||||
local ui_renderer_materials = {...}
|
||||
|
||||
if _injected_materials[ui_renderer_creator] then
|
||||
for _, injected_material in ipairs(_injected_materials[ui_renderer_creator]) do
|
||||
table.insert(ui_renderer_materials, "material")
|
||||
table.insert(ui_renderer_materials, injected_material)
|
||||
end
|
||||
is_modified = true
|
||||
end
|
||||
|
||||
-- DEBUG INFO
|
||||
|
||||
if _show_debug_info then
|
||||
vmf:info("UI_RENDERER CREATED BY:")
|
||||
vmf:info(" %s", ui_renderer_creator)
|
||||
vmf:info("UI_RENDERER MATERIALS:")
|
||||
for n, material in ipairs(ui_renderer_materials) do
|
||||
vmf:info(" [%s]: %s:", n, material)
|
||||
end
|
||||
end
|
||||
|
||||
-- CREATING THE NEW UI_RENDERER AND SAVING SOME DATA INSIDE OF IT
|
||||
|
||||
local ui_renderer = func(world, unpack(ui_renderer_materials))
|
||||
|
||||
_ui_renderers[ui_renderer] = true
|
||||
|
||||
local vmf_data = {}
|
||||
vmf_data.original_materials = {...}
|
||||
vmf_data.ui_renderer_creator = ui_renderer_creator
|
||||
vmf_data.is_modified = is_modified
|
||||
rawset(ui_renderer, "vmf_data", vmf_data)
|
||||
|
||||
return ui_renderer
|
||||
end)
|
||||
|
||||
|
||||
vmf:hook_safe(UIRenderer, "destroy", function(self)
|
||||
_ui_renderers[self] = nil
|
||||
end)
|
||||
|
||||
|
||||
vmf:hook(UIAtlasHelper, "has_atlas_settings_by_texture_name", function(func, texture_name, ...)
|
||||
|
||||
if _custom_ui_atlas_settings[texture_name] then
|
||||
return true
|
||||
end
|
||||
|
||||
return func(texture_name, ...)
|
||||
end)
|
||||
|
||||
|
||||
vmf:hook(UIAtlasHelper, "get_atlas_settings_by_texture_name", function(func, texture_name, ...)
|
||||
|
||||
if _custom_none_atlas_textures[texture_name] then
|
||||
return
|
||||
end
|
||||
|
||||
if _custom_ui_atlas_settings[texture_name] then
|
||||
return _custom_ui_atlas_settings[texture_name]
|
||||
end
|
||||
|
||||
return func(texture_name, ...)
|
||||
end)
|
||||
|
||||
-- ####################################################################################################################
|
||||
-- ##### VMF internal functions and variables #########################################################################
|
||||
-- ####################################################################################################################
|
||||
|
@ -276,16 +39,7 @@ vmf.load_custom_textures_settings = function()
|
|||
end
|
||||
|
||||
vmf.reset_guis = function()
|
||||
for ui_renderer, _ in pairs(_ui_renderers) do
|
||||
local vmf_data = rawget(ui_renderer, "vmf_data")
|
||||
if vmf_data.is_modified then
|
||||
World.destroy_gui(ui_renderer.world, ui_renderer.gui)
|
||||
World.destroy_gui(ui_renderer.world, ui_renderer.gui_retained)
|
||||
ui_renderer.gui = World.create_screen_gui(ui_renderer.world, "immediate", unpack(vmf_data.original_materials))
|
||||
ui_renderer.gui_retained = World.create_screen_gui(ui_renderer.world, unpack(vmf_data.original_materials))
|
||||
vmf_data.is_modified = false
|
||||
end
|
||||
end
|
||||
-- @TODO: Method to reset VMF-spawned guis
|
||||
end
|
||||
|
||||
-- ####################################################################################################################
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
local vmf = get_mod("VMF")
|
||||
|
||||
local _custom_view_data = vmf:persistent_table("custom_view_data")
|
||||
|
||||
local _ingame_ui
|
||||
local _ingame_ui_disabled
|
||||
-- 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 _loaded_views = {}
|
||||
|
||||
local ERRORS = {
|
||||
THROWABLE = {
|
||||
|
@ -57,10 +57,8 @@ local ERRORS = {
|
|||
-- #####################################################################################################################
|
||||
|
||||
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
|
||||
-- @TODO: Add active setting per mechanism type
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
@ -74,76 +72,60 @@ local function inject_view(view_name)
|
|||
|
||||
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
|
||||
-- Check for collisions. @TODO: Check for collisions by mod
|
||||
--if _ingame_ui._view_list[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)
|
||||
local success = vmf.safe_call(mod, ERRORS.PREFIX.view_initializing, init_view_function,
|
||||
view_settings, {})
|
||||
if success then
|
||||
_ingame_ui.views[view_name] = view
|
||||
_ingame_ui._view_list[view_name] = view_settings
|
||||
else
|
||||
vmf.throw_error(ERRORS.THROWABLE.view_initializing_failed)
|
||||
end
|
||||
|
||||
-- Inject view transitions.
|
||||
for transition_name, transition_function in pairs(transitions) do
|
||||
_ingame_ui_transitions[transition_name] = transition_function
|
||||
end
|
||||
--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
|
||||
--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 then
|
||||
-- If some custom view is active, safely close it.
|
||||
if _views_data[_ingame_ui.current_view] then
|
||||
-- Hack to ensure cursor stack safety.
|
||||
ShowCursorStack.stack_depth = ShowCursorStack.stack_depth + 1
|
||||
_ingame_ui:handle_transition("exit_menu")
|
||||
ShowCursorStack.stack_depth = 1
|
||||
ShowCursorStack.pop()
|
||||
end
|
||||
|
||||
for view_name, view_data in pairs(_views_data) do
|
||||
for view_name, _ 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, view)
|
||||
end
|
||||
_ingame_ui.views[view_name] = nil
|
||||
end
|
||||
_ingame_ui._view_list[view_name] = nil
|
||||
end
|
||||
end
|
||||
|
||||
for _, view_data in pairs(_views_data) do
|
||||
--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
|
||||
-- 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
|
||||
-- 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
|
||||
|
||||
|
||||
|
@ -189,7 +171,9 @@ local function validate_view_data(view_data)
|
|||
vmf.throw_error(ERRORS.THROWABLE.init_view_function_wrong_type, type(view_settings.init_view_function))
|
||||
end
|
||||
|
||||
-- Verify active if present
|
||||
local active = view_settings.active
|
||||
if active then
|
||||
if type(active) ~= "table" then
|
||||
vmf.throw_error(ERRORS.THROWABLE.active_wrong_type, type(active))
|
||||
end
|
||||
|
@ -204,8 +188,11 @@ local function validate_view_data(view_data)
|
|||
vmf.throw_error(ERRORS.THROWABLE.active_element_wrong_type, level_name, type(value))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Verify blocked transitions if present
|
||||
local blocked_transitions = view_settings.blocked_transitions
|
||||
if blocked_transitions then
|
||||
if type(blocked_transitions) ~= "table" then
|
||||
vmf.throw_error(ERRORS.THROWABLE.blocked_transitions_wrong_type, type(blocked_transitions))
|
||||
end
|
||||
|
@ -229,6 +216,43 @@ local function validate_view_data(view_data)
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Checks:
|
||||
-- * View registered
|
||||
-- * View has settings
|
||||
-- * View is either loaded or configured to load on call
|
||||
-- View settings only apply when the app has switched to the view loader.
|
||||
local function check_load_status(view_name)
|
||||
|
||||
local view_settings = _views_data[view_name] and _views_data[view_name].view_settings
|
||||
return view_settings and _loaded_views[view_name] or
|
||||
(_custom_view_data.loader_initialized and
|
||||
(view_settings.load_always or
|
||||
view_settings.is_hub and view_settings.load_in_hub))
|
||||
end
|
||||
|
||||
|
||||
-- Checks:
|
||||
-- * View registered
|
||||
-- * View is loaded/loadable
|
||||
-- * View is not already active
|
||||
-- * View is not in the middle of closing
|
||||
local function can_open_view(view_name)
|
||||
|
||||
if _ingame_ui then
|
||||
if
|
||||
_views_data[view_name] and
|
||||
_custom_view_data.loader_initialized and
|
||||
not Managers.ui:view_active(view_name) and
|
||||
not Managers.ui:is_view_closing(view_name)
|
||||
then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
-- #####################################################################################################################
|
||||
|
@ -243,46 +267,8 @@ end
|
|||
* 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
|
||||
--]]
|
||||
function VMFMod:handle_transition(transition_name, ignore_active_menu, fade, transition_params)
|
||||
if vmf.check_wrong_argument_type(self, "handle_transition", "transition_name", transition_name, "string") then
|
||||
return
|
||||
end
|
||||
|
||||
local vt2_player_list_active
|
||||
if not VT1 then
|
||||
local ingame_player_list_ui = _ingame_ui.ingame_hud:component("IngamePlayerListUI")
|
||||
vt2_player_list_active = ingame_player_list_ui and ingame_player_list_ui:is_active()
|
||||
end
|
||||
|
||||
if _ingame_ui
|
||||
and not _ingame_ui_disabled
|
||||
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.return_to_title_screen
|
||||
and (
|
||||
VT1
|
||||
and not _ingame_ui.menu_suspended
|
||||
and not _ingame_ui.popup_join_lobby_handler.visible
|
||||
or not VT1
|
||||
and not Managers.transition:in_fade_active()
|
||||
and not _ingame_ui:cutscene_active()
|
||||
and not _ingame_ui:unavailable_hero_popup_active()
|
||||
and (not vt2_player_list_active or ignore_active_menu)
|
||||
)
|
||||
then
|
||||
if fade then
|
||||
vmf.safe_call_nr(self, {ERRORS.PREFIX.handle_transition_fade, transition_name}, _ingame_ui.transition_with_fade,
|
||||
_ingame_ui, transition_name,
|
||||
transition_params)
|
||||
else
|
||||
vmf.safe_call_nr(self, {ERRORS.PREFIX.handle_transition_no_fade, transition_name}, _ingame_ui.handle_transition,
|
||||
_ingame_ui, transition_name,
|
||||
transition_params)
|
||||
end
|
||||
function VMFMod:handle_transition()
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
@ -298,6 +284,11 @@ function VMFMod:register_view(view_data)
|
|||
view_data = table.clone(view_data)
|
||||
|
||||
local view_name = view_data.view_name
|
||||
view_data.view_settings.name = view_name
|
||||
|
||||
if view_data.view_settings.close_on_hotkey_pressed == nil then
|
||||
view_data.view_settings.close_on_hotkey_pressed = true
|
||||
end
|
||||
|
||||
if not vmf.safe_call_nrc(self, {ERRORS.PREFIX.register_view_validation, view_name}, validate_view_data,
|
||||
view_data) then
|
||||
|
@ -307,7 +298,8 @@ function VMFMod:register_view(view_data)
|
|||
_views_data[view_name] = {
|
||||
mod = self,
|
||||
view_settings = view_data.view_settings,
|
||||
view_transitions = view_data.view_transitions
|
||||
view_transitions = view_data.view_transitions,
|
||||
view_options = view_data.view_options,
|
||||
}
|
||||
|
||||
if _ingame_ui then
|
||||
|
@ -323,7 +315,34 @@ end
|
|||
-- ##### Hooks #########################################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
||||
vmf:hook_safe(IngameUI, "init", function(self)
|
||||
|
||||
-- Track the creation of the view loader
|
||||
vmf:hook_safe(ViewLoader, "init", function()
|
||||
_custom_view_data.loader_initialized = true
|
||||
end)
|
||||
|
||||
-- Track the deletion of the view loader
|
||||
vmf:hook_safe(ViewLoader, "destroy", function()
|
||||
_custom_view_data.loader_initialized = false
|
||||
end)
|
||||
|
||||
|
||||
-- Track the loading of views, set the loader flag if class selection is reached
|
||||
vmf:hook_safe(UIManager, "load_view", function(self, view_name)
|
||||
if view_name == "class_selection_view" then
|
||||
_custom_view_data.loader_initialized = true
|
||||
end
|
||||
_loaded_views[view_name] = true
|
||||
end)
|
||||
|
||||
-- Track the unloading of views
|
||||
vmf:hook_safe(UIManager, "unload_view", function(self, view_name)
|
||||
_loaded_views[view_name] = nil
|
||||
end)
|
||||
|
||||
|
||||
-- Store the view handler for later use and inject views
|
||||
vmf:hook_safe(UIViewHandler, "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
|
||||
|
@ -332,17 +351,6 @@ vmf:hook_safe(IngameUI, "init", function(self)
|
|||
end
|
||||
end)
|
||||
|
||||
|
||||
vmf:hook_safe(IngameUI, "destroy", function()
|
||||
remove_injected_views(false)
|
||||
_ingame_ui = nil
|
||||
end)
|
||||
|
||||
|
||||
vmf:hook_safe(IngameUI, "update", function(self, dt_, t_, disable_ingame_ui)
|
||||
_ingame_ui_disabled = disable_ingame_ui
|
||||
end)
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### VMF internal functions and variables ##########################################################################
|
||||
-- #####################################################################################################################
|
||||
|
@ -356,7 +364,8 @@ 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, keybind_transition_data, can_be_opened, is_keybind_pressed)
|
||||
function vmf.keybind_toggle_view(mod, view_name, keybind_transition_data, can_perform_action, is_keybind_pressed)
|
||||
--[[
|
||||
if _ingame_ui then
|
||||
local view_data = _views_data[view_name]
|
||||
if not view_data or (view_data.mod ~= mod) then
|
||||
|
@ -377,7 +386,7 @@ function vmf.keybind_toggle_view(mod, view_name, keybind_transition_data, can_be
|
|||
end
|
||||
end
|
||||
-- Can open views only when keybind is pressed.
|
||||
elseif can_be_opened and is_keybind_pressed then
|
||||
elseif can_perform_action and is_keybind_pressed then
|
||||
if keybind_transition_data.open_view_transition_name then
|
||||
if view_data.view_transitions[keybind_transition_data.open_view_transition_name] then
|
||||
mod:handle_transition(keybind_transition_data.open_view_transition_name, true,
|
||||
|
@ -391,6 +400,56 @@ function vmf.keybind_toggle_view(mod, view_name, keybind_transition_data, can_be
|
|||
end
|
||||
end
|
||||
end
|
||||
--]]
|
||||
|
||||
if _ingame_ui then
|
||||
|
||||
-- Check that the view is registered
|
||||
local view_data = _views_data[view_name]
|
||||
if not view_data or (view_data.mod ~= mod) then
|
||||
mod:error(ERRORS.REGULAR.view_not_registered, view_name)
|
||||
return
|
||||
end
|
||||
|
||||
-- If the view is open, this is a toggle close
|
||||
if Managers.ui:view_active(view_name) then
|
||||
|
||||
-- Don't close the view if it's already closing
|
||||
if not Managers.ui:is_view_closing(view_name) then
|
||||
local force_close = true
|
||||
Managers.ui:close_view(view_name, force_close)
|
||||
end
|
||||
|
||||
-- Otherwise, this is a toggle open
|
||||
elseif can_perform_action and is_keybind_pressed then
|
||||
|
||||
local validation_function = view_data.view_settings.validation_function
|
||||
local can_open_and_validated = can_open_view(view_name) and (not validation_function or validation_function())
|
||||
|
||||
-- Checks for inactive, not closing, no other open view, loaded/loadable, and validation
|
||||
if not can_open_and_validated then
|
||||
return
|
||||
end
|
||||
|
||||
local view_options = view_data.view_options
|
||||
local close_all = view_options and view_options.close_all or false
|
||||
local close_previous = view_options and view_options.close_previous or false
|
||||
local close_transition_time = view_options and view_options.close_transition_time or nil
|
||||
local transition_time = view_options and view_options.transition_time or nil
|
||||
|
||||
local view_context = {}
|
||||
local use_transition_ui = view_data.view_settings.use_transition_ui
|
||||
local no_transition_ui = use_transition_ui == false
|
||||
local view_settings_override = no_transition_ui and {
|
||||
use_transition_ui = false
|
||||
}
|
||||
|
||||
-- Open the view with default parameters
|
||||
Managers.ui:open_view(view_name, transition_time, close_previous,
|
||||
close_all, close_transition_time, view_context, view_settings_override)
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- #####################################################################################################################
|
||||
|
@ -398,4 +457,4 @@ end
|
|||
-- #####################################################################################################################
|
||||
|
||||
-- If VMF is reloaded mid-game, get ingame_ui.
|
||||
_ingame_ui = (VT1 and Managers.matchmaking and Managers.matchmaking.ingame_ui) or (Managers.ui and Managers.ui._ingame_ui)
|
||||
_ingame_ui = Managers.ui and Managers.ui._view_handler
|
||||
|
|
|
@ -1,273 +0,0 @@
|
|||
local vmf = get_mod("VMF")
|
||||
|
||||
-- Legacy definitions can't be stripped because it will break following mods.
|
||||
-- Warning for these mods is not shown and their authors were notified about the update.
|
||||
-- However, new mods should not use legacy definitions.
|
||||
local LEGACY_MODS_VT2 = {
|
||||
["crosshairs"] = true, -- Crosshairs Fix (by Skwuruhl)
|
||||
["BuffInfo"] = true, -- Buff Info (by 🔰SkacikPL🗾)
|
||||
["Spooktober"] = true, -- Spooktober (by 🔰SkacikPL🗾)
|
||||
["fly"] = true, -- Aussiemon's Free Flight Mod (by tour.dlang.org/)
|
||||
["Console"] = true, -- Console (by 🔰SkacikPL🗾)
|
||||
["lootRatAmmo"] = true, -- Sack Rat drops ammo (by NonzeroGeoduck7)
|
||||
["ff_notifier"] = true, -- The Not-So-Friendly-Fire Snitch (by Zaphio)
|
||||
["NoGlow"] = true, -- No Glow On Unique Weapons (by prop joe)
|
||||
["StreamingInfo"] = true, -- Info Dump For Streaming (by prop joe)
|
||||
["keyPickupMessage"] = true, -- Notice Key Pickup (by NonzeroGeoduck7)
|
||||
["oldTorch"] = true, -- Torch is not a weapon (by NonzeroGeoduck7)
|
||||
["color"] = true, -- Colorful Unique Weapons (by tour.dlang.org/)
|
||||
["zoom sens"] = true, -- Customizable Zoom Sensitivity (by Skwuruhl)
|
||||
["item_filter"] = true, -- Item Filter (by Gohas)
|
||||
["QuickGameMapSelect"] = true, -- Quick Play - select maps in random order (by NonzeroGeoduck7)
|
||||
["deedNoPickup"] = true, -- Deeds - Tomes / Grims in No-Pickup Mutation (by NonzeroGeoduck7)
|
||||
["hostQuickPlay"] = true, -- Host Solo Quick Play Games (by NonzeroGeoduck7)
|
||||
["RerollImprovements"] = true, -- Reroll Improvements (by prop joe)
|
||||
["bots_impulse_control"] = true, -- Bot Improvements - Impulse Control (by Squatting Bear)
|
||||
["Headshot Only"] = true, -- Headshot Only Mode (by Gohas)
|
||||
["convenience_key_actions"] = true, -- Convenience Key Actions (by Squatting Bear)
|
||||
["Stances"] = true, -- Stances (by 🔰SkacikPL🗾)
|
||||
["bloodyWeapons"] = true, -- Blood for the blood god (by ElCamino)
|
||||
["HideBuffs"] = true, -- UI Tweaks (by prop joe)
|
||||
["Pause"] = true, -- Pause (by prop joe)
|
||||
["armory"] = true, -- Armory (by Fracticality)
|
||||
["InstaKick"] = true, -- Instant Kick (by NonzeroGeoduck7)
|
||||
["AimLines"] = true, -- Aim Lines (by 🔰SkacikPL🗾)
|
||||
["ChatWheel"] = true, -- Chat Wheel (by NonzeroGeoduck7)
|
||||
["traps"] = true, -- Traps (by 🔰SkacikPL🗾)
|
||||
["BossTimer"] = true, -- BossKillTimer (by NonzeroGeoduck7)
|
||||
["Parry Indicator"] = true, -- Parry Indicator (by Gohas)
|
||||
["MMONames"] = true, -- MMO Names (by 🔰SkacikPL🗾)
|
||||
["lumberfoots"] = true, -- Handmaiden has a limited vocabulary (by raindish)
|
||||
["NoWobble"] = true, -- No Wobble (by 🔰SkacikPL🗾)
|
||||
["Weapon Zoom"] = true, -- Weapon Zoom (by Fracticality)
|
||||
["RTAT"] = true, -- Game Speed Changer (by Seelixh 🐥🦆)
|
||||
["loadout_manager_vt2"] = true, -- Loadout Manager (by Squatting Bear)
|
||||
["toggle-crits"] = true, -- Toggle Crits (by Orange Chris)
|
||||
["VermintideReloaded"] = true, -- Vermintide: Reloaded (by Alone and Afraid)
|
||||
["Instagib"] = true, -- Instagib (by Seelixh 🐥🦆)
|
||||
["fb"] = true, -- Fortress Brawl + Melee Friendly Fire (by tour.dlang.org/)
|
||||
["SPF"] = true, -- Single Player Framework (by 🔰SkacikPL🗾)
|
||||
["disco"] = true, -- Disco Lights! (by tour.dlang.org/)
|
||||
["Characters"] = true, -- Any Character! (by tour.dlang.org/)
|
||||
["Bestiary"] = true, -- Bestiary (by Fracticality)
|
||||
["Fleeting time"] = true, -- Fleeting time (by th-om)
|
||||
["gm"] = true, -- Red Shell Disabler + God Mode (by tour.dlang.org/)
|
||||
["GiveWeapon"] = true, -- Give Weapon (by prop joe)
|
||||
["MoreItemsLibrary"] = true, -- More Items Library (by Aussiemon)
|
||||
["Mission timer"] = true, -- Mission timer (by th-om)
|
||||
["feigndeath"] = true, -- Feign Death (by 🔰SkacikPL🗾)
|
||||
["UltReset"] = true, -- Ready Ult (by prop joe)
|
||||
["NerfBats"] = true, -- Nerf Bats (by dragonman68)
|
||||
["perfectdark"] = true, -- Perfect Dark (by 🔰SkacikPL🗾)
|
||||
["Extra-Sensory Deprivation"] = true, -- Extra-Sensory Deprivation (by Dwarf3d)
|
||||
["NoUltCooldown"] = true, -- No Ult Cooldown (by ThePageMan)
|
||||
["BoltStaffTweaks"] = true, -- Bolt Staff Tweaks (by dragonman68)
|
||||
["preparations"] = true, -- Adaptation (Access inventory in match) (by 🔰SkacikPL🗾)
|
||||
["Less Annoying Friendly Fire"] = true, -- Less Annoying Friendly Fire (by pixaal)
|
||||
["RestartLevelCommand"] = true, -- Restart Level Command (by prop joe)
|
||||
["D-Lang"] = true, -- D-Lang (by tour.dlang.org/)
|
||||
["Larger Hordes"] = true, -- Larger Hordes (by \҉/̵i͏cto̡r͘ S̛al͡t̕zpy̵r͏e̢)
|
||||
["Barrels"] = true, -- Barrels Spawns For The Memes! aka the Barrel Meta (by tour.dlang.org/)
|
||||
["Fail Level Hotkey"] = true, -- Restart Level or Return to Keep Hotkeys (by \҉/̵i͏cto̡r͘ S̛al͡t̕zpy̵r͏e̢)
|
||||
["Needii"] = true, -- Needii (by Tomoko 👿)
|
||||
["LockedAndLoaded"] = true, -- LockedAndLoaded (by Badwin)
|
||||
["StickyGrim"] = true, -- StickyGrim (by Badwin)
|
||||
["Dofile"] = true, -- Execute External Lua File (by prop joe)
|
||||
["SoundEventMonitor"] = true, -- [Tool] Sound Event Monitor (by Aussiemon)
|
||||
["E308TestMod"] = true, -- Ammo Decimal Leftovers (by Sgt. Buttersworth [E-308])
|
||||
["Waypoints"] = true, -- Waypoints (by Badwin)
|
||||
["BotImprovements_HeroSelection"] = true, -- Bot Improvements - Hero Selection (by Grimalackt)
|
||||
["NumericUI"] = true, -- Numeric UI (by Necrossin)
|
||||
["BotImprovements_Combat"] = true, -- Bot Improvements - Combat (by Grimalackt)
|
||||
["CreatureSpawner"] = true, -- Creature Spawner (by Aussiemon)
|
||||
["SpawnTweaks"] = true, -- Spawn Tweaks (by prop joe)
|
||||
["MutatorsSelector"] = true, -- Mutators Selector (by prop joe)
|
||||
["FailLevelCommand"] = true, -- Fail/Win/Restart Level Command (by prop joe)
|
||||
["ItemSpawner"] = true, -- Item Spawner (by prop joe)
|
||||
["ui_improvements"] = true, -- UI Improvements (by grasmann)
|
||||
["SkipCutscenes"] = true, -- Skip Cutscenes (by Aussiemon)
|
||||
["HeatIndicator"] = true, -- Heat Indicator (by grasmann)
|
||||
["Healthbars"] = true, -- Healthbars (by grasmann)
|
||||
["ChatBlock"] = true, -- Chat Block (by grasmann)
|
||||
["ShowDamage"] = true, -- Show Damage (by grasmann)
|
||||
["Killbots"] = true, -- Killbots (by prop joe)
|
||||
["CustomHUD"] = true, -- Custom HUD (by prop joe)
|
||||
["ThirdPersonEquipment"] = true, -- Third Person Equipment (by grasmann)
|
||||
["CrosshairCustomization"] = true, -- Crosshair Customization (by prop joe)
|
||||
["TrueSoloQoL"] = true, -- True Solo QoL Tweaks (by prop joe)
|
||||
["NeuterUltEffects"] = true, -- Neuter Ult Effects (by prop joe)
|
||||
["PositiveReinforcementTweaks"] = true, -- Killfeed Tweaks (by prop joe)
|
||||
["ThirdPerson"] = true, -- Third Person (by grasmann)
|
||||
}
|
||||
|
||||
vmf.initialize_mod_options_legacy = function (mod, widgets_definition)
|
||||
if VT1 or LEGACY_MODS_VT2[mod:get_name()] then
|
||||
mod:info("Using deprecated widget definitions. Please, update your mod.")
|
||||
else
|
||||
mod:warning("Using deprecated widget definitions. Please, update your mod.")
|
||||
end
|
||||
|
||||
local mod_settings_list_widgets_definitions = {}
|
||||
|
||||
local new_widget_definition
|
||||
local new_widget_index
|
||||
|
||||
local options_menu_favorite_mods = vmf:get("options_menu_favorite_mods")
|
||||
local options_menu_collapsed_widgets = vmf:get("options_menu_collapsed_widgets")
|
||||
local mod_collapsed_widgets = nil
|
||||
if options_menu_collapsed_widgets then
|
||||
mod_collapsed_widgets = options_menu_collapsed_widgets[mod:get_name()]
|
||||
end
|
||||
|
||||
-- defining header widget
|
||||
|
||||
new_widget_index = 1
|
||||
|
||||
new_widget_definition = {}
|
||||
|
||||
new_widget_definition.type = "header"
|
||||
new_widget_definition.index = new_widget_index
|
||||
new_widget_definition.mod_name = mod:get_name()
|
||||
new_widget_definition.readable_mod_name = mod:get_readable_name()
|
||||
new_widget_definition.tooltip = mod:get_description()
|
||||
new_widget_definition.default = true
|
||||
new_widget_definition.is_togglable = mod:get_internal_data("is_togglable") and
|
||||
not mod:get_internal_data("is_mutator")
|
||||
new_widget_definition.is_collapsed = vmf:get("options_menu_collapsed_mods")[mod:get_name()]
|
||||
|
||||
|
||||
if options_menu_favorite_mods then
|
||||
for _, current_mod_name in pairs(options_menu_favorite_mods) do
|
||||
if current_mod_name == mod:get_name() then
|
||||
new_widget_definition.is_favorited = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(mod_settings_list_widgets_definitions, new_widget_definition)
|
||||
|
||||
-- defining its subwidgets
|
||||
|
||||
if widgets_definition then
|
||||
local level = 1
|
||||
local parent_number = new_widget_index
|
||||
local parent_widget = {["widget_type"] = "header", ["sub_widgets"] = widgets_definition}
|
||||
local current_widget = widgets_definition[1]
|
||||
local current_widget_index = 1
|
||||
|
||||
local parent_number_stack = {}
|
||||
local parent_widget_stack = {}
|
||||
local current_widget_index_stack = {}
|
||||
|
||||
while new_widget_index <= 1024 do
|
||||
|
||||
-- if 'nil', we reached the end of the current level widgets list and need to go up
|
||||
if current_widget then
|
||||
|
||||
new_widget_index = new_widget_index + 1
|
||||
|
||||
new_widget_definition = {}
|
||||
|
||||
new_widget_definition.type = current_widget.widget_type -- all
|
||||
new_widget_definition.index = new_widget_index -- all [gen]
|
||||
new_widget_definition.depth = level -- all [gen]
|
||||
new_widget_definition.mod_name = mod:get_name() -- all [gen]
|
||||
new_widget_definition.setting_id = current_widget.setting_name -- all
|
||||
new_widget_definition.title = current_widget.text -- all
|
||||
new_widget_definition.tooltip = current_widget.tooltip and (current_widget.text .. "\n" ..
|
||||
current_widget.tooltip) -- all [optional]
|
||||
new_widget_definition.unit_text = current_widget.unit_text -- numeric [optional]
|
||||
new_widget_definition.range = current_widget.range -- numeric
|
||||
new_widget_definition.decimals_number = current_widget.decimals_number -- numeric [optional]
|
||||
new_widget_definition.options = current_widget.options -- dropdown
|
||||
new_widget_definition.default_value = current_widget.default_value -- all
|
||||
new_widget_definition.function_name = current_widget.action -- keybind [optional?]
|
||||
new_widget_definition.show_widget_condition = current_widget.show_widget_condition -- all
|
||||
new_widget_definition.parent_index = parent_number -- all [gen]
|
||||
|
||||
if mod_collapsed_widgets then
|
||||
new_widget_definition.is_collapsed = mod_collapsed_widgets[current_widget.setting_name]
|
||||
end
|
||||
|
||||
if type(mod:get(current_widget.setting_name)) == "nil" then
|
||||
mod:set(current_widget.setting_name, current_widget.default_value)
|
||||
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
|
||||
vmf.add_mod_keybind(
|
||||
mod,
|
||||
new_widget_definition.setting_id,
|
||||
{
|
||||
trigger = new_widget_definition.keybind_trigger,
|
||||
type = new_widget_definition.keybind_type,
|
||||
keys = keybind,
|
||||
function_name = new_widget_definition.function_name
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(mod_settings_list_widgets_definitions, new_widget_definition)
|
||||
end
|
||||
|
||||
if current_widget and (
|
||||
current_widget.widget_type == "header" or
|
||||
current_widget.widget_type == "group" or
|
||||
current_widget.widget_type == "checkbox" or
|
||||
current_widget.widget_type == "dropdown"
|
||||
) and current_widget.sub_widgets then
|
||||
|
||||
-- going down to the first subwidget
|
||||
|
||||
level = level + 1
|
||||
|
||||
table.insert(parent_number_stack, parent_number)
|
||||
parent_number = new_widget_index
|
||||
|
||||
table.insert(parent_widget_stack, parent_widget)
|
||||
parent_widget = current_widget
|
||||
|
||||
table.insert(current_widget_index_stack, current_widget_index)
|
||||
current_widget_index = 1
|
||||
current_widget = current_widget.sub_widgets[1]
|
||||
|
||||
else
|
||||
current_widget_index = current_widget_index + 1
|
||||
if parent_widget.sub_widgets[current_widget_index] then
|
||||
-- going to the next widget
|
||||
current_widget = parent_widget.sub_widgets[current_widget_index]
|
||||
else
|
||||
|
||||
-- going up to the widget next to the parent one
|
||||
level = level - 1
|
||||
parent_number = table.remove(parent_number_stack)
|
||||
parent_widget = table.remove(parent_widget_stack)
|
||||
current_widget_index = table.remove(current_widget_index_stack)
|
||||
if not current_widget_index then
|
||||
break
|
||||
end
|
||||
current_widget_index = current_widget_index + 1
|
||||
-- widget next to parent one, or 'nil', if there are no more widgets on this level
|
||||
current_widget = parent_widget.sub_widgets[current_widget_index]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if new_widget_index == 1025 then
|
||||
mod:error("(vmf_options_view) The limit of 256 options widgets was reached. You can't add any more widgets.")
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(vmf.options_widgets_data, mod_settings_list_widgets_definitions)
|
||||
end
|
|
@ -11,6 +11,7 @@ local _commands_list = {}
|
|||
local _command_index = 0 -- 0 => nothing selected
|
||||
|
||||
local _commands_list_gui_draw
|
||||
local _commands_list_gui_destroy
|
||||
|
||||
local _chat_history = {}
|
||||
local _chat_history_index = 0
|
||||
|
@ -21,14 +22,34 @@ local _chat_history_remove_dups_last = false
|
|||
local _chat_history_remove_dups_all = false
|
||||
local _chat_history_save_commands_only = false
|
||||
|
||||
local _queued_command -- is a workaround for VT2 where raycast is blocked during ui update
|
||||
local _chat_message
|
||||
local _previous_chat_message
|
||||
local _queued_command
|
||||
|
||||
-- ####################################################################################################################
|
||||
-- ##### Local functions ##############################################################################################
|
||||
-- ####################################################################################################################
|
||||
|
||||
local function initialize_drawing_function()
|
||||
_commands_list_gui_draw = dofile("scripts/mods/vmf/modules/ui/chat/commands_list_gui")
|
||||
if not _commands_list_gui_draw then
|
||||
local commands_list_gui = vmf:dofile("dmf/scripts/mods/vmf/modules/ui/chat/commands_list_gui")
|
||||
_commands_list_gui_draw = commands_list_gui.draw
|
||||
_commands_list_gui_destroy = commands_list_gui.destroy
|
||||
end
|
||||
end
|
||||
|
||||
local function destroy_command_gui()
|
||||
if _commands_list_gui_destroy then
|
||||
_commands_list_gui_destroy()
|
||||
_commands_list_gui_draw = nil
|
||||
_commands_list_gui_destroy = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function clean_chat_notifications()
|
||||
if Managers.event then
|
||||
Managers.event:trigger("event_clear_notifications")
|
||||
end
|
||||
end
|
||||
|
||||
local function clean_chat_history()
|
||||
|
@ -36,42 +57,42 @@ local function clean_chat_history()
|
|||
_chat_history_index = 0
|
||||
end
|
||||
|
||||
local function get_chat_index(chat_gui)
|
||||
return chat_gui._input_field_widget.content.caret_position
|
||||
end
|
||||
|
||||
local function get_chat_message(chat_gui)
|
||||
return chat_gui._input_field_widget.content.input_text or ""
|
||||
end
|
||||
|
||||
local function set_chat_message(chat_gui, message)
|
||||
chat_gui.chat_message = message
|
||||
chat_gui.chat_index = KeystrokeHelper.num_utf8chars(chat_gui.chat_message) + 1
|
||||
chat_gui.chat_input_widget.content.text_index = 1
|
||||
_chat_message = message
|
||||
chat_gui._input_field_widget.content.input_text = message
|
||||
chat_gui._input_field_widget.content.caret_position = Utf8.string_length(_chat_message) + 1
|
||||
end
|
||||
|
||||
-- ####################################################################################################################
|
||||
-- ##### Hooks ########################################################################################################
|
||||
-- ####################################################################################################################
|
||||
|
||||
vmf:hook_safe(WorldManager, "create_world", function(self_, name)
|
||||
if name == "top_ingame_view" then
|
||||
-- Handle chat actions when the chat window is active
|
||||
vmf:hook("ConstantElementChat", "_handle_active_chat_input", function(func, self, input_service, ui_renderer, ...)
|
||||
initialize_drawing_function()
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
vmf:hook_safe("ChatGui", "block_input", function()
|
||||
_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")) or Keyboard.pressed(Keyboard.button_index("numpad enter")) then
|
||||
_chat_message = get_chat_message(self)
|
||||
_chat_opened = true
|
||||
|
||||
-- if message is sending
|
||||
if input_service:get("send_chat_message") 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 _chat_message ~= ""
|
||||
and not (_chat_history_remove_dups_last and (_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)
|
||||
table.insert(_chat_history, 1, _chat_message)
|
||||
|
||||
if #_chat_history == _chat_history_max + 1 then
|
||||
table.remove(_chat_history, #_chat_history)
|
||||
|
@ -80,7 +101,7 @@ vmf:hook("ChatGui", "_update_input", function(func, self, input_service, menu_in
|
|||
if _chat_history_remove_dups_all then
|
||||
|
||||
for i = 2, #_chat_history do
|
||||
if _chat_history[i] == self.chat_message then
|
||||
if _chat_history[i] == _chat_message then
|
||||
table.remove(_chat_history, i)
|
||||
break
|
||||
end
|
||||
|
@ -91,7 +112,7 @@ vmf:hook("ChatGui", "_update_input", function(func, self, input_service, menu_in
|
|||
-- command execution
|
||||
if _command_index ~= 0 then
|
||||
local args = {}
|
||||
for arg in string.gmatch(self.chat_message, "%S+") do
|
||||
for arg in string.gmatch(_chat_message, "%S+") do
|
||||
table.insert(args, arg)
|
||||
end
|
||||
table.remove(args, 1)
|
||||
|
@ -107,13 +128,21 @@ vmf:hook("ChatGui", "_update_input", function(func, self, input_service, menu_in
|
|||
set_chat_message(self, "")
|
||||
|
||||
command_executed = true
|
||||
|
||||
elseif string.sub(_chat_message, 1, 1) == "/" then
|
||||
vmf:notify(vmf:localize("chat_command_not_recognized") .. ": " .. _chat_message)
|
||||
set_chat_message(self, "")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local old_chat_message = self.chat_message
|
||||
local old_chat_message = _chat_message
|
||||
|
||||
local chat_focused, chat_closed, chat_close_time = func(self, input_service, menu_input_service,
|
||||
dt, no_unblock, chat_enabled, ...)
|
||||
local result = func(self, input_service, ui_renderer, ...)
|
||||
|
||||
-- Get completion state
|
||||
local input_widget = self._input_field_widget
|
||||
local chat_closed = not input_widget.content.is_writing
|
||||
|
||||
if chat_closed then
|
||||
set_chat_message(self, "")
|
||||
|
@ -123,24 +152,19 @@ vmf:hook("ChatGui", "_update_input", function(func, self, input_service, menu_in
|
|||
_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
|
||||
-- getting state of 'arrow right', 'arrow up' and 'arrow down' buttons
|
||||
local arrow_right_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
|
||||
if stroke == Keyboard.RIGHT and Keyboard.button(Keyboard.button_index("left ctrl")) == 0 then
|
||||
arrow_right_pressed = true
|
||||
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
|
||||
|
@ -151,14 +175,12 @@ vmf:hook("ChatGui", "_update_input", function(func, self, input_service, menu_in
|
|||
-- chat history
|
||||
if _chat_history_enabled then
|
||||
|
||||
-- reverse result of native chat history in VT2
|
||||
if not VT1 and input_service.get(input_service, "chat_next_old_message") or
|
||||
input_service.get(input_service, "chat_previous_old_message") then
|
||||
if arrow_up_pressed then
|
||||
set_chat_message(self, old_chat_message)
|
||||
end
|
||||
|
||||
-- message was modified by player
|
||||
if self.chat_message ~= self.previous_chat_message then
|
||||
if _chat_message ~= _previous_chat_message then
|
||||
_chat_history_index = 0
|
||||
end
|
||||
if arrow_up_pressed or arrow_down_pressed then
|
||||
|
@ -171,7 +193,7 @@ vmf:hook("ChatGui", "_update_input", function(func, self, input_service, menu_in
|
|||
|
||||
set_chat_message(self, _chat_history[new_index])
|
||||
|
||||
self.previous_chat_message = self.chat_message
|
||||
_previous_chat_message = _chat_message
|
||||
|
||||
_chat_history_index = new_index
|
||||
else -- new_index == 0
|
||||
|
@ -181,41 +203,15 @@ vmf:hook("ChatGui", "_update_input", function(func, self, input_service, menu_in
|
|||
end
|
||||
end
|
||||
|
||||
-- ctrl + v
|
||||
if VT1 and Keyboard.pressed(Keyboard.button_index("v")) and Keyboard.button(Keyboard.button_index("left ctrl")) == 1 then
|
||||
local new_chat_message = self.chat_message
|
||||
|
||||
-- remove carriage returns
|
||||
local clipboard_data = tostring(Clipboard.get()):gsub("\r", "")
|
||||
|
||||
-- remove invalid characters
|
||||
if Utf8.valid(clipboard_data) then
|
||||
new_chat_message = new_chat_message .. clipboard_data
|
||||
else
|
||||
local valid_data = ""
|
||||
clipboard_data:gsub(".", function(c)
|
||||
if Utf8.valid(c) then
|
||||
valid_data = valid_data .. c
|
||||
end
|
||||
end)
|
||||
new_chat_message = new_chat_message .. valid_data
|
||||
end
|
||||
|
||||
set_chat_message(self, new_chat_message)
|
||||
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 string.sub(_chat_message, 1, 1) == "/" then
|
||||
|
||||
-- if there's no space after '/part_of_command_name' and if TAB was pressed
|
||||
if not string.find(self.chat_message, " ") and tab_pressed and
|
||||
-- if TAB was pressed with caret at the end of the string
|
||||
(string.len(self.chat_message) + 1) == self.chat_index and
|
||||
local autocompleting = false
|
||||
|
||||
-- if there's no space after '/part_of_command_name' and if arrow_right was pressed
|
||||
if not string.find(_chat_message, " ") and arrow_right_pressed and
|
||||
-- if arrow_right was pressed with caret at the end of the string
|
||||
(string.len(_chat_message) + 1) == get_chat_index(self) and
|
||||
-- if there are any commands matching entered '/part_of_command_name
|
||||
(#_commands_list > 0) then
|
||||
|
||||
|
@ -224,16 +220,15 @@ vmf:hook("ChatGui", "_update_input", function(func, self, input_service, menu_in
|
|||
set_chat_message(self, "/" .. _commands_list[_command_index].name)
|
||||
|
||||
-- so the next block won't update the commands list
|
||||
old_chat_message = self.chat_message
|
||||
autocompleting = true
|
||||
end
|
||||
|
||||
|
||||
if self.chat_message ~= old_chat_message then
|
||||
|
||||
if not autocompleting or not vmf._commands_list_gui_draw then
|
||||
-- get '/part_of_command_name' without '/'
|
||||
local command_name_contains = self.chat_message:match("%S+"):sub(2, -1)
|
||||
local command_name_contains = _chat_message:match("%S+"):sub(2, -1)
|
||||
|
||||
if string.find(self.chat_message, " ") then
|
||||
if string.find(_chat_message, " ") then
|
||||
_commands_list = vmf.get_commands_list(command_name_contains, true)
|
||||
else
|
||||
_commands_list = vmf.get_commands_list(command_name_contains)
|
||||
|
@ -248,7 +243,7 @@ vmf:hook("ChatGui", "_update_input", function(func, self, input_service, menu_in
|
|||
|
||||
|
||||
-- chat message was modified and doesn't start with '/'
|
||||
elseif self.chat_message ~= old_chat_message and #_commands_list > 0 then
|
||||
elseif #_commands_list > 0 then
|
||||
_commands_list = {}
|
||||
_command_index = 0
|
||||
end
|
||||
|
@ -258,7 +253,7 @@ vmf:hook("ChatGui", "_update_input", function(func, self, input_service, menu_in
|
|||
end
|
||||
end
|
||||
|
||||
return chat_focused, chat_closed, chat_close_time
|
||||
return result
|
||||
end)
|
||||
|
||||
-- ####################################################################################################################
|
||||
|
@ -303,16 +298,19 @@ vmf.execute_queued_chat_command = function()
|
|||
end
|
||||
end
|
||||
|
||||
vmf.destroy_command_gui = function()
|
||||
destroy_command_gui()
|
||||
end
|
||||
|
||||
-- ####################################################################################################################
|
||||
-- ##### Script #######################################################################################################
|
||||
-- ####################################################################################################################
|
||||
|
||||
vmf.load_chat_history_settings()
|
||||
vmf:command("clean_chat_notifications", vmf:localize("clean_chat_notifications"), clean_chat_notifications)
|
||||
|
||||
if _chat_history_save then
|
||||
_chat_history = vmf:get("chat_history") or _chat_history
|
||||
end
|
||||
|
||||
if Managers.world and Managers.world:has_world("top_ingame_view") then
|
||||
initialize_drawing_function()
|
||||
end
|
||||
initialize_drawing_function()
|
|
@ -2,7 +2,10 @@ local vmf = get_mod("VMF")
|
|||
|
||||
local MULTISTRING_INDICATOR_TEXT = "[...]"
|
||||
|
||||
local FONT_TYPE = "hell_shark_arial"
|
||||
local DEFAULT_HUD_SCALE = 100
|
||||
|
||||
local FONT_TYPE = "arial"
|
||||
local FONT_MATERIAL = "content/ui/fonts/arial"
|
||||
local FONT_SIZE = 22
|
||||
|
||||
local MAX_COMMANDS_VISIBLE = 5
|
||||
|
@ -11,41 +14,118 @@ local STRING_HEIGHT = 25
|
|||
local STRING_Y_OFFSET = 7
|
||||
local STRING_X_MARGIN = 10
|
||||
|
||||
local OFFSET_X = 0
|
||||
local OFFSET_Y = 200
|
||||
local OFFSET_X = 10
|
||||
local OFFSET_Y = 300
|
||||
local OFFSET_Z = 880
|
||||
local WIDTH = 550
|
||||
|
||||
local _gui = World.create_screen_gui(Managers.world:world("top_ingame_view"), "immediate",
|
||||
"material", "materials/fonts/gw_fonts")
|
||||
local BASE_COMMAND_TEXT_WIDTH = WIDTH - STRING_X_MARGIN * 2
|
||||
|
||||
local _gui
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### 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 function create_gui()
|
||||
if not _gui then
|
||||
local world_manager = Managers.world
|
||||
if world_manager and world_manager:has_world("level_world") then
|
||||
_gui = World.create_screen_gui(world_manager:world("level_world"), "immediate")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function destroy_gui()
|
||||
if _gui then
|
||||
local world_manager = Managers.world
|
||||
if world_manager and world_manager:has_world("level_world") then
|
||||
World.destroy_gui(world_manager:world("level_world"), _gui)
|
||||
end
|
||||
_gui = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function get_hud_scale()
|
||||
local save_data = Managers.save:account_data()
|
||||
local interface_settings = save_data.interface_settings
|
||||
local hud_scale = interface_settings.hud_scale or DEFAULT_HUD_SCALE
|
||||
|
||||
return hud_scale
|
||||
end
|
||||
|
||||
|
||||
local function get_text_size(text, font_type, font_size)
|
||||
local font_data = Managers.font:data_by_type(font_type)
|
||||
local font = font_data.path
|
||||
local additional_settings = {
|
||||
flags = font_data.render_flags or 0
|
||||
}
|
||||
|
||||
local min, max, caret = Gui2.slug_text_extents(_gui, text, font, font_size, additional_settings)
|
||||
local min_x, min_y = Vector3.to_elements(min)
|
||||
local max_x, max_y = Vector3.to_elements(max)
|
||||
local width = max_x - min_x
|
||||
local height = max_y - min_y
|
||||
|
||||
return width, height, min, caret
|
||||
end
|
||||
|
||||
|
||||
local function get_text_width(text, font, font_size)
|
||||
local text_extent_min, text_extent_max = Gui.slug_text_extents(_gui, text, font, 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 function get_scaled_font_size_by_width(text, font_type, font_size, max_width)
|
||||
local scale = RESOLUTION_LOOKUP.scale
|
||||
local min_font_size = 1
|
||||
local scaled_font_size = math.max(font_size * scale, 1)
|
||||
local text_width = get_text_size(text, font_type, scaled_font_size)
|
||||
|
||||
if max_width < text_width then
|
||||
repeat
|
||||
if font_size <= min_font_size then
|
||||
break
|
||||
end
|
||||
|
||||
font_size = math.max(font_size - 1, min_font_size)
|
||||
scaled_font_size = math.max(font_size * scale, 1)
|
||||
text_width = math.floor(get_text_size(text, font_type, scaled_font_size))
|
||||
until text_width <= max_width
|
||||
end
|
||||
|
||||
return font_size
|
||||
end
|
||||
|
||||
|
||||
local function word_wrap(text, font, font_size, max_width)
|
||||
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)
|
||||
return Gui.slug_word_wrap(_gui, text, font, font_size, max_width * scale, return_dividers,
|
||||
soft_dividers, reuse_global_table, 0)
|
||||
end
|
||||
|
||||
|
||||
local function draw(commands_list, selected_command_index)
|
||||
-- VT2 requires applying additional HUD scaling
|
||||
if not VT1 and UISettings.use_custom_hud_scale then
|
||||
UPDATE_RESOLUTION_LOOKUP(true, UISettings.hud_scale * 0.01)
|
||||
|
||||
create_gui()
|
||||
if not _gui then
|
||||
return
|
||||
end
|
||||
|
||||
-- Apply additional HUD scaling
|
||||
local hud_scale = get_hud_scale()
|
||||
local should_scale = hud_scale ~= DEFAULT_HUD_SCALE
|
||||
if should_scale then
|
||||
UPDATE_RESOLUTION_LOOKUP(true, hud_scale * 0.01)
|
||||
end
|
||||
|
||||
local selected_command_new_index = 0
|
||||
|
@ -57,7 +137,7 @@ local function draw(commands_list, selected_command_index)
|
|||
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.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
|
||||
|
@ -67,38 +147,36 @@ local function draw(commands_list, selected_command_index)
|
|||
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]
|
||||
local font_size = FONT_SIZE
|
||||
|
||||
for i, command in ipairs(displayed_commands) do
|
||||
font_size = get_scaled_font_size_by_width(command.name, FONT_TYPE, FONT_SIZE, BASE_COMMAND_TEXT_WIDTH)
|
||||
|
||||
-- draw "/command_name" text
|
||||
local scaled_offet_x = (OFFSET_X + STRING_X_MARGIN) * scale
|
||||
local scaled_offset_y = (OFFSET_Y - STRING_HEIGHT * (i + selected_strings_number - 1) + STRING_Y_OFFSET) * scale
|
||||
|
||||
local string_position = Vector3(scaled_offet_x, scaled_offset_y, OFFSET_Z + 2)
|
||||
Gui.text(_gui, command.name, font_material, font_size, font_name, string_position, Color(255, 100, 255, 100))
|
||||
Gui.slug_text(_gui, command.name, FONT_MATERIAL, font_size, string_position, nil, Color(255, 100, 255, 100))
|
||||
|
||||
local command_text_strings = word_wrap(command.full_text, font_material, font_size, WIDTH - STRING_X_MARGIN * 2)
|
||||
local command_text_strings = word_wrap(command.full_text, FONT_MATERIAL, font_size, BASE_COMMAND_TEXT_WIDTH)
|
||||
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)
|
||||
local command_text_width = WIDTH - STRING_X_MARGIN * 2 - (multistring_indicator_width / scale)
|
||||
command_text_strings = word_wrap(command.full_text, font_material, font_size, command_text_width)
|
||||
local multistring_indicator_width = get_text_width(MULTISTRING_INDICATOR_TEXT, FONT_MATERIAL, font_size)
|
||||
local command_text_width = BASE_COMMAND_TEXT_WIDTH - (multistring_indicator_width / scale)
|
||||
command_text_strings = word_wrap(command.full_text, FONT_MATERIAL, font_size, command_text_width)
|
||||
|
||||
-- draw that [...] thing
|
||||
local multistring_offset_x = (OFFSET_X + WIDTH) * scale - multistring_indicator_width
|
||||
local multistring_indicator_position = Vector3(multistring_offset_x, 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))
|
||||
Gui.slug_text(_gui, MULTISTRING_INDICATOR_TEXT, FONT_MATERIAL, font_size,
|
||||
multistring_indicator_position, nil, Color(255, 100, 100, 100))
|
||||
end
|
||||
first_description_string = string.sub(command_text_strings[1], #command.name + 2)
|
||||
else
|
||||
|
@ -106,19 +184,19 @@ local function draw(commands_list, selected_command_index)
|
|||
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_width = get_text_width(command.name, FONT_MATERIAL, font_size)
|
||||
|
||||
local first_description_pos_x = string_position.x + first_description_string_width
|
||||
local first_description_string_position = Vector3(first_description_pos_x, 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))
|
||||
Gui.slug_text(_gui, first_description_string, FONT_MATERIAL, font_size,
|
||||
first_description_string_position, nil, 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))
|
||||
Gui.slug_text(_gui, command_text_strings[j], FONT_MATERIAL, font_size,
|
||||
string_position, nil, Color(255, 255, 255, 255))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -149,15 +227,15 @@ local function draw(commands_list, selected_command_index)
|
|||
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_width = get_text_width(total_number_indicator, FONT_MATERIAL, font_size)
|
||||
local total_number_indicator_x = (WIDTH) * scale - total_number_indicator_width
|
||||
local total_number_indicator_y = (OFFSET_Y + STRING_Y_OFFSET) * scale
|
||||
local total_number_indicator_position = Vector3(total_number_indicator_x, total_number_indicator_y, OFFSET_Z + 2)
|
||||
Gui.text(_gui, total_number_indicator, font_material, font_size, font_name,
|
||||
total_number_indicator_position, Color(255, 100, 100, 100))
|
||||
Gui.slug_text(_gui, total_number_indicator, FONT_MATERIAL, font_size,
|
||||
total_number_indicator_position, nil, Color(255, 100, 100, 100))
|
||||
end
|
||||
|
||||
if not VT1 and UISettings.use_custom_hud_scale then
|
||||
if should_scale then
|
||||
UPDATE_RESOLUTION_LOOKUP(true)
|
||||
end
|
||||
end
|
||||
|
@ -186,4 +264,4 @@ end
|
|||
-- ##### Return ########################################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
||||
return draw
|
||||
return { draw = draw, destroy = destroy_gui }
|
||||
|
|
|
@ -1,424 +0,0 @@
|
|||
local vmf = get_mod("VMF")
|
||||
|
||||
local _MUTATORS = vmf.mutators
|
||||
|
||||
local _SELECTED_DIFFICULTY_KEY -- Currently selected difficulty in the map view.
|
||||
|
||||
local _DEFINITIONS = dofile("scripts/mods/vmf/modules/ui/mutators/mutators_gui_definitions")
|
||||
local _UI_SCENEGRAPH
|
||||
local _MUTATOR_LIST_WIDGETS = {}
|
||||
local _PARTY_BUTTON_WIDGET
|
||||
local _NO_MUTATORS_TEXT_WIDGET
|
||||
local _OTHER_WIDGETS = {}
|
||||
|
||||
local _IS_MUTATOR_LIST_VISIBLE -- 'true' if Mutator view is active, 'false' if Party view is active.
|
||||
local _CURRENT_PAGE_NUMBER
|
||||
local _TOTAL_PAGES_NUMBER
|
||||
|
||||
local _IS_MUTATORS_GUI_INITIALIZED = false
|
||||
|
||||
-- ####################################################################################################################
|
||||
-- ##### Local functions ##############################################################################################
|
||||
-- ####################################################################################################################
|
||||
|
||||
local function get_map_view()
|
||||
local map_view_exists, map_view = pcall(function () return Managers.matchmaking.ingame_ui.views.map_view end)
|
||||
if map_view_exists then
|
||||
return map_view
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Toggles mutator list (switches between Party and Mutators views).
|
||||
local function show_mutator_list(map_view, is_visible)
|
||||
|
||||
_IS_MUTATOR_LIST_VISIBLE = is_visible
|
||||
|
||||
if is_visible then
|
||||
|
||||
-- Banner
|
||||
local banner_widget = map_view.background_widgets[3]
|
||||
banner_widget.style.text.localize = false
|
||||
banner_widget.style.tooltip_text.localize = false
|
||||
banner_widget.content.text = vmf:localize("mutators_title")
|
||||
banner_widget.content.tooltip_text = vmf:localize("mutators_banner_tooltip")
|
||||
|
||||
-- Players list
|
||||
for _, widget in ipairs(map_view.player_list_widgets) do
|
||||
widget.content.visible = false
|
||||
end
|
||||
|
||||
-- Players counter
|
||||
map_view.player_list_conuter_text_widget.content.visible = false
|
||||
else
|
||||
|
||||
-- Banner
|
||||
local banner_widget = map_view.background_widgets[3]
|
||||
banner_widget.style.text.localize = true
|
||||
banner_widget.style.tooltip_text.localize = true
|
||||
banner_widget.content.text = "map_party_title"
|
||||
banner_widget.content.tooltip_text = "map_party_setting_tooltip"
|
||||
|
||||
-- Players list
|
||||
for _, widget in ipairs(map_view.player_list_widgets) do
|
||||
widget.content.visible = true
|
||||
end
|
||||
|
||||
-- Players counter
|
||||
map_view.player_list_conuter_text_widget.content.visible = true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function change_map_view_look(map_view, is_vmf_look)
|
||||
|
||||
if is_vmf_look then
|
||||
map_view.ui_scenegraph.settings_button.position[1] = -50
|
||||
map_view.ui_scenegraph.friends_button.position[1] = 50
|
||||
map_view.ui_scenegraph.lobby_button.position[1] = 150
|
||||
else
|
||||
map_view.ui_scenegraph.settings_button.position[1] = -100
|
||||
map_view.ui_scenegraph.friends_button.position[1] = 0
|
||||
map_view.ui_scenegraph.lobby_button.position[1] = 100
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Used in the next function to calculate tooltip offset, since Fatshark's solution doesn't support
|
||||
-- tooltips with cursor being in the left-bottom corner.
|
||||
local function calculate_tooltip_offset (widget_content, widget_style, ui_renderer)
|
||||
|
||||
local input_service = ui_renderer.input_service
|
||||
if input_service then
|
||||
|
||||
local cursor_position = UIInverseScaleVectorToResolution(input_service:get("cursor"))
|
||||
if cursor_position then
|
||||
|
||||
local text = widget_content.tooltip_text
|
||||
local max_width = widget_style.max_width
|
||||
|
||||
local font, font_size = UIFontByResolution(widget_style)
|
||||
local font_name = font[3]
|
||||
local font_material = font[1]
|
||||
|
||||
local _, font_min, font_max = UIGetFontHeight(ui_renderer.gui, font_name, font_size)
|
||||
|
||||
local texts = UIRenderer.word_wrap(ui_renderer, text, font_material, font_size, max_width)
|
||||
local num_texts = #texts
|
||||
local full_font_height = (font_max + math.abs(font_min)) * RESOLUTION_LOOKUP.inv_scale
|
||||
|
||||
local tooltip_height = full_font_height * num_texts
|
||||
|
||||
widget_style.cursor_offset[1] = widget_style.cursor_default_offset[1]
|
||||
widget_style.cursor_offset[2] = widget_style.cursor_default_offset[2] - (tooltip_height * UIResolutionScale())
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Callback function for mutator widgets. It's not defined in definitions file because it works with mutators array.
|
||||
-- And it's easier to work with it from there.
|
||||
local function offset_function_callback(ui_scenegraph_, style, content, ui_renderer)
|
||||
|
||||
local mutator = content.mutator
|
||||
|
||||
-- Find out if mutator can be enabled.
|
||||
local can_be_enabled = true
|
||||
|
||||
local mutator_compatibility_config = mutator:get_internal_data("mutator_config").compatibility
|
||||
local is_mostly_compatible = mutator_compatibility_config.is_mostly_compatible
|
||||
local except = mutator_compatibility_config.except
|
||||
|
||||
for _, other_mutator in ipairs(_MUTATORS) do
|
||||
if other_mutator:is_enabled() and other_mutator ~= mutator then
|
||||
can_be_enabled = can_be_enabled and (is_mostly_compatible and not except[other_mutator] or
|
||||
not is_mostly_compatible and except[other_mutator])
|
||||
end
|
||||
end
|
||||
|
||||
can_be_enabled = can_be_enabled and mutator_compatibility_config.compatible_difficulties[_SELECTED_DIFFICULTY_KEY]
|
||||
|
||||
content.can_be_enabled = can_be_enabled
|
||||
|
||||
-- Enable/disable mutator.
|
||||
if content.highlight_hotspot.on_release then
|
||||
if mutator:is_enabled() then
|
||||
vmf.mod_state_changed(mutator:get_name(), false)
|
||||
elseif can_be_enabled then
|
||||
vmf.mod_state_changed(mutator:get_name(), true)
|
||||
end
|
||||
end
|
||||
|
||||
-- Build tooltip (only for currently selected mutator widget).
|
||||
if content.highlight_hotspot.is_hover then
|
||||
|
||||
-- DESCRIPTION
|
||||
|
||||
local tooltip_text = content.description
|
||||
|
||||
-- MUTATORS COMPATIBILITY
|
||||
|
||||
local incompatible_mods = {}
|
||||
if next(except) then
|
||||
tooltip_text = tooltip_text .. (is_mostly_compatible and vmf:localize("tooltip_incompatible_mutators") or
|
||||
vmf:localize("tooltip_compatible_mutators"))
|
||||
|
||||
for other_mutator, _ in pairs(except) do
|
||||
table.insert(incompatible_mods, " * " .. other_mutator:get_readable_name())
|
||||
end
|
||||
|
||||
tooltip_text = tooltip_text .. table.concat(incompatible_mods, "\n")
|
||||
else
|
||||
if is_mostly_compatible then
|
||||
tooltip_text = tooltip_text .. vmf:localize("tooltip_compatible_with_all_mutators")
|
||||
else
|
||||
tooltip_text = tooltip_text .. vmf:localize("tooltip_incompatible_with_all_mutators")
|
||||
end
|
||||
end
|
||||
|
||||
-- DIFFICULTIES COMPATIBILITY
|
||||
|
||||
local difficulties = {}
|
||||
local compatible_difficulties_number = mutator_compatibility_config.compatible_difficulties_number
|
||||
if compatible_difficulties_number < 8 then
|
||||
tooltip_text = tooltip_text .. (compatible_difficulties_number > 4 and
|
||||
vmf:localize("tooltip_incompatible_diffs") or
|
||||
vmf:localize("tooltip_compatible_diffs"))
|
||||
|
||||
for difficulty_key, is_compatible in pairs(mutator_compatibility_config.compatible_difficulties) do
|
||||
if compatible_difficulties_number > 4 and not is_compatible
|
||||
or not (compatible_difficulties_number > 4) and is_compatible then
|
||||
table.insert(difficulties, " * " .. vmf:localize(difficulty_key))
|
||||
end
|
||||
end
|
||||
|
||||
tooltip_text = tooltip_text .. table.concat(difficulties, "\n")
|
||||
else
|
||||
tooltip_text = tooltip_text .. vmf:localize("tooltip_compatible_with_all_diffs")
|
||||
end
|
||||
|
||||
-- CONFLICTS
|
||||
|
||||
if not can_be_enabled then
|
||||
tooltip_text = tooltip_text .. vmf:localize("tooltip_conflicts")
|
||||
|
||||
local conflicting_mods = {}
|
||||
for _, other_mutator in ipairs(_MUTATORS) do
|
||||
if other_mutator:is_enabled() and other_mutator ~= mutator then
|
||||
if not (is_mostly_compatible and not except[other_mutator] or
|
||||
not is_mostly_compatible and except[other_mutator]) then
|
||||
|
||||
table.insert(conflicting_mods, " * " .. other_mutator:get_readable_name() ..
|
||||
vmf:localize("tooltip_append_mutator"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #conflicting_mods > 0 then
|
||||
tooltip_text = tooltip_text .. table.concat(conflicting_mods, "\n") .. "\n"
|
||||
end
|
||||
|
||||
if not mutator_compatibility_config.compatible_difficulties[_SELECTED_DIFFICULTY_KEY] then
|
||||
tooltip_text = tooltip_text .. " * " .. vmf:localize(_SELECTED_DIFFICULTY_KEY) ..
|
||||
vmf:localize("tooltip_append_difficulty")
|
||||
end
|
||||
end
|
||||
|
||||
-- Applying tooltip
|
||||
|
||||
content.tooltip_text = tooltip_text
|
||||
calculate_tooltip_offset(content, style.tooltip_text, ui_renderer)
|
||||
end
|
||||
|
||||
-- Visual changing (text color and checkboxes).
|
||||
local is_enabled = content.mutator:is_enabled()
|
||||
|
||||
style.text.text_color = content.can_be_enabled and (is_enabled and content.text_color_enabled or
|
||||
content.text_color_disabled) or content.text_color_inactive
|
||||
|
||||
content.checkbox_texture = is_enabled and content.checkbox_checked_texture or
|
||||
content.checkbox_unchecked_texture
|
||||
end
|
||||
|
||||
|
||||
local function initialize_scrollbar()
|
||||
|
||||
local scrollbar_widget_content = _OTHER_WIDGETS.scrollbar.content
|
||||
|
||||
if _TOTAL_PAGES_NUMBER > 1 then
|
||||
scrollbar_widget_content.visible = true
|
||||
scrollbar_widget_content.scroll_bar_info.bar_height_percentage = 1 / _TOTAL_PAGES_NUMBER
|
||||
else
|
||||
scrollbar_widget_content.visible = false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function initialize_mutators_ui(map_view)
|
||||
|
||||
-- Scenegraph
|
||||
_UI_SCENEGRAPH = UISceneGraph.init_scenegraph(_DEFINITIONS.scenegraph_definition)
|
||||
|
||||
-- Creating mutator list widgets and calculating total pages number
|
||||
for i, mutator in ipairs(_MUTATORS) do
|
||||
local offset = ((i - 1) % 8) + 1
|
||||
_MUTATOR_LIST_WIDGETS[i] = UIWidget.init(_DEFINITIONS.create_mutator_widget(mutator, offset_function_callback))
|
||||
_MUTATOR_LIST_WIDGETS[i].offset = {0, -32 * offset, 0}
|
||||
_MUTATOR_LIST_WIDGETS[i].content.mutator = mutator
|
||||
end
|
||||
_CURRENT_PAGE_NUMBER = 1
|
||||
_TOTAL_PAGES_NUMBER = math.floor(#_MUTATORS / 8) + ((#_MUTATORS % 8 > 0) and 1 or 0)
|
||||
|
||||
-- Party button
|
||||
_PARTY_BUTTON_WIDGET = UIWidget.init(_DEFINITIONS.party_button_widget_defenition)
|
||||
|
||||
-- "No mutators installed" text
|
||||
_NO_MUTATORS_TEXT_WIDGET = UIWidget.init(_DEFINITIONS.no_mutators_text_widget)
|
||||
|
||||
-- Other widgets
|
||||
for widget_name, widget in pairs(_DEFINITIONS.widgets_definition) do
|
||||
_OTHER_WIDGETS[widget_name] = UIWidget.init(widget)
|
||||
end
|
||||
|
||||
-- Modify original map_view look
|
||||
change_map_view_look(map_view, true)
|
||||
show_mutator_list(map_view, true)
|
||||
|
||||
-- Find out if scrollbar is needed, calculate scrollbar size
|
||||
initialize_scrollbar()
|
||||
|
||||
_IS_MUTATORS_GUI_INITIALIZED = true
|
||||
end
|
||||
|
||||
|
||||
local function draw(map_view, dt)
|
||||
local input_service = map_view.input_manager:get_service("map_menu")
|
||||
local ui_renderer = map_view.ui_renderer
|
||||
local render_settings = map_view.render_settings
|
||||
|
||||
UIRenderer.begin_pass(ui_renderer, _UI_SCENEGRAPH, input_service, dt, nil, render_settings)
|
||||
|
||||
-- Party button
|
||||
UIRenderer.draw_widget(ui_renderer, _PARTY_BUTTON_WIDGET)
|
||||
|
||||
if _IS_MUTATOR_LIST_VISIBLE then
|
||||
if #_MUTATORS > 0 then
|
||||
-- Mutator list (render only 8 (or less) currently visible mutator widgets)
|
||||
for i = ((_CURRENT_PAGE_NUMBER - 1) * 8 + 1), (_CURRENT_PAGE_NUMBER * 8) do
|
||||
if not _MUTATOR_LIST_WIDGETS[i] then
|
||||
break
|
||||
end
|
||||
UIRenderer.draw_widget(ui_renderer, _MUTATOR_LIST_WIDGETS[i])
|
||||
end
|
||||
else
|
||||
UIRenderer.draw_widget(ui_renderer, _NO_MUTATORS_TEXT_WIDGET)
|
||||
end
|
||||
|
||||
-- Other widgets
|
||||
for _, widget in pairs(_OTHER_WIDGETS) do
|
||||
UIRenderer.draw_widget(ui_renderer, widget)
|
||||
end
|
||||
end
|
||||
|
||||
UIRenderer.end_pass(ui_renderer)
|
||||
end
|
||||
|
||||
|
||||
-- Sets new scrollbar position (called when user changes the current page number with mouse scroll input)
|
||||
local function update_scrollbar_position()
|
||||
local scrollbar_widget_content = _OTHER_WIDGETS.scrollbar.content
|
||||
local percentage = (1 / (_TOTAL_PAGES_NUMBER - 1)) * (_CURRENT_PAGE_NUMBER - 1)
|
||||
scrollbar_widget_content.scroll_bar_info.value = percentage
|
||||
scrollbar_widget_content.scroll_bar_info.old_value = percentage
|
||||
end
|
||||
|
||||
|
||||
-- Reads scrollbar input and if it was changed, set current page according to the new scrollbar position
|
||||
local function update_scrollbar_input()
|
||||
local scrollbar_widget_content = _OTHER_WIDGETS.scrollbar.content
|
||||
if scrollbar_widget_content.visible then
|
||||
local scrollbar_info = scrollbar_widget_content.scroll_bar_info
|
||||
local value = scrollbar_info.value
|
||||
local old_value = scrollbar_info.old_value
|
||||
if value ~= old_value then
|
||||
_CURRENT_PAGE_NUMBER = math.clamp(math.ceil(value / (1 / _TOTAL_PAGES_NUMBER)), 1, _TOTAL_PAGES_NUMBER)
|
||||
scrollbar_info.old_value = value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Reads mousewheel scrolls from corresponding widget and changes current page number, if possible.
|
||||
local function update_mousewheel_scroll_area_input()
|
||||
local widget_content = _OTHER_WIDGETS.mousewheel_scroll_area.content
|
||||
local mouse_scroll_value = widget_content.scroll_value
|
||||
if mouse_scroll_value ~= 0 then
|
||||
_CURRENT_PAGE_NUMBER = math.clamp(_CURRENT_PAGE_NUMBER + mouse_scroll_value, 1, _TOTAL_PAGES_NUMBER)
|
||||
widget_content.scroll_value = 0
|
||||
update_scrollbar_position()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function update_mutators_ui(map_view, dt)
|
||||
|
||||
-- Show/hide mutator list if party button was pressed
|
||||
local transitioning = map_view:transitioning()
|
||||
local friends_menu_active = map_view.friends:is_active()
|
||||
if not transitioning and not friends_menu_active then
|
||||
local mutators_button_content = _PARTY_BUTTON_WIDGET.content
|
||||
if mutators_button_content.button_hotspot.on_release then
|
||||
map_view:play_sound("Play_hud_select")
|
||||
show_mutator_list(map_view, mutators_button_content.toggled)
|
||||
mutators_button_content.toggled = not mutators_button_content.toggled
|
||||
end
|
||||
end
|
||||
|
||||
update_mousewheel_scroll_area_input()
|
||||
update_scrollbar_input()
|
||||
draw(map_view, dt)
|
||||
end
|
||||
|
||||
-- ####################################################################################################################
|
||||
-- ##### Hooks ########################################################################################################
|
||||
-- ####################################################################################################################
|
||||
|
||||
vmf:hook_safe(MapView, "init", function (self)
|
||||
initialize_mutators_ui(self)
|
||||
end)
|
||||
|
||||
|
||||
vmf:hook_safe(MapView, "update", function (self, dt)
|
||||
|
||||
if self.menu_active and _IS_MUTATORS_GUI_INITIALIZED then
|
||||
|
||||
-- Parse currently selected difficulty in the map_view
|
||||
local difficulty_data = self.selected_level_index and self:get_difficulty_data(self.selected_level_index)
|
||||
local difficulty_layout = difficulty_data and difficulty_data[self.selected_difficulty_stepper_index]
|
||||
_SELECTED_DIFFICULTY_KEY = difficulty_layout and difficulty_layout.key
|
||||
|
||||
update_mutators_ui(self, dt)
|
||||
end
|
||||
end)
|
||||
|
||||
-- ####################################################################################################################
|
||||
-- ##### VMF internal functions and variables #########################################################################
|
||||
-- ####################################################################################################################
|
||||
|
||||
-- Changes map_view VMF way
|
||||
function vmf.modify_map_view()
|
||||
local map_view = get_map_view()
|
||||
if map_view then
|
||||
initialize_mutators_ui(map_view)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Restores map_view to its defaults
|
||||
function vmf.reset_map_view()
|
||||
local map_view = get_map_view()
|
||||
if map_view then
|
||||
change_map_view_look(map_view, false)
|
||||
show_mutator_list(map_view, false)
|
||||
end
|
||||
end
|
|
@ -1,326 +0,0 @@
|
|||
local vmf = get_mod("VMF")
|
||||
|
||||
|
||||
local scenegraph_definition = {
|
||||
|
||||
sg_root = {
|
||||
size = {1920, 1080},
|
||||
position = {0, 0, UILayer.default},
|
||||
|
||||
is_root = true,
|
||||
},
|
||||
|
||||
-- Fix for FullHD windowed (not fullscreen) mode (if everything else will inherit from sg_root, its children will
|
||||
-- stick to the window border instead of the black gap)
|
||||
sg_placeholder = {
|
||||
size = {1920, 1080},
|
||||
position = {0, 0, 1},
|
||||
|
||||
parent = "sg_root",
|
||||
|
||||
horizontal_alignment = "center",
|
||||
vertical_alignment = "center"
|
||||
},
|
||||
|
||||
sg_mutators_list_background = {
|
||||
size = {547, 313},
|
||||
position = {-2, -2, 2}, -- @TODO: fix the actual image (-2 px plus image overlaping text)
|
||||
|
||||
parent = "sg_placeholder",
|
||||
|
||||
horizontal_alignment = "left",
|
||||
vertical_alignment = "bottom"
|
||||
},
|
||||
|
||||
sg_mutators_button = {
|
||||
size = {64, 64},
|
||||
position = {87, 430.5, 2},
|
||||
|
||||
parent = "sg_placeholder",
|
||||
|
||||
horizontal_alignment = "left",
|
||||
vertical_alignment = "bottom"
|
||||
},
|
||||
|
||||
sg_mutators_list = {
|
||||
size = {370, 265},
|
||||
position = {80, 61, 3},
|
||||
|
||||
parent = "sg_placeholder",
|
||||
|
||||
vertical_alignment = "bottom",
|
||||
horizontal_alignment = "left"
|
||||
},
|
||||
|
||||
sg_mutators_list_start = {
|
||||
size = {1, 1},
|
||||
offset = {0, 0, 3},
|
||||
|
||||
parent = "sg_mutators_list",
|
||||
|
||||
vertical_alignment = "top",
|
||||
horizontal_alignment = "left"
|
||||
},
|
||||
|
||||
sg_no_mutators_text = {
|
||||
size = {310, 30},
|
||||
position = {0, 10, 1},
|
||||
|
||||
parent = "sg_mutators_list",
|
||||
|
||||
vertical_alignment = "center",
|
||||
horizontal_alignment = "center",
|
||||
},
|
||||
|
||||
sg_scrollbar = {
|
||||
size = {0, 290}, -- X size doesn't affect scrollbar width
|
||||
position = {452, 52, 3},
|
||||
|
||||
parent = "sg_placeholder",
|
||||
|
||||
vertical_alignment = "bottom",
|
||||
horizontal_alignment = "left"
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
local widgets_definition = {
|
||||
|
||||
-- That photoshopped background texture which expands displayed list area
|
||||
mutator_list_background = {
|
||||
scenegraph_id = "sg_root",
|
||||
element = {
|
||||
passes = {
|
||||
{
|
||||
pass_type = "texture",
|
||||
style_id = "mutators_list_background",
|
||||
texture_id = "mutators_list_background_texture_id"
|
||||
}
|
||||
}
|
||||
},
|
||||
content = {
|
||||
mutators_list_background_texture_id = "map_view_mutators_area",
|
||||
},
|
||||
style = {
|
||||
mutators_list_background = {
|
||||
scenegraph_id = "sg_mutators_list_background"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
-- Widgets that detects mousewheel scrolls inside itself
|
||||
mousewheel_scroll_area = {
|
||||
scenegraph_id = "sg_mutators_list",
|
||||
element = {
|
||||
passes = {
|
||||
{
|
||||
pass_type = "scroll",
|
||||
scroll_function = function (ui_scenegraph_, style_, content, input_service_, scroll_axis)
|
||||
content.scroll_value = content.scroll_value - scroll_axis.y
|
||||
end
|
||||
}
|
||||
}
|
||||
},
|
||||
content = {
|
||||
scroll_value = 0
|
||||
},
|
||||
style = {}
|
||||
},
|
||||
|
||||
-- Scrollbar
|
||||
scrollbar = UIWidgets.create_scrollbar(scenegraph_definition.sg_scrollbar.size[2], "sg_scrollbar")
|
||||
}
|
||||
widgets_definition.scrollbar.content.disable_frame = true -- Hide scrollbar frame
|
||||
|
||||
|
||||
-- The 4th button, which will toggle old "Party" view (which is replaced by "Mutators" view)
|
||||
local party_button_widget_defenition = UIWidgets.create_octagon_button(
|
||||
{
|
||||
"map_view_party_button",
|
||||
"map_view_party_button_lit"
|
||||
},
|
||||
{
|
||||
"map_party_title",
|
||||
"map_party_title"
|
||||
},
|
||||
"sg_mutators_button"
|
||||
)
|
||||
|
||||
|
||||
-- Text displayed when user has 0 mutators
|
||||
local no_mutators_text_widget = {
|
||||
scenegraph_id = "sg_no_mutators_text",
|
||||
element = {
|
||||
passes = {
|
||||
{
|
||||
pass_type = "text",
|
||||
|
||||
style_id = "text",
|
||||
text_id = "text"
|
||||
},
|
||||
{
|
||||
pass_type = "hotspot",
|
||||
|
||||
content_id = "tooltip_hotspot"
|
||||
},
|
||||
{
|
||||
pass_type = "tooltip_text",
|
||||
|
||||
style_id = "tooltip_text",
|
||||
text_id = "tooltip_text",
|
||||
content_check_function = function (ui_content)
|
||||
return ui_content.tooltip_hotspot.is_hover
|
||||
end
|
||||
}
|
||||
}
|
||||
},
|
||||
content = {
|
||||
text = vmf:localize("no_mutators"),
|
||||
tooltip_text = vmf:localize("no_mutators_tooltip"),
|
||||
tooltip_hotspot = {},
|
||||
color = Colors.get_color_table_with_alpha("slate_gray", 255)
|
||||
},
|
||||
style = {
|
||||
|
||||
text = {
|
||||
vertical_alignment = "center",
|
||||
horizontal_alignment = "center",
|
||||
font_size = 22,
|
||||
localize = false,
|
||||
word_wrap = true,
|
||||
font_type = "hell_shark",
|
||||
text_color = Colors.get_color_table_with_alpha("slate_gray", 255),
|
||||
offset = {0, 2, 4}
|
||||
},
|
||||
|
||||
tooltip_text = {
|
||||
font_size = 24,
|
||||
max_width = 500,
|
||||
localize = false,
|
||||
horizontal_alignment = "left",
|
||||
vertical_alignment = "top",
|
||||
font_type = "hell_shark",
|
||||
text_color = Colors.get_color_table_with_alpha("white", 255),
|
||||
line_colors = {},
|
||||
offset = {0, 0, 50}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-- Creates a widget for every mutator (that string with checkbox)
|
||||
local function create_mutator_widget(mutator, offset_function_callback)
|
||||
return {
|
||||
scenegraph_id = "sg_mutators_list_start",
|
||||
element = {
|
||||
passes = {
|
||||
{
|
||||
pass_type = "hotspot",
|
||||
|
||||
content_id = "highlight_hotspot"
|
||||
},
|
||||
{
|
||||
pass_type = "local_offset",
|
||||
|
||||
-- The function is executed inside of 'mutators_gui.lua', since it has to interact with mutator list a lot
|
||||
offset_function = offset_function_callback
|
||||
},
|
||||
{
|
||||
pass_type = "texture",
|
||||
|
||||
style_id = "hover_texture",
|
||||
texture_id = "hover_texture",
|
||||
content_check_function = function (content)
|
||||
return content.can_be_enabled and content.highlight_hotspot.is_hover
|
||||
end
|
||||
},
|
||||
{
|
||||
pass_type = "text",
|
||||
|
||||
style_id = "text",
|
||||
text_id = "text"
|
||||
},
|
||||
{
|
||||
pass_type = "texture",
|
||||
|
||||
style_id = "checkbox_style",
|
||||
texture_id = "checkbox_texture"
|
||||
},
|
||||
{
|
||||
pass_type = "tooltip_text",
|
||||
|
||||
text_id = "tooltip_text",
|
||||
style_id = "tooltip_text",
|
||||
content_check_function = function (content)
|
||||
return content.highlight_hotspot.is_hover
|
||||
end
|
||||
},
|
||||
}
|
||||
},
|
||||
content = {
|
||||
mutator = nil, -- is added after creation (i can't add mutator here now, becuase UIWidget.init() clones tables)
|
||||
|
||||
text = mutator:get_readable_name(),
|
||||
description = mutator:get_description() or vmf:localize("mutator_no_description_provided"),
|
||||
|
||||
can_be_enabled = false,
|
||||
|
||||
highlight_hotspot = {},
|
||||
|
||||
tooltip_text = "", -- always changes in local_offset pass
|
||||
|
||||
hover_texture = "playerlist_hover",
|
||||
|
||||
checkbox_texture = "checkbox_unchecked", -- always changes in local_offset pass
|
||||
|
||||
-- Presets
|
||||
checkbox_unchecked_texture = "checkbox_unchecked",
|
||||
checkbox_checked_texture = "checkbox_checked",
|
||||
|
||||
text_color_disabled = Colors.get_color_table_with_alpha("white", 255),
|
||||
text_color_enabled = Colors.get_color_table_with_alpha("cheeseburger", 255),
|
||||
text_color_inactive = Colors.get_color_table_with_alpha("slate_gray", 255),
|
||||
},
|
||||
style = {
|
||||
|
||||
text = {
|
||||
offset = {10, -2, 2},
|
||||
font_size = 24,
|
||||
font_type = "hell_shark",
|
||||
dynamic_font = true,
|
||||
text_color = Colors.get_color_table_with_alpha("white", 255) -- always changes in local_offset pass
|
||||
},
|
||||
|
||||
hover_texture = {
|
||||
size = {370, 32},
|
||||
offset = {0, 0, 1}
|
||||
},
|
||||
|
||||
checkbox_style = {
|
||||
size = {20, 20},
|
||||
offset = {340, 6, 2},
|
||||
color = {255, 255, 255, 255}
|
||||
},
|
||||
|
||||
tooltip_text = {
|
||||
font_type = "hell_shark",
|
||||
font_size = 18,
|
||||
cursor_side = "right",
|
||||
max_width = 425,
|
||||
cursor_offset = {0, 0}, -- always changes in local_offset pass
|
||||
cursor_default_offset = {27, -27}
|
||||
},
|
||||
|
||||
size = {370, 32}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
return {
|
||||
scenegraph_definition = scenegraph_definition,
|
||||
widgets_definition = widgets_definition,
|
||||
party_button_widget_defenition = party_button_widget_defenition,
|
||||
no_mutators_text_widget = no_mutators_text_widget,
|
||||
create_mutator_widget = create_mutator_widget
|
||||
}
|
|
@ -1,126 +1,389 @@
|
|||
local vmf = get_mod("VMF")
|
||||
|
||||
local _button_injection_data = vmf:persistent_table("button_injection_data")
|
||||
local OptionsUtilities = require("scripts/utilities/ui/options")
|
||||
local InputUtils = require("scripts/managers/input/input_utils")
|
||||
|
||||
local _type_template_map = {}
|
||||
|
||||
if VT1 then
|
||||
-- ####################################################################################################################
|
||||
-- ##### Local functions ##############################################################################################
|
||||
-- ####################################################################################################################
|
||||
|
||||
-- Create value slider template
|
||||
local create_header_template = function (self, params)
|
||||
|
||||
-- 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_open" 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_open",
|
||||
fade = false
|
||||
local template = {
|
||||
category = params.category,
|
||||
display_name = params.readable_mod_name or params.title,
|
||||
group_name = params.mod_name,
|
||||
tooltip_text = params.tooltip,
|
||||
widget_type = "group_header",
|
||||
}
|
||||
for i = 1, #layout_data do
|
||||
if layout_data[i].transition == "options_menu" and layout_data[i + 1].transition ~= "vmf_options_view_open" then
|
||||
table.insert(layout_data, i + 1, mods_options_button)
|
||||
break
|
||||
return template
|
||||
end
|
||||
_type_template_map["header"] = create_header_template
|
||||
|
||||
|
||||
-- Create percentage slider template
|
||||
local create_percent_slider_template = function (self, params)
|
||||
|
||||
params.on_value_changed_function = function(new_value)
|
||||
get_mod(params.mod_name):set(params.setting_id, new_value)
|
||||
|
||||
return true
|
||||
end
|
||||
params.value_get_function = function()
|
||||
return get_mod(params.mod_name):get(params.setting_id)
|
||||
end
|
||||
|
||||
func(self, layout_data, ...)
|
||||
params.display_name = params.title
|
||||
params.apply_on_drag = true
|
||||
params.default_value = params.default_value
|
||||
params.normalized_step_size = 1 / 100
|
||||
|
||||
for _, button_info in ipairs(self.active_button_data) do
|
||||
if button_info.transition == "vmf_options_view_open" 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_open" then
|
||||
return button_index
|
||||
end
|
||||
local template = OptionsUtilities.create_percent_slider_template(params)
|
||||
|
||||
template.after = params.parent_index
|
||||
template.category = params.category
|
||||
template.indentation_level = params.depth
|
||||
template.tooltip_text = params.tooltip
|
||||
|
||||
return template
|
||||
end
|
||||
_type_template_map["percent_slider"] = create_percent_slider_template
|
||||
|
||||
|
||||
-- Create value slider template
|
||||
local create_value_slider_template = function (self, params)
|
||||
|
||||
params.on_value_changed_function = function(new_value)
|
||||
get_mod(params.mod_name):set(params.setting_id, new_value)
|
||||
|
||||
return true
|
||||
end
|
||||
params.value_get_function = function()
|
||||
return get_mod(params.mod_name):get(params.setting_id)
|
||||
end
|
||||
|
||||
params.display_name = params.title
|
||||
params.apply_on_drag = true
|
||||
params.default_value = params.default_value
|
||||
params.max_value = params.range[2]
|
||||
params.min_value = params.range[1]
|
||||
params.num_decimals = params.decimals_number
|
||||
params.step_size_value = math.pow(10, params.decimals_number * -1)
|
||||
params.type = "value_slider"
|
||||
|
||||
-- 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)
|
||||
local template = OptionsUtilities.create_value_slider_template(params)
|
||||
|
||||
template.after = params.parent_index
|
||||
template.category = params.category
|
||||
template.indentation_level = params.depth
|
||||
template.tooltip_text = params.tooltip
|
||||
|
||||
return template
|
||||
end
|
||||
_type_template_map["value_slider"] = create_value_slider_template
|
||||
_type_template_map["numeric"] = create_value_slider_template
|
||||
|
||||
|
||||
-- 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_open",
|
||||
fade = false
|
||||
-- Create checkbox template
|
||||
local create_checkbox_template = function (self, params)
|
||||
local template = {
|
||||
after = params.parent_index,
|
||||
category = params.category,
|
||||
default_value = params.default_value,
|
||||
display_name = params.title,
|
||||
indentation_level = params.depth,
|
||||
tooltip_text = params.tooltip,
|
||||
value_type = "boolean",
|
||||
}
|
||||
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_open" then
|
||||
table.insert(layout, i + 1, mod_options_button)
|
||||
break
|
||||
template.on_activated = function(new_value)
|
||||
get_mod(params.mod_name):set(params.setting_id, new_value)
|
||||
|
||||
return true
|
||||
end
|
||||
template.get_function = function()
|
||||
return get_mod(params.mod_name):get(params.setting_id)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
return template
|
||||
end
|
||||
_type_template_map["checkbox"] = create_checkbox_template
|
||||
|
||||
|
||||
-- Create dropdown template
|
||||
local create_dropdown_template = function (self, params)
|
||||
|
||||
for i = 1, #params.options do
|
||||
params.options[i].id = i - 1
|
||||
params.options[i].display_name = params.options[i].text
|
||||
end
|
||||
|
||||
local template = {
|
||||
after = params.parent_index,
|
||||
category = params.category,
|
||||
default_value = params.default_value,
|
||||
display_name = params.title,
|
||||
indentation_level = params.depth,
|
||||
options = params.options,
|
||||
tooltip_text = params.tooltip,
|
||||
widget_type = "dropdown",
|
||||
}
|
||||
template.on_activated = function(new_value)
|
||||
get_mod(params.mod_name):set(params.setting_id, new_value)
|
||||
|
||||
return true
|
||||
end
|
||||
template.get_function = function()
|
||||
return get_mod(params.mod_name):get(params.setting_id)
|
||||
end
|
||||
|
||||
return template
|
||||
end
|
||||
_type_template_map["dropdown"] = create_dropdown_template
|
||||
|
||||
|
||||
local set_new_keybind = function (self, keybind_widget_content)
|
||||
vmf.add_mod_keybind(
|
||||
get_mod(keybind_widget_content.mod_name),
|
||||
keybind_widget_content.setting_id,
|
||||
{
|
||||
global = keybind_widget_content.keybind_global,
|
||||
trigger = keybind_widget_content.keybind_trigger,
|
||||
type = keybind_widget_content.keybind_type,
|
||||
keys = keybind_widget_content.keys,
|
||||
function_name = keybind_widget_content.function_name,
|
||||
view_name = keybind_widget_content.view_name,
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
-- Create keybind template
|
||||
local create_keybind_template = function (self, params)
|
||||
local reserved_keys = {}
|
||||
local cancel_keys = {
|
||||
"keyboard_esc"
|
||||
}
|
||||
local devices = {
|
||||
"keyboard",
|
||||
"mouse",
|
||||
"xbox_controller",
|
||||
"ps4_controller"
|
||||
}
|
||||
|
||||
local template = {
|
||||
widget_type = "keybind",
|
||||
service_type = "Ingame",
|
||||
tooltip_text = params.tooltip,
|
||||
display_name = params.title,
|
||||
group_name = params.category,
|
||||
category = params.category,
|
||||
after = params.parent_index,
|
||||
devices = devices,
|
||||
sort_order = params.sort_order,
|
||||
cancel_keys = cancel_keys,
|
||||
reserved_keys = reserved_keys,
|
||||
indentation_level = params.depth,
|
||||
mod_name = params.mod_name,
|
||||
setting_id = params.setting_id,
|
||||
|
||||
on_activated = function (new_value, old_value)
|
||||
|
||||
for i = 1, #cancel_keys do
|
||||
local cancel_key = cancel_keys[i]
|
||||
if cancel_key == new_value.main then
|
||||
|
||||
-- Prevent unbinding the mod options menu
|
||||
if params.setting_id ~= "open_vmf_options" then
|
||||
|
||||
params.keybind_text = ""
|
||||
params.keys = {}
|
||||
|
||||
set_new_keybind(self, params)
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1, #reserved_keys do
|
||||
local reserved_key = reserved_keys[i]
|
||||
if reserved_key == new_value.main then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local device_type = InputUtils.key_device_type(new_value.main)
|
||||
local key_name = InputUtils.local_key_name(new_value.main, device_type)
|
||||
|
||||
params.keybind_text = key_name
|
||||
params.keys = {key_name}
|
||||
|
||||
set_new_keybind(self, params)
|
||||
return true
|
||||
end,
|
||||
|
||||
get_function = function (template)
|
||||
|
||||
local setting = get_mod(template.mod_name):get(template.setting_id)
|
||||
local local_name = setting and setting[1]
|
||||
if not local_name then
|
||||
return false
|
||||
end
|
||||
|
||||
local global_name = InputUtils.local_to_global_name(local_name, "keyboard")
|
||||
return {
|
||||
main = global_name,
|
||||
disablers = {},
|
||||
enablers = {},
|
||||
}
|
||||
end,
|
||||
}
|
||||
|
||||
return template
|
||||
end
|
||||
_type_template_map["keybind"] = create_keybind_template
|
||||
|
||||
|
||||
local function widget_data_to_template(self, data)
|
||||
if data and data.type and type(data.type) == "string" and _type_template_map[data.type] then
|
||||
return _type_template_map[data.type](self, data)
|
||||
else
|
||||
vmf:dump(data, "widget", 1)
|
||||
vmf.throw_error("[widget \"%s\"]: 'type' field must contain valid widget type name.", data.setting_id)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Add mod categories to options view
|
||||
local create_mod_category = function (self, categories, widget_data)
|
||||
local category = {
|
||||
can_be_reset = widget_data.can_be_reset or true,
|
||||
display_name = widget_data.readable_mod_name or widget_data.mod_name or "",
|
||||
icon = widget_data.icon_material or "content/ui/materials/icons/system/settings/category_gameplay",
|
||||
custom = true
|
||||
}
|
||||
categories[#categories + 1] = category
|
||||
return category
|
||||
end
|
||||
|
||||
-- ####################################################################################################################
|
||||
-- ##### Hooks ########################################################################################################
|
||||
-- ####################################################################################################################
|
||||
|
||||
-- ####################################################################################################################
|
||||
-- ##### VMF internal functions and variables #########################################################################
|
||||
-- ####################################################################################################################
|
||||
|
||||
|
||||
-- Add mod settings to options view
|
||||
vmf.create_mod_options_settings = function (self, options_templates)
|
||||
local categories = options_templates.categories
|
||||
local settings = options_templates.settings
|
||||
|
||||
for _, mod_data in ipairs(vmf.options_widgets_data) do
|
||||
local category = create_mod_category(self, categories, mod_data[1])
|
||||
|
||||
for _, widget_data in ipairs(mod_data) do
|
||||
local template = widget_data_to_template(self, widget_data)
|
||||
if template then
|
||||
template.custom = true
|
||||
template.category = category.display_name
|
||||
|
||||
settings[#settings + 1] = template
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return options_templates
|
||||
|
||||
--[[local settings = OptionsView._options_templates.settings
|
||||
|
||||
for name, this_mod in pairs(Mods) do
|
||||
-- Custom settings
|
||||
if type(this_mod) == "table" and this_mod.options then
|
||||
|
||||
local text = this_mod.text or name
|
||||
Mods.Localization.add("loc_settings_menu_group_mods_"..name, text)
|
||||
|
||||
local options_no_after = 0
|
||||
for _, option in pairs(this_mod.options) do
|
||||
if not option.after then
|
||||
options_no_after = options_no_after + 1
|
||||
end
|
||||
end
|
||||
|
||||
if options_no_after > 0 then
|
||||
settings[#settings+1] = {
|
||||
widget_type = "group_header",
|
||||
group_name = "mods_settings",
|
||||
display_name = "loc_settings_menu_group_mods_"..name,
|
||||
category = "loc_settings_menu_category_mods",
|
||||
custom = true,
|
||||
}
|
||||
end
|
||||
|
||||
for _, setting in pairs(this_mod.options) do
|
||||
setting.custom = true
|
||||
setting.category = setting.category or "loc_settings_menu_category_mods"
|
||||
setting.indentation_level = setting.after and 1 or 0
|
||||
if setting.after then
|
||||
local index = self:after_index(OptionsView, setting.after)
|
||||
table.insert(settings, index, setting)
|
||||
else
|
||||
settings[#settings+1] = setting
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end]]
|
||||
end
|
||||
|
||||
|
||||
vmf.initialize_vmf_options_view = function ()
|
||||
vmf:dofile("scripts/mods/vmf/modules/ui/options/vmf_options_view")
|
||||
_button_injection_data.mod_options_button_disabled = false
|
||||
vmf:add_require_path("dmf/scripts/mods/vmf/modules/ui/options/vmf_options_view")
|
||||
vmf:add_require_path("dmf/scripts/mods/vmf/modules/ui/options/vmf_options_view_definitions")
|
||||
vmf:add_require_path("dmf/scripts/mods/vmf/modules/ui/options/vmf_options_view_settings")
|
||||
vmf:add_require_path("dmf/scripts/mods/vmf/modules/ui/options/vmf_options_view_content_blueprints")
|
||||
|
||||
vmf:register_view({
|
||||
view_name = "vmf_options_view",
|
||||
view_settings = {
|
||||
init_view_function = function (ingame_ui_context)
|
||||
return true
|
||||
end,
|
||||
class = "VMFOptionsView",
|
||||
disable_game_world = false,
|
||||
display_name = "loc_options_view_display_name",
|
||||
game_world_blur = 1.1,
|
||||
load_always = true,
|
||||
load_in_hub = true,
|
||||
package = "packages/ui/views/options_view/options_view",
|
||||
path = "dmf/scripts/mods/vmf/modules/ui/options/vmf_options_view",
|
||||
state_bound = true,
|
||||
enter_sound_events = {
|
||||
"wwise/events/ui/play_ui_enter_short"
|
||||
},
|
||||
exit_sound_events = {
|
||||
"wwise/events/ui/play_ui_back_short"
|
||||
},
|
||||
wwise_states = {
|
||||
options = "ingame_menu"
|
||||
}
|
||||
},
|
||||
view_transitions = {},
|
||||
view_options = {
|
||||
close_all = false,
|
||||
close_previous = false,
|
||||
close_transition_time = nil,
|
||||
transition_time = nil
|
||||
}
|
||||
})
|
||||
|
||||
vmf:dofile("dmf/scripts/mods/vmf/modules/ui/options/vmf_options_view")
|
||||
end
|
||||
|
||||
|
||||
vmf.disable_mods_options_button = function ()
|
||||
_button_injection_data.mod_options_button_disabled = true
|
||||
end
|
||||
-- ####################################################################################################################
|
||||
-- ##### Script #######################################################################################################
|
||||
-- ####################################################################################################################
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,780 @@
|
|||
local vmf = get_mod("VMF")
|
||||
|
||||
local _view_settings = vmf:dofile("dmf/scripts/mods/vmf/modules/ui/options/vmf_options_view_settings")
|
||||
|
||||
local ButtonPassTemplates = require("scripts/ui/pass_templates/button_pass_templates")
|
||||
local CheckboxPassTemplates = require("scripts/ui/pass_templates/checkbox_pass_templates")
|
||||
local DropdownPassTemplates = require("scripts/ui/pass_templates/dropdown_pass_templates")
|
||||
local InputUtils = require("scripts/managers/input/input_utils")
|
||||
local KeybindPassTemplates = require("scripts/ui/pass_templates/keybind_pass_templates")
|
||||
local SliderPassTemplates = require("scripts/ui/pass_templates/slider_pass_templates")
|
||||
local UIFonts = require("scripts/managers/ui/ui_fonts")
|
||||
local UIFontSettings = require("scripts/managers/ui/ui_font_settings")
|
||||
local UIRenderer = require("scripts/managers/ui/ui_renderer")
|
||||
|
||||
local grid_size = _view_settings.grid_size
|
||||
local grid_width = grid_size[1]
|
||||
|
||||
local settings_grid_width = 1000
|
||||
local settings_value_width = 500
|
||||
local settings_value_height = 64
|
||||
|
||||
local group_header_height = 80
|
||||
|
||||
local DEFAULT_NUM_DECIMALS = 0
|
||||
|
||||
local value_font_style = table.clone(UIFontSettings.list_button)
|
||||
value_font_style.offset = {
|
||||
settings_grid_width - settings_value_width + 25,
|
||||
0,
|
||||
8
|
||||
}
|
||||
|
||||
local description_font_style = table.clone(UIFontSettings.list_button)
|
||||
description_font_style.offset = {
|
||||
25,
|
||||
0,
|
||||
3
|
||||
}
|
||||
|
||||
local header_font_style = table.clone(UIFontSettings.header_2)
|
||||
header_font_style.text_vertical_alignment = "bottom"
|
||||
|
||||
local blueprints = {
|
||||
spacing_vertical = {
|
||||
size = {
|
||||
grid_width,
|
||||
20
|
||||
}
|
||||
},
|
||||
settings_button = {
|
||||
size = {
|
||||
grid_width,
|
||||
settings_value_height
|
||||
},
|
||||
pass_template = ButtonPassTemplates.list_button,
|
||||
init = function (parent, widget, entry, callback_name)
|
||||
local content = widget.content
|
||||
local hotspot = content.hotspot
|
||||
|
||||
hotspot.pressed_callback = function ()
|
||||
local is_disabled = entry.disabled or false
|
||||
|
||||
if is_disabled then
|
||||
return
|
||||
end
|
||||
|
||||
callback(parent, callback_name, widget, entry)()
|
||||
end
|
||||
|
||||
local display_name = entry.display_name
|
||||
content.text = display_name
|
||||
content.entry = entry
|
||||
end
|
||||
},
|
||||
button = {
|
||||
size = {
|
||||
settings_grid_width,
|
||||
settings_value_height
|
||||
},
|
||||
pass_template = ButtonPassTemplates.settings_button(settings_grid_width, settings_value_height, settings_value_width, true),
|
||||
init = function (parent, widget, entry, callback_name)
|
||||
local content = widget.content
|
||||
|
||||
content.hotspot.pressed_callback = function ()
|
||||
local is_disabled = entry.disabled or false
|
||||
|
||||
if is_disabled then
|
||||
return
|
||||
end
|
||||
|
||||
callback(parent, callback_name, widget, entry)()
|
||||
end
|
||||
|
||||
local display_name = entry.display_name
|
||||
content.text = display_name
|
||||
content.button_text = Localize("loc_settings_change")
|
||||
content.entry = entry
|
||||
end
|
||||
},
|
||||
group_header = {
|
||||
size = {
|
||||
settings_grid_width,
|
||||
group_header_height
|
||||
},
|
||||
pass_template = {
|
||||
{
|
||||
pass_type = "text",
|
||||
value_id = "text",
|
||||
style = header_font_style,
|
||||
value = Localize("loc_settings_option_unavailable")
|
||||
}
|
||||
},
|
||||
init = function (parent, widget, entry, callback_name)
|
||||
local content = widget.content
|
||||
local display_name = entry.display_name
|
||||
content.text = display_name
|
||||
end
|
||||
},
|
||||
checkbox = {
|
||||
size = {
|
||||
settings_grid_width,
|
||||
settings_value_height
|
||||
},
|
||||
pass_template_function = function (parent, config, size)
|
||||
return CheckboxPassTemplates.settings_checkbox(size[1], settings_value_height, settings_value_width, 2, true)
|
||||
end,
|
||||
init = function (parent, widget, entry, callback_name)
|
||||
local content = widget.content
|
||||
local display_name = entry.display_name or Localize("loc_settings_option_unavailable")
|
||||
content.text = display_name
|
||||
content.entry = entry
|
||||
|
||||
for i = 1, 2 do
|
||||
local widget_option_id = "option_" .. i
|
||||
content[widget_option_id] = i == 1 and Managers.localization:localize("loc_setting_checkbox_on") or Managers.localization:localize("loc_setting_checkbox_off")
|
||||
end
|
||||
end,
|
||||
update = function (parent, widget, input_service, dt, t)
|
||||
local content = widget.content
|
||||
local entry = content.entry
|
||||
local value = entry:get_function()
|
||||
local on_activated = entry.on_activated
|
||||
local pass_input = true
|
||||
local hotspot = content.hotspot
|
||||
local is_disabled = entry.disabled or false
|
||||
content.disabled = is_disabled
|
||||
local new_value = nil
|
||||
|
||||
if hotspot.on_pressed and not is_disabled then
|
||||
new_value = not value
|
||||
end
|
||||
|
||||
for i = 1, 2 do
|
||||
local widget_option_id = "option_hotspot_" .. i
|
||||
local option_hotspot = content[widget_option_id]
|
||||
local is_selected = value and i == 1 or not value and i == 2
|
||||
option_hotspot.is_selected = is_selected
|
||||
end
|
||||
|
||||
if new_value ~= nil and new_value ~= value then
|
||||
on_activated(new_value, entry)
|
||||
end
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
local function slider_init_function(parent, widget, entry, callback_name)
|
||||
local content = widget.content
|
||||
local display_name = entry.display_name or Localize("loc_settings_option_unavailable")
|
||||
content.text = display_name
|
||||
content.entry = entry
|
||||
content.area_length = settings_value_width
|
||||
content.step_size = entry.normalized_step_size
|
||||
|
||||
if not entry.normalized_step_size and entry.step_size_value then
|
||||
local value_range = entry.max_value - entry.min_value
|
||||
content.step_size = entry.step_size_value / value_range
|
||||
end
|
||||
|
||||
content.apply_on_drag = entry.apply_on_drag and true
|
||||
local get_function = entry.get_function
|
||||
local value = get_function(entry)
|
||||
content.previous_slider_value = value
|
||||
content.slider_value = value
|
||||
|
||||
content.pressed_callback = function ()
|
||||
local is_disabled = entry.is_disabled
|
||||
|
||||
if is_disabled then
|
||||
return
|
||||
end
|
||||
|
||||
callback(parent, callback_name, widget, entry)()
|
||||
end
|
||||
end
|
||||
|
||||
blueprints.percent_slider = {
|
||||
size = {
|
||||
settings_grid_width,
|
||||
settings_value_height
|
||||
},
|
||||
pass_template_function = function (parent, config, size)
|
||||
return SliderPassTemplates.settings_percent_slider(size[1], settings_value_height, settings_value_width, true)
|
||||
end,
|
||||
init = function (parent, widget, entry, callback_name)
|
||||
slider_init_function(parent, widget, entry, callback_name)
|
||||
end,
|
||||
update = function (parent, widget, input_service, dt, t)
|
||||
local content = widget.content
|
||||
local entry = content.entry
|
||||
local pass_input = true
|
||||
local is_disabled = entry.disabled or false
|
||||
content.disabled = is_disabled
|
||||
local using_gamepad = not parent:using_cursor_navigation()
|
||||
local get_function = entry.get_function
|
||||
local value = get_function(entry) / 100
|
||||
local on_activated = entry.on_activated
|
||||
local format_value_function = entry.format_value_function
|
||||
local drag_value, new_value = nil
|
||||
local apply_on_drag = content.apply_on_drag and not is_disabled
|
||||
local drag_active = content.drag_active and not is_disabled
|
||||
local focused = using_gamepad and content.exclusive_focus and not is_disabled
|
||||
|
||||
if drag_active or focused then
|
||||
if not parent._selected_settings_widget then
|
||||
parent:set_exclusive_focus_on_grid_widget(widget.name)
|
||||
end
|
||||
|
||||
local slider_value = content.slider_value
|
||||
drag_value = slider_value or get_function(entry) / 100
|
||||
elseif not focused then
|
||||
local previous_slider_value = content.previous_slider_value
|
||||
local slider_value = content.slider_value
|
||||
|
||||
if content.drag_previously_active then
|
||||
parent:set_exclusive_focus_on_grid_widget(nil)
|
||||
|
||||
if previous_slider_value ~= slider_value then
|
||||
new_value = slider_value
|
||||
drag_value = new_value or get_function(entry) / 100
|
||||
end
|
||||
elseif value ~= slider_value then
|
||||
content.slider_value = value
|
||||
content.previous_slider_value = value
|
||||
content.scroll_add = nil
|
||||
end
|
||||
|
||||
content.previous_slider_value = slider_value
|
||||
end
|
||||
|
||||
content.drag_previously_active = drag_active
|
||||
local display_value = format_value_function((drag_value or value) * 100)
|
||||
|
||||
if display_value then
|
||||
content.value_text = display_value
|
||||
end
|
||||
|
||||
local hotspot = content.hotspot
|
||||
|
||||
if hotspot.on_pressed and not is_disabled then
|
||||
if focused then
|
||||
new_value = content.slider_value
|
||||
elseif using_gamepad then
|
||||
content.pressed_callback()
|
||||
end
|
||||
end
|
||||
|
||||
if focused and parent:can_exit() then
|
||||
parent:set_can_exit(false)
|
||||
end
|
||||
|
||||
if apply_on_drag and drag_value and not new_value and content.slider_value ~= content.previous_slider_value then
|
||||
new_value = content.slider_value
|
||||
end
|
||||
|
||||
if new_value then
|
||||
on_activated(new_value * 100, entry)
|
||||
|
||||
content.slider_value = new_value
|
||||
content.previous_slider_value = new_value
|
||||
content.scroll_add = nil
|
||||
end
|
||||
|
||||
return pass_input
|
||||
end
|
||||
}
|
||||
|
||||
blueprints.value_slider = {
|
||||
size = {
|
||||
settings_grid_width,
|
||||
settings_value_height
|
||||
},
|
||||
pass_template_function = function (parent, config, size)
|
||||
return SliderPassTemplates.settings_value_slider(size[1], settings_value_height, settings_value_width, true)
|
||||
end,
|
||||
init = function (parent, widget, entry, callback_name)
|
||||
slider_init_function(parent, widget, entry, callback_name)
|
||||
end,
|
||||
update = function (parent, widget, input_service, dt, t)
|
||||
local content = widget.content
|
||||
local entry = content.entry
|
||||
local pass_input = true
|
||||
local is_disabled = entry.disabled or false
|
||||
content.disabled = is_disabled
|
||||
local using_gamepad = not parent:using_cursor_navigation()
|
||||
local min_value = entry.min_value
|
||||
local max_value = entry.max_value
|
||||
local get_function = entry.get_function
|
||||
local explode_function = entry.explode_function
|
||||
local value = get_function(entry)
|
||||
local normalized_value = math.normalize_01(value, min_value, max_value)
|
||||
local on_activated = entry.on_activated
|
||||
local format_value_function = entry.format_value_function
|
||||
local drag_value, new_normalized_value = nil
|
||||
local apply_on_drag = content.apply_on_drag and not is_disabled
|
||||
local drag_active = content.drag_active and not is_disabled
|
||||
local drag_previously_active = content.drag_previously_active
|
||||
local focused = content.exclusive_focus and using_gamepad and not is_disabled
|
||||
|
||||
if drag_active or focused then
|
||||
if not parent._selected_settings_widget then
|
||||
parent:set_exclusive_focus_on_grid_widget(widget.name)
|
||||
end
|
||||
|
||||
local slider_value = content.slider_value
|
||||
drag_value = slider_value and explode_function(slider_value, entry) or get_function(entry)
|
||||
elseif not focused or drag_previously_active then
|
||||
local previous_slider_value = content.previous_slider_value
|
||||
local slider_value = content.slider_value
|
||||
|
||||
if drag_previously_active then
|
||||
parent:set_exclusive_focus_on_grid_widget(nil)
|
||||
|
||||
if previous_slider_value ~= slider_value then
|
||||
new_normalized_value = slider_value
|
||||
drag_value = slider_value and explode_function(slider_value, entry) or get_function(entry)
|
||||
end
|
||||
elseif normalized_value ~= slider_value then
|
||||
content.slider_value = normalized_value
|
||||
content.previous_slider_value = normalized_value
|
||||
content.scroll_add = nil
|
||||
end
|
||||
|
||||
content.previous_slider_value = slider_value
|
||||
end
|
||||
|
||||
content.drag_previously_active = drag_active
|
||||
local display_value = format_value_function(drag_value or value)
|
||||
|
||||
if display_value then
|
||||
content.value_text = display_value
|
||||
end
|
||||
|
||||
local hotspot = content.hotspot
|
||||
|
||||
if hotspot.on_pressed then
|
||||
if focused then
|
||||
new_normalized_value = content.slider_value
|
||||
elseif using_gamepad then
|
||||
content.pressed_callback()
|
||||
end
|
||||
end
|
||||
|
||||
if focused and parent:can_exit() then
|
||||
parent:set_can_exit(false)
|
||||
end
|
||||
|
||||
if apply_on_drag and drag_value and not new_normalized_value and content.slider_value ~= content.previous_slider_value then
|
||||
new_normalized_value = content.slider_value
|
||||
end
|
||||
|
||||
if new_normalized_value then
|
||||
local new_value = explode_function(new_normalized_value, entry)
|
||||
|
||||
on_activated(new_value, entry)
|
||||
|
||||
content.slider_value = new_normalized_value
|
||||
content.previous_slider_value = new_normalized_value
|
||||
content.scroll_add = nil
|
||||
end
|
||||
|
||||
return pass_input
|
||||
end
|
||||
}
|
||||
|
||||
blueprints.slider = {
|
||||
size = {
|
||||
settings_grid_width,
|
||||
settings_value_height
|
||||
},
|
||||
pass_template_function = function (parent, config, size)
|
||||
return SliderPassTemplates.settings_value_slider(size[1], settings_value_height, settings_value_width, true)
|
||||
end,
|
||||
init = function (parent, widget, entry, callback_name)
|
||||
local content = widget.content
|
||||
local display_name = entry.display_name or Localize("loc_settings_option_unavailable")
|
||||
content.text = display_name
|
||||
content.entry = entry
|
||||
content.area_length = settings_value_width
|
||||
content.step_size = entry.step_size_fraction
|
||||
content.apply_on_drag = entry.apply_on_drag and true
|
||||
local get_function = entry.get_function
|
||||
local value, value_fraction = get_function(entry)
|
||||
content.previous_slider_value = value_fraction
|
||||
content.slider_value = value_fraction
|
||||
content.pressed_callback = callback(parent, callback_name, widget, entry)
|
||||
end,
|
||||
update = function (parent, widget, input_service, dt, t)
|
||||
local content = widget.content
|
||||
local entry = content.entry
|
||||
local pass_input = true
|
||||
local is_disabled = entry.disabled or false
|
||||
content.disabled = is_disabled
|
||||
local using_gamepad = not parent:using_cursor_navigation()
|
||||
local get_function = entry.get_function
|
||||
local value, value_fraction = get_function(entry)
|
||||
local on_activated = entry.on_activated
|
||||
local format_value_function = entry.format_value_function
|
||||
local num_decimals = entry.num_decimals
|
||||
local drag_value, new_value_fraction = nil
|
||||
local apply_on_drag = entry.apply_on_drag and not is_disabled
|
||||
local drag_active = content.drag_active and not is_disabled
|
||||
local drag_previously_active = content.drag_previously_active
|
||||
local focused = content.exclusive_focus and using_gamepad and not is_disabled
|
||||
|
||||
if drag_active or focused then
|
||||
drag_value = math.lerp(entry.min_value, entry.max_value, content.slider_value)
|
||||
elseif not focused or drag_previously_active then
|
||||
local previous_slider_value = content.previous_slider_value
|
||||
local slider_value = content.slider_value
|
||||
|
||||
if drag_previously_active then
|
||||
if previous_slider_value ~= slider_value then
|
||||
new_value_fraction = slider_value
|
||||
drag_value = math.lerp(entry.min_value, entry.max_value, new_value_fraction)
|
||||
end
|
||||
elseif value_fraction ~= slider_value then
|
||||
content.slider_value = value_fraction
|
||||
content.previous_slider_value = value_fraction
|
||||
content.scroll_add = nil
|
||||
end
|
||||
|
||||
content.previous_slider_value = slider_value
|
||||
end
|
||||
|
||||
content.drag_previously_active = drag_active
|
||||
local display_value = nil
|
||||
|
||||
if format_value_function then
|
||||
display_value = format_value_function(entry, drag_value or value)
|
||||
else
|
||||
local number_format = string.format("%%.%sf", num_decimals or DEFAULT_NUM_DECIMALS)
|
||||
display_value = string.format(number_format, drag_value or value)
|
||||
end
|
||||
|
||||
if display_value then
|
||||
content.value_text = display_value
|
||||
end
|
||||
|
||||
local hotspot = content.hotspot
|
||||
|
||||
if hotspot.on_pressed and not is_disabled then
|
||||
if focused then
|
||||
new_value_fraction = content.slider_value
|
||||
elseif not hotspot.is_hover then
|
||||
content.pressed_callback()
|
||||
end
|
||||
end
|
||||
|
||||
if focused and parent:can_exit() then
|
||||
parent:set_can_exit(false)
|
||||
end
|
||||
|
||||
if apply_on_drag and drag_value and not new_value_fraction and content.slider_value ~= content.previous_slider_value then
|
||||
new_value_fraction = content.slider_value
|
||||
end
|
||||
|
||||
if new_value_fraction then
|
||||
local new_value = math.lerp(entry.min_value, entry.max_value, new_value_fraction)
|
||||
|
||||
on_activated(new_value, entry)
|
||||
|
||||
content.slider_value = new_value_fraction
|
||||
content.previous_slider_value = new_value_fraction
|
||||
content.scroll_add = nil
|
||||
end
|
||||
|
||||
return pass_input
|
||||
end
|
||||
}
|
||||
|
||||
local max_visible_options = _view_settings.max_visible_dropdown_options or 5
|
||||
blueprints.dropdown = {
|
||||
size = {
|
||||
settings_grid_width,
|
||||
settings_value_height
|
||||
},
|
||||
pass_template_function = function (parent, entry, size)
|
||||
local has_options_function = entry.options_function ~= nil
|
||||
local has_dynamic_contents = entry.has_dynamic_contents
|
||||
local display_name = entry.display_name or Localize("loc_settings_option_unavailable")
|
||||
local options = entry.options_function and entry.options_function() or entry.options
|
||||
local num_visible_options = math.min(#options, max_visible_options)
|
||||
|
||||
return DropdownPassTemplates.settings_dropdown(size[1], settings_value_height, settings_value_width, num_visible_options, true)
|
||||
end,
|
||||
init = function (parent, widget, entry, callback_name)
|
||||
local content = widget.content
|
||||
local display_name = entry.display_name or Localize("loc_settings_option_unavailable")
|
||||
content.text = display_name
|
||||
content.entry = entry
|
||||
local has_options_function = entry.options_function ~= nil
|
||||
local has_dynamic_contents = entry.has_dynamic_contents
|
||||
local options = entry.options or entry.options_function and entry.options_function()
|
||||
local options_by_id = {}
|
||||
local num_options = #options
|
||||
local num_visible_options = math.min(num_options, max_visible_options)
|
||||
content.num_visible_options = num_visible_options
|
||||
local optional_num_decimals = entry.optional_num_decimals
|
||||
local number_format = string.format("%%.%sf", optional_num_decimals or DEFAULT_NUM_DECIMALS)
|
||||
|
||||
for i = 1, num_options do
|
||||
local option = options[i]
|
||||
options_by_id[option.id] = option
|
||||
end
|
||||
|
||||
content.number_format = number_format
|
||||
content.options_by_id = options_by_id
|
||||
content.options = options
|
||||
|
||||
content.hotspot.pressed_callback = function ()
|
||||
local is_disabled = entry.disabled or false
|
||||
|
||||
if is_disabled then
|
||||
return
|
||||
end
|
||||
|
||||
callback(parent, callback_name, widget, entry)()
|
||||
end
|
||||
|
||||
local widget_type = widget.type
|
||||
local template = blueprints[widget_type]
|
||||
local size = template.size
|
||||
content.area_length = size[2] * num_visible_options
|
||||
local scroll_length = math.max(size[2] * num_options - content.area_length, 0)
|
||||
content.scroll_length = scroll_length
|
||||
local spacing = 0
|
||||
local scroll_amount = scroll_length > 0 and (size[2] + spacing) / scroll_length or 0
|
||||
content.scroll_amount = scroll_amount
|
||||
local value = entry.get_function and entry:get_function() or entry.default_value
|
||||
end,
|
||||
update = function (parent, widget, input_service, dt, t)
|
||||
local content = widget.content
|
||||
local entry = content.entry
|
||||
local pass_input = true
|
||||
local is_disabled = entry.disabled or false
|
||||
content.disabled = is_disabled
|
||||
local using_gamepad = not parent:using_cursor_navigation()
|
||||
local offset = widget.offset
|
||||
local style = widget.style
|
||||
local options = content.options
|
||||
local options_by_id = content.options_by_id
|
||||
local num_visible_options = content.num_visible_options
|
||||
local num_options = #options
|
||||
local focused = content.exclusive_focus and not is_disabled
|
||||
|
||||
if focused and parent:can_exit() then
|
||||
content.selected_index = nil
|
||||
|
||||
parent:set_can_exit(false)
|
||||
end
|
||||
|
||||
local selected_index = content.selected_index
|
||||
local value, new_value = nil
|
||||
local hotspot = content.hotspot
|
||||
local hotspot_style = style.hotspot
|
||||
|
||||
if selected_index and focused then
|
||||
if using_gamepad and hotspot.on_pressed then
|
||||
new_value = options[selected_index].id
|
||||
end
|
||||
|
||||
hotspot_style.on_pressed_sound = hotspot_style.on_pressed_fold_in_sound
|
||||
else
|
||||
hotspot_style.on_pressed_sound = hotspot_style.on_pressed_fold_out_sound
|
||||
end
|
||||
|
||||
value = entry.get_function and entry:get_function() or content.internal_value or "<not selected>"
|
||||
|
||||
local preview_option = options_by_id[value]
|
||||
local preview_option_id = preview_option and preview_option.id
|
||||
local preview_value = preview_option and preview_option.display_name or Localize("loc_settings_option_unavailable")
|
||||
|
||||
content.value_text = preview_value
|
||||
|
||||
local widget_type = widget.type
|
||||
local template = blueprints[widget_type]
|
||||
local size = template.size
|
||||
local scroll_amount = parent:settings_scroll_amount()
|
||||
local scroll_area_height = parent:settings_grid_length()
|
||||
local dropdown_length = size[2] * (num_visible_options + 1)
|
||||
local grow_downwards = true
|
||||
|
||||
if scroll_area_height <= offset[2] - scroll_amount + dropdown_length then
|
||||
grow_downwards = false
|
||||
end
|
||||
|
||||
content.grow_downwards = grow_downwards
|
||||
local new_selection_index = nil
|
||||
|
||||
if not selected_index or not focused then
|
||||
for i = 1, #options do
|
||||
local option = options[i]
|
||||
|
||||
if option.id == preview_option_id then
|
||||
selected_index = i
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
selected_index = selected_index or 1
|
||||
end
|
||||
|
||||
if selected_index and focused then
|
||||
if input_service:get("navigate_up_continuous") then
|
||||
if grow_downwards then
|
||||
new_selection_index = math.max(selected_index - 1, 1)
|
||||
else
|
||||
new_selection_index = math.min(selected_index + 1, num_options)
|
||||
end
|
||||
elseif input_service:get("navigate_down_continuous") then
|
||||
if grow_downwards then
|
||||
new_selection_index = math.min(selected_index + 1, num_options)
|
||||
else
|
||||
new_selection_index = math.max(selected_index - 1, 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if new_selection_index or not content.selected_index then
|
||||
if new_selection_index then
|
||||
selected_index = new_selection_index
|
||||
end
|
||||
|
||||
if num_visible_options < num_options then
|
||||
local step_size = 1 / num_options
|
||||
local new_scroll_percentage = math.min(selected_index - 1, num_options) * step_size
|
||||
content.scroll_percentage = new_scroll_percentage
|
||||
content.scroll_add = nil
|
||||
end
|
||||
|
||||
content.selected_index = selected_index
|
||||
end
|
||||
|
||||
local scroll_percentage = content.scroll_percentage
|
||||
|
||||
if scroll_percentage then
|
||||
local step_size = 1 / (num_options - (num_visible_options - 1))
|
||||
content.start_index = math.max(1, math.ceil(scroll_percentage / step_size))
|
||||
end
|
||||
|
||||
local option_hovered = false
|
||||
local option_index = 1
|
||||
local start_index = content.start_index or 1
|
||||
local end_index = math.min(start_index + num_visible_options - 1, num_options)
|
||||
local using_scrollbar = num_visible_options < num_options
|
||||
|
||||
for i = start_index, end_index do
|
||||
local option_text_id = "option_text_" .. option_index
|
||||
local option_hotspot_id = "option_hotspot_" .. option_index
|
||||
local outline_style_id = "outline_" .. option_index
|
||||
local option_hotspot = content[option_hotspot_id]
|
||||
option_hovered = option_hovered or option_hotspot.is_hover
|
||||
option_hotspot.is_selected = i == selected_index
|
||||
local option = options[i]
|
||||
|
||||
if not new_value and focused and not using_gamepad and option_hotspot.on_pressed then
|
||||
option_hotspot.on_pressed = nil
|
||||
new_value = option.id
|
||||
content.selected_index = i
|
||||
end
|
||||
|
||||
local option_display_name = option.display_name
|
||||
content[option_text_id] = option_display_name
|
||||
local options_y = size[2] * option_index
|
||||
style[option_hotspot_id].offset[2] = grow_downwards and options_y or -options_y
|
||||
style[option_text_id].offset[2] = grow_downwards and options_y or -options_y
|
||||
local entry_length = using_scrollbar and settings_value_width - style.scrollbar_hotspot.size[1] or settings_value_width
|
||||
style[outline_style_id].size[1] = entry_length
|
||||
style[option_text_id].size[1] = settings_value_width
|
||||
option_index = option_index + 1
|
||||
end
|
||||
|
||||
local value_changed = new_value ~= nil
|
||||
|
||||
if value_changed and new_value ~= value then
|
||||
local on_activated = entry.on_activated
|
||||
|
||||
on_activated(new_value, entry)
|
||||
end
|
||||
|
||||
local scrollbar_hotspot = content.scrollbar_hotspot
|
||||
local scrollbar_hovered = scrollbar_hotspot.is_hover
|
||||
pass_input = using_gamepad or value_changed or not option_hovered and not scrollbar_hovered
|
||||
|
||||
return pass_input
|
||||
end
|
||||
}
|
||||
|
||||
blueprints.keybind = {
|
||||
size = {
|
||||
settings_grid_width,
|
||||
settings_value_height
|
||||
},
|
||||
pass_template = KeybindPassTemplates.settings_keybind(settings_grid_width, settings_value_height, settings_value_width),
|
||||
init = function (parent, widget, entry, callback_name)
|
||||
local content = widget.content
|
||||
local display_name = entry.display_name or Localize("loc_settings_option_unavailable")
|
||||
content.text = display_name
|
||||
content.entry = entry
|
||||
content.key_unassigned_string = Managers.localization:localize("loc_keybind_unassigned")
|
||||
end,
|
||||
update = function (parent, widget, input_service, dt, t)
|
||||
local content = widget.content
|
||||
local entry = content.entry
|
||||
local value = entry:get_function()
|
||||
local preview_value = value and InputUtils.localized_string_from_key_info(value) or content.key_unassigned_string
|
||||
content.value_text = preview_value
|
||||
local hotspot = content.hotspot
|
||||
|
||||
if hotspot.on_released then
|
||||
parent:show_keybind_popup(widget, entry, content.entry.cancel_keys)
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
local description_font_style = table.clone(UIFontSettings.body_small)
|
||||
description_font_style.offset = {
|
||||
25,
|
||||
0,
|
||||
3
|
||||
}
|
||||
description_font_style.text_horizontal_alignment = "left"
|
||||
description_font_style.text_vertical_alignment = "center"
|
||||
description_font_style.hover_text_color = Color.ui_brown_super_light(255, true)
|
||||
|
||||
blueprints.description = {
|
||||
size = {
|
||||
settings_grid_width - 225,
|
||||
settings_value_height
|
||||
},
|
||||
pass_template = {
|
||||
{
|
||||
value_id = "text",
|
||||
pass_type = "text",
|
||||
style_id = "text",
|
||||
style = description_font_style,
|
||||
value = Localize("loc_settings_option_unavailable")
|
||||
}
|
||||
},
|
||||
init = function (parent, widget, entry, callback_name)
|
||||
local content = widget.content
|
||||
local style = widget.style
|
||||
local text_style = style.text
|
||||
local display_name = entry.display_name
|
||||
local display_text = display_name
|
||||
local ui_renderer = parent._ui_renderer
|
||||
local size = content.size
|
||||
local text_options = UIFonts.get_font_options_by_style(text_style)
|
||||
local _, height = UIRenderer.text_size(ui_renderer, display_text, text_style.font_type, text_style.font_size, size, text_options)
|
||||
size[2] = math.ceil(height)
|
||||
content.text = display_text
|
||||
end,
|
||||
update = function (parent, widget, input_service, dt, t)
|
||||
return
|
||||
end
|
||||
}
|
||||
|
||||
return settings("VMFOptionsViewContentBlueprints", blueprints)
|
|
@ -0,0 +1,454 @@
|
|||
local vmf = get_mod("VMF")
|
||||
|
||||
local _view_settings = vmf:dofile("dmf/scripts/mods/vmf/modules/ui/options/vmf_options_view_settings")
|
||||
|
||||
local ScrollbarPassTemplates = require("scripts/ui/pass_templates/scrollbar_pass_templates")
|
||||
local UIFontSettings = require("scripts/managers/ui/ui_font_settings")
|
||||
local UIWidget = require("scripts/managers/ui/ui_widget")
|
||||
local UIWorkspaceSettings = require("scripts/settings/ui/ui_workspace_settings")
|
||||
|
||||
local scrollbar_width = _view_settings.scrollbar_width
|
||||
|
||||
local grid_size = _view_settings.grid_size
|
||||
local grid_width = grid_size[1]
|
||||
local grid_height = grid_size[2]
|
||||
local grid_blur_edge_size = _view_settings.grid_blur_edge_size
|
||||
|
||||
local mask_size = {
|
||||
grid_width + grid_blur_edge_size[1] * 2,
|
||||
grid_height + grid_blur_edge_size[2] * 2
|
||||
}
|
||||
local mask_offset_y = 16
|
||||
local settings_mask_size = {
|
||||
1080 + grid_blur_edge_size[1] * 2,
|
||||
grid_height + grid_blur_edge_size[2]
|
||||
}
|
||||
|
||||
local settings_grid_height = grid_height + mask_offset_y
|
||||
|
||||
local tooltip_text_style = table.clone(UIFontSettings.body)
|
||||
tooltip_text_style.text_horizontal_alignment = "left"
|
||||
tooltip_text_style.text_vertical_alignment = "center"
|
||||
tooltip_text_style.horizontal_alignment = "left"
|
||||
tooltip_text_style.vertical_alignment = "center"
|
||||
tooltip_text_style.color = Color.white(255, true)
|
||||
tooltip_text_style.offset = {
|
||||
0,
|
||||
0,
|
||||
2
|
||||
}
|
||||
|
||||
local scenegraph_definition = {
|
||||
screen = UIWorkspaceSettings.screen,
|
||||
tooltip = {
|
||||
vertical_alignment = "top",
|
||||
parent = "screen",
|
||||
horizontal_alignment = "left",
|
||||
size = {
|
||||
0,
|
||||
0
|
||||
},
|
||||
position = {
|
||||
0,
|
||||
0,
|
||||
200
|
||||
}
|
||||
},
|
||||
background = {
|
||||
vertical_alignment = "top",
|
||||
parent = "screen",
|
||||
horizontal_alignment = "left",
|
||||
size = {
|
||||
grid_width,
|
||||
grid_height
|
||||
},
|
||||
position = {
|
||||
180,
|
||||
240,
|
||||
1
|
||||
}
|
||||
},
|
||||
background_icon = {
|
||||
vertical_alignment = "center",
|
||||
parent = "screen",
|
||||
horizontal_alignment = "center",
|
||||
size = {
|
||||
1250,
|
||||
1250
|
||||
},
|
||||
position = {
|
||||
0,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
grid_start = {
|
||||
vertical_alignment = "top",
|
||||
parent = "background",
|
||||
horizontal_alignment = "left",
|
||||
size = {
|
||||
0,
|
||||
0
|
||||
},
|
||||
position = {
|
||||
0,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
grid_content_pivot = {
|
||||
vertical_alignment = "top",
|
||||
parent = "grid_start",
|
||||
horizontal_alignment = "left",
|
||||
size = {
|
||||
0,
|
||||
0
|
||||
},
|
||||
position = {
|
||||
0,
|
||||
0,
|
||||
1
|
||||
}
|
||||
},
|
||||
grid_mask = {
|
||||
vertical_alignment = "center",
|
||||
parent = "background",
|
||||
horizontal_alignment = "center",
|
||||
size = mask_size,
|
||||
position = {
|
||||
0,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
grid_interaction = {
|
||||
vertical_alignment = "top",
|
||||
parent = "background",
|
||||
horizontal_alignment = "left",
|
||||
size = {
|
||||
grid_width + scrollbar_width * 2,
|
||||
mask_size[2]
|
||||
},
|
||||
position = {
|
||||
0,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
scrollbar = {
|
||||
vertical_alignment = "center",
|
||||
parent = "background",
|
||||
horizontal_alignment = "right",
|
||||
size = {
|
||||
scrollbar_width,
|
||||
grid_height
|
||||
},
|
||||
position = {
|
||||
50,
|
||||
0,
|
||||
1
|
||||
}
|
||||
},
|
||||
button = {
|
||||
vertical_alignment = "left",
|
||||
parent = "grid_content_pivot",
|
||||
horizontal_alignment = "top",
|
||||
size = {
|
||||
500,
|
||||
64
|
||||
},
|
||||
position = {
|
||||
0,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
title_divider = {
|
||||
vertical_alignment = "top",
|
||||
parent = "screen",
|
||||
horizontal_alignment = "left",
|
||||
size = {
|
||||
335,
|
||||
18
|
||||
},
|
||||
position = {
|
||||
180,
|
||||
145,
|
||||
1
|
||||
}
|
||||
},
|
||||
title_text = {
|
||||
vertical_alignment = "bottom",
|
||||
parent = "title_divider",
|
||||
horizontal_alignment = "left",
|
||||
size = {
|
||||
500,
|
||||
50
|
||||
},
|
||||
position = {
|
||||
0,
|
||||
-35,
|
||||
1
|
||||
}
|
||||
},
|
||||
settings_grid_background = {
|
||||
vertical_alignment = "top",
|
||||
parent = "screen",
|
||||
horizontal_alignment = "right",
|
||||
size = {
|
||||
1000,
|
||||
settings_grid_height
|
||||
},
|
||||
position = {
|
||||
-180,
|
||||
130,
|
||||
1
|
||||
}
|
||||
},
|
||||
settings_grid_start = {
|
||||
vertical_alignment = "top",
|
||||
parent = "settings_grid_background",
|
||||
horizontal_alignment = "left",
|
||||
size = {
|
||||
0,
|
||||
0
|
||||
},
|
||||
position = {
|
||||
0,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
settings_grid_content_pivot = {
|
||||
vertical_alignment = "top",
|
||||
parent = "settings_grid_start",
|
||||
horizontal_alignment = "left",
|
||||
size = {
|
||||
0,
|
||||
0
|
||||
},
|
||||
position = {
|
||||
0,
|
||||
0,
|
||||
1
|
||||
}
|
||||
},
|
||||
settings_scrollbar = {
|
||||
vertical_alignment = "top",
|
||||
parent = "settings_grid_background",
|
||||
horizontal_alignment = "right",
|
||||
size = {
|
||||
scrollbar_width,
|
||||
grid_height - 26
|
||||
},
|
||||
position = {
|
||||
50,
|
||||
45,
|
||||
1
|
||||
}
|
||||
},
|
||||
settings_grid_mask = {
|
||||
vertical_alignment = "top",
|
||||
parent = "settings_grid_background",
|
||||
horizontal_alignment = "center",
|
||||
size = settings_mask_size,
|
||||
position = {
|
||||
0,
|
||||
mask_offset_y,
|
||||
0
|
||||
}
|
||||
},
|
||||
settings_grid_interaction = {
|
||||
vertical_alignment = "top",
|
||||
parent = "settings_grid_background",
|
||||
horizontal_alignment = "left",
|
||||
size = {
|
||||
1000 + scrollbar_width * 2,
|
||||
mask_size[2]
|
||||
},
|
||||
position = {
|
||||
0,
|
||||
0,
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
local widget_definitions = {
|
||||
settings_overlay = UIWidget.create_definition({
|
||||
{
|
||||
pass_type = "rect",
|
||||
style = {
|
||||
offset = {
|
||||
0,
|
||||
0,
|
||||
20
|
||||
},
|
||||
color = {
|
||||
160,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
}, "screen"),
|
||||
background = UIWidget.create_definition({
|
||||
{
|
||||
pass_type = "rect",
|
||||
style = {
|
||||
color = {
|
||||
160,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
}, "screen"),
|
||||
title_divider = UIWidget.create_definition({
|
||||
{
|
||||
pass_type = "texture",
|
||||
value = "content/ui/materials/dividers/skull_rendered_left_01"
|
||||
}
|
||||
}, "title_divider"),
|
||||
title_text = UIWidget.create_definition({
|
||||
{
|
||||
value_id = "text",
|
||||
style_id = "text",
|
||||
pass_type = "text",
|
||||
value = vmf:localize("mods_options"),
|
||||
style = table.clone(UIFontSettings.header_1)
|
||||
}
|
||||
}, "title_text"),
|
||||
background_icon = UIWidget.create_definition({
|
||||
{
|
||||
value = "content/ui/vector_textures/symbols/cog_skull_01",
|
||||
pass_type = "slug_icon",
|
||||
style = {
|
||||
offset = {
|
||||
0,
|
||||
0,
|
||||
0
|
||||
},
|
||||
color = {
|
||||
80,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
}, "background_icon"),
|
||||
tooltip = UIWidget.create_definition({
|
||||
{
|
||||
pass_type = "rect",
|
||||
style = {
|
||||
vertical_alignment = "center",
|
||||
horizontal_alignment = "center",
|
||||
offset = {
|
||||
0,
|
||||
0,
|
||||
0
|
||||
},
|
||||
color = Color.ui_terminal(255, true),
|
||||
size_addition = {
|
||||
23,
|
||||
23
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
pass_type = "rect",
|
||||
style = {
|
||||
vertical_alignment = "center",
|
||||
horizontal_alignment = "center",
|
||||
offset = {
|
||||
0,
|
||||
0,
|
||||
1
|
||||
},
|
||||
color = Color.black(255, true),
|
||||
size_addition = {
|
||||
20,
|
||||
20
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
value_id = "text",
|
||||
style_id = "text",
|
||||
pass_type = "text",
|
||||
value = "",
|
||||
style = tooltip_text_style
|
||||
}
|
||||
}, "tooltip", {
|
||||
visible = false
|
||||
}),
|
||||
scrollbar = UIWidget.create_definition(ScrollbarPassTemplates.default_scrollbar, "scrollbar"),
|
||||
grid_mask = UIWidget.create_definition({
|
||||
{
|
||||
value = "content/ui/materials/offscreen_masks/ui_overlay_offscreen_vertical_blur",
|
||||
pass_type = "texture",
|
||||
style = {
|
||||
color = {
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
255
|
||||
}
|
||||
}
|
||||
}
|
||||
}, "grid_mask"),
|
||||
grid_interaction = UIWidget.create_definition({
|
||||
{
|
||||
pass_type = "hotspot",
|
||||
content_id = "hotspot"
|
||||
}
|
||||
}, "grid_interaction"),
|
||||
settings_scrollbar = UIWidget.create_definition(ScrollbarPassTemplates.default_scrollbar, "settings_scrollbar"),
|
||||
settings_grid_mask = UIWidget.create_definition({
|
||||
{
|
||||
value = "content/ui/materials/offscreen_masks/ui_overlay_offscreen_vertical_blur",
|
||||
pass_type = "texture",
|
||||
style = {
|
||||
color = {
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
255
|
||||
}
|
||||
}
|
||||
}
|
||||
}, "settings_grid_mask"),
|
||||
settings_grid_interaction = UIWidget.create_definition({
|
||||
{
|
||||
pass_type = "hotspot",
|
||||
content_id = "hotspot"
|
||||
}
|
||||
}, "settings_grid_interaction")
|
||||
}
|
||||
local legend_inputs = {
|
||||
{
|
||||
input_action = "back",
|
||||
on_pressed_callback = "cb_on_back_pressed",
|
||||
display_name = "loc_settings_menu_close_menu",
|
||||
alignment = "left_alignment"
|
||||
},
|
||||
{
|
||||
input_action = "next",
|
||||
display_name = "loc_settings_menu_reset_to_default",
|
||||
on_pressed_callback = "cb_reset_category_to_default",
|
||||
visibility_function = function (parent)
|
||||
return not not parent._selected_category and parent._categories_by_display_name[parent._selected_category].can_be_reset
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
local VMFOptionsViewDefinitions = {
|
||||
legend_inputs = legend_inputs,
|
||||
widget_definitions = widget_definitions,
|
||||
scenegraph_definition = scenegraph_definition
|
||||
}
|
||||
|
||||
return settings("VMFOptionsViewDefinitions", VMFOptionsViewDefinitions)
|
|
@ -0,0 +1,20 @@
|
|||
local vmf_options_view_settings = {
|
||||
scrollbar_width = 10,
|
||||
max_visible_dropdown_options = 5,
|
||||
indentation_spacing = 40,
|
||||
shading_environment = "content/shading_environments/ui/system_menu",
|
||||
grid_size = {
|
||||
500,
|
||||
800
|
||||
},
|
||||
grid_spacing = {
|
||||
0,
|
||||
10
|
||||
},
|
||||
grid_blur_edge_size = {
|
||||
8,
|
||||
8
|
||||
}
|
||||
}
|
||||
|
||||
return settings("VMFOptionsViewSettings", vmf_options_view_settings)
|
4
vmf/scripts/mods/vmf/modules/vmf_dummy.lua
Normal file
4
vmf/scripts/mods/vmf/modules/vmf_dummy.lua
Normal file
|
@ -0,0 +1,4 @@
|
|||
local vmf = get_mod("VMF")
|
||||
|
||||
-- Add vmf functions with a value of dummy_func if they need to be defined while a module is disabled.
|
||||
local dummy_func = function() return end
|
|
@ -11,7 +11,7 @@ end
|
|||
-- #####################################################################################################################
|
||||
|
||||
-- Defining VMFMod class.
|
||||
VMFMod = class(VMFMod)
|
||||
VMFMod = class("VMFMod")
|
||||
|
||||
-- Creating mod data table when object of VMFMod class is created.
|
||||
function VMFMod:init(mod_name)
|
||||
|
|
|
@ -168,40 +168,18 @@ function vmf.initialize_mod_data(mod, mod_data)
|
|||
vmf.register_mod_as_mutator(mod, mod_data.mutator_settings)
|
||||
end
|
||||
|
||||
-- Mod's options initialization (with legacy widget definitions support)
|
||||
-- Mod's options initialization
|
||||
if mod_data.options or ((mod_data.is_togglable and not mod_data.is_mutator) and not mod_data.options_widgets) then
|
||||
local success, error_message = pcall(vmf.initialize_mod_options, mod, mod_data.options)
|
||||
if not success then
|
||||
mod:error(ERRORS.REGULAR.mod_options_initializing_failed, error_message)
|
||||
return
|
||||
end
|
||||
elseif mod_data.options_widgets then
|
||||
vmf.initialize_mod_options_legacy(mod, mod_data.options_widgets)
|
||||
end
|
||||
|
||||
-- Textures initialization @TODO: move to a separate function
|
||||
if type(mod_data.custom_gui_textures) == "table" then
|
||||
local custom_gui_textures = mod_data.custom_gui_textures
|
||||
|
||||
if type(custom_gui_textures.textures) == "table" then
|
||||
vmf.custom_textures(mod, unpack(custom_gui_textures.textures))
|
||||
end
|
||||
|
||||
if type(custom_gui_textures.atlases) == "table" then
|
||||
for _, atlas_settings in ipairs(custom_gui_textures.atlases) do
|
||||
if type(atlas_settings) == "table" then
|
||||
vmf.custom_atlas(mod, unpack(atlas_settings))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if type(custom_gui_textures.ui_renderer_injections) == "table" then
|
||||
for _, injection_settings in ipairs(custom_gui_textures.ui_renderer_injections) do
|
||||
if type(injection_settings) == "table" then
|
||||
vmf.inject_materials(mod, unpack(injection_settings))
|
||||
end
|
||||
end
|
||||
end
|
||||
-- @TODO: Not implemented
|
||||
end
|
||||
|
||||
return true
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
local vmf = get_mod("VMF")
|
||||
|
||||
local vmf_mod_data = {}
|
||||
vmf_mod_data.name = "Vermintide Mod Framework"
|
||||
vmf_mod_data.name = "Darktide Mod Framework"
|
||||
vmf_mod_data.options = {
|
||||
widgets = {
|
||||
{
|
||||
|
@ -10,11 +10,7 @@ vmf_mod_data.options = {
|
|||
default_value = {"f4"},
|
||||
keybind_trigger = "pressed",
|
||||
keybind_type = "view_toggle",
|
||||
view_name = "vmf_options_view",
|
||||
transition_data = {
|
||||
open_view_transition_name = "vmf_options_view_open",
|
||||
close_view_transition_name = "vmf_options_view_close"
|
||||
}
|
||||
view_name = "vmf_options_view"
|
||||
},
|
||||
{
|
||||
setting_id = "vmf_options_scrolling_speed",
|
||||
|
@ -60,40 +56,67 @@ vmf_mod_data.options = {
|
|||
default_value = "default",
|
||||
options = {
|
||||
{text = "settings_default", value = "default"},
|
||||
{text = "settings_custom", value = "custom", show_widgets = {1, 2, 3, 4, 5}},
|
||||
{text = "settings_custom", value = "custom", show_widgets = {1, 2, 3, 4, 5, 6}},
|
||||
},
|
||||
sub_widgets = {
|
||||
{
|
||||
setting_id = "output_mode_echo",
|
||||
setting_id = "output_mode_notification",
|
||||
type = "dropdown",
|
||||
default_value = 3,
|
||||
default_value = 5,
|
||||
options = {
|
||||
{text = "output_disabled", value = 0},
|
||||
{text = "output_log", value = 1},
|
||||
{text = "output_chat", value = 2},
|
||||
{text = "output_log_and_chat", value = 3},
|
||||
{text = "output_notification", value = 3},
|
||||
{text = "output_log_and_chat", value = 4},
|
||||
{text = "output_log_and_notification", value = 5},
|
||||
{text = "output_chat_and_notification", value = 6},
|
||||
{text = "output_all", value = 7},
|
||||
}
|
||||
},
|
||||
{
|
||||
setting_id = "output_mode_echo",
|
||||
type = "dropdown",
|
||||
default_value = 4,
|
||||
options = {
|
||||
{text = "output_disabled", value = 0},
|
||||
{text = "output_log", value = 1},
|
||||
{text = "output_chat", value = 2},
|
||||
{text = "output_notification", value = 3},
|
||||
{text = "output_log_and_chat", value = 4},
|
||||
{text = "output_log_and_notification", value = 5},
|
||||
{text = "output_chat_and_notification", value = 6},
|
||||
{text = "output_all", value = 7},
|
||||
}
|
||||
},
|
||||
{
|
||||
setting_id = "output_mode_error",
|
||||
type = "dropdown",
|
||||
default_value = 3,
|
||||
default_value = 4,
|
||||
options = {
|
||||
{text = "output_disabled", value = 0},
|
||||
{text = "output_log", value = 1},
|
||||
{text = "output_chat", value = 2},
|
||||
{text = "output_log_and_chat", value = 3},
|
||||
{text = "output_notification", value = 3},
|
||||
{text = "output_log_and_chat", value = 4},
|
||||
{text = "output_log_and_notification", value = 5},
|
||||
{text = "output_chat_and_notification", value = 6},
|
||||
{text = "output_all", value = 7},
|
||||
}
|
||||
},
|
||||
{
|
||||
setting_id = "output_mode_warning",
|
||||
type = "dropdown",
|
||||
default_value = 3,
|
||||
default_value = 4,
|
||||
options = {
|
||||
{text = "output_disabled", value = 0},
|
||||
{text = "output_log", value = 1},
|
||||
{text = "output_chat", value = 2},
|
||||
{text = "output_log_and_chat", value = 3},
|
||||
{text = "output_notification", value = 3},
|
||||
{text = "output_log_and_chat", value = 4},
|
||||
{text = "output_log_and_notification", value = 5},
|
||||
{text = "output_chat_and_notification", value = 6},
|
||||
{text = "output_all", value = 7},
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -104,7 +127,11 @@ vmf_mod_data.options = {
|
|||
{text = "output_disabled", value = 0},
|
||||
{text = "output_log", value = 1},
|
||||
{text = "output_chat", value = 2},
|
||||
{text = "output_log_and_chat", value = 3},
|
||||
{text = "output_notification", value = 3},
|
||||
{text = "output_log_and_chat", value = 4},
|
||||
{text = "output_log_and_notification", value = 5},
|
||||
{text = "output_chat_and_notification", value = 6},
|
||||
{text = "output_all", value = 7},
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -115,7 +142,11 @@ vmf_mod_data.options = {
|
|||
{text = "output_disabled", value = 0},
|
||||
{text = "output_log", value = 1},
|
||||
{text = "output_chat", value = 2},
|
||||
{text = "output_log_and_chat", value = 3},
|
||||
{text = "output_notification", value = 3},
|
||||
{text = "output_log_and_chat", value = 4},
|
||||
{text = "output_log_and_notification", value = 5},
|
||||
{text = "output_chat_and_notification", value = 6},
|
||||
{text = "output_all", value = 7},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -139,7 +170,7 @@ vmf_mod_data.options = {
|
|||
{
|
||||
setting_id = "chat_history_remove_dups",
|
||||
type = "checkbox",
|
||||
default_value = false,
|
||||
default_value = true,
|
||||
sub_widgets = {
|
||||
{
|
||||
setting_id = "chat_history_remove_dups_mode",
|
||||
|
@ -170,7 +201,10 @@ vmf.on_setting_changed = function (setting_id)
|
|||
|
||||
if setting_id == "vmf_options_scrolling_speed" then
|
||||
|
||||
-- Not necessary until the view is loaded
|
||||
if vmf.load_vmf_options_view_settings then
|
||||
vmf.load_vmf_options_view_settings()
|
||||
end
|
||||
|
||||
elseif setting_id == "developer_mode" then
|
||||
|
||||
|
@ -192,6 +226,7 @@ vmf.on_setting_changed = function (setting_id)
|
|||
vmf.load_custom_textures_settings()
|
||||
|
||||
elseif setting_id == "logging_mode"
|
||||
or setting_id == "output_mode_notification"
|
||||
or setting_id == "output_mode_echo"
|
||||
or setting_id == "output_mode_error"
|
||||
or setting_id == "output_mode_warning"
|
||||
|
@ -234,7 +269,11 @@ if not vmf:get("vmf_initialized") then
|
|||
vmf.load_custom_textures_settings()
|
||||
vmf.load_dev_console_settings()
|
||||
vmf.load_chat_history_settings()
|
||||
--vmf.load_vmf_options_view_settings()
|
||||
|
||||
-- Not necessary until the view is loaded
|
||||
if vmf.load_vmf_options_view_settings then
|
||||
vmf.load_vmf_options_view_settings()
|
||||
end
|
||||
|
||||
vmf:set("vmf_initialized", true)
|
||||
end
|
||||
|
|
|
@ -12,9 +12,6 @@ local PUBLIC_STATUSES = {
|
|||
|
||||
local ERRORS = {
|
||||
REGULAR = {
|
||||
-- check_vt1:
|
||||
cant_use_vmf_package_manager_in_vt1 = "[VMF Package Manager] (%s): you can't use VMF package manager in VT1 " ..
|
||||
"because VT1 mods don't support more than 1 resource package.",
|
||||
-- VMFMod:load_package:
|
||||
package_already_loaded = "[VMF Package Manager] (load_package): package '%s' has already been loaded.",
|
||||
package_not_found = "[VMF Package Manager] (load_package): could not find package '%s'.",
|
||||
|
@ -41,21 +38,13 @@ local WARNINGS = {
|
|||
-- ##### Local functions ###############################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
||||
local function check_vt1(mod, function_name)
|
||||
if VT1 then
|
||||
mod:error(ERRORS.REGULAR.cant_use_vmf_package_manager_in_vt1, function_name)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Brings resources of the loaded package in game and executes callback. Or unloads package's resources, if loading was
|
||||
-- cancelled.
|
||||
local function flush_package(package_name)
|
||||
local package_data = _packages[package_name]
|
||||
|
||||
if package_data.status == "loading_cancelled" then
|
||||
Mod.release_resource_package(package_data.resource_package)
|
||||
Application.release_resource_package(package_data.resource_package)
|
||||
_packages[package_name] = nil
|
||||
else
|
||||
package_data.resource_package:flush()
|
||||
|
@ -88,8 +77,7 @@ end
|
|||
* sync [boolean] : (optional) load the packages synchronously, freezing the game until it is loaded
|
||||
--]]
|
||||
function VMFMod:load_package(package_name, callback, sync)
|
||||
if check_vt1(self, "load_package") or
|
||||
vmf.check_wrong_argument_type(self, "load_package", "package_name", package_name, "string") or
|
||||
if vmf.check_wrong_argument_type(self, "load_package", "package_name", package_name, "string") or
|
||||
vmf.check_wrong_argument_type(self, "load_package", "callback", callback, "function", "nil") or
|
||||
vmf.check_wrong_argument_type(self, "load_package", "sync", sync, "boolean", "nil")
|
||||
then
|
||||
|
@ -101,7 +89,7 @@ function VMFMod:load_package(package_name, callback, sync)
|
|||
return
|
||||
end
|
||||
|
||||
local resource_package = Mod.resource_package(self:get_internal_data("mod_handle"), package_name)
|
||||
local resource_package = Application.resource_package(package_name)
|
||||
if not resource_package then
|
||||
self:error(ERRORS.REGULAR.package_not_found, package_name)
|
||||
return
|
||||
|
@ -150,8 +138,7 @@ end
|
|||
* package_name [string]: package name. needs to be the full path to the `.package` file without the extension
|
||||
--]]
|
||||
function VMFMod:unload_package(package_name)
|
||||
if check_vt1(self, "unload_package") or
|
||||
vmf.check_wrong_argument_type(self, "unload_package", "package_name", package_name, "string")
|
||||
if vmf.check_wrong_argument_type(self, "unload_package", "package_name", package_name, "string")
|
||||
then
|
||||
return
|
||||
end
|
||||
|
@ -168,7 +155,7 @@ function VMFMod:unload_package(package_name)
|
|||
elseif package_status == "loading" then
|
||||
_packages[package_name].status = "loading_cancelled"
|
||||
elseif package_status == "loaded" then
|
||||
Mod.release_resource_package(_packages[package_name].resource_package)
|
||||
Application.release_resource_package(_packages[package_name].resource_package)
|
||||
_packages[package_name] = nil
|
||||
end
|
||||
end
|
||||
|
@ -179,8 +166,7 @@ end
|
|||
* package_name [string]: package name. needs to be the full path to the `.package` file without the extension
|
||||
--]]
|
||||
function VMFMod:package_status(package_name)
|
||||
if check_vt1(self, "package_status") or
|
||||
vmf.check_wrong_argument_type(self, "package_status", "package_name", package_name, "string")
|
||||
if vmf.check_wrong_argument_type(self, "package_status", "package_name", package_name, "string")
|
||||
then
|
||||
return
|
||||
end
|
||||
|
|
|
@ -1,48 +1,46 @@
|
|||
local vmf
|
||||
|
||||
-- Global variable indicating which version of the game is currently running
|
||||
VT1 = not pcall(require, "PlayFab.json")
|
||||
VT1 = false
|
||||
|
||||
-- Native mod object used by Fatshark mod manager
|
||||
local vmf_mod_object = {}
|
||||
|
||||
-- Global method to load a file through iowith a return
|
||||
local mod_dofile = Mods.file.dofile
|
||||
|
||||
-- #####################################################################################################################
|
||||
-- ##### Initialization ################################################################################################
|
||||
-- #####################################################################################################################
|
||||
|
||||
function vmf_mod_object:init()
|
||||
dofile("scripts/mods/vmf/modules/vmf_mod_data")
|
||||
dofile("scripts/mods/vmf/modules/vmf_mod_manager")
|
||||
dofile("scripts/mods/vmf/modules/vmf_package_manager")
|
||||
dofile("scripts/mods/vmf/modules/core/safe_calls")
|
||||
dofile("scripts/mods/vmf/modules/core/events")
|
||||
dofile("scripts/mods/vmf/modules/core/settings")
|
||||
dofile("scripts/mods/vmf/modules/core/logging")
|
||||
dofile("scripts/mods/vmf/modules/core/misc")
|
||||
dofile("scripts/mods/vmf/modules/core/persistent_tables")
|
||||
dofile("scripts/mods/vmf/modules/debug/dev_console")
|
||||
dofile("scripts/mods/vmf/modules/debug/table_dump")
|
||||
dofile("scripts/mods/vmf/modules/core/hooks")
|
||||
dofile("scripts/mods/vmf/modules/core/toggling")
|
||||
dofile("scripts/mods/vmf/modules/core/keybindings")
|
||||
dofile("scripts/mods/vmf/modules/core/chat")
|
||||
dofile("scripts/mods/vmf/modules/core/localization")
|
||||
dofile("scripts/mods/vmf/modules/core/options")
|
||||
dofile("scripts/mods/vmf/modules/legacy/options")
|
||||
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_views")
|
||||
dofile("scripts/mods/vmf/modules/ui/chat/chat_actions")
|
||||
dofile("scripts/mods/vmf/modules/ui/options/mod_options")
|
||||
dofile("scripts/mods/vmf/modules/vmf_options")
|
||||
|
||||
if VT1 then
|
||||
dofile("scripts/mods/vmf/modules/core/mutators/mutators_manager")
|
||||
dofile("scripts/mods/vmf/modules/ui/mutators/mutators_gui")
|
||||
else
|
||||
dofile("scripts/mods/vmf/modules/gui/custom_hud_components")
|
||||
end
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/vmf_mod_data")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/vmf_mod_manager")
|
||||
--mod_dofile("dmf/scripts/mods/vmf/modules/vmf_dummy")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/vmf_package_manager")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/core/safe_calls")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/core/events")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/core/settings")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/core/logging")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/core/misc")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/core/persistent_tables")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/debug/dev_console")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/debug/table_dump")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/core/hooks")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/core/require")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/core/toggling")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/core/keybindings")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/core/chat")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/core/localization")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/core/options")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/core/network")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/core/commands")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/gui/custom_textures")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/gui/custom_views")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/ui/chat/chat_actions")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/ui/options/mod_options")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/vmf_options")
|
||||
mod_dofile("dmf/scripts/mods/vmf/modules/core/mutators/mutators_manager")
|
||||
|
||||
vmf = get_mod("VMF")
|
||||
vmf.delayed_chat_messages_hook()
|
||||
|
@ -61,7 +59,6 @@ function vmf_mod_object:update(dt)
|
|||
vmf.mods_update_event(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
|
||||
|
||||
|
@ -70,9 +67,6 @@ function vmf_mod_object:update(dt)
|
|||
vmf.create_network_dictionary()
|
||||
vmf.ping_vmf_users()
|
||||
|
||||
if VT1 then vmf.modify_map_view() end
|
||||
if VT1 then vmf.mutators_delete_raw_config() end
|
||||
|
||||
vmf.all_mods_loaded_event()
|
||||
|
||||
vmf.all_mods_were_loaded = true
|
||||
|
@ -84,32 +78,27 @@ function vmf_mod_object:on_unload()
|
|||
print("VMF:ON_UNLOAD()")
|
||||
vmf.save_chat_history()
|
||||
vmf.save_unsaved_settings_to_file()
|
||||
vmf.network_unload()
|
||||
vmf.destroy_command_gui()
|
||||
end
|
||||
|
||||
|
||||
function vmf_mod_object:on_reload()
|
||||
print("VMF:ON_RELOAD()")
|
||||
vmf.disable_mods_options_button()
|
||||
if VT1 then
|
||||
vmf.reset_map_view()
|
||||
else
|
||||
vmf.remove_injected_hud_components()
|
||||
end
|
||||
vmf.mods_unload_event(false)
|
||||
vmf.remove_custom_views()
|
||||
vmf.unload_all_resource_packages()
|
||||
vmf.hooks_unload()
|
||||
vmf.reset_guis()
|
||||
vmf.destroy_command_gui()
|
||||
end
|
||||
|
||||
|
||||
function vmf_mod_object:on_game_state_changed(status, state)
|
||||
print("VMF:ON_GAME_STATE_CHANGED(), status: " .. tostring(status) .. ", state: " .. tostring(state))
|
||||
if VT1 then vmf.check_old_vmf() end
|
||||
vmf.mods_game_state_changed_event(status, state)
|
||||
vmf.save_unsaved_settings_to_file()
|
||||
vmf.apply_delayed_hooks(status, state)
|
||||
vmf.destroy_command_gui()
|
||||
|
||||
if status == "enter" and state == "StateIngame" then
|
||||
vmf.create_keybinds_input_service()
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
boot_package = "resource_packages/vmf/vmf_resources"
|
|
@ -1,8 +1,5 @@
|
|||
return {
|
||||
run = function()
|
||||
return dofile("scripts/mods/vmf/vmf_loader")
|
||||
end,
|
||||
packages = {
|
||||
"resource_packages/vmf"
|
||||
}
|
||||
return Mods.file.dofile("dmf/scripts/mods/vmf/vmf_loader")
|
||||
end
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue