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

1485 lines
76 KiB
Plaintext
Raw Permalink 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 first act of the addon's storyline. ---
--- ---
--- Each task is composed of the following elements : ---
--- - A act_1_task_X_start(...) function, used to start the task; ---
--- - A act_1_task_X_end(...) function, used to end the task; ---
--- - A xr_effects.act_1_task_X_init(...) function, called when the task has started; ---
--- - A xr_effects.act_1_task_X_complete(...) function, called when the task was successfully completed; ---
--- - A xr_effects.act_1_task_X_fail(...) function, called when the task was failed; ---
--- - A task_functor.act_1_task_X_title_f(...) function, called periodically to get the name; ---
--- - A task_functor.act_1_task_X_descr_f(...) function, called periodically to get the description; ---
--- - A task_functor.act_1_task_X_target_f(...) function, called periodically to get the target (for the PDA); ---
--- - A task_status_functor.act_1_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 level_object_by_id = western_goods_utils.level_object_by_id
local spawn_squad = western_goods_utils.spawn_squad
local spawn_dead_squad = western_goods_utils.spawn_dead_squad
local spawn_story_squad = western_goods_utils.spawn_story_squad
local send_dialog = western_goods_dialogs_manager.send_dialog
local heli_spawn = western_goods_helicopter.heli_spawn
local heli_register = western_goods_helicopter.heli_register
local HELICOPTERS_MODES = western_goods_helicopter.HELICOPTERS_MODES
-- Constants
local CONST_TASK_2_HARD_DELAY = 1800000 -- milliseconds
local CONST_TASK_3_HARD_DELAY = 1800000 -- milliseconds
local CONST_TASK_1_HELI_DIST = 625 -- meters squared
local CONST_TASK_1_SUPPLIES_DIST = 25 -- meters squared
local CONST_TASK_1_PROGRESS_DELAY = 43000 -- milliseconds
local CONST_TASK_2_HELI_DELAY = 50000 -- milliseconds
local CONST_TASK_2_SHOW_TARGETS_DELAY = 300000 -- milliseconds
local CONST_TASK_2_REP_THEFT = -100 -- reputation points
local CONST_TASK_3_PROGRESS_DELAY = 38000 -- milliseconds
local CONST_TASK_3_SHOW_TARGETS_DELAY = 300000 -- milliseconds
local CONST_TASK_3_DIALOG_DIST = 625 -- meters squared
local CONST_TASK_3_DIALOG_EX_DIST = 2500 -- meters squared
local CONST_TASK_3_BRIDGE_DIST = 10000 -- meters squared
local CONST_TASK_3_HELI_DIST = 100 -- meters squared
local CONST_TASK_3_NPC_DIST = 25 -- meters squared
-- Task variables
TASK_1_CACHE = {}
TASK_2_CACHE = {}
TASK_3_CACHE = {}
-- ---------------------------------------------------------------------------------------------------------------------
-- ACT 1 - HELICOPTER DELIVERY
-- ---------------------------------------------------------------------------------------------------------------------
-- TASK 1 - ROAD BUMPS
-- ---------------------------------------------------------------------------------------------------------------------
--- Function used to start Act 1, Task 1.
--- @param first_speaker cse_alife_object
--- @param second_speaker cse_alife_object
--- @return nil
function act_1_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_1_task_1", npc:id())
dbg_printf("[WG] Tasks Act 1 | Task 1 - Task started...")
end
--- Function used to end Act 1, Task 1.
--- @return nil
function act_1_task_1_end()
task_manager.get_task_manager():set_task_completed("western_goods_act_1_task_1")
dbg_printf("[WG] Tasks Act 1 | 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_1_task_1_init(actor,npc)
-- Process info portions
western_goods_utils.give_info("western_goods_act_1_task_1_active")
western_goods_utils.give_info("western_goods_act_1_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_1_task_1_complete(actor,npc)
-- Process info portions
western_goods_utils.rem_info("western_goods_act_1_task_1_active")
western_goods_utils.give_info("western_goods_act_1_task_1_finished")
western_goods_utils.rem_info("western_goods_act_1_task_1_init")
-- Process quest items
xr_effects.remove_item(actor, npc, {"wg_act_1_task_1_quest_item"})
-- Set up task 2
TASK_2_CACHE.available_time = time_global() + CONST_TASK_2_HARD_DELAY
-- Process reward
xr_effects.reward_random_money(actor,npc,{"5000","10000"})
xr_effects.reward_stash(actor,npc,{"true"})
xr_effects.complete_task_inc_goodwill(actor,npc,{"75","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_1_task_1_fail(actor,npc)
-- Process info portions
western_goods_utils.rem_info("western_goods_act_1_task_1_active")
western_goods_utils.rem_info("western_goods_act_1_task_1_init")
-- Process penalty
xr_effects.fail_task_dec_goodwill(actor,npc,{"75","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_1_task_1_title_f(task_id,field,p,tsk)
if true then
return western_goods_utils.get_translation("st_wg_act_1_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_1_task_1_stage_<stage>_descr.
--- @param task_id number
--- @param field string
--- @param p any
--- @param tsk CGameTask
--- @return string
function task_functor.act_1_task_1_descr_f(task_id,field,p,tsk)
if true then
return western_goods_utils.get_translation("st_wg_act_1_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_1_task_1_target_f(task_id,field,p,tsk)
if western_goods_utils.has_info("western_goods_act_1_task_1_init") then
if tsk.stage == 0 then
local heli_se = western_goods_utils.server_object_by_sid("western_goods_helicopter_landed")
return heli_se and heli_se.id
end
if tsk.stage == 1 then
local quest_item_se = western_goods_utils.server_object_by_sid("wg_act_1_task_1_quest_item")
return quest_item_se and quest_item_se.parent_id == 65535 and quest_item_se.id
end
if tsk.stage == 2 then
local quest_item_se = western_goods_utils.server_object_by_sid("wg_act_1_task_1_quest_item")
return quest_item_se and quest_item_se.parent_id == 65535 and quest_item_se.id
end
if tsk.stage == 3 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_1_task_1_status_f(tsk,task_id)
if western_goods_utils.has_info("western_goods_act_1_task_1_init") then
-- First stage : Get to the helicopter
if tsk.stage == 0 then
-- Retrieve the server and game objects (they may not always exist - if they aren't spawned yet or unavailable)
local helicopter_se = western_goods_utils.server_object_by_sid("western_goods_helicopter_landed")
local helicopter_obj = western_goods_utils.level_object_by_sid("western_goods_helicopter_landed")
-- If the heli hasn't been created, then create it
if not helicopter_se then
create_story_helicopter("zaton")
return
end
-- Continue if the player is closer than 25m from the helicopter
if not helicopter_obj or western_goods_utils.get_distance_sqr(helicopter_obj:position(), db.actor:position()) > CONST_TASK_1_HELI_DIST then return end
-- Send a dialog between the task giver and the player
if not TASK_1_CACHE.stage_0_dialog_sent then
local sender_1 = alife():actor()
local sender_2 = tsk.task_giver_id and alife_object(tsk.task_giver_id)
local dialog_sender_1 = { name=sender_1:character_name(), icon=sender_1:character_icon()}
local dialog_sender_2 = { name=sender_2:character_name(), icon=sender_2:character_icon()}
send_dialog({
{sender=dialog_sender_2.name, icon=dialog_sender_2.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_1_stage_0_message_1")},
{sender=dialog_sender_1.name, icon=dialog_sender_1.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_1_stage_0_message_2")},
{sender=dialog_sender_2.name, icon=dialog_sender_2.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_1_stage_0_message_3")},
{sender=dialog_sender_1.name, icon=dialog_sender_1.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_1_stage_0_message_4")},
{sender=dialog_sender_2.name, icon=dialog_sender_2.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_1_stage_0_message_5")},
{sender=dialog_sender_2.name, icon=dialog_sender_2.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_1_stage_0_message_6")}
})
TASK_1_CACHE.progress_timer = time_global() + CONST_TASK_1_PROGRESS_DELAY
TASK_1_CACHE.stage_0_dialog_sent = true
end
dbg_printf("[WG] Tasks Act 1 | Task 1 - Stage 0 - Player arrived at helicopter...")
-- Progress the task
if TASK_1_CACHE.progress_timer < time_global() then
tsk.stage = 1
return
end
end
-- Second stage : Get to the crew at the sawmill and recover the supplies
if tsk.stage == 1 then
-- Retrieve the server and game objects (they may not always exist - if they aren't spawned yet or unavailable)
local supplies_se = western_goods_utils.server_object_by_sid("wg_act_1_task_1_quest_item")
local supplies_obj = western_goods_utils.level_object_by_sid("wg_act_1_task_1_quest_item")
-- Spawn the crew of the helicopter dead at the sawmill
if not TASK_1_CACHE.stage_1_npcs_spawned then
-- Spawn the merc squad, dead, at the sawmill
spawn_dead_squad("western_goods_act_1_task_1_heli_crew_squad", "zat_b104_zombied")
-- Spawn the zombies that supposedly killed them
spawn_dead_squad("western_goods_act_1_task_1_zombie_1_squad", "zat_b104_zombied")
spawn_dead_squad("western_goods_act_1_task_1_zombie_2_squad", "zat_b104_zombied")
spawn_dead_squad("western_goods_act_1_task_1_zombie_3_squad", "zat_b104_zombied")
spawn_dead_squad("western_goods_act_1_task_1_zombie_4_squad", "zat_b104_zombied")
-- Give an info portion so the spawning code doesn't run twice
TASK_1_CACHE.stage_1_npcs_spawned = true
-- Return so the squad has time to spawn in the game world
return
end
-- Spawn the supplies if they don't exist in the world
if not supplies_se then
-- Spawn the supplies, objective the player has to retrieve
supplies_se = alife_create("wg_act_1_task_1_quest_item", vector():set(-317.4,9.8,425.5), 327181, 4137)
dbg_printf("[WG] Tasks Act 1 | Task 1 - Stage 1 - Quest item spawned %s", supplies_se.id)
return
end
-- Wait for the player to be close to the supplies
if not supplies_obj or western_goods_utils.get_distance_sqr(supplies_obj:position(), db.actor:position()) > CONST_TASK_1_SUPPLIES_DIST then return end
-- Send a dialog between the task giver and the player
if not TASK_1_CACHE.stage_1_dialog_sent then
local sender_1 = alife():actor()
local sender_2 = tsk.task_giver_id and alife_object(tsk.task_giver_id)
local dialog_sender_1 = { name=sender_1:character_name(), icon=sender_1:character_icon()}
local dialog_sender_2 = { name=sender_2:character_name(), icon=sender_2:character_icon()}
send_dialog({
{sender=dialog_sender_1.name, icon=dialog_sender_1.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_1_stage_1_message_1")},
{sender=dialog_sender_2.name, icon=dialog_sender_2.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_1_stage_1_message_2")},
{sender=dialog_sender_1.name, icon=dialog_sender_1.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_1_stage_1_message_3")},
{sender=dialog_sender_2.name, icon=dialog_sender_2.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_1_stage_1_message_4")}
})
TASK_1_CACHE.stage_1_dialog_sent = true
end
dbg_printf("[WG] Tasks Act 1 | Task 1 - Stage 1 - Player arrived at the dead squad...")
-- Progress the task
tsk.stage = 2
return
end
-- Third stage : Take the supplies and bring them back to the trader
if tsk.stage == 2 then
-- Retrieve the server object (they may not exist - if they have been picked up by the player)
local supplies_se = western_goods_utils.server_object_by_sid("wg_act_1_task_1_quest_item")
-- If the supplies cannot be found (which is odd), roll back to stage 1
if not supplies_se then
-- Spawn the supplies, objective the player has to retrieve
supplies_se = alife_create("wg_act_1_task_1_quest_item", vector():set(-317.4,9.8,425.5), 327181, 4137)
dbg_printf("[WG] Tasks Act 1 | Task 1 - Stage 2 - Quest 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 1 | Task 1 - Stage 2 - Player picked up the supplies %s", supplies_se.id)
tsk.stage = 3
return
end
end
-- Fourth stage : Bringing the supplies to the trader. If the supplies are dropped, rollback to the previous stage
if tsk.stage == 3 then
-- Retrieve the server object (they may exist - if they have been dropped by the player)
local supplies_se = western_goods_utils.server_object_by_sid("wg_act_1_task_1_quest_item")
-- If the supplies cannot be found (which is odd), roll back to stage 2
if not supplies_se then
dbg_printf("[WG] Tasks Act 1 | Task 1 - Stage 3 - Supplies disappeared, rolling back to stage 2...")
tsk.stage = 2
return
end
if not western_goods_utils.has_info("western_goods_act_1_task_1_ready_finished") then
dbg_printf("[WG] Tasks Act 1 | Task 1 - Task ready to be completed...")
western_goods_utils.give_info("western_goods_act_1_task_1_ready_finished")
end
-- 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 1 | Task 1 - Stage 3 - Player dropped the supplies %s", supplies_se.id)
if western_goods_utils.has_info("western_goods_act_1_task_1_ready_finished") then
dbg_printf("[WG] Tasks Act 1 | Task 1 - Task no longer ready to be completed...")
western_goods_utils.rem_info("western_goods_act_1_task_1_ready_finished")
end
tsk.stage = 2
return
end
end
end
end
-- ---------------------------------------------------------------------------------------------------------------------
-- TASK 2 - CONSPIRACY
-- ---------------------------------------------------------------------------------------------------------------------
--- Function used to start Act 1, Task 2.
--- @param first_speaker cse_alife_object
--- @param second_speaker cse_alife_object
--- @return nil
function act_1_task_2_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_1_task_2", npc:id())
dbg_printf("[WG] Tasks Act 1 | Task 2 - Task started...")
end
--- Function used to end Act 1, Task 2.
--- @return nil
function act_1_task_2_end()
task_manager.get_task_manager():set_task_completed("western_goods_act_1_task_2")
dbg_printf("[WG] Tasks Act 1 | 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_1_task_2_init(actor,npc)
-- Process info portions
western_goods_utils.give_info("western_goods_act_1_task_2_active")
western_goods_utils.give_info("western_goods_act_1_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_1_task_2_complete(actor,npc)
-- Process info portions
western_goods_utils.rem_info("western_goods_act_1_task_2_active")
western_goods_utils.rem_info("western_goods_act_1_task_2_init")
western_goods_utils.give_info("western_goods_act_1_task_2_finished")
-- Process quest items
xr_effects.remove_item(actor, npc, {"wg_act_1_task_2_quest_item_1"})
xr_effects.remove_item(actor, npc, {"wg_act_1_task_2_quest_item_2"})
-- Set up task 3
TASK_3_CACHE.available_time = time_global() + CONST_TASK_3_HARD_DELAY
-- Process reward
xr_effects.reward_random_money(actor,npc,{"10000","20000"})
xr_effects.complete_task_inc_goodwill(actor,npc,{"75","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_1_task_2_fail(actor,npc)
-- Process info portions
western_goods_utils.rem_info("western_goods_act_1_task_2_active")
western_goods_utils.rem_info("western_goods_act_1_task_2_init")
western_goods_utils.rem_info("western_goods_jupiter_informant_first_meet_over")
-- Process penalty
xr_effects.fail_task_dec_goodwill(actor,npc,{"50","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_1_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_1_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_1_task_2_stage_<stage>_descr.
--- @param task_id number
--- @param field string
--- @param p any
--- @param tsk CGameTask
--- @return string
function task_functor.act_1_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_1_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_1_task_2_target_f(task_id,field,p,tsk)
if western_goods_utils.has_info("western_goods_act_1_task_2_init") then
if tsk.stage == 0 then
if western_goods_mcm.get_config("guided_tasks") then
local informant_se = western_goods_utils.server_object_by_sid("stalker_jupiter_informant_squad")
return informant_se and informant_se.id
end
end
if tsk.stage == 1 then
if western_goods_mcm.get_config("guided_tasks") then
local quest_item_1 = western_goods_utils.server_object_by_sid("wg_act_1_task_2_quest_item_1")
local quest_item_2 = western_goods_utils.server_object_by_sid("wg_act_1_task_2_quest_item_2")
return (quest_item_1 and quest_item_1.parent_id == 65535 and quest_item_1.id) or (quest_item_2 and quest_item_2.parent_id == 65535 and quest_item_2.id)
end
end
if tsk.stage == 2 then
local timer = TASK_2_CACHE.stage_2_show_targets_timer
if timer and timer < time_global() then
local squad_1_se = western_goods_utils.server_object_by_sid("western_goods_act_1_task_2_bandit_4_squad")
local squad_2_se = western_goods_utils.server_object_by_sid("western_goods_act_1_task_2_bandit_5_squad")
local squad_3_se = western_goods_utils.server_object_by_sid("western_goods_act_1_task_2_bandit_6_squad")
return (squad_1_se and squad_1_se.id) or (squad_2_se and squad_2_se.id) or (squad_3_se and squad_3_se.id)
end
end
if tsk.stage == 3 then
local quest_item_1 = western_goods_utils.server_object_by_sid("wg_act_1_task_2_quest_item_1")
local quest_item_2 = western_goods_utils.server_object_by_sid("wg_act_1_task_2_quest_item_2")
return (quest_item_1 and quest_item_1.parent_id == 65535 and quest_item_1.id) or (quest_item_2 and quest_item_2.parent_id == 65535 and quest_item_2.id)
end
if tsk.stage == 4 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_1_task_2_status_f(tsk,task_id)
if western_goods_utils.has_info("western_goods_act_1_task_2_init") then
-- First stage : Find and question the informant at Yanov Station
if tsk.stage == 0 then
-- Retrieve the server object (they may not always exist - if they aren't spawned yet or unavailable)
local informant_squad_se = western_goods_utils.server_object_by_sid("stalker_jupiter_informant_squad")
-- Delete the old helicopter from the lake in Zaton
if not TASK_2_CACHE.stage_0_zaton_heli_released then
release_story_helicopter()
TASK_2_CACHE.stage_0_zaton_heli_released = true
end
-- Spawn a new one at the Water Processing Station
if not TASK_2_CACHE.stage_0_wps_heli_created then
create_story_helicopter("wps")
TASK_2_CACHE.stage_0_wps_heli_created = true
end
-- Spawn the informant if it doesn't exist
if not informant_squad_se then
informant_squad_se = spawn_story_squad("stalker_jupiter_informant_squad", vector():set(-55,3.8,211), SIMBOARD.smarts_by_names["jup_a6"])
return
end
-- When the player is done talking with the informant
if not western_goods_utils.has_info("western_goods_jupiter_informant_first_meet_over") then return end
-- Send a dialog between the task giver and the player
if not TASK_2_CACHE.stage_0_dialog_sent then
dbg_printf("[WG] Tasks Act 1 | Task 2 - Stage 0 - Player met informant...")
local sender_1 = alife():actor()
local sender_2 = tsk.task_giver_id and alife_object(tsk.task_giver_id)
local dialog_sender_1 = { name=sender_1:character_name(), icon=sender_1:character_icon()}
local dialog_sender_2 = { name=sender_2:character_name(), icon=sender_2:character_icon()}
send_dialog({
{sender=dialog_sender_1.name, icon=dialog_sender_1.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_2_stage_0_message_1")},
{sender=dialog_sender_2.name, icon=dialog_sender_2.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_2_stage_0_message_2")},
{sender=dialog_sender_1.name, icon=dialog_sender_1.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_2_stage_0_message_3")},
{sender=dialog_sender_2.name, icon=dialog_sender_2.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_2_stage_0_message_4")}
})
TASK_2_CACHE.stage_0_dialog_sent = true
end
tsk.stage = 1
return
end
-- Second stage : Go to the container warehouse and find clues as to what happened to the helicopter
if tsk.stage == 1 then
-- Retrieve the server object (they may not always exist - if they aren't spawned yet)
local helicopter_se = TASK_2_CACHE.stage_1_helicopter_id and alife_object(TASK_2_CACHE.stage_1_helicopter_id)
-- Retrieve the server objects (they may not always exist - if they aren't spawned yet)
local quest_item_1_se = western_goods_utils.server_object_by_sid("wg_act_1_task_2_quest_item_1")
local quest_item_2_se = western_goods_utils.server_object_by_sid("wg_act_1_task_2_quest_item_2")
-- Populate the bandit camp
if not TASK_2_CACHE.stage_1_bandits_spawned then
local se_squad_1 = spawn_squad("western_goods_act_1_task_2_bandit_1_squad", vector():set(-424.8,0,-339.4), 12910, 4469)
local se_squad_2 = spawn_squad("western_goods_act_1_task_2_bandit_2_squad", vector():set(-394.9,0,-386.9), 52206, 4470)
local se_squad_3 = spawn_squad("western_goods_act_1_task_2_bandit_3_squad", vector():set(-405,0,-344), 36889, 4469)
se_squad_1.scripted_target = "jup_a12"
se_squad_2.scripted_target = "jup_a12"
se_squad_3.scripted_target = "jup_a12"
TASK_2_CACHE.stage_1_bandits_spawned = true
return
end
-- Delete the old helicopter from the Water Processing Station in Zaton
if not TASK_2_CACHE.stage_1_wps_heli_released then
release_story_helicopter()
TASK_2_CACHE.stage_1_wps_heli_released = true
end
-- Spawn the helicopter and make it go towards Yanov station, then make it head to the container warehouse
if not TASK_2_CACHE.stage_1_helicopter_spawned then
helicopter_se = heli_spawn("western_goods_helicopter",vector():set(346.7,7,984.4),674394,4724)
TASK_2_CACHE.stage_1_heli_ready_timer = time_global() + CONST_TASK_2_HELI_DELAY
TASK_2_CACHE.stage_1_helicopter_id = helicopter_se.id
TASK_2_CACHE.stage_1_helicopter_spawned = true
return
end
-- Spawn the Gauss rifle if it hasn't been spawned yet
if not quest_item_1_se then
quest_item_1_se = alife_create("wg_act_1_task_2_quest_item_1",vector():set(-445,0.8,-356.8),1056,4818)
dbg_printf("[WG] Tasks Act 1 | Task 2 - Stage 1 - Quest item no 1 spawned %s", quest_item_1_se.id)
return
end
-- Spawn the note if it hasn't been spawned yet
if not quest_item_2_se then
quest_item_2_se = alife_create("wg_act_1_task_2_quest_item_2",vector():set(-446.9,1.2,-384.3),756,4818)
dbg_printf("[WG] Tasks Act 1 | Task 2 - Stage 1 - Quest item no 2 spawned %s", quest_item_2_se.id)
return
end
-- Send a message and set the helicopter to move to the player after 60 seconds
local timer = TASK_2_CACHE.stage_1_heli_ready_timer
if not timer or timer >= time_global() then return end
-- Send the helicopter to protect the player, and send a dialog to the player
if not TASK_2_CACHE.stage_1_helicopter_sent then
dbg_printf("[WG] Tasks Act 1 | Task 2 - Stage 1 - Helicopter moving to player...")
heli_register(TASK_2_CACHE.stage_1_helicopter_id,HELICOPTERS_MODES.PROTECT_ACTOR,nil)
local attack_key_str = western_goods_utils.get_key_translation(western_goods_mcm.get_config("heli_attack"))
-- Send a dialog to the player to inform him the helicopter is coming
local dialog_sender = {name="Mercenary helicopter", icon="ui_inGame2_PD_DownToEarth"}
send_dialog({
{sender=dialog_sender.name, icon=dialog_sender.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_2_stage_1_message_1",attack_key_str)}
})
TASK_2_CACHE.stage_1_helicopter_sent = true
end
-- If the player picked one of the items, isn't wearing a disguise, and hasn't been punished yet (punish the player only once, not every refresh...)
if not TASK_2_CACHE.stage_1_target_1_robbed and not (quest_item_1_se and quest_item_1_se.parent_id == 65535) then
local nearest_npc, nearest_npc_dist = utils_obj.get_nearest_stalker(db.actor)
-- Punish the player if he's not wearing a disguise, and a bandit is close to him
if nearest_npc_dist and nearest_npc:character_community() == "bandit" and nearest_npc_dist < 10 and not gameplay_disguise.is_actor_disguised() then
dbg_printf("[WG] Tasks Act 1 | Task 2 - Stage 1 - Nearest %s %s is %s m away", nearest_npc:character_community(), nearest_npc:section(), nearest_npc_dist)
-- Degrade player reputation
game_relations.change_faction_relations("bandit", get_actor_true_community(), CONST_TASK_2_REP_THEFT)
dbg_printf("[WG] Tasks Act 1 | Task 2 - Stage 1 - Player punished for stealing %s", CONST_TASK_2_REP_THEFT)
-- Make the npc send a message to the player
local dialog_sender = { name= nearest_npc:character_name(), icon= nearest_npc:character_icon()}
send_dialog({
{sender=dialog_sender.name, icon=dialog_sender.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_2_stage_1_message_3")}
},true)
end
-- Save that the item has been robbed (so that the player doesn't get punished if he gets exposed way later)
TASK_2_CACHE.stage_1_target_1_robbed = true
end
-- If the player picked one of the items, isn't wearing a disguise, and hasn't been punished yet (punish the player only once, not every refresh...)
if not TASK_2_CACHE.stage_1_target_2_robbed and not (quest_item_2_se and quest_item_2_se.parent_id == 65535) then
local nearest_npc, nearest_npc_dist = utils_obj.get_nearest_stalker(db.actor)
-- Punish the player if he's not wearing a disguise, and a bandit is close to him
if nearest_npc_dist and nearest_npc:character_community() == "bandit" and nearest_npc_dist < 10 and not gameplay_disguise.is_actor_disguised() then
dbg_printf("[WG] Tasks Act 1 | Task 2 - Stage 1 - Nearest %s %s is %s m away", nearest_npc:character_community(), nearest_npc:section(), nearest_npc_dist)
-- Degrade player reputation
game_relations.change_faction_relations("bandit", get_actor_true_community(), CONST_TASK_2_REP_THEFT)
dbg_printf("[WG] Tasks Act 1 | Task 2 - Stage 1 - Player punished for stealing %s", CONST_TASK_2_REP_THEFT)
-- Make the npc send a message to the player
local dialog_sender = { name= nearest_npc:character_name(), icon= nearest_npc:character_icon()}
send_dialog({
{sender=dialog_sender.name, icon=dialog_sender.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_2_stage_1_message_4")}
},true)
end
-- Save that the item has been robbed (so that the player doesn't get punished if he gets exposed way later)
TASK_2_CACHE.stage_1_target_2_robbed = true
end
-- If the player picked up both quest items, progress the task
if not (quest_item_1_se and quest_item_1_se.parent_id == 65535) and not (quest_item_2_se and quest_item_2_se.parent_id == 65535) then
dbg_printf("[WG] Tasks Act 1 | Task 2 - Stage 1 - Player picked up the items - %s - %s", quest_item_1_se.id, quest_item_2_se.id)
-- If the player is hostile to bandits, progress to stage 2, otherwise, progress to stage 3
if game_relations.is_factions_enemies(get_actor_true_community(), "bandit") and not gameplay_disguise.is_actor_disguised() then
-- Spawn the bandit reinforcements around the Container Warehouse area
if not TASK_2_CACHE.stage_2_bandit_squads_spawned then
local squad_1_se = spawn_squad("western_goods_act_1_task_2_bandit_4_squad", vector():set(-424.8,0,-339.4), 12910, 4469)
local squad_2_se = spawn_squad("western_goods_act_1_task_2_bandit_5_squad", vector():set(-394.9,0,-386.9), 52206, 4470)
local squad_3_se = spawn_squad("western_goods_act_1_task_2_bandit_6_squad", vector():set(-405,0,-344), 36889, 4469)
squad_1_se.scripted_target = "jup_a12"
squad_2_se.scripted_target = "jup_a12"
squad_3_se.scripted_target = "jup_a12"
-- Save the time the fight was started at, to indicate the targets after 2 in-game hours
TASK_2_CACHE.stage_2_show_targets_timer = time_global() + CONST_TASK_2_SHOW_TARGETS_DELAY
TASK_2_CACHE.stage_2_bandit_squads_spawned = true
-- Alert the player about the incoming bandits
local dialog_sender = {name="Mercenary helicopter", icon="ui_inGame2_PD_DownToEarth"}
send_dialog({
{sender=dialog_sender.name, icon=dialog_sender.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_2_stage_2_message_1")}
})
return
end
tsk.stage = 2
return
else
-- Return the helicopter to the processing station
heli_register(TASK_2_CACHE.stage_1_helicopter_id,HELICOPTERS_MODES.LEAVE_AT_POINT,vector():set(346.7,7,984.4))
-- Spawn a new one at the Water Processing Station
create_story_helicopter("wps")
-- Notify the player that the heli is leaving
local dialog_sender = {name="Mercenary helicopter", icon="ui_inGame2_PD_DownToEarth"}
send_dialog({
{sender=dialog_sender.name, icon=dialog_sender.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_2_stage_1_message_2")}
})
tsk.stage = 3
return
end
end
end
-- Third stage : Face off the bandits reinforcements
if tsk.stage == 2 then
-- Retrieve the server objects (they may not always exist - if they aren't spawned yet or unavailable)
local squad_1_se = western_goods_utils.server_object_by_sid("western_goods_act_1_task_2_bandit_4_squad")
local squad_2_se = western_goods_utils.server_object_by_sid("western_goods_act_1_task_2_bandit_5_squad")
local squad_3_se = western_goods_utils.server_object_by_sid("western_goods_act_1_task_2_bandit_6_squad")
-- If all of the squads are dead, or the player is wearing a disguise
if (not squad_1_se and not squad_2_se and not squad_3_se) or gameplay_disguise.is_actor_disguised() then
dbg_printf("[WG] Tasks Act 1 | Task 2 - Stage 2 - Bandit squads cleared...")
-- Retrieve the game object (may not exist if the helicopter has been destroyed)
local helicopter_se = TASK_2_CACHE.stage_1_helicopter_id and alife_object(TASK_2_CACHE.stage_1_helicopter_id)
local helicopter_obj = helicopter_se and level_object_by_id(helicopter_se.id)
if helicopter_obj then
-- Return the helicopter to the processing station
heli_register(helicopter_obj:id(),HELICOPTERS_MODES.LEAVE_AT_POINT,vector():set(346.7,7,984.4))
dbg_printf("[WG] Tasks Act 1 | Task 2 - Stage 2 - Helicopter moving out to WPS...")
-- Inform the player that the helicopter is leaving
local dialog_sender = {name="Mercenary helicopter", icon="ui_inGame2_PD_DownToEarth"}
send_dialog({
{sender=dialog_sender.name, icon=dialog_sender.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_2_stage_2_message_2")}
})
end
-- And progress the task
tsk.stage = 3
return
end
end
-- Fourth stage : Bring back the quest items
if tsk.stage == 3 then
-- Retrieve the server objects
local quest_item_1 = western_goods_utils.server_object_by_sid("wg_act_1_task_2_quest_item_1")
local quest_item_2 = western_goods_utils.server_object_by_sid("wg_act_1_task_2_quest_item_2")
-- Spawn the Gauss rifle if it disappeared
if not quest_item_1 then
quest_item_1 = alife_create("wg_act_1_task_2_quest_item_1",vector():set(-445,0.8,-356.8),1056,4818)
dbg_printf("[WG] Tasks Act 1 | Task 2 - Stage 3 - Quest item no 1 respawned %s", quest_item_1.id)
return
end
-- Spawn the note if it disappeared
if not quest_item_2 then
quest_item_2 = alife_create("wg_act_1_task_2_quest_item_2",vector():set(-446.9,1.2,-384.3),756,4818)
dbg_printf("[WG] Tasks Act 1 | Task 2 - Stage 3 - Quest item no 2 respawned %s", quest_item_2.id)
return
end
-- If the player picked up both quest items, progress to stage 4
if not (quest_item_1 and quest_item_1.parent_id ~= AC_ID) and not (quest_item_2 and quest_item_2.parent_id ~= AC_ID) then
dbg_printf("[WG] Tasks Act 1 | Task 2 - Stage 3 - Player picked up the items - %s - %s", quest_item_1.id, quest_item_2.id)
tsk.stage = 4
return
end
end
-- Fifth stage : If one of the quest items are dropped, roll back to fourth stage
if tsk.stage == 4 then
-- Retrieve the server objects
local quest_item_1 = western_goods_utils.server_object_by_sid("wg_act_1_task_2_quest_item_1")
local quest_item_2 = western_goods_utils.server_object_by_sid("wg_act_1_task_2_quest_item_2")
if not western_goods_utils.has_info("western_goods_act_1_task_2_ready_finished") then
dbg_printf("[WG] Tasks Act 1 | Task 2 - Task ready to be completed...")
western_goods_utils.give_info("western_goods_act_1_task_2_ready_finished")
end
-- If the player dropped one of the quest items, rollback to stage 3
if (not quest_item_1 or quest_item_1 and quest_item_1.parent_id ~= AC_ID) or (not quest_item_2 or quest_item_2 and quest_item_2.parent_id ~= AC_ID) then
dbg_printf("[WG] Tasks Act 1 | Task 2 - Stage 4 - Player dropped the items - %s - %s", quest_item_1.id, quest_item_2.id)
if western_goods_utils.has_info("western_goods_act_1_task_2_ready_finished") then
dbg_printf("[WG] Tasks Act 1 | Task 2 - Task no longer ready to be completed...")
western_goods_utils.rem_info("western_goods_act_1_task_2_ready_finished")
end
tsk.stage = 3
return
end
end
end
end
-- ---------------------------------------------------------------------------------------------------------------------
-- TASK 3 - THE RESCUE
-- ---------------------------------------------------------------------------------------------------------------------
--- Function used to start Act 1, Task 3.
--- @param first_speaker cse_alife_object
--- @param second_speaker cse_alife_object
--- @return nil
function act_1_task_3_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_1_task_3", npc:id())
dbg_printf("[WG] Tasks Act 1 | Task 3 - Task started...")
end
--- Function used to end Act 1, Task 3.
--- @return nil
function act_1_task_3_end()
task_manager.get_task_manager():set_task_completed("western_goods_act_1_task_3")
dbg_printf("[WG] Tasks Act 1 | Task 3 - 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_1_task_3_init(actor,npc)
-- Process info portions
western_goods_utils.give_info("western_goods_act_1_task_3_active")
western_goods_utils.give_info("western_goods_act_1_task_3_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_1_task_3_complete(actor,npc)
-- Process info portions
western_goods_utils.rem_info("western_goods_act_1_task_3_active")
western_goods_utils.rem_info("western_goods_act_1_task_3_init")
western_goods_utils.give_info("western_goods_act_1_task_3_finished")
western_goods_utils.give_info("western_goods_act_1_finished")
-- Process reward
xr_effects.reward_random_money(actor,npc,{"10000","15000"})
xr_effects.complete_task_inc_goodwill(actor,npc,{"100","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_1_task_3_fail(actor,npc)
-- Process info portions
western_goods_utils.rem_info("western_goods_act_1_task_3_active")
western_goods_utils.rem_info("western_goods_act_1_task_3_init")
western_goods_utils.rem_info("western_goods_crew_member_rescue_over")
-- 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_1_task_3_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_1_task_3_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_1_task_3_stage_<stage>_descr.
--- @param task_id number
--- @param field string
--- @param p any
--- @param tsk CGameTask
--- @return string
function task_functor.act_1_task_3_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_1_task_3_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_1_task_3_target_f(task_id,field,p,tsk)
if western_goods_utils.has_info("western_goods_act_1_task_3_init") then
if tsk.stage == 0 then
local crew_member_se = western_goods_utils.server_object_by_sid("stalker_crew_member")
return crew_member_se and crew_member_se.id
end
if tsk.stage == 1 then
local smart = SIMBOARD:get_smart_by_name("red_smart_terrain_bridge")
return smart and smart.id
end
if tsk.stage == 2 then
-- Wait 5 real life minutes before starting to indicate where the remaining enemies are
local timer = TASK_3_CACHE.stage_2_show_targets_timer
if timer and timer <= time_global() then
local squad_1_se = western_goods_utils.server_object_by_sid("western_goods_act_1_task_3_mono_bridge_1_squad")
local squad_2_se = western_goods_utils.server_object_by_sid("western_goods_act_1_task_3_mono_bridge_2_squad")
return (squad_1_se and squad_1_se.id) or (squad_2_se and squad_2_se.id)
end
end
if tsk.stage == 3 then
local smart = SIMBOARD:get_smart_by_name("red_smart_terrain_bridge")
return smart and smart.id
end
if tsk.stage == 4 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_1_task_3_status_f(tsk,task_id)
if western_goods_utils.has_info("western_goods_act_1_task_3_init") then
-- First stage : Find and rescue the crew member
if tsk.stage == 0 then
-- Retrieve the server and game objects (they may not always exist - if they aren't spawned yet or unavailable)
local crew_member_se = western_goods_utils.server_object_by_sid("stalker_crew_member")
local crew_member_obj = crew_member_se and level_object_by_id(crew_member_se.id)
-- Retrieve the server and game objects (they may not always exist - if they aren't spawned yet or unavailable)
local crew_member_squad_se = western_goods_utils.server_object_by_sid("stalker_crew_member_squad")
-- Spawn the crew member squad
if not crew_member_squad_se then
crew_member_squad_se = spawn_story_squad("stalker_crew_member_squad", vector():set(35.3,1,42.9), SIMBOARD.smarts_by_names["lim_smart_terrain_6"])
return
end
-- Spawn monolith squads to guard the crew member
if not TASK_3_CACHE.stage_0_mono_guard_spawned then
local se_squad_1 = spawn_squad("western_goods_act_1_task_3_mono_guards_1_squad", vector():set(17.2,1,47.3), 35710, 2475)
local se_squad_2 = spawn_squad("western_goods_act_1_task_3_mono_guards_2_squad", vector():set(42,-3,50.4), 48878, 2475)
se_squad_1.scripted_target = "lim_smart_terrain_6"
se_squad_2.scripted_target = "lim_smart_terrain_6"
TASK_3_CACHE.stage_0_mono_guard_spawned = true
return
end
-- When the player has rescued the crew member, progress further
if not western_goods_utils.has_info("western_goods_crew_member_rescue_over") then return end
if crew_member_obj and not TASK_3_CACHE.stage_0_crew_member_companion then
dbg_printf("[WG] Tasks Act 1 | Task 3 - Stage 0 - Player rescued crew member...")
-- Add the crew member to the player squad
dialogs_axr_companion.become_actor_companion(db.actor,crew_member_obj)
TASK_3_CACHE.stage_0_crew_member_companion = true
end
-- Send a dialog between the player and the task giver
if not TASK_3_CACHE.stage_0_dialog_sent then
local sender_1 = alife():actor()
local sender_2 = alife_object(tsk.task_giver_id)
local dialog_sender_1 = { name=sender_1:character_name(), icon=sender_1:character_icon()}
local dialog_sender_2 = { name=sender_2:character_name(), icon=sender_2:character_icon()}
send_dialog({
{sender=dialog_sender_1.name, icon=dialog_sender_1.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_3_stage_0_message_1")},
{sender=dialog_sender_2.name, icon=dialog_sender_2.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_3_stage_0_message_2")},
{sender=dialog_sender_1.name, icon=dialog_sender_1.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_3_stage_0_message_3")},
{sender=dialog_sender_2.name, icon=dialog_sender_2.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_3_stage_0_message_4")},
{sender=dialog_sender_1.name, icon=dialog_sender_1.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_3_stage_0_message_5")}
})
TASK_3_CACHE.progress_timer = time_global() + CONST_TASK_3_PROGRESS_DELAY
TASK_3_CACHE.stage_0_dialog_sent = true
end
-- Progress the task
if TASK_3_CACHE.progress_timer < time_global() then
tsk.stage = 1
return
end
end
-- Second stage : Get to the bridge in the Red Forest
if tsk.stage == 1 then
-- Retrieve the server and game objects
local crew_member_squad_se = western_goods_utils.server_object_by_sid("stalker_crew_member_squad")
-- Respawn the crew member squad
if not crew_member_squad_se then
crew_member_squad_se = spawn_story_squad("stalker_crew_member_squad", vector():set(35.3,1,42.9), SIMBOARD.smarts_by_names["lim_smart_terrain_6"])
return
end
-- Spawn the enemy squads at the bridge in the Red Forest, if they haven't already, or if they all died
if not TASK_3_CACHE.stage_1_mono_bridge_spawned then
-- Spawn the squads
local monolith_se_squad_1 = spawn_squad("western_goods_act_1_task_3_mono_bridge_1_squad", vector():set(-114,-0.6,-214.3), 8509, 2792)
local monolith_se_squad_2 = spawn_squad("western_goods_act_1_task_3_mono_bridge_2_squad", vector():set(-111.5,-0.6,-210.6), 8905, 2792)
monolith_se_squad_1.scripted_target = "red_smart_terrain_bridge"
monolith_se_squad_2.scripted_target = "red_smart_terrain_bridge"
TASK_3_CACHE.stage_1_mono_bridge_spawned = true
return
end
-- Wait for the player to be in Limansk
if level.name() == "l10_limansk" then
local player_pos = db.actor:position()
-- Positions of the smart terrains the player needs to reach to get a lore dump
local dialog_1_activation_pos = SIMBOARD:get_smart_by_name("lim_smart_terrain_5").position
local dialog_2_activation_pos = SIMBOARD:get_smart_by_name("lim_smart_terrain_3").position
local dialog_3_activation_pos = SIMBOARD:get_smart_by_name("lim_smart_terrain_1").position
-- Distances between the player and the smart terrains
local dist_to_dialog_1 = dialog_1_activation_pos and western_goods_utils.get_distance_sqr(player_pos, dialog_1_activation_pos)
local dist_to_dialog_2 = dialog_2_activation_pos and western_goods_utils.get_distance_sqr(player_pos, dialog_2_activation_pos)
local dist_to_dialog_3 = dialog_3_activation_pos and western_goods_utils.get_distance_sqr(player_pos, dialog_3_activation_pos)
-- Conditions for dialogs
local dialog_1_cond = dist_to_dialog_1 < CONST_TASK_3_DIALOG_DIST and not TASK_3_CACHE.stage_1_dialog_1_sent
local dialog_2_cond = dist_to_dialog_2 < CONST_TASK_3_DIALOG_DIST and not TASK_3_CACHE.stage_1_dialog_2_sent and TASK_3_CACHE.stage_1_dialog_1_sent
local dialog_3_cond = dist_to_dialog_3 < CONST_TASK_3_DIALOG_EX_DIST and not TASK_3_CACHE.stage_1_dialog_3_sent and TASK_3_CACHE.stage_1_dialog_2_sent
if dialog_1_cond then
dbg_printf("[WG] Tasks Act 1 | Task 3 - Stage 1 - Player reached dialog point 1...")
local sender_1 = alife():actor()
local sender_2 = western_goods_utils.server_object_by_sid("stalker_crew_member")
local dialog_sender_1 = { name=sender_1:character_name(), icon=sender_1:character_icon()}
local dialog_sender_2 = { name=sender_2:character_name(), icon=sender_2:character_icon()}
send_dialog({
{sender=dialog_sender_2.name, icon=dialog_sender_2.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_3_stage_1_message_1")},
{sender=dialog_sender_1.name, icon=dialog_sender_1.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_3_stage_1_message_2")}
})
TASK_3_CACHE.stage_1_dialog_1_sent = true
end
if dialog_2_cond then
dbg_printf("[WG] Tasks Act 1 | Task 3 - Stage 1 - Player reached dialog point 2...")
local sender_1 = alife():actor()
local sender_2 = western_goods_utils.server_object_by_sid("stalker_crew_member")
local dialog_sender_1 = { name=sender_1:character_name(), icon=sender_1:character_icon()}
local dialog_sender_2 = { name=sender_2:character_name(), icon=sender_2:character_icon()}
send_dialog({
{sender=dialog_sender_1.name, icon=dialog_sender_1.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_3_stage_1_message_3")},
{sender=dialog_sender_2.name, icon=dialog_sender_2.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_3_stage_1_message_4")},
{sender=dialog_sender_1.name, icon=dialog_sender_1.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_3_stage_1_message_5")},
{sender=dialog_sender_2.name, icon=dialog_sender_2.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_3_stage_1_message_6")}
})
TASK_3_CACHE.stage_1_dialog_2_sent = true
end
if dialog_3_cond then
dbg_printf("[WG] Tasks Act 1 | Task 3 - Stage 1 - Player reached dialog point 3...")
local sender_1 = alife():actor()
local sender_2 = western_goods_utils.server_object_by_sid("stalker_crew_member")
local dialog_sender_1 = { name=sender_1:character_name(), icon=sender_1:character_icon()}
local dialog_sender_2 = { name=sender_2:character_name(), icon=sender_2:character_icon()}
send_dialog({
{sender=dialog_sender_1.name, icon=dialog_sender_1.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_3_stage_1_message_7")},
{sender=dialog_sender_2.name, icon=dialog_sender_2.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_3_stage_1_message_8")},
{sender=dialog_sender_1.name, icon=dialog_sender_1.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_3_stage_1_message_9")},
{sender=dialog_sender_2.name, icon=dialog_sender_2.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_3_stage_1_message_10")}
})
TASK_3_CACHE.stage_1_dialog_3_sent = true
end
end
-- Wait for the player to be in the Red Forest
if level.name() == "l10_red_forest" then
-- Retrieve the player and target positions
local player_pos = db.actor:position()
local objective_pos = SIMBOARD:get_smart_by_name("red_smart_terrain_bridge").position
-- Wait for the player to be closer than 100m from the bridge
if not objective_pos or western_goods_utils.get_distance_sqr(player_pos, objective_pos) > CONST_TASK_3_BRIDGE_DIST then return end
dbg_printf("[WG] Tasks Act 1 | Task 3 - Stage 1 - Player reached Read Forest's bridge to Limansk...")
-- Save the time to indicate the remaining enemies on the PDA 5 real-life mins later
TASK_3_CACHE.stage_2_show_targets_timer = time_global() + CONST_TASK_3_SHOW_TARGETS_DELAY
-- Progress the task
tsk.stage = 2
return
end
end
-- Third stage : Fight off the enemy squads
if tsk.stage == 2 then
-- Retrieve the server objects (they may not always exist - if they aren't spawned yet or unavailable)
local monolith_se_squad_1 = western_goods_utils.server_object_by_sid("western_goods_act_1_task_3_mono_bridge_1_squad")
local monolith_se_squad_2 = western_goods_utils.server_object_by_sid("western_goods_act_1_task_3_mono_bridge_2_squad")
-- Retrieve the server objects
local crew_member_squad_se = western_goods_utils.server_object_by_sid("stalker_crew_member_squad")
-- Respawn the crew member squad
if not crew_member_squad_se then
crew_member_squad_se = spawn_story_squad("stalker_crew_member_squad", vector():set(35.3,1,42.9), SIMBOARD.smarts_by_names["lim_smart_terrain_6"])
return
end
-- Progress only when all enemy squads are dead
if monolith_se_squad_1 or monolith_se_squad_2 then return end
dbg_printf("[WG] Tasks Act 1 | Task 3 - Stage 2 - All Monolith squads dead...")
-- Progress the task
tsk.stage = 3
return
end
-- Fourth stage : Wait for the helicopter to pickup the rescued crew member
if tsk.stage == 3 then
-- Retrieve the server object of the rescue helicopter
local helicopter_se = TASK_3_CACHE.stage_3_helicopter_id and alife_object(TASK_3_CACHE.stage_3_helicopter_id)
local helicopter_obj = helicopter_se and level_object_by_id(helicopter_se.id)
-- Retrieve the server and game object of the crew member
local crew_member_se = western_goods_utils.server_object_by_sid("stalker_crew_member")
local crew_member_obj = crew_member_se and level_object_by_id(crew_member_se.id)
-- Retrieve the server and game object of the crew member's squad
local crew_member_squad_se = western_goods_utils.server_object_by_sid("stalker_crew_member_squad")
-- Define the helicopter spawn and landing positions
local heli_spawn_pos = vector():set(348.8,10.9,149.2)
local heli_landing_pos = vector():set(-188.7,5.3,-195.8)
-- Delete the old helicopter from the Water Processing Station in Zaton
if not TASK_3_CACHE.stage_3_wps_heli_released then
release_story_helicopter()
TASK_2_CACHE.stage_3_wps_heli_released = true
end
-- Spawn the helicopter and direct it to the landing zone
if not TASK_3_CACHE.stage_3_helicopter_spawned then
helicopter_se = heli_spawn("western_goods_helicopter",heli_spawn_pos,8256,2792)
dbg_printf("[WG] Tasks Act 1 | Task 3 - Stage 3 - Helicopter spawned %s", helicopter_se.id)
heli_register(helicopter_se.id,HELICOPTERS_MODES.LAND_AT_POINT, heli_landing_pos)
dbg_printf("[WG] Tasks Act 1 | Task 3 - Stage 3 - Helicopter moving to player...")
TASK_3_CACHE.stage_3_helicopter_id = helicopter_se.id
TASK_3_CACHE.stage_3_helicopter_spawned = true
return
end
-- If the evac helicopter was destroyed, fail the task
if helicopter_obj and helicopter_obj:get_helicopter():GetfHealth() <= 0 then
dbg_printf("[WG] Tasks Act 1 | Task 3 - Stage 3 - Helicopter was destroyed - task failed")
return "fail"
end
-- If the crew member hasn't extracted, and isn't alive, respawn him
if not crew_member_squad_se and not western_goods_utils.has_info("western_goods_act_1_task_3_crew_member_extracted") then
crew_member_squad_se = spawn_story_squad("stalker_crew_member_squad", vector():set(35.3,1,42.9), SIMBOARD.smarts_by_names["lim_smart_terrain_6"])
return
end
-- Send a dialog to the player to inform him the helicopter is coming
if not TASK_3_CACHE.stage_3_dialog_sent then
local sender_1 = alife():actor()
local dialog_sender_1 = { name=sender_1:character_name(), icon=sender_1:character_icon()}
local dialog_sender_2 = { name="Mercenary helicopter", icon="ui_inGame2_PD_DownToEarth"}
send_dialog({
{sender=dialog_sender_1.name, icon=dialog_sender_1.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_3_stage_3_message_1")},
{sender=dialog_sender_2.name, icon=dialog_sender_2.icon, message=western_goods_utils.get_translation("st_wg_trader_act_1_task_3_stage_3_message_2")}
})
TASK_3_CACHE.stage_3_dialog_sent = true
end
-- Get the distance between the helicopter and the landing zone, and wait for it to be smaller than 5m
local dist_to_landing = helicopter_obj and western_goods_utils.get_distance_sqr(helicopter_obj:position(), heli_landing_pos)
if (not dist_to_landing) or dist_to_landing and dist_to_landing > CONST_TASK_3_HELI_DIST then return end
-- Set the rescued companion to head towards the helicopter
if not TASK_3_CACHE.stage_3_evac_order_given then
dbg_printf("[WG] Tasks Act 1 | Task 3 - Stage 3 - Helicopter reached landing zone (dist:%s)", dist_to_landing)
-- Remove companion from player squad
axr_companions.remove_from_actor_squad(crew_member_obj)
-- Force the npc logic back in, because dialogs_axr_companion fails to restore it for some reason
xr_logic.set_new_scheme_and_logic(crew_member_obj,"beh","beh@get_to_heli","logic",nil,"scripts\\western_goods_crew_member.ltx")
crew_member_se.scripted_target = "red_smart_terrain_bridge"
dbg_printf("[WG] Tasks Act 1 | Task 3 - Stage 3 - Crew member freed and moving to helicopter...")
TASK_3_CACHE.stage_3_evac_order_given = true
end
-- Get the distance to the heli, and wait for it to be smaller than 5m
local dist_to_heli = crew_member_obj and western_goods_utils.get_distance_sqr(crew_member_obj:position(), heli_landing_pos)
if (not dist_to_heli) or dist_to_heli and dist_to_heli > CONST_TASK_3_NPC_DIST then return end
-- Extract the squad member
if not TASK_3_CACHE.stage_3_crew_member_extracted then
-- Release the squad member (as in he got in the helicopter)
alife_release(crew_member_se)
-- Give the info that the crew member was successfully extracted
western_goods_utils.give_info("western_goods_act_1_task_3_crew_member_extracted")
dbg_printf("[WG] Tasks Act 1 | Task 3 - Stage 3 - Crew member extracted...")
-- Make the heli leave the level
heli_register(helicopter_se.id,HELICOPTERS_MODES.LEAVE_AT_POINT,heli_spawn_pos)
-- Spawn a new one at the Water Processing Station
create_story_helicopter("wps")
dbg_printf("[WG] Tasks Act 1 | Task 3 - Stage 3 - Helicopter leaving to Book Store...")
TASK_3_CACHE.stage_3_crew_member_extracted = true
end
-- Progress the task
tsk.stage = 4
return
end
-- Fifth stage : Return for the reward
if tsk.stage == 4 then
local crew_member_squad_se = western_goods_utils.server_object_by_sid("stalker_crew_member_squad")
if not western_goods_utils.has_info("western_goods_act_1_task_3_ready_finished") then
dbg_printf("[WG] Tasks Act 1 | Task 3 - Task ready to be completed...")
western_goods_utils.give_info("western_goods_act_1_task_3_ready_finished")
end
-- Spawn the rescued crew member in the book store
if not crew_member_squad_se then
crew_member_squad_se = spawn_squad("stalker_crew_member_squad", vector():set(-182.1,0.6,-343.9), 33349, 5019)
crew_member_squad_se.scripted_target = "pri_a18_smart_terrain"
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("actor_on_first_update", task_2_setup)
RegisterScriptCallback("actor_on_first_update", task_3_setup)
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 = {}
local TASK_3_SAVE = {}
-- Make copies of task caches
copy_table(TASK_1_SAVE, TASK_1_CACHE)
copy_table(TASK_2_SAVE, TASK_2_CACHE)
copy_table(TASK_3_SAVE, TASK_3_CACHE)
-- Pre-process tables
TASK_1_SAVE.progress_timer = (TASK_1_SAVE.progress_timer or 0) - time_global()
TASK_2_SAVE.available_time = (TASK_2_SAVE.available_time or 0) - time_global()
TASK_3_SAVE.available_time = (TASK_3_SAVE.available_time or 0) - time_global()
TASK_3_SAVE.progress_timer = (TASK_3_SAVE.progress_timer or 0) - time_global()
TASK_2_SAVE.stage_1_heli_ready_timer = (TASK_2_SAVE.stage_1_heli_ready_timer or 0) - time_global()
TASK_2_SAVE.stage_2_show_targets_timer = (TASK_2_SAVE.stage_2_show_targets_timer or 0) - time_global()
TASK_3_SAVE.stage_2_show_targets_timer = (TASK_3_SAVE.stage_2_show_targets_timer or 0) - time_global()
-- Save tables
m_data.wg_act_1_task_1_cache = TASK_1_SAVE
m_data.wg_act_1_task_2_cache = TASK_2_SAVE
m_data.wg_act_1_task_3_cache = TASK_3_SAVE
-- Debug prints
dbg_printf("[WG] Tasks Act 1 | Task 1 - Saved variables...\n%s",utils_data.print_table(TASK_1_SAVE, false, true))
dbg_printf("[WG] Tasks Act 1 | Task 2 - Saved variables...\n%s",utils_data.print_table(TASK_2_SAVE, false, true))
dbg_printf("[WG] Tasks Act 1 | Task 3 - Saved variables...\n%s",utils_data.print_table(TASK_3_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_1_task_1_cache or {}
local TASK_2_SAVE = m_data.wg_act_1_task_2_cache or {}
local TASK_3_SAVE = m_data.wg_act_1_task_3_cache or {}
-- Post-process tables
TASK_1_SAVE.progress_timer = (TASK_1_SAVE.progress_timer or 0) + time_global()
TASK_2_SAVE.available_time = (TASK_2_SAVE.available_time or 0) + time_global()
TASK_3_SAVE.available_time = (TASK_3_SAVE.available_time or 0) + time_global()
TASK_3_SAVE.progress_timer = (TASK_3_SAVE.progress_timer or 0) + time_global()
TASK_2_SAVE.stage_1_heli_ready_timer = (TASK_2_SAVE.stage_1_heli_ready_timer or 0) + time_global()
TASK_2_SAVE.stage_2_show_targets_timer = (TASK_2_SAVE.stage_2_show_targets_timer or 0) + time_global()
TASK_3_SAVE.stage_2_show_targets_timer = (TASK_3_SAVE.stage_2_show_targets_timer or 0) + time_global()
-- Restore task caches
copy_table(TASK_1_CACHE, TASK_1_SAVE)
copy_table(TASK_2_CACHE, TASK_2_SAVE)
copy_table(TASK_3_CACHE, TASK_3_SAVE)
-- Debug prints
dbg_printf("[WG] Tasks Act 1 | Task 1 - Loaded variables...\n%s",utils_data.print_table(TASK_1_CACHE, false, true))
dbg_printf("[WG] Tasks Act 1 | Task 2 - Loaded variables...\n%s",utils_data.print_table(TASK_2_CACHE, false, true))
dbg_printf("[WG] Tasks Act 1 | Task 3 - Loaded variables...\n%s",utils_data.print_table(TASK_3_CACHE, false, true))
end
-- ---------------------------------------------------------------------------------------------------------------------
-- General functions
-- ---------------------------------------------------------------------------------------------------------------------
--- 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_1_task_1_finished")
local task_2_started = western_goods_utils.has_info("western_goods_act_1_task_2_active")
local task_2_done = western_goods_utils.has_info("western_goods_act_1_task_2_finished")
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 timeout_ready then
dbg_printf("[WG] Tasks Act 1 | Task 2 - Task set up...")
western_goods_utils.give_info("western_goods_act_1_task_2_available")
else
dbg_printf("[WG] Tasks Act 1 | Task 2 - What's missing? timeout_ready:%s (%s/%s)", timeout_ready, time_global(), TASK_2_CACHE.available_time)
end
end
function task_3_setup()
-- Conditions
local task_2_done = western_goods_utils.has_info("western_goods_act_1_task_2_finished")
local task_3_started = western_goods_utils.has_info("western_goods_act_1_task_3_active")
local task_3_done = western_goods_utils.has_info("western_goods_act_1_task_3_finished")
local timeout_ready = time_global() > (TASK_3_CACHE.available_time or 0)
-- If the task is available
if not task_2_done or task_3_started or task_3_done then return end
-- If the task should be set up
if timeout_ready then
dbg_printf("[WG] Tasks Act 1 | Task 3 - Task set up...")
western_goods_utils.give_info("western_goods_act_1_task_3_available")
else
dbg_printf("[WG] Tasks Act 1 | Task 3 - What's missing? timeout_ready:%s (%s/%s)", timeout_ready, time_global(), TASK_3_CACHE.available_time)
end
end
--- Function used to give the player an item at the start of Act 1, Task 1.
--- Warning : This function should be called while a dialog is running.
--- @param first_speaker cse_alife_object
--- @param second_speaker cse_alife_object
--- @return nil
function task_1_give_item(first_speaker, second_speaker)
dialogs.relocate_item_section_to_actor(first_speaker, second_speaker, "wg_gps")
end
--- Function used to spawn a static helicopter at the Waste Processing Station if it doesn't exist.
--- @param location string
--- @return boolean
function create_story_helicopter(location)
local const_sec = "western_goods_helicopter_landed"
if not western_goods_utils.server_object_by_sid(const_sec) then
local pos, lvid, gvid
if location == "zaton" then
pos = vector():set(-4.5,-7,536.8)
lvid = 676572
gvid = 4330
elseif location == "wps" then
pos = vector():set(284.1,28.1,-364)
lvid = 1598393
gvid = 4454
else
printf("![WG] ERROR | Tasks Act 1 | Invalid spawn location for %s : loc:%s", const_sec, location)
return false
end
local heli_se = alife_create(const_sec,pos,lvid,gvid)
if heli_se then
dbg_printf("[WG] Tasks Act 1 | Created %s(%s) at loc:%s x:%s,y:%s,z:%s lvid:%s gvid:%s", const_sec, heli_se.id, location, pos.x, pos.y, pos.z, lvid, gvid)
return true
else
printf("![WG] ERROR | Tasks Act 1 | Failed to create %s at loc:%s x:%s,y:%s,z:%s lvid:%s gvid:%s", const_sec, location, pos.x, pos.y, pos.z, lvid, gvid)
return false
end
end
return false
end
--- Function used to release the helicopter at the Waste Processing Station if it exists.
--- @return nil
function release_story_helicopter()
local wps_heli_se = western_goods_utils.server_object_by_sid("western_goods_helicopter_landed")
if wps_heli_se then
alife_release(wps_heli_se)
dbg_printf("[WG] Tasks Act 1 | Released western_goods_helicopter_landed(%s)", wps_heli_se.id)
return true
end
return false
end
--- Function used to give the player a reward for finishing act 1.
--- Warning : This function should be called while a dialog is running.
--- @param first_speaker cse_alife_object
--- @param second_speaker cse_alife_object
--- @return nil
function give_final_reward(first_speaker, second_speaker)
dialogs.relocate_item_section_to_actor(first_speaker, second_speaker, "af_compass_af_aam")
end