-- ============================================================ -- -- Preconditions for Tasks (xr_conditions.script) -- DoctorX Call of The Zone 1.2 -- -- Modified by: DoctorX -- Last revised: August 18, 2020 -- -- ============================================================ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- Global Vars -- -- Created by DoctorX -- for DoctorX Questlines 2.0 -- November 09, 2019 -- -- ------------------------------------------------------------------------------------------------ -- List of blacklisted smart terrains: local drx_ql_xrc_smart_blacklist = {} -- List of smart terrains to exclude as task target areas: local drx_ql_xrc_smart_blacklist = {} -- List of smart terrains to exclude as task target areas: local drx_ql_xrc_miracle_machine_smarts = {} -- List of smart terrains to exclude as task target areas: local drx_ql_xrc_brain_scorcher_smart_prefixes = {} -- List of NPC name sub-strings to include as assassination task targets: local drx_ql_xrc_assassination_target_names = {} -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- ________________________________________________________________________________________________ function moon_cotz_actor_reached_rank( actor, npc, p ) if ( ( not p ) or ( #p < 1 ) or ( not p[1] ) or ( p[1] == "" ) ) then return false end local current_actor_rank = ranks.get_obj_rank_name( actor ) if ( p[1] ~= current_actor_rank ) then local low, high = ranks.get_rank_interval( p[1] ) if ( actor:character_rank() < high ) then return false end end return true end -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_is_npc function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Checks if the current speaker is the specified npc -- -- Usage: -- drx_ql_is_npc( p[1], {p[2], ...} ) -- -- Parameters: -- p[1], {p[2], ...} (type: string, npc names) -- - NPC section names to check -- -- Persistent storage: -- drx_ql_current_npc_speaker_id (type: npc id) -- - Id of the current npc the actor is speaking to -- -- Return value (type: bool): -- Returns true if the speaker is a specified npc, false otherwise -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified January 27, 2019 -- ------------------------------------------------------------------------------------------------ -- Check if speaker is specified npc: function drx_ql_is_npc( actor, npc, p ) -- Check if input was specified: if ( (not p) or (#p < 1) or (not p[1]) or (p[1] == "") ) then return true end -- Verify db.actor is available: if ( not db.actor ) then printf( "DRX QL Error: Unable to check if current speaker is specified npc, db.actor not available" ) return false end -- Get the id of the npc the actor is currently speaking with: local npc_id = utils.load_var( db.actor, "drx_ql_current_npc_speaker_id", nil ) if ( npc_id == nil ) then printf( "DRX QL Error: Unable to check if current speaker is specified npc, could not get id of current speaker" ) return false end local npc_obj = alife( ):object( npc_id ) if ( not npc_obj ) then printf( "DRX QL Error: Unable to check if current speaker is specified npc, could not current speaker object" ) return false end -- Get the npc name: local npc_name if ( npc_obj:section_name( ) == "m_trader" ) then npc_name = npc_obj:name( ) else npc_name = npc_obj:section_name( ) end -- Check current npc against specified list: for i = 1, ( #p ) do if ( p[i] == npc_name ) then return true end end -- Set return value: return false end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_is_enemy function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Checks if a faction is an enemy of the player's faction -- -- Usage: -- drx_ql_is_enemy( p[1] ) -- -- Parameters: -- p[1] (type: string, faction name) -- - Faction to check is enemy of player -- -- Return value (type: bool): -- Returns true if the faction is an enemy of the player faction, false otherwise -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified February 05, 2019 -- ------------------------------------------------------------------------------------------------ -- Check if faction is enemy of player: function drx_ql_is_enemy( actor, npc, p ) -- Check if faction is enemy of the player: local is_enemy = false if ( (p) and (#p > 0) and (p[1] ~= nil) ) then if ( relation_registry.community_relation( p[1], alife( ):actor( ):community( ) ) <= game_relations.ENEMIES ) then is_enemy = true end end -- Set return value: return is_enemy end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_is_hostage function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Checks if a npc is kidnapped -- -- Usage: -- drx_ql_is_hostage( ) -- -- Parameters: -- none -- -- Return value (type: bool): -- Returns true if the npc is kidnapped, false otherwise -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified May 16, 2019 -- ------------------------------------------------------------------------------------------------ -- Check if npc is kidnapped: function drx_ql_is_hostage( actor, npc, p ) -- Check if npc is hostage: local is_hostage = false if ( xrs_kill_wounded.hostage_list[npc:id( )] ~= nil ) then is_hostage = true end -- Set return value: return is_hostage end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_actor_faction function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Checks if the player is a member of the specified faction -- -- Usage: -- drx_ql_actor_faction( p[1] ) -- -- Parameters: -- p[1] (type: array, faction names) -- - List of faction names to check if the actor belongs to (optional, if omitted function will return true) -- -- Return value (type: bool): -- Returns true if the actor is a member of one of the factions in the list, false otherwise -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified October 13, 2017 -- ------------------------------------------------------------------------------------------------ -- Check if actor is member of faction: function drx_ql_actor_faction( actor, npc, p ) -- Validate input: if ( (not p) or (#p < 1) or (p[1] == nil) ) then return true end -- Get actor faction: local actor_faction = alife( ):actor( ):community( ) if ( (not actor_faction) or (actor_faction == "") ) then printf( "DRX QL Error: Unable to determine actor faction, actor does not belong to any community" ) return false end actor_faction = string.gsub( actor_faction, "actor_", "" ) -- Check actor faction against specified list of factions: for i = 1, ( #p ) do if ( actor_faction == p[i] ) then return true end end -- Set return value: return false end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_wg_fail function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Determines if a task should be failed due to choices made at WG -- -- Usage: -- drx_ql_wg_fail( p[1] ) -- -- Parameters: -- p[1] (type: string, task name) -- - Name of the task calling this function -- -- Return value (type: bool): -- Returns true if the task should be failed, false otherwise -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified February 17, 2019 -- ------------------------------------------------------------------------------------------------ -- Check if task should be failed due to WG: function drx_ql_wg_fail( actor, npc, p ) -- Fail current task if wrong choice at WG: if ( has_alife_info( "actor_made_wish" ) ) then if ( (p) and (#p > 0) and (p[1]) and (string.find( p[1], "_sl_task_" )) ) then return true end end -- Set return value: return false end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_find_occupied_smart function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Selects a random occupied smart terrain and stores it in a pstor var -- - Modification of xr_effects.find_smart_under_faction_control_ex (CoC 1.5b r4) -- -- Usage: -- drx_ql_find_occupied_smart( p[1]:p[2]:p[3]:... ) -- -- Parameters: -- p[1] (type: bool) -- - True if including all levels, false if including only local levels -- p[2] (type: string, faction name) -- - Faction of the task giver -- p[3] (type: string, task id) -- - Name of the task calling this precondition -- p[4]:... (type:string, faction names) -- - Allowable factions (or "monster") that occupy the smart terrain (optional; if omitted a mutual enemy will be selected) -- -- Persistent storage: -- {task_id}_enemy_faction (type: string, faction name) -- - Name of enemy faction for specified task -- drx_ql_{task_id}_{npc_id}_target_smart (type: smart terrain id) -- - Id of selected target smart terrain for specified task -- drx_ql_current_npc_speaker_id (type: npc id) -- - Id of the current npc the actor is speaking to -- -- Ini requirements: -- drx\drx_ql_config.ltx -- [sim_task_props] -- min_enemy_count (type: int) -- - Minimum enemy count for assault task targets -- assault_radius (type: float, meters) -- - Assault task target radius -- target_min_radius (type: float, meters) -- - Minimum distance from actor to search for target areas -- target_max_radius (type: float, meters) -- - Maximum distance from actor to search for target areas -- target_exp (type: int) -- - Exponent to use for max target distance calculations -- target_tiers (type: int) -- - Number of tiers to divide potential targets into -- -- Return value (type: bool): -- Returns true if a valid occupied smart terrain is found, false otherwise -- -- Notes: -- - Selected occupied smart terrain id will be stored in pstor var drx_ql_{task_id}_{npc_id}_target_smart -- - Enemy faction name will be stored in pstor var drx_ql_{task_id}_{npc_id}_enemy_faction -- - Smart terrains are considered invalid targets if blacklisted or affected by the Brain Scorcher while it is active -- - Smart terrains are weighted to give higher probablity of being selected if closer to actor -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified October 10, 2019 -- ------------------------------------------------------------------------------------------------ -- Select random occupied smart terrain: function drx_ql_find_occupied_smart( actor, npc, p ) -- <>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<> -- CotZ for IWP: Added toggle for nocturnal mutants -- -- Author(s) : moonshroom -- Added : 04/12/2024 12:05 am -- Ver. : Indev 6.6 -- <>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<> local settings = ini_file( "drx\\drx_cotz_config.ltx" ) -- <>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<> -- Location of the settings file: local ini = ini_file( "drx\\drx_ql_config.ltx" ) if ( not ini ) then printf( "DRX QL Error: Unable to select occupied smart terrain, cannot locate ini file" ) return false end -- Validate input: if ( #p < 3 ) then printf( "DRX QL Error: Unable to select occupied smart terrain, invalid parameters supplied" ) return false end -- Verify db.actor is available: if ( not db.actor ) then printf( "DRX QL Error: Unable to select occupied smart terrain, db.actor not available" ) return false end -- Get the id of the npc the actor is currently speaking with: local npc_id = utils.load_var( db.actor, "drx_ql_current_npc_speaker_id", nil ) if ( npc_id == nil ) then printf( "DRX QL Error: Unable to select occupied smart terrain, could not get id of current speaker" ) return false end -- Check for previous stored tagets: local stored_target = utils.load_var( db.actor, string.format( "drx_ql_%s_%s_target_smart", p[3], npc_id ) ) local enemy_faction = utils.load_var( db.actor, string.format( "drx_ql_%s_%s_enemy_faction", p[3], npc_id ) ) if ( stored_target and enemy_faction ) then -- Check if target is still valid: local smrt = SIMBOARD.smarts[stored_target] for k, squad in pairs( smrt.squads ) do if ( (squad) and (squad.player_id == enemy_faction) ) then return true end end return false end -- Build list of enemy factions: local enemy_faction_list = {} if ( #p > 3 ) then for j = 4, ( #p ) do -- table.insert( enemy_faction_list, p[j] ) enemy_faction_list[#enemy_faction_list + 1] = p[j] end else enemy_faction_list = drx_ql_task_funcs.drx_ql_build_mutual_enemy_list( p[2] ) end -- Ensure an enemy faction was found: if ( #enemy_faction_list < 1 ) then return false end -- Determine target search radius: local min_radius = (ini:r_float_ex( "sim_task_props", "target_min_radius" ) or 0) local max_radius = (ini:r_float_ex( "sim_task_props", "target_max_radius" ) or 0) local radius_exp = (ini:r_float_ex( "sim_task_props", "target_exp" ) or 0) local target_tiers = (ini:r_float_ex( "sim_task_props", "target_tiers" ) or 0) local radius = min_radius if ( min_radius > max_radius ) then min_radius = max_radius end local delta = (max_radius - min_radius) if ( delta > 0 ) then radius = (min_radius + math.ceil( (delta * ((math.random( delta ) ^ radius_exp) / (delta ^ radius_exp))) )) end -- Build list of occupied smart terrains: local smart_list = {} local min_enemy_count = (ini:r_float_ex( "sim_task_props", "min_enemy_count" ) or 1) local assault_radius = ((ini:r_float_ex( "sim_task_props", "assault_radius" ) or 0) ^ 2) for name, smart in pairs( SIMBOARD.smarts_by_names ) do -- Inspect next smart terrain: if ( (p[1] or smart.online) and (smart.sim_avail == nil or smart.sim_avail and xr_logic.pick_section_from_condlist(actor, smart, smart.sim_avail) == "true") ) then local smrt = SIMBOARD.smarts[smart.id] if ( smrt ) then -- Check if current smart terrain is occupied: local enemy_faction for k, squad in pairs( smrt.squads ) do if ( (squad) and (p[1] or squad.stay_time) and (squad.current_target_id) and (squad.current_target_id == smart.id) and (not squad:get_script_target( )) ) then -- Check if occupiers are designated factions: for i = 1, ( #enemy_faction_list ) do local enemy_count = 0 if ( (squad.player_id == enemy_faction_list[i]) or ((enemy_faction_list[i] == "monster") and (is_squad_monster[squad.player_id])) ) then -- <>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<> -- CotZ for IWP: Added toggle for nocturnal mutants -- -- Author(s) : moonshroom -- Added : 04/12/2024 12:05 am -- Ver. : Indev 6.6 -- <>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<> local go_ahead = true if ( ( settings:r_bool_ex( "misc_settings", "enable_nocturnal_mutants" ) == true ) or false ) then -- Special checks for "Kill mutant" tasks -- Certain mutants only come online at certain hours of the day. -- This is to ensure the game chooses a mutant squad that can come online at the current time. local hour = level.get_time_hours() if ( squad.player_id == "monster_predatory_day" ) then if ( hour <= 5 or hour >= 22 ) then go_ahead = false end elseif ( squad.player_id == "monster_zombied_day" ) then if ( hour <= 5 or hour >= 19 ) then go_ahead = false end elseif ( squad.player_id == "monster_predatory_night" ) then if ( hour >= 5 and hour <= 19 ) then go_ahead = false end elseif ( squad.player_id == "monster_zombied_night" ) then if ( hour >= 5 and hour <= 19 ) then go_ahead = false end end end -- <>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<> -- Check if target is within smart terrain: if ( go_ahead == true ) then local se_target = alife( ):object( squad.id ) if ( (assault_radius == 0) or ((smrt.smrt) and (smrt.smrt.position:distance_to_sqr( se_target.position ) <= assault_radius)) ) then -- Check if minimum enemy count is satisfied: for k in squad:squad_members( ) do enemy_count = (enemy_count + 1) if ( enemy_count >= min_enemy_count ) then enemy_faction = enemy_faction_list[i] break end end end end end end -- Check if smart was determined to be occupied by designated faction: if ( enemy_faction ) then break end end end -- Check if smart terrain not blacklisted or currently affected by the Brain Scorcher: if ( (enemy_faction) and (drx_ql_smart_not_blacklisted( actor, npc, {name} )) and (drx_ql_smart_not_scorched( actor, npc, {name} )) ) then -- Weight the smart terrain as inverse of distance to actor: local weight = 1 local nearest_smart_id = smart_terrain.nearest_to_actor_smart.id local nearest_smart_obj = alife( ):object( nearest_smart_id ) if ( not nearest_smart_obj ) then printf( "DRX QL Error: Unable to find smart terrain closest to actor" ) else local dist = utils.graph_distance( nearest_smart_obj.m_game_vertex_id, smart.m_game_vertex_id ) if ( dist > radius ) then weight = 0 elseif ( (radius > 0) and (target_tiers > 0) ) then weight = (target_tiers - math.ceil( (dist / (radius / target_tiers)) ) + 1) end end -- Add the current smart terrain to the list of occupied smarts: for n = 1, ( weight ) do -- table.insert( smart_list, {smart.id, enemy_faction} ) smart_list[#smart_list + 1] = {smart.id, enemy_faction} end end end end end -- Check if at least one smart terrain was found: if ( #smart_list < 1 ) then return false end -- Select random smart terrain from list and save in pstor var: local index = math.random( #smart_list ) utils.save_var( db.actor, string.format( "drx_ql_%s_%s_target_smart", p[3], npc_id ), smart_list[index][1] ) utils.save_var( db.actor, string.format( "drx_ql_%s_%s_enemy_faction", p[3], npc_id ), smart_list[index][2] ) -- Set return value: return true end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ function if_actor_powerful(actor, npc) -- return ((actor:character_reputation()) >= 1999) return ( db.actor:money() >= 50000) end function if_actor_not_powerful(actor, npc) -- return ((actor:character_reputation()) <= 1999) return ( db.actor:money() < 50000) end -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_find_occupied_smart_local function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Selects a random occupied smart terrain on the current level and stores it in a pstor var -- -- Usage: -- drx_ql_find_occupied_smart_local( p[1]:p[2]:... ) -- -- Parameters: -- p[1] (type: string, faction name) -- - Faction of the task giver -- p[2] (type: string, task id) -- - Name of the task calling this precondition -- p[3]:... (type:string, faction names) -- - Allowable factions (or "monster") that occupy the smart terrain (optional; if omitted a mutual enemy will be selected) -- -- Return value (type: bool): -- Returns true if a valid occupied smart terrain is found, false otherwise -- -- Notes: -- - Selected occupied smart terrain id will be stored in pstor var drx_ql_{task_id}_target_smart -- - Smart terrains are considered invalid targets if blacklisted or affected by the Brain Scorcher while it is active -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified August 03, 2019 -- ------------------------------------------------------------------------------------------------ -- Select random occupied smart terrain on current level: function drx_ql_find_occupied_smart_local( actor, npc, p ) -- Transfer parameters: local param_array = {false} for i = 1, ( #p ) do -- table.insert( param_array, p[i] ) param_array[#param_array + 1] = p[i] end -- Set return value: return drx_ql_find_occupied_smart( actor, npc, param_array ) end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_find_occupied_smart_ext function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Selects a random occupied smart terrain on any level and stores it in a pstor var -- -- Usage: -- drx_ql_find_occupied_smart_ext( p[1]:p[2]:... ) -- -- Parameters: -- p[1] (type: string, faction name) -- - Faction of the task giver -- p[2] (type: string, task id) -- - Name of the task calling this precondition -- p[3]:... (type:string, faction names) -- - Allowable factions (or "monster") that occupy the smart terrain (optional; if omitted a mutual enemy will be selected) -- -- Return value (type: bool): -- Returns true if a valid occupied smart terrain is found, false otherwise -- -- Notes: -- - Selected occupied smart terrain id will be stored in pstor var drx_ql_{task_id}_target_smart -- - Smart terrains are considered invalid targets if blacklisted or affected by the Brain Scorcher while it is active -- - Smart terrains are weighted to give higher probablity of being selected if closer to actor -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified August 03, 2019 -- ------------------------------------------------------------------------------------------------ -- Select random occupied smart terrain on any level: function drx_ql_find_occupied_smart_ext( actor, npc, p ) -- Transfer parameters: local param_array = {true} for i = 1, ( #p ) do -- table.insert( param_array, p[i] ) param_array[#param_array + 1] = p[i] end -- Set return value: return drx_ql_find_occupied_smart( actor, npc, param_array ) end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_find_unoccupied_lair_local function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Selects a random unoccupied smart terrain on the current level and stores it in a pstor var -- -- Usage: -- drx_ql_find_unoccupied_lair_local( p[1] ) -- -- Parameters: -- p[1] (type: string, task id) -- - Name of the task calling this precondition -- -- Persistent storage: -- drx_ql_{task_id}_{npc_id}_target_smart (type: smart terrain id) -- - Id of selected target smart terrain for specified task -- drx_ql_current_npc_speaker_id (type: npc id) -- - Id of the current npc the actor is speaking to -- -- Return value (type: bool): -- Returns true if a valid unoccupied smart terrain is found, false otherwise -- -- Notes: -- - Selected unoccupied smart terrain id will be stored in pstor var drx_ql_{task_id}_target_smart -- - Smart terrains are considered invalid targets if blacklisted or affected by the Brain Scorcher while it is active -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified May 19, 2019 -- ------------------------------------------------------------------------------------------------ -- Select random unoccupied smart terrain on current level: function drx_ql_find_unoccupied_lair_local( actor, npc, p ) -- Validate input: if ( #p < 1 ) then printf( "DRX QL Error: Unable to select unoccupied smart terrain, invalid parameters supplied" ) return false end -- Verify db.actor is available: if ( not db.actor ) then printf( "DRX QL Error: Unable to select unoccupied smart terrain, db.actor not available" ) return false end -- Get the id of the npc the actor is currently speaking with: local npc_id = utils.load_var( db.actor, "drx_ql_current_npc_speaker_id", nil ) if ( npc_id == nil ) then printf( "DRX QL Error: Unable to select unoccupied smart terrain, could not get id of current speaker" ) return false end -- -- Check for previous stored tagets: -- local stored_target = utils.load_var( db.actor, string.format( "drx_ql_%s_%s_target_smart", p[1], npc_id ), nil ) -- if ( stored_target ) then -- return true -- end -- Build list of unoccupied smart terrains: local smart_list = {} for name, smart in pairs( SIMBOARD.smarts_by_names ) do -- Inspect next smart terrain: if ( (smart.online) and (smart.sim_avail == nil or smart.sim_avail and xr_logic.pick_section_from_condlist(actor, smart, smart.sim_avail) == "true") ) then local smrt = SIMBOARD.smarts[smart.id] if ( smrt ) then -- Check if the current smart is a lair: if ( (smart.props) and (smart.props.lair) and ( smart.props.lair > 0) ) then -- Check if current smart terrain is occupied: local smart_is_occupied = false for k, squad in pairs( smrt.squads ) do if ( (squad) and (squad.stay_time) and (squad.current_target_id) and (squad.current_target_id == smart.id) and (not squad:get_script_target( )) ) then smart_is_occupied = true break end end -- Check if smart terrain not blacklisted or currently affected by the Brain Scorcher: if ( (not smart_is_occupied) and (drx_ql_smart_not_blacklisted( actor, npc, {name} )) and (drx_ql_smart_not_scorched( actor, npc, {name} )) ) then -- Add the current smart terrain to the list of occupied smarts: -- table.insert( smart_list, smart.id ) smart_list[#smart_list + 1] = smart.id end end end end end -- Check if at least one smart terrain was found: if ( #smart_list < 1 ) then return false end -- Select random smart terrain from list and save in pstor var: utils.save_var( db.actor, string.format( "drx_ql_%s_%s_target_smart", p[1], npc_id ), smart_list[math.random( #smart_list )] ) -- Set return value: return true end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_find_guide_target function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Selects a random unoccupied smart terrain as a guide task target -- -- Usage: -- drx_ql_find_guide_target( p[1] ) -- -- Parameters: -- p[1] (type: string, task id) -- - Name of the task calling this precondition -- -- Persistent storage: -- drx_ql_{task_id}_{npc_id}_target_smart (type: smart terrain id) -- - Id of selected target smart terrain for specified task -- {task_id}_reward_value (type: int) -- - Value of the task reward -- {task_id}_target_level (type: section name) -- - Name of level the task target is on -- drx_ql_current_npc_speaker_id (type: npc id) -- - Id of the current npc the actor is speaking to -- -- Ini requirements: -- drx\drx_ql_config.ltx -- [sim_task_props] -- guide_task_max_npcs (type: int) -- - Maximum task giver squad size for guide tasks -- target_min_radius (type: float, meters) -- - Minimum distance from actor to search for target areas -- target_max_radius (type: float, meters) -- - Maximum distance from actor to search for target areas -- target_exp (type: int) -- - Exponent to use for max target distance calculations -- target_tiers (type: int) -- - Number of tiers to divide potential targets into -- [reward_values] -- distance_rate (type: float, RU) -- - Rate per unit distance for guide task rewards -- -- Return value (type: bool): -- Returns true if a valid guide task target is found, false otherwise -- -- Notes: -- - Selected smart terrain id will be stored in pstor var drx_ql_{npc_id}_{task_id}_target_smart -- - Smart terrains are considered invalid targets if blacklisted or affected by the Brain Scorcher while it is active -- - Smart terrains are weighted to give higher probablity of being selected if closer to actor -- - Smart terrains on the current level will not be selected -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified April 27, 2019 -- ------------------------------------------------------------------------------------------------ -- Select random unoccupied smart terrain on any level: function drx_ql_find_guide_target( actor, npc, p ) -- Location of the settings file: local ini = ini_file( "drx\\drx_ql_config.ltx" ) if ( not ini ) then printf( "DRX QL Error: Unable to select guide task target, cannot locate ini file" ) return false end -- Validate input: if ( #p < 1 ) then printf( "DRX QL Error: Unable to select guide task target, invalid parameters supplied" ) return false end -- Verify db.actor is available: if ( not db.actor ) then printf( "DRX QL Error: Unable to select guide task target, db.actor not available" ) return false end -- Get the id of the npc the actor is currently speaking with: local npc_id = utils.load_var( db.actor, "drx_ql_current_npc_speaker_id", nil ) if ( npc_id == nil ) then printf( "DRX QL Error: Unable to find guide task target, could not get id of current speaker" ) return false end -- Check npc squad size: local guide_task_max_npcs = (ini:r_float_ex( "sim_task_props", "guide_task_max_npcs" ) or 0) if ( guide_task_max_npcs > 0 ) then if ( not drx_ql_npc_squad_size( actor, npc, {guide_task_max_npcs} ) ) then return false end end -- Check for previous stored tagets: local target_smart = utils.load_var( db.actor, string.format( "drx_ql_%s_%s_target_smart", p[1], npc_id ), nil ) -- If no stored targets then generate new one: if ( not target_smart ) then -- Determine target search radius: local min_radius = (ini:r_float_ex( "sim_task_props", "target_min_radius" ) or 0) local max_radius = (ini:r_float_ex( "sim_task_props", "target_max_radius" ) or 0) local radius_exp = (ini:r_float_ex( "sim_task_props", "target_exp" ) or 0) local target_tiers = (ini:r_float_ex( "sim_task_props", "target_tiers" ) or 0) local radius = min_radius if ( min_radius > max_radius ) then min_radius = max_radius end local delta = (max_radius - min_radius) if ( delta > 0 ) then radius = (min_radius + math.ceil( (delta * ((math.random( delta ) ^ radius_exp) / (delta ^ radius_exp))) )) end -- Build list of unoccupied smart terrains: local smart_list = {} for name, smart in pairs( SIMBOARD.smarts_by_names ) do -- Inspect next smart terrain: if (smart.sim_avail == nil or smart.sim_avail and xr_logic.pick_section_from_condlist(actor, smart, smart.sim_avail) == "true") then local smrt = SIMBOARD.smarts[smart.id] if ( smrt ) then -- Check if current smart is not on same level as actor: if ( game_graph( ):vertex( smart.m_game_vertex_id ):level_id( ) ~= game_graph( ):vertex( db.actor:game_vertex_id( ) ):level_id( ) ) then -- Check if current smart terrain is occupied: local smart_is_occupied = false for k, squad in pairs( smrt.squads ) do if ( (squad.id) and (alife( ):object( squad.id )) ) then smart_is_occupied = true break end end -- Check if smart terrain not blacklisted or currently affected by the Brain Scorcher: if ( (not smart_is_occupied) and (drx_ql_smart_not_blacklisted( actor, npc, {name} )) and (drx_ql_smart_not_scorched( actor, npc, {name} )) ) then -- Weight the smart terrain as inverse of distance to actor: local weight = 1 local nearest_smart_id = smart_terrain.nearest_to_actor_smart.id local nearest_smart_obj = alife( ):object( nearest_smart_id ) if ( not nearest_smart_obj ) then printf( "DRX QL Error: Unable to find smart terrain closest to actor" ) else local dist = utils.graph_distance( nearest_smart_obj.m_game_vertex_id, smart.m_game_vertex_id ) if ( dist > radius ) then weight = 0 elseif ( (radius > 0) and (target_tiers > 0) ) then weight = (target_tiers - math.ceil( (dist / (radius / target_tiers)) ) + 1) end end -- Add the current smart terrain to the list of unoccupied smarts: for n = 1, ( weight ) do -- table.insert( smart_list, smart.id ) smart_list[#smart_list + 1] = smart.id end end end end end end -- Check if at least one smart terrain was found: if ( #smart_list < 1 ) then return false end -- Select random smart terrain from list and save in pstor var: target_smart = smart_list[math.random( #smart_list )] utils.save_var( db.actor, string.format( "drx_ql_%s_%s_target_smart", p[1], npc_id ), target_smart ) end -- Save target level name: local smrt = SIMBOARD.smarts[target_smart].smrt if ( not smrt ) then printf( "DRX QL Error: Unable to store guide task target level name, smart terrain is invalid" ) return false end local target_level = alife( ):level_name( game_graph( ):vertex( smrt.m_game_vertex_id ):level_id( ) ) utils.save_var( db.actor, string.format( "%s_target_level", p[1] ), target_level ) -- Calculate distance to target: local nearest_smart_id = smart_terrain.nearest_to_actor_smart.id local nearest_smart_obj = alife( ):object( nearest_smart_id ) if ( not nearest_smart_obj ) then printf( "DRX QL Error: Unable to find smart terrain closest to actor" ) return false end local dist = utils.graph_distance( nearest_smart_obj.m_game_vertex_id, smrt.m_game_vertex_id ) -- Calculate reward value: local distance_rate = (ini:r_float_ex( "reward_values", "distance_rate" ) or 0) local reward_value = (dist * distance_rate) -- Store reward value: utils.save_var( db.actor, string.format( "%s_reward_value", p[1] ), reward_value ) -- Set return value: return true end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_find_unoccupied_smart_terrain function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Selects a random unoccupied smart terrain -- -- Usage: -- drx_ql_find_unoccupied_smart_terrain( p[1] ) -- -- Parameters: -- p[1] (type: string, task id) -- - Name of the task calling this precondition -- -- Persistent storage: -- drx_ql_{task_id}_{npc_id}_target_smart (type: smart terrain id) -- - Id of selected target smart terrain for specified task -- drx_ql_current_npc_speaker_id (type: npc id) -- - Id of the current npc the actor is speaking to -- -- Ini requirements: -- drx\drx_ql_config.ltx -- [sim_task_props] -- target_min_radius (type: float, meters) -- - Minimum distance from actor to search for target areas -- target_max_radius (type: float, meters) -- - Maximum distance from actor to search for target areas -- target_exp (type: int) -- - Exponent to use for max target distance calculations -- target_tiers (type: int) -- - Number of tiers to divide potential targets into -- -- Return value (type: bool): -- Returns true if an unoccupied smart terrain is found, false otherwise -- -- Notes: -- - Selected smart terrain id will be stored in pstor var drx_ql_{npc_id}_{task_id}_target_smart -- - Smart terrains are considered invalid targets if blacklisted or affected by the Brain Scorcher while it is active -- - Smart terrains are weighted to give higher probablity of being selected if closer to actor -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified January 30, 2019 -- ------------------------------------------------------------------------------------------------ -- Select random unoccupied smart terrain on any level: function drx_ql_find_unoccupied_smart_terrain( actor, npc, p ) -- Location of the settings file: local ini = ini_file( "drx\\drx_ql_config.ltx" ) if ( not ini ) then printf( "DRX QL Error: Unable to select unoccupied smart target, cannot locate ini file" ) return false end -- Validate input: if ( #p < 1 ) then printf( "DRX QL Error: Unable to select unoccupied smart terrain, invalid parameters supplied" ) return false end -- Verify db.actor is available: if ( not db.actor ) then printf( "DRX QL Error: Unable to select unoccupied smart terrain, db.actor not available" ) return false end -- Get the id of the npc the actor is currently speaking with: local npc_id = utils.load_var( db.actor, "drx_ql_current_npc_speaker_id", nil ) if ( npc_id == nil ) then printf( "DRX QL Error: Unable to select unoccupied smart terrain, could not get id of current speaker" ) return false end -- Check for previous stored tagets: local target_smart = utils.load_var( db.actor, string.format( "drx_ql_%s_%s_target_smart", p[1], npc_id ), nil ) -- If no stored targets then generate new one: if ( not target_smart ) then -- Determine target search radius: local min_radius = (ini:r_float_ex( "sim_task_props", "target_min_radius" ) or 0) local max_radius = (ini:r_float_ex( "sim_task_props", "target_max_radius" ) or 0) local radius_exp = (ini:r_float_ex( "sim_task_props", "target_exp" ) or 0) local target_tiers = (ini:r_float_ex( "sim_task_props", "target_tiers" ) or 0) local radius = min_radius if ( min_radius > max_radius ) then min_radius = max_radius end local delta = (max_radius - min_radius) if ( delta > 0 ) then radius = (min_radius + math.ceil( (delta * ((math.random( delta ) ^ radius_exp) / (delta ^ radius_exp))) )) end -- Build list of unoccupied smart terrains: local smart_list = {} for name, smart in pairs( SIMBOARD.smarts_by_names ) do -- Inspect next smart terrain: if (smart.sim_avail == nil or smart.sim_avail and xr_logic.pick_section_from_condlist(actor, smart, smart.sim_avail) == "true") then local smrt = SIMBOARD.smarts[smart.id] if ( smrt ) then -- Check if current smart terrain is occupied: local smart_is_occupied = false for k, squad in pairs( smrt.squads ) do if ( (squad.id) and (alife( ):object( squad.id )) ) then smart_is_occupied = true break end end -- Check if smart terrain not blacklisted or currently affected by the Brain Scorcher: if ( (not smart_is_occupied) and (drx_ql_smart_not_blacklisted( actor, npc, {name} )) and (drx_ql_smart_not_scorched( actor, npc, {name} )) ) then -- Weight the smart terrain as inverse of distance to actor: local weight = 1 local nearest_smart_id = smart_terrain.nearest_to_actor_smart.id local nearest_smart_obj = alife( ):object( nearest_smart_id ) if ( not nearest_smart_obj ) then printf( "DRX QL Error: Unable to find smart terrain closest to actor" ) else local dist = utils.graph_distance( nearest_smart_obj.m_game_vertex_id, smart.m_game_vertex_id ) if ( dist > radius ) then weight = 0 elseif ( (radius > 0) and (target_tiers > 0) ) then weight = (target_tiers - math.ceil( (dist / (radius / target_tiers)) ) + 1) end end -- Add the current smart terrain to the list of unoccupied smarts: for n = 1, ( weight ) do -- table.insert( smart_list, smart.id ) smart_list[#smart_list + 1] = smart.id end end end end end -- Check if at least one smart terrain was found: if ( #smart_list < 1 ) then return false end -- Select random smart terrain from list and save in pstor var: target_smart = smart_list[math.random( #smart_list )] utils.save_var( db.actor, string.format( "drx_ql_%s_%s_target_smart", p[1], npc_id ), target_smart ) end -- Set return value: return true end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_find_brawl_terrain function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Selects an area to stage a brawl task -- -- Usage: -- drx_ql_find_brawl_terrain( p[1]:p[2]:... ) -- -- Parameters: -- p[1] (type: string, faction name) -- - Faction of the task giver -- p[2] (type: string, task id) -- - Name of the task calling this precondition -- p[3]:... (type: string, faction names) -- - Allowable enemy factions (optional; if omitted a mutual enemy will be selected) -- -- Persistent storage: -- drx_ql_{task_id}_{npc_id}_enemy_smart_name (type: smart terrain name) -- - Name of the stored enemy smart terrain for a brawl task -- drx_ql_{task_id}_{npc_id}_friendly_smart_name (type: smart terrain name) -- - Name of the stored friendly smart terrain for a brawl task -- drx_ql_{task_id}_{npc_id}_enemy_faction (type: faction name) -- - Name of the stored enemy faction -- drx_ql_{task_id}_{npc_id}_friendly_faction (type: faction name) -- - Name of the stored friendly faction -- drx_ql_current_npc_speaker_id (type: npc id) -- - Id of the current npc the actor is speaking to -- -- Ini requirements: -- drx\drx_ql_config.ltx -- [brawl_settings] -- max_squad_count (type: int) -- - Maximum number of brawl squads per faction -- [brawl_locations] (type: string, pairs of smart terrain names) -- - Smart terrain pairs for brawl tasks -- [sim_task_props] -- target_min_radius (type: float, meters) -- - Minimum distance from actor to search for target areas -- target_max_radius (type: float, meters) -- - Maximum distance from actor to search for target areas -- target_exp (type: int) -- - Exponent to use for max target distance calculations -- target_tiers (type: int) -- - Number of tiers to divide potential targets into -- -- Return value (type: bool): -- Returns true if a brawl area and valid enemy is deternined, false otherwise -- -- Notes: -- - Terrain names will be stored in pstor vars drx_ql_{task_id}_{npc_id}_enemy_smart_name and drx_ql_{task_id}_{npc_id}_friendly_smart_name -- - Faction names will be stored in pstor vars drx_ql_{task_id}_{npc_id}_enemy_faction and drx_ql_{task_id}_{npc_id}_friendly_faction -- - Smart terrains are considered invalid targets if affected by the Brain Scorcher while it is active -- - Smart terrains are weighted to give higher probability of being selected if closer to actor -- - Enemy smart terrain will be furthest smart terrain from actor out of the pair -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified July 23, 2019 -- ------------------------------------------------------------------------------------------------ -- Select brawl location: function drx_ql_find_brawl_terrain( actor, npc, p ) -- Location of the settings file: local drx_ql_ini = ini_file( "drx\\drx_ql_config.ltx" ) if ( not drx_ql_ini ) then printf( "DRX QL Error: Unable to select brawl location, cannot locate drx_ql_ini file" ) return false end -- Location of dynamic population file. local drx_dp_ini = ini_file( "drx\\drx_dp_config.ltx" ) if ( not drx_dp_ini ) then printf( "-CotZ for IWP | xr_conditions.script / drx_ql_find_brawl_terrain() | [ERR]: Cannot find drx_dp_ini file" ) return false end -- Validate input: if ( #p < 2 ) then printf( "DRX QL Error: Unable to select brawl location, invalid parameters supplied" ) return false end -- Verify db.actor is available: if ( not db.actor ) then printf( "DRX QL Error: Unable to select brawl location, db.actor not available" ) return false end -- Get the id of the npc the actor is currently speaking with: local npc_id = utils.load_var( db.actor, "drx_ql_current_npc_speaker_id", nil ) if ( npc_id == nil ) then printf( "DRX QL Error: Unable to select brawl location, could not get id of current speaker" ) return false end -- Get max number of brawl squads: local max_squad_count = (drx_ql_ini:r_float_ex( "brawl_settings", "max_squad_count" ) or 0) if ( max_squad_count < 1 ) then return false end -- Check if brawl task parameters already determined and still valid: local stored_enemy_smart = utils.load_var( db.actor, string.format( "drx_ql_%s_%s_enemy_smart_name", p[2], npc_id ) ) local stored_friendly_smart = utils.load_var( db.actor, string.format( "drx_ql_%s_%s_friendly_smart_name", p[2], npc_id ) ) local stored_enemy_faction = utils.load_var( db.actor, string.format( "drx_ql_%s_%s_enemy_faction", p[2], npc_id ) ) local stored_friendly_faction = utils.load_var( db.actor, string.format( "drx_ql_%s_%s_friendly_faction", p[2], npc_id ) ) if ( stored_enemy_smart and stored_friendly_smart and stored_enemy_faction and stored_friendly_faction ) then if ( not drx_ql_is_enemy( actor, npc, {stored_enemy_faction} ) ) then return false end if ( not is_factions_enemies( actor, npc, {stored_friendly_faction, stored_enemy_faction} ) ) then return false end for r = 1, ( max_squad_count ) do if ( get_story_squad( string.format( "drx_ql_brawl_squad_%s_%s", stored_enemy_faction, r ) ) ) then return false end if ( get_story_squad( string.format( "drx_ql_brawl_squad_%s_%s", stored_friendly_faction, r ) ) ) then return false end end return true end -- Determine target search radius: local min_radius = (drx_ql_ini:r_float_ex( "sim_task_props", "target_min_radius" ) or 0) local max_radius = (drx_ql_ini:r_float_ex( "sim_task_props", "target_max_radius" ) or 0) local radius_exp = (drx_ql_ini:r_float_ex( "sim_task_props", "target_exp" ) or 0) local target_tiers = (drx_ql_ini:r_float_ex( "sim_task_props", "target_tiers" ) or 0) local radius = min_radius if ( min_radius > max_radius ) then min_radius = max_radius end local delta = (max_radius - min_radius) if ( delta > 0 ) then radius = (min_radius + math.ceil( (delta * ((math.random( delta ) ^ radius_exp) / (delta ^ radius_exp))) )) end -- Build list of valid brawl areas: local brawl_areas = alun_utils.collect_section( drx_ql_ini, "brawl_locations", true ) if ( (not brawl_areas) or (table.size( brawl_areas ) < 1) ) then printf( "DRX QL Error: Unable to select brawl location, no brawl areas defined in config" ) return false end local smart_list = {} for smart_a, smart_b in pairs( brawl_areas ) do -- Get the current smart terrain objects: local smart_a_obj = SIMBOARD.smarts_by_names[smart_a] local smart_b_obj = SIMBOARD.smarts_by_names[smart_b] if ( smart_a_obj and smart_b_obj) then -- Check if smart terrains are not currently affected by the Brain Scorcher: if ( (drx_ql_smart_not_scorched( actor, npc, {smart_a} )) and (drx_ql_smart_not_scorched( actor, npc, {smart_b} )) ) then -- Weight the smart terrain as inverse of distance to actor: local weight = 1 local nearest_smart_id = smart_terrain.nearest_to_actor_smart.id local nearest_smart_obj = alife( ):object( nearest_smart_id ) if ( not nearest_smart_obj ) then printf( "DRX QL Error: Unable to find smart terrain closest to actor" ) else local dist = utils.graph_distance( nearest_smart_obj.m_game_vertex_id, smart_a_obj.m_game_vertex_id ) if ( dist > radius ) then weight = 0 elseif ( (radius > 0) and (target_tiers > 0) ) then weight = (target_tiers - math.ceil( (dist / (radius / target_tiers)) ) + 1) end end -- Add the current smart terrain to the list of valid brawl areas: for n = 1, ( weight ) do -- table.insert( smart_list, smart_a ) smart_list[#smart_list + 1] = smart_a end end end end -- Check if at least one smart terrain was found: if ( #smart_list < 1 ) then return false end -- Select a smart terrain pair for brawl area: local enemy_smart_name = smart_list[math.random( #smart_list )] local friendly_smart_name = brawl_areas[enemy_smart_name] local enemy_smart_obj = SIMBOARD.smarts_by_names[enemy_smart_name] local friendly_smart_obj = SIMBOARD.smarts_by_names[friendly_smart_name] if ( (not enemy_smart_obj) or (not friendly_smart_obj) ) then return false end -- <>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<> -- CotZ for IWP: Dynamic population faction weights (drx_dp_ini.ltx) influences chosen enemy faction for brwal tasks -- -- Author(s) : moonshroom -- Added : 14/01/2025 5:27 pm -- Ver. : Indev 6.6 -- <>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<> -- Get friendly faction and check brawl squads not already spawned: local friendly_faction = p[1] for i = 1, ( max_squad_count ) do if ( get_story_squad( string.format( "drx_ql_brawl_squad_%s_%s", friendly_faction, i ) ) ) then return false end end -- /////////////////////////////////////////////////////////////////////////////////////////////////// -- Select enemy faction and check brawl squads not already spawned: local enemy_faction_list_1 = {} -- First enemy faction list. Used to dump all parameter/mutual enemy factions. local enemy_faction_pop_mults = {} local enemy_faction_list_2 = {} -- Second enemy faction list. The first list but filtered through. local brawl_level = alife():level_name( game_graph():vertex(enemy_smart_obj.m_game_vertex_id):level_id() ) local base_multiplier = drx_ql_ini:r_float_ex( "brawl_settings", "dyn_pop_base_multiplier" ) or 5 -- Fill first enemy faction list with explicit parameter factions ( p[3] -> p[n] ) or mutual enemy list. if ( #p > 2 ) then for j = 3, ( #p ) do local curr_fact = p[j] if ( drx_ql_is_enemy( actor, npc, {curr_fact} ) and is_factions_enemies( actor, npc, {friendly_faction, curr_fact} ) ) then enemy_faction_list_1[#enemy_faction_list_1 + 1] = curr_fact end end else enemy_faction_list_1 = drx_ql_task_funcs.drx_ql_build_mutual_enemy_list( friendly_faction ) end if ( #enemy_faction_list_1 < 1 ) then return false end -- /////////////////////////////////////////////////////////////////////////////////////////////////// -- Filter out first enemy faction list. Any faction that is not set to spawn in the current level (commented out -- or weight set to 0) is not added to the second enemy faction list. for i = 1, #enemy_faction_list_1 do local pop_mult = drx_dp_ini:r_string_ex( string.format( "%s_factions", brawl_level ), enemy_faction_list_1[i] ) or "1" pop_mult = tonumber( xr_logic.pick_section_from_condlist( db.actor, db.actor, alun_utils.parse_condlist( pop_mult ) ) ) or 0 if ( pop_mult and pop_mult > 0 ) then enemy_faction_list_2[#enemy_faction_list_2 + 1] = enemy_faction_list_1[i] enemy_faction_pop_mults[#enemy_faction_pop_mults + 1] = pop_mult end end if ( #enemy_faction_list_2 < 1 ) then return false end -- /////////////////////////////////////////////////////////////////////////////////////////////////// -- Increase weight for each outstanding faction based on dynamic population value. for i = 1, #enemy_faction_list_2 do -- local pop_mult = drx_dp_ini:r_string_ex( string.format( "%s_factions", brawl_level ), enemy_faction_list_2[i] ) or "1" -- pop_mult = tonumber( xr_logic.pick_section_from_condlist( db.actor, db.actor, alun_utils.parse_condlist( pop_mult ) ) ) or 0 local weight = math.ceil(base_multiplier * enemy_faction_pop_mults[i]) if ( weight < 1 ) then weight = 1 end -- Multiply faction based on multiplier. for j = 1, weight do enemy_faction_list_2[#enemy_faction_list_2 + 1] = enemy_faction_list_2[i] end end -- Shuffle list. for i = 1, math.random( 5 ) do for j = 1, ( #enemy_faction_list_2 - 1 ) do local k = math.random( j, #enemy_faction_list_2 ) enemy_faction_list_2[j], enemy_faction_list_2[k] = enemy_faction_list_2[k], enemy_faction_list_2[j] end end -- /////////////////////////////////////////////////////////////////////////////////////////////////// local enemy_faction = enemy_faction_list_2[math.random( #enemy_faction_list_2 )] for k = 1, ( max_squad_count ) do if ( get_story_squad( string.format( "drx_ql_brawl_squad_%s_%s", enemy_faction, k ) ) ) then return false end end -- <>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<> -- Determine which smart terrain is enemy spawn and which is friendly rendezvous: local nearest_smart_id = smart_terrain.nearest_to_actor_smart.id local nearest_smart_obj = alife( ):object( nearest_smart_id ) if ( nearest_smart_obj ) then local dist_enemy = utils.graph_distance( nearest_smart_obj.m_game_vertex_id, enemy_smart_obj.m_game_vertex_id ) local dist_friendly = utils.graph_distance( nearest_smart_obj.m_game_vertex_id, friendly_smart_obj.m_game_vertex_id ) if ( dist_friendly > dist_enemy ) then local temp_swap = enemy_smart_name enemy_smart_name = friendly_smart_name friendly_smart_name = temp_swap end end -- Store brawl task parameters: utils.save_var( db.actor, string.format( "drx_ql_%s_%s_enemy_smart_name", p[2], npc_id ), enemy_smart_name ) utils.save_var( db.actor, string.format( "drx_ql_%s_%s_friendly_smart_name", p[2], npc_id ), friendly_smart_name ) utils.save_var( db.actor, string.format( "drx_ql_%s_%s_enemy_faction", p[2], npc_id ), enemy_faction ) utils.save_var( db.actor, string.format( "drx_ql_%s_%s_friendly_faction", p[2], npc_id ), friendly_faction ) -- Set return value: return true end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_find_brawl_terrain_ambush function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Selects an area to stage a brawl task with an ambush waiting for the player -- -- Usage: -- drx_ql_find_brawl_terrain( p[1]:p[2]:... ) -- -- Parameters: -- p[1] (type: string, faction name) -- - Faction of the task giver -- p[2] (type: string, task id) -- - Name of the task calling this precondition -- p[3]:... (type: string, faction names) -- - Allowable enemy factions (optional; if omitted a mutual enemy will be selected) -- -- Persistent storage: -- drx_ql_{task_id}_{npc_id}_enemy_smart_name (type: smart terrain name) -- - Name of the stored enemy smart terrain for a brawl task -- drx_ql_{task_id}_{npc_id}_ambush_smart_name (type: smart terrain name) -- - Name of the stored ambush smart terrain for a brawl task -- drx_ql_{task_id}_{npc_id}_enemy_faction (type: faction name) -- - Name of the stored enemy faction -- drx_ql_{task_id}_{npc_id}_ambush_faction (type: faction name) -- - Name of the stored ambush faction (always same as enemy faction, can be adjusted if need be) -- drx_ql_current_npc_speaker_id (type: npc id) -- - Id of the current npc the actor is speaking to -- -- Ini requirements: -- drx\drx_ql_config.ltx -- [brawl_settings] -- max_squad_count (type: int) -- - Maximum number of brawl squads per faction -- [brawl_locations] (type: string, pairs of smart terrain names) -- - Smart terrain pairs for brawl tasks -- [sim_task_props] -- target_min_radius (type: float, meters) -- - Minimum distance from actor to search for target areas -- target_max_radius (type: float, meters) -- - Maximum distance from actor to search for target areas -- target_exp (type: int) -- - Exponent to use for max target distance calculations -- target_tiers (type: int) -- - Number of tiers to divide potential targets into -- -- Return value (type: bool): -- Returns true if a brawl area and valid enemy is deternined, false otherwise -- -- Notes: -- - Terrain names will be stored in pstor vars drx_ql_{task_id}_{npc_id}_enemy_smart_name and drx_ql_{task_id}_{npc_id}_ambush_smart_name -- - Faction names will be stored in pstor vars drx_ql_{task_id}_{npc_id}_enemy_faction and drx_ql_{task_id}_{npc_id}_ambush_faction -- - Smart terrains are considered invalid targets if affected by the Brain Scorcher while it is active -- - Smart terrains are weighted to give higher probability of being selected if closer to actor -- - Enemy smart terrain will be furthest smart terrain from actor out of the pair -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX, Modified by GoozE for ambush tasks -- for DoctorX Call of The Zone -- Last modified August 9th, 2022 -- ------------------------------------------------------------------------------------------------ -- Select brawl location for ambush: function drx_ql_find_brawl_terrain_ambush( actor, npc, p ) -- Location of the settings file: local drx_ql_ini = ini_file( "drx\\drx_ql_config.ltx" ) if ( not drx_ql_ini ) then printf( "DRX QL Error: Unable to select brawl location, cannot locate drx_ql_ini file" ) return false end -- Location of dynamic population file. local drx_dp_ini = ini_file( "drx\\drx_dp_config.ltx" ) if ( not drx_dp_ini ) then printf( "-CotZ for IWP | xr_conditions.script / drx_ql_find_brawl_terrain() | [ERR]: Cannot find drx_dp_ini file" ) return false end -- Validate input: if ( #p < 2 ) then printf( "DRX QL Error: Unable to select brawl location, invalid parameters supplied" ) return false end -- Verify db.actor is available: if ( not db.actor ) then printf( "DRX QL Error: Unable to select brawl location, db.actor not available" ) return false end -- Get the id of the npc the actor is currently speaking with: local npc_id = utils.load_var( db.actor, "drx_ql_current_npc_speaker_id", nil ) if ( npc_id == nil ) then printf( "DRX QL Error: Unable to select brawl location, could not get id of current speaker" ) return false end -- Get max number of brawl squads: local max_squad_count = (drx_ql_ini:r_float_ex( "brawl_settings", "max_squad_count" ) or 0) if ( max_squad_count < 1 ) then return false end -- Check if brawl task parameters already determined and still valid: local stored_enemy_smart = utils.load_var( db.actor, string.format( "drx_ql_%s_%s_enemy_smart_name", p[2], npc_id ) ) local stored_ambush_smart = utils.load_var( db.actor, string.format( "drx_ql_%s_%s_ambush_smart_name", p[2], npc_id ) ) local stored_enemy_faction = utils.load_var( db.actor, string.format( "drx_ql_%s_%s_enemy_faction", p[2], npc_id ) ) local stored_ambush_faction = utils.load_var( db.actor, string.format( "drx_ql_%s_%s_ambush_faction", p[2], npc_id ) ) local stored_friendly_faction = utils.load_var( db.actor, string.format( "drx_ql_%s_%s_friendly_faction", p[2], npc_id ) ) if ( stored_enemy_smart and stored_ambush_smart and stored_enemy_faction and stored_friendly_faction ) then if ( not drx_ql_is_enemy( actor, npc, {stored_enemy_faction} ) ) then return false end if ( not is_factions_enemies( actor, npc, {stored_friendly_faction, stored_enemy_faction} ) ) then return false end for r = 1, ( max_squad_count ) do if ( get_story_squad( string.format( "drx_ql_brawl_squad_%s_%s", stored_enemy_faction, r ) ) ) then return false end if ( get_story_squad( string.format( "drx_ql_brawl_squad_ambush_%s_%s", stored_ambush_faction, r ) ) ) then return false end end return true end -- Determine target search radius: local min_radius = (drx_ql_ini:r_float_ex( "sim_task_props", "target_min_radius" ) or 0) local max_radius = (drx_ql_ini:r_float_ex( "sim_task_props", "target_max_radius" ) or 0) local radius_exp = (drx_ql_ini:r_float_ex( "sim_task_props", "target_exp" ) or 0) local target_tiers = (drx_ql_ini:r_float_ex( "sim_task_props", "target_tiers" ) or 0) local radius = min_radius if ( min_radius > max_radius ) then min_radius = max_radius end local delta = (max_radius - min_radius) if ( delta > 0 ) then radius = (min_radius + math.ceil( (delta * ((math.random( delta ) ^ radius_exp) / (delta ^ radius_exp))) )) end -- Build list of valid brawl areas: local brawl_areas = alun_utils.collect_section( drx_ql_ini, "brawl_locations", true ) if ( (not brawl_areas) or (table.size( brawl_areas ) < 1) ) then printf( "DRX QL Error: Unable to select brawl location, no brawl areas defined in config" ) return false end local smart_list = {} for smart_a, smart_b in pairs( brawl_areas ) do -- Get the current smart terrain objects: local smart_a_obj = SIMBOARD.smarts_by_names[smart_a] local smart_b_obj = SIMBOARD.smarts_by_names[smart_b] if ( smart_a_obj and smart_b_obj) then -- Check if smart terrains are not currently affected by the Brain Scorcher: if ( (drx_ql_smart_not_scorched( actor, npc, {smart_a} )) and (drx_ql_smart_not_scorched( actor, npc, {smart_b} )) ) then -- Weight the smart terrain as inverse of distance to actor: local weight = 1 local nearest_smart_id = smart_terrain.nearest_to_actor_smart.id local nearest_smart_obj = alife( ):object( nearest_smart_id ) if ( not nearest_smart_obj ) then printf( "DRX QL Error: Unable to find smart terrain closest to actor" ) else local dist = utils.graph_distance( nearest_smart_obj.m_game_vertex_id, smart_a_obj.m_game_vertex_id ) if ( dist > radius ) then weight = 0 elseif ( (radius > 0) and (target_tiers > 0) ) then weight = (target_tiers - math.ceil( (dist / (radius / target_tiers)) ) + 1) end end -- Add the current smart terrain to the list of valid brawl areas: for n = 1, ( weight ) do smart_list[#smart_list + 1] = smart_a end end end end -- Check if at least one smart terrain was found: if ( #smart_list < 1 ) then return false end -- Select a smart terrain pair for brawl area: local enemy_smart_name = smart_list[math.random( #smart_list )] local ambush_smart_name = brawl_areas[enemy_smart_name] local enemy_smart_obj = SIMBOARD.smarts_by_names[enemy_smart_name] local ambush_smart_obj = SIMBOARD.smarts_by_names[ambush_smart_name] if ( (not enemy_smart_obj) or (not ambush_smart_obj) ) then return false end -- <>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<> -- CotZ for IWP: Dynamic population faction weights (drx_dp_ini.ltx) influences chosen enemy faction for brwal tasks -- -- Author(s) : moonshroom -- Added : 14/01/2025 5:27 pm -- Ver. : Indev 6.6 -- <>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<> -- Get friendly faction, no longer check if squad has spawned as this precondition requires an ambush to take place local friendly_faction = p[1] -- /////////////////////////////////////////////////////////////////////////////////////////////////// -- Select enemy faction and check brawl squads not already spawned: local enemy_faction_list_1 = {} -- First enemy faction list. Used to dump all parameter/mutual enemy factions. local enemy_faction_pop_mults = {} local enemy_faction_list_2 = {} -- Second enemy faction list. The first list but filtered through. local brawl_level = alife():level_name( game_graph():vertex(enemy_smart_obj.m_game_vertex_id):level_id() ) local base_multiplier = drx_ql_ini:r_float_ex( "brawl_settings", "dyn_pop_base_multiplier" ) or 5 -- Fill first enemy faction list with explicit parameter factions ( p[3] -> p[n] ) or mutual enemy list. if ( #p > 2 ) then for j = 3, ( #p ) do local curr_fact = p[j] if ( drx_ql_is_enemy( actor, npc, {curr_fact} ) and is_factions_enemies( actor, npc, {friendly_faction, curr_fact} ) ) then enemy_faction_list_1[#enemy_faction_list_1 + 1] = curr_fact end end else enemy_faction_list_1 = drx_ql_task_funcs.drx_ql_build_mutual_enemy_list( friendly_faction ) end if ( #enemy_faction_list_1 < 1 ) then return false end -- /////////////////////////////////////////////////////////////////////////////////////////////////// -- Filter out first enemy faction list. Any faction that is not set to spawn in the current level (commented out -- or weight set to 0) is not added to the second enemy faction list. for i = 1, #enemy_faction_list_1 do local pop_mult = drx_dp_ini:r_string_ex( string.format( "%s_factions", brawl_level ), enemy_faction_list_1[i] ) or "1" pop_mult = tonumber( xr_logic.pick_section_from_condlist( db.actor, db.actor, alun_utils.parse_condlist( pop_mult ) ) ) or 0 if ( pop_mult and pop_mult > 0 ) then enemy_faction_list_2[#enemy_faction_list_2 + 1] = enemy_faction_list_1[i] enemy_faction_pop_mults[#enemy_faction_pop_mults + 1] = pop_mult end end if ( #enemy_faction_list_2 < 1 ) then return false end -- /////////////////////////////////////////////////////////////////////////////////////////////////// -- Increase weight for each outstanding faction based on dynamic population value. for i = 1, #enemy_faction_list_2 do -- local pop_mult = drx_dp_ini:r_string_ex( string.format( "%s_factions", brawl_level ), enemy_faction_list_2[i] ) or "1" -- pop_mult = tonumber( xr_logic.pick_section_from_condlist( db.actor, db.actor, alun_utils.parse_condlist( pop_mult ) ) ) or 0 local weight = math.ceil(base_multiplier * enemy_faction_pop_mults[i]) if ( weight < 1 ) then weight = 1 end -- Multiply faction based on multiplier. for j = 1, weight do enemy_faction_list_2[#enemy_faction_list_2 + 1] = enemy_faction_list_2[i] end end -- Shuffle list. for i = 1, math.random( 5 ) do for j = 1, ( #enemy_faction_list_2 - 1 ) do local k = math.random( j, #enemy_faction_list_2 ) enemy_faction_list_2[j], enemy_faction_list_2[k] = enemy_faction_list_2[k], enemy_faction_list_2[j] end end -- /////////////////////////////////////////////////////////////////////////////////////////////////// local enemy_faction = enemy_faction_list_2[math.random( #enemy_faction_list_2 )] local ambush_faction = enemy_faction for k = 1, ( max_squad_count ) do if ( get_story_squad( string.format( "drx_ql_brawl_squad_%s_%s", enemy_faction, k ) ) ) then return false end if ( get_story_squad( string.format( "drx_ql_brawl_squad_ambush_%s_%s", ambush_faction, k ) ) ) then return false end end -- <>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<> -- Determine which smart terrain is enemy spawn and which is ambush location: local nearest_smart_id = smart_terrain.nearest_to_actor_smart.id local nearest_smart_obj = alife( ):object( nearest_smart_id ) if ( nearest_smart_obj ) then local dist_enemy = utils.graph_distance( nearest_smart_obj.m_game_vertex_id, enemy_smart_obj.m_game_vertex_id ) local dist_ambush = utils.graph_distance( nearest_smart_obj.m_game_vertex_id, ambush_smart_obj.m_game_vertex_id ) if ( dist_ambush > dist_enemy ) then local temp_swap = enemy_smart_name enemy_smart_name = ambush_smart_name ambush_smart_name = temp_swap end end -- Store brawl task parameters: utils.save_var( db.actor, string.format( "drx_ql_%s_%s_enemy_smart_name", p[2], npc_id ), enemy_smart_name ) utils.save_var( db.actor, string.format( "drx_ql_%s_%s_ambush_smart_name", p[2], npc_id ), ambush_smart_name ) utils.save_var( db.actor, string.format( "drx_ql_%s_%s_enemy_faction", p[2], npc_id ), enemy_faction ) utils.save_var( db.actor, string.format( "drx_ql_%s_%s_ambush_faction", p[2], npc_id ), ambush_faction ) utils.save_var( db.actor, string.format( "drx_ql_%s_%s_friendly_faction", p[2], npc_id ), friendly_faction ) -- Set return value: return true end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_find_special_smart_terrain function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Selects a special smart terrain -- -- Usage: -- drx_ql_find_special_smart_terrain( p[1], p[2] ) -- -- Parameters: -- p[1] (type: string, task id) -- - Name of the task calling this precondition -- p[2] (type: string, section name) -- - Section within the main config file containing the desired list of special smart terrain names -- -- Persistent storage: -- drx_ql_{task_id}_{npc_id}_target_smart (type: smart terrain id) -- - Id of selected target smart terrain for specified task -- drx_ql_current_npc_speaker_id (type: npc id) -- - Id of the current npc the actor is speaking to -- -- Ini requirements: -- drx\drx_ql_config.ltx -- [sim_task_props] -- target_min_radius (type: float, meters) -- - Minimum distance from actor to search for target areas -- target_max_radius (type: float, meters) -- - Maximum distance from actor to search for target areas -- target_exp (type: int) -- - Exponent to use for max target distance calculations -- target_tiers (type: int) -- - Number of tiers to divide potential targets into -- [{p[2]}] (type: string, smart terrain section names) -- - List of special smart terrain names -- -- Return value (type: bool): -- Returns true if a valid smart terrain is found, false otherwise -- -- Notes: -- - Selected smart terrain id will be stored in pstor var drx_ql_{npc_id}_{task_id}_target_smart -- - Smart terrains are considered invalid targets if blacklisted or affected by the Brain Scorcher while it is active -- - Smart terrains are weighted to give higher probablity of being selected if closer to actor -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified January 30, 2019 -- ------------------------------------------------------------------------------------------------ -- Select special smart terrain: function drx_ql_find_special_smart_terrain( actor, npc, p ) -- Location of the settings file: local ini = ini_file( "drx\\drx_ql_config.ltx" ) if ( not ini ) then printf( "DRX QL Error: Unable to select special smart target, cannot locate ini file" ) return false end -- Validate input: if ( #p < 2 ) then printf( "DRX QL Error: Unable to select special smart terrain, invalid parameters supplied" ) return false end -- Verify db.actor is available: if ( not db.actor ) then printf( "DRX QL Error: Unable to select special smart terrain, db.actor not available" ) return false end -- Get the id of the npc the actor is currently speaking with: local npc_id = utils.load_var( db.actor, "drx_ql_current_npc_speaker_id", nil ) if ( npc_id == nil ) then printf( "DRX QL Error: Unable to select special smart terrain, could not get id of current speaker" ) return false end -- Check for previous stored tagets: local target_smart = utils.load_var( db.actor, string.format( "drx_ql_%s_%s_target_smart", p[1], npc_id ), nil ) -- If no stored targets then generate new one: if ( not target_smart ) then -- Determine target search radius: local min_radius = (ini:r_float_ex( "sim_task_props", "target_min_radius" ) or 0) local max_radius = (ini:r_float_ex( "sim_task_props", "target_max_radius" ) or 0) local radius_exp = (ini:r_float_ex( "sim_task_props", "target_exp" ) or 0) local target_tiers = (ini:r_float_ex( "sim_task_props", "target_tiers" ) or 0) local radius = min_radius if ( min_radius > max_radius ) then min_radius = max_radius end local delta = (max_radius - min_radius) if ( delta > 0 ) then radius = (min_radius + math.ceil( (delta * ((math.random( delta ) ^ radius_exp) / (delta ^ radius_exp))) )) end -- Build list of anomalous smart terrains: local smart_list = {} local special_smart_list = alun_utils.collect_section( ini, p[2] ) for i = 1, ( #special_smart_list ) do -- Inspect next smart terrain: local name = special_smart_list[i] local smart = SIMBOARD.smarts_by_names[name] if ( smart ) then -- Check if smart terrain not blacklisted or currently affected by the Brain Scorcher: if ( (drx_ql_smart_not_blacklisted( actor, npc, {name} )) and (drx_ql_smart_not_scorched( actor, npc, {name} )) ) then -- Weight the smart terrain as inverse of distance to actor: local weight = 1 local nearest_smart_id = smart_terrain.nearest_to_actor_smart.id local nearest_smart_obj = alife( ):object( nearest_smart_id ) if ( not nearest_smart_obj ) then printf( "DRX QL Error: Unable to find smart terrain closest to actor" ) else local dist = utils.graph_distance( nearest_smart_obj.m_game_vertex_id, smart.m_game_vertex_id ) if ( dist > radius ) then weight = 0 elseif ( (radius > 0) and (target_tiers > 0) ) then weight = (target_tiers - math.ceil( (dist / (radius / target_tiers)) ) + 1) end end -- Add the current smart terrain to the list of unoccupied smarts: for n = 1, ( weight ) do -- table.insert( smart_list, smart.id ) smart_list[#smart_list + 1] = smart.id end end end end -- Check if at least one smart terrain was found: if ( #smart_list < 1 ) then return false end -- Select random smart terrain from list and save in pstor var: target_smart = smart_list[math.random( #smart_list )] utils.save_var( db.actor, string.format( "drx_ql_%s_%s_target_smart", p[1], npc_id ), target_smart ) end -- Set return value: return true end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_smart_not_blacklisted function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Checks if a smart terrain target is not blacklisted as a valid target area -- -- Usage: -- drx_ql_smart_not_blacklisted( p[1] ) -- -- Parameters: -- p[1] (type: string, smart terrain name) -- - Name of the smart terrain to check -- -- Return value (type: bool): -- Returns true if the smart terrain is not blacklisted, false otherwise -- -- Notes: -- - Blacklisted smart terrains are specified in config file; typically are faction bases or no fire zones -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified September 14, 2019 -- ------------------------------------------------------------------------------------------------ -- Check if smart terrain is not blacklisted: function drx_ql_smart_not_blacklisted( actor, npc, p ) -- Location of the settings file: local ini = ini_file( "drx\\drx_ql_config.ltx" ) if ( not ini ) then printf( "DRX QL Error: Unable to check if smart terrain blacklisted, cannot locate ini file" ) return false end -- Validate input: if ( #p < 1 ) then printf( "DRX QL Error: Unable to check if smart terrain blacklisted, invalid parameters supplied" ) return false end -- Check smart terrain against blacklist: if ( (drx_ql_xrc_smart_blacklist) and (drx_ql_xrc_smart_blacklist[p[1]]) ) then return false end -- Set return value: return true end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_smart_not_scorched function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Checks if a smart terrain target is not currently inaccessible due to Brain Scorcher being active -- -- Usage: -- drx_ql_smart_not_scorched( p[1] ) -- -- Parameters: -- p[1] (type: string, smart terrain name) -- - Name of the smart terrain to check -- -- Return value (type: bool): -- Returns true if the smart terrain is not inaccessible due to Brain Scorcher activity, false otherwise -- -- Notes: -- - If player is Monolith, this function will return true regardless of Brain Scorcher status -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified September 14, 2019 -- ------------------------------------------------------------------------------------------------ -- Check if smart terrain is not currently inaccessible due to Brain Scorcher activity: function drx_ql_smart_not_scorched( actor, npc, p ) -- Validate input: if ( #p < 1 ) then printf( "DRX QL Error: Unable to check if smart terrain scorched, invalid parameters supplied" ) return false end -- Check if player faction is Monolith: if ( string.gsub( alife( ):actor( ):community( ), "actor_", "" ) == "monolith" ) then return true end -- Check if smart terrain inaccessible due to Miracle Machine activity: if ( not has_alife_info( "yan_labx16_switcher_primary_off" ) ) then if ( (drx_ql_xrc_miracle_machine_smarts) and (drx_ql_xrc_miracle_machine_smarts[p[1]]) ) then return false end end -- Check if smart terrain inaccessible due to Brain Scorcher activity: if ( not has_alife_info( "bar_deactivate_radar_done" ) ) then if ( (drx_ql_xrc_brain_scorcher_smart_prefixes) and (#drx_ql_xrc_brain_scorcher_smart_prefixes > 0) ) then for i = 1, ( #drx_ql_xrc_brain_scorcher_smart_prefixes ) do if ( string.sub( p[1], 1, string.len( drx_ql_xrc_brain_scorcher_smart_prefixes[i] ) ) == drx_ql_xrc_brain_scorcher_smart_prefixes[i] ) then return false end end end end -- Set return value: return true end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_find_assassination_target function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Finds a target for an assassination task -- - Modification of xr_effects.on_init_bounty_hunt (CoC 1.5b r4) -- -- Usage: -- drx_ql_find_assassination_target( p[1]:p[2]:... ) -- -- Parameters: -- p[1] (type: string, faction name) -- - Faction of the task giver giving the task -- p[2] (type: string, task id) -- - Name of the task calling this function -- p[3]:... (type: string, faction names) -- - Allowable factions for the taget (optional; if omitted a mutual enemy will be selected) -- -- Persistent storage: -- drx_ql_{task_id}_{npc_id}_assassination_target (type: npc id) -- - Id of selected assassination target for specified task -- drx_ql_current_npc_speaker_id (type: npc id) -- - Id of the current npc the actor is speaking to -- -- Ini requirements: -- drx\drx_ql_config.ltx -- [sim_task_props] -- target_min_radius (type: float, meters) -- - Minimum distance from actor to search for target areas -- target_max_radius (type: float, meters) -- - Maximum distance from actor to search for target areas -- target_exp (type: int) -- - Exponent to use for max target distance calculations -- target_tiers (type: int) -- - Number of tiers to divide potential targets into -- misc\drx_ql_tasks\drx_ql_tasks_*.ltx -- [{task_id}] -- prisoner_task (type: bool) -- - Indicates an assassination task is a prisoner task (max squad size of 1) -- -- Return value (type: bool): -- Returns true if a valid assassination target is found, false otherwise -- -- Notes: -- - Selected target id is stored in pstor var drx_ql_{task_id}_{npc_id}_assassination_target -- - Targets located at smart terrains that are blacklisted or affected by the Brain Scorcher are considered invalid -- - Targets are weighted to give higher probablity of being selected if closer to actor -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Call of The Zone 1.2 -- Last modified August 18, 2020 -- ------------------------------------------------------------------------------------------------ -- Find target for assasination task: function drx_ql_find_assassination_target( actor, npc, p ) -- Location of the settings file: local ini = ini_file( "drx\\drx_ql_config.ltx" ) if ( not ini ) then printf( "DRX QL Error: Unable to select assassination target, cannot locate ini file" ) return false end -- Validate input: if ( #p < 2 ) then printf( "DRX QL Error: Unable to select assassination task target, invalid parameters supplied" ) return false end -- Verify db.actor is available: if ( not db.actor ) then printf( "DRX QL Error: Unable to select assassination task target, db.actor not available" ) return false end -- Get the id of the npc the actor is currently speaking with: local npc_id = utils.load_var( db.actor, "drx_ql_current_npc_speaker_id", nil ) if ( npc_id == nil ) then printf( "DRX QL Error: Unable to select assassination task target, could not get id of current speaker" ) return false end -- Check if a target has already been selected: local target_id = utils.load_var( db.actor, string.format( "drx_ql_%s_%s_assassination_target", p[2], npc_id ), nil ) if ( target_id ) then -- Check if stored target is still valid: local se_obj = alife( ):object( target_id ) if ( (se_obj) and (IsStalker( nil, se_obj:clsid( ) )) and (se_obj:alive( )) ) then return true else return false end end -- Build list of enemy factions: local enemy_faction_list = {} if ( #p > 2 ) then for j = 3, ( #p ) do -- table.insert( enemy_faction_list, p[j] ) enemy_faction_list[#enemy_faction_list + 1] = p[j] end else enemy_faction_list = drx_ql_task_funcs.drx_ql_build_mutual_enemy_list( p[1] ) end -- Ensure an enemy faction was found: if ( #enemy_faction_list < 1 ) then return false end -- Determine target search radius: local min_radius = (ini:r_float_ex( "sim_task_props", "target_min_radius" ) or 0) local max_radius = (ini:r_float_ex( "sim_task_props", "target_max_radius" ) or 0) local radius_exp = (ini:r_float_ex( "sim_task_props", "target_exp" ) or 0) local target_tiers = (ini:r_float_ex( "sim_task_props", "target_tiers" ) or 0) local radius = min_radius if ( min_radius > max_radius ) then min_radius = max_radius end local delta = (max_radius - min_radius) if ( delta > 0 ) then radius = (min_radius + math.ceil( (delta * ((math.random( delta ) ^ radius_exp) / (delta ^ radius_exp))) )) end -- Check if task is prisoner task: local prisoner_task = (task_manager.task_ini:r_bool_ex( p[2], "prisoner_task" ) or false) -- Build list of potential targets: local target_list = {} for i = 1, ( 65534 ) do -- Check if current npc is a valid potential target: local se_obj = alife( ):object( i ) -- <>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<> -- CotZ for IWP: Added checks for if the selected NPC is already a prisoner target -- for traditional assassination tasks and vice versa. -- -- Author(s) : moonshroom -- Added : 09/01/2025 7:24 pm -- Ver. : Indev 6.6 -- <>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<> if ( (se_obj) and (IsStalker( nil, se_obj:clsid( ) )) and (se_obj:alive( )) and (i ~= npc_id) and ( ( prisoner_task and not alife():has_info(i, "moon_cotz_is_assassin_target") ) or ( not prisoner_task and not alife():has_info(i, "moon_cotz_is_prisoner_target") ) ) ) then -- <>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<>--<> local sec_name_valid = false for m = 1, ( #drx_ql_xrc_assassination_target_names ) do if ( string.find( se_obj:section_name( ), drx_ql_xrc_assassination_target_names[m] ) ) then sec_name_valid = true break end end if ( (sec_name_valid) and (get_object_story_id( i ) == nil) and ((se_obj.group_id == nil) or (se_obj.group_id == 65535) or (get_object_story_id( se_obj.group_id ) == nil)) ) then -- Check if the current potential target belongs to an enemy faction: local target_faction = alife_character_community( se_obj ) for k = 1, ( #enemy_faction_list ) do if ( target_faction == enemy_faction_list[k] ) then -- If prisoner task check if squad size is 1: local squad_size = 0 if ( prisoner_task ) then local squad = get_object_squad( se_obj ) if ( squad ) then for member in ( squad:squad_members( ) ) do local member_obj = (member.object or (member.id and alife( ):object( member.id ))) if ( member_obj and member_obj:alive( ) ) then squad_size = (squad_size + 1) if ( squad_size > 1 ) then break end end end end end if ( (not prisoner_task) or ((prisoner_task) and (squad_size == 1)) ) then -- Get the smart terrain the potential target is currently located at: local smrt_obj = (se_obj.m_smart_terrain_id and alife( ):object( se_obj.m_smart_terrain_id )) if ( smrt_obj ) then -- Check if the target smart is not blacklisted: local smart_name = smrt_obj:name( ) if ( (drx_ql_smart_not_blacklisted( actor, npc, {smart_name} )) and (drx_ql_smart_not_scorched( actor, npc, {smart_name} )) ) then -- Weight the target as inverse of distance to actor: local weight = 1 local nearest_smart_id = smart_terrain.nearest_to_actor_smart.id local nearest_smart_obj = alife( ):object( nearest_smart_id ) if ( not nearest_smart_obj ) then printf( "DRX QL Error: Unable to find smart terrain closest to actor" ) else local dist = utils.graph_distance( nearest_smart_obj.m_game_vertex_id, smrt_obj.m_game_vertex_id ) if ( dist > radius ) then weight = 0 elseif ( (radius > 0) and (target_tiers > 0) ) then weight = (target_tiers - math.ceil( (dist / (radius / target_tiers)) ) + 1) end end -- Add the potential target to the target list: for n = 1, ( weight ) do -- table.insert( target_list, i ) target_list[#target_list + 1] = i end break end end end end end end end end -- Ensure at least one potential target was found: if ( #target_list < 1 ) then return false end -- Select random target from the list and store it: utils.save_var( db.actor, string.format( "drx_ql_%s_%s_assassination_target", p[2], npc_id ), target_list[math.random( #target_list )] ) -- Set return value: return true end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_is_alive function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Determines if an npc is alive -- - Modification of is_alive (CoC 1.5b r4) -- -- Usage: -- drx_ql_is_alive( p[1] ) -- -- Parameters: -- p[1] (type: string, npc name) -- - NPC section name of the NPC to check -- -- Return value (type: bool): -- Returns true if the NPC is alive, false otherwise -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified January 23, 2018 -- ------------------------------------------------------------------------------------------------ -- Check if NPC is alive: function drx_ql_is_alive( actor, npc, p ) -- Validate input: if ( (not p) or (#p < 1) ) then printf( "DRX QL Error: Could not determine if NPC is alive, input is invalid" ) return false end -- Special check for Sidorovich: if ( p[1] == "esc_m_trader" ) then return true end -- Special check for Owl: if ( p[1] == "zat_b30_owl_stalker_trader" ) then return true end -- Get the NPC id: local npc_id = get_story_object_id( p[1] ) if ( npc_id == nil ) then printf( "DRX QL Error: Could not determine if NPC is alive, could not find NPC id" ) return false end -- Get the NPC object: local npc_obj = alife_object( npc_id ) if ( not npc_obj ) then printf( "DRX QL Error: Could not determine if NPC is alive, could not find NPC object" ) return false end -- Check if the NPC is alive: if ( (not IsStalker( nil, npc_obj:clsid( ) )) or (not npc_obj:alive( )) ) then return false end -- Set return value: return true end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_task_giver_alive function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Determines if an task giver is alive -- - Modification of task_giver_alive (CoC 1.5b r4) -- -- Usage: -- drx_ql_task_giver_alive( p[1] ) -- -- Parameters: -- p[1] (type: string, task id) -- - Task id of the task to check if giver is alive -- -- Return value (type: bool): -- Returns true if the task giver is alive, false otherwise -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified March 15, 2019 -- ------------------------------------------------------------------------------------------------ -- Check if NPC is alive: function drx_ql_task_giver_alive( actor, npc, p ) -- Validate input: if ( (not p) or (#p < 1) ) then printf( "DRX QL Error: Could not determine if task giver is alive, input is invalid" ) return false end -- Get the task giver: local tsk = task_manager.get_task_manager( ).task_info[p[1]] if ( not tsk ) then return false end local npc_id = tsk.task_giver_id if ( not npc_id ) then return false end -- Get the NPC object: local npc_obj = alife_object( npc_id ) if ( not npc_obj ) then return false end -- Special checks: local npc_name if ( npc_obj:section_name( ) == "m_trader" ) then npc_name = npc_obj:name( ) else npc_name = npc_obj:section_name( ) end if ( npc_name ) then if ( npc_name == "esc_m_trader" ) then return true end if ( npc_name == "zat_b30_owl_stalker_trader" ) then return true end end -- Check if the NPC is alive: if ( npc_obj:clsid( ) == clsid.online_offline_group_s ) then return true end if ( (IsStalker( nil, npc_obj:clsid( ) )) and (npc_obj:alive( )) ) then return true end -- Set return value: return false end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_random_chance function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Randomly returns true or false -- - Modification of random_chance (CoC 1.5b r4) -- -- Usage: -- drx_ql_random_chance( p[1] ) -- -- Parameters: -- p[1] (type: int) -- - Percent chance (0 - 100) (optional; default = 50) -- -- Return value (type: bool): -- Randomly returns true or false -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified November 21, 2017 -- ------------------------------------------------------------------------------------------------ -- Randomly return true or false: function drx_ql_random_chance( actor, npc, p ) -- Format percent chance: local percent_chance = 50 if ( (p) and (p[1] ~= nil) ) then percent_chance = tonumber( p[1] ) end -- Check if percent chance is 0: if ( percent_chance <= 0 ) then return false end -- Generate random return: local retval = false if ( math.random( 100 ) <= percent_chance ) then retval = true end -- Set return value: return retval end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_heli_on_level function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Checks if a helicopter exists on the specified level -- -- Usage: -- drx_ql_heli_on_level( p[1] ) -- -- Parameters: -- p[1] (type: string, level name) -- - Name of the level to check for a helicopter -- -- Return value (type: bool): -- Returns true if a helicopter exists on the specified level, false otherwise -- -- Notes: -- - This function will always return true if the actor is not on the specified level -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified December 26, 2017 -- ------------------------------------------------------------------------------------------------ -- Check if helicopter exists on specified level: function drx_ql_heli_on_level( actor, npc, p ) -- Check if the current level is not the specified level: if ( (p) and (p[1] ~= nil) ) then if ( level.name( ) ~= p[1] ) then return true end end -- Check if a helicopter exists on the current level: local retval = heli_exist_on_level( actor, npc ) -- Set return value: return retval end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_has_havfetch_task function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Checks if the player is on a high value fetch item task -- -- Usage: -- drx_ql_has_havfetch_task( p[1] ) -- -- Parameters: -- p[1] (type: string, task name) -- - Name of the task calling this function -- -- Persistent storage: -- {task_id}_fetch (type: object id) -- - Id of requested items for fetch task -- {task_id}_reward (type: object id) -- - Name of task reward item -- -- Return value (type: bool): -- Returns true if the player is on a high value fetch item task, false otherwise -- -- Notes: -- - If a valid havfetch task is found, the fetch item will be stored in pstor var {p[1]}_reward -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified January 08, 2018 -- ------------------------------------------------------------------------------------------------ -- Check if player is on high value fetch item task: function drx_ql_has_havfetch_task( actor, npc, p ) -- Validate input: if ( (#p < 1) or (not p[1]) ) then printf( "DRX QL Error: Unable to determine if actor on havfetch task, invalid parameters supplied" ) return false end -- Verify db.actor is available: if ( not db.actor ) then printf( "DRX QL Error: Unable to determine if actor on havfetch task, db.actor not available" ) return false end -- Reset stored task reward: utils.save_var( db.actor, string.format( "%s_reward", p[1] ), nil ) -- Check if the player is on at least one high value fetch item task: if ( (not drx_ql_task_funcs.drx_ql_havfetch_tasks) or (#drx_ql_task_funcs.drx_ql_havfetch_tasks < 1) ) then return false end -- Build a list of valid havfetch items: local havfetch_items_list = {} for i = 1, ( #drx_ql_task_funcs.drx_ql_havfetch_tasks ) do local current_task = drx_ql_task_funcs.drx_ql_havfetch_tasks[i] local task_info = task_manager.get_task_manager( ).task_info[current_task] if ( task_info ) then if ( task_info.stage < 2 ) then local havfetch_item = utils.load_var( db.actor, string.format( "%s_fetch", current_task ) ) if ( havfetch_item ) then -- table.insert( havfetch_items_list, havfetch_item ) havfetch_items_list[#havfetch_items_list + 1] = havfetch_item end end end end -- Check at least one havfetch item was found: if ( #havfetch_items_list < 1 ) then return false end -- Select random havfetch item from the list to use as task reward: utils.save_var( db.actor, string.format( "%s_reward", p[1] ), havfetch_items_list[math.random( #havfetch_items_list )] ) -- Set return value: return true end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_squad_on_level function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Determines if a specified squad is currently on a specified level -- - Modification of npc_on_level (CoC 1.5b r4) -- -- Usage: -- drx_ql_squad_on_level( p[1], p[2] ) -- -- Parameters: -- p[1] (type: string, section name) -- - Squad name -- p[2] (type: string, section name) -- - Level name -- -- Return value (type: bool): -- Returns true if the squad is on the level, false otherwise -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified January 14, 2018 -- ------------------------------------------------------------------------------------------------ -- Check if squad is on level: function drx_ql_squad_on_level( actor, npc, p ) -- Validate input: if ( #p < 2 ) then printf( "DRX QL Error: Invalid parameters supplied for drx_ql_squad_on_level" ) return false end -- Get the squad local squad = get_story_squad( p[1] ) if ( not squad ) then return false end -- Check if the squad is on the level: if ( alife( ):level_name( game_graph( ):vertex( squad.m_game_vertex_id ):level_id( ) ) ~= p[2] ) then return false end -- Set return value: return true end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_actor_on_revenge_target_level function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Determines if the actor is on the same level as the revenge target -- -- Usage: -- drx_ql_actor_on_revenge_target_level( ) -- -- Parameters: -- p[1] (type: string, task name) -- - Name of the task calling this function -- -- Return value (type: bool): -- Returns true if actor and target are on same level, false otherwise -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified March 27, 2018 -- ------------------------------------------------------------------------------------------------ -- Check if actor is on revenge target level: function drx_ql_actor_on_revenge_target_level( actor, npc, p ) -- Get revenge target id: local revenge_target_id = axr_task_manager.bounties_by_id[p[1]] if ( not revenge_target_id ) then printf( "DRX QL Error: Unable to determine if actor on revenge target level, target id is invalid" ) return false end -- Get the target object: local target_obj = alife( ):object( revenge_target_id ) if ( not target_obj ) then printf( "DRX QL Error: Unable to determine if actor on revenge target level, target object is invalid" ) return false end -- Check if the target is not on the current level: if ( not simulation_objects.is_on_the_same_level( target_obj, alife( ):actor( ) ) ) then return false end -- Set return value: return true end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_actor_near_location function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Determines if the actor is near a smart terrain or special location -- -- Usage: -- drx_ql_actor_near_location( ) -- -- Parameters: -- p[1] (type: string, location name) -- - Smart terrain section name or special location name -- p[2] (type: int, meters) -- - Maximum distance from location -- -- Ini requirements: -- drx\drx_ql_locations.ltx -- [{special_location_name}] -- pos_x (type: float) -- - x-position of the location -- pos_y (type: float) -- - y-position of the location -- pos_z (type: float) -- - z-position of the location -- lvid (type: int, level vertex id) -- - Level vertex id of the location -- gvid (type: int, game vertex id) -- - Game vertex id of the location -- -- Return value (type: bool): -- Returns true if the actor is near the location, false otherwise -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified November 27, 2019 -- ------------------------------------------------------------------------------------------------ -- Check if actor is near location: function drx_ql_actor_near_location( actor, npc, p ) -- Locations ini file: local loc_ini = ini_file( "drx\\drx_ql_locations.ltx" ) if ( not loc_ini ) then printf( "DRX QL: Cannot determine if actor near location, locations ini file not found" ) return false end -- Validate input: if ( (not p) or (#p < 2) ) then printf( "DRX QL Error: Cannot determine if actor near location, invalid parameters supplied" ) return false end -- Ensure db.actor available: if ( not db.actor ) then return false end -- Get location info: local pos_x = 0 local pos_y = 0 local pos_z = 0 local lvid = 0 local gvid = 0 local smart = SIMBOARD.smarts_by_names[p[1]] if ( smart ) then pos_x = smart.position.x pos_y = smart.position.y pos_z = smart.position.z lvid = smart.m_level_vertex_id gvid = smart.m_game_vertex_id else pos_x = (loc_ini:r_float_ex( p[1], "pos_x" ) or 0) pos_y = (loc_ini:r_float_ex( p[1], "pos_y" ) or 0) pos_z = (loc_ini:r_float_ex( p[1], "pos_z" ) or 0) lvid = (loc_ini:r_float_ex( p[1], "lvid" ) or 0) gvid = (loc_ini:r_float_ex( p[1], "gvid" ) or 0) end -- Check if actor on same level: if ( alife( ):level_name( game_graph( ):vertex( gvid ):level_id( ) ) ~= level.name( ) ) then return false end -- Check if actor near location: local pos = vector( ):set( pos_x, pos_y, pos_z ) if ( db.actor:position( ):distance_to_sqr( pos ) < (tonumber( p[2] ) * tonumber( p[2] )) ) then return false end -- Set return value: return true end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_npc_squad_size function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Checks if the size of the squad the actor is speaking to is within specified range -- -- Usage: -- drx_ql_npc_squad_size( p[1], p[2] ) -- -- Parameters: -- p[1] (type: int) -- - Minimum squad size -- p[2] (type: int) -- - Maximum squad size (optional; if omitted squad size must equal p[1] to return true) -- -- Persistent storage: -- drx_ql_current_npc_speaker_id (type: npc id) -- - Id of the current npc the actor is speaking to -- -- Return value (type: bool): -- Returns true if speaker npc squad size is within range, false otherwise -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified April 27, 2019 -- ------------------------------------------------------------------------------------------------ -- Check speaker npc squad size: function drx_ql_npc_squad_size( actor, npc, p ) -- Validate input: if ( (not p) or (#p < 1) or (p[1] == nil) ) then printf( "DRX QL Error: Unable to determine npc squad size, invalid parameters supplied" ) return false end -- Get the id of the npc the actor is currently speaking with: local npc_id = utils.load_var( db.actor, "drx_ql_current_npc_speaker_id", nil ) if ( npc_id == nil ) then printf( "DRX QL Error: Unable to determine npc squad size, could not get id of current speaker" ) return false end -- Get speaker squad: local speaker_obj = alife( ):object( npc_id ) if ( not speaker_obj ) then printf( "DRX QL Error: Unable to determine npc squad size, task giver is invalid" ) return false end local squad = get_object_squad( speaker_obj ) if ( not squad ) then printf( "DRX QL Error: Unable to determine npc squad size, could not get speaker squad" ) return false end -- Check each squad member: local squad_size = 0 for member in ( squad:squad_members( ) ) do local member_obj = (member.object or (member.id and alife( ):object( member.id ))) if ( member_obj and member_obj:alive( ) ) then squad_size = (squad_size + 1) end end -- Check if squad size in range: if ( (#p < 2) or (p[2] == nil) ) then if ( squad_size ~= p[1] ) then return false end else local min_count = p[1] local max_count = p[2] if ( min_count > max_count ) then min_count = p[2] max_count = p[1] end if ( (squad_size < min_count) or (squad_size > max_count) ) then return false end end -- Set return value: return true end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_min_stingrays_done function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Checks whether or not the minimum number of Stingray missions have been completed -- -- Usage: -- drx_ql_min_stingrays_done( ) -- -- Parameters: -- none -- -- Ini requirements: -- drx\drx_ql_config.ltx -- [operation_fairway_props] -- stingray_locations (type: int) -- - Number of Stingray crash sites -- min_sites (type: int) -- - Minimum number of Stingray crash sites to visit before final Stingray mission -- -- Return value (type: bool): -- Returns true if minimum number of Stingray missions have been completed, false otherwise -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified October 29, 2019 -- ------------------------------------------------------------------------------------------------ -- Check Stingray completed missions count: function drx_ql_min_stingrays_done( actor, npc, p ) -- Location of the settings file: local ini = ini_file( "drx\\drx_ql_config.ltx" ) if ( not ini ) then printf( "DRX QL Error: Unable to check Stingray tasks completed count, cannot locate ini file" ) return false end -- Get mission counts: local total_missions = (ini:r_float_ex( "operation_fairway_props", "stingray_locations" ) or 0) local min_sites = (ini:r_float_ex( "operation_fairway_props", "min_sites" ) or 0) if ( min_sites > (total_missions - 1) ) then min_sites = (total_missions - 1) end -- Get count of Stingray missions completed: local completed_count = 0 for i = 1, ( total_missions ) do if ( has_alife_info( string.format( "drx_ql_info_stingray_%s_done", i ) ) ) then completed_count = (completed_count + 1) end end -- Check if requested count matched or exceeded: if ( completed_count >= min_sites ) then return true end -- Set return value: return false end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ --[[ modified by Alundaio (original: ???? GSC) -]] -- used by tasks in precondition to randomly have it available to give the illusion to player that sim stalkers only sometimes have task function random_chance(actor,npc,p) local r = p and p[1] and tonumber(p[1]) or 50 if (math.random(1,100) < r) then return true end return false end -- checks if a task has stage of equal value -- param 1 - task_id -- param 2 - value function check_task_stage(actor,npc,p) local tm = task_manager.get_task_manager() local task_info = tm.task_info if (task_info[p[1]] and task_info[p[1]].stage == tonumber(p[2])) then --printf("p[1] = %s stage=%s",p[1],p[2]) return true end return false end function is_bounty(actor,npc,p) local id = npc:id() for task_id,npc_id in pairs(axr_task_manager.bounties_by_id) do if (npc_id == id) then return true end end return false end function npc_not_actor(actor,npc,p) return npc:id() ~= 0 or false end function dist_to_task_target_anomaly_le(actor,npc,p) local d = p[1] if not (d) then return false end local name = db.actor and utils.load_var(db.actor,"task_target_anomaly") if not (name) then return false end local anomaly = db.anomaly_by_name[name] if not (anomaly and anomaly.object) then return false end return heli_alife.distance_2d_sqr(npc:position(),anomaly.object:position()) <= tonumber(d)^2 end function has_task_target_anomaly(actor,npc,p) return db.actor and utils.load_var(db.actor,"task_target_anomaly") ~= nil end function actor_has_companion_slot(actor,npc,p) return db.actor and utils.load_var(db.actor,p[1] or "task_companion_slot_1") == nil end --param 1 dist --param 2 smart_name (optional) function check_nearest_smart_dist_le(actor,npc,p) if (smart_terrain.nearest_to_actor_smart.id and smart_terrain.nearest_to_actor_smart.dist > tonumber(p[1])) then return false end local smart = p[2] and SIMBOARD.smarts_by_names[p[2]] if (smart and smart_terrain.nearest_to_actor_smart.id and smart.id == smart_terrain.nearest_to_actor_smart.id) then return true end return false end function check_smart_dist_le(actor,npc,p) local smart = p[2] and SIMBOARD.smarts_by_names[p[2]] if (smart and smart.position:distance_to(npc:position()) <= (tonumber(p[1]) or 0)) then return true end return false end ----------------------- -- Task ----------------------- function has_task(actor,npc,p) local task_info = p[1] and task_manager.get_task_manager().task_info[p[1]] if not (task_info) then return false end return p[2] == nil or task_info.status == p[2] end function has_task_on_stage(actor,npc,p) local task_info = p[1] and task_manager.get_task_manager().task_info[p[1]] if not (task_info) then return false end return task_info.stage == (tonumber(p[2] or 0) or 0) end function has_task_not_completed(actor,npc,p) local task_info = p[1] and task_manager.get_task_manager().task_info[p[1]] if not (task_info) then return false end return task_info.status ~= "completed" and task_info.status ~= "fail" end function has_completed_task_prerequisites(actor,npc,p) if not (p[1]) then return true end local precond = task_manager.task_ini:r_string_ex(p[1],"precondition") if (precond and precond ~= "") then precond = xr_logic.parse_condlist(npc, p[1], "precondition", task_manager.task_ini:r_string_ex(p[1],"precondition") or "true") if (precond == "false") then return false end if (db.actor and xr_logic.pick_section_from_condlist(db.actor, npc, precond) == "false") then return false end end local prereq = task_manager.task_ini:r_string_ex(p[1],"prerequisites") if (prereq == nil or prereq == "") then return true end local tm = task_manager.get_task_manager() local info = tm.task_info if (info[p[1]]) then -- HAS TASK ALREADY! return false end prereq = alun_utils.str_explode(prereq,",") local tsk for i=1,#prereq do tsk = prereq[i] if not (info[tsk]) then --printf("no tsk %s",tsk) return false end if (info[tsk].status ~= "completed" and info[tsk].status ~= "fail") then --printf("task %s not completed %s",task,info[tsk].status) return false end end return true end function dist_to_lvid_ge(actor,npc,p) local vid = tonumber(p[1]) if not (vid) then return false end local position = level.vertex_position(vid) if (npc:position():distance_to_sqr(position) >= tonumber(p[2])) then return true end return false end function dist_to_job_point_le(actor, npc, p) if not (npc) then return false end local smart = xr_gulag.get_npc_smart(npc) if smart == nil then return false end local npc_info = smart.npc_info[npc:id()] if npc_info then local npc_job = npc_info.job if (npc_job) then local alife_task = npc_job.alife_task if alife_task then return npc:position():distance_to_sqr(alife_task:position()) <= p[1]*p[1] end end end return false end function sim_avail(actor,npc,p) local smart = p[1] and SIMBOARD.smarts_by_names[p[1]] if not (smart) then return false end return smart.sim_avail == nil or smart.sim_avail and xr_logic.pick_section_from_condlist(actor, smart, smart.sim_avail) == "true" or false end -- param 1 = smart -- param 2 = section logic (omit 'logic@') -- param 3 = distance function dist_to_obj_on_job_le(actor,npc,p) if not (npc) then return false end local board = SIMBOARD local smart = p[1] and board and board.smarts_by_names[p[1]] if not (smart) then return false end local obj = smart and smart.npc_by_job_section["logic@"..p[2]] obj = obj and (db.storage[obj] and db.storage[obj].object or level.object_by_id(obj)) if not (obj) then return false end return npc:position():distance_to_sqr(obj:position()) <= tonumber(p[3])^2 end function dist_to_job_point_ge(actor, npc, p) if not (npc) then return false end local smart = xr_gulag.get_npc_smart(npc) if smart == nil then return false end local npc_info = smart.npc_info[npc:id()] if npc_info then local npc_job = npc_info.job if (npc_job) then local alife_task = npc_job.alife_task if alife_task then return npc:position():distance_to_sqr(alife_task:position()) >= p[1]*p[1] end end end return false end -- Каждая функция в этом файле используется как условие xr_logic: {=функция !функция} -- Если в функцию необходимо передавать параметры, то: {=функция(парам1:парам2:...) !функция(парам1:парам2:...)} -- Формат: function f(actor, npc). В случае передачи параметров: function f(actor, npc, p). -- ---------------------------------------------------------------------------------------------------- -- Functions for working specifically with combat_ignore_cond -- ---------------------------------------------------------------------------------------------------- -- Match name against all given params (ex. {=check_enemy_name(name1:name2:name3:name4)}) function check_enemy_name (enemy , npc , p) local name if enemy and enemy:alive() then name = enemy:name() for i, v in pairs(p) do if string.find( name, v ) ~= nil then return true end end end return false end function is_enemy_fighting_actor(enemy,npc) if (enemy:alive() and xr_combat_ignore.fighting_with_actor_npcs[enemy:id()]) then return true end return false end function is_enemy_actor(enemy, npc) return enemy:id() == 0 end function is_enemy_actor_or_companion(enemy,npc) if (IsMonster(enemy)) then return false end if not (npc:relation(enemy) >= game_object.enemy) then return false end local id = enemy:id() if (id == 0) then return true end if (alife():has_info(id,"npcx_is_companion")) then return true end return false end -- current enemy at a distance greater than or equal to the specified Distance -- для combat_ignore -- param 1 - distance^2 function fighting_dist_ge(enemy, npc, p) return enemy:alive() and enemy:position():distance_to_sqr(npc:position()) >= p[1]^2 or false end -- param 1 - distance^2 function fighting_dist_le(enemy, npc, p) return enemy:alive() and enemy:position():distance_to_sqr(npc:position()) <= p[1]^2 or false end -- param 1 zone name function enemy_in_zone(enemy, npc, p) return utils.npc_in_zone(enemy, p[1]) or false end -- param 1 - Community to match function is_enemy_community(enemy,npc,p) return enemy:alive() and character_community(enemy) == p[1] end -- Check for membership to one of the enemy groups (simboard.group_id_by_levels) -- Each Squad spawned is given a group id according to level -- All squads on a certain level will have matching level group ids -- (You can check more than one) function enemy_group(enemy, npc, p) if not (enemy:alive()) then return false end local g = enemy:group() for i, v in pairs(p) do if v == g then --printf("_bp: [%s]'s enemy is from group [%d]", npc:name(), v) return true end end return false end -- see above function is_enemy_same_group(actor,npc) local st = db.storage[npc:id()] local enemy = st and st.enemy_id and (db.storage[st.enemy_id] and db.storage[st.enemy_id].object or level.object_by_id(st.enemy_id)) if (not enemy:alive()) then return false end return enemy:group() == npc:group() end ------------------------------- -- Conditions for enemies before combat_ignore_cond is checked ------------------------------- function see_pure_enemy(actor,npc) local st = db.storage[npc:id()] local enemy = st and st.enemy_id and db.storage[st.enemy_id] and db.storage[st.enemy_id].object return enemy and enemy:alive() and npc:see(enemy) or false end function pure_enemy_dist_le(actor,npc,p) local st = db.storage[npc:id()] local enemy = st and st.enemy_id and db.storage[st.enemy_id] and db.storage[st.enemy_id].object return enemy and enemy:alive() and enemy:position():distance_to_sqr(npc:position()) <= p[1]^2 or false end -- Returns true if a NPC's pure enemy (before ignore_cond) occupies a job by section for a given smart -- param 1 is the job section (ie. "logic@walker1") -- param 2 is the smart name. If it's empty it will default to calling NPC's smart function is_enemy_on_job(actor, npc, p) local st = db.storage[npc:id()] local enemy = st and st.enemy_id and db.storage[st.enemy_id] and db.storage[st.enemy_id].object if (enemy == nil or not enemy:alive()) then return false end local smart if p and p[2] then smart = SIMBOARD:get_smart_by_name(p[2]) else smart = xr_gulag.get_npc_smart(enemy) end if smart == nil then return false end for id,npc_info in pairs(smart.npc_info) do if (npc_info.job and npc_info.job.section == p[1]) then return true end end return false end -- Returns true if enemy's smart equals the given smart -- param 1 is the smart name function enemy_smart(actor, npc, p) local st = db.storage[npc:id()] local enemy = st and st.enemy_id and db.storage[st.enemy_id] and db.storage[st.enemy_id].object if (enemy == nil or not enemy:alive()) then return false end local smart = xr_gulag.get_npc_smart(enemy) if not (smart) then return false end return p and smart:name() == p[1] end function has_pure_enemy(actor, npc) local st = db.storage[npc:id()] local enemy = st and st.enemy_id and db.storage[st.enemy_id] and db.storage[st.enemy_id].object return enemy and enemy:alive() or false end -- param 1 - Enemy's Time-in-memory less_than_equal_to function pure_enemy_mem_time_le(actor, npc, p) local st = db.storage[npc:id()] local enemy = st and st.enemy_id and (db.storage[st.enemy_id] and db.storage[st.enemy_id].object or level.object_by_id(st.enemy_id)) return p and p[1] and enemy and enemy:alive() and npc:memory_time(enemy) <= tonumber(p[1]) or false end -- ---------------------------------------------------------------------------------------------------- -- Общие функции -- ---------------------------------------------------------------------------------------------------- -- видим ли мы еще "черный экран" или нет? function black_screen(actor, npc) return device().precache_frame > 1 end --Проверка, встречается ли в имени чувака одна из строк . function check_npc_name (actor , npc , p) if npc:name() == nil then return false end for k,v in pairs(p) do if string.find( npc:name(), v ) then return true end end return false end function check_squad_name (actor , npc , p) local squad = get_object_squad(npc) if not (squad) then return end local sq for k,v in pairs(p) do sq = get_story_squad(v) if (sq and sq.id == squad.id) then return true end end return end function is_playing_sound (actor, npc) return xr_sound.sound_table[npc:id()] ~= nil end -- проверка, что актер жив function actor_alive(actor, npc) if db.actor and db.actor:alive() then return true end return false end function see_npc(actor, npc, p) local npc1 = get_story_object(p[1]) if npc and npc1 then --printf("cond : [%s]->[%s]", npc:name(), npc1:name()) return npc:see(npc1) else return false end end function actor_see_npc(actor, npc) return db.actor and db.actor:see(npc) end function npc_in_actor_frustum(actor, npc) return npc_in_actor_frustrum(npc) end function is_wounded(actor, npc) return xr_wounded.is_wounded(npc) end function dist_to_actor_le(actor, npc, p) local d = p[1] if d == nil then printf("Wrong parameter in 'dist_to_actor_le' function!!!") end return d and npc:position():distance_to_sqr(actor:position()) <= tonumber(d)^2 end function dist_to_actor_ge(actor, npc, p) local d = p[1] if d == nil then printf("Wrong parameter in 'dist_to_actor_ge' function!!!") end return d and npc:position():distance_to_sqr(actor:position()) >= tonumber(d)^2 end -- проверка того что npc находится в заданной зоне -- !!! ВЫЗЫВАТЬ ТОЛЬКО ИЗ SPACE RESTRICTOR !!! -- параметры: [sid1:sid2:...] -- !!! НЕКОРРЕКТНО РАБОТАЕТ ДЛЯ ОБЬЕКТОВ В offline'e !!! -- !!! ДЛЯ ГАРАНТИИ ИСПОЛЬЗОВАТЬ one_obj_in_zone !!! function obj_in_zone(actor, zone, p) local sim = alife() for i, v in pairs(p) do local id = get_story_object_id(v) local se_obj = id and alife():object_by_id(id) if se_obj and zone:inside(se_obj.position) then return true end end return false end -- checks if any npc who has jobs at a given smart is in a given zone -- param 1 - smart name -- param 2 - zone name function check_npc_from_gulag_in_zone(actor,npc,p) local smart = p[1] and SIMBOARD.smarts_by_names[p[1]] if not (smart) then return false end local zone = p[2] and db.zone_by_name[p[2]] if not (zone) then return false end for id,se_obj in pairs(smart.npc_info) do if (se_obj.position and zone:inside(se_obj.position)) then return true end end return false end -- параметры: [sid:def*] def=true|false -- * параметр не обязателен function one_obj_in_zone(actor, zone, p) --local def_offline = (p[2] ~= "false") -- default (true) result if npc in offline local id = get_story_object_id(p[1]) local se_obj = id and alife_object(id) if se_obj then -- npc is online return zone:inside(se_obj.position) else -- npc is offline return (p[2] ~= "false") -- default (true) result if npc in offline end end function story_obj_in_zone_by_name (actor, npc, p) local id = get_story_object_id(p[1]) local se_obj = id and alife_object(id) local zone = db.zone_by_name[p[2]] if se_obj and zone then -- npc is online return zone:inside(se_obj.position) end return false end function actor_inside_me(actor,zone,p) return zone:inside(actor:position()) end function actor_in_zone(actor, npc, p) if not (db.actor) then return false end for i=1,#p do local zone = db.zone_by_name[p[i]] if (zone and zone:inside(db.actor:position())) then return true end end return false end function actor_out_zone(actor, npc, p) return not utils.npc_in_zone(db.actor, p[1]) end function npc_in_zone(actor, npc, p) if not (npc and type(npc.position) == "function") then return false end for i=1,#p do local zone = db.zone_by_name[p[i]] if (zone and zone:inside(npc:position())) then return true end end return false end -- true, если здоровье npc <= заданному значению -- false в противном случае function health_le(actor, npc, p) return p[1] and npc.health < p[1] end -- true, если здоровье актера <= заданному значению -- false в противном случае function actor_health_le(actor, npc, p) return p[1] and actor.health < p[1] end -- true, если здоровье вертолёта <= заданному значению -- false в противном случае function heli_health_le(actor, obj, p) return p[1] and obj:get_helicopter():GetfHealth() < p[1] end -- видит ли вертолёт npc (по story id) function heli_see_npc(actor, obj, p) if p[1] then local o = get_story_object( p[1] ) return o ~= nil and obj:get_helicopter():isVisible( o ) else return false end end function heli_see_actor(actor, obj) return actor ~= nil and obj:get_helicopter():isVisible( actor ) end function gulag_state(actor, npc, p) if xr_gulag.getGulagState(p[1]) == p[2] then return true end return false end function npc_community(actor, npc, p) local npc_obj if p[1] == nil then printf("Wrong number of params in npc_community") end if type(npc.id) ~= "function" then npc_obj = db.storage[npc.id] and db.storage[npc.id].object if npc_obj == nil then return npc:community() == p[1] end else npc_obj = npc end if character_community(npc_obj) == p[1] then return true end return false end function npc_rank(actor, npc, p) if p[1] == nil then printf("Wrong number of params in npc_rank") end if ranks.get_obj_rank_name(npc) == p[1] then return true end return false end function npc_profile(actor, npc, p) if p[1] == nil then printf("Wrong number of params in npc_profile") end if npc:profile_name() == p[1] then return true end return false end -- Проверка того что удар был нанесен кем-то из npc указанных в списке. -- Параметры это story_id персонажей. Можно задавать несколько story_id. function hitted_by(actor, npc, p) local npc1 local t = db.storage[npc:id()].hit if t then for i, v in pairs(p) do npc1 = get_story_object(v) if npc1 and t.who == npc1:id() then return true end end end return false end -- Функция проверки попадания в кость по её индексу.(Проверка только для секции hit) function hitted_on_bone(actor, npc, p) for k,v in pairs (p) do if db.storage[npc:id()].hit.bone_index == npc:get_bone_id(v) then return true end end return false end -- Проверка, что лучшее оружие персонажа - пистолет function best_pistol(actor, npc) return npc:item_in_slot(1) ~= nil end -- Проверка, что персонажу нанесли смертельный хит. Проверять ТОЛЬКО на on_hit function deadly_hit(actor, npc) if db.storage[npc:id()] == nil or db.storage[npc:id()].hit == nil then --printf("deadly hit false") return false end --printf("deadly hit [%s]", tostring(db.storage[npc:id()].hit.deadly_hit == true)) return db.storage[npc:id()].hit.deadly_hit == true end -- Проверка того что персонаж был убит кем-то из npc указанных в списке. -- Параметры это story_id персонажей. Можно задавать несколько story_id. function killed_by(actor, npc, p) local npc1 local t = db.storage[npc:id()].death if t then for i, v in pairs(p) do npc1 = get_story_object(v) if npc1 and t.killer == npc1:id() then --printf("_bp: killed_by(%d)", v) return true end end end return false end -- проверка (по story_id) все ли проверяемые сталкеры живы -- TODO: исправить ситуацию, когда выдается неправильный результат для обьектов, которые -- не успели проспавниться. function is_alive_all(actor, npc, p) local sim = alife() for i, v in pairs(p) do local id = get_story_object_id(v) local se_obj = id and sim:object(id) if se_obj and (not IsStalker(nil,se_obj:clsid()) or not se_obj:alive()) then return false end end return true end -- проверка (по story_id) того, что чотя бы один из проверяемых сталкеров жив -- TODO: исправить ситуацию, когда выдается неправильный результат для обьектов, которые -- не успели проспавниться. function is_alive_one(actor, npc, p) local sim = alife() for i, v in pairs(p) do local id = get_story_object_id(v) local se_obj = id and sim:object(id) if se_obj and IsStalker(nil,se_obj:clsid()) and se_obj:alive() then return true end end return false end -- проверка (по story_id) того, что проверяемыq npc жив -- TODO: исправить ситуацию, когда выдается неправильный результат для обьектов, которые -- не успели проспавниться. function is_alive(actor, npc, p) local id if npc == nil or (p and p[1]) then id = get_story_object_id(p[1]) elseif (type(npc.id) == "number") then id = npc.id else id = npc:id() end if npc1 == nil then return false end local se_obj = id and alife_object(id) if se_obj and IsStalker(nil,se_obj:clsid()) and se_obj:alive() then return true end return false end -- проверка (по story_id) все ли проверяемые сталкеры мертвы -- TODO: исправить ситуацию, когда выдается неправильный результат для обьектов, которые -- не успели проспавниться. function is_dead_all(actor, npc, p) local npc1 for i, v in pairs(p) do npc1 = get_story_object(v) if npc1 then if npc1:alive() then return false else --printf("_bp: is_dead_all(%d) = true", v) return true end end return false end return true end -- проверка (по story_id) того, что хотя бы один из проверяемых сталкеров мертв -- TODO: исправить ситуацию, когда выдается неправильный результат для обьектов, которые -- не успели проспавниться. function is_dead_one(actor, npc, p) local npc1 for i, v in pairs(p) do npc1 = get_story_object(v) if not npc1 or not npc1:alive() then --printf("_bp: is_dead_one(%d) = true", v) return true end end return false end -- проверка (по story_id) того, что хотя бы один из проверяемых сталкеров мертв -- TODO: исправить ситуацию, когда выдается неправильный результат для обьектов, которые -- не успели проспавниться. function is_dead(actor, npc, p) local npc1 npc1 = get_story_object(p[1]) return not npc1 or not npc1:alive() end -- Проверяет , существует ли обьект с заданным стори айди. function story_object_exist(actor, npc, p) local npc1 = get_story_object(p[1]) return npc1 ~= nil end --[[ -- проверка (по story_id) того, что нашим врагом есть хотя бы кото-то один из списка function check_fighting(actor, npc, p) local enemy_id = db.storage[npc:id()].enemy_id local enemy = db.storage[enemy_id] and db.storage[enemy_id].object local sid if enemy and enemy:alive() then sid = enemy:story_id() for i, v in pairs(p) do --printf("_bp: %s.check_fighting(%d)", npc:name(), v) if type(v) == 'number' and sid == v then --printf("TRUE") return true end end end --printf("_bp: check_fighting() = false") return false end ]]-- -- true, если у актёра в инвентаре есть указанный предмет -- false, если нету, либо не задана секция предмета function actor_has_item(actor, npc, p) local story_actor = get_story_object("actor") return p[1] ~= nil and story_actor and story_actor:object( p[1] ) ~= nil end function npc_has_item(actor, npc, p) return p[1] ~= nil and npc:object( p[1] ) ~= nil end -- проверяет наличие заданого количества предметов в инвентаре игрока. function actor_has_item_count(actor, npc, p) local item_section = p[1] local need_count = tonumber(p[2]) local has_count = 0 local function calc(temp, item) --printf("item [%s]",tostring(item:section())) if item:section() == item_section then has_count = has_count + 1 end end actor:iterate_inventory(calc, actor) return has_count >= need_count end -- возвращает true, если в текущей схеме персонажа взведён указанный сигнал. function signal(actor, npc, p) if p[1] then local st = db.storage[npc:id()] local sigs = st[st.active_scheme].signals -- printf( "xr_conditions.signal: npc=%s, scheme=%s", npc:name(), tostring(st.active_scheme) ) return sigs ~= nil and sigs[p[1]] == true else return false end end -- возвращает true, если значение указанного счётчика актёра больше указанного числа function counter_greater(actor, npc, p) if p[1] and p[2] then local c = utils.load_var(actor, p[1], 0) -- if c > p[2] then printf("pl: counter [%s] greater [%s]", p[1], p[2]) end return c > p[2] else return false end end function counter_equal(actor, npc, p) if p[1] and p[2] then local c = utils.load_var(actor, p[1], 0) return c == p[2] else return false end end --[[ -- определяет нечётность интервала игрового времени. интервал нужно передать в p[1] function odd_time_interval(actor, npc, p) return odd( game.time() / p[1] ) end ]]-- ------------------------------------------------------------------------------------------------------- -- Функции поддержки kamp function _kamp_talk(actor, npc) if xr_kamp.kamp_stalkers[npc:id()] then return xr_kamp.kamp_stalkers[npc:id()] end return false end function _used(actor, npc) return npc:is_talking() end ------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------- -- Функции поддержки безопасности баз function check_smart_alarm_status(actor, npc, p) local smart = p[1] and SIMBOARD:get_smart_by_name(p[1]) return smart and smart:check_alarm() or false end ------------------------------------------------------------------------------------------------------- function has_enemy(actor, npc) local be = npc:best_enemy() return be and be:alive() or false end function has_actor_enemy(actor, npc) local best_enemy = npc:best_enemy() return db.actor and best_enemy ~= nil and best_enemy:id() == db.actor:id() end function see_enemy(actor, npc) local enemy = npc:best_enemy() if enemy ~= nil then return npc:see(enemy) end return false end function has_enemy_in_current_loopholes_fov(actor, npc) return npc:in_smart_cover() and npc:best_enemy() ~= nil and npc:in_current_loophole_fov( npc:best_enemy():position()) or false end function talking(actor, npc) return actor:is_talking() end function npc_talking(actor, npc) return npc:is_talking() end function see_actor(actor, npc) return npc:alive() and npc:see(actor) end function actor_enemy(actor, npc) local t = db.storage[npc:id()].death return npc:relation(actor) >= game_object.enemy or (t ~= nil and t.killer == actor:id()) end function actor_friend(actor, npc) return npc:relation(actor) == game_object.friend end function actor_neutral(actor, npc) return npc:relation(actor) == game_object.neutral end function is_factions_enemies(actor, npc, p) if(p[1] and p[2]) then return game_relations.is_factions_enemies(p[1], p[2]) else return false end end function is_factions_neutrals(actor, npc, p) return not(is_factions_enemies(actor, npc, p) or is_factions_friends(actor, npc, p)) end function is_factions_friends(actor, npc, p) if(p[1] and p[2]) then return game_relations.is_factions_friends(p[1], p[2]) else return false end end function is_faction_enemy_to_actor(actor, npc, p) if(p[1]~=nil) then -- return db.actor:community_goodwill(p[1])<-1000 return db.actor and relation_registry.community_goodwill(p[1], db.actor:id())<=-1000 else return false end end function is_faction_friend_to_actor(actor, npc, p) if(p[1]~=nil) then -- return db.actor:community_goodwill(p[1])>1000 return db.actor and relation_registry.community_goodwill(p[1], db.actor:id())>=1000 else return false end end function is_faction_neutral_to_actor(actor, npc, p) return not(is_faction_enemy_to_actor(actor, npc, p) or is_faction_friend_to_actor(actor, npc, p)) end function is_squad_friend_to_actor(actor, npc, p) if(p[1]~=nil) then -- printf("squad check goodwill1 [%s]", p[1]) return game_relations.check_all_squad_members(p[1], "friend") else -- printf("squad check goodwill5 [%s]", p[1]) return false end end function is_squad_enemy_to_actor(actor, npc, p) if not p then printf("Not enough arguments in 'is_squad_enemy_to_actor' funciton!") end for k,v in pairs(p) do -- printf("squad check goodwill1 [%s]", v) if game_relations.check_all_squad_members(v, "enemy") then return true end end return false end function is_squad_neutral_to_actor(actor, npc, p) return not(is_squad_enemy_to_actor(actor, npc, p) or is_squad_friend_to_actor(actor, npc, p)) end -- текущий враг актёр? function fighting_actor(actor, npc) local enemy_id = db.storage[npc:id()].enemy_id local enemy = db.storage[enemy_id] and db.storage[enemy_id].object return enemy and enemy:id() == actor:id() or false end function hit_by_actor(actor, npc) local t = db.storage[npc:id()].hit local hit_by_actor = (t ~= nil and t.who == actor:id()) return hit_by_actor end function killed_by_actor(actor, npc) local t = db.storage[npc:id()].death local killed_by_actor = t ~= nil and t.killer == actor:id() return killed_by_actor end function actor_has_weapon (actor, npc) local obj = actor:active_item() if (obj and IsWeapon(obj)) then return true end return false end function actor_active_detector(actor, npc, p) local detector_section = p and p[1] if detector_section == nil then printf("Wrong parameters in function 'actor_active_detector'") end local actor_detector = db.actor and db.actor:active_detector() return (actor_detector ~= nil) and actor_detector:section() == detector_section end function heavy_wounded(actor, npc) return xr_wounded.is_heavy_wounded_by_id( npc:id() ) end --[[ Проверка на заданный период времени Время задается в минутах Параметры: (time_shift:period_min) time_shift - периодичность срабатывания period - период срабатывания на котором мы получаем true Примеры: time_period(60:10) - возвращает true каждый час на протяжении первых 10 минут --]] function time_period(actor, npc, p) local tshift, period = p[1], p[2] if tshift ~= nil and period ~= nil and db.actor ~= nil then return tshift > period and level.get_time_minutes() % tshift <= period end return false end function is_rain (actor, npc) return db.actor ~= nil and level.rain_factor() > 0 end function is_heavy_rain (actor, npc) return db.actor ~= nil and level.rain_factor() >= 0.5 end function is_day (actor, npc) return db.actor ~= nil and level.get_time_hours() >= 6 and level.get_time_hours() < 21 end function is_dark_night (actor, npc) return db.actor ~= nil and (level.get_time_hours() < 3 or level.get_time_hours() > 22) end function is_night (actor, npc) return db.actor ~= nil and (level.get_time_hours() < 5 or level.get_time_hours() > 19) end function is_jup_a12_mercs_time (actor, npc) return db.actor ~= nil and (level.get_time_hours() >= 1 and level.get_time_hours() < 5) end function zat_b7_is_night (actor, npc) return db.actor ~= nil and (level.get_time_hours() >= 23 or level.get_time_hours() < 5) end function zat_b7_is_late_attack_time (actor, npc) return db.actor ~= nil and (level.get_time_hours() >= 23 or level.get_time_hours() < 9) end function mob_has_enemy(actor, npc) -- return false if npc == nil then return false end --if npc:get_enemy () then printf ("ENEMY PRESENT") else printf ("ENEMY NOT PRESENT") end return npc:get_enemy() ~= nil end function mob_was_hit(actor, npc) local h = npc:get_monster_hit_info() if h.who and h.time ~= 0 then return true end return false end function actor_on_level(actor, npc, p) for k,v in pairs (p) do --printf("level name: [%s], needed level name: [%s]", level.name(), v`) if v == level.name() then return true end end return false end function npc_on_level(actor, npc, p) local sim = alife() local se_obj = p[1] and npc and (type(npc.id) == "function" and sim:object(npc:id()) or npc) if not (se_obj) then return false end local gg = game_graph() if (sim:level_name(gg:vertex(se_obj.m_game_vertex_id):level_id()) == p[1]) then return true end return false end function treasure_exist(actor, npc, p) --printf("%s %s", actor:name(), npc:name()) return true end --[[ --'----------------------------------------------------------------------------------- --' Cover support --'----------------------------------------------------------------------------------- --' Если возвращает true, то солдаты за каверами начинают стрелять. function cover_attack(actor, npc) --' Берем сквад объекта local squad = get_object_squad(npc) if squad == nil then return false end return squad:cover_attack() end ]]-- --'----------------------------------------------------------------------------------- --' Squad support --'----------------------------------------------------------------------------------- function squad_in_zone(actor, npc, p) local story_id = p[1] local zone_name = p[2] if story_id == nil then printf("Insufficient params in squad_in_zone function. story_id[%s], zone_name[%s]", tostring(story_id), tostring(zone_name)) end if zone_name == nil then zone_name = npc:name() end local sim_board = SIMBOARD local squad = get_story_squad(story_id) if squad == nil then --printf("There is no squad with id[%s]", tostring(squad_id)) return false end local zone = db.zone_by_name[zone_name] if zone == nil then --printf("There is no squad with id[%s]", tostring(zone_name)) return false end local sim = alife() for k in squad:squad_members() do local se_obj = k.id and sim:object(k.id) if (se_obj and zone:inside(se_obj.position)) then return true end end return false end function squad_has_enemy(actor, npc, p) local story_id = p[1] if story_id == nil then printf("Insufficient params in squad_has_enemy function. story_id [%s]", tostring(story_id)) end local squad = get_story_squad(story_id) if squad == nil then --printf("There is no squad with id[%s]", tostring(story_id)) return false end for k in squad:squad_members() do local npc_obj = k.id and db.storage[k.id] and db.storage[k.id].object if (npc_obj and npc_obj:best_enemy() ~= nil) then return true end end return false end -- Functions for Yantar function squad_in_zone_all(actor, npc, p) local story_id = p[1] local zone_name = p[2] if story_id == nil or zone_name == nil then printf("Insufficient params in squad_in_zone_all function. story_id[%s], zone_name[%s]", tostring(story_id), tostring(zone_name)) end local squad = get_story_squad(story_id) if squad == nil then --printf("There is no squad with id[%s]", tostring(story_id)) return false end local zone = db.zone_by_name[zone_name] if zone == nil then --printf("There is no squad with id[%s]", tostring(zone_name)) return false end local sim = alife() for k in squad:squad_members() do local se_obj = k.object or k.id and sim:object(k.id) if (se_obj and zone:inside(se_obj.position)) then return true end end return true end function squads_in_zone_b41(actor, npc, p) local smart = SIMBOARD:get_smart_by_name("jup_b41") local zone = db.zone_by_name["jup_b41_sr_light"] local al = alife() if zone == nil then return false end if smart == nil then return false end for k,v in pairs(SIMBOARD.smarts[smart.id].squads) do if v ~= nil then for j in v:squad_members() do if not zone:inside(j.object.position) then return false end end end end return true end function squads_in_zone_yan_smart_terrain_6_4(actor, npc, p) local smart = SIMBOARD:get_smart_by_name("yan_smart_terrain_6_4") local zone = db.zone_by_name["yan_smart_terrain_6_4_sr_light"] local al = alife() if zone == nil then return false end if smart == nil then return false end for k,v in pairs(SIMBOARD.smarts[smart.id].squads) do if v ~= nil then for j in v:squad_members() do if not zone:inside(j.object.position) then return false end end end end return true end --' Проверка в таргет кондлисте задания, соответствует ли имя сквада переданному function target_squad_name(actor, obj, p) if p[1] == nil then printf("Wrong parameters") end if not(obj) then return false end --callstack() if IsStalker(obj) or IsMonster(obj) then local squad = obj.group_id and obj.group_id ~= 65535 and alife_object(obj.group_id) if not (squad) then return false end if string.find( squad:section_name(), p[1] ) ~= nil then return true end --return alife_object(obj.group_id):section_name() == p[1] end return obj:section_name() == p[1] end --' Проверка в таргет кондлисте задания, соответствует ли имя смарта переданному function target_smart_name(actor, smart, p) if p[1] == nil then printf("Wrong parameters") end --callstack() return smart:name() == p[1] end --' Проверяет жив ли отряд с указанным ID function squad_exist(actor, npc, p) local story_id = p[1] if story_id == nil then printf("Wrong parameter story_id[%s] in squad_exist function", tostring(story_id)) end local squad = get_story_squad(story_id) -- if squad == nil then -- return false -- end return squad ~= nil -- return squad.squad_power > 0 end function is_squad_commander(actor, npc) if (type(npc.id) == "number") then npc_id = npc.id else npc_id = npc:id() end local squad = get_object_squad(npc) return squad and squad:commander_id() == npc_id or squad == nil end function squad_npc_count_ge(actor, npc, p) local story_id = p[1] if story_id == nil then printf("Wrong parameter squad_id[%s] in 'squad_npc_count_ge' function", tostring(squad_id)) end local squad = get_story_squad(story_id) if squad then return squad:npc_count() > tonumber(p[2]) else return false end end function surge_complete() if (psi_storm_manager and psi_storm_manager.is_loaded()) then if not (psi_storm_manager.is_finished()) then return false end end if (surge_manager and surge_manager.is_loaded()) then return surge_manager.is_finished() end return true end function surge_started() if (psi_storm_manager and psi_storm_manager.is_loaded() and psi_storm_manager.is_started()) then return true end if (surge_manager and surge_manager.is_loaded() and surge_manager.is_started()) then return true end return false end function surge_kill_all() return surge_manager and surge_manager.is_killing_all() or false end function signal_rocket_flying(actor, npc, p) if p==nil then printf("Signal rocket name is not set!") end if db.signal_light[p[1]] then return db.signal_light[p[1]]:is_flying() else printf("No such signal rocket: [%s] on level", tostring(p[1])) end return false end function quest_npc_enemy_actor(actor, npc, p) if p[1] == nil then printf("wrong story id") else local obj = get_story_object(p[1]) if obj and IsStalker(obj) then if db.actor and obj:general_goodwill(db.actor)<=-1000 then return true end end end return false end function animpoint_reached(actor, npc) local animpoint_storage = db.storage[npc:id()].animpoint if animpoint_storage == nil then return false end local animpoint_class = animpoint_storage.animpoint return animpoint_class:position_riched() end --[[ function npc_stay_offline(actor, npc, p) if p == nil then printf("Wrong parameter!!!") end if npc and db.actor then if is_smart_in_combat(actor, npc, p) then if npc.position:distance_to(db.actor:position())>=30 or game_relations.get_gulag_relation_actor(p[1], "enemy") then return true end end end return false end ]]-- -- проверка того что дистанция до обьекта >= заданной -- параметры: [sid,dist] function distance_to_obj_ge(actor, npc, p) local npc_id = get_story_object_id(p[1]) local npc1 = npc_id and alife_object(npc_id) if npc1 then return db.actor ~= nil and db.actor:position():distance_to_sqr(npc1.position) >= p[2]*p[2] end return false end function distance_to_obj_le(actor, npc, p) local npc_id = get_story_object_id(p[1]) local npc1 = npc_id and alife_object(npc_id) if npc1 then return db.actor ~= nil and db.actor:position():distance_to_sqr(npc1.position) < p[2]*p[2] end return false end function in_dest_smart_cover(actor, npc, p) return npc:in_smart_cover() end function active_item(actor, npc, p) if p and p[1] then for k,v in pairs(p) do if actor:item_in_slot(3) ~= nil and actor:item_in_slot(3):section() == v then return true end end end return false end function actor_nomove_nowpn() local itm = db.actor ~= nil and db.actor:active_item() if not (itm) then return true end if not (IsWeapon(itm)) then return true end if (db.actor:is_talking()) then return true end return false end function jup_b16_is_zone_active(actor, npc) return has_alife_info(npc:name()) end --Функция проверки состояния видимости кровососа. -- Возможный набор параметров --> story_id:visibility_state(можно вызывать откуда угодно) или visibility_state(если вызывается из кастомдаты кровососа) -- visibility_state --> -- 0 - невидимый -- 1 - полувидимый -- 2 - полностью видимый function check_bloodsucker_state(actor, npc, p) if (p and p[1]) == nil then printf("Wrong parameters in function 'check_bloodsucker_state'!!!") end local state = p[1] if p[2] ~= nil then state = p[2] npc = get_story_object(p[1]) end if npc ~= nil then return npc:get_visibility_state () == tonumber(state) end return false end function dist_to_story_obj_ge(actor, npc, p) local id = db.actor and p and get_story_object_id(p[1]) local se_obj = id and alife_object(id) return se_obj and se_obj.position:distance_to(db.actor:position()) > tonumber(p[2]) or false end function actor_has_nimble_weapon(actor, npc) local need_item = {} need_item["wpn_groza_nimble"] = true need_item["wpn_desert_eagle_nimble"] = true need_item["wpn_fn2000_nimble"] = true need_item["wpn_g36_nimble"] = true need_item["wpn_protecta_nimble"] = true need_item["wpn_mp5_nimble"] = true need_item["wpn_sig220_nimble"] = true need_item["wpn_spas12_nimble"] = true need_item["wpn_usp_nimble"] = true need_item["wpn_vintorez_nimble"] = true need_item["wpn_svu_nimble"] = true need_item["wpn_svd_nimble"] = true for k,v in pairs(need_item) do if actor:object(k) ~= nil then return true end end return false end function actor_has_active_nimble_weapon(actor, npc) local need_item = {} need_item["wpn_groza_nimble"] = true need_item["wpn_desert_eagle_nimble"] = true need_item["wpn_fn2000_nimble"] = true need_item["wpn_g36_nimble"] = true need_item["wpn_protecta_nimble"] = true need_item["wpn_mp5_nimble"] = true need_item["wpn_sig220_nimble"] = true need_item["wpn_spas12_nimble"] = true need_item["wpn_usp_nimble"] = true need_item["wpn_vintorez_nimble"] = true need_item["wpn_svu_nimble"] = true need_item["wpn_svd_nimble"] = true if actor:item_in_slot(2) ~= nil and need_item[actor:item_in_slot(2):section()] == true then return true end if actor:item_in_slot(3) ~= nil and need_item[actor:item_in_slot(3):section()] == true then return true end return false end function jup_b202_inventory_box_empty(actor, npc) local inv_box = get_story_object("jup_b202_actor_treasure") return inv_box:is_inv_box_empty() end --[[ function jup_b46_actor_has_active_science_detector(actor, npc) if actor:item_in_slot(9) ~= nil and actor:item_in_slot(9):section() == "detector_scientific" then return true end return false end ]]-- function is_in_danger(actor, npc, p) if p and p[1] then npc = get_story_object(p[1]) end --printf("npc: [%s] is in danger [%s]", tostring(npc:id()), tostring(db.storage[npc:id()].danger_flag)) return db.storage[npc:id()].danger_flag end function object_exist(actor , npc , p) return get_story_object(p[1]) ~= nil end function squad_curr_action(actor, npc, p) local squad = get_object_squad(npc) return squad.current_action and squad.current_action == p[1] end function is_monster_snork(actor, npc) return npc:clsid() == clsid.snork_s end function is_monster_dog(actor, npc) return npc:clsid() == clsid.dog_s end function is_monster_psy_dog(actor, npc) return npc:clsid() == clsid.psy_dog_s end function is_monster_polter(actor, npc) return npc:clsid() == clsid.poltergeist_s end function is_monster_tushkano(actor, npc) return npc:clsid() == clsid.tushkano_s end function is_monster_burer(actor, npc) return npc:clsid() == clsid.burer_s end function is_monster_controller(actor, npc) return npc:clsid() == clsid.controller_s end function is_monster_flesh(actor, npc) return npc:clsid() == clsid.flesh_s end function is_monster_boar(actor, npc) return npc:clsid() == clsid.boar_s end function dead_body_searching(actor, npc) return ActorMenu.get_menu_mode() == 4 end function jup_b47_npc_online(actor, npc, p) -- printf("function jup_b47_npc_online: story_obj[%s]", tostring(p[1])) local story_obj = get_story_object(p[1]) if story_obj == nil then return false end local obj = alife_object(story_obj:id()) return obj ~= nil end function anomaly_has_artefact(actor, npc, p) local az_name = p and p[1] local af_name = p and p[2] local anomal_zone = db.anomaly_by_name[az_name] if anomal_zone == nil then return false end if anomal_zone.spawned_count < 1 then return false end if af_name == nil then local af_table = {} for k,v in pairs(anomal_zone.artefact_ways_by_id) do local se_obj = alife_object(tonumber(k)) if (se_obj) then af_table[#af_table+1] = se_obj:section_name() end end return true, af_table end for k,v in pairs(anomal_zone.artefact_ways_by_id) do local se_obj = alife_object(tonumber(k)) if (se_obj and se_obj:section_name() == af_name) then return true end end return false end function zat_b29_anomaly_has_af(actor, npc, p) local az_name = p and p[1] local af_name local anomal_zone = db.anomaly_by_name[az_name] if anomal_zone == nil then return false end if anomal_zone.spawned_count < 1 then return false end for i = 16, 23 do if has_alife_info(dialogs_zaton.zat_b29_infop_bring_table[i]) then af_name = dialogs_zaton.zat_b29_af_table[i] break end end for k,v in pairs(anomal_zone.artefact_ways_by_id) do if alife_object(tonumber(k)) and af_name == alife_object(tonumber(k)):section_name() then give_info(az_name) return true end end return false end function jup_b221_who_will_start(actor, npc, p) -- доступные параметры: ability - проверить есть ли доступные темы, choose - выбрать групировку которая начнет перепалку local reachable_theme = {} local faction_table = {} local info_table = { ---------duty [1] = "jup_b25_freedom_flint_gone", [2] = "jup_b25_flint_blame_done_to_duty", [3] = "jup_b4_monolith_squad_in_duty", [4] = "jup_a6_duty_leader_bunker_guards_work", [5] = "jup_a6_duty_leader_employ_work", [6] = "jup_b207_duty_wins", ---------freedom [7] = "jup_b207_freedom_know_about_depot", [8] = "jup_b46_duty_founder_pda_to_freedom", [9] = "jup_b4_monolith_squad_in_freedom", [10] = "jup_a6_freedom_leader_bunker_guards_work", [11] = "jup_a6_freedom_leader_employ_work", [12] = "jup_b207_freedom_wins" } --Составляем таблицу достыпных тем(тлько номера тем). for k,v in pairs(info_table) do if k <= 6 then faction_table[1] = "duty" faction_table[2] = "0" else faction_table[1] = "freedom" faction_table[2] = "6" end if (has_alife_info(v)) and (not has_alife_info("jup_b221_" .. faction_table[1] .. "_main_" .. tostring(k - tonumber(faction_table[2])) .. "_played")) then table.insert(reachable_theme,k) printf("jup_b221_who_will_start: table reachable_theme ------------------------------> [%s]", tostring(k)) end end if (p and p[1]) == nil then printf("No such parameters in function 'jup_b221_who_will_start'") end if tostring(p[1]) == "ability" then return #reachable_theme ~= 0 -- если таблица пуста значит нет доступных тем и ненадо играть сцену elseif tostring(p[1]) == "choose" then ---------Выберем рандомам елемент составленой таблицы и проверим к какой груперовки он пренадлежит :) return reachable_theme[math.random(1, #reachable_theme)] <= 6 -- если меньше 6-ти значит ДОЛГ если больше 6-ти значит СВОБОДА else printf("Wrong parameters in function 'jup_b221_who_will_start'") end end function pas_b400_actor_far_forward(actor, npc) local fwd_obj = get_story_object("pas_b400_fwd") if fwd_obj then if (db.actor and distance_between(fwd_obj, db.actor) > distance_between(fwd_obj, npc)) then return false end else return false end local distance = 70 * 70 local self_dist = npc:position():distance_to_sqr(actor:position()) if self_dist < distance then return false end local squad = get_object_squad(npc) if (squad) then local sim = alife() for k in squad:squad_members() do local se_obj = k.object or k.id and sim:object(k.id) if (se_obj and se_obj.position:distance_to_sqr(actor:position()) < distance) then return true end end end --printf("npc: [%s], actor is far forward - self_dist [%s] distance [%s]", tostring(npc:name()), self_dist, distance) return true end function pas_b400_actor_far_backward(actor, npc) local bwd_obj = get_story_object("pas_b400_bwd") if bwd_obj then if (db.actor and distance_between(bwd_obj, db.actor) > distance_between(bwd_obj, npc)) then return false end else return false end local distance = 70 * 70 local self_dist = npc:position():distance_to_sqr(actor:position()) if self_dist < distance then return false end local squad = get_object_squad(npc) if (squad) then local sim = alife() for k in squad:squad_members() do local se_obj = k.object or k.id and sim:object(k.id) if (se_obj and se_obj.position:distance_to_sqr(actor:position()) < distance) then return true end end end --printf("npc: [%s], actor is far backward - self_dist [%s] distance [%s]", tostring(npc:name()), self_dist, distance) return true end function pri_a28_actor_is_far(actor, npc) local distance = 150 * 150 local squad = get_story_squad("pri_a16_military_squad") for k in squad:squad_members() do local npc_dist = k.object.position:distance_to_sqr(actor:position()) if npc_dist < distance then return false end end return true end function check_enemy_smart(actor , npc , p) local enemy_id = db.storage[npc:id()].enemy_id local enemy = db.storage[enemy_id] and db.storage[enemy_id].object if enemy == nil or enemy_id == alife():actor().id then return false end local enemy_smart = xr_gulag.get_npc_smart(enemy) if (enemy_smart ~= nil) and (enemy_smart:name() == p[1]) then return true end return false end function zat_b103_actor_has_needed_food(actor, npc, p) return (dialogs_zaton.zat_b103_actor_has_needed_food(actor, npc)) or (has_alife_info("zat_b103_merc_task_done")) end function zat_b29_rivals_dialog_precond(actor, npc) local squads_table = { "zat_b29_stalker_rival_default_1_squad", "zat_b29_stalker_rival_default_2_squad", "zat_b29_stalker_rival_1_squad", "zat_b29_stalker_rival_2_squad" } local zones_table = { "zat_b29_sr_1", "zat_b29_sr_2", "zat_b29_sr_3", "zat_b29_sr_4", "zat_b29_sr_5", } local f_squad = false local squad = get_object_squad(npc) if not (squad) then return false end for k,v in pairs(squads_table) do if squad:section_name() == v then f_squad = true break end end if not f_squad then return false end for k,v in pairs(zones_table) do if utils.npc_in_zone(npc, v) then return true end end return false end function polter_ignore_actor(actor, npc) return npc:poltergeist_get_actor_ignore() end function burer_gravi_attack(actor, npc) return npc:burer_get_force_gravi_attack() end function burer_anti_aim(actor, npc) return npc:burer_get_force_anti_aim() end function jup_b202_actor_treasure_not_in_steal(actor, npc) local before = ((not has_alife_info("jup_b52_actor_items_can_be_stolen")) and (not has_alife_info("jup_b202_actor_items_returned"))) local after = (has_alife_info("jup_b52_actor_items_can_be_stolen") and has_alife_info("jup_b202_actor_items_returned")) return (before or after) end function jup_b25_senya_spawn_condition(actor, npc) return (has_alife_info("jup_b16_oasis_found") or has_alife_info("zat_b57_bloodsucker_lair_clear") or has_alife_info("jup_b6_complete_end") or has_alife_info("zat_b215_gave_maps")) and has_alife_info("zat_b106_search_soroka") end function jup_b25_flint_gone_condition(actor, npc) return has_alife_info("jup_b25_flint_blame_done_to_duty") or has_alife_info("jup_b25_flint_blame_done_to_freedom") or has_alife_info("zat_b106_found_soroka_done") end ------------------------------------------------------------------------------------------------------------------------------------------- -- STALKER TRADE FUNCTIONS ------------------------------------------------------------------------------------------------------------------------------------------- -- online only function npc_has_items_to_sell(actor,npc,p) if not (npc) then return false end local id = type(npc.id) == "function" and npc:id() or npc.id local st = db.storage[id] if not (st) then return false end if not (st.has_items_to_sell) then return false end local smart = p and p[1] and SIMBOARD.smarts_by_names[p[1]] if (smart and smart.npc_info[id]) then local job = smart.npc_info[id].job if (job and job.idle and time_global() < job.idle) then return false end end return true end ------------------------------------------------------------------------------------------------------------------------------------------- -- end of STALKER TRADE FUNCTIONS ------------------------------------------------------------------------------------------------------------------------------------------- function check_deimos_phase(actor, npc, p) if(p[1] and p[2]) then local obj = db.storage[npc:id()] local delta = sr_deimos.check_intensity_delta(obj) if(p[2]=="increasing" and delta) then return false elseif(p[2]=="decreasing" and not(delta)) then return false end if(p[1]=="disable_bound") then if(p[2]=="increasing") then if not(sr_deimos.check_disable_bound(obj)) then return true end elseif(p[2]=="decreasing") then return sr_deimos.check_disable_bound(obj) end elseif(p[1]=="lower_bound") then if(p[2]=="increasing") then if not(sr_deimos.check_lower_bound(obj)) then return true end elseif(p[2]=="decreasing") then return sr_deimos.check_lower_bound(obj) end elseif(p[1]=="upper_bound") then if(p[2]=="increasing") then if not(sr_deimos.check_upper_bound(obj)) then return true end elseif(p[2]=="decreasing") then return sr_deimos.check_upper_bound(obj) end end end end -------------------------------------------------------------------------------- function actor_in_surge_cover(actor, npc, p) return surge_manager and surge_manager.actor_in_cover() end function is_door_blocked_by_npc(actor, obj) return obj:is_door_blocked_by_npc() end function has_active_tutorial() return game.has_active_tutorial() end function upgrade_hint_kardan(actor, npc, p) local hint_table = {} local can_upgrade = 0 local tools = (p and tonumber(p[1])) or 0 if not has_alife_info("zat_b3_all_instruments_brought") then if not has_alife_info("zat_b3_tech_instrument_1_brought") and (tools == 0 or tools == 1) then table.insert(hint_table, "st_upgr_toolkit_1") elseif tools == 1 then can_upgrade = can_upgrade + 1 end if not has_alife_info("zat_b3_tech_instrument_2_brought") and (tools == 0 or tools == 2) then table.insert(hint_table, "st_upgr_toolkit_2") elseif tools == 2 then can_upgrade = can_upgrade + 1 end if not has_alife_info("zat_b3_tech_instrument_3_brought") and (tools == 0 or tools == 3) then table.insert(hint_table, "st_upgr_toolkit_3") elseif tools == 3 then can_upgrade = can_upgrade + 1 end else can_upgrade = can_upgrade + 1 end if not has_alife_info("zat_b3_tech_see_produce_62") then if (tools == 1) and not has_alife_info("zat_b3_tech_have_one_dose") then table.insert(hint_table, "st_upgr_vodka") elseif (tools ~= 1) and (not has_alife_info("zat_b3_tech_have_couple_dose")) then table.insert(hint_table, "st_upgr_vodka") else can_upgrade = can_upgrade + 1 end else can_upgrade = can_upgrade + 1 end inventory_upgrades.cur_hint = hint_table return can_upgrade >= 2 end -- AtmosFear Psi Storm function psi_storm_complete() return psi_storm_manager.is_finished() end -- Returns true if calling NPC has the surrender flag to actor function npc_surrendered(actor,npc,p) if not (db.actor) then return false end local st = db.storage[npc:id()] return st and st.victim_surrender == db.actor:id() or false end -- Returns true if calling NPC has the surrender flag function npc_surrendered_any(actor,npc,p) local st = db.storage[npc:id()] return st and st.victim_surrender >= 0 and st.victim_surrender < 65534 or false end function actor_class(actor,npc,p) return p and p[1] and axr_misery and axr_misery.ActorClass == p[1] or false end function black_road_enabled(actor,npc,p) return axr_misery and axr_misery.MiseryBlackRoad or false end -- Returns true if a squad behavior equals given -- param 1 is player_id (squad behavior, see _g.script) function squad_behavior(actor,npc,p) local se_npc = type(npc.id) == "function" and alife_object(npc:id()) or npc local squad = se_npc and se_npc.parent_id ~= 65535 and alife_object(se_npc.parent_id) return p and p[1] and squad and squad.player_id == p[1] or false end -- Returns true if a anim state equals given -- param 1 is state function anim_state(actor,npc,p) return p and p[1] and state_mgr.get_state(npc) == p[1] or false end -- Returns if a smart is empty -- param 1 is smart name function smart_empty(actor,npc,p) local board = SIMBOARD local smart = p[1] and board.smarts_by_names[p[1]] if not (smart) then return false end local smart_id = smart.id local sim = alife() for k,v in pairs(board.smarts[smart_id].squads) do if (v.id and sim:object(v.id)) then return false end end return true end function npc_squad_has_enemy(actor,npc,p) local squad = get_object_squad(npc) if not (squad) then return false end for k in squad:squad_members() do local o = k.id and db.storage[k.id] and db.storage[k.id].object if (o and o:best_enemy()) then return true end end end -- Returns true if a NPC at a certain smart was hit by calling NPC -- param 1 is smart name function hit_by_enemy_on_smart(actor,npc,p) local id = db.storage[npc:id()].hitted_by local who = id and level.object_by_id(id) if (not who or not who:alive()) then return false end local smart = xr_gulag.get_npc_smart(who) return p and p[1] and smart and smart:name() == p[1] or false end function is_upgrading(actor, npc, p) -- pda.upgrade_closed = not pda.upgrade_closed -- return not pda.upgrade_closed return pda.upgrade_closed end --'----------------------------------------------------------------------------------- --' Minigun support --'----------------------------------------------------------------------------------- function is_minigun_see_actor (actor, npc) local mgun = npc:get_car() return mgun:IsObjectVisible(actor) end function is_minigun_see_current_target(actor,npc,p) local st = npc and db.storage[npc:id()] if not (st) then return false end if not (st.ph_minigun) then return false end if (st.ph_minigun.fire_target == nil or st.ph_minigun.fire_target == "") then return false end if (st.ph_minigun.target_obj == nil or not st.ph_minigun.target_obj:alive()) then return false end return npc:get_car():IsObjectVisible(st.ph_minigun.target_obj) end function squad_hit_by_smart(actor,npc,p) local squad = get_object_squad(npc) if not (squad) then return false end local storage = db.storage for k in squad:squad_members() do local who = k.id and storage[k.id] and storage[k.id].hitted_by and storage[storage[k.id].hitted_by] and storage[storage[k.id].hitted_by].object local smart = who and xr_gulag.get_npc_smart(who) if (smart and smart:name() == p[1]) then return true end end return false end function actor_look_away(actor,npc) if not (db.actor) then return false end if (not db.actor:see(npc) or (alun_utils.angle_diff(db.actor:direction(), db.actor:position():sub(npc:position()))<90 and npc:position():distance_to(db.actor:position()) > 2.5) or npc:position():distance_to(db.actor:position())>100) then return true end return false end function between_time(actor,npc,p) return in_time_interval(tonumber(p[1]),tonumber(p[2])) or false end function squad_is_monster(actor,squad,p) return is_squad_monster[squad.player_id] == true end function companion_at_index(actor,npc,p) if (p == nil or p[1] == nil) then return false end local id = db.actor ~= nil and utils.load_var(db.actor,"companion_"..p[1],nil) return id and id == npc:id() or false end function after_first_meet(actor,npc,p) local st = db.storage[npc:id()] if not (st) then return false end return st.after_first_meet == true end function squad_on_actor_map(actor,npc,p) local squad = get_object_squad(npc) if not (squad) then return false end local gg = game_graph() return gg:vertex(alife():actor().m_game_vertex_id):level_id() == gg:vertex(squad.m_game_vertex_id):level_id() end -- Returns true if squad commander's active section equals param 1 -- param 2 - active section (ie. "logic@walker1") function squad_commander_active_section(actor,npc,p) local squad = p[1] and get_object_squad(npc) local st = squad and squad:commander_id() and db.storage[squad:commander_id()] return st and st.active_section == p[1] or false end -- Returns true if a NPC on a given job has a patrol index equalling given -- param 1 is smart name -- param 2 is job section (ie. "logic@walker_1") in this case omit 'logic@' -- param 3 is index -- param 4 is min allowed radius to patrol point -- param 5 is boolean, returns true if job is empty function job_on_point(actor,npc,p) -- p[1] = smart_name p[2] = logic p[3] = index p[4] = less_than_equal_to Distance p[5] = return true if object is dead or doesn't exist local board = SIMBOARD local smart = p[1] and board and board.smarts_by_names[p[1]] local id = smart and smart.npc_by_job_section["logic@"..p[2]] local st = id and db.storage[id] local obj = st and st.object if not (obj) then return p[5] == "true" end local index = st and st.active_scheme and st.active_scheme == "beh" and utils.load_var(obj,"path_index",nil) or obj:get_current_point_index() if (p[4] and index == tonumber(p[3])) then if (st and st.active_scheme and st.active_scheme == "beh") then if (axr_beh.am_i_reached(obj)) then return true end return false end local path = obj:patrol() local pos = path and patrol(path):point(tonumber(p[3])) local dist = pos and obj:position():distance_to_sqr(pos) return dist <= tonumber(p[4]) end return index == tonumber(p[3]) end -- Checks if npc distance is less than equal to given patrol point -- param 1 is patrol path name -- param 2 is path index -- param 3 is distance less than equal function dist_to_point_le(actor,npc,p) local pat = p[1] and level.patrol_path_exists(p[1]) and patrol(p[1]) if (pat) then local index = p[2] and tonumber(p[2]) or 0 local dist = npc:position():distance_to_sqr(pat:point(index)) return dist <= tonumber(p[3]) end return false end function beh_reached(actor,npc,p) return p[1] and axr_beh.am_i_reached(npc,tonumber(p[1])) or axr_beh.am_i_reached(npc) end -- Returns true if a NPC occupies a given job -- param 1 is smart name -- param 2 is job section (ie. "logic@walker_1") in this case omit 'logic@' -- param 3 is optional check for active_section function obj_on_job(actor,npc,p) local board = SIMBOARD local smart = p[1] and board and board.smarts_by_names[p[1]] local id = smart and smart.npc_by_job_section["logic@"..p[2]] local st = id and db.storage[id] local obj = st and st.object --printf("obj=%s",obj and obj:name()) if not (p[3]) then return obj and obj:alive() or false end return st and st.active_section == p[3] or false end -- Returns true if a NPC sees another NPC on a given job -- param 1 is smart name -- param 2 is job section (ie. "logic@walker_1") in this case omit 'logic@' function see_obj_on_job(actor,npc,p) local board = SIMBOARD local smart = p[1] and board and board.smarts_by_names[p[1]] local id = smart and smart.npc_by_job_section["logic@"..p[2]] local obj = id and db.storage[id] and db.storage[id].object return obj and obj:alive() and npc:memory_time(obj) < 5000 or false end -- Returns true if a NPC on a given job is in danger mode -- param 1 is smart name -- param 2 is job section (ie. "logic@walker_1") in this case omit 'logic@' function obj_on_job_danger(actor,npc,p) local board = SIMBOARD local smart = p[1] and board and board.smarts_by_names[p[1]] local id = smart and smart.npc_by_job_section["logic@"..p[2]] local obj = id and db.storage[id] and db.storage[id].object return obj and db.storage[id].danger_flag or false end -- Returns true if a NPC on a given job was hit by calling NPC -- param 1 is smart name -- param 2 is job section (ie. "logic@walker_1") in this case omit 'logic@' function obj_on_job_hit_by(actor,npc,p) local board = SIMBOARD local smart = p[1] and board and board.smarts_by_names[p[1]] local id = smart and smart.npc_by_job_section["logic@"..p[2]] local obj = id and db.storage[id] and db.storage[id].object if (not obj and not obj:alive()) then return false end local who_id = db.storage[id].hitted_by return who_id and who_id == npc:id() or false end -- Returns true if a NPC's on a given job has a given active section -- param 1 is smart name -- param 2 is job section (ie. "logic@walker_1") in this case omit 'logic@' -- param 3 is active section (ie. "walker@walker_1_surge_1") function job_active_section(actor,npc,p) local board = SIMBOARD local smart = p[1] and board and board.smarts_by_names[p[1]] local id = smart and smart.npc_by_job_section["logic@"..p[2]] local obj = id and db.storage[id] and db.storage[id].object return obj and db.storage[id].active_section == p[3] or false end -- Returns true if ONLY monster-type NPC's occupy a given smart -- param 1 is smart name function is_only_monsters_on_jobs(actor,npc,p) local board = SIMBOARD local smart = p[1] and board and board.smarts_by_names[p[1]] if not (smart) then return false end for id,npc_info in pairs(smart.npc_info) do if (npc_info.stype ~= modules.stype_mobile) then return false end end return true end -- Returns true if ONLY stalkers occuppy a given smart -- param 1 is smart name function is_only_stalkers_on_jobs(actor,npc,p) local board = SIMBOARD local smart = p[1] and board and board.smarts_by_names[p[1]] if not (smart) then return false end for id,npc_info in pairs(smart.npc_info) do if (npc_info.stype == modules.stype_mobile or npc_info.stype == modules.stype_heli) then return false end end return true end -- Returns true if ONLY a given community occupies a given smart -- param 1 is smart name function is_only_community_on_jobs(actor,npc,p) local board = SIMBOARD local smart = p[1] and board and board.smarts_by_names[p[1]] if not (smart) then return false end for id,npc_info in pairs(smart.npc_info) do if (npc_info.se_obj and (npc_info.stype == 0 or npc_info.stype == 1) and npc_info.se_obj:character_community() ~= p[2]) then return false end end return true end function smart_squad_by_faction_in_radius(actor,npc,p) local smart = p[1] and p[2] and p[3] and SIMBOARD.smarts_by_names[p[1]] if not (smart) then return false end local smrt = SIMBOARD.smarts[smart.id] if not (smrt) then return false end for k,squad in pairs(smrt.squads) do if (squad and squad.player_id == p[2] and squad.position:distance_to_sqr(smart.position) <= tonumber(p[3])^2) then return true end end return false end -- Returns true if squads by behavior faction (player_id) of a given count exist at smart out of squads currently entered in smart -- param1 - smart_name -- param2 - greater than equal to number -- param3 - check if stay time is less than equal given seconds -- param4 - include check for squads with scripted target (usually quest squads) -- param5+ - faction aka player_id function smart_stayed_squad_count_ge_by_faction(actor,npc,p) if (#p < 5) then return false end local smart = SIMBOARD.smarts_by_names[p[1]] if not (smart) then return false end local smrt = SIMBOARD.smarts[smart.id] if not (smrt) then return false end local count = 0 for k,squad in pairs(smrt.squads) do if (squad and simulation_objects.is_on_the_same_level(squad, smart) and squad.current_target_id and squad.current_target_id == smart.id and squad.current_action == 1) then if (p[4] == "true" or not squad:get_script_target()) then if (squad.stay_time) and (p[3] == "nil" or game.get_game_time():diffSec(squad.stay_time) <= (tonumber(p[3]) or 0)) then for i=5,#p do if (p[i] == "monster" and is_squad_monster[squad.player_id] or squad.player_id == p[i]) then count = count + 1 if (count >= tonumber(p[2])) then squad.stay_time = game.get_game_time() return true end break end end end end end end return false end -- checks all smarts on actor level with above function -- param1 - greater than equal to number -- param2 - check if stay time is less than equal given seconds -- param3 - allow check for target_smart param for squads; such as scripted target. -- param4+ - faction aka player_id function all_avail_smart_stayed_squad_count_ge_by_faction(actor,npc,p) if (#p >= 4) then for name,v in pairs(SIMBOARD.smarts_by_names) do if (v.online) and (v.sim_avail == nil or v.sim_avail and xr_logic.pick_section_from_condlist(actor, v, v.sim_avail) == "true") then if (smart_stayed_squad_count_ge_by_faction(actor,npc,{name,unpack(p)}) == true) then return true end end end end return false end -- same as above but checks all levels function all_avail_smart_stayed_squad_count_ge_by_faction_ex(actor,npc,p) if (#p >= 4) then local sim = alife() local gg = game_graph() local actor_level = sim:level_name(gg:vertex(sim:actor().m_game_vertex_id):level_id()) for name,v in pairs(SIMBOARD.smarts_by_names) do if (v.sim_avail == nil or v.sim_avail and xr_logic.pick_section_from_condlist(actor, v, v.sim_avail) == "true") then local smart_level = alife():level_name(game_graph():vertex(v.m_game_vertex_id):level_id()) if (v.online or DEACTIVATE_SIM_ON_NON_LINKED_LEVELS ~= true or string.find(simulation_objects.config:r_value(actor_level,"target_maps",0,""),smart_level)) then if (smart_stayed_squad_count_ge_by_faction(actor,npc,{name,unpack(p)}) == true) then return true end end end end end return false end -- Returns true if squads by behavior faction (player_id) of a given count exist at smart out of entire population; including arriving -- param1 - smart_name -- param2 - faction aka player_id -- param3 - greater than equal to number function smart_squad_count_ge_by_faction(actor,npc,p) local smart = p[1] and p[2] and p[3] and SIMBOARD.smarts_by_names[p[1]] if not (smart) then return false end local smrt = SIMBOARD.smarts[smart.id] if not (smrt) then return false end local count = 0 for k,squad in pairs(smrt.squads) do if (squad and squad.player_id == p[2]) then count = count + 1 end end return count >= tonumber(p[4]) end function smart_population_count(actor,npc,p) local smart = p[1] and SIMBOARD.smarts_by_names[p[1]] if not (smart) then return false end return p[2] and SIMBOARD.smarts[smart.id] and SIMBOARD.smarts[smart.id].population == tonumber(p[2]) or false end function smart_population_count_ge(actor,npc,p) local smart = p[1] and SIMBOARD.smarts_by_names[p[1]] if not (smart) then return false end return p[2] and SIMBOARD.smarts[smart.id] and SIMBOARD.smarts[smart.id].population >= tonumber(p[2]) or false end function smart_population_count_le(actor,npc,p) local smart = p[1] and SIMBOARD.smarts_by_names[p[1]] if not (smart) then return false end return p[2] and SIMBOARD.smarts[smart.id] and SIMBOARD.smarts[smart.id].population <= tonumber(p[2]) or false end -- Returns true if any instance of given community occupies a given smart -- param 1 is smart name function is_community_on_jobs(actor,npc,p) local board = SIMBOARD local smart = p[1] and board and board.smarts_by_names[p[1]] if not (smart) then return false end for id,npc_info in pairs(smart.npc_info) do if (npc_info.se_obj and (npc_info.stype == 0 or npc_info.stype == 1) and npc_info.se_obj:character_community() == p[2]) then return true end end return false end function is_community_arriving(actor,npc,p) local board = SIMBOARD local smart = p[1] and board and board.smarts_by_names[p[1]] if not (smart) then return false end for id,se_obj in pairs(smart.arriving_npc) do if (se_obj and IsStalker(se_obj) and se_obj:character_community() == p[2]) then return true end end return false end function community_arriving_dist_ge(actor,npc,p) local board = SIMBOARD local smart = p[1] and board and board.smarts_by_names[p[1]] if not (smart) then return false end local exist for id,se_obj in pairs(smart.arriving_npc) do if (se_obj and IsStalker(se_obj) and se_obj:character_community() == p[2]) then exist = true if (se_obj.position:distance_to_sqr(smart.position) >= tonumber(p[3] or 10000)) then return true end end end -- community does not exist at smart return true if not (exist) then return true end return false end --' SP -- Sidor --------------------------------------- function trade_exchanged(actor, npc) local npc_id = npc:id() return (db.storage[npc_id].mob_trade and db.storage[npc_id].mob_trade.exchanged) --or false end function trading(actor, npc) local npc_id = npc:id() return (db.storage[npc_id].mob_trade and db.storage[npc_id].mob_trade.trading) --or false end function trade_sell_coast_b_1000(actor, npc) return db.storage[npc:id()].mob_trade.sell_coast > 1000 end function trade_sell_coast_b_0_m_1000(actor, npc) return db.storage[npc:id()].mob_trade.sell_coast > 0 and db.storage[npc:id()].mob_trade.sell_coast < 1000 end function trade_buy_coast(actor, npc) return db.storage[npc:id()].mob_trade.buy_coast > 1000 end function trade_all_money(actor, npc) return db.storage[npc:id()].mob_trade.all_money > 2000 end -- Sidor --------------------------------------- --' SP function npc_in_actor_community(actor,npc,p) local comm = character_community(npc) return db.actor and string.find(character_community(db.actor),comm) ~= nil end function actor_community(actor,npc,p) if not (db.actor) then return false end for i=1,#p do if (character_community(db.actor) == p[i]) then return true end end return false end function actor_goodwill_ge(actor,npc,p) if (p[1] and p[2]) then return db.actor and relation_registry.community_goodwill(p[1], db.actor:id()) >= tonumber(p[2]) end return false end function task_giver_alive(actor,npc,p) if not (p[1]) then return false end local tm = task_manager.get_task_manager() local tsk = tm.task_info[p[1]] if not (tsk) then return true end local se_obj = tsk.task_giver_id and alife_object(tsk.task_giver_id) if (se_obj) then if (se_obj:clsid() == clsid.online_offline_group_s) then return true -- if squad exist then atleast 1 npc should be alive elseif (se_obj:alive()) then return true end end return false end function is_a_task_giver(actor,npc,p) local tm = task_manager.get_task_manager() for task_id,tsk in pairs(tm.task_info) do if (tsk.task_giver_id and tsk.task_giver_id == npc:id()) then return true end end return false end -- param1 - smart by name -- param2 - community -- param3 - if true, then it will check_npc_name for that faction (This is used for suitable in logic section) function smart_under_faction_control(actor,npc,p) local board = SIMBOARD local smart = p[1] and board and board.smarts_by_names[p[1]] if not (smart) then return false end if not (smart.faction and smart.faction_controlled) then return false end if not (p[2] and smart.faction == p[2]) then return false end if (p[3] and p[3] == "true") then if npc:name() == nil then return false end local f = { ["stalker"] = "stalker", ["bandit"] = "bandit", ["killer"] = "merc", ["dolg"] = "duty", ["freedom"] = "freedom", ["monolith"] = "monolith", ["ecolog"] = "ecolog", ["csky"] = "csky", ["army"] = "military" } if not (f[p[2]]) then return false end if string.find( npc:name(), f[p[2]] ) then return true end else return true end return false end function smart_under_faction_war(actor,npc,p) local board = SIMBOARD local smart = p[1] and board and board.smarts_by_names[p[1]] if not (smart) then return false end return smart.faction_war_in_progress == true end ------------------------------------------------------------------------------------- -- Special for Radar ------------------------------------------------------------------------------------- function rad_pass_time(actor, npc) return true -- return level.get_time_hours() >= 10 and level.get_time_hours() < 11 end function heli_dist_to_max_bounding_le(actor,npc,p) local heli = npc and npc:get_helicopter() if not (heli) then return false end local d = p[1] and tonumber(p[1]) local dist = d and heli_alife.distance_2d_sqr(npc:position(),level.get_bounding_volume().max) return dist and dist <= d or false end function heavy_pockets_functor(actor,npc,p) return has_alife_info("achieved_heavy_pockets") end ------------------------------------------------------------------------------------ -- Special functions for OLD ARENA ------------------------------------------------------------------------------------ function bar_arena_actor_inside(actor,npc) local t = db.zone_by_name["bar_arena_waiter"] if t and db.actor then if t:inside(db.actor:position()) then return true end end return false end function bar_arena_actor_fight(actor,npc) local t = db.zone_by_name["bar_arena_sr"] local tt = db.zone_by_name["bar_arena_waiter"] if t and tt and db.actor then if t:inside(db.actor:position()) and not tt:inside(db.actor:position()) then return true end end return false end function bar_arena_actor_outside(actor,npc) local t = db.zone_by_name["bar_arena_waiter_out"] if t and db.actor then if t:inside(db.actor:position()) then return true end end return false end function actor_dead(actor, npc) if db.actor and not db.actor:alive() then return true end return false end ------------------------------------------------------------------------------------ -- Special functions for NEW ARENA ------------------------------------------------------------------------------------ function bar_actor_rank_stalker (actor,npc) if db.actor and db.actor:character_rank() > 1499 then return true end return false end function bar_actor_rank_veteran (actor,npc) if db.actor and db.actor:character_rank() > 2999 then return true end return false end function bar_arena_fight_3_end (actor,npc) return has_alife_info("bar_arena_fight_3_stalker_1_die") and has_alife_info("bar_arena_fight_3_stalker_2_die") end function bar_arena_fight_4_end (actor,npc) return has_alife_info("bar_arena_fight_4_stalker_1_die") and has_alife_info("bar_arena_fight_4_stalker_2_die") and has_alife_info("bar_arena_fight_4_stalker_3_die") end function bar_arena_fight_5_end (actor,npc) return has_alife_info("bar_arena_fight_5_stalker_1_die") and has_alife_info("bar_arena_fight_5_stalker_2_die") end function bar_arena_fight_6_end (actor,npc) return has_alife_info("bar_arena_fight_6_stalker_1_die") and has_alife_info("bar_arena_fight_6_stalker_2_die") and has_alife_info("bar_arena_fight_6_stalker_3_die") and has_alife_info("bar_arena_fight_6_stalker_4_die") and has_alife_info("bar_arena_fight_6_stalker_5_die") and has_alife_info("bar_arena_fight_6_stalker_6_die") end function bar_arena_fight_8_end (actor,npc) return has_alife_info("bar_arena_fight_8_stalker_1_die") and has_alife_info("bar_arena_fight_8_stalker_2_die") and has_alife_info("bar_arena_fight_8_stalker_3_die") and has_alife_info("bar_arena_fight_8_stalker_4_die") end function heli_exist_on_level(actor,npc) if (is_empty(db.heli)) then return false end return true end function is_actor_surge_immuned(actor,npc,p) return utils.load_var(db.actor,"surge_immuned",false) == true end function get_start_time_elapsed_ge(actor,npc,p) return p[1] and tonumber(game.get_game_time():diffSec(level.get_start_time())) >= tonumber(p[1]) end -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- drx_ql_xrc_load_settings function -- -- ------------------------------------------------------------------------------------------------ -- -- Description: -- - Loads program settings from config file -- -- Usage: -- drx_ql_xrc_load_settings( ) -- -- Parameters: -- none -- -- Ini requirements: -- drx\drx_ql_config.ltx -- [smart_target_blacklist] (type: string, smart terrain names) -- - List of smart terrain names to exclude as target areas -- [brain_scorcher_smart_prefixes] (type: string, smart terrain level prefixes) -- - List of level prefixes for smart terrains affected by the Brain Scorcher -- [miracle_machine_smarts] (type: string, smart terrain names) -- - Smart terrains inaccessible while Miracle Machine active -- [assassination_target_names] (type: string, npc name substrings) -- - NPC name sub-strings for potential assassination targets -- -- Return value (type: nil): -- none -- -- ------------------------------------------------------------------------------------------------ -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified November 09, 2019 -- ------------------------------------------------------------------------------------------------ -- Load settings: function drx_ql_xrc_load_settings( ) -- Location of the settings file: local ini = ini_file( "drx\\drx_ql_config.ltx" ) if ( not ini ) then printf( "DRX QL Error: Unable to load settings for xr_conditions, cannot locate ini file" ) return false end -- List of smart terrains to exclude as task target areas: drx_ql_xrc_smart_blacklist = alun_utils.collect_section( ini, "smart_target_blacklist", true ) -- List of smart terrains to exclude as task target areas: drx_ql_xrc_miracle_machine_smarts = alun_utils.collect_section( ini, "miracle_machine_smarts", true ) -- List of smart terrains to exclude as task target areas: drx_ql_xrc_brain_scorcher_smart_prefixes = alun_utils.collect_section( ini, "brain_scorcher_smart_prefixes" ) -- NPC name sub-strings for potential assassination targets drx_ql_xrc_assassination_target_names = alun_utils.collect_section( ini, "assassination_target_names" ) -- Set return value: return end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ -- ________________________________________________________________________________________________ -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- Callback functions -- -- Created by DoctorX -- for DoctorX Questlines 2.0 -- Last modified September 14, 2019 -- -- ------------------------------------------------------------------------------------------------ -- On game load: local function drx_ql_xrc_on_game_load_callback( ) -- Load settings: drx_ql_xrc_load_settings( ) end -- ------------------------------------------------------------------------------------------------ -- On game start: function on_game_start( ) RegisterScriptCallback( "on_game_load", drx_ql_xrc_on_game_load_callback ) end -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\