[Custom Views] Add a lot of checks with feedback
This commit is contained in:
parent
f3443dc32e
commit
0e51011f6c
4 changed files with 181 additions and 43 deletions
|
@ -215,7 +215,7 @@ local function perform_keybind_action(data, is_pressed)
|
||||||
call_function(data.mod, data.function_name, is_pressed)
|
call_function(data.mod, data.function_name, is_pressed)
|
||||||
return true
|
return true
|
||||||
elseif data.type == "view_toggle" and data.mod:is_enabled() then
|
elseif data.type == "view_toggle" and data.mod:is_enabled() then
|
||||||
vmf.keybind_toggle_view(data.view_name, can_perform_action, is_pressed)
|
vmf.keybind_toggle_view(data.mod, data.view_name, can_perform_action, is_pressed)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -25,10 +25,11 @@ function vmf.throw_error(error_message, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function vmf.catch_errors(mod, error_format, exec_function, ...)
|
function vmf.catch_errors(mod, error_prefix, additional_error_prefix_info, exec_function, ...)
|
||||||
local success, error_message = pcall(exec_function, ...)
|
local success, error_message = pcall(exec_function, ...)
|
||||||
if not success then
|
if not success then
|
||||||
mod:error(string.format(error_format, error_message))
|
error_prefix = string.format(error_prefix, additional_error_prefix_info)
|
||||||
|
mod:error(string.format("%s: %s", error_prefix, error_message))
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -5,20 +5,56 @@ local _ingame_ui = nil
|
||||||
local _ingame_ui_transitions = require("scripts/ui/views/ingame_ui_settings").transitions
|
local _ingame_ui_transitions = require("scripts/ui/views/ingame_ui_settings").transitions
|
||||||
local _views_data = {}
|
local _views_data = {}
|
||||||
|
|
||||||
|
local ERRORS = {
|
||||||
|
THROWABLE = {
|
||||||
|
-- inject_view:
|
||||||
|
view_already_exists = "view with name '%s' already persists in original game.",
|
||||||
|
transition_already_exists = "transition with name '%s' already persists in original game.",
|
||||||
|
view_initializing_failed = "was not able to initialize '%s' view: execution of 'init_view_function' failed.",
|
||||||
|
-- validate_view_data:
|
||||||
|
view_data_wrong_type = "view data must be a table, not %s.",
|
||||||
|
view_name_wrong_type = "'view_name' must be a string, not %s.",
|
||||||
|
view_transitions_wrong_type = "'view_transitions' must be a table, not %s.",
|
||||||
|
view_settings_wrong_type = "'view_settings' must be a table, not %s.",
|
||||||
|
transition_wrong_type = "all transitions inside 'view_transitions' must be functions, but '%s' transition is %s.",
|
||||||
|
transition_name_taken = "transition name '%s' is already used by '%s' mod for '%s' view.",
|
||||||
|
init_view_function_wrong_type = "'view_settings.init_view_function' must be a function, not %s.",
|
||||||
|
active_wrong_type = "'view_settings.active' must be a table, not %s.",
|
||||||
|
active_missing_element = "'view_settings.active' must contain 2 elements: 'inn' and 'ingame'.",
|
||||||
|
active_element_wrong_name = "the only allowed names for 'view_settings.active' elements are 'inn' and 'ingame'; " ..
|
||||||
|
"you can't name your element '%s'.",
|
||||||
|
active_element_wrong_type = "'view_settings.active.%s' must be boolean, not %s.",
|
||||||
|
blocked_transitions_wrong_type = "'view_settings.blocked_transitions' (optional) must be a table, not %s.",
|
||||||
|
blocked_transitions_missing_element = "'view_settings.blocked_transitions' must contain 2 table elements: " ..
|
||||||
|
"'inn' and 'ingame'.",
|
||||||
|
blocked_transitions_element_wrong_name = "the only allowed names for 'view_settings.active' elements are " ..
|
||||||
|
"'inn' and 'ingame'; you can't name your element '%s'.",
|
||||||
|
blocked_transitions_element_wrong_type = "'view_settings.blocked_transitions.%s' must be a table, not %s.",
|
||||||
|
blocked_transition_invalid = "you can't put transition '%s' into 'view_settings.blocked_transitions.%s', " ..
|
||||||
|
"because it's not listed in 'view_transitions'.",
|
||||||
|
blocked_transition_wrong_value = "invalid value for 'view_settings.blocked_transitions.%s.%s'; must be 'true'.",
|
||||||
|
keybind_transitions_wrong_type = "'view_settings.keybind_transitions' (optional) must be a table, not %s.",
|
||||||
|
open_view_transition_wrong_type = "'view_settings.keybind_transitions.open_view_transition' (optional) must be " ..
|
||||||
|
"a string, not %s.",
|
||||||
|
transition_fade_wrong_type = "'view_settings.keybind_transitions.transition_fade' (optional) must be a boolean, " ..
|
||||||
|
"not %s.",
|
||||||
|
},
|
||||||
|
REGULAR = {
|
||||||
|
view_not_registered = "[Custom Views] Opening view with keybind: view '%s' wasn't registered for this mod."
|
||||||
|
},
|
||||||
|
PREFIX = {
|
||||||
|
register_view_validating = "[Custom Views] (register_view) View data validating '%s'",
|
||||||
|
register_view_injection = "[Custom Views] (register_view) View injection '%s'",
|
||||||
|
ingameui_hook_injection = "[Custom Views] View injection '%s'"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
-- #####################################################################################################################
|
-- #####################################################################################################################
|
||||||
-- ##### Local functions ###############################################################################################
|
-- ##### Local functions ###############################################################################################
|
||||||
-- #####################################################################################################################
|
-- #####################################################################################################################
|
||||||
|
|
||||||
local function find_view_owner(view_name)
|
-- @THROWS_ERRORS
|
||||||
end
|
local function inject_view(view_name)
|
||||||
|
|
||||||
|
|
||||||
local function find_transition_owner(transition_name)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- Throws error.
|
|
||||||
local function inject_elements(view_name)
|
|
||||||
local view_settings = _views_data[view_name].view_settings
|
local view_settings = _views_data[view_name].view_settings
|
||||||
|
|
||||||
local mod = _views_data[view_name].mod
|
local mod = _views_data[view_name].mod
|
||||||
|
@ -28,11 +64,11 @@ local function inject_elements(view_name)
|
||||||
|
|
||||||
-- Check for collisions.
|
-- Check for collisions.
|
||||||
if _ingame_ui.views[view_name] then
|
if _ingame_ui.views[view_name] then
|
||||||
-- @TODO: throw error
|
vmf.throw_error(ERRORS.THROWABLE["view_already_exists"], view_name)
|
||||||
end
|
end
|
||||||
for transition_name, _ in pairs(transitions) do
|
for transition_name, _ in pairs(transitions) do
|
||||||
if _ingame_ui_transitions[transition_name] then
|
if _ingame_ui_transitions[transition_name] then
|
||||||
-- @TODO: throw error
|
vmf.throw_error(ERRORS.THROWABLE["transition_already_exists"], transition_name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -41,7 +77,7 @@ local function inject_elements(view_name)
|
||||||
if success then
|
if success then
|
||||||
_ingame_ui.views[view_name] = view
|
_ingame_ui.views[view_name] = view
|
||||||
else
|
else
|
||||||
-- @TODO: throw error
|
vmf.throw_error(ERRORS.THROWABLE["view_initializing_failed"], view_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Inject view transitions.
|
-- Inject view transitions.
|
||||||
|
@ -56,7 +92,7 @@ local function inject_elements(view_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function remove_injected_elements(on_reload)
|
local function remove_injected_views(on_reload)
|
||||||
-- These elements should be removed only on_reload, because, otherwise, they will be deleted automatically.
|
-- These elements should be removed only on_reload, because, otherwise, they will be deleted automatically.
|
||||||
if on_reload and _ingame_ui then
|
if on_reload and _ingame_ui then
|
||||||
-- If some custom view is active, close it.
|
-- If some custom view is active, close it.
|
||||||
|
@ -73,28 +109,125 @@ local function remove_injected_elements(on_reload)
|
||||||
end
|
end
|
||||||
_ingame_ui.views[view_name] = nil
|
_ingame_ui.views[view_name] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Remove blocked transitions
|
|
||||||
local blocked_transitions = view_data.view_settings.blocked_transitions[_ingame_ui.is_in_inn and "inn" or
|
|
||||||
"ingame"]
|
|
||||||
for blocked_transition_name, _ in pairs(blocked_transitions) do
|
|
||||||
_ingame_ui.blocked_transitions[blocked_transition_name] = nil
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Remove injected transitions.
|
|
||||||
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
|
for transition_name, _ in pairs(view_data.view_transitions) do
|
||||||
_ingame_ui_transitions[transition_name] = nil
|
_ingame_ui_transitions[transition_name] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Remove blocked transitions
|
||||||
|
local blocked_transitions = view_data.view_settings.blocked_transitions[_ingame_ui.is_in_inn and "inn" or "ingame"]
|
||||||
|
for blocked_transition_name, _ in pairs(blocked_transitions) do
|
||||||
|
_ingame_ui.blocked_transitions[blocked_transition_name] = nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Throws error.
|
-- @THROWS_ERRORS
|
||||||
-- Make, so blocked transitions can be only the one from this view, so they won't need further checks
|
|
||||||
local function validate_view_data(view_data)
|
local function validate_view_data(view_data)
|
||||||
|
-- Basic checks.
|
||||||
|
if type(view_data) ~= "table" then
|
||||||
|
vmf.throw_error(ERRORS.THROWABLE["view_data_wrong_type"], type(view_data))
|
||||||
|
end
|
||||||
|
if type(view_data.view_name) ~= "string" then
|
||||||
|
vmf.throw_error(ERRORS.THROWABLE["view_name_wrong_type"], type(view_data.view_name))
|
||||||
|
end
|
||||||
|
if type(view_data.view_transitions) ~= "table" then
|
||||||
|
vmf.throw_error(ERRORS.THROWABLE["view_transitions_wrong_type"], type(view_data.view_transitions))
|
||||||
|
end
|
||||||
|
if type(view_data.view_settings) ~= "table" then
|
||||||
|
vmf.throw_error(ERRORS.THROWABLE["view_settings_wrong_type"], type(view_data.view_settings))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- VIEW TRANSITIONS
|
||||||
|
|
||||||
|
local view_transitions = view_data.view_transitions
|
||||||
|
for transition_name, transition_function in pairs(view_transitions) do
|
||||||
|
if type(transition_function) ~= "function" then
|
||||||
|
vmf.throw_error(ERRORS.THROWABLE["transition_wrong_type"], transition_name, type(transition_function))
|
||||||
|
end
|
||||||
|
for another_view_name, another_view_data in pairs(_views_data) do
|
||||||
|
for another_transition_name, _ in pairs(another_view_data.view_transitions) do
|
||||||
|
if transition_name == another_transition_name then
|
||||||
|
vmf.throw_error(ERRORS.THROWABLE["transition_name_taken"], transition_name, another_view_data.mod:get_name(),
|
||||||
|
another_view_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- VIEW SETTINGS
|
||||||
|
|
||||||
|
local view_settings = view_data.view_settings
|
||||||
|
|
||||||
|
-- Use default values for optional fields if they are not defined.
|
||||||
|
view_settings.blocked_transitions = view_settings.blocked_transitions or {inn = {}, ingame = {}}
|
||||||
|
view_settings.keybind_transitions = view_settings.keybind_transitions or {}
|
||||||
|
|
||||||
|
-- Verify everything.
|
||||||
|
if type(view_settings.init_view_function) ~= "function" then
|
||||||
|
vmf.throw_error(ERRORS.THROWABLE["init_view_function_wrong_type"], type(view_settings.init_view_function))
|
||||||
|
end
|
||||||
|
|
||||||
|
local active = view_settings.active
|
||||||
|
if type(active) ~= "table" then
|
||||||
|
vmf.throw_error(ERRORS.THROWABLE["active_wrong_type"], type(active))
|
||||||
|
end
|
||||||
|
if not active.inn or not active.ingame then
|
||||||
|
vmf.throw_error(ERRORS.THROWABLE["active_missing_element"])
|
||||||
|
end
|
||||||
|
for level_name, value in pairs(active) do
|
||||||
|
if level_name ~= "inn" and level_name ~= "ingame" then
|
||||||
|
vmf.throw_error(ERRORS.THROWABLE["active_element_wrong_name"], level_name)
|
||||||
|
end
|
||||||
|
if type(value) ~= "boolean" then
|
||||||
|
vmf.throw_error(ERRORS.THROWABLE["active_element_wrong_type"], level_name, type(value))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local blocked_transitions = view_settings.blocked_transitions
|
||||||
|
if type(blocked_transitions) ~= "table" then
|
||||||
|
vmf.throw_error(ERRORS.THROWABLE["blocked_transitions_wrong_type"], type(blocked_transitions))
|
||||||
|
end
|
||||||
|
if not blocked_transitions.inn or not blocked_transitions.ingame then
|
||||||
|
vmf.throw_error(ERRORS.THROWABLE["blocked_transitions_missing_element"])
|
||||||
|
end
|
||||||
|
for level_name, level_blocked_transitions in pairs(blocked_transitions) do
|
||||||
|
if level_name ~= "inn" and level_name ~= "ingame" then
|
||||||
|
vmf.throw_error(ERRORS.THROWABLE["blocked_transitions_element_wrong_name"], level_name)
|
||||||
|
end
|
||||||
|
if type(level_blocked_transitions) ~= "table" then
|
||||||
|
vmf.throw_error(ERRORS.THROWABLE["blocked_transitions_element_wrong_type"], level_name,
|
||||||
|
type(level_blocked_transitions))
|
||||||
|
end
|
||||||
|
for transition_name, value in pairs(level_blocked_transitions) do
|
||||||
|
if not view_transitions[transition_name] then
|
||||||
|
vmf.throw_error(ERRORS.THROWABLE["blocked_transition_invalid"], transition_name, level_name)
|
||||||
|
end
|
||||||
|
if value ~= true then
|
||||||
|
vmf.throw_error(ERRORS.THROWABLE["blocked_transition_wrong_value"], level_name, transition_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local keybind_transitions = view_settings.keybind_transitions
|
||||||
|
if type(keybind_transitions) ~= "table" then
|
||||||
|
vmf.throw_error(ERRORS.THROWABLE["keybind_transitions_wrong_type"], type(keybind_transitions))
|
||||||
|
end
|
||||||
|
if keybind_transitions.open_view_transition and type(keybind_transitions.open_view_transition) ~= "string" then
|
||||||
|
vmf.throw_error(ERRORS.THROWABLE["open_view_transition_wrong_type"], type(keybind_transitions.open_view_transition))
|
||||||
|
end
|
||||||
|
if keybind_transitions.close_view_transition and type(keybind_transitions.close_view_transition) ~= "string" then
|
||||||
|
vmf.throw_error(ERRORS.THROWABLE["close_view_transition_wrong_type"],
|
||||||
|
type(keybind_transitions.close_view_transition))
|
||||||
|
end
|
||||||
|
if keybind_transitions.transition_fade and type(keybind_transitions.transition_fade) ~= "boolean" then
|
||||||
|
vmf.throw_error(ERRORS.THROWABLE["transition_fade_wrong_type"], type(keybind_transitions.transition_fade))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- #####################################################################################################################
|
-- #####################################################################################################################
|
||||||
|
@ -130,22 +263,23 @@ end
|
||||||
|
|
||||||
|
|
||||||
function VMFMod:register_view(view_data)
|
function VMFMod:register_view(view_data)
|
||||||
if vmf.check_wrong_argument_type(self, "register_view", "view_data", view_data, "table") then
|
-- @TODO: load table from file, check if it's table
|
||||||
|
view_data = table.clone(view_data)
|
||||||
|
|
||||||
|
local view_name = view_data.view_name
|
||||||
|
|
||||||
|
if vmf.catch_errors(self, ERRORS.PREFIX["register_view_validating"], view_name, validate_view_data, view_data) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if vmf.catch_errors(self, "(register_view) view data validating: %s", validate_view_data, view_data) then
|
_views_data[view_name] = {
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
_views_data[view_data.view_name] = {
|
|
||||||
mod = self,
|
mod = self,
|
||||||
view_settings = table.clone(view_data.view_settings),
|
view_settings = view_data.view_settings,
|
||||||
view_transitions = table.clone(view_data.view_transitions)
|
view_transitions = view_data.view_transitions
|
||||||
}
|
}
|
||||||
|
|
||||||
if _ingame_ui then
|
if _ingame_ui then
|
||||||
if vmf.catch_errors(self, "(custom views) view injection: %s", inject_elements, view_data.view_name) then
|
if vmf.catch_errors(self, ERRORS.PREFIX["register_view_injection"], view_name, inject_view, view_name) then
|
||||||
_views_data[view_data.view_name] = nil
|
_views_data[view_data.view_name] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -158,7 +292,7 @@ end
|
||||||
vmf:hook_safe(IngameUI, "init", function(self)
|
vmf:hook_safe(IngameUI, "init", function(self)
|
||||||
_ingame_ui = self
|
_ingame_ui = self
|
||||||
for view_name, _ in pairs(_views_data) do
|
for view_name, _ in pairs(_views_data) do
|
||||||
if vmf.catch_errors(self, "(custom views) view injection: %s", inject_elements, view_name) then
|
if vmf.catch_errors(self, ERRORS.PREFIX["ingameui_hook_injection"], view_name, inject_view, view_name) then
|
||||||
_views_data[view_name] = nil
|
_views_data[view_name] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -166,8 +300,8 @@ end)
|
||||||
|
|
||||||
|
|
||||||
vmf:hook_safe(IngameUI, "destroy", function()
|
vmf:hook_safe(IngameUI, "destroy", function()
|
||||||
|
remove_injected_views(false)
|
||||||
_ingame_ui = nil
|
_ingame_ui = nil
|
||||||
remove_injected_elements(false)
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- #####################################################################################################################
|
-- #####################################################################################################################
|
||||||
|
@ -175,15 +309,17 @@ end)
|
||||||
-- #####################################################################################################################
|
-- #####################################################################################################################
|
||||||
|
|
||||||
function vmf.remove_custom_views()
|
function vmf.remove_custom_views()
|
||||||
remove_injected_elements(true)
|
remove_injected_views(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function vmf.keybind_toggle_view(view_name, can_be_opened, is_keybind_pressed)
|
function vmf.keybind_toggle_view(mod, view_name, can_be_opened, is_keybind_pressed)
|
||||||
--@TODO: check if there's the custom view at all. If not, show error.
|
|
||||||
|
|
||||||
if _ingame_ui then
|
if _ingame_ui then
|
||||||
local mod = _views_data[view_name].mod
|
if not _views_data[view_name] or (_views_data[view_name].mod ~= mod) then
|
||||||
|
mod:error(ERRORS.REGULAR["view_not_registered"], view_name)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local keybind_transitions = _views_data[view_name].view_settings.keybind_transitions
|
local keybind_transitions = _views_data[view_name].view_settings.keybind_transitions
|
||||||
if _ingame_ui.current_view == view_name then
|
if _ingame_ui.current_view == view_name then
|
||||||
if keybind_transitions.close_view_transition then
|
if keybind_transitions.close_view_transition then
|
||||||
|
|
|
@ -4298,6 +4298,7 @@ local view_data = {
|
||||||
view_transitions = {
|
view_transitions = {
|
||||||
vmf_options_view = function (self)
|
vmf_options_view = function (self)
|
||||||
self.current_view = "vmf_options_view"
|
self.current_view = "vmf_options_view"
|
||||||
|
self.menu_active = true
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue