Divergent/mods/Western Goods/gamedata/scripts/western_goods_tasks_act_2.s...

1325 lines
65 KiB
Plaintext
Raw Normal View History

2024-03-17 20:18:03 -04:00
---==================================================================================================================---
--- ---
--- Original Author(s) : NLTP_ASHES ---
--- Edited : N/A ---
--- Date : 17/04/2023 ---
--- License : Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) ---
--- ---
--- Script used to manage the tasks of the second act of the addon's storyline. ---
--- ---
--- Each task is composed of the following elements : ---
--- - A act_2_task_X_start(...) function, used to start the task; ---
--- - A act_2_task_X_end(...) function, used to end the task; ---
--- - A xr_effects.act_2_task_X_init(...) function, called when the task has started; ---
--- - A xr_effects.act_2_task_X_complete(...) function, called when the task was successfully completed; ---
--- - A xr_effects.act_2_task_X_fail(...) function, called when the task was failed; ---
--- - A task_functor.act_2_task_X_title_f(...) function, called periodically to get the name; ---
--- - A task_functor.act_2_task_X_descr_f(...) function, called periodically to get the description; ---
--- - A task_functor.act_2_task_X_target_f(...) function, called periodically to get the target (for the PDA); ---
--- - A task_status_functor.act_2_task_X_status_f(...) function, called periodically to run the core logic. ---
--- ---
---==================================================================================================================---
-- ---------------------------------------------------------------------------------------------------------------------
-- Constants, global variables and imported functions
-- ---------------------------------------------------------------------------------------------------------------------
-- Imported functions
local dbg_printf = western_goods_utils.dbg_printf
local send_dialog = western_goods_dialogs_manager.send_dialog
-- Constants
local CONST_SPAWN_THROTTLE = 5000 -- milliseconds
local CONST_TASK_2_HARD_DELAY = 1800000 -- milliseconds
local CONST_TASK_2_SOFT_DELAY_MIN = 600 -- seconds
local CONST_TASK_2_SOFT_DELAY_MAX = 1800 -- seconds
local CONST_TASK_2_ECOLOG_TELEPORT_DELAY = 50000 -- milliseconds
local CONST_TASK_2_STAGE_6_PROGRESS_DELAY = 30000 -- milliseconds
local CONST_TASK_2_STAGE_8_PROGRESS_DELAY = 60000 -- milliseconds
local CONST_TASK_2_STAGE_9_PROGRESS_DELAY = 40000 -- milliseconds
local CONST_TASK_2_BANDIT_AUTOKILL_DELAY = 40000 -- milliseconds
local CONST_TASK_2_CLEAR_SMART_DIST = 2500 -- meters squared
local CONST_TASK_2_ZOMBIE_1_DIST = 15625 -- meters squared
local CONST_TASK_2_SCHOOL_CLOSE = 400 -- meters squared
local CONST_TASK_2_SCHOOL_FAR = 4900 -- meters squared
-- Task variables
TASK_1_CACHE = {}
TASK_2_CACHE = {}
-- ---------------------------------------------------------------------------------------------------------------------
-- ACT 2 - CONTRACT WORK
-- ---------------------------------------------------------------------------------------------------------------------
-- TASK 1 - KOLOBOK
-- ---------------------------------------------------------------------------------------------------------------------
--- Function used to start Act 2, Task 1.
--- @param first_speaker cse_alife_object
--- @param second_speaker cse_alife_object
--- @return nil
function act_2_task_1_start(first_speaker, second_speaker)
local npc = dialogs.who_is_npc(first_speaker, second_speaker)
task_manager.get_task_manager():give_task("western_goods_act_2_task_1", npc:id())
dbg_printf("[WG] Tasks Act 2 | Task 1 - Task started...")
end
--- Function used to end Act 2, Task 1.
--- @return nil
function act_2_task_1_end()
local kolobok_found = false
-- Items in player inventory that have a section among the ones in fallback_items
local secondary_items_found = {}
-- Iterate player inventory to find if he has kolobok, and gather fallback items in the process
western_goods_utils.inventory_iter(db.actor,function(owner, obj)
local is_main_item = obj:section() == TASK_1_CACHE.main_item
local is_secondary_item = western_goods_utils.table_contains(TASK_1_CACHE.secondary_items, obj:section())
if is_main_item then
kolobok_found = true
return true
elseif is_secondary_item then
table.insert(secondary_items_found, obj)
end
end)
-- If the player doesn't have a Kolobok, use one of the fallback items
if not kolobok_found then
for _,obj in pairs(secondary_items_found) do
western_goods_utils.open_container(obj,false)
break
end
end
task_manager.get_task_manager():set_task_completed("western_goods_act_2_task_1")
TASK_2_CACHE.available_time = time_global() + CONST_TASK_2_HARD_DELAY
dbg_printf("[WG] Tasks Act 2 | Task 1 - Task completed...")
end
--- Function called when the task is initiated.
--- @param actor cse_alife_object
--- @param npc cse_alife_object
--- @return nil
function xr_effects.act_2_task_1_init(actor,npc)
-- Prepare task cache table
TASK_1_CACHE.main_item = "af_fuzz_kolobok"
TASK_1_CACHE.secondary_items = {
"af_fuzz_kolobok_lead_box",
"af_fuzz_kolobok_af_aac",
"af_fuzz_kolobok_af_aam",
"af_fuzz_kolobok_af_iam"
}
-- Spawn task target if it doesn't exist in the world
local main_item_exists = false
western_goods_utils.server_objects_iter(function(se_obj)
if se_obj:section_name() == TASK_1_CACHE.main_item then
dbg_printf("[WG] Tasks Act 2 | Task 1 - Main target already exist in the world %s", se_obj.id)
main_item_exists = true
return true
end
end)
if not main_item_exists then
dbg_printf("[WG] Tasks Act 2 | Task 1 - Main target does not exist in the world...")
local viable_spawn_anomalies = task_1_get_static_chemical_anomalies() or {}
local spawn_anomaly_id = random_key_table(viable_spawn_anomalies)
local spawn_anomaly = spawn_anomaly_id and alife_object(spawn_anomaly_id)
if spawn_anomaly then
alife_create(TASK_1_CACHE.main_item, spawn_anomaly.position, spawn_anomaly.m_level_vertex_id, spawn_anomaly.m_game_vertex_id)
dbg_printf("[WG] Tasks Act 2 | Task 1 - Spawned main target at pos:%s lvid:%s gvid:%s", spawn_anomaly.position, spawn_anomaly.m_level_vertex_id, spawn_anomaly.m_game_vertex_id)
end
end
-- Process info portions
western_goods_utils.give_info("western_goods_act_2_task_1_active")
western_goods_utils.give_info("western_goods_act_2_task_1_init")
end
--- Function called when the task is successfully completed.
--- @param actor cse_alife_object
--- @param npc cse_alife_object
--- @return nil
function xr_effects.act_2_task_1_complete(actor,npc)
-- Process info portions
western_goods_utils.rem_info("western_goods_act_2_task_1_active")
western_goods_utils.rem_info("western_goods_act_2_task_1_init")
western_goods_utils.give_info("western_goods_act_2_task_1_finished")
-- Process quest items
xr_effects.remove_item(actor, npc, {"af_fuzz_kolobok"})
-- Process reward
xr_effects.reward_random_money(actor,npc,{"10000","15000"})
xr_effects.complete_task_inc_goodwill(actor,npc,{"25","killer"})
end
--- Function called when the task is failed.
--- @param actor cse_alife_object
--- @param npc cse_alife_object
--- @return nil
function xr_effects.act_2_task_1_fail(actor,npc)
-- Process info portions
western_goods_utils.rem_info("western_goods_act_2_task_1_active")
western_goods_utils.rem_info("western_goods_act_2_task_1_init")
-- Process penalty
xr_effects.fail_task_dec_goodwill(actor,npc,{"35","killer"})
end
--- Function used to retrieve the title of the mission (displayed in the PDA).
--- @param task_id number
--- @param field string
--- @param p any
--- @param tsk CGameTask
--- @return string
function task_functor.act_2_task_1_title_f(task_id,field,p,tsk)
if true then -- Cond to force my IDE to fold this fucking function correctly
return western_goods_utils.get_translation("st_wg_act_2_task_1_title")
end
end
--- Function used to retrieve the description of the mission (displayed in the PDA).
--- Warning : naming contract on the translation string : st_wg_trader_act_2_task_1_stage_<stage>_descr.
--- @param task_id number
--- @param field string
--- @param p any
--- @param tsk CGameTask
--- @return string
function task_functor.act_2_task_1_descr_f(task_id,field,p,tsk)
if true then -- Cond to force my IDE to fold this fucking function correctly
return western_goods_utils.get_translation("st_wg_act_2_task_1_stage_" .. tostring(tsk.stage) .. "_descr")
end
end
--- Function used to retrieve the target of the mission (marker displayed (or not) in the PDA).
--- @param task_id number
--- @param field string
--- @param p any
--- @param tsk CGameTask
--- @return number
function task_functor.act_2_task_1_target_f(task_id,field,p,tsk)
if western_goods_utils.has_info("western_goods_act_2_task_1_init") then
if tsk.stage == 0 then
return nil
end
if tsk.stage == 1 then
return tsk.task_giver_id
end
end
end
--- Function used to manage the mission logic as a whole.
--- @param tsk CGameTask
--- @param task_id number
--- @return string
function task_status_functor.act_2_task_1_status_f(tsk,task_id)
if western_goods_utils.has_info("western_goods_act_2_task_1_init") then
-- First stage : Find the artefact
if tsk.stage == 0 then
western_goods_utils.inventory_iter(db.actor,function(owner, obj)
local is_main_item = obj:section() == TASK_1_CACHE.main_item
local is_secondary_item = western_goods_utils.table_contains(TASK_1_CACHE.secondary_items, obj:section())
if is_main_item or is_secondary_item then
dbg_printf("[WG] Tasks Act 2 | Task 1 - Stage 0 - Player found primary (%s) or secondary item (%s)",is_main_item,is_secondary_item)
tsk.stage = 1
return true
end
end)
end
-- Second stage : Deliver the artefact
if tsk.stage == 1 then
local has_item = false
western_goods_utils.inventory_iter(db.actor,function(owner, obj)
local is_main_item = obj:section() == TASK_1_CACHE.main_item
local is_secondary_item = western_goods_utils.table_contains(TASK_1_CACHE.secondary_items, obj:section())
if is_main_item or is_secondary_item then
has_item = true
return true
end
end)
if not has_item then
if western_goods_utils.has_info("western_goods_act_2_task_1_ready_finished") then
dbg_printf("[WG] Tasks Act 2 | Task 1 - Stage 1 - Task no longer ready to be completed...")
western_goods_utils.rem_info("western_goods_act_2_task_1_ready_finished")
end
tsk.stage = 0
return
end
if not western_goods_utils.has_info("western_goods_act_2_task_1_ready_finished") then
dbg_printf("[WG] Tasks Act 2 | Task 1 - Stage 1 - Task ready to be completed...")
western_goods_utils.give_info("western_goods_act_2_task_1_ready_finished")
end
end
end
end
-- ---------------------------------------------------------------------------------------------------------------------
-- TASK 2 - RETRIEVAL MISSION
-- ---------------------------------------------------------------------------------------------------------------------
--- Function used to start Act 2, Task 2.
--- @param task_giver cse_alife_object
--- @return nil
function act_2_task_2_start(task_giver)
task_manager.get_task_manager():give_task("western_goods_act_2_task_2", task_giver.id)
dbg_printf("[WG] Tasks Act 2 | Task 2 - Task started...")
end
--- Function used to end Act 2, Task 2.
--- @return nil
function act_2_task_2_end()
task_manager.get_task_manager():set_task_completed("western_goods_act_2_task_2")
dbg_printf("[WG] Tasks Act 2 | Task 2 - Task completed...")
end
--- Function called when the task is initiated.
--- @param actor cse_alife_object
--- @param npc cse_alife_object
--- @return nil
function xr_effects.act_2_task_2_init(actor,npc)
TASK_2_CACHE = {
main_item = "wg_act_2_task_2_quest_item",
marked_enemies = { },
objects = {
btr = { sec="veh_btr", x=-32.9, y=-0.5, z=20.9, lvid=155155, gvid=4971 },
sleeping_bag = { sec="itm_sleepbag", x=26.6, y=-4.3, z=129.2, lvid=232806, gvid=4970 }
},
squads = {
army_spawn_data = { sec="western_goods_act_2_task_2_army_squad", smart="pri_sim_3" },
mono_spawn_data = { sec="western_goods_act_2_task_2_mono_squad", smart="pri_sim_3" },
snork_spawn_data = { sec="western_goods_act_2_task_2_snork_squad", smart="pri_b301" },
ecolog_spawn_data = { sec="western_goods_act_2_task_2_ecolog_squad", smart="pri_a28_school", smart2="pri_b303" },
zombie_1_spawn_data = { sec="western_goods_act_2_task_2_zombie_1_squad", smart="pri_sim_3" },
zombie_2_spawn_data = { sec="western_goods_act_2_task_2_zombie_2_squad", smart="pri_sim_3" },
bandit_1_spawn_data = { sec="western_goods_act_2_task_2_bandit_1_squad", smart="pri_b301" },
bandit_2_spawn_data = { sec="western_goods_act_2_task_2_bandit_2_squad", smart="pri_a28_school" },
controller_spawn_data = { sec="western_goods_act_2_task_2_controller_squad", smart="pri_b303" },
lynn_spawn_data = { sec="stalker_dunn_lynn_squad", smart="ds2_lager_st" },
lynn_guards_spawn_data = { sec="western_goods_act_2_task_2_lynn_guards_squad", smart="ds2_lager_st" }
},
restrictors = {
zombie = { ax=-97.150344848633, ay=-1.1752129793167, az=-45.473430633545, bx=-81.533813476563, by=5.2640190124512, bz=-64.902114868164 },
snork = { ax=17.24550819397, ay=-4.823212146759, az=129.98883056641, bx=29.521947860718, by=-1.0844190120697, bz=121.89762878418 },
school = { ax=2.7305309772491, ay=-4.823212146759, az=180.73071289063, bx=76.082359313965, by=17.772342681885, bz=112.70443725586 },
overlook = { ax=27.744569778442, ay=7.1062412261963, az=129.99130249023, bx=53.035232543945, by=16.793748855591, bz=121.89153289795 },
kbo = { ax=3.7310662269592, ay=4.2264876365662, az=272.10140991211, bx=14.69687461853, by=8.0234889984131, bz=265.22302246094 }
}
}
-- Process info portions
western_goods_utils.give_info("western_goods_act_2_task_2_active")
western_goods_utils.give_info("western_goods_act_2_task_2_init")
end
--- Function called when the task is successfully completed.
--- @param actor cse_alife_object
--- @param npc cse_alife_object
--- @return nil
function xr_effects.act_2_task_2_complete(actor,npc)
-- Process info portions
western_goods_utils.rem_info("western_goods_act_2_task_2_active")
western_goods_utils.rem_info("western_goods_act_2_task_2_init")
western_goods_utils.give_info("western_goods_act_2_task_2_finished")
western_goods_utils.give_info("western_goods_act_2_finished")
-- Clean up
task_2_clean_squads()
-- Process quest items
xr_effects.remove_item(actor, npc, {"wg_act_2_task_2_quest_item"})
-- Process reward
xr_effects.reward_random_money(actor,npc,{"15000","20000"})
xr_effects.complete_task_inc_goodwill(actor,npc,{"100","isg"})
xr_effects.complete_task_inc_goodwill(actor,npc,{"25","killer"})
end
--- Function called when the task is failed.
--- @param actor cse_alife_object
--- @param npc cse_alife_object
--- @return nil
function xr_effects.act_2_task_2_fail(actor,npc)
-- Process info portions
western_goods_utils.rem_info("western_goods_act_2_task_2_active")
western_goods_utils.rem_info("western_goods_act_2_task_2_init")
-- Process penalty
xr_effects.fail_task_dec_goodwill(actor,npc,{"40","killer"})
end
--- Function used to retrieve the title of the mission (displayed in the PDA).
--- @param task_id number
--- @param field string
--- @param p any
--- @param tsk CGameTask
--- @return string
function task_functor.act_2_task_2_title_f(task_id,field,p,tsk)
if true then -- Cond to force my IDE to fold this fucking function correctly
return western_goods_utils.get_translation("st_wg_act_2_task_2_title")
end
end
--- Function used to retrieve the description of the mission (displayed in the PDA).
--- Warning : naming contract on the translation string : st_wg_trader_act_2_task_2_stage_<stage>_descr.
--- @param task_id number
--- @param field string
--- @param p any
--- @param tsk CGameTask
--- @return string
function task_functor.act_2_task_2_descr_f(task_id,field,p,tsk)
if true then -- Cond to force my IDE to fold this fucking function correctly
return western_goods_utils.get_translation("st_wg_act_2_task_2_stage_" .. tostring(tsk.stage) .. "_descr")
end
end
--- Function used to retrieve the target of the mission (marker displayed (or not) in the PDA).
--- @param task_id number
--- @param field string
--- @param p any
--- @param tsk CGameTask
--- @return number
function task_functor.act_2_task_2_target_f(task_id,field,p,tsk)
if western_goods_utils.has_info("western_goods_act_2_task_2_init") then
if tsk.stage == 0 then
local smart = SIMBOARD:get_smart_by_name("pri_sim_3")
return smart and smart.id
end
if tsk.stage == 1 then
local zombie_1_squad_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.squads.zombie_1_spawn_data.sec)
return zombie_1_squad_se and zombie_1_squad_se.id
end
if tsk.stage == 2 then
local zombie_2_squad_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.squads.zombie_2_spawn_data.sec)
return zombie_2_squad_se and zombie_2_squad_se.id
end
if tsk.stage == 3 then
-- Put a red marker over all the pursuers (only in pripyat, to avoid cheating)
if level.name() == "pripyat" then
task_2_mark_current_enemies(TASK_2_CACHE.marked_enemies)
end
-- Return nil as to not place an actual task marker
return nil
end
if tsk.stage == 4 then
-- Remove left-over markers (just in case)
if level.name() == "pripyat" then
task_2_mark_current_enemies(TASK_2_CACHE.marked_enemies)
end
local restrictor = db.zone_by_name["pri_surge_hide_b301"]
return restrictor and restrictor:id()
end
if tsk.stage == 5 then
local snork_squad_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.squads.snork_spawn_data.sec)
local restrictor = db.zone_by_name["pri_surge_hide_b301"]
return (snork_squad_se and snork_squad_se.id) or (restrictor and restrictor:id())
end
if tsk.stage == 6 then
local restrictor = db.zone_by_name["pri_surge_hide_b301"]
return restrictor and restrictor:id()
end
if tsk.stage == 7 then
local smart = SIMBOARD:get_smart_by_name("pri_b301")
return smart and smart.id
end
if tsk.stage == 8 then
local smart = SIMBOARD:get_smart_by_name("pri_b301")
return smart and smart.id
end
if tsk.stage == 9 then
local smart = SIMBOARD:get_smart_by_name("pri_b301")
return smart and smart.id
end
if tsk.stage == 10 then
local restrictor = db.zone_by_name["pri_surge_hide_b301"]
return restrictor and restrictor:id()
end
if tsk.stage == 11 then
local smart = SIMBOARD:get_smart_by_name("pri_b303")
return smart and smart.id
end
if tsk.stage == 12 then
local supplies_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.main_item)
return supplies_se and supplies_se.id
end
if tsk.stage == 13 then
local lynn_squad_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.squads.lynn_spawn_data.sec)
return lynn_squad_se and lynn_squad_se.id
end
end
end
--- Function used to manage the mission logic as a whole.
--- @param tsk CGameTask
--- @param task_id number
--- @return string
function task_status_functor.act_2_task_2_status_f(tsk,task_id)
if western_goods_utils.has_info("western_goods_act_2_task_2_init") then
-- Throughout the whole task : clear the two smarts so nothing interfere with the task
task_2_clear_smart_terrain("pri_a28_school")
task_2_clear_smart_terrain("pri_b301")
task_2_clear_smart_terrain("pri_b303")
-- First stage : Get to the Outskirts, somewhere close to the zombies
if tsk.stage == 0 then
local zombie_1_squad_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.squads.zombie_1_spawn_data.sec)
-- Spawn Zombie squad
if not TASK_2_CACHE.zombie_1_spawned and not zombie_1_squad_se then
local spawn_data = TASK_2_CACHE.squads.zombie_1_spawn_data
zombie_1_squad_se = western_goods_utils.spawn_squad_smart(spawn_data.sec, spawn_data.smart)
if zombie_1_squad_se then
TASK_2_CACHE.zombie_1_spawned = true
return
else
printf("![WG] ERROR | Tasks Act 2 | Failed to spawn %s smart:%s",spawn_data.sec, spawn_data.smart)
return "fail"
end
end
if level.name() ~= "pripyat" then return end
local dist_to_zombies = zombie_1_squad_se and western_goods_utils.get_distance_sqr(alife():actor().position,zombie_1_squad_se.position)
if (not dist_to_zombies) or dist_to_zombies and dist_to_zombies > CONST_TASK_2_ZOMBIE_1_DIST then return end
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 0 - Player close to Zombied squad %s", dist_to_zombies)
tsk.stage = 1
end
-- Second stage : Kill the zombies and get close
if tsk.stage == 1 then
local zombie_1_squad_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.squads.zombie_1_spawn_data.sec)
-- If the zombie squad is dead, and the player is near them
if not zombie_1_squad_se and western_goods_restrictors.in_bounds(db.actor, TASK_2_CACHE.restrictors.zombie) then
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 1 - First Zombied squad is dead...")
local zombie_2_squad_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.squads.zombie_2_spawn_data.sec)
-- Spawn Zombie squad for next stage
if not TASK_2_CACHE.zombie_2_spawned and not zombie_2_squad_se then
local spawn_data = TASK_2_CACHE.squads.zombie_2_spawn_data
zombie_2_squad_se = western_goods_utils.spawn_squad_smart(spawn_data.sec, spawn_data.smart)
if zombie_2_squad_se then
TASK_2_CACHE.spawn_throttle = time_global() + CONST_SPAWN_THROTTLE
TASK_2_CACHE.zombie_2_spawned = true
return
else
printf("![WG] ERROR | Tasks Act 2 | Failed to spawn %s smart:%s",spawn_data.sec, spawn_data.smart)
return "fail"
end
end
tsk.stage = 2
end
end
-- Third stage : Wait for zombies to die or take part in the fight
if tsk.stage == 2 then
local zombie_2_squad_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.squads.zombie_2_spawn_data.sec)
local army_squad_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.squads.army_spawn_data.sec)
-- Spawn Army squad
if TASK_2_CACHE.spawn_throttle < time_global() and not TASK_2_CACHE.army_squad_spawned and not army_squad_se then
local spawn_data = TASK_2_CACHE.squads.army_spawn_data
army_squad_se = western_goods_utils.spawn_squad_smart(spawn_data.sec, spawn_data.smart)
if army_squad_se then
TASK_2_CACHE.spawn_throttle = time_global() + CONST_SPAWN_THROTTLE
TASK_2_CACHE.army_squad_spawned = true
return
else
printf("![WG] ERROR | Tasks Act 2 | Failed to spawn %s smart:%s",spawn_data.sec, spawn_data.smart)
return "fail"
end
end
-- Spawn Army BTR
if TASK_2_CACHE.spawn_throttle < time_global() and not TASK_2_CACHE.army_btr_spawned then
local spawn_data = TASK_2_CACHE.objects.btr
local btr_se = alife_create(spawn_data.sec, vector():set(spawn_data.x, spawn_data.y, spawn_data.z), spawn_data.lvid, spawn_data.gvid)
if btr_se then
btr_se.angle = vector():set(0,-1.5708,0)
logic_enforcer.assign(btr_se.id,'scripts\\tasks\\veh_idle.ltx','logic','ph_car@idle')
TASK_2_CACHE.spawn_throttle = time_global() + CONST_SPAWN_THROTTLE
TASK_2_CACHE.army_btr_spawned = true
return
else
printf("![WG] ERROR | Tasks Act 2 | Failed to spawn %s x:%s y:%s z:%s lvid:%s gvid:%s",spawn_data.sec, spawn_data.x, spawn_data.y, spawn_data.z, spawn_data.lvid, spawn_data.gvid)
return "fail"
end
end
-- When zombies are dead, progress task
if TASK_2_CACHE.zombie_2_spawned and not zombie_2_squad_se then
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 2 - Second Zombied squad is dead...")
send_dialog({
{sender="Anonymous", icon="ui_inGame2_no_data", message=western_goods_utils.get_translation("st_wg_trader_act_2_task_2_stage_2_message_1")}
})
if western_goods_utils.is_player_fighting() then
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 2 - Player is fighting :\n%s", utils_data.print_table(xr_combat_ignore.fighting_with_actor_npcs, false, true))
tsk.stage = 3
else
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 2 - Player is not fighting...")
tsk.stage = 4
end
end
end
-- Fourth stage : Get to the basement of the school but kill pursuers
if tsk.stage == 3 then
if western_goods_utils.is_player_fighting() then
local restrictor = db.zone_by_name["pri_surge_hide_b301"]
local dist_to_basement = restrictor and western_goods_utils.get_distance_sqr(alife():actor().position,restrictor:position())
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 3 - Player is fighting :\n%s", utils_data.print_table(xr_combat_ignore.fighting_with_actor_npcs, false, true))
if dist_to_basement and dist_to_basement <= CONST_TASK_2_SCHOOL_FAR and dist_to_basement >= CONST_TASK_2_SCHOOL_CLOSE then
if not TASK_2_CACHE.message_school_pursuers_far_sent then
send_dialog({
{sender="Anonymous", icon="ui_inGame2_no_data", message=western_goods_utils.get_translation("st_wg_trader_act_2_task_2_message_school_pursuers_far")}
},true)
TASK_2_CACHE.message_school_pursuers_far_sent = true
end
return
elseif dist_to_basement and dist_to_basement < CONST_TASK_2_SCHOOL_CLOSE then
send_dialog({
{sender="Anonymous", icon="ui_inGame2_no_data", message=western_goods_utils.get_translation("st_wg_trader_act_2_task_2_message_school_pursuers_close")}
},true)
return "fail"
else
TASK_2_CACHE.message_school_pursuers_far_sent = false
return
end
else
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 3 - Player is not fighting...")
TASK_2_CACHE.message_school_pursuers_far_sent = false
tsk.stage = 4
end
end
-- Fifth stage : Get to the basement of the school without being seen
if tsk.stage == 4 then
local restrictor = db.zone_by_name["pri_surge_hide_b301"]
if not western_goods_utils.is_player_fighting() then
local dist_to_basement = restrictor and western_goods_utils.get_distance_sqr(alife():actor().position,restrictor:position())
if dist_to_basement and dist_to_basement < CONST_TASK_2_SCHOOL_CLOSE then
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 4 - Player arrived at the school dist(sqr):%s",dist_to_basement)
local snork_squad_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.squads.snork_spawn_data.sec)
-- Spawn snork squad
if not TASK_2_CACHE.snork_spawned and not snork_squad_se then
local spawn_data = TASK_2_CACHE.squads.snork_spawn_data
snork_squad_se = western_goods_utils.spawn_squad_smart(spawn_data.sec, spawn_data.smart)
if snork_squad_se then
TASK_2_CACHE.snork_spawned = true
tsk.stage = 5
return
else
printf("![WG] ERROR | Tasks Act 2 | Failed to spawn %s smart:%s",spawn_data.sec, spawn_data.smart)
return "fail"
end
end
end
else
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 4 - Player is fighting :\n%s", utils_data.print_table(xr_combat_ignore.fighting_with_actor_npcs, false, true))
tsk.stage = 3
end
end
-- Sixth stage : Kill the snorks
if tsk.stage == 5 then
local snork_squad_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.squads.snork_spawn_data.sec)
-- Dialog when player reaches school
if not TASK_2_CACHE.stage_5_dialog_sent then
local dialog_sender_1 = { name=alife():actor():character_name(), icon=alife():actor():character_icon()}
local dialog_sender_2 = { name="Anonymous", icon="ui_inGame2_no_data"}
send_dialog({
{sender=dialog_sender_1.name, icon=dialog_sender_1.icon, message=western_goods_utils.get_translation("st_wg_trader_act_2_task_2_stage_5_message_1")},
{sender=dialog_sender_2.name, icon=dialog_sender_2.icon, message=western_goods_utils.get_translation("st_wg_trader_act_2_task_2_stage_5_message_2")}
})
TASK_2_CACHE.stage_5_dialog_sent = true
end
-- Spawn sleeping bag
if not TASK_2_CACHE.sleeping_bag_spawned then
local spawn_data = TASK_2_CACHE.objects.sleeping_bag
local sleep_bag_se = alife_create(spawn_data.sec, vector():set(spawn_data.x, spawn_data.y, spawn_data.z), spawn_data.lvid, spawn_data.gvid)
if sleep_bag_se then
TASK_2_CACHE.sleeping_bag_spawned = true
return
else
printf("![WG] ERROR | Tasks Act 2 | Failed to spawn %s x:%s y:%s z:%s lvid:%s gvid:%s",spawn_data.sec, spawn_data.x, spawn_data.y, spawn_data.z, spawn_data.lvid, spawn_data.gvid)
return "fail"
end
end
-- Fail task if player gets out of school
if not western_goods_restrictors.in_bounds(db.actor, TASK_2_CACHE.restrictors.school) then
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 5 - Player left restrictor bounds...")
send_dialog({
{sender="Anonymous", icon="ui_inGame2_no_data", message=western_goods_utils.get_translation("st_wg_trader_act_2_task_2_message_left_position")}
},true)
return "fail"
end
-- If the zombie squad is dead, and the player is near them
if TASK_2_CACHE.snork_spawned and not snork_squad_se and western_goods_restrictors.in_bounds(db.actor, TASK_2_CACHE.restrictors.snork) then
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 5 - Snork squad is dead...")
local bandit_1_squad_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.squads.bandit_1_spawn_data.sec)
-- Spawn bandit squad for next stage
if not TASK_2_CACHE.bandit_1_spawned and not bandit_1_squad_se then
local spawn_data = TASK_2_CACHE.squads.bandit_1_spawn_data
bandit_1_squad_se = western_goods_utils.spawn_squad_smart(spawn_data.sec, spawn_data.smart)
if bandit_1_squad_se then
TASK_2_CACHE.bandit_1_spawned = true
return
else
printf("![WG] ERROR | Tasks Act 2 | Failed to spawn %s smart:%s",spawn_data.sec, spawn_data.smart)
return "fail"
end
end
tsk.stage = 6
end
end
-- Seventh stage : Kill the bandits
if tsk.stage == 6 then
local bandit_1_squad_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.squads.bandit_1_spawn_data.sec)
-- Fail task if player gets out of school
if not western_goods_restrictors.in_bounds(db.actor, TASK_2_CACHE.restrictors.school) then
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 6 - Player left restrictor bounds...")
send_dialog({
{sender="Anonymous", icon="ui_inGame2_no_data", message=western_goods_utils.get_translation("st_wg_trader_act_2_task_2_message_left_position")}
},true)
return "fail"
end
-- If bandits are dead, or if they are not enemy with the player
if TASK_2_CACHE.bandit_1_spawned and not bandit_1_squad_se then
if not TASK_2_CACHE.stage_6_dialog_sent then
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 6 - First bandit squad is dead...")
local dialog_sender_1 = { name="Anonymous", icon="ui_inGame2_no_data"}
local dialog_sender_2 = { name=alife():actor():character_name(), icon=alife():actor():character_icon()}
send_dialog({
{sender=dialog_sender_1.name, icon=dialog_sender_1.icon, message=western_goods_utils.get_translation("st_wg_trader_act_2_task_2_stage_6_message_1")},
{sender=dialog_sender_2.name, icon=dialog_sender_2.icon, message=western_goods_utils.get_translation("st_wg_trader_act_2_task_2_stage_6_message_2")},
{sender=dialog_sender_1.name, icon=dialog_sender_1.icon, message=western_goods_utils.get_translation("st_wg_trader_act_2_task_2_stage_6_message_3")},
{sender=dialog_sender_2.name, icon=dialog_sender_2.icon, message=western_goods_utils.get_translation("st_wg_trader_act_2_task_2_stage_6_message_4")}
})
TASK_2_CACHE.progress_timer = time_global() + CONST_TASK_2_STAGE_6_PROGRESS_DELAY
TASK_2_CACHE.stage_6_dialog_sent = true
end
if TASK_2_CACHE.progress_timer < time_global() then
tsk.stage = 7
end
end
end
-- Eighth stage : Get to the 3rd floor of the school
if tsk.stage == 7 then
-- When the player gets in position, progress task
if western_goods_restrictors.in_bounds(db.actor, TASK_2_CACHE.restrictors.overlook) then
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 7 - Player reached third floor of school...")
tsk.stage = 8
return
end
end
-- Ninth stage : Wait for ecologs to arrive
if tsk.stage == 8 then
local ecolog_squad_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.squads.ecolog_spawn_data.sec)
-- Spawn Ecolog squad
if not TASK_2_CACHE.ecolog_spawned and not ecolog_squad_se then
local spawn_data = TASK_2_CACHE.squads.ecolog_spawn_data
ecolog_squad_se = western_goods_utils.spawn_squad_smart(spawn_data.sec, spawn_data.smart)
if ecolog_squad_se then
TASK_2_CACHE.progress_timer = time_global() + CONST_TASK_2_STAGE_8_PROGRESS_DELAY
TASK_2_CACHE.ecolog_spawned = true
return
else
printf("![WG] ERROR | Tasks Act 2 | Failed to spawn %s smart:%s",spawn_data.sec, spawn_data.smart)
return "fail"
end
end
-- If player leaves his position, fail task
if not western_goods_restrictors.in_bounds(db.actor, TASK_2_CACHE.restrictors.overlook) then
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 8 - Player left restrictor bounds...")
send_dialog({
{sender="Anonymous", icon="ui_inGame2_no_data", message=western_goods_utils.get_translation("st_wg_trader_act_2_task_2_message_left_position")}
},true)
return "fail"
end
-- After 45s, progress task
if TASK_2_CACHE.progress_timer < time_global() then
tsk.stage = 9
end
end
-- Tenth stage : Wait for the bandits to die
if tsk.stage == 9 then
local bandit_1_squad_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.squads.bandit_1_spawn_data.sec)
local bandit_2_squad_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.squads.bandit_2_spawn_data.sec)
-- Spawn Bandit squad
if not TASK_2_CACHE.bandit_2_squad_spawned and not bandit_2_squad_se then
local spawn_data = TASK_2_CACHE.squads.bandit_2_spawn_data
bandit_2_squad_se = western_goods_utils.spawn_squad_smart(spawn_data.sec, spawn_data.smart)
if bandit_2_squad_se then
-- Dialog when bandit spawn
if not TASK_2_CACHE.stage_9_dialog_sent then
local dialog_sender = { name="Anonymous", icon="ui_inGame2_no_data"}
send_dialog({
{sender=dialog_sender.name, icon=dialog_sender.icon, message=western_goods_utils.get_translation("st_wg_trader_act_2_task_2_stage_9_message")}
})
TASK_2_CACHE.stage_9_dialog_sent = true
end
TASK_2_CACHE.bandit_2_autokill_timer = time_global() + CONST_TASK_2_BANDIT_AUTOKILL_DELAY
TASK_2_CACHE.bandit_2_squad_spawned = true
return
else
printf("![WG] ERROR | Tasks Act 2 | Failed to spawn %s smart:%s",spawn_data.sec, spawn_data.smart)
return "fail"
end
end
-- If player leaves his position, fail task
if not western_goods_restrictors.in_bounds(db.actor, TASK_2_CACHE.restrictors.overlook) then
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 9 - Player left restrictor bounds...")
send_dialog({
{sender="Anonymous", icon="ui_inGame2_no_data", message=western_goods_utils.get_translation("st_wg_trader_act_2_task_2_message_left_position")}
},true)
return "fail"
end
-- After 20s, start killing the bandits (both bandit_1 and bandit_2 squads) automatically
if not TASK_2_CACHE.bandit_2_autokilled and TASK_2_CACHE.bandit_2_autokill_timer < time_global() then
if bandit_1_squad_se then
for npc in bandit_1_squad_se:squad_members() do
local npc_obj = db.storage[npc.id] and db.storage[npc.id].object
if (npc_obj and npc_obj:alive()) then
western_goods_utils.next_tick(surge_manager.make_dead,npc_obj:id())
end
end
end
if bandit_2_squad_se then
for npc in bandit_2_squad_se:squad_members() do
local npc_obj = db.storage[npc.id] and db.storage[npc.id].object
if (npc_obj and npc_obj:alive()) then
western_goods_utils.next_tick(surge_manager.make_dead,npc_obj:id())
end
end
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 9 - Bandit squads auto-killed...")
end
TASK_2_CACHE.progress_timer = time_global() + CONST_TASK_2_STAGE_9_PROGRESS_DELAY
TASK_2_CACHE.bandit_2_autokilled = true
end
-- When all bandits are dead, progress task
if not bandit_1_squad_se and not bandit_2_squad_se and TASK_2_CACHE.progress_timer < time_global() then
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 9 - Both bandit squads are dead...")
tsk.stage = 10
end
end
-- Eleventh stage : Take cover from the emission
if tsk.stage == 10 then
local ecolog_squad_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.squads.ecolog_spawn_data.sec)
-- Start emission and make ecologs flee
if not TASK_2_CACHE.surge_started then
surge_manager.start_surge()
-- Give info to link script with logic scheme
western_goods_utils.give_info("western_goods_act_2_task_2_ecolog_flee")
TASK_2_CACHE.ecolog_teleport_timer = time_global() + CONST_TASK_2_ECOLOG_TELEPORT_DELAY
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 10 - Emission started...")
TASK_2_CACHE.surge_started = true
end
-- Teleport ecolog squad because stalker and pathfinding definitely don't make one...
if TASK_2_CACHE.ecolog_teleport_timer < time_global() and not TASK_2_CACHE.ecolog_teleported or not ecolog_squad_se then
local cond_no_rem_no_exist = not TASK_2_CACHE.old_ecolog_squad_removed and not ecolog_squad_se
local cond_no_rem_exist = not TASK_2_CACHE.old_ecolog_squad_removed and ecolog_squad_se
local cond_rem_no_exist = TASK_2_CACHE.old_ecolog_squad_removed and not ecolog_squad_se
-- Release 'old' squad
if cond_no_rem_exist then
SIMBOARD:remove_squad(ecolog_squad_se)
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 10 - Ecolog squad removed...")
TASK_2_CACHE.old_ecolog_squad_removed = true
return
end
-- Create 'new' squad at new location
if cond_rem_no_exist or cond_no_rem_no_exist then
local spawn_data = TASK_2_CACHE.squads.ecolog_spawn_data
ecolog_squad_se = western_goods_utils.spawn_squad_smart(spawn_data.sec, spawn_data.smart2)
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 10 - Ecolog squad re-added...")
TASK_2_CACHE.ecolog_teleported = true
return
end
end
if TASK_2_CACHE.surge_started and surge_manager.is_finished() and TASK_2_CACHE.ecolog_teleported and ecolog_squad_se then
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 10 - Emission finished...")
for member in ecolog_squad_se:squad_members() do
local member_se = alife_object(member.id)
if member_se:section_name() ~= "stalker_ecolog_convoy_yellow" then
CreateTimeEvent("western_goods_delay_kill_squad",member.id,0,surge_manager.make_dead,member.id)
end
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 10 - Trimmed Ecolog squad...")
end
local dialog_sender_1 = { name="Anonymous", icon="ui_inGame2_no_data"}
local dialog_sender_2 = { name=alife():actor():character_name(), icon=alife():actor():character_icon()}
send_dialog({
{sender=dialog_sender_1.name, icon=dialog_sender_1.icon, message=western_goods_utils.get_translation("st_wg_trader_act_2_task_2_stage_10_message_1")},
{sender=dialog_sender_2.name, icon=dialog_sender_2.icon, message=western_goods_utils.get_translation("st_wg_trader_act_2_task_2_stage_10_message_2")},
{sender=dialog_sender_1.name, icon=dialog_sender_1.icon, message=western_goods_utils.get_translation("st_wg_trader_act_2_task_2_stage_10_message_3")}
})
tsk.stage = 11
end
end
-- Twelfth stage : Get to the yellow crewmate & kill the controller
if tsk.stage == 11 then
local controller_squad_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.squads.controller_spawn_data.sec)
local supplies_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.main_item)
-- Wait for the player to reach the KBO
if not TASK_2_CACHE.kbo_room_reached then
if not western_goods_restrictors.in_bounds(db.actor, TASK_2_CACHE.restrictors.kbo) then
return
end
TASK_2_CACHE.kbo_room_reached = true
western_goods_utils.give_info("western_goods_act_2_task_2_ecolog_calm")
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 11 - Calmed Yellow crewmate...")
end
-- Send signal to logic scheme
western_goods_utils.give_info("western_goods_act_2_task_2_ecolog_calm")
-- Wait for player to finish dialog with crewmate
if not western_goods_utils.has_info("western_goods_ecolog_convoy_yellow_meet_over") then return end
-- Spawn Controller squad
if not TASK_2_CACHE.controller_squad_spawned and not controller_squad_se then
local spawn_data = TASK_2_CACHE.squads.controller_spawn_data
controller_squad_se = western_goods_utils.spawn_squad_smart(spawn_data.sec, spawn_data.smart)
if controller_squad_se then
western_goods_utils.give_info("western_goods_act_2_task_2_controller_spawned")
TASK_2_CACHE.controller_squad_spawned = true
return
else
printf("![WG] ERROR | Tasks Act 2 | Failed to spawn %s smart:%s",spawn_data.sec, spawn_data.smart)
return "fail"
end
end
-- Wait for controller to be dead
if not TASK_2_CACHE.controller_squad_killed then
if TASK_2_CACHE.controller_squad_spawned and controller_squad_se then
return
end
TASK_2_CACHE.controller_squad_killed = true
western_goods_utils.give_info("western_goods_act_2_task_2_controller_killed")
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 11 - Controller is dead...")
end
-- Send signal to logic scheme
western_goods_utils.give_info("western_goods_act_2_task_2_controller_killed")
-- Wait for player to finish second dialog
if not western_goods_utils.has_info("western_goods_ecolog_convoy_yellow_second_meet_over") then return end
local lynn_squad_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.squads.lynn_spawn_data.sec)
local lynn_guards_squad_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.squads.lynn_guards_spawn_data.sec)
-- Spawn Dunn Lynn squad
if not TASK_2_CACHE.lynn_squad_spawned and not lynn_squad_se then
local spawn_data = TASK_2_CACHE.squads.lynn_spawn_data
lynn_squad_se = western_goods_utils.spawn_squad_smart(spawn_data.sec, spawn_data.smart)
if lynn_squad_se then
local dialog_sender_1 = { name=alife():actor():character_name(), icon=alife():actor():character_icon()}
local dialog_sender_2 = { name="Anonymous", icon="ui_inGame2_no_data"}
send_dialog({
{sender=dialog_sender_1.name, icon=dialog_sender_1.icon, message=western_goods_utils.get_translation("st_wg_trader_act_2_task_2_stage_11_message_1")},
{sender=dialog_sender_2.name, icon=dialog_sender_2.icon, message=western_goods_utils.get_translation("st_wg_trader_act_2_task_2_stage_11_message_2")}
})
TASK_2_CACHE.lynn_squad_spawned = true
return
else
printf("![WG] ERROR | Tasks Act 2 | Failed to spawn %s smart:%s",spawn_data.sec, spawn_data.smart)
return "fail"
end
end
-- Spawn Dunn Lynn guards squad
if not TASK_2_CACHE.lynn_guards_squad_spawned and not lynn_guards_squad_se then
local spawn_data = TASK_2_CACHE.squads.lynn_guards_spawn_data
lynn_guards_squad_se = western_goods_utils.spawn_squad_smart(spawn_data.sec, spawn_data.smart)
if lynn_guards_squad_se then
TASK_2_CACHE.lynn_guards_squad_spawned = true
return
else
printf("![WG] ERROR | Tasks Act 2 | Failed to spawn %s smart:%s",spawn_data.sec, spawn_data.smart)
return "fail"
end
end
if not (supplies_se and (supplies_se.parent_id ~= AC_ID)) then
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 11 - Player has the main item %s",supplies_se.id)
tsk.stage = 13
else
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 11 - Player does not have the main item...")
tsk.stage = 12
end
end
-- Thirteenth stage : Get the case
if tsk.stage == 12 then
local supplies_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.main_item)
-- If the supplies cannot be found, respawn them
if not supplies_se then
-- Spawn the supplies, objective the player has to retrieve
supplies_se = alife_create(TASK_2_CACHE.main_item, vector():set(4.8,4.6,269.3), 204172, 4926)
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 12 - Main item respawned %s", supplies_se.id)
return
end
-- If the player picked up the supplies, progress to stage 3
if not (supplies_se and (supplies_se.parent_id ~= AC_ID)) then
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 12 - Player picked up the main item %s", supplies_se.id)
tsk.stage = 13
return
end
end
-- Thirteenth stage : Return the case
if tsk.stage == 13 then
local supplies_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.main_item)
-- If the supplies cannot be found (which is odd), roll back to stage 12
if not supplies_se then
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 13 - Main item disappeared, rolling back to stage 12...")
tsk.stage = 12
return
end
western_goods_utils.give_info("western_goods_act_2_task_2_ready_finished")
-- If the player dropped the supplies, rollback to stage 2
if supplies_se and (supplies_se.parent_id ~= AC_ID) then
dbg_printf("[WG] Tasks Act 2 | Task 2 - Stage 13 - Player dropped the main item %s", supplies_se.id)
western_goods_utils.rem_info("western_goods_act_2_task_2_ready_finished")
tsk.stage = 12
return
end
end
end
end
-- ---------------------------------------------------------------------------------------------------------------------
-- Callbacks registration
-- ---------------------------------------------------------------------------------------------------------------------
--- Function used to register callbacks.
--- @return nil
function on_game_start()
RegisterScriptCallback("save_state", save_state)
RegisterScriptCallback("load_state", load_state)
RegisterScriptCallback("on_game_load", task_2_prefetch_models)
RegisterScriptCallback("actor_on_first_update", task_2_setup)
RegisterScriptCallback("actor_on_first_update", task_2_remove_dunn_lynn_after_task)
end
-- ---------------------------------------------------------------------------------------------------------------------
-- Data persistence
-- ---------------------------------------------------------------------------------------------------------------------
--- Function used to store information in the save file.
--- @param m_data table
--- @return nil
function save_state(m_data)
-- Prepare save tables
local TASK_1_SAVE = {}
local TASK_2_SAVE = {}
-- Make copies of task caches
copy_table(TASK_1_SAVE, TASK_1_CACHE)
copy_table(TASK_2_SAVE, TASK_2_CACHE)
-- Pre-process tables
TASK_2_SAVE.available_time = (TASK_2_SAVE.available_time or 0) - time_global()
TASK_2_SAVE.spawn_throttle = (TASK_2_SAVE.spawn_throttle or 0) - time_global()
TASK_2_SAVE.progress_timer = (TASK_2_SAVE.progress_timer or 0) - time_global()
TASK_2_SAVE.bandit_2_autokill_timer = (TASK_2_SAVE.bandit_2_autokill_timer or 0) - time_global()
-- Save tables
m_data.wg_act_2_task_1_cache = TASK_1_SAVE
m_data.wg_act_2_task_2_cache = TASK_2_SAVE
-- Debug prints
dbg_printf("[WG] Tasks Act 2 | Task 1 - Saved variables...\n%s",utils_data.print_table(TASK_1_SAVE, false, true))
dbg_printf("[WG] Tasks Act 2 | Task 2 - Saved variables...\n%s",utils_data.print_table(TASK_2_SAVE, false, true))
end
--- Function used to load information stored in the save file.
--- @param m_data table
--- @return nil
function load_state(m_data)
-- Retrieve save tables
local TASK_1_SAVE = m_data.wg_act_2_task_1_cache or {}
local TASK_2_SAVE = m_data.wg_act_2_task_2_cache or {}
-- Post-process tables
TASK_2_SAVE.available_time = (TASK_2_SAVE.available_time or 0) + time_global()
TASK_2_SAVE.spawn_throttle = (TASK_2_SAVE.spawn_throttle or 0) + time_global()
TASK_2_SAVE.progress_timer = (TASK_2_SAVE.progress_timer or 0) + time_global()
TASK_2_SAVE.bandit_2_autokill_timer = (TASK_2_SAVE.bandit_2_autokill_timer or 0) + time_global()
-- Restore task caches
copy_table(TASK_1_CACHE, TASK_1_SAVE)
copy_table(TASK_2_CACHE, TASK_2_SAVE)
-- Debug prints
dbg_printf("[WG] Tasks Act 2 | Task 1 - Loaded variables...\n%s",utils_data.print_table(TASK_1_CACHE, false, true))
dbg_printf("[WG] Tasks Act 2 | Task 2 - Loaded variables...\n%s",utils_data.print_table(TASK_2_CACHE, false, true))
end
-- ---------------------------------------------------------------------------------------------------------------------
-- General functions
-- ---------------------------------------------------------------------------------------------------------------------
--- Function used to get a table where keys are IDs of chemical anomalies and values are boolean true.
--- @return table
function task_1_get_static_chemical_anomalies()
local viable_anomalies = {}
-- Set to false all dynamic anomalies
for _,v in pairs(bind_anomaly_field.dyn_anomalies) do
for id, _ in pairs(v) do
viable_anomalies[id] = false
end
end
-- Go through all IDs
western_goods_utils.server_objects_iter(function(se_obj)
if IsAnomaly(se_obj) then
if viable_anomalies[se_obj.id] == nil then
-- If the ID is unknown, then check for correct type of anomalies
if string.find(se_obj:section_name(), "chemical") or string.find(se_obj:section_name(), "acidic") then
viable_anomalies[se_obj.id] = true
end
else
-- If the ID isn't nil, then it's a dynamic anomaly, we set to nil
viable_anomalies[se_obj.id] = nil
end
end
end)
dbg_printf("[WG] Tasks Act 2 | Task 1 - Number of matching anomalies : %s", size_table(viable_anomalies))
return viable_anomalies
end
--- Function used to setup the automatic start of Act 2, Task 2.
--- @return nil
function task_2_setup()
-- Conditions
local task_1_done = western_goods_utils.has_info("western_goods_act_2_task_1_finished")
local task_2_started = western_goods_utils.has_info("western_goods_act_2_task_2_active")
local task_2_done = western_goods_utils.has_info("western_goods_act_2_task_2_finished")
local task_opted_in = western_goods_utils.has_info("western_goods_act_2_task_2_opted_in")
local timeout_ready = time_global() > (TASK_2_CACHE.available_time or 0)
-- If the task is available
if not task_1_done or task_2_started or task_2_done then return end
-- If the task should be set up
if task_opted_in and timeout_ready then
local delay = math.random(CONST_TASK_2_SOFT_DELAY_MIN, CONST_TASK_2_SOFT_DELAY_MAX)
CreateTimeEvent("western_goods_setup_task", "act_2_task_2", delay, function ()
dbg_printf("[WG] Tasks Act 2 | Task 2 - Task set up, time before start : %s",delay)
-- Give the task some time after loading the save
act_2_task_2_start(western_goods_utils.server_object_by_sid("cit_killers_merc_trader_stalker"))
-- Send a message to the player
send_dialog({
{sender="Anonymous", icon="ui_inGame2_no_data", message=western_goods_utils.get_translation("st_wg_act_2_task_2_job_descr")}
}, true)
return true
end)
else
dbg_printf("[WG] Tasks Act 2 | Task 2 - What's missing? task_opted_in:%s - timeout_ready:%s (%s/%s)", task_opted_in, timeout_ready, time_global(), TASK_2_CACHE.available_time)
end
end
--- Function used to clean up the squads after Act 2, Task 2.
--- @return nil
function task_2_clean_squads()
western_goods_utils.server_objects_iter(function(se_obj)
for _,spawn_data in pairs(TASK_2_CACHE.squads) do
local is_from_task_2 = se_obj:section_name() == spawn_data.sec
local is_not_dunn_lynn = se_obj:section_name() ~= TASK_2_CACHE.squads.lynn_spawn_data.sec
local is_not_dunn_guards = se_obj:section_name() ~= TASK_2_CACHE.squads.lynn_guards_spawn_data.sec
if is_from_task_2 and is_not_dunn_lynn and is_not_dunn_guards then
dbg_printf("[WG] Tasks Act 2 | Task 2 - Removing '%s'",se_obj:name())
SIMBOARD:remove_squad(se_obj)
end
end
end)
dbg_printf("[WG] Tasks Act 2 | Task 2 - Removed squads ...")
end
--- Function used to mark on the PDA the enemies currently fighting with the player.
--- @param marked_enemies table
--- @return nil
function task_2_mark_current_enemies(marked_enemies)
for _,id in pairs(marked_enemies) do
level.map_remove_object_spot(id, "anomaly_thermal")
end
empty_table(marked_enemies)
for id,_ in pairs(xr_combat_ignore.fighting_with_actor_npcs) do
if not western_goods_utils.table_contains(marked_enemies, id) then
table.insert(marked_enemies, id)
level.map_add_object_spot_ser(id, "anomaly_thermal", "Enemy")
end
end
end
--- Function called from dialog to give the player the quest item during Act 2, Task 2.
--- @param first_speaker game_object
--- @param second_speaker game_object
--- @return nil
function task_2_give_quest_item(first_speaker, second_speaker)
dialogs.relocate_item_section_to_actor(first_speaker, second_speaker, "wg_act_2_task_2_quest_item")
end
--- Function used to desspawn Dunn Lynn and his guards after Act 2, Task 2 is done and the player is on another level.
--- @return nil
function task_2_remove_dunn_lynn_after_task()
if western_goods_utils.has_info("western_goods_act_2_task_2_finished") and not (TASK_2_CACHE.lynn_squad_removed and TASK_2_CACHE.lynn_guards_squad_removed) then
local lynn_squad_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.squads.lynn_spawn_data.sec)
local lynn_guards_squad_se = western_goods_utils.server_object_by_sid(TASK_2_CACHE.squads.lynn_guards_spawn_data.sec)
dbg_printf("[WG] Tasks Act 2 | Task 2 - Trying to remove Lynn and/or his guards")
-- Remove Dunn Lynn squad if it wasn't removed yet
if not TASK_2_CACHE.lynn_squad_removed and lynn_squad_se and not simulation_objects.is_on_the_same_level(alife():actor(), lynn_squad_se) then
SIMBOARD:remove_squad(lynn_squad_se)
dbg_printf("[WG] Tasks Act 2 | Task 2 - Removed '%s'", lynn_squad_se:section_name())
end
-- Remove Dunn Lynn guards squad if it wasn't removed yet
if not TASK_2_CACHE.lynn_guards_squad_removed and lynn_guards_squad_se and not simulation_objects.is_on_the_same_level(alife():actor(), lynn_guards_squad_se)then
SIMBOARD:remove_squad(lynn_guards_squad_se)
dbg_printf("[WG] Tasks Act 2 | Task 2 - Removed '%s'", lynn_guards_squad_se:section_name())
end
end
end
--- Function used to clear all the squads at a given smart terrain, except the squads defined in TASK_2_CACHE.
--- @param smart_name string
--- @return nil
function task_2_clear_smart_terrain(smart_name)
local smart_clear = SIMBOARD:get_smart_by_name(smart_name)
for id,bool in pairs(SIMBOARD.smarts[smart_clear.id].squads) do
local can_stay = false
local squad = alife_object(id)
if squad then
for _,spawn_data in pairs(TASK_2_CACHE.squads) do
if squad:section_name() == spawn_data.sec then
can_stay = true
end
end
local is_another_map = not simulation_objects.is_on_the_same_level(alife():actor(), squad)
local distance = western_goods_utils.get_distance_sqr(alife():actor().position,squad.position)
if not can_stay and (is_another_map or distance > CONST_TASK_2_CLEAR_SMART_DIST) then
SIMBOARD:remove_squad(squad)
dbg_printf("[WG] Tasks Act 2 | Task 2 - Released random squad '%s' from smart '%s' (dist(sqr):%s)",squad:name(),smart_clear:name(),distance)
end
end
end
end
--- Function used to front load some models if Act 2, Task 2 is active.
--- @return nil
function task_2_prefetch_models()
local tsk = task_manager.get_task_manager().task_info["western_goods_act_2_task_2"]
if tsk and level.name() == "pripyat" then
game.prefetch_model([[dynamics\vehicles\veh_btr\veh_btr_u_01.ogf]])
end
end