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