989 lines
34 KiB
Plaintext
989 lines
34 KiB
Plaintext
|
---==================================================================================================================---
|
||
|
--- ---
|
||
|
--- Original Author(s) : NLTP_ASHES ---
|
||
|
--- Edited : N/A ---
|
||
|
--- Date : 05/02/2024 ---
|
||
|
--- License : Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) ---
|
||
|
--- ---
|
||
|
--- Script used to define various common functions defined in the scripts of the Western Goods addon. ---
|
||
|
--- ---
|
||
|
---==================================================================================================================---
|
||
|
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
-- Constants, global variables and imported functions
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
|
||
|
local ini_loc = ini_file_ex([[localization.ltx]])
|
||
|
local ini_keys = ini_file_ex([[plugins\western_goods\misc\western_goods_key_localization.ltx]])
|
||
|
local ini_trade = ini_file([[items\trade\trade_generic.ltx]])
|
||
|
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
-- Debug Functions
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
|
||
|
--- Function used to print information into the console/log file only when Western Goods' debug mode is enabled.
|
||
|
--- @return nil
|
||
|
function dbg_printf(...)
|
||
|
if not western_goods_mcm.get_config("debug_mode") then return end
|
||
|
printf(...)
|
||
|
end
|
||
|
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
-- Table Functions
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
|
||
|
--- Function used to know if a certain value is contained in a table. This function is generic enough so that it'd work on any table.
|
||
|
--- @param tab table
|
||
|
--- @param val any
|
||
|
--- @return boolean
|
||
|
function table_contains(tab, val)
|
||
|
if not validate_params({tab,val,"table_contains"}) then return end
|
||
|
for _,value in pairs(tab) do
|
||
|
if val == value then
|
||
|
return true
|
||
|
end
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
--- Function used to get a random id (as an int) from a given table.
|
||
|
--- Returns a random number between 1 and the size of the table.
|
||
|
--- @param tab table
|
||
|
--- @return number
|
||
|
function table_random_id(tab)
|
||
|
if not validate_params({tab,"table_random_id"}) then return end
|
||
|
return math.random(1,#tab)
|
||
|
end
|
||
|
|
||
|
--- Function used to get a random key from a key-value table.
|
||
|
--- @param tab table
|
||
|
--- @return any
|
||
|
function table_random_key(tab)
|
||
|
if not validate_params({tab,"table_random_key"}) then return end
|
||
|
|
||
|
local n = {}
|
||
|
for k,v in pairs(tab) do
|
||
|
n[#n+1] = k
|
||
|
end
|
||
|
return #n > 0 and n[math.random(#n)] or nil
|
||
|
end
|
||
|
|
||
|
--- Function used to know if an element of a table has a next.
|
||
|
--- @param tab table
|
||
|
--- @param pos number
|
||
|
--- @return boolean
|
||
|
function has_next(tab, pos)
|
||
|
if not validate_params({tab,pos,"has_next"}) then return end
|
||
|
return next(tab,pos) ~= nil
|
||
|
end
|
||
|
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
-- Data Functions
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
|
||
|
--- Function used to get a given variable as a number. Returns nil if the variable cannot be casted.
|
||
|
--- @param var any
|
||
|
--- @return number
|
||
|
function as_number(var)
|
||
|
if not validate_params({var,"as_number"}) then return end
|
||
|
|
||
|
if type(var) == "string" then
|
||
|
return tonumber(var)
|
||
|
end
|
||
|
if type(var) == "number" then
|
||
|
return var
|
||
|
end
|
||
|
|
||
|
printf("![WG] ERROR | Utils | Cannot cast variable to number")
|
||
|
|
||
|
return var
|
||
|
end
|
||
|
|
||
|
--- Function used to get a given variable as a string. Returns nil if the variable cannot be casted.
|
||
|
--- The parameter length can be used to specify the length of the return value, when working with numbers.
|
||
|
--- The function will add 0s before the number to match the desired length.
|
||
|
--- @param var any
|
||
|
--- @param length number
|
||
|
--- @return string
|
||
|
function as_string(var, length)
|
||
|
if not validate_params({var,"as_string"}) then return end
|
||
|
|
||
|
if type(var) == "number" then
|
||
|
local ret = tostring(var)
|
||
|
if length then
|
||
|
while #ret < length do
|
||
|
ret = "0" .. ret
|
||
|
end
|
||
|
end
|
||
|
return ret
|
||
|
end
|
||
|
if type(var) == "boolean" then
|
||
|
return tostring(var)
|
||
|
end
|
||
|
if type(var) == "string" then
|
||
|
return var
|
||
|
end
|
||
|
|
||
|
printf("![WG] ERROR | Utils | Cannot cast variable to string")
|
||
|
|
||
|
return var
|
||
|
end
|
||
|
|
||
|
--- Function used to get a given variable as a boolean. Returns nil if the variable cannot be casted.
|
||
|
--- @param var any
|
||
|
--- @return boolean
|
||
|
function as_boolean(var)
|
||
|
if not validate_params({var,"as_boolean"}) then return end
|
||
|
|
||
|
if type(var) == "string" then
|
||
|
if var == "true" then
|
||
|
return true
|
||
|
elseif var == "false" then
|
||
|
return false
|
||
|
end
|
||
|
end
|
||
|
if type(var) == "boolean" then
|
||
|
return var
|
||
|
end
|
||
|
|
||
|
printf("![WG] ERROR | Utils | Cannot cast variable to boolean")
|
||
|
|
||
|
return var
|
||
|
end
|
||
|
|
||
|
--- Function used to clear the code a bit.
|
||
|
--- @param num number
|
||
|
--- @return number
|
||
|
function round_number(num)
|
||
|
if not validate_params({num,"round_number"}) then return end
|
||
|
|
||
|
return tostring(math.floor(num+0.5))
|
||
|
end
|
||
|
|
||
|
--- Function used to know if there exists a translation for a given game string.
|
||
|
--- @param str string
|
||
|
--- @return boolean
|
||
|
function has_translation(str)
|
||
|
if not validate_params({str,"has_translation"}) then return end
|
||
|
|
||
|
return game.translate_string(str) ~= str
|
||
|
end
|
||
|
|
||
|
--- Function used to know if there exists a translation for a given game string.
|
||
|
--- @param str string
|
||
|
--- @return boolean
|
||
|
function get_translation(str, ...)
|
||
|
if not validate_params({str,"get_translation"}) then return end
|
||
|
|
||
|
if not has_translation(str) then
|
||
|
return str.."[Missing translation]"
|
||
|
end
|
||
|
|
||
|
local string = game.translate_string(str)
|
||
|
|
||
|
if (select('#',...) >= 1) then
|
||
|
local i = 0
|
||
|
local p = {...}
|
||
|
local function sr()
|
||
|
i = i + 1
|
||
|
if (type(p[i]) == "userdata") then
|
||
|
return "userdata"
|
||
|
end
|
||
|
return tostring(p[i])
|
||
|
end
|
||
|
string = string.gsub(string,"%%s",sr)
|
||
|
end
|
||
|
|
||
|
return string
|
||
|
end
|
||
|
|
||
|
--- Function used to print the a textual version of a DIK key.
|
||
|
--- @author RavenAscendant
|
||
|
--- @param dik DIK_keys
|
||
|
--- @return string
|
||
|
function get_key_translation(dik)
|
||
|
if not validate_params({dik,"get_key_translation"}) then return end
|
||
|
|
||
|
local loc = ini_loc:r_value("string_table","language")
|
||
|
loc = ini_keys:section_exist(loc) and loc or "eng"
|
||
|
return ini_keys:r_value(loc, dik,0,"<!!!>")
|
||
|
end
|
||
|
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
-- Info Portions Functions
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
|
||
|
--- Function used to give an info portion to a game object.
|
||
|
--- If no game object is passed, defaults back to db.actor.
|
||
|
--- @param info_name string
|
||
|
--- @param who game_object
|
||
|
--- @return nil
|
||
|
function give_info(info_name, who)
|
||
|
if not validate_params({info_name,"give_info"}) then return end
|
||
|
|
||
|
-- Default values
|
||
|
who = who or db.actor
|
||
|
|
||
|
if not has_info(info_name,who) then
|
||
|
who:give_info_portion(info_name)
|
||
|
dbg_printf("[WG] Utils | Info portion '%s' added to '%s'", info_name, who:name())
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--- Function used to check if a game object has a given info portion.
|
||
|
--- If no game object is passed, defaults back to db.actor.
|
||
|
--- @param info_name string
|
||
|
--- @param who game_object
|
||
|
--- @return nil
|
||
|
function has_info(info_name, who)
|
||
|
if not validate_params({info_name,"has_info"}) then return end
|
||
|
|
||
|
-- Default values
|
||
|
who = who or db.actor
|
||
|
|
||
|
return alife():has_info(who:id(), info_name)
|
||
|
end
|
||
|
|
||
|
--- Function used to remove an info portion from a game object.
|
||
|
--- If no game object is passed, defaults back to db.actor.
|
||
|
--- @param info_name string
|
||
|
--- @param who game_object
|
||
|
--- @return nil
|
||
|
function rem_info(info_name, who)
|
||
|
if not validate_params({info_name,"rem_info"}) then return end
|
||
|
|
||
|
-- Default values
|
||
|
who = who or db.actor
|
||
|
|
||
|
if has_info(info_name,who) then
|
||
|
who:disable_info_portion(info_name)
|
||
|
dbg_printf("[WG] Utils | Info portion '%s' disabled from '%s'", info_name, who:name())
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
-- Distance Functions
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
|
||
|
--- Function used to get the distance between two points in on the X and Z axis.
|
||
|
--- @param position_1 vector
|
||
|
--- @param position_2 vector
|
||
|
--- @return number
|
||
|
function get_distance(position_1, position_2)
|
||
|
if not validate_params({position_1,position_2,"get_distance"}) then return end
|
||
|
|
||
|
return position_1:distance_to(position_2) or 100000
|
||
|
end
|
||
|
|
||
|
--- Function used to get the distance between two points in on the X and Z axis.
|
||
|
--- @param position_1 vector
|
||
|
--- @param position_2 vector
|
||
|
--- @return number
|
||
|
function get_distance_sqr(position_1, position_2)
|
||
|
if not validate_params({position_1,position_2,"get_distance_sqr"}) then return end
|
||
|
|
||
|
return position_1:distance_to_sqr(position_2) or 10000000000
|
||
|
end
|
||
|
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
-- Objects Getter Functions
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
|
||
|
--- Function used to iterate through each existing server object.
|
||
|
--- The function passed in arguments will be called once per game object, with a guarantee that the server object exists and isn't nil.
|
||
|
--- Extra arguments passed to 'server_objects_iter' function are passed on to 'func' function.
|
||
|
--- If 'func' function returns true, then the iteration stops.
|
||
|
--- @param func function
|
||
|
--- @param ... table
|
||
|
--- @return nil
|
||
|
function server_objects_iter(func, ...)
|
||
|
if not validate_params({func,"server_objects_iter"}) then return end
|
||
|
|
||
|
dbg_printf("[WG] Utils | Iterating through all possible IDs...")
|
||
|
|
||
|
local args = {...}
|
||
|
|
||
|
if alife().iterate_object ~= nil then
|
||
|
alife():iterate_object(function(se_obj)
|
||
|
return func(se_obj, unpack(args))
|
||
|
end)
|
||
|
else
|
||
|
for id=0, 65534 do
|
||
|
local se_obj = alife_object(id)
|
||
|
if se_obj and func(se_obj, unpack(args)) then
|
||
|
return
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--- Function used to retrieve a server object by its story ID.
|
||
|
--- @param story_id string
|
||
|
--- @return cse_alife_object|nil
|
||
|
function server_object_by_sid(story_id)
|
||
|
if not validate_params({story_id,"server_object_by_sid"}) then return end
|
||
|
|
||
|
return get_story_se_object(story_id)
|
||
|
end
|
||
|
|
||
|
--- Function used to retrieve a level object by its story ID.
|
||
|
--- @param story_id string
|
||
|
--- @return game_object|nil
|
||
|
function level_object_by_sid(story_id)
|
||
|
if not validate_params({story_id,"level_object_by_sid"}) then return end
|
||
|
|
||
|
local se_obj = server_object_by_sid(story_id)
|
||
|
return se_obj and level_object_by_id(se_obj.id)
|
||
|
end
|
||
|
|
||
|
--- Function used to get game object by its ID if it exists on the current level.
|
||
|
--- @param id number
|
||
|
--- @return game_object
|
||
|
function level_object_by_id(id)
|
||
|
if not validate_params({id,"level_object_by_id"}) then return end
|
||
|
|
||
|
return db.storage[id] and db.storage[id].object or level.object_by_id(id)
|
||
|
end
|
||
|
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
-- Objects Functions
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
|
||
|
--- Function used to get the character ID of an NPC.
|
||
|
--- @param se_obj cse_alife_object|game_object
|
||
|
--- @return string
|
||
|
function get_character_id(se_obj)
|
||
|
if not validate_params({se_obj,"get_character_id"}) then return end
|
||
|
|
||
|
-- Get cse_alife_object if game_object is passed
|
||
|
if type(se_obj.id) == "function" then
|
||
|
se_obj = alife_object(se_obj:id())
|
||
|
end
|
||
|
|
||
|
if not IsStalker(se_obj) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local net_data = utils_stpk.get_stalker_data(se_obj)
|
||
|
if not net_data then
|
||
|
printf("![WG] ERROR | Utils | Failed to get Stalker Net Data for %s", se_obj:section_name())
|
||
|
return
|
||
|
end
|
||
|
|
||
|
dbg_printf("[WG] Utils | NPC %s(%s) has character ID '%s'", se_obj:section_name(), se_obj.id, net_data.specific_character)
|
||
|
return net_data.specific_character
|
||
|
end
|
||
|
|
||
|
--- Function used to get the list of dialogs of a given character.
|
||
|
--- @param character_id string
|
||
|
--- @return table
|
||
|
function get_dialog_list(character_id)
|
||
|
if not validate_params({character_id,"get_dialog_list"}) then return end
|
||
|
|
||
|
dbg_printf("[WG] Utils | Getting dialog list for '%s'...", character_id)
|
||
|
|
||
|
local profiler = profile_timer()
|
||
|
|
||
|
profiler:start()
|
||
|
|
||
|
local dialog_list = {}
|
||
|
|
||
|
local file_list = str_explode(ini_sys:r_string("profiles", "specific_characters_files"),",")
|
||
|
|
||
|
for _,file_name in pairs(file_list) do
|
||
|
local xml = CScriptXmlInit()
|
||
|
xml:ParseDirFile([[gameplay]],file_name..".xml")
|
||
|
|
||
|
local char_count = xml:GetNodesNum("", 0, "specific_character")
|
||
|
for i=0, char_count-1 do
|
||
|
|
||
|
local char_id = xml:ReadAttribute("specific_character", i, "id")
|
||
|
if char_id == character_id then
|
||
|
|
||
|
xml:NavigateToNode("specific_character", i)
|
||
|
|
||
|
local diag_count = xml:GetNodesNum("specific_character", i, "actor_dialog")
|
||
|
for y=0, diag_count-1 do
|
||
|
table.insert(dialog_list, xml:ReadValue("actor_dialog", y))
|
||
|
end
|
||
|
|
||
|
profiler:stop()
|
||
|
|
||
|
dbg_printf("[WG] Utils | Dialog list of '%s' retrieved in %sms :\n%s", character_id, profiler:time()/1000, utils_data.print_table(dialog_list, false, true))
|
||
|
|
||
|
return dialog_list
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
profiler:stop()
|
||
|
|
||
|
printf("![WG] ERROR | Utils | Impossible to get dialog list, character '%s' not found !", character_id)
|
||
|
end
|
||
|
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
-- File Functions
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
|
||
|
--- Function used to list all the files in a given folder (excluding the sub-folders).
|
||
|
--- @param path string
|
||
|
--- @return table
|
||
|
function list_files(path)
|
||
|
local file_list_fs = getFS():file_list_open("$game_config$", path, bit_or(FS.FS_ListFiles, FS.FS_RootOnly))
|
||
|
local file_list = {}
|
||
|
|
||
|
local count = file_list_fs and file_list_fs:Size() or 0
|
||
|
if count <= 0 then
|
||
|
return file_list
|
||
|
end
|
||
|
|
||
|
for i = 1, count do
|
||
|
local file_name = file_list_fs:GetAt(i - 1)
|
||
|
if file_name then
|
||
|
file_list[#file_list + 1] = file_name
|
||
|
end
|
||
|
end
|
||
|
return file_list
|
||
|
end
|
||
|
|
||
|
--- Function used to list all the texture descriptions matching a given pattern.
|
||
|
--- Texture descriptions have to be in gamedata/configs/ui/textures_descr/.
|
||
|
--- This function takes on average 300ms, prioritize using it in on_game_load to avoid stutters.
|
||
|
--- @param pattern string
|
||
|
--- @return table
|
||
|
function collect_texture_descriptions(pattern)
|
||
|
this.dbg_printf("[WG] Utils | Getting matching texture descriptions for '%s'...", pattern)
|
||
|
|
||
|
local profiler = profile_timer()
|
||
|
profiler:start()
|
||
|
|
||
|
local file_list = this.list_files("ui\\textures_descr\\")
|
||
|
local matches = {}
|
||
|
|
||
|
for _,file_name in pairs(file_list) do
|
||
|
local xml = CScriptXmlInit()
|
||
|
xml:ParseDirFile("ui\\textures_descr", file_name)
|
||
|
xml:NavigateToNode("w", 0)
|
||
|
|
||
|
local file_count = xml:GetNodesNum("", 0, "file")
|
||
|
for i=0, file_count-1 do
|
||
|
|
||
|
xml:NavigateToNode("file", i)
|
||
|
|
||
|
local texture_count = xml:GetNodesNum("file", i, "texture")
|
||
|
for y=0, texture_count-1 do
|
||
|
|
||
|
local texture_descr_id = xml:ReadAttribute("texture", y, "id")
|
||
|
if string.find(texture_descr_id, pattern) then
|
||
|
|
||
|
table.insert(matches, texture_descr_id)
|
||
|
end
|
||
|
end
|
||
|
xml:NavigateToRoot()
|
||
|
xml:NavigateToNode("w", 0)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
profiler:stop()
|
||
|
this.dbg_printf("[WG] Utils | Found %s matching texture descriptions for %s in %sms :\n%s", size_table(matches), pattern, profiler:time()/1000, utils_data.print_table(matches, false, true))
|
||
|
|
||
|
return matches
|
||
|
end
|
||
|
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
-- Objects Functions
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
|
||
|
--- Function used to get the current renderer used by the game.
|
||
|
--- @return "dx8"|"dx9"|"dx10"|"dx11"|
|
||
|
function get_renderer()
|
||
|
local renderers = {
|
||
|
["renderer_r4"] = "dx11",
|
||
|
["renderer_r3"] = "dx10",
|
||
|
["renderer_r2a"] = "dx9",
|
||
|
["renderer_r2.5"] = "dx9",
|
||
|
["renderer_r1"] = "dx8",
|
||
|
}
|
||
|
|
||
|
return renderers[get_console_cmd(0,"renderer")]
|
||
|
end
|
||
|
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
-- Squads Functions
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
|
||
|
--- Function used to spawn a story squad.
|
||
|
--- @param sec string
|
||
|
--- @param pos vector
|
||
|
--- @param smart cse_alife_smart_zone
|
||
|
--- @return cse_alife_online_offline_group
|
||
|
function spawn_story_squad(sec, pos, smart)
|
||
|
if not validate_params({sec,smart,"spawn_story_squad"}) then return end
|
||
|
|
||
|
-- Make sure the squad isn't already spawned in
|
||
|
local se_squad = this.server_object_by_sid(sec)
|
||
|
if se_squad then
|
||
|
printf("![WG] ERROR | Utils | Squad already exists - '%s'", se_squad:section_name())
|
||
|
return se_squad
|
||
|
end
|
||
|
|
||
|
-- Default value
|
||
|
pos = pos or smart.position
|
||
|
|
||
|
-- Spawn squad on the appropriate smart terrain
|
||
|
se_squad = spawn_squad(sec,pos,smart.m_level_vertex_id,smart.m_game_vertex_id, smart)
|
||
|
if se_squad then
|
||
|
-- Force the squad to stay there
|
||
|
se_squad.scripted_target = smart:name()
|
||
|
|
||
|
dbg_printf("[WG] Utils | Story squad %s(%s) created at %s", sec, se_squad.id, smart:name())
|
||
|
return se_squad
|
||
|
else
|
||
|
printf("![WG] ERROR | Utils | Failed to create squad (%s)",sec)
|
||
|
return
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--- Function used to spawn a simulation squad, at a given point in space.
|
||
|
--- @param sec string
|
||
|
--- @param pos vector
|
||
|
--- @param lvid number
|
||
|
--- @param gvid number
|
||
|
--- @param smart cse_alife_smart_zone
|
||
|
--- @return cse_alife_online_offline_group
|
||
|
function spawn_squad(sec, pos, lvid, gvid, smart)
|
||
|
if not validate_params({sec,pos,lvid,gvid,"spawn_squad"}) then return end
|
||
|
|
||
|
local squad = alife_create(sec, pos, lvid, gvid)
|
||
|
if squad then
|
||
|
squad:create_npc(smart, pos, lvid, gvid)
|
||
|
|
||
|
if smart then
|
||
|
SIMBOARD:assign_squad_to_smart(squad, smart.id)
|
||
|
else
|
||
|
SIMBOARD:assign_squad_to_smart(squad)
|
||
|
end
|
||
|
|
||
|
for k in squad:squad_members() do
|
||
|
local se_obj = k.object or k.id and alife_object(k.id)
|
||
|
if (se_obj) then
|
||
|
SIMBOARD:setup_squad_and_group(se_obj)
|
||
|
SendScriptCallback("squad_on_npc_creation",squad,se_obj,smart)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
dbg_printf("[WG] Utils | Squad with id %s created", squad.id)
|
||
|
|
||
|
return squad
|
||
|
else
|
||
|
printf("![WG] ERROR | Utils | Failed to create squad (%s)",sec)
|
||
|
return
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--- Function used to spawn a simulation squad, at a given smart terrain.
|
||
|
--- @param sec string
|
||
|
--- @param smart_name string
|
||
|
--- @return cse_alife_online_offline_group
|
||
|
function spawn_squad_smart(sec, smart_name)
|
||
|
if not validate_params({sec, smart_name,"spawn_squad_smart"}) then return end
|
||
|
|
||
|
local squad = SIMBOARD:create_squad(SIMBOARD.smarts_by_names[smart_name],sec)
|
||
|
if squad then
|
||
|
dbg_printf("[WG] Utils | Squad with id %s created", squad.id)
|
||
|
return squad
|
||
|
else
|
||
|
printf("![WG] ERROR | Utils | Failed to create squad (%s)",sec)
|
||
|
return
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--- Function used to spawn a dead squad, at a given smart terrain.
|
||
|
--- @param sec string
|
||
|
--- @param smart_name string
|
||
|
--- @return number
|
||
|
function spawn_dead_squad(sec, smart_name)
|
||
|
if not validate_params({sec,smart_name,"spawn_dead_squad"}) then return end
|
||
|
|
||
|
local squad = spawn_squad_smart(sec, smart_name)
|
||
|
if squad then
|
||
|
local member_id = nil
|
||
|
|
||
|
for npc in squad:squad_members() do
|
||
|
CreateTimeEvent("western_goods_delay_kill_squad",npc.id,1,surge_manager.make_dead,npc.id)
|
||
|
member_id = npc.id
|
||
|
end
|
||
|
|
||
|
dbg_printf("[WG] Utils | Squad with id %s killed", squad.id)
|
||
|
return member_id
|
||
|
else
|
||
|
printf("![WG] ERROR | Utils | Failed to create dead squad (%s)",sec)
|
||
|
return
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--- Function used to get the ID from a random member of a squad.
|
||
|
--- @param squad cse_alife_online_offline_group
|
||
|
--- @return number
|
||
|
function get_member_id_from_squad(squad)
|
||
|
if not validate_params({squad,"get_member_id_from_squad"}) then return end
|
||
|
|
||
|
for member in squad:squad_members() do
|
||
|
return member.id
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--- Function used to get a "random" member of a squad.
|
||
|
--- @param squad cse_alife_online_offline_group
|
||
|
--- @return cse_alife_object
|
||
|
function get_member_from_squad(squad)
|
||
|
if not validate_params({squad,"get_member_from_squad"}) then return end
|
||
|
|
||
|
for member in squad:squad_members() do
|
||
|
return alife_object(member.id)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--- Function used to find an NPC by its section name in the player squad.
|
||
|
--- Warning : if multiple NPCs with that section exist, it'll only return the first one it finds.
|
||
|
--- @param sec string
|
||
|
--- @return cse_alife_object
|
||
|
function get_member_from_actor_squad(sec)
|
||
|
if not validate_params({sec,"get_member_from_actor_squad"}) then return end
|
||
|
|
||
|
local actor_squad = axr_companions.list_actor_squad_by_id()
|
||
|
for _,id in pairs(actor_squad) do
|
||
|
local se_npc = alife_object(id)
|
||
|
if se_npc and se_npc:section_name() == sec then
|
||
|
return se_npc
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--- Function used to check if a given object id is a member of a given squad.
|
||
|
--- @param squad cse_alife_online_offline_group
|
||
|
--- @param id number
|
||
|
--- @return boolean
|
||
|
function is_id_from_squad(squad, id)
|
||
|
if not validate_params({squad,id,"is_id_from_squad"}) then return end
|
||
|
|
||
|
for member in squad:squad_members() do
|
||
|
if member.id == id then
|
||
|
return true
|
||
|
end
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
--- Function used to iterate through and execute a function on each member of a squad.
|
||
|
--- @param squad cse_alife_online_offline_group
|
||
|
--- @param func function
|
||
|
--- @return nil
|
||
|
function squad_iterate_members(squad, func, ...)
|
||
|
if not validate_params({squad,func,"squad_iterate_members"}) then return end
|
||
|
|
||
|
local args = {...}
|
||
|
|
||
|
for member in squad:squad_members() do
|
||
|
local se_obj = alife_object(member.id)
|
||
|
if (se_obj) then
|
||
|
func(se_obj, unpack(args))
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--- Function used to get the squad of an object.
|
||
|
--- Unlike the global 'get_object_squad', this function checks if the passed object is the actor.
|
||
|
--- Doing so will kill the script engine thread, and this function ensures this never happens.
|
||
|
--- @param obj game_object
|
||
|
--- @return cse_alife_online_offline_group
|
||
|
function get_squad_of(obj)
|
||
|
if (not validate_params({obj,"get_squad_of"})) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
-- Calling get_object_squad() or actor object grounds the game !
|
||
|
if (obj:id() == AC_ID) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
-- Maybe don't call 'get_squad_of' on a door or something
|
||
|
if not (IsStalker(obj) or IsMonster(obj)) then
|
||
|
printf("~[WG] WARNING | Utils | Aborting, trying to get the squad of an object that cannot be in a squad (obj : '%s')", obj and obj:name())
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local squad = get_object_squad(obj)
|
||
|
|
||
|
if not squad then
|
||
|
printf("~[WG] WARNING | Utils | Object '%s' has no squad, this will probably crash the game eventually since stalkers/mutants MUST be in squads !", obj and obj:name())
|
||
|
return
|
||
|
end
|
||
|
|
||
|
return squad
|
||
|
end
|
||
|
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
-- Bone Functions
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
|
||
|
--- Function used to get the position of a bone by its name.
|
||
|
--- This function should be used in case the bone is from a world model.
|
||
|
--- @author Thial
|
||
|
--- @param obj game_object
|
||
|
--- @param bone string
|
||
|
--- @return vector
|
||
|
function bone_position(obj, bone)
|
||
|
if not validate_params({obj,bone,"bone_position"}) then return end
|
||
|
|
||
|
return obj:bone_position(obj:get_bone_id(bone, false), false)
|
||
|
end
|
||
|
|
||
|
--- Function used to get the position of a bone by its name.
|
||
|
--- This function should be used in case the bone is from a hud model.
|
||
|
--- @param obj game_object
|
||
|
--- @param hud_bone string
|
||
|
--- @param bone string
|
||
|
--- @return vector
|
||
|
function bone_hud_position(obj, hud_bone, bone)
|
||
|
if not validate_params({obj,hud_bone,bone,"bone_hud_position"}) then return end
|
||
|
|
||
|
-- Cant find hud bone in TPS, use default bone
|
||
|
if level.get_active_cam() ~= 0 then
|
||
|
printf("~[WG] WARNING | Utils | Trying to get HUD bone '%s' position in third person ! Fallback to '%s'", hud_bone, bone)
|
||
|
return this.bone_position(obj, bone)
|
||
|
end
|
||
|
|
||
|
return obj:bone_position(obj:get_bone_id(hud_bone, true), true)
|
||
|
end
|
||
|
|
||
|
--- Function used to set the visibility a bone off a game object.
|
||
|
--- This function can be used as a getter or as a setter.
|
||
|
--- This function should be used in case the bone is from a world model.
|
||
|
--- @param obj game_object
|
||
|
--- @param bone string
|
||
|
--- @param visibility boolean
|
||
|
--- @return nil
|
||
|
function bone_visibility(obj, bone, visibility)
|
||
|
if not validate_params({obj,bone,"bone_visibility"}) then return end
|
||
|
|
||
|
if visibility == nil then
|
||
|
return obj:bone_visible(bone, false)
|
||
|
end
|
||
|
|
||
|
obj:set_bone_visible(bone, visibility, true, false)
|
||
|
end
|
||
|
|
||
|
--- Function used to set the visibility a bone off a game object.
|
||
|
--- This function can be used as a getter or as a setter.
|
||
|
--- This function should be used in case the bone is from a hud model.
|
||
|
--- @param obj game_object
|
||
|
--- @param hud_bone string
|
||
|
--- @param bone string
|
||
|
--- @param visibility boolean
|
||
|
--- @return nil
|
||
|
function bone_hud_visibility(obj, hud_bone, bone, visibility)
|
||
|
if not validate_params({obj,hud_bone,bone,"bone_hud_visibility"}) then return end
|
||
|
|
||
|
-- Cant find hud bone in TPS, use default bone
|
||
|
if level.get_active_cam() ~= 0 then
|
||
|
printf("~[WG] WARNING | Utils | Trying to set HUD bone '%s' visibility in third person ! Fallback to '%s'", hud_bone, bone)
|
||
|
return this.bone_visibility(obj, bone, visibility)
|
||
|
end
|
||
|
|
||
|
if visibility == nil then
|
||
|
return obj:bone_visible(hud_bone, true)
|
||
|
end
|
||
|
|
||
|
obj:set_bone_visible(hud_bone, visibility, true, true)
|
||
|
end
|
||
|
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
-- Validation Functions
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
|
||
|
--- Function used to check if all necessary parameters were correctly passed to a function.
|
||
|
--- Last element of the table has to be the name of the function calling validate_params.
|
||
|
--- Use like this : 'if not validate_params({param_1,param_1,"my_function"}) then return end'
|
||
|
--- @param params table
|
||
|
--- @return boolean
|
||
|
function validate_params(params)
|
||
|
local index = 1
|
||
|
for i,_ in pairs(params) do
|
||
|
if i ~= index then
|
||
|
printf("![WG] ERROR | Utils | Missing argument no %s for '%s()'!",index,params[#params])
|
||
|
return false
|
||
|
end
|
||
|
index = index + 1
|
||
|
end
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
--- Function used to check if an object is a type of PDA device.
|
||
|
--- @param obj game_object
|
||
|
--- @return boolean
|
||
|
function validate_is_pda(obj)
|
||
|
if not validate_params({obj,"validate_is_pda"}) then return end
|
||
|
|
||
|
local pda_sections = {
|
||
|
["device_pda_1"] = true,
|
||
|
["device_pda_2"] = true,
|
||
|
["device_pda_3"] = true,
|
||
|
["device_pda_milspec"] = true
|
||
|
}
|
||
|
|
||
|
return pda_sections[obj:section()]
|
||
|
end
|
||
|
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
-- Other Utils Functions
|
||
|
-- ---------------------------------------------------------------------------------------------------------------------
|
||
|
|
||
|
--- Function used to iterate through the inventory of a game object.
|
||
|
--- The function passed in arguments will be called once per game object inside of the inventory.
|
||
|
--- Extra arguments passed to 'inventory_iter' function are passed on to 'func' function.
|
||
|
--- If 'func' function returns true, then the iteration stops.
|
||
|
--- @param who game_object
|
||
|
--- @param func function
|
||
|
--- @param ... table
|
||
|
--- @return nil
|
||
|
function inventory_iter(who ,func, ...)
|
||
|
if not validate_params({who,func,"inventory_iter"}) then return end
|
||
|
|
||
|
dbg_printf("[WG] Utils | Iterating inventory of '%s'",who:section())
|
||
|
|
||
|
local args = {...}
|
||
|
who:iterate_inventory(function(owner, obj)
|
||
|
return func(owner, obj, unpack(args))
|
||
|
end,who)
|
||
|
end
|
||
|
|
||
|
--- Function used to find an NPC by its section name in the player squad.
|
||
|
--- @author mr_demonized
|
||
|
--- @param func function
|
||
|
--- @param ... table
|
||
|
--- @return nil
|
||
|
function next_tick(func, ...)
|
||
|
if not validate_params({func,"next_tick"}) then return end
|
||
|
|
||
|
local args = {...}
|
||
|
AddUniqueCall(function()
|
||
|
func(unpack(args))
|
||
|
return true
|
||
|
end)
|
||
|
end
|
||
|
|
||
|
--- Function used to sperate an artefact from its container.
|
||
|
--- This function is a copy of itms_manager.use_arty_container(), where the anim is optional.
|
||
|
--- @author Alundaio,Tronex
|
||
|
--- @param obj game_object
|
||
|
--- @param play_anim boolean
|
||
|
--- @return nil
|
||
|
function open_container(obj, play_anim)
|
||
|
if not validate_params({obj,"open_container"}) then return end
|
||
|
|
||
|
local break_con
|
||
|
local break_arty
|
||
|
local sec = obj:section()
|
||
|
|
||
|
if (string.find(sec, "(lead.-_box)",3)) then
|
||
|
break_con = "lead_box"
|
||
|
break_arty = sec:gsub("_lead_box", "")
|
||
|
elseif (string.find(sec, "(af.-_iam)",3)) then
|
||
|
break_con = "af_iam"
|
||
|
break_arty = sec:gsub("_af_iam", "")
|
||
|
elseif (string.find(sec, "(af.-_aac)",3)) then
|
||
|
break_con = "af_aac"
|
||
|
break_arty = sec:gsub("_af_aac", "")
|
||
|
elseif (string.find(sec, "(af.-_aam)",3)) then
|
||
|
break_con = "af_aam"
|
||
|
break_arty = sec:gsub("_af_aam", "")
|
||
|
end
|
||
|
|
||
|
if break_con and break_arty and ini_sys:section_exist(break_con) and ini_sys:section_exist(break_arty) then
|
||
|
local cond = obj:condition()
|
||
|
|
||
|
-- Hack to prevent Artefacts Containers exploit
|
||
|
-- (gaining rank by receiving artefacts)
|
||
|
_G.ARTY_FROM_CONT = true
|
||
|
|
||
|
if play_anim == nil or play_anim == true then
|
||
|
actor_effects.play_item_fx(break_con .. "_dummy")
|
||
|
end
|
||
|
|
||
|
alife_create_item(break_con, db.actor)
|
||
|
alife_create_item(break_arty, db.actor, { cond = cond } )
|
||
|
alife_release(obj)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--- Function used to check if there are NPCs currently in a fight with the player.
|
||
|
--- @return boolean
|
||
|
function is_player_fighting()
|
||
|
return not is_empty(xr_combat_ignore.fighting_with_actor_npcs)
|
||
|
end
|
||
|
|
||
|
--- Function used to get the real community of the NPC by checking spawn id.
|
||
|
--- @author HarukaSai
|
||
|
--- @param npc game_object
|
||
|
--- @param default string
|
||
|
--- @return string
|
||
|
function get_real_community(npc, default)
|
||
|
if not validate_params({npc,"get_real_community"}) then return end
|
||
|
|
||
|
local furniture = {
|
||
|
["esc_m_trader"] = true,
|
||
|
["red_m_lesnik"] = true
|
||
|
}
|
||
|
|
||
|
local blacklisted_comms = {
|
||
|
["trader"] = true,
|
||
|
["monster"] = true
|
||
|
}
|
||
|
|
||
|
if furniture[npc:name()] then
|
||
|
return "stalker"
|
||
|
end
|
||
|
local community = character_community(npc)
|
||
|
if not blacklisted_comms[community] then
|
||
|
return community
|
||
|
end
|
||
|
local squad = this.get_squad_of(npc)
|
||
|
local squad_community = squad and squad:get_squad_community()
|
||
|
if not squad_community or not blacklisted_comms[squad_community] then
|
||
|
return squad_community
|
||
|
else
|
||
|
return default
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--- Function used to get the price of a item (or multiple) according to progression difficulty.
|
||
|
--- The price is the estimated price when the player buys an item from a trader.
|
||
|
--- Original author is RavenAscendant. Function was edited by NLTP_ASHES.
|
||
|
--- @author RavenAscendant
|
||
|
--- @param section string
|
||
|
--- @param count number
|
||
|
--- @param default number
|
||
|
--- @return number
|
||
|
function get_section_price(section, count, default)
|
||
|
if not validate_params({section,"get_section_price"}) then return end
|
||
|
|
||
|
-- Default values
|
||
|
count = count or 1
|
||
|
default = default or 0
|
||
|
|
||
|
local condlist = ini_trade and ini_trade:r_string_to_condlist("trader","discounts")
|
||
|
local sect = condlist and xr_logic.pick_section_from_condlist(db.actor, nil, condlist)
|
||
|
|
||
|
if sect == nil or sect == "" then
|
||
|
return default
|
||
|
end
|
||
|
|
||
|
local cost = SYS_GetParam(2, section, "cost", 0)
|
||
|
local factor = ini_trade:r_float_ex(sect,"sell") or 1
|
||
|
local eco_factor = game_difficulties.get_eco_factor("sell") or 1
|
||
|
local discount = factor * eco_factor
|
||
|
|
||
|
return (cost * discount) * count
|
||
|
end
|