--[[
m23.lua
SR3 Mission Script
DATE: 09/14/10
AUTHOR: matt.gawalek
]]--
-- Debug flags --
-- Tweakable Parameters --
M23_tweak_values = {
boss_battle_self_descruct_time_limit = 5 * 60 * 1000, -- 5 minutes, in milliseconds
escape_time_limit = 45000, -- milliseconds
fight_looping_cam_shake_amount = 0.2,
escape_looping_cam_shake_amount = 2.0,
taunt_idx_max = 3
}
-- Checkpoints --
M23_checkpoints = {
start = {
name = MISSION_START_CHECKPOINT, -- defined in ug_lib.lua
player_starts = { "nav_player_start_chk_01_ 001", "nav_player_start_chk_01_ 002" },
},
saints_hq = {
name = "m23_checkpoint_saints_hq",
player_starts = { "nav_player_start_chk_01b 001", "nav_player_start_chk_01b 002" },
},
landing_deck = {
name = "m23_checkpoint_landing_deck",
player_starts = { "nav_player_start_chk_02_ 001", "nav_player_start_chk_02_ 002" },
},
bridge = {
name = "m23_checkpoint_bridge",
player_starts = { "nav_player_start_chk_03_ 001", "nav_player_start_chk_03_ 002" },
},
escape = {
name = "m23_checkpoint_escape",
player_starts = { "nav_player_start_chk_04_ 001", "nav_player_start_chk_04_ 002" },
},
}
M23_cyrus_boss_ai = {
start_path_1 = "path_cyrus_boss_start 001",
start_path_2 = "path_cyrus_boss_start 002",
-- Define Cyrus's various strafe attacks
strafe_attacks = {
{
paths = {
left_to_right = {
intro = "path_cyrus_strafe_lr_intro 001",
middle = "path_cyrus_strafe_lr_middle 001",
outro = "path_cyrus_strafe_lr_outro 001",
},
right_to_left = {
intro = "path_cyrus_strafe_rl_intro 001",
middle = "path_cyrus_strafe_rl_middle 001",
outro = "path_cyrus_strafe_rl_outro 001",
},
},
vtol_speed = 35,
refire_delay = 5.0,
error_radius = 1.5,
},
},
-- Define Cyrus's varius sweep attacks
sweep_attacks = {
{
paths = {
left_to_right = {
intro = "path_cyrus_sweep_lr_intro 001",
middle = "path_cyrus_sweep_lr_middle 001",
outro = "path_cyrus_sweep_lr_outro 001",
},
right_to_left = {
intro = "path_cyrus_sweep_rl_intro 001",
middle = "path_cyrus_sweep_rl_middle 001",
outro = "path_cyrus_sweep_rl_outro 001",
},
},
refire_delay = 0.0,
vtol_speed = 30,
},
},
chase_attacks = {
{
paths = {
intro = "path_cyrus_sweep_intro 001",
outro = "path_cyrus_sweep_outro 001",
},
duration = 20.0,
},
},
reinforcement_attacks = {
{
cyrus_paths = {
left_to_right = "path_cyrus_reinforcement_lr 001",
right_to_left = "path_cyrus_reinforcement_rl 001",
},
transport_paths = {
{
approach_path = "path_cyrus_transport 001",
exit_path = "path_cyrus_trans_exit 001",
},
{
approach_path = "path_cyrus_transport 002",
exit_path = "path_cyrus_trans_exit 002",
},
{
approach_path = "path_cyrus_transport 003",
exit_path = "path_cyrus_trans_exit 003",
},
},
},
},
reinforce_threads = {
{
func = "m23_cyrus_boss_ai_reinforcement_thread"
},
},
attack_threads = {
--[[
{
func = "m23_cyrus_boss_ai_chase_thread",
},
--]]
{
func = "m23_cyrus_boss_ai_strafe_thread",
},
{
func = "m23_cyrus_boss_ai_sweep_thread",
},
},
}
-- Groups --
M23_groups = {
mission_start = {
name = "Group_mission_start 001",
courtesy_cars = { "veh_courtesy_car 001", "veh_courtesy_car 002" },
},
courtesy_helis = {
name = "group_courtesy_helis",
vehicles = { "veh_courtesy_heli 001", "veh_courtesy_heli 002" },
},
daedalus_vtols_01 = {
name = "Group_daedalus_vtols 001",
vtols = { "veh_daedalus_vtol 001", "veh_daedalus_vtol 002", "veh_daedalus_vtol 004", "veh_daedalus_vtol 005", "veh_daedalus_vtol 006" },
},
daedalus_escape_vtols = {
name = "Group_daedalus_vtols 002",
vtols = { "veh_daedalus_vtol 007", "veh_daedalus_vtol 008" },
},
daedalus_tanks_01 = {
name = "Group_daedalus_tanks 001",
tanks = {
{
vehicle = "veh_daedalus_tank_01_ 001",
chars = { "char_tank_driver_01_ 001", "char_tank_gunner_01_ 001" },
},
{
vehicle = "veh_daedalus_tank_01_ 002",
chars = { "char_tank_driver_01_ 002", "char_tank_gunner_01_ 002" },
},
{
vehicle = "veh_daedalus_tank_01_ 003",
chars = { "char_tank_driver_01_ 003", "char_tank_gunner_01_ 003" },
},
},
},
daedalus_tanks_02 = {
name = "Group_daedalus_tanks 002",
tanks = {
{
vehicle = "veh_daedalus_tank_02_ 001",
chars = { "char_tank_gunner_02_ 001"--[[, "char_tank_driver_02_ 001"--]] },
thread_ids = { --[[-1,--]] -1 },
},
{
vehicle = "veh_daedalus_tank_02_ 002",
chars = { "char_tank_gunner_02_ 002"--[[, ""char_tank_driver_02_ 002"--]] },
thread_ids = { --[[-1,--]] -1 },
},
{
vehicle = "veh_daedalus_tank_02_ 003",
chars = { "char_tank_gunner_02_ 003"--[[, ""char_tank_driver_02_ 003"--]] },
thread_ids = { --[[-1,--]] -1 },
},
{
vehicle = "veh_daedalus_tank_02_ 004",
chars = { "char_tank_gunner_02_ 004"--[[, ""char_tank_driver_02_ 004"--]] },
thread_ids = { --[[-1,--]] -1 },
},
},
},
daedalus_apc_01 = {
name = "group_daedalus_apc 001",
},
--[[
attack_vtols_01 = {
name = "Group_attack_vtols 001",
vtols = {
{
vehicle = "veh_attack_vtol 001",
chars = { "char_attack_vtol_pilot 001" },
lift_off_pos = "nav_attack_vtol_takeoff 001",
delay = 0.0,
thread_id = -1,
},
{
vehicle = "veh_attack_vtol 002",
chars = { "char_attack_vtol_pilot 002" },
lift_off_pos = "nav_attack_vtol_takeoff 002",
delay = 6.0,
thread_id = -1,
},
},
},
--]]
dog_fight_01 = {
name = "Group_dog_fight 001",
lead_group = {
vehicle = "veh_saint_dog_fight_01_ 001",
chars = { "char_saint_dog_fight_01_ 001" },
path = "path_dog_fight_01_ 001",
},
chase_groups = {
{
vehicle = "veh_stag_dog_fight_01_ 001",
chars = { "char_stag_dog_fight_01_ 001" },
follow_dist = 40.0
},
},
thread_id = -1,
},
dog_fight_02 = {
name = "Group_dog_fight 002",
lead_group = {
vehicle = "veh_stag_dog_fight_02_ 001",
chars = { "char_stag_dog_fight_02_ 001" },
path = "path_dog_fight_02_ 001",
},
chase_groups = {
{
vehicle = "veh_saint_dog_fight_02_ 001",
chars = { "char_saint_dog_fight_02_ 001" },
follow_dist = 40.0
},
--[[
{
vehicle = "veh_saint_dog_fight_02_ 002",
chars = { "char_saint_dog_fight_02_ 002" },
follow_dist = 40.0
}
--]]
},
thread_id = -1,
},
saints_landing_deck_reinforcements = {
name = "group_saints_ld_reinforce",
groups = {
{
vehicle = "veh_saints_reinforce 001",
chars = { "char_saints_reinforce 001", "char_saints_reinforce 002" },
path = "path_saints_reinforce 001",
landing_nav = "nav_saints_reinforce_land 001",
thread_id = -1,
},
{
vehicle = "veh_saints_reinforce 002",
chars = { "char_saints_reinforce 003", "char_saints_reinforce 004" },
path = "path_saints_reinforce 002",
landing_nav = "nav_saints_reinforce_land 002",
thread_id = -1,
},
},
},
bomb_spawn_01 = {
name = "group_bomb_spawn 001",
},
bomb_spawn_02 = {
name = "group_bomb_spawn 002",
},
bomb_spawn_03 = {
name = "group_bomb_spawn 003",
},
amb_spawn_01 = {
name = "group_stag_amb 001",
},
ammo_stash_02 = {
name = "Group_ammo_stash 002",
miniguns = { "itm_weapon_minigun 001", "itm_weapon_minigun 002" },
},
cyrus_vtol = {
name = "Group_cryrus_vtol 001",
cyrus = "char_cyrus 001",
vtol = "veh_cyrus_vtol 001",
},
transport_vtol_01 = {
name = "Group_transport_vtol 001",
vtol = "veh_transport_vtol_01_ 001",
pilots = { "char_transport_vtol_01_pilot" },
soldiers = { "char_transport_vtol_01_ 002", "char_transport_vtol_01_ 003", "char_transport_vtol_01_ 004", "char_transport_vtol_01_ 005", "char_transport_vtol_01_ 006" },
},
}
-- Navpoints --
M23_navpoints = {
saints_hq_gps = "nav_saints_hq_gps 001",
saints_hq_elevator = "nav_saints_hq_elevator 001",
daedalus_landing_pos = "nav_landing_pos 001",
daedalus_self_destruct_audio_pos = "nav_self_destruct_audio",
mission_end = { "nav_local_end 001", "nav_remote_end 001" },
}
-- Triggers --
M23_triggers = {
saints_hq_activate_helis = "tgr_activate_helis",
awesome_daedalus = "tgr_awesome_daedalus",
saints_hq_elev_up = "tgr_saints_hq_elev_up 001",
saints_hq_elev_down = "tgr_saints_hq_elev_down 001",
saints_hq_gps = "tgr_saints_hq_gps 001",
saints_hq_entry = "tgr_saints_hq_entry",
daedalus_airspace = "tgr_daedalus_airspace 001",
daedalus_gunfire = "tgr_daedalus_gunfire",
daedalus_defensive_area = "tgr_daedalus_defenses 001",
daedalus_landing_zone = "tgr_daedalus_landing_zone 001",
rear_deck = "tgr_rear_deck",
cyrus_combat = "tgr_cyrus_combat 001",
flee_region = "tgr_flee_region 001",
daedalus_blast_radius = "tgr_daedalus_blast_radius 001",
}
-- Kill Regions --
M23_kill_regions = { "tgr_daed_eng_kill 001", "tgr_daed_eng_kill 002", "tgr_daed_eng_kill 003", "tgr_daed_eng_kill 004",
"tgr_daed_eng_kill 005", "tgr_daed_eng_kill 006", "tgr_daed_eng_kill 007", "tgr_daed_eng_kill 008",
"tgr_daed_eng_kill 009", "tgr_daed_eng_kill 010", "tgr_daed_eng_kill 011", "tgr_daed_eng_kill 012",
"tgr_daed_eng_kill 013", "tgr_daed_eng_kill 014", "tgr_daed_eng_kill 015", "tgr_daed_eng_kill 016",
}
-- Characters --
-- Vehicles --
-- Continuous Spawn Groups --
M23_spawn_groups = {
defensive_01 = "Group_stag_cs_defense 001",
defensive_02 = "Group_stag_cs_defense 002",
offensive_01 = "Group_stag_cs_offense 001",
offensive_02 = "Group_stag_cs_offense 002",
riotshield_01 = "Group_stag_cs_riotshield 001",
-- sniper_01 = "Group_stag_cs_sniper 001",
}
-- Spawn Regions --
M23_spawn_regions = {
landing_deck = { "spr_landing_deck 001", "spr_landing_deck 002", "spr_landing_deck 003", "spr_landing_deck 004",
"spr_landing_deck 005", "spr_landing_deck 006", "spr_landing_deck 007", "spr_landing_deck 008",
"spr_landing_deck 009", "spr_landing_deck 010", "spr_landing_deck 011" },
cyrus_deck = { "spr_deck_reinforce 001", "spr_deck_reinforce 002", "spr_deck_reinforce 003",
"spr_deck_reinforce 004", "spr_deck_reinforce 005" },
}
-- Movers --
M23_movers = {
hangar_door = "mvr_daed_door 001",
daedalus_guns = {
{
mover_name = "daedalus_gun_1",
effect_nav = "daed_muzzle_vfx_1",
death_vfx = "daed_death_vfx 001",
firing_thread = INVALID_THREAD_HANDLE,
},
{
mover_name = "daedalus_gun_2",
effect_nav = "daed_muzzle_vfx_2",
death_vfx = "daed_death_vfx 002",
firing_thread = INVALID_THREAD_HANDLE,
},
{
mover_name = "daedalus_gun_3",
effect_nav = "daed_muzzle_vfx_3",
death_vfx = "daed_death_vfx 003",
firing_thread = INVALID_THREAD_HANDLE,
},
{
mover_name = "daedalus_gun_4",
effect_nav = "daed_muzzle_vfx_4",
death_vfx = "daed_death_vfx 004",
firing_thread = INVALID_THREAD_HANDLE,
},
},
}
-- Cutscenes --
M23_cutscenes = {
intro = "23_in",
cyrus_death = "23_z01",
outro = "23_out",
}
-- Text --
M23_objectives = {
get_helicopter = {
tag = "M23_OBJ_GET_HELICOPTER",
icon = OI_ASSET_LOCATION,
},
go_to_daedalus = {
tag = "M23_OBJ_GO_TO_DAEDALUS",
icon = OI_ASSET_LOCATION,
},
land_on_daedalus = {
tag = "M23_OBJ_LAND_ON_DAEDALUS",
icon = OI_ASSET_LOCATION,
},
destroy_guns = {
tag = "M23_OBJ_DESTROY_GUNS",
icon = OI_ASSET_KILL,
},
destroy_defenses = {
tag = "M23_OBJ_DESTROY_DEFENSES",
icon = OI_ASSET_KILL,
},
plant_bombs = {
tag = "M23_OBJ_PLANT_BOMB",
icon = OI_ASSET_USE,
},
fly_to_side_deck = {
tag = "M23_OBJ_FLY_TO_SIDE_DECK",
icon = OI_ASSET_LOCATION,
},
fly_to_rear_deck = {
tag = "M23_OBJ_FLY_TO_REAR_DECK",
icon = OI_ASSET_LOCATION,
},
kill_cyrus = {
tag = "M23_OBJ_KILL_CYRUS",
icon = OI_ASSET_KILL,
},
escape = {
tag = "M23_OBJ_ESCAPE",
icon = OI_ASSET_LOCATION,
},
}
-- Threads --
M23_threads = {
mission_start_vo = -1,
wait_for_heli_takeoff = -1,
cyrus_attack_bridge = -1,
cyrus_boss_ai = -1,
cyrus_boss_action = -1,
cyrus_fire_weapon_thread = -1,
cyrus_transport_thread = -1,
cyrus_destroy_player_helis = -1,
explosions = -1,
async_delay_thread = -1,
daedalus_guns_thread = -1,
}
M23_music = {
mission_start = "M23_music_mission_start",--
mission_start_end = "M23_music_mission_start_end",--
daedalus_battle = "M23_music_daedalus_battle",--
daedalus_battle_end = "M23_music_daedalus_battle_end",--
cyrus_fight = "M23_music_cyrus_boss_fight",--
cyrus_fight_end = "M23_music_cyrus_boss_fight_end",--
checkpoint_saints_hq = "M23_music_mission_start_checkpoint",--
checkpoint_daedalus = "M23_music_daedalus_checkpoint",--
checkpoint_cyrus_fight = "M23_music_cyrus_fight_checkpoint",
stop_all = "M23_music_stop",
}
M23_personas = {
pierce = {
name = "Pierce",
persona_id = -1,
},
oleg = {
name = "Oleg",
persona_id = -1,
},
kinzie = {
name = "Kinzie",
persona_id = -1,
},
angel = {
name = "Angel",
persona_id = -1,
},
cyrus = {
name = "Cyrus",
persona_id = -1,
},
interrogation = {
name = "Interrogation",
persona_id = -1,
},
}
M23_conversations = {
mission_start = {
name = "M23_Mission_Start",
handle = INVALID_CONVERSATION_HANDLE,
},
awesome_daedalus = {
name = "M23_Awesome_Daedalus",
handle = INVALID_CONVERSATION_HANDLE,
},
not_making_a_dent = {
name = "M23_Not_Making_A_Dent",
handle = INVALID_CONVERSATION_HANDLE,
},
cyrus_start = {
name = "M23_Cyrus_Start",
handle = INVALID_CONVERSATION_HANDLE,
},
fireworks_started = {
name = "M23_Cyrus_Low_Health",
handle = INVALID_CONVERSATION_HANDLE,
},
times_up = {
name = "M23_Time_Running_Out_2",
handle = INVALID_CONVERSATION_HANDLE,
},
}
M23_dialog_lines = {
oleg_phone_call = "M23_Oleg_Update_Call",
kinzie_phone_call = "M23_Kinzie_Update_Call",
angel_phone_call = "M23_Angel_Update_Call",
take_out_turrets = "M23_Take_Out_Turrets",
turrets_down = "M23_Turrets_Down",
first_bomb = "M23_Plant_First_Bomb",
second_bomb = "M23_Plant_Second_Bomb",
third_bomb = "M23_Plant_Third_Bomb",
fourth_bomb = "M23_Plant_Fourth_Bomb",
callout_weapons = "M23_Call_Out_Weapons_On_Deck",
-- clear_the_deck = "M23_Clear_the_Deck",
-- get_to_bridge = "M23_Get_to_Bridge",
-- armory = "M23_Armory",
find_cyrus = "M23_Cyrus_Finds_You_On_Deck",
self_destruct = "M23_Self_Destruct_Start",
cyrus_call_backup = "M23_Call_Backup",
cyrus_taunts = { "M23_Cyrus_Taunt_01", "M23_Cyrus_Taunt_02", "M23_Cyrus_Taunt_03" },
abandon_ship = "M23_Time_Running_Out_01",
escape = "M23_Escape",
}
-- Other --
M23_vfx = {
daedalus_explosoin = "vfx_daedalus_explosion 001",
}
M23_missile_racks = {
{
thread = INVALID_THREAD_HANDLE,
cur_rack = 1,
target = "rack_target",
racks = {
-- Racks will be chosen in sequence and a missile will fire from each nav point in that rack in sequence.
{
"rack<005>", "rack<004>", "rack<001>",
},
{
"rack<006>", "rack<003>", "rack<002>",
},
{
"rack<009>", "rack<008>", "rack<007>",
},
{
"rack<012>", "rack<011>", "rack<010>",
},
{
"rack<013>", "rack<014>", "rack<015>",
},
{
"rack<016>", "rack<017>", "rack<018>",
},
},
},
{
thread = INVALID_THREAD_HANDLE,
cur_rack = 1,
target = "rack_target2",
racks = {
-- Racks will be chosen in sequence and a missile will fire from each nav point in that rack in sequence.
{
"rack2<005>", "rack2<004>", "rack2<001>",
},
{
"rack2<006>", "rack2<003>", "rack2<002>",
},
{
"rack2<009>", "rack2<008>", "rack2<007>",
},
{
"rack2<012>", "rack2<011>", "rack2<010>",
},
{
"rack2<013>", "rack2<014>", "rack2<015>",
},
{
"rack2<016>", "rack2<017>", "rack2<018>",
},
},
},
}
M23_daedalus_guns = {
effects = {
"Vfx Daedalus Gun 01<001>", "Vfx Daedalus Gun 01<002>", "Vfx Daedalus Gun 01<003>", "Vfx Daedalus Gun 01<004>", "Vfx Daedalus Gun 01<005>", "Vfx Daedalus Gun 01<006>",
"Vfx Daedalus Gun 01<007>", "Vfx Daedalus Gun 01<008>", "Vfx Daedalus Gun 01<009>", "Vfx Daedalus Gun 01<010>", "Vfx Daedalus Gun 01<011>", "Vfx Daedalus Gun 01<012>",
"Vfx Daedalus Gun 02<001>", "Vfx Daedalus Gun 02<002>", "Vfx Daedalus Gun 02<003>", "Vfx Daedalus Gun 02<004>", "Vfx Daedalus Gun 02<005>", "Vfx Daedalus Gun 02<006>",
"Vfx Daedalus Gun 02<007>", "Vfx Daedalus Gun 02<008>", "Vfx Daedalus Gun 02<009>", "Vfx Daedalus Gun 02<010>", "Vfx Daedalus Gun 02<011>", "Vfx Daedalus Gun 02<012>",
"Vfx Daedalus Gun 03<001>", "Vfx Daedalus Gun 03<002>", "Vfx Daedalus Gun 03<003>", "Vfx Daedalus Gun 03<004>", "Vfx Daedalus Gun 03<005>", "Vfx Daedalus Gun 03<006>",
"Vfx Daedalus Gun 03<007>", "Vfx Daedalus Gun 03<008>", "Vfx Daedalus Gun 03<009>", "Vfx Daedalus Gun 03<010>", "Vfx Daedalus Gun 03<011>", "Vfx Daedalus Gun 03<012>",
"Vfx Daedalus Gun 03<013>", "Vfx Daedalus Gun 03<014>", "Vfx Daedalus Gun 03<015>", "Vfx Daedalus Gun 03<016>", "Vfx Daedalus Gun 03<017>", "Vfx Daedalus Gun 03<018>",
"Vfx Daedalus Gun 03<019>", "Vfx Daedalus Gun 03<020>",
"Vfx Daedalus Gun 04<001>", "Vfx Daedalus Gun 04<002>",
},
threads = {
[1] = INVALID_THREAD_HANDLE,
[2] = INVALID_THREAD_HANDLE,
[3] = INVALID_THREAD_HANDLE,
[4] = INVALID_THREAD_HANDLE,
[5] = INVALID_THREAD_HANDLE,
[6] = INVALID_THREAD_HANDLE,
[7] = INVALID_THREAD_HANDLE,
[8] = INVALID_THREAD_HANDLE,
[9] = INVALID_THREAD_HANDLE,
[10] = INVALID_THREAD_HANDLE,
},
active_effect_handles = {
[1] = -1,
[2] = -1,
[3] = -1,
[4] = -1,
[5] = -1,
[6] = -1,
[7] = -1,
[8] = -1,
[9] = -1,
[10] = -1,
},
active_audio_loops = {
[1] = -1,
[2] = -1,
[3] = -1,
[4] = -1,
[5] = -1,
[6] = -1,
[7] = -1,
[8] = -1,
[9] = -1,
[10] = -1,
},
}
M23_explosions = {
effect = "VTOL_M_Exp-NPC",
self_destruct_begin_explosions = { "warning_splosion <001>", "warning_splosion <002>", "warning_splosion <003>" },
self_destruct_explosions = { "self_destruct_splosion <001>", "self_destruct_splosion <002>", "self_destruct_splosion <003>",
"self_destruct_splosion <004>", "self_destruct_splosion <005>", "self_destruct_splosion <006>",
"self_destruct_splosion <007>", "self_destruct_splosion <008>", "self_destruct_splosion <009>",
"self_destruct_splosion <010>", "self_destruct_splosion <011>", "self_destruct_splosion <012>",
"self_destruct_splosion <013>", "self_destruct_splosion <014>", "self_destruct_splosion <015>",
"self_destruct_splosion <016>", "self_destruct_splosion <017>", "self_destruct_splosion <018>",
"self_destruct_splosion <019>", "self_destruct_splosion <020>", "self_destruct_splosion <021>",
"self_destruct_splosion <022>" },
vtol_escape_explosions = { "self_destruct_splosion <023>", "self_destruct_splosion <024>", "self_destruct_splosion <025>",
"self_destruct_splosion <026>", "self_destruct_splosion <027>", "self_destruct_splosion <028>",
"self_destruct_splosion <029>", },
}
M23_smoke = {
city_carnage = { "vfx_smoke_city 001", "vfx_smoke_city 002", "vfx_smoke_city 003", "vfx_smoke_city 004", "vfx_smoke_city 005" },
daedalus_rear = { "vfx_smoke_rear 001", "vfx_smoke_rear 002", "vfx_smoke_rear 003", "vfx_smoke_rear 004" },
daedalus_fore = { "vfx_smoke_fore 001", "vfx_smoke_fore 002", "vfx_smoke_fore 003" },
}
M23_flags = {
showed_courtesy_helis = false,
player_entered_saints_hq = false,
player_got_heli = { false, false },
daedalus_airspace_entered = false,
daedalus_defensive_area_entered = false,
daedalus_landing_complete = false,
destruct_start = false,
cyrus_vtol_destroyed = false,
escape_timer_expired = false,
do_cyrus_reinforcements = false,
cyrus_final_phase = false,
cyrus_fly_left_to_right = true,
local_player_escaped = false,
remote_player_escaped = false,
daedalus_main_cannons_firing = false,
play_kinzie_call_on_next_vehicle_enter = false,
daedalus_gunfire_enabled_local = false,
daedalus_gunfire_enabled_remote = false,
leaving_daedalus_fails_mission = false,
}
M23_runtime = {
daedalus_guns_alive = 0,
daedalus_surface_tanks_alive = 0,
cyrus_taunt_idx = 0,
local_player_last_vehicle = nil,
remote_player_last_vehicle = nil,
cur_bomb_planted = false,
cur_cell_call = "",
ambush_groups = { },
}
M23_bomb_triggers = {
landing_deck_bomb_1 = {
trigger_name = "tgr_plant_bomb_1",
minimap_icon = MINIMAP_ICON_USE,
ingame_effect = INGAME_EFFECT_LOCATION,
oi_asset = OI_ASSET_USE,
oi_flags = OI_FLAGS_LOCATION,
objective = M23_objectives.plant_bombs,
play_bomb_anim = true,
anim_nav = "bomb_1_anim_orient",
},
landing_deck_bomb_2 = {
trigger_name = "tgr_plant_bomb_2",
minimap_icon = MINIMAP_ICON_USE,
ingame_effect = INGAME_EFFECT_LOCATION,
oi_asset = OI_ASSET_USE,
oi_flags = OI_FLAGS_LOCATION,
objective = M23_objectives.plant_bombs,
play_bomb_anim = true,
anim_nav = "bomb_2_anim_orient",
},
fly_to_side = {
trigger_name = "tgr_fly_to_side",
minimap_icon = MINIMAP_ICON_LOCATION,
ingame_effect = INGAME_EFFECT_LOCATION,
oi_asset = OI_ASSET_LOCATION,
oi_flags = OI_FLAGS_LOCATION,
objective = M23_objectives.fly_to_rear_deck,
play_bomb_anim = false,
},
side_deck_bomb = {
trigger_name = "tgr_plant_bomb_3",
minimap_icon = MINIMAP_ICON_USE,
ingame_effect = INGAME_EFFECT_LOCATION,
oi_asset = OI_ASSET_USE,
oi_flags = OI_FLAGS_LOCATION,
objective = M23_objectives.plant_bombs,
play_bomb_anim = true,
anim_nav = "bomb_3_anim_orient",
},
fly_to_rear = {
trigger_name = "tgr_fly_to_rear",
minimap_icon = MINIMAP_ICON_LOCATION,
ingame_effect = INGAME_EFFECT_LOCATION,
oi_asset = OI_ASSET_LOCATION,
oi_flags = OI_FLAGS_LOCATION,
objective = M23_objectives.fly_to_rear_deck,
play_bomb_anim = false,
},
rear_deck_bomb = {
trigger_name = "tgr_plant_bomb_4",
minimap_icon = MINIMAP_ICON_USE,
ingame_effect = INGAME_EFFECT_LOCATION,
oi_asset = OI_ASSET_USE,
oi_flags = OI_FLAGS_LOCATION,
objective = M23_objectives.plant_bombs,
play_bomb_anim = true,
anim_nav = "bomb_4_anim_orient",
},
}
M23_audio_emitters = {
daedalus_alarm = "M23_alarm",
}
-- *************************
--
-- Standard functions
--
-- *************************
-- This is the primary entry point for the mission, and is responsible for starting up the mission
-- at the specified checkpoint.
-- CALLED FROM CODE
--
-- m23_checkpoint: The checkpoint the mission should begin at
-- is_restart: TRUE if the mission is restarting, FALSE otherwise
--
function m23_start(m23_checkpoint, is_restart)
-- Check if this mission starting from the beginning
if (m23_checkpoint == M23_checkpoints.start.name) then
if (is_restart == false) then
-- First time playing mission
-- Play an intro cutscene???
if (M23_cutscenes.intro ~= "") then
cutscene_play(M23_cutscenes.intro, nil, {M23_checkpoints.start.player_starts[1], M23_checkpoints.start.player_starts[2]}, false)
end
end
end
-- Handle mission initialization for the current checkpoint
m23_initialize(m23_checkpoint, is_restart)
-- Run the mission from the current checkpoint
m23_run(m23_checkpoint)
end
-- This is the primary function responsible for running the entire mission from start to finish.
--
-- first_checkpoint: The first checkpoint to begin running the mission at
--
function m23_run(first_checkpoint)
local current_checkpoint = first_checkpoint
mission_set_cancel_warp_location(M23_checkpoints.start.player_starts[1], M23_checkpoints.start.player_starts[2])
-- Run the mission from the beginning
if (current_checkpoint == M23_checkpoints.start.name) then
m23_get_to_saints_hq()
-- Set the checkpoint
current_checkpoint = M23_checkpoints.saints_hq.name
mission_set_checkpoint(M23_checkpoints.saints_hq.name, true)
end
mission_set_cancel_warp_location(M23_checkpoints.saints_hq.player_starts[1], M23_checkpoints.saints_hq.player_starts[2])
if (current_checkpoint == M23_checkpoints.saints_hq.name) then
m23_aquire_helicopter()
m23_get_to_daedalus()
--m23_approach_daedalus_landing_area()
m23_daedalus_destroy_guns()
-- m23_daedalus_destroy_tanks()
m23_land_on_the_daedalus()
-- Set the checkpoint
current_checkpoint = M23_checkpoints.landing_deck.name
mission_set_checkpoint(M23_checkpoints.landing_deck.name, true)
end
-- At this point, if a player leaves the Daedalus, they fail the mission.
M23_flags.leaving_daedalus_fails_mission = true
if (current_checkpoint == M23_checkpoints.landing_deck.name) then
character_ragdoll_set_last_resort_position(M23_checkpoints.landing_deck.player_starts[1])
audio_ambient_emitter_start(M23_audio_emitters.daedalus_alarm)
m23_plant_bombs()
audio_ambient_emitter_stop(M23_audio_emitters.daedalus_alarm)
-- Set the checkpoint
current_checkpoint = M23_checkpoints.bridge.name
mission_set_checkpoint(M23_checkpoints.bridge.name, true)
end
if (current_checkpoint == M23_checkpoints.bridge.name) then
character_ragdoll_set_last_resort_position(M23_checkpoints.bridge.player_starts[1])
m23_cyrus_fly_by()
-- Progress music.
m23_post_music_event(M23_music.cyrus_fight)
m23_kill_cyrus()
m23_play_cyrus_death_scene()
-- Set the checkpoint
current_checkpoint = M23_checkpoints.escape.name
mission_set_checkpoint(M23_checkpoints.escape.name, true)
end
-- At this point, the players no longer fail for leaving the Daedalus.
M23_flags.leaving_daedalus_fails_mission = false
if (current_checkpoint == M23_checkpoints.escape.name) then
character_ragdoll_set_last_resort_position(M23_checkpoints.escape.player_starts[1])
m23_escape_from_daedalus()
end
--m23_success() -- Called from C after mission_end_success
fade_out(0.5)
fade_out_block()
city_zone_swap("daedext1", DISABLE) -- remove daedalus exterior
delay(1.0)
-- Call mission success??
mission_end_success("m23", M23_cutscenes.outro, M23_navpoints.mission_end)
end
-- This is the primary function responsible for cleaning up the entire mission
-- CALLED FROM CODE (+++MUST RETURN IMMEDIATLY+++)
--
function m23_cleanup()
-- Call all of the clean-up functions for each section of this mission
m23_cleanup_aquire_heli()
m23_cleanup_get_to_daedalus()
--m23_cleanup_approach_daedalus_landing_area()
--m23_cleanup_daedalus_destroy_tanks()
m23_cleanup_daedalus_destroy_guns()
m23_cleanup_land_on_the_daedalus()
m23_cleanup_plant_bombs()
m23_cleanup_cyrus_fly_by()
m23_cleanup_kill_cyrus()
m23_cleanup_escape_from_daedalus()
m23_cleanup_cyrus_death_scene()
m23_exited_daedalus_gunfire(nil, nil, nil, true)
-- Destroy any active continous spawn groups
m23_cleanup_landing_deck_cont_spawn(true)
-- Clear the any notoriety
notoriety_reset("police")
-- notoriety_set_desired_vehicle_count("police", -1)
m23_set_notoriety_aircraft_enabled(true)
-- set_stag_active(false) -- Commented this out because we are setting it now only with mission success because of critical path -JB
-- Clear all the peds fleeing, and stop the continuous explosion
set_civilians_flee(false)
continuous_explosion_stop()
-- Make sure any triggers have been disabled
for i, trigger in pairs(M23_triggers) do
on_trigger("", trigger)
on_trigger_exit("", trigger)
trigger_enable(trigger, false)
marker_remove_trigger(trigger)
end
-- Cleanup kill regions
for i, region in pairs(M23_kill_regions) do
trigger_enable(region, false)
on_trigger("", region)
marker_remove_trigger(region)
end
-- Make sure all groups have been destroyed
for i, group in pairs(M23_groups) do
if (group_is_loaded(group.name) == true) then
group_destroy(group.name)
end
end
-- Make sure all threads are inactive
for i, thread in pairs(M23_threads) do
if (thread ~= -1 and thread_check_done(thread) == false) then
thread_kill(thread)
end
end
-- Make sure all the threads processing the dog-fights are cleaned up
if (M23_groups.dog_fight_01.thread_id ~= -1 and thread_check_done(M23_groups.dog_fight_01.thread_id) == false) then
thread_kill(M23_groups.dog_fight_01.thread_id)
end
if (M23_groups.dog_fight_02.thread_id ~= -1 and thread_check_done(M23_groups.dog_fight_02.thread_id) == false) then
thread_kill(M23_groups.dog_fight_02.thread_id)
end
--[[
-- Make sure all the threads processing the VTOLs taking off are cleaned up
for i, vtol in pairs(M23_groups.attack_vtols_01.vtols) do
if (vtol.thread_id ~= -1 and thread_check_done(vtol.thread_id) == false) then
thread_kill(vtol.thread_id)
end
end
--]]
-- Make sure all the threads processing the guys running to their tanks are cleaned up
for i, tank in pairs(M23_groups.daedalus_tanks_02.tanks) do
for j, char in pairs(tank.chars) do
if (tank.thread_ids[j] ~= -1 and thread_check_done(tank.thread_ids[j]) == false) then
thread_kill(tank.thread_ids[j])
end
end
end
-- Unload off-screen personas
for i, persona in pairs(M23_personas) do
if (persona.persona_id ~= -1) then
audio_persona_remove_2d(persona.persona_id)
persona.persona_id = -1
end
end
-- Clean-up any conversations
for i, convo in pairs(M23_conversations) do
if (convo.handle ~= INVALID_CONVERSATION_HANDLE) then
audio_conversation_end(convo.handle)
end
end
-- Stop the explosions thread
if (M23_threads.explosions ~= INVALID_THREAD_HANDLE) then
thread_kill(M23_threads.explosions)
M23_threads.explosions = INVALID_THREAD_HANDLE
end
-- Make sure the thread processing the Saints' landing deck reinforcements is killed.
for i, group in pairs(M23_groups.saints_landing_deck_reinforcements.groups) do
if (group.thread_id ~= -1 and thread_check_done(group.thread_id) == false) then
thread_kill(group.thread_id)
group.thread_id = -1
end
end
m23_end_ambient_explosion_audio()
m23_disable_mission_ambient_emitters()
-- Clean up zone swaps.
city_zone_swap("daedext1", DISABLE) -- remove daedalus exterior
-- Tell audio to stop everything that's going on.
audio_object_post_event("M23_unload")
-- Stop any cell calls.
audio_remove_mission_cellphone(M23_runtime.cur_cell_call)
-- Stop using the mission specified ragdoll get up last resort.
character_ragdoll_clear_last_resort_position()
end
-- Called when the mission has ended with success
-- CALLED FROM CODE (+++MUST RETURN IMMEDIATLY+++)
--
function m23_success()
--[[ INSERT ANY MISSION SPECIFIC SUCCESS STUFF ]]--
-- if (M23_cutscenes.outro ~= "") then
-- bink_play(M23_cutscenes.outro)
-- end
--'Win the Game' World State Changes
local skip_teleport = true
m23_m24_coop_skip(skip_teleport) -- do the stuff we'd do in the skip case
-- Stop music.
m23_post_music_event(M23_music.stop_all)
end
-- *************************
--
-- Local functions
--
-- *************************
-- Initialize the mission for the specified checkpoint
--
-- checkpoint: Checkpoint to initialize the mission to
-- is_restart: Whether or not the mission is being restarted.
--
function m23_initialize(checkpoint, is_restart)
-- Make sure the screen is completly faded out
mission_start_fade_out(0.0)
-- Set the mission author
set_mission_author("matt.gawalek")
-- Common initialization
m23_initialize_common()
-- Checkpoint specific initialization
m23_initialize_checkpoint(checkpoint, is_restart)
-- Start fading in
mission_start_fade_in()
end
-- ***************************************************
-- m23_initialize Helper Functions
-- ***************************************************
-- Handle any common initialization
--
function m23_initialize_common()
-- Perform zone swaps
city_zone_swap("daedext1", true) -- swap in the daedalus exterior
-- Begin constant rumble-booms in the background.
m23_begin_ambient_explosion_audio()
-- Set up kill regions
for i, region in pairs(M23_kill_regions) do
trigger_enable(region, true)
on_trigger("m23_engine_death_cb", region)
end
-- Turn on notoriety, but only spawn air units
notoriety_spawn_only_air(true)
set_stag_active(true)
notoriety_set("police", 5)
notoriety_set_can_decay(false)
-- Spawn the initial script objects for the Daedalus
group_create(M23_groups.ammo_stash_02.name, true)
-- Create the second group of tanks, and hide all the characters
group_create(M23_groups.daedalus_tanks_02.name, true)
for i, tank in pairs(M23_groups.daedalus_tanks_02.tanks) do
for j, char in pairs(tank.chars) do
character_hide(char)
end
end
group_create(M23_groups.daedalus_apc_01.name, true)
-- Load off-screen personas
for i, persona in pairs(M23_personas) do
persona.persona_id = audio_persona_load_2d(persona.name)
end
M23_runtime.local_player_last_vehicle = nil
M23_runtime.remote_player_last_vehicle = nil
-- Activate the trigger to begin gunfire around the Daedalus.
trigger_enable(M23_triggers.daedalus_gunfire, true)
on_trigger("m23_entered_daedalus_gunfire", M23_triggers.daedalus_gunfire)
on_trigger_exit("m23_exited_daedalus_gunfire", M23_triggers.daedalus_gunfire)
end
-- Begins the constant background explosion audio for the player.
--
function m23_begin_ambient_explosion_audio()
audio_object_post_event("M23_AMB_CITY_BOMBARDMENT", nil, nil, LOCAL_PLAYER)
if coop_is_active() == true then
audio_object_post_event("M23_AMB_CITY_BOMBARDMENT", nil, nil, REMOTE_PLAYER)
end
end
-- Ends the constant background explosion audio for the player.
--
function m23_end_ambient_explosion_audio()
audio_object_post_event("M23_AMB_CITY_BOMBARDMENT_STOP", nil, nil, LOCAL_PLAYER)
if coop_is_active() == true then
audio_object_post_event("M23_AMB_CITY_BOMBARDMENT_STOP", nil, nil, REMOTE_PLAYER)
end
end
-- Disables ambient emitters for this mission.
--
function m23_disable_mission_ambient_emitters()
for i, emitter in pairs(M23_audio_emitters) do
audio_ambient_emitter_stop(emitter)
end
end
-- Creates the group of VTOLs on the landing deck.
function m23_create_landing_deck_vtols()
group_create(M23_groups.daedalus_vtols_01.name, true)
turn_invulnerable(M23_groups.daedalus_vtols_01.vtols[1])
on_vehicle_enter("m23_invuln_vtol_enter", M23_groups.daedalus_vtols_01.vtols[1])
end
-- Detonates and then shortly destroys the landing deck VTOL group. We need
-- to do this to free up more high component vehicles.
function m23_obliterate_landing_deck_vtols()
if group_is_loaded(M23_groups.daedalus_vtols_01.name) then
for i, vtol in pairs(M23_groups.daedalus_vtols_01.vtols) do
vehicle_detonate(vtol)
end
end
-- Delay regardless to keep timing consistent
delay(3.0)
if group_is_loaded(M23_groups.daedalus_vtols_01.name) then
group_destroy(M23_groups.daedalus_vtols_01.name)
end
end
-- Lights a character on fire, explodes them, and kills them.
-- Basically it ruins their day.
--
-- character: (string) name of the character to kill
-- light_on_fire: (bool, optional; default = false) whether or not we should light the character on fire
-- explode: (bool, optional; default = false) whether or not the character should explode
-- allow_revive: (bool, optional; default = true) whether or not the player should be revivable.
--
function m23_insta_kill_character(character, light_on_fire, explode, allow_revive)
if light_on_fire == true then
character_ignite(character)
end
if explode == true then
explosion_create("VTOL_M_Exp-NPC", character)
end
if allow_revive == nil then
allow_revive = true
end
character_kill(character, true, nil, allow_revive)
end
-- Begins firing missiles from the Daedalus missile racks and the main cannons and starts up ambient emitters.
function m23_entered_daedalus_gunfire(character, trigger)
-- Make sure this is one of the players and if it is flag them as enabled for playing gunfire effects.
if character == LOCAL_PLAYER then
M23_flags.daedalus_gunfire_enabled_local = true
elseif character == REMOTE_PLAYER then
M23_flags.daedalus_gunfire_enabled_remote = true
else
-- Not a player.
return
end
-- Only enable the missile racks if both players are in range.
local missiles_enabled = true
if coop_is_active() then
-- Check both players.
missiles_enabled = M23_flags.daedalus_gunfire_enabled_local and M23_flags.daedalus_gunfire_enabled_remote
else
-- Only need to check the local player.
missiles_enabled = M23_flags.daedalus_gunfire_enabled_local
end
if missiles_enabled then
for i, rack in pairs(M23_missile_racks) do
rack.thread = thread_new("m23_missile_rack_thread", rack)
end
end
-- Start up a gunfire thread if we don't have one running yet.
if M23_threads.daedalus_guns_thread == INVALID_THREAD_HANDLE then
M23_threads.daedalus_guns_thread = thread_new("m23_daedalus_guns_thread")
end
M23_flags.daedalus_main_cannons_firing = true
end
-- Stops any threads firing missiles from the Daedalus missile racks and stops ambient emitters.
--
-- register_callback: (bool) if true, we'll register a trigger callback to start
-- up the missiles firing again if the player gets close to
-- the Daedalus again.
-- shut_it_all_down: (bool, optional) if true, we shut everything down for both players, used during cleanup.
--
function m23_exited_daedalus_gunfire(character, trigger, register_callback, shut_it_all_down)
-- Make sure this is one of the players and if it is flag them as disabled for playing gunfire effects.
if shut_it_all_down == true then
-- Clean everything up.
M23_flags.daedalus_gunfire_enabled_local = false
M23_flags.daedalus_gunfire_enabled_remote = false
else
if character == LOCAL_PLAYER then
M23_flags.daedalus_gunfire_enabled_local = false
elseif character == REMOTE_PLAYER then
M23_flags.daedalus_gunfire_enabled_remote = false
else
-- Not a player.
return
end
end
-- If either player left the area, stop the missile racks. Explosions tend
-- to queue up and all go at once if they're created in an unloaded zone.
for i, rack in pairs(M23_missile_racks) do
if rack.thread ~= INVALID_THREAD_HANDLE then
thread_kill(rack.thread)
rack.thread = INVALID_THREAD_HANDLE
end
end
-- If both players left the area, stop the gunfire thread.
if M23_flags.daedalus_gunfire_enabled_local == false and M23_flags.daedalus_gunfire_enabled_remote == false then
M23_daedalus_guns.threads = cleanup_threads(M23_daedalus_guns.threads)
M23_threads.daedalus_guns_thread = cleanup_threads(M23_threads.daedalus_guns_thread)
m23_stop_all_daedalus_gun_effects()
end
M23_flags.daedalus_main_cannons_firing = false
-- If a player exits this trigger after they've landed on the Daedalus, this is now a
-- failure condition since we have to disable the crib garage.
if M23_flags.leaving_daedalus_fails_mission == true then
mission_end_failure("m23", "m23_failure_cyrus_escaped")
end
end
-- Callback function for when the player enters the invulnerable VTOL.
function m23_invuln_vtol_enter(player, vehicle, seat)
turn_vulnerable(vehicle)
on_vehicle_enter("", vehicle)
end
-- Callback function for when the player flies a vehicle/lands in the engines
function m23_engine_death_cb(char, trigger)
if character_is_in_vehicle(char) then
vehicle_detonate(get_char_vehicle_name(char))
end
delay(1.0)
m23_insta_kill_character(char, true)
end
-- Helper function that spawns and sets up the initial stuff for
-- the mission. Used by the first two checkpoints.
--
function m23_initialize_start_common()
-- Create all the mission start script objects
group_create(M23_groups.mission_start.name, true)
-- Setup the courtesy helicopters (invulnerable + infinite mass)
for i, heli in pairs(M23_groups.courtesy_helis.vehicles) do
turn_invulnerable(heli)
vehicle_infinite_mass(heli, true)
end
-- Set all the peds fleeing from the Daedalus and start a continuous explosion
set_ped_density(1.0)
set_traffic_density(1.0)
set_civilians_flee(true, false)
continuous_explosion_start("Daedalus", M23_navpoints.daedalus_landing_pos)
continuous_explosion_follow_target_add(LOCAL_PLAYER)
continuous_explosion_avoid_object_add(LOCAL_PLAYER)
if (coop_is_active() == true) then
continuous_explosion_follow_target_add(REMOTE_PLAYER)
continuous_explosion_avoid_object_add(REMOTE_PLAYER)
end
for i, vfx in pairs(M23_smoke.city_carnage) do
effect_play(vfx, true)
end
-- Add the courtesy vehicles to the continuous explosion avoid list
continuous_explosion_avoid_object_add(M23_groups.mission_start.courtesy_cars[1])
if (coop_is_active() == true) then
continuous_explosion_avoid_object_add(M23_groups.mission_start.courtesy_cars[2])
else
-- If coop isn't active, then hide the second courtesy vehicles
vehicle_hide(M23_groups.mission_start.courtesy_cars[2])
end
for i, heli in pairs(M23_groups.courtesy_helis.vehicles) do
continuous_explosion_avoid_object_add(heli)
end
-- Create the script group for the daedalus tanks
group_create(M23_groups.daedalus_tanks_01.name, true)
for i, tank in pairs(M23_groups.daedalus_tanks_01.tanks) do
vehicle_suppress_npc_exit(tank.vehicle, true)
vehicle_enter_group_teleport(tank.chars, tank.vehicle)
--vehicle_enter_teleport(tank.chars[1], tank.vehicle, 1)
--vehicle_enter_teleport(tank.chars[2], tank.vehicle, 2)
-- Make the tank stationary
vehicle_disable_chase(tank.vehicle, true)
vehicle_disable_flee(tank.vehicle, true)
end
--[[
-- Create the script group for the attack VTOLs
group_create(M23_groups.attack_vtols_01.name, true)
for i, vtol in pairs(M23_groups.attack_vtols_01.vtols) do
for j, char in pairs(vtol.chars) do
vehicle_enter_teleport(char, vtol.vehicle, (j - 1))
end
end
--]]
end
-- Checkpoint specific initialization
--
-- checkpoint: The checkpoint to be initialized
-- is_restart: Whether or not the mission is being restarted.
--
function m23_initialize_checkpoint(checkpoint, is_restart)
if (checkpoint == M23_checkpoints.start.name) then
if is_restart then
-- Teleport the players into position if we're restarting. Otherwise the cutscene will do this.
teleport_coop(M23_checkpoints.start.player_starts[1], M23_checkpoints.start.player_starts[2])
end
-- Create the courtesy helicopters hidden, and unhide them when the player gets closer.
group_create_hidden(M23_groups.courtesy_helis.name, true)
trigger_enable(M23_triggers.saints_hq_activate_helis, true)
on_trigger("m23_saints_hq_activate_helis_cb", M23_triggers.saints_hq_activate_helis)
m23_initialize_start_common()
m23_create_landing_deck_vtols()
--[[ TODO: This should be unnecessary once the saints HQ crib is up and running ]]--
-- Setup the trigger for the Saint's HQ
--trigger_enable(M23_triggers.saints_hq_elev_up, true)
trigger_enable(M23_triggers.saints_hq_elev_down, true)
trigger_enable(M23_triggers.saints_hq_gps, true)
on_trigger("", M23_triggers.saints_hq_elev_up)
on_trigger("m23_saints_hq_elev_down_cb", M23_triggers.saints_hq_elev_down)
on_trigger("m23_saints_hq_gps_cb", M23_triggers.saints_hq_gps)
--[[ END TODO ]]--
m23_post_music_event(M23_music.mission_start)
end
if (checkpoint == M23_checkpoints.saints_hq.name) then
-- Teleport the players into position
teleport_coop(M23_checkpoints.saints_hq.player_starts[1], M23_checkpoints.saints_hq.player_starts[2])
group_create(M23_groups.courtesy_helis.name, true)
m23_initialize_start_common()
m23_create_landing_deck_vtols()
m23_post_music_event(M23_music.checkpoint_saints_hq)
end
if (checkpoint == M23_checkpoints.landing_deck.name) then
-- Teleport the players into position
teleport_coop(M23_checkpoints.landing_deck.player_starts[1], M23_checkpoints.landing_deck.player_starts[2])
m23_set_notoriety_aircraft_enabled(false)
m23_entered_daedalus_gunfire(LOCAL_PLAYER)
if coop_is_active() then
m23_entered_daedalus_gunfire(REMOTE_PLAYER)
end
-- Start the continious spawn on the landing deck
m23_setup_landing_deck_cont_spawn()
m23_create_landing_deck_vtols()
m23_post_music_event(M23_music.checkpoint_daedalus)
end
if (checkpoint == M23_checkpoints.bridge.name) then
-- Teleport the players into position
teleport_coop(M23_checkpoints.bridge.player_starts[1], M23_checkpoints.bridge.player_starts[2])
m23_set_notoriety_aircraft_enabled(false)
m23_entered_daedalus_gunfire(LOCAL_PLAYER)
if coop_is_active() then
m23_entered_daedalus_gunfire(REMOTE_PLAYER)
end
end
if (checkpoint == M23_checkpoints.escape.name) then
-- Teleport the players into position
teleport_coop(M23_checkpoints.escape.player_starts[1], M23_checkpoints.escape.player_starts[2])
m23_post_music_event(M23_music.checkpoint_cyrus_fight)
audio_object_post_event("M23_SFX_DAEDALUS_SELF_DESTRUCT", nil, nil, M23_navpoints.daedalus_self_destruct_audio_pos)
m23_set_notoriety_aircraft_enabled(false)
end
end
-- ***************************************************
-- m23_run Helper Functions
-- ***************************************************
-- Returns which player(s) are in range to see the gunfire effects on the Daedalus.
--
function m23_get_daedalus_gunfire_sync_flags()
local sync_flags = SYNC_ALL
if M23_flags.daedalus_gunfire_enabled_local ~= true then
sync_flags = SYNC_REMOTE
end
if M23_flags.daedalus_gunfire_enabled_remote ~= true then
if sync_flags ~= SYNC_ALL then
return -1
end
sync_flags = SYNC_LOCAL
end
return sync_flags
end
-- Posts a music audio event.
function m23_post_music_event(event_name)
audio_object_post_event(event_name, nil, nil, LOCAL_PLAYER)
end
function m23_cell_convo_finished()
M23_runtime.cur_cell_call = ""
end
function m23_play_cell_convo(conv_name, receiving_call, persona_override_id)
while M23_runtime.cur_cell_call ~= "" do
thread_yield()
end
M23_runtime.cur_cell_call = conv_name
audio_play_for_mission_cellphone(conv_name, receiving_call, true, "", "m23_cell_convo_finished", persona_override_id)
while M23_runtime.cur_cell_call ~= "" do
thread_yield()
end
end
-- Stops all the gun effects from the Daedalus.
--
function m23_stop_all_daedalus_gun_effects()
for i in pairs(M23_daedalus_guns.active_effect_handles) do
if M23_daedalus_guns.active_effect_handles[i] ~= -1 then
-- Stop the effect.
effect_stop(M23_daedalus_guns.active_effect_handles[i])
M23_daedalus_guns.active_effect_handles[i] = -1
end
end
for i in pairs(M23_daedalus_guns.active_audio_loops) do
if M23_daedalus_guns.active_audio_loops[i] > 0 then
-- Stop the audio.
audio_stop(M23_daedalus_guns.active_audio_loops[i])
M23_daedalus_guns.active_audio_loops[i] = -1
end
end
end
-- Have Cyrus fly a path for the boss battle.
function m23_cyrus_pathfind(left_to_right_path, right_to_left_path, speed)
local path_name = ""
if M23_flags.cyrus_fly_left_to_right == true then
path_name = left_to_right_path
else
path_name = right_to_left_path
end
if (helicopter_fly_to_direct_dont_stop(M23_groups.cyrus_vtol.vtol, speed, path_name) == false) then
--message("Failed path: " .. path_name, 4.0)
end
end
-- Get to the Saints HQ
function m23_get_to_saints_hq()
-- Set the objective text
objective_text(0, M23_objectives.get_helicopter.tag, nil, nil, SYNC_ALL, M23_objectives.get_helicopter.icon)
-- Add a waypoint to the Saint's HQ
mission_waypoint_add(M23_navpoints.saints_hq_gps, SYNC_ALL)
--marker_add_trigger(M23_triggers.saints_hq_gps, MINIMAP_ICON_LOCATION, INGAME_EFFECT_LOCATION, OI_ASSET_LOCATION, OI_FLAGS_LOCATION, SYNC_ALL)
marker_add_trigger(M23_triggers.saints_hq_elev_up, MINIMAP_ICON_LOCATION, INGAME_EFFECT_LOCATION, OI_ASSET_LOCATION, OI_FLAGS_LOCATION, SYNC_ALL)
-- Kick off a thread to process the VO for the mission start
M23_threads.mission_start_vo = thread_new("m23_thread_process_mission_start_vo")
-- Set up the trigger at the top of the elevator.
trigger_enable(M23_triggers.saints_hq_entry, true)
on_trigger("m23_saints_hq_entry_cb", M23_triggers.saints_hq_entry)
-- Register a callback for when a player enters a vehicle
on_vehicle_enter("m23_player_entered_vehicle_cb", LOCAL_PLAYER)
if (coop_is_active() == true) then
on_vehicle_enter("m23_player_entered_vehicle_cb", REMOTE_PLAYER)
end
-- Wait for a player to enter the Saints HQ or get a heli elsewhere
while (M23_flags.player_entered_saints_hq == false and M23_flags.player_got_heli[1] == false and M23_flags.player_got_heli[2] == false) do
thread_yield()
end
m23_cleanup_get_to_saints_hq()
end
-- Thread to process the series of phone call to the player from the Saints
function m23_thread_process_mission_start_vo()
M23_conversations.mission_start.handle = audio_conversation_load(M23_conversations.mission_start.name)
audio_conversation_play(M23_conversations.mission_start.handle)
audio_conversation_wait_for_end(M23_conversations.mission_start.handle)
delay(6.0)
m23_play_cell_convo(M23_dialog_lines.oleg_phone_call, true, M23_personas.oleg.persona_id)
delay(6.0)
local audio_id = audio_play_persona_line_2d(M23_personas.kinzie.persona_id, M23_dialog_lines.kinzie_phone_call)
while(audio_is_playing(audio_id) == true) do
thread_yield()
end
delay(8.0)
audio_id = audio_play_persona_line_2d(M23_personas.angel.persona_id, M23_dialog_lines.angel_phone_call)
while(audio_is_playing(audio_id) == true) do
thread_yield()
end
end
-- Cleanup for the "Get to Saints HQ" objective.
--
function m23_cleanup_get_to_saints_hq()
trigger_enable(M23_triggers.saints_hq_entry, false)
on_trigger("", M23_triggers.saints_hq_entry)
-- Register a callback for when a player enters a vehicle
on_vehicle_enter("", LOCAL_PLAYER)
if (coop_is_active() == true) then
on_vehicle_enter("", REMOTE_PLAYER)
end
end
-- Callback for when a player enters the Saints HQ
--
function m23_saints_hq_entry_cb()
M23_flags.player_entered_saints_hq = true
end
-- Wait until at least one player aquires a helicopter
function m23_aquire_helicopter()
-- Set the objective text
objective_text(0, M23_objectives.get_helicopter.tag, nil, nil, SYNC_ALL, M23_objectives.get_helicopter.icon)
-- If none of the player's have a heli yet, then highlight the courtesy helicopters
if (M23_flags.showed_courtesy_helis == false) then
if (M23_flags.player_got_heli[1] == false and M23_flags.player_got_heli[2] == false) then
for i, heli in pairs(M23_groups.courtesy_helis.vehicles) do
marker_add(heli, MINIMAP_ICON_LOCATION, OI_ASSET_LOCATION, OI_FLAGS_LOCATION, SYNC_ALL)
on_vehicle_destroyed("m23_courtesy_heli_destroyed_cb", heli)
end
M23_flags.showed_courtesy_helis = true
end
end
-- Register a callback for when a player enters a vehicle
on_vehicle_enter("m23_player_entered_vehicle_cb", LOCAL_PLAYER)
if (coop_is_active() == true) then
on_vehicle_enter("m23_player_entered_vehicle_cb", REMOTE_PLAYER)
end
-- Wait until at least one player has aquired a helicopter
local player_has_heli = false
while(M23_flags.player_got_heli[1] == false and M23_flags.player_got_heli[2] == false) do
thread_yield()
end
m23_cleanup_aquire_heli()
end
-- Cleanup for the aquire heli objective
function m23_cleanup_aquire_heli()
for i, heli in pairs(M23_groups.courtesy_helis.vehicles) do
marker_remove(heli)
on_vehicle_destroyed("", heli)
end
marker_remove_trigger(M23_triggers.saints_hq_elev_up)
-- Register a callback for when a player enters a vehicle
on_vehicle_enter("", LOCAL_PLAYER)
if (coop_is_active() == true) then
on_vehicle_enter("", REMOTE_PLAYER)
end
end
-- Callback when a player enters any vehicle
--
-- player: Player that entered a vehicle
-- vehicle: Vehicle that the player entered
-- seat: Seat index
function m23_track_player_enter_vehicle_cb(player, vehicle, seat)
if player == LOCAL_PLAYER then
if seat == 0 then
M23_runtime.local_player_last_vehicle = vehicle
else
-- Player is riding as a passenger, don't track this vehicle.
M23_runtime.local_player_last_vehicle = nil
end
elseif player == REMOTE_PLAYER then
if seat == 0 then
M23_runtime.remote_player_last_vehicle = vehicle
else
-- Player is riding as a passenger, don't track this vehicle.
M23_runtime.remote_player_last_vehicle = nil
end
end
if M23_flags.play_kinzie_call_on_next_vehicle_enter then
M23_flags.play_kinzie_call_on_next_vehicle_enter = false
m23_play_cell_convo(M23_conversations.awesome_daedalus.name, false)
end
end
-- player: Player that entered a vehicle
-- vehicle: Vehicle that the player entered
-- seat: Seat index
function m23_player_entered_vehicle_cb(player, vehicle, seat)
local vehicle_type = get_char_vehicle_type(player)
if (vehicle_type == VT_AIRPLANE or vehicle_type == VT_HELICOPTER or vehicle_type == VT_VTOL) then
-- Update which player's are in a heli
if (player == LOCAL_PLAYER) then
M23_flags.player_got_heli[1] = true
else
M23_flags.player_got_heli[2] = true
end
if (coop_is_active() == true) then
if (M23_flags.player_got_heli[1] == false or M23_flags.player_got_heli[2] == false) then
if (vehicle == M23_groups.courtesy_helis.vehicles[1] or vehicle == M23_groups.courtesy_helis.vehicles[2]) then
M23_threads.wait_for_heli_takeoff = thread_new("m23_wait_for_heli_takeoff_thread", vehicle)
end
else
marker_remove(M23_groups.courtesy_helis.vehicles[1], SYNC_ALL)
marker_remove(M23_groups.courtesy_helis.vehicles[2], SYNC_ALL)
end
end
-- Check if this was one of the courtesy helicopters
for i, heli in pairs(M23_groups.courtesy_helis.vehicles) do
if (vehicle == heli) then
turn_vulnerable(heli)
vehicle_infinite_mass(heli, false)
on_vehicle_destroyed("", heli)
end
end
end
end
-- Function for when a heli is destroyed before it's made vulnerable
function m23_courtesy_heli_destroyed_cb(heli)
marker_remove(heli, SYNC_ALL)
on_vehicle_destroyed("", heli)
end
--[[
OBJECTIVE: Get to the Daedalus
]]--
-- Fly to the Daedalus's airspace
function m23_get_to_daedalus()
objective_text(0, M23_objectives.go_to_daedalus.tag, nil, nil, SYNC_ALL, M23_objectives.go_to_daedalus.icon)
-- Make sure the waypoint to the Saint's HQ has been removed
mission_waypoint_remove()
--[[
-- Play the newsbreak for the result of M22.
radio_newsbreak("M22_Kill_Killbane")
--]]
-- Remove the courtesy helicopter markers for any player in a heli
if (M23_flags.player_got_heli[1] == true) then
marker_remove(M23_groups.courtesy_helis.vehicles[1], SYNC_LOCAL)
marker_remove(M23_groups.courtesy_helis.vehicles[2], SYNC_LOCAL)
end
if (M23_flags.player_got_heli[2] == true) then
marker_remove(M23_groups.courtesy_helis.vehicles[1], SYNC_REMOTE)
marker_remove(M23_groups.courtesy_helis.vehicles[2], SYNC_REMOTE)
end
-- Make the courtesy helis vulnerable, normally massive again.
for i, heli in pairs(M23_groups.courtesy_helis.vehicles) do
turn_vulnerable(heli)
vehicle_infinite_mass(heli, false)
on_vehicle_destroyed("", heli)
end
marker_add(M23_navpoints.daedalus_landing_pos, MINIMAP_ICON_LOCATION, OI_ASSET_LOCATION, OI_FLAGS_LOCATION, SYNC_ALL)
--[[
-- Set up the trigger to play the "whoa, the Daedalus is neato!" conversation.
trigger_enable(M23_triggers.awesome_daedalus, true)
on_trigger("m23_awesome_daedalus_convo_cb", M23_triggers.awesome_daedalus)
--]]
-- Activate the trigger for the Daedalus's airspace (to update the mission objective)
trigger_enable(M23_triggers.daedalus_airspace, true)
on_trigger("m23_daedalus_airspace_entered_cb", M23_triggers.daedalus_airspace)
M23_flags.daedalus_airspace_entered = false
-- Wait until the player enter's the Daedalus's airspace
while(M23_flags.daedalus_airspace_entered == false) do
thread_yield()
end
m23_cleanup_get_to_daedalus()
end
-- Cleanup function for the objective to get to the Daedalus
function m23_cleanup_get_to_daedalus()
-- Remove landing marker
marker_remove(M23_navpoints.daedalus_landing_pos, SYNC_ALL)
on_trigger("", M23_triggers.daedalus_airspace)
on_trigger_exit("", M23_triggers.daedalus_airspace)
trigger_enable(M23_triggers.daedalus_airspace, false)
end
-- Callback for when a player enters the trigger to play the "awesome Daedalus" convo.
--
function m23_awesome_daedalus_convo_cb(player, trigger)
m23_play_cell_convo(M23_conversations.awesome_daedalus.name, false)
on_trigger("", trigger)
trigger_enable(trigger, false)
end
-- Callback for when a player enters the Daedalus's airspace
--
-- player: Player that triggered the trigger region
-- trigger: Trigger region that got triggered
function m23_daedalus_airspace_entered_cb(player, trigger)
M23_flags.daedalus_airspace_entered = true
end
--[[
OBJECTIVE: Approach landing area
--]]
--[[
-- Update the obejctive for the player to land on the Daedalus, until they get within the defensive range of the Daedalus
function m23_approach_daedalus_landing_area()
-- Update the obective text
objective_text(0, M23_objectives.land_on_daedalus.tag, nil, nil, SYNC_ALL, M23_objectives.land_on_daedalus.icon)
-- notoriety_set_desired_vehicle_count("police", 0)
notoriety_force_no_spawn("STAG", true)
-- Kick off the threads to process the dog-fights going on around the Daedalus
M23_groups.dog_fight_01.thread_id = thread_new("m23_process_dog_fight", M23_groups.dog_fight_01)
M23_groups.dog_fight_02.thread_id = thread_new("m23_process_dog_fight", M23_groups.dog_fight_02)
-- Give the attack VTOLs orders to attack the players
for i, vtol in pairs(M23_groups.attack_vtols_01.vtols) do
vtol.thread_id = thread_new("m23_process_attack_vtol_take_off", vtol.vehicle, vtol.chars[1], vtol.lift_off_pos, vtol.delay)
end
-- Enable the trigger for the Daedalus's defenses
trigger_enable(M23_triggers.daedalus_defensive_area, true)
on_trigger("m23_daedalus_defensive_area_enetered_cb", M23_triggers.daedalus_defensive_area)
M23_flags.daedalus_defensive_area_entered = false
-- Wait until the player gets within the Daedalus's defensive range
while(M23_flags.daedalus_defensive_area_entered == false) do
thread_yield()
end
m23_cleanup_approach_daedalus_landing_area()
end
-- Cleanup for the land on the Daedalus obejctive
function m23_cleanup_approach_daedalus_landing_area()
trigger_enable(M23_triggers.daedalus_defensive_area, false)
on_trigger("", M23_triggers.daedalus_defensive_area)
end
-- Callback for when a player enters the Daedalus's defensive area
--
-- player: Player that triggered the trigger region
-- trigger: Trigger region that got triggered
function m23_daedalus_defensive_area_enetered_cb()
M23_flags.daedalus_defensive_area_entered = true
end
--]]
--[[
OBJECTIVE: Take out the Daedalus' guns
--]]
-- Process the guns on the Daedalus the player is instructed to destroy
function m23_daedalus_destroy_guns()
m23_set_notoriety_aircraft_enabled(false)
-- Play some dialog
audio_play_persona_line(LOCAL_PLAYER, M23_dialog_lines.take_out_turrets)
-- Get all the guns we need to destroy
M23_runtime.daedalus_guns_alive = 0
for i, gun in pairs(M23_movers.daedalus_guns) do
if (mesh_mover_destroyed(gun.mover_name) == false) then
-- Register a callback for when this gun is destroyed.
on_mover_destroyed("m23_callback_daedalus_gun_destroyed_cb", gun.mover_name)
-- Start up a thread to fire the cannon periodically.
gun.firing_thread = thread_new("m23_daedalus_cannon_firing_thread", gun.effect_nav, 1.0, 3.0)
-- Add a marker for this gun.
marker_add(gun.mover_name, MINIMAP_ICON_KILL, OI_ASSET_KILL_FULL, OI_FLAGS_FULL)
-- Update our gun count.
M23_runtime.daedalus_guns_alive = M23_runtime.daedalus_guns_alive + 1
end
end
-- Kick off the threads to process the dog-fights going on around the Daedalus
M23_groups.dog_fight_01.thread_id = thread_new("m23_process_dog_fight", M23_groups.dog_fight_01)
M23_groups.dog_fight_02.thread_id = thread_new("m23_process_dog_fight", M23_groups.dog_fight_02)
--[[
-- Give the attack VTOLs orders to attack the players
for i, vtol in pairs(M23_groups.attack_vtols_01.vtols) do
vtol.thread_id = thread_new("m23_process_attack_vtol_take_off", vtol.vehicle, vtol.chars[1], vtol.lift_off_pos, vtol.delay)
end
--]]
group_create(M23_groups.bomb_spawn_01)
group_create(M23_groups.bomb_spawn_02)
group_create(M23_groups.bomb_spawn_03)
group_create(M23_groups.amb_spawn_01)
-- If there is at least one gun alive, then display the destroy defenses objective.
if (M23_runtime.daedalus_guns_alive > 0) then
objective_text(0, M23_objectives.destroy_guns.tag, nil, nil, SYNC_ALL, M23_objectives.destroy_guns.icon)
end
-- Progress music.
m23_post_music_event(M23_music.mission_start_end)
delay(9.0)
-- Progress music.
m23_post_music_event(M23_music.daedalus_battle)
-- Wait until all the guns have been destroyed.
while (M23_runtime.daedalus_guns_alive > 0) do
thread_yield()
end
m23_end_ambient_explosion_audio()
-- Play some dialog
audio_play_persona_line(LOCAL_PLAYER, M23_dialog_lines.turrets_down)
m23_cleanup_daedalus_destroy_guns()
delay(1.0)
m23_play_cell_convo(M23_conversations.not_making_a_dent.name, false)
end
-- Cleanup for the objective to destroy the Daedalus guns.
function m23_cleanup_daedalus_destroy_guns()
for i, gun in pairs(M23_movers.daedalus_guns) do
on_mover_destroyed("", gun.mover_name)
marker_remove(gun.mover_name)
thread_kill(gun.firing_thread)
gun.firing_thread = INVALID_THREAD_HANDLE
end
end
-- Callback for when a gun on the Daedalus gets destroyed.
function m23_callback_daedalus_gun_destroyed_cb(gun_mover_name)
-- Find the gun in question.
local found_gun = nil
for i, cur_gun in pairs(M23_movers.daedalus_guns) do
if cur_gun.mover_name == gun_mover_name then
found_gun = cur_gun
break
end
end
on_mover_destroyed("", found_gun.mover_name)
marker_remove(found_gun.mover_name)
explosion_create("M23_daedalus_cannon_exp", found_gun.death_vfx)
-- Stop the thread that's firing the cannon.
thread_kill(found_gun.firing_thread)
found_gun.firing_thread = INVALID_THREAD_HANDLE
M23_runtime.daedalus_guns_alive = M23_runtime.daedalus_guns_alive - 1
end
--[[
OBJECTIVE: Take out the surface defenses
--]]
-- Process the tanks on the Daedalus the player is instructed to destroy
function m23_daedalus_destroy_tanks()
-- Start the continious spawn on the landing deck
m23_setup_landing_deck_cont_spawn()
-- Play some dialog
audio_play_persona_line(LOCAL_PLAYER, M23_dialog_lines.clear_the_deck)
-- Check if any tanks are alive
M23_runtime.daedalus_surface_tanks_alive = 0
for i, tank in pairs(M23_groups.daedalus_tanks_01.tanks) do
if (vehicle_is_destroyed(tank.vehicle) == false) then
-- Make the tank fire at the closest player
--force_fire_object_position(tank.chars[1], CLOSEST_PLAYER, true)
-- Add a marker and register a callback so we know when it's destroyed
on_vehicle_destroyed("m23_callback_daedalus_tank_destroyed_cb", tank.vehicle)
marker_add(tank.vehicle, MINIMAP_ICON_KILL, OI_ASSET_KILL_FULL)
-- Update our count of tanks that are alive
M23_runtime.daedalus_surface_tanks_alive = M23_runtime.daedalus_surface_tanks_alive + 1
end
end
-- If there is at least one tank alive, then display the destroy defenses objective
if (M23_runtime.daedalus_surface_tanks_alive > 0) then
objective_text(0, M23_objectives.destroy_defenses.tag, nil, nil, SYNC_ALL, M23_objectives.destroy_defenses.icon)
end
-- Wait until all the tanks have been destroyed
while (M23_runtime.daedalus_surface_tanks_alive > 0) do
thread_yield()
end
m23_cleanup_daedalus_destroy_tanks()
end
-- Cleanup for the objective to destroy the Daedalus tanks
function m23_cleanup_daedalus_destroy_tanks()
for i, tank in pairs(M23_groups.daedalus_tanks_01.tanks) do
on_vehicle_destroyed("", tank.vehicle)
marker_remove(tank.vehicle)
end
end
-- Callback for when a tank on the Daedalus gets destroyed
function m23_callback_daedalus_tank_destroyed_cb(vehicle)
on_vehicle_destroyed("", vehicle)
marker_remove(vehicle)
M23_runtime.daedalus_surface_tanks_alive = M23_runtime.daedalus_surface_tanks_alive - 1
end
--[[
OBJECT: Land on the Daedalus
--]]
-- Process the player landing on the Daedalus
function m23_land_on_the_daedalus()
-- Start the continious spawn on the landing deck
m23_setup_landing_deck_cont_spawn()
-- Enable the trigger for the Daedalus's landing zone, and add a marker at the landing area
trigger_enable(M23_triggers.daedalus_landing_zone, true)
on_trigger("m23_daedalus_landing_zone_entered_cb", M23_triggers.daedalus_landing_zone)
M23_flags.daedalus_landing_complete = false
-- The player can exit a vehicle inside the trigger without "entering" the trigger.
on_vehicle_exit("m23_daedalus_landing_player_exited_vehicle", LOCAL_PLAYER)
if coop_is_active() == true then
on_vehicle_exit("m23_daedalus_landing_player_exited_vehicle", REMOTE_PLAYER)
end
-- Show the "land on the daedalus" objective
objective_text(0, M23_objectives.land_on_daedalus.tag, nil, nil, SYNC_ALL, M23_objectives.land_on_daedalus.icon)
marker_add(M23_navpoints.daedalus_landing_pos, MINIMAP_ICON_LOCATION, OI_ASSET_LOCATION, OI_FLAGS_LOCATION, SYNC_ALL)
-- Wait until they have landed
while(M23_flags.daedalus_landing_complete == false) do
thread_yield()
end
m23_cleanup_land_on_the_daedalus()
end
-- Cleanup function for landing on the Daedalus
function m23_cleanup_land_on_the_daedalus()
trigger_enable(M23_triggers.daedalus_landing_zone, false)
on_trigger("", M23_triggers.daedalus_landing_zone)
on_vehicle_exit("", LOCAL_PLAYER)
if coop_is_active() == true then
on_vehicle_exit("", REMOTE_PLAYER)
end
marker_remove(M23_navpoints.daedalus_landing_pos)
continuous_explosion_stop()
end
-- The player can exit a vehicle inside the trigger without "entering" the trigger,
-- so do a manual check.
--
-- character: (string) the name of the character that exited a vehicle.
--
function m23_daedalus_landing_player_exited_vehicle(character)
if object_is_in_trigger(M23_triggers.daedalus_landing_zone, character) then
M23_flags.daedalus_landing_complete = true
end
end
-- Callback for when a player enters the trigger on the Daedalus's landing zone
--
-- player: Player that triggered the trigger region
-- trigger: Trigger region that got triggered
function m23_daedalus_landing_zone_entered_cb(player, trigger)
M23_flags.daedalus_landing_complete = true
end
-- Thread function to land Saints reinforcements on the landing deck area.
function m23_do_landing_deck_saints_reinforcements_thread(group)
-- Put the guys we spawned in their helicopters.
vehicle_enter_group_teleport(group.chars, group.vehicle)
for i, dude in pairs(group.chars) do
turn_invulnerable(dude)
end
turn_invulnerable(group.vehicle)
-- Path to the landing zone
-- direct call to helicopter_fly_to_do because we don't want to wait for completion
helicopter_fly_to_do(group.vehicle, -1, true, "", false, group.path, 1.0)
while (get_char_vehicle_is_in_air(group.chars[1]) == true) do
thread_yield()
end
helicopter_fly_to_stop(group.vehicle)
vehicle_stop(group.vehicle, true)
-- Have the dudes get out and make the helicopter unenterable.
for i, dude in pairs(group.chars) do
vehicle_exit(dude, true)
end
delay(10.0)
for i, dude in pairs(group.chars) do
turn_vulnerable(dude)
end
turn_vulnerable(group.vehicle)
end
--[[
OBJECTIVE: Plant bombs on the Daedalus.
--]]
-- The player has to plant bombs at various places around the Daedalus.
--
function m23_plant_bombs()
-- Register callbacks to track the players' vehicles so we can blow them up before
-- the Cyrus boss fight.
on_vehicle_enter("m23_track_player_enter_vehicle_cb", LOCAL_PLAYER)
if (coop_is_active() == true) then
on_vehicle_enter("m23_track_player_enter_vehicle_cb", REMOTE_PLAYER)
end
-- Set bombs and fly to places.
m23_set_bomb_or_fly_to(M23_bomb_triggers.landing_deck_bomb_1)
audio_play_persona_line(LOCAL_PLAYER, M23_dialog_lines.first_bomb)
m23_set_bomb_or_fly_to(M23_bomb_triggers.landing_deck_bomb_2)
audio_play_persona_line(LOCAL_PLAYER, M23_dialog_lines.second_bomb)
M23_flags.play_kinzie_call_on_next_vehicle_enter = true
m23_set_bomb_or_fly_to(M23_bomb_triggers.fly_to_side)
m23_set_bomb_or_fly_to(M23_bomb_triggers.side_deck_bomb)
audio_play_persona_line(LOCAL_PLAYER, M23_dialog_lines.third_bomb)
--m23_set_bomb_or_fly_to(M23_bomb_triggers.fly_to_rear)
m23_set_bomb_or_fly_to(M23_bomb_triggers.rear_deck_bomb)
-- Progress music.
m23_post_music_event(M23_music.daedalus_battle_end)
audio_play_persona_line(LOCAL_PLAYER, M23_dialog_lines.fourth_bomb)
objective_text_clear(0)
m23_cleanup_plant_bombs()
end
-- Enables a trigger and sets a marker for either a bomb or a "fly to" location
-- and waits for the player to finish doing that.
--
function m23_set_bomb_or_fly_to(trigger)
M23_runtime.cur_bomb_planted = false
trigger_enable(trigger.trigger_name)
on_trigger("m23_bomb_planted_or_flown_to_cb", trigger.trigger_name)
marker_add_trigger(trigger.trigger_name, trigger.minimap_icon, trigger.ingame_effect, trigger.oi_asset, trigger.oi_flags, SYNC_ALL)
objective_text(0, trigger.objective.tag, nil, nil, SYNC_ALL, trigger.objective.icon)
-- Wait for the player to plant the bomb or fly to the destination.
while M23_runtime.cur_bomb_planted == false do
thread_yield()
end
end
-- Cleans up a bomb or "fly to" trigger.
--
function m23_cleanup_bomb_trigger(trigger)
trigger_enable(trigger.trigger_name, false)
on_trigger("", trigger.trigger_name)
marker_remove_trigger(trigger.trigger_name)
end
-- Called when the player plants a bomb or makes it to a "fly to" trigger.
--
function m23_bomb_planted_or_flown_to_cb(character, trigger)
-- Find the trigger we just hit.
local found_trigger = nil
for i, cur_trigger in pairs(M23_bomb_triggers) do
if cur_trigger.trigger_name == trigger then
found_trigger = cur_trigger
break
end
end
-- Clean up the trigger before we do anything else.
m23_cleanup_bomb_trigger(found_trigger)
if found_trigger.play_bomb_anim == true then
-- Set up the character for the animation.
character_prevent_explosion_fling(character, true)
character_allow_ragdoll(character, false)
character_prevent_flinching(character, true)
--player_controls_disable(character)
-- Play the intro animation and align the player to the orientation nav point.
action_play(character, "plant explosive enter", "plant explosive enter", false, 0.8, false, false, found_trigger.anim_nav)
-- Play the middle section of the animation.
set_animation_state(character, "plant explosive idle")
if character_is_dead(character) == false then
delay(3.0)
end
clear_animation_state(character)
-- Play the outro animation.
action_play(character, "plant explosive exit")
-- Clean up any settings we applied for the animation.
player_controls_enable(character)
character_prevent_flinching(character, false)
character_allow_ragdoll(character, true)
character_prevent_explosion_fling(character, false)
end
M23_runtime.cur_bomb_planted = true
end
-- Clean up anything for the plant bombs objective.
--
function m23_cleanup_plant_bombs()
-- Clean up bomb triggers.
M23_runtime.cur_bomb_planted = false
for i, trigger in pairs(M23_bomb_triggers) do
m23_cleanup_bomb_trigger(trigger)
end
-- Clean up vehicle callbacks.
on_vehicle_enter("", LOCAL_PLAYER)
if (coop_is_active() == true) then
on_vehicle_enter("", REMOTE_PLAYER)
end
end
--[[
OBJECTIVE: Kill Cyrus (boss battle)
--]]
-- Cyrus flies over the rear deck and destroys the players' helicopter(s)
--
function m23_cyrus_fly_by()
-- Prevent the players from getting back in their helicopters.
if M23_runtime.local_player_last_vehicle ~= "" and M23_runtime.local_player_last_vehicle ~= nil and M23_runtime.local_player_last_vehicle ~= LOCAL_PLAYER then
set_unjackable_flag(M23_runtime.local_player_last_vehicle)
end
if M23_runtime.remote_player_last_vehicle ~= "" and M23_runtime.remote_player_last_vehicle ~= nil and M23_runtime.remote_player_last_vehicle ~= REMOTE_PLAYER then
set_unjackable_flag(M23_runtime.remote_player_last_vehicle)
end
-- Create Cyrus and his vtol, and put Cyrus in the driver's seat
group_create_hidden(M23_groups.cyrus_vtol.name, true)
vehicle_enter_teleport(M23_groups.cyrus_vtol.cyrus, M23_groups.cyrus_vtol.vtol)
vehicle_max_speed(M23_groups.cyrus_vtol.vtol, 150.0)
turn_invulnerable(M23_groups.cyrus_vtol.vtol)
turn_invulnerable(M23_groups.cyrus_vtol.cyrus) -- Cyrus himself is invulnerable until his VTOL hits 25% health.
vehicle_prevent_explosion_fling(M23_groups.cyrus_vtol.vtol, true)
vehicle_infinite_mass(M23_groups.cyrus_vtol.vtol, true) -- Don't allow ramming Cyrus with another heli to knock him off his path.
helicopter_set_dont_death_spiral(M23_groups.cyrus_vtol.vtol, true) -- Don't allow ramming Cyrus with another heli to death spiral him.
set_dont_attack_me_on_sight_flag(M23_groups.cyrus_vtol.cyrus, true) -- We don't want the dogfighting Saints to attack Cyrus.
set_unjackable_flag(M23_groups.cyrus_vtol.vtol, true) -- Don't allow the player to RC Cyrus.
-- Register the players as targets.
ai_add_enemy_target(M23_groups.cyrus_vtol.cyrus, LOCAL_PLAYER, ATTACK_ON_SIGHT, true)
if (coop_is_active() == true) then
ai_add_enemy_target(M23_groups.cyrus_vtol.cyrus, REMOTE_PLAYER, ATTACK_ON_SIGHT, true)
end
set_perfect_aim(M23_groups.cyrus_vtol.cyrus, true)
--set_ignore_ai_flag(M23_groups.cyrus_vtol.cyrus, true)
-- Start a thread to blow up the players' helicopter(s)
M23_threads.cyrus_destroy_player_helis = thread_new("m23_cyrus_destroy_player_helis_thread")
-- Play some dialog
audio_play_persona_line(LOCAL_PLAYER, M23_dialog_lines.find_cyrus)
-- Update the objective text with the new objective
-- objective_text(0, M23_objectives.kill_cyrus.tag, nil, nil, SYNC_ALL, M23_objectives.kill_cyrus.icon)
-- marker_add(M23_groups.cyrus_vtol.vtol, MINIMAP_ICON_KILL, OI_ASSET_KILL_FULL, OI_FLAGS_FULL)
-- Fly the intro flyover path.
group_show(M23_groups.cyrus_vtol)
helicopter_fly_to_direct_dont_stop(M23_groups.cyrus_vtol.vtol, 30, M23_cyrus_boss_ai.start_path_1)
-- Update the objective text with the new objective
M23_conversations.cyrus_start.handle = audio_conversation_load(M23_conversations.cyrus_start.name)
audio_conversation_play(M23_conversations.cyrus_start.handle)
objective_text(0, M23_objectives.kill_cyrus.tag, nil, nil, SYNC_ALL, M23_objectives.kill_cyrus.icon)
marker_add(M23_groups.cyrus_vtol.vtol, MINIMAP_ICON_KILL, OI_ASSET_KILL_FULL, OI_FLAGS_FULL)
-- Fly the outro flyover path.
helicopter_fly_to_direct_dont_stop(M23_groups.cyrus_vtol.vtol, 75, M23_cyrus_boss_ai.start_path_2)
set_perfect_aim(M23_groups.cyrus_vtol.cyrus, false)
--set_ignore_ai_flag(M23_groups.cyrus_vtol.cyrus, false)
m23_cleanup_cyrus_fly_by()
end
-- Thread function to process Cyrus firing at and destroying the players' helicopters.
--
function m23_cyrus_destroy_player_helis_thread()
-- Blow up the players' helicopter(s).
-- Note: the player's vehicle name is set to LOCAL_PLAYER if it is a non-scripted vehicle I guess.
delay(3.0)
-- Destroy the nearest player helicopter first.
local first_heli = nil
local second_heli = nil
local local_dist = nil
local remote_dist = nil
if M23_runtime.local_player_last_vehicle ~= "" and M23_runtime.local_player_last_vehicle ~= nil and M23_runtime.local_player_last_vehicle ~= LOCAL_PLAYER then
if object_is_in_trigger(M23_triggers.rear_deck, M23_runtime.local_player_last_vehicle ) then
local_dist = get_dist(M23_groups.cyrus_vtol.vtol, M23_runtime.local_player_last_vehicle)
end
end
if M23_runtime.remote_player_last_vehicle ~= "" and M23_runtime.remote_player_last_vehicle ~= nil and M23_runtime.remote_player_last_vehicle ~= REMOTE_PLAYER then
if object_is_in_trigger(M23_triggers.rear_deck, M23_runtime.remote_player_last_vehicle ) then
remote_dist = get_dist(M23_groups.cyrus_vtol.vtol, M23_runtime.remote_player_last_vehicle)
end
end
if local_dist ~= nil and remote_dist ~= nil then
if local_dist < remote_dist then
first_heli = M23_runtime.local_player_last_vehicle
second_heli = M23_runtime.remote_player_last_vehicle
else
first_heli = M23_runtime.remote_player_last_vehicle
second_heli = M23_runtime.local_player_last_vehicle
end
else
if local_dist ~= nil then
first_heli = M23_runtime.local_player_last_vehicle
elseif remote_dist ~= nil then
first_heli = M23_runtime.remote_player_last_vehicle
end
end
-- Attack and destroy the first heli.
if first_heli ~= nil then
M23_threads.async_delay_thread = thread_new("delay", 6.0)
-- Fire at the heli for a while or until it blows up.
while thread_check_done(M23_threads.async_delay_thread) == false do
helicopter_shoot_vehicle(M23_groups.cyrus_vtol.vtol, first_heli, true, 1.0, false)
thread_yield()
if vehicle_is_destroyed(first_heli) == true then
thread_kill(M23_threads.async_delay_thread)
break
end
end
-- Force it to blow up.
if vehicle_is_destroyed(first_heli) == false then
vehicle_detonate(first_heli)
end
end
-- Attack and destroy the second heli.
if second_heli ~= nil then
M23_threads.async_delay_thread = thread_new("delay", 6.0)
-- Fire at the heli for a while or until it blows up.
while thread_check_done(M23_threads.async_delay_thread) == false do
helicopter_shoot_vehicle(M23_groups.cyrus_vtol.vtol, second_heli, true, 1.0, false)
thread_yield()
end
-- Force it to blow up.
if vehicle_is_destroyed(second_heli) == false then
vehicle_detonate(second_heli)
end
end
end
-- Clean up anything for the fly by function.
--
function m23_cleanup_cyrus_fly_by()
thread_kill(M23_threads.cyrus_destroy_player_helis)
M23_threads.cyrus_destroy_player_helis = -1
thread_kill(M23_threads.async_delay_thread)
M23_threads.async_delay_thread = -1
end
-- Process the Cyrus boss battle
function m23_kill_cyrus()
-- Update the objective text with the new objective
-- objective_text(0, M23_objectives.kill_cyrus.tag, nil, nil, SYNC_ALL, M23_objectives.kill_cyrus.icon)
-- Start up the dog fight threads again if they've stopped.
if (M23_groups.dog_fight_01.thread_id == -1 or thread_check_done(M23_groups.dog_fight_01.thread_id) == true) then
M23_groups.dog_fight_01.thread_id = thread_new("m23_process_dog_fight", M23_groups.dog_fight_01)
end
if (M23_groups.dog_fight_02.thread_id == -1 or thread_check_done(M23_groups.dog_fight_02.thread_id) == true) then
M23_groups.dog_fight_02.thread_id = thread_new("m23_process_dog_fight", M23_groups.dog_fight_02)
end
-- Setup Cyrus's VTOL
M23_flags.cyrus_vtol_destroyed = false
on_vehicle_destroyed("m23_cyrus_vtol_destroyed_cb", M23_groups.cyrus_vtol.vtol)
turn_vulnerable(M23_groups.cyrus_vtol.vtol)
turn_invulnerable(M23_groups.cyrus_vtol.cyrus) -- Cyrus himself is invulnerable until his VTOL hits 25% health.
trigger_enable(M23_triggers.cyrus_combat, true)
on_trigger_exit("m23_failure_leave_cb", M23_triggers.cyrus_combat)
-- marker_add(M23_groups.cyrus_vtol.vtol, MINIMAP_ICON_KILL, OI_ASSET_KILL_FULL, OI_FLAGS_FULL)
-- Enable spawn regions on the deck for reinforcements to arrive from.
continuous_spawn_regions_enable(M23_spawn_regions.cyrus_deck, true)
-- Setup callbacks for when Cyrus' health gets to certain levels.
on_damage("m23_cyrus_health_75", M23_groups.cyrus_vtol.vtol, 0.75)
-- Prevent Cyrus from falling below 75% health.
local new_min = get_max_hit_points(M23_groups.cyrus_vtol.vtol) * 0.75
set_min_hit_points(M23_groups.cyrus_vtol.vtol, new_min)
-- Start the continuous spawn
-- m23_setup_bridge_stairs_cont_spawn()
-- Create a new thread to process Cyrus's boss AI
M23_threads.cyrus_boss_ai = thread_new("m23_process_cyrus_boss_ai_thread")
while audio_conversation_playing(M23_conversations.cyrus_start.handle) do
thread_yield()
end
-- Make the miniguns obvious
if coop_is_active() then
for i, gun in pairs(M23_groups.ammo_stash_02.miniguns) do
if ((not inv_has_item("brute_minigun", LOCAL_PLAYER)) or (not inv_has_item("brute_minigun", REMOTE_PLAYER))) then
marker_add(gun, MINIMAP_ICON_USE, OI_ASSET_USE, OI_FLAGS_DEFAULT, SYNC_ALL)
on_pickup("m23_minigun_cb", gun)
end
end
else
for i, gun in pairs(M23_groups.ammo_stash_02.miniguns) do
if (not inv_has_item("brute_minigun")) then
marker_add(gun, MINIMAP_ICON_USE, OI_ASSET_USE, OI_FLAGS_DEFAULT, SYNC_ALL)
on_pickup("m23_minigun_cb", gun)
end
end
end
audio_play_persona_line(LOCAL_PLAYER, M23_dialog_lines.callout_weapons)
-- Prep the Cyrus death cutscene.
zscene_prep(M23_cutscenes.cyrus_death)
-- Wait until Cyrus's VTOL is destroyed or the self desctruct timer runs out.
while(M23_flags.cyrus_vtol_destroyed == false and M23_flags.escape_timer_expired == false) do
thread_yield()
end
if M23_flags.escape_timer_expired == true then
-- The self destruct timer expired. Things go boom.
m23_do_boss_battle_self_destruct()
end
m23_cleanup_kill_cyrus()
end
-- The player picked up a minigun
function m23_minigun_cb()
for i, gun in pairs(M23_groups.ammo_stash_02.miniguns) do
marker_remove(gun, SYNC_ALL)
on_pickup("", gun)
end
end
-- Cleanup the Cyrus boss battle
function m23_cleanup_kill_cyrus()
on_vehicle_destroyed("", M23_groups.cyrus_vtol.vtol)
on_damage("", M23_groups.cyrus_vtol.vtol, 1.0)
marker_remove(M23_groups.cyrus_vtol.vtol, SYNC_ALL)
thread_kill(M23_threads.cyrus_boss_ai)
on_trigger_exit("", M23_triggers.cyrus_combat)
trigger_enable(M23_triggers.cyrus_combat, false)
-- End camera shake
camera_shake_stop()
-- Disable the spawn regions on the deck.
continuous_spawn_regions_enable(M23_spawn_regions.cyrus_deck, false)
continuous_spawn_stop(M23_spawn_groups.offensive_01)
continuous_spawn_stop(M23_spawn_groups.offensive_02)
-- Cleanup miniguns
for i, gun in pairs(M23_groups.ammo_stash_02.miniguns) do
marker_remove(gun, SYNC_ALL)
on_pickup("", gun)
end
end
-- Callback for when Cyrus's VTOL has been destroyed
function m23_cyrus_vtol_destroyed_cb()
M23_flags.cyrus_vtol_destroyed = true
end
-- Callback for when Cyrus's VTOL reaches 75% health.
--
-- vehicle: (string) name of the vehicle damaged
-- attacker: (string) name of person causing the damage
function m23_cyrus_health_75(vehicle, attacker)
--message("75 health")
M23_flags.do_cyrus_reinforcements = true
M23_flags.destruct_start = true
M23_tweak_values.taunt_idx_max = 4
-- Keep Cyrus at 75% health until the reinforcement action finishes.
vehicle_set_invulnerable(M23_groups.cyrus_vtol.vtol)
-- Prevent Cyrus from falling below 50% health for the next stage.
local new_min = get_max_hit_points(M23_groups.cyrus_vtol.vtol) * 0.50
set_min_hit_points(M23_groups.cyrus_vtol.vtol, new_min)
on_damage("m23_cyrus_health_50", M23_groups.cyrus_vtol.vtol, 0.50)
-- Start the self destruct countdown timer.
delay(rand_float(1.0, 5.0))
m23_boss_battle_self_destruct_timer_start()
end
-- Callback for when Cyrus's VTOL reaches 50% health.
--
-- vehicle: (string) name of the vehicle damaged
-- attacker: (string) name of person causing the damage
function m23_cyrus_health_50(vehicle, attacker)
--message("50 health")
M23_flags.do_cyrus_reinforcements = true
-- Stumble the players (can't call non-blocking because it will never give control back to the player)
thread_new("m23_stumble", LOCAL_PLAYER)
if (coop_is_active() == true) then
thread_new("m23_stumble", REMOTE_PLAYER)
end
-- Play some audio.
audio_object_post_event("m23_sfx_daedalus_shudders")
-- Keep Cyrus at 50% health until the reinforcement action finishes.
vehicle_set_invulnerable(M23_groups.cyrus_vtol.vtol)
-- Prevent Cyrus from falling below 25% health for the next stage.
local new_min = get_max_hit_points(M23_groups.cyrus_vtol.vtol) * 0.25
set_min_hit_points(M23_groups.cyrus_vtol.vtol, new_min)
on_damage("m23_cyrus_health_25", M23_groups.cyrus_vtol.vtol, 0.25)
end
-- Callback for when Cyrus's VTOL reaches 25% health.
--
-- vehicle: (string) name of the vehicle damaged
-- attacker: (string) name of person causing the damage
function m23_cyrus_health_25(vehicle, attacker)
--message("25% health")
M23_flags.do_cyrus_reinforcements = true
M23_flags.cyrus_final_phase = true
-- Keep Cyrus at 25% health until the reinforcement action finishes.
vehicle_set_invulnerable(M23_groups.cyrus_vtol.vtol)
M23_conversations.times_up.handle = audio_conversation_load(M23_conversations.times_up.name)
audio_conversation_play(M23_conversations.times_up.handle)
-- Prevent Cyrus from falling below 10% health so that he doesn't blow up
-- before we can hide him and play the z-scene of him crashing.
local new_min = get_max_hit_points(M23_groups.cyrus_vtol.vtol) * 0.10
set_min_hit_points(M23_groups.cyrus_vtol.vtol, new_min)
on_damage("m23_cyrus_health_15", M23_groups.cyrus_vtol.vtol, 0.15)
end
-- Callback for when Cyrus's VTOL reaches 15% health. This
-- is when the engine will catch fire, and we should consider Cyrus
-- dead.
--
-- vehicle: (string) name of the vehicle damaged
-- attacker: (string) name of person causing the damage
function m23_cyrus_health_15(vehicle, attacker)
--message("15 health")
-- Keep Cyrus from blowing up so that we don't see debris falling from the sky
-- after the z-scene.
local max_hp = get_max_hit_points(M23_groups.cyrus_vtol.vtol)
local target_hp = max_hp * 0.20 -- above "on fire" threshold
set_current_hit_points(M23_groups.cyrus_vtol.vtol, target_hp)
vehicle_set_smoke_and_fire_state(M23_groups.cyrus_vtol.vtol, false, false)
vehicle_set_invulnerable(M23_groups.cyrus_vtol.vtol)
-- Consider Cyrus dead at this point, since the z-scene shows him crashing into the deck (we don't want him to blow up twice).
M23_flags.cyrus_vtol_destroyed = true
on_damage("", M23_groups.cyrus_vtol.vtol, 1.0)
end
-- Start a timer for the self destruct, do some explosions, and make dudes stumble.
function m23_boss_battle_self_destruct_timer_start()
-- Make something explode.
for i, explosion in pairs(M23_explosions.self_destruct_begin_explosions) do
explosion_create(M23_explosions.effect, explosion)
delay(0.1)
end
for i, vfx in pairs(M23_smoke.daedalus_rear) do
effect_play(vfx, true)
end
M23_daedalus_guns.threads = cleanup_threads(M23_daedalus_guns.threads)
M23_threads.daedalus_guns_thread = cleanup_threads(M23_threads.daedalus_guns_thread)
m23_stop_all_daedalus_gun_effects()
-- Stumble the players (can't call non-blocking because it will never give control back to the player)
thread_new("m23_stumble", LOCAL_PLAYER)
if (coop_is_active() == true) then
thread_new("m23_stumble", REMOTE_PLAYER)
end
-- Play some audio.
audio_object_post_event("m23_sfx_daedalus_shudders")
-- Start a camera shake
camera_shake_play_looping("cargo_plane_interior", M23_tweak_values.fight_looping_cam_shake_amount)
m23_obliterate_landing_deck_vtols()
delay(1.0)
-- Start the timer.
hud_timer_set(1, M23_tweak_values.boss_battle_self_descruct_time_limit, "m23_cb_boss_battle_self_destruct_time_expired")
m23_play_cell_convo(M23_conversations.fireworks_started.name, false)
M23_flags.escape_timer_expired = false
delay(1.0)
audio_object_post_event("M23_SFX_DAEDALUS_SELF_DESTRUCT_ALARM", nil, nil, M23_navpoints.daedalus_self_destruct_audio_pos)
end
-- Plays a stumble on the player.
function m23_stumble(player)
player_release_human_shield(player, true)
if (character_is_in_vehicle(player) == false and character_is_ready(player) and (not character_is_jumping(player))) then
action_play(player, "Plane Stumble Right")
end
end
-- Callback for when the self-destruct timer expires during the Cyrus fight
function m23_cb_boss_battle_self_destruct_time_expired()
M23_flags.escape_timer_expired = true
hud_timer_stop(1)
end
-- Triggers a bunch of explosions and then forcibly kills the players
function m23_do_boss_battle_self_destruct()
-- Start a thread to create random explosions
M23_threads.explosions = thread_new("m23_self_destruct_explosions_thread")
-- Let stuff blow up for a while.
delay(5)
-- Kill the player(s)
m23_insta_kill_character(LOCAL_PLAYER, true, true)
if coop_is_active() then
m23_insta_kill_character(REMOTE_PLAYER, true, true)
end
while (true) do
thread_yield()
end
end
-- Genarate random explosions
function m23_self_destruct_explosions_thread()
while(true) do
-- Delay a random time and explode a random navpoint
delay(rand_float(0.2, 0.35))
explosion_create(M23_explosions.effect, get_random_table_entry(M23_explosions.self_destruct_explosions))
end
end
-- Genarate less random explosions
function m23_vtol_escape_explosions_thread()
while(true) do
-- Delay a random time and explode a random navpoint
delay(rand_float(0.2, 0.35))
explosion_create(M23_explosions.effect, get_random_table_entry(M23_explosions.vtol_escape_explosions))
end
end
--[[
Z-SCENE: Cyrus Dies!
--]]
function m23_play_cyrus_death_scene()
cutscene_play(M23_cutscenes.cyrus_death, nil, M23_checkpoints.escape.player_starts)
-- Make Cyrus go bye-bye
group_destroy(M23_groups.cyrus_vtol.name)
end
function m23_cleanup_cyrus_death_scene()
--[[ NOTHING TO DO ]]--
end
--[[
OBJECTIVE: Escape!!!
--]]
function m23_escape_from_daedalus()
-- Update the objective text with the new objective
objective_text(0, M23_objectives.escape.tag, nil, nil, SYNC_ALL, M23_objectives.escape.icon)
-- Start a camera shake
camera_shake_play_looping("cargo_plane_interior", M23_tweak_values.escape_looping_cam_shake_amount)
mesh_mover_hide(M23_movers.hangar_door)
M23_threads.explosions = thread_new("m23_self_destruct_explosions_thread")
M23_threads.explosions = thread_new("m23_vtol_escape_explosions_thread")
for i, vfx in pairs(M23_smoke.daedalus_rear) do
effect_play(vfx, true)
end
for i, vfx in pairs(M23_smoke.daedalus_fore) do
effect_play(vfx, true)
end
delay(0.5)
-- Stumble the players (can't call non-blocking because it will never give control back to the player)
thread_new("m23_stumble", LOCAL_PLAYER)
if (coop_is_active() == true) then
thread_new("m23_stumble", REMOTE_PLAYER)
end
-- Play some VO
audio_play_persona_line(LOCAL_PLAYER, M23_dialog_lines.escape)
-- Set up callbacks for the players exiting the blast radius trigger.
trigger_enable(M23_triggers.daedalus_blast_radius, true)
on_trigger_exit("m23_cb_player_escaped", M23_triggers.daedalus_blast_radius)
on_trigger("m23_cb_player_unescaped", M23_triggers.daedalus_blast_radius)
-- Set up callbacks for trying to flee the Daedalus without taking a VTOL
trigger_enable(M23_triggers.flee_region, true)
on_trigger_exit("m23_cb_fleeing_check", M23_triggers.flee_region)
M23_flags.local_player_escaped = (object_is_in_trigger(M23_triggers.daedalus_blast_radius, LOCAL_PLAYER) == false)
M23_flags.remote_player_escaped = true
if (coop_is_active()) then
M23_flags.remote_player_escaped = (object_is_in_trigger(M23_triggers.daedalus_blast_radius, REMOTE_PLAYER) == false)
end
-- Setup the escape VTOLS
group_create_hidden(M23_groups.daedalus_escape_vtols.name, true)
vehicle_show(M23_groups.daedalus_escape_vtols.vtols[1])
on_vehicle_enter("m23_entered_escape_vtol", M23_groups.daedalus_escape_vtols.vtols[1])
marker_add(M23_groups.daedalus_escape_vtols.vtols[1], MINIMAP_ICON_LOCATION, OI_ASSET_LOCATION, OI_FLAGS_LOCATION, SYNC_ALL)
vehicle_set_invulnerable(M23_groups.daedalus_escape_vtols.vtols[1])
if coop_is_active() then
vehicle_show(M23_groups.daedalus_escape_vtols.vtols[2])
on_vehicle_enter("m23_entered_escape_vtol", M23_groups.daedalus_escape_vtols.vtols[2])
marker_add(M23_groups.daedalus_escape_vtols.vtols[2], MINIMAP_ICON_LOCATION, OI_ASSET_LOCATION, OI_FLAGS_LOCATION, SYNC_ALL)
vehicle_set_invulnerable(M23_groups.daedalus_escape_vtols.vtols[2])
end
-- Set the HUD timer, and wait for it to expire
hud_timer_set(1, M23_tweak_values.escape_time_limit, "m23_cb_escape_time_expired")
M23_flags.escape_timer_expired = false
while(M23_flags.escape_timer_expired == false and (M23_flags.local_player_escaped == false or M23_flags.remote_player_escaped == false)) do
thread_yield()
end
if M23_flags.local_player_escaped or M23_flags.remote_player_escaped then
-- Progress music if a player escaped.
m23_post_music_event(M23_music.cyrus_fight_end)
end
-- Stop the HUD timer in case the players escaped early.
hud_timer_stop(1)
effect_play(M23_vfx.daedalus_explosoin)
-- Kill any players caught in the explosion radius
if (M23_flags.local_player_escaped == false) then
local vehicle_name = get_char_vehicle_name(LOCAL_PLAYER)
if (vehicle_name ~= nil and vehicle_name ~= "") then
vehicle_detonate(vehicle_name)
end
m23_insta_kill_character(LOCAL_PLAYER, true, true, false)
end
if (M23_flags.remote_player_escaped == false) then
local vehicle_name = get_char_vehicle_name(REMOTE_PLAYER)
if (vehicle_name ~= nil and vehicle_name ~= "") then
vehicle_detonate(vehicle_name)
end
m23_insta_kill_character(REMOTE_PLAYER, true, true, false)
end
-- If either player died, delay and allow the mission to fail for their death.
if M23_flags.remote_player_escaped == false or M23_flags.local_player_escaped == false then
delay(20.0)
end
m23_cleanup_escape_from_daedalus()
end
function m23_cleanup_escape_from_daedalus()
-- Cleanup the escape VTOLS
for i, vtol in pairs(M23_groups.daedalus_escape_vtols.vtols) do
on_vehicle_enter("", vtol)
marker_remove(vtol)
end
on_vehicle_enter("", M23_groups.daedalus_vtols_01.vtols[1])
-- Clear triggers.
on_trigger_exit("", M23_triggers.daedalus_blast_radius)
trigger_enable(M23_triggers.daedalus_blast_radius, false)
on_trigger("", M23_triggers.daedalus_blast_radius)
on_trigger_exit("", M23_triggers.flee_region)
trigger_enable(M23_triggers.flee_region, false)
-- End camera shake
camera_shake_stop()
-- Stop the timer
hud_timer_stop(1)
end
-- Callback for when the player enters one of the courtesy escape vehicles
function m23_entered_escape_vtol(player, vtol, seat)
-- Just remove the marker
if coop_is_active() then
marker_remove(vtol)
on_vehicle_enter("", vtol)
else
for i, vehicle in pairs(M23_groups.daedalus_escape_vtols.vtols) do
marker_remove(vehicle)
on_vehicle_enter("", vehicle)
end
end
end
-- Callback for when the escape time expires
function m23_cb_escape_time_expired()
M23_flags.escape_timer_expired = true
hud_timer_stop(1)
end
-- Callback for when a player exits the blast radius
function m23_cb_player_escaped(human, trigger)
if human == LOCAL_PLAYER then
M23_flags.local_player_escaped = true
elseif human == REMOTE_PLAYER then
M23_flags.remote_player_escaped = true
end
end
-- Callback for when a player enters the blast radius
function m23_cb_player_unescaped(human, trigger)
if human == LOCAL_PLAYER then
M23_flags.local_player_escaped = false
elseif human == REMOTE_PLAYER then
M23_flags.remote_player_escaped = false
end
end
-- Callback for when the player tries to flee to see if they're in a VTOL
function m23_cb_fleeing_check(human, trigger)
if character_is_in_vehicle(human) then
trigger_enable(M23_triggers.flee_region, false)
on_trigger_exit("", M23_triggers.flee_region)
else
explosion_create("VTOL_M_Exp-NPC", human)
delay(0.5)
m23_insta_kill_character(human, true, true)
end
end
-- ***************************************************
-- Miscellaneous m23 Helper Funcrtions
-- ***************************************************
-- Sets notoriety aircraft spawn groups as enabled or disabled
function m23_set_notoriety_aircraft_enabled(enabled)
notoriety_force_no_spawn_group("stag_attack_heli", not enabled)
notoriety_force_no_spawn_group("stag_heli", not enabled)
notoriety_force_no_spawn_group("police_heli", not enabled)
notoriety_force_no_spawn_group("police_blackhawk", not enabled)
notoriety_force_no_spawn_group("police_attack_heli", not enabled)
end
-- Setup and start the continuous spawn for the landing deck of the Daedalus
function m23_setup_landing_deck_cont_spawn()
-- Start the continuous spawn
spawn_region_max_spawn_dist(150.0)
continuous_spawn_regions_enable(M23_spawn_regions.landing_deck, true)
continuous_spawn_start(M23_spawn_groups.defensive_01, 0, 5)
continuous_spawn_start(M23_spawn_groups.defensive_02, 0, 5)
continuous_spawn_start(M23_spawn_groups.offensive_01, 0, 5)
continuous_spawn_start(M23_spawn_groups.riotshield_01, 0, 5)
end
-- Stop the landing deck continuous spawn
--
-- immediate: True to immediately destroy any active obejcts, false if they should be passed to the ambient system
function m23_cleanup_landing_deck_cont_spawn(immediate)
spawn_region_max_spawn_dist_reset()
continuous_spawn_regions_enable(M23_spawn_regions.landing_deck, false)
continuous_spawn_stop(M23_spawn_groups.defensive_01)
continuous_spawn_stop(M23_spawn_groups.defensive_02)
continuous_spawn_stop(M23_spawn_groups.offensive_01)
continuous_spawn_stop(M23_spawn_groups.riotshield_01)
end
-- Setup an ambush group
--
-- group_table: Lua table defining the ambush group
function m23_helper_setup_ambush_group(group_table)
-- Register this ambush group
M23_runtime.ambush_groups[#M23_runtime.ambush_groups + 1] = group_table
-- Enable the trigger(s)
for i, trigger in pairs(group_table.triggers) do
trigger_enable(trigger, true)
on_trigger("m23_ambush_triggered_cb", trigger)
end
end
-- Cleanup an ambush group
function m23_helper_cleanup_ambush_group(group_table, destroy)
-- Find and un-register this ambush group
for i, group in pairs(M23_runtime.ambush_groups) do
if (M23_runtime.ambush_groups[i].name == group_table.name) then
M23_runtime.ambush_groups[i] = M23_runtime.ambush_groups[#M23_runtime.ambush_groups]
M23_runtime.ambush_groups[#M23_runtime.ambush_groups] = nil
break
end
end
-- Disabled the trigger(s)
for i, trigger in pairs(group_table.triggers) do
trigger_enable(trigger, false)
on_trigger("", trigger)
end
end
-- Callback when the player enters an ambush trigger
--
-- triggerer: Play that entered the trigger
-- trigger: Name of the trigger that was entered
function m23_ambush_triggered_cb(triggerer, trigger)
-- Look-up the script group for this trigger volume
local group_table = nil
for i, ambush_group in pairs(M23_runtime.ambush_groups) do
for j, ambush_trigger in pairs(ambush_group.triggers) do
if (ambush_trigger == trigger) then
group_table = ambush_group
end
end
end
if (group_table ~= nil) then
for i, char in pairs(group_table.chars) do
if (char.behavior == "advance") then
npc_leash_remove(char.name)
ai_add_enemy_target(char.name, CLOSEST_PLAYER, ATTACK_ON_SIGHT, false)
ai_do_scripted_move(char.name, char.nav, true, false)
elseif (char.behavior == "attack_region") then
npc_leash_remove(char.name)
ai_add_enemy_target(char.name, CLOSEST_PLAYER, ATTACK_ON_SIGHT, false)
ai_attack_region(char.name, char.nav, char.radius)
elseif (char.behavior == "take_cover") then
npc_leash_remove(char.name)
ai_add_enemy_target(char.name, CLOSEST_PLAYER, ATTACK_ON_SIGHT, false)
ai_do_scripted_take_cover(char.name)
end
end
end
end
-- *************************
--
-- Callback functions
--
-- *************************
-- Callback to unhide/activate the courtesy helis at the Saints HQ
--
-- player: Player that triggered the trigger region
-- trigger: Trigger region that got triggered
function m23_saints_hq_activate_helis_cb(player, trigger)
-- Clear the trigger.
trigger_enable(M23_triggers.saints_hq_activate_helis, false)
on_trigger("", M23_triggers.saints_hq_activate_helis)
-- Unhide the helis.
group_show(M23_groups.courtesy_helis.name)
end
-- Callback when a player triggers the elevator up at the Saints HQ
--
-- player: Player that triggered the trigger region
-- trigger: Trigger region that got triggered
function m23_saints_hq_elev_up_cb(player, trigger)
end
-- Callback when a player enters the triggers the elevator down at the Saints HQ
--
-- player: Player that triggered the trigger region
-- trigger: Trigger region that got triggered
function m23_saints_hq_elev_down_cb(player, trigger)
marker_remove_trigger(M23_triggers.saints_hq_elev_up)
mission_waypoint_remove()
m23_saints_hq_entry_cb()
end
-- Callback when a player gets to the base of the Saints HQ
--
-- player: Player that triggered the trigger region
-- trigger: Trigger region that got triggered
function m23_saints_hq_gps_cb(player, trigger)
waypoint_remove()
--marker_remove_trigger(M23_triggers.saints_hq_gps, SYNC_ALL)
--marker_add_trigger(M23_triggers.saints_hq_elev_up, MINIMAP_ICON_LOCATION, INGAME_EFFECT_LOCATION, OI_ASSET_LOCATION, OI_FLAGS_LOCATION, SYNC_ALL)
end
-- Callback when the player leaves the Daedalus during the Cyrus battle.
--
-- player: Player that triggered the trigger region
-- trigger: Trigger region that got triggered
function m23_failure_leave_cb(player, trigger)
mission_end_failure("m23", "m23_failure_cyrus_escaped")
end
-- *************************
--
-- Thread functions
--
-- *************************
function m23_missile_rack_thread(missile_rack)
-- Loop until the thread is killed.
while true do
delay(rand_float(5.0, 8.0))
local volleys = rand_int(1, 3)
for i = 1, volleys do
local cur_rack = missile_rack.racks[missile_rack.cur_rack]
for i, missile in pairs(cur_rack) do
projectile_fire_from_navpoint(missile, missile_rack.target, "Explosive-RocketLauncher", 2.0)
delay(0.25)
end
missile_rack.cur_rack = missile_rack.cur_rack + 1
if missile_rack.cur_rack > #(missile_rack.racks) then
missile_rack.cur_rack = 1
end
delay(0.33)
end
end
end
-- Manages firing bursts from all the various guns on the Daedalus.
--
function m23_daedalus_guns_thread()
local thread_idx = 1
for i=1, sizeof_table(M23_daedalus_guns.threads) do
if M23_daedalus_guns.threads[i] ~= INVALID_THREAD_HANDLE then
thread_kill(M23_daedalus_guns.threads[i])
M23_daedalus_guns.threads[i] = INVALID_THREAD_HANDLE
end
end
-- Loop until killed.
local cur_effect = ""
local thread_idx = 1
while true do
delay(rand_float(0.1, 0.5))
threads_wait_for_completion(M23_daedalus_guns.threads[thread_idx])
cur_effect = get_random_table_entry(M23_daedalus_guns.effects)
M23_daedalus_guns.threads[thread_idx] = thread_new("m23_daedalus_gun_subthread", cur_effect, thread_idx)
thread_idx = thread_idx + 1
if thread_idx > sizeof_table(M23_daedalus_guns.threads) then
thread_idx = 1
end
end
end
-- Fires a burst from a gun on the Daedalus.
--
function m23_daedalus_gun_subthread(effect_nav, thread_idx)
-- Determine who needs to see the effects.
local sync_flags = m23_get_daedalus_gunfire_sync_flags()
if sync_flags < 0 then
-- Huh, guess we're done.
return
end
M23_daedalus_guns.active_effect_handles[thread_idx] = effect_play(effect_nav, true, sync_flags)
-- Start some looping audio for the gun.
M23_daedalus_guns.active_audio_loops[thread_idx] = audio_object_post_event("M23_vfx_artillery", nil, nil, effect_nav)
delay(rand_float(0.5, 2.0))
if M23_daedalus_guns.active_effect_handles[thread_idx] ~= -1 then
-- Stop the effect.
effect_stop(M23_daedalus_guns.active_effect_handles[thread_idx], sync_flags)
M23_daedalus_guns.active_effect_handles[thread_idx] = -1
end
if M23_daedalus_guns.active_audio_loops[thread_idx] > 0 then
-- Stop the audio.
audio_stop(M23_daedalus_guns.active_audio_loops[thread_idx])
M23_daedalus_guns.active_audio_loops[thread_idx] = -1
end
end
-- Fires a cannon on the Daedalus periodically.
--
-- effect_nav: (string) name of the nav point to play the effect at.
-- refire_min: (float) minimum time between shots.
-- refire_max: (float) maximum time between shots.
--
function m23_daedalus_cannon_firing_thread(effect_nav, refire_min, refire_max)
-- Continue looping until the thread is killed.
while true do
delay(rand_float(refire_min, refire_max))
if M23_flags.daedalus_main_cannons_firing == true then
-- Determine who needs to see the effects.
local sync_flags = m23_get_daedalus_gunfire_sync_flags()
if sync_flags > 0 then
effect_play(effect_nav, false, sync_flags)
end
--explosion_create("M23_daedalus_cannon", effect_nav, nil, false, effect_nav)
end
end
end
-- Waits for a helicopter to take off before removing it's HUD marker
--
-- helicopter: The helicopter to wait until it goes airborn
function m23_wait_for_heli_takeoff_thread(helicopter)
-- Wait for it to go airborn, and then remove the marker
while(vehicle_in_air(helicopter) == false) do
thread_yield()
end
marker_remove(helicopter, SYNC_ALL)
end
-- Process a set of vehicle's in a dog-fight around the VTOL
--
-- dog_fight_group: A table containing all of the info for this dog-fight (should modle attack_vtols_01)
function m23_process_dog_fight(dog_fight_group)
group_create(dog_fight_group.name, true)
-- Put all the guys in their vehicles
vehicle_enter_group_teleport(dog_fight_group.lead_group.chars, dog_fight_group.lead_group.vehicle)
set_attack_enemies_flag(dog_fight_group.lead_group.chars[1], false) -- Don't allow the leader to attack enemies (especially Cyrus)
for i, chase_group in pairs(dog_fight_group.chase_groups) do
vehicle_enter_group_teleport(chase_group.chars, chase_group.vehicle)
turn_invulnerable(chase_group.vehicle, true) -- allow player damage
turn_invulnerable(chase_group.chars[1], true) -- allow player damage
end
-- Start the lead vehicle on their path, and continue looping until the lead vehicle is destroyed
while(vehicle_is_destroyed(dog_fight_group.lead_group.vehicle) == false) do
local any_chase_heli_alive = false
-- Put all the chase vehicles on chase paths
for i, chase_group in pairs(dog_fight_group.chase_groups) do
if (vehicle_is_destroyed(chase_group.vehicle) == false) then
any_chase_heli_alive = true
helicopter_fly_to_do(chase_group.vehicle, -1, true, dog_fight_group.lead_group.vehicle, true, dog_fight_group.lead_group.path, chase_group.follow_dist, false)
helicopter_go_to_set_target(chase_group.vehicle, dog_fight_group.lead_group.vehicle)
ai_add_enemy_target(chase_group.chars[1], dog_fight_group.lead_group.chars[1], ATTACK_NOW_NEVER_LOSE, true)
end
end
if (any_chase_heli_alive == false) then
-- All chase helis are dead, have the lead vehicle retreat when he's done.
break
end
-- Set the lead vehicle going
turn_invulnerable(dog_fight_group.lead_group.vehicle, true) -- allow player damage
turn_invulnerable(dog_fight_group.lead_group.chars[1], true) -- allow player damage
helicopter_fly_to_direct_dont_stop(dog_fight_group.lead_group.vehicle, 30, dog_fight_group.lead_group.path)
thread_yield()
end
-- Have everyone retreat and stop fighting. We don't want any Saints to attack Cyrus.
for i, chase_group in pairs(dog_fight_group.chase_groups) do
helicopter_enter_retreat(chase_group.vehicle)
set_ignore_ai_flag(chase_group.chars[1], false)
set_attack_enemies_flag(chase_group.chars[1], false) -- Don't allow the leader to attack enemies (especially Cyrus)
end
helicopter_enter_retreat(dog_fight_group.lead_group.vehicle)
delay(10.0)
release_to_world(dog_fight_group.name)
group_destroy(dog_fight_group.name)
end
-- Process an attack VTOL that takes off from the Daedalus, and chases the player
--
-- vtol: The VTOL that is taking off (should already have a guy in it)
-- pilot: The pilot flying the VTOL
-- take_off_pos: The navpoint the VTOL should fly to when taking off (before chasing)
-- delay_time: How much time to wait, before the VTOl should take off (in seconds)
function m23_process_attack_vtol_take_off(vtol, pilot, take_off_pos, delay_time)
delay(delay_time)
-- Make the VTOl take off
helicopter_fly_to_direct_dont_stop(vtol, -1, take_off_pos)
-- Engage the player(s)
vehicle_chase(vtol, LOCAL_PLAYER, false, false, true, false)
ai_add_enemy_target(pilot, CLOSEST_PLAYER, ATTACK_NOW_NEVER_LOSE, true)
helicopter_shoot_human(vtol, LOCAL_PLAYER, false)
helicopter_go_to_set_target(vtol, LOCAL_PLAYER)
end
-- Process a stag soldier running to a tank
--
-- tank: The tank they are running to
-- char: The guy running to the tank
-- seat: The seat the guy should enter
function m23_process_run_to_tank_thread(tank, char, seat)
character_show(char)
-- Enter the tank if it is still alive
if (vehicle_is_destroyed(tank) == false) then
vehicle_enter(char, tank, seat, true)
end
-- Clear the ignore ai flag, and attack the closest player
set_ignore_ai_flag(char, false)
ai_add_enemy_target(char, LOCAL_PLAYER, ATTACK_ON_SIGHT)
if (coop_is_active() == true) then
ai_add_enemy_target(char, REMOTE_PLAYER, ATTACK_ON_SIGHT)
end
-- If the tank and character are still alive, and this character got into the driver's seat, then add a marker on the tank
if (vehicle_is_destroyed(tank) == false and character_is_dead(char) == false --[[and character_is_in_a_driver_seat(char) == true--]]) then
-- marker_add(tank, MINIMAP_ICON_KILL, OI_ASSET_KILL_FULL)
on_vehicle_destroyed("m23_callback_daedalus_tank_destroyed_cb", tank)
M23_runtime.daedalus_surface_tanks_alive = M23_runtime.daedalus_surface_tanks_alive + 1
end
end
-- Thread to process Cyrus's boss AI
function m23_process_cyrus_boss_ai_thread()
local sequence_idx = 1
while(true) do
if (M23_flags.do_cyrus_reinforcements == true) then
-- If Cyrus' health has hit a milestone, then call in reinforcements.
sequence_idx = rand_int(1, #(M23_cyrus_boss_ai.reinforce_threads))
M23_threads.cyrus_boss_action = thread_new(M23_cyrus_boss_ai.reinforce_threads[sequence_idx].func)
M23_flags.do_cyrus_reinforcements = false
else
-- Call the next attack sequence, and wait for it to complete
sequence_idx = rand_int(1, #(M23_cyrus_boss_ai.attack_threads))
M23_threads.cyrus_boss_action = thread_new(M23_cyrus_boss_ai.attack_threads[sequence_idx].func)
end
M23_runtime.cyrus_taunt_idx = M23_runtime.cyrus_taunt_idx + 1
if (M23_runtime.cyrus_taunt_idx < 0) or (M23_runtime.cyrus_taunt_idx > M23_tweak_values.taunt_idx_max) then
M23_runtime.cyrus_taunt_idx = 1
end
while(thread_check_done(M23_threads.cyrus_boss_action) == false) do
thread_yield()
end
-- Fly back the other way next time
M23_flags.cyrus_fly_left_to_right = not M23_flags.cyrus_fly_left_to_right
--[[
-- Move to the next attack in the sequence table
sequence_idx = sequence_idx + 1
if (sequence_idx > #(M23_cyrus_boss_ai.attack_threads)) then
sequence_idx = 1
end
--]]
thread_yield() -- Make sure we don't stall the game if an acttack thread returns early
end
end
-- Helper thread to process Cyrus shooting the charge laser at a random player
--
-- refire_delay: Time delay between shots (in seconds)
-- error_radius: Error radius around the player for the shots (in meters)
function m23_cyrus_boss_fire_at_player_thread(refire_delay, error_radius)
delay(2.0)
-- Figure out the target player (%50/%50 split, for either player in coop)
local target_player = LOCAL_PLAYER
if (coop_is_active() and rand_int(1, 2) == 2) then
target_player = REMOTE_PLAYER
end
-- Register the enemy player
ai_add_enemy_target(M23_groups.cyrus_vtol.cyrus, target_player, ATTACK_NOW_NEVER_LOSE, true)
if (M23_runtime.cyrus_taunt_idx == 4) then
audio_play_persona_line_2d(M23_personas.interrogation.persona_id, M23_dialog_lines.abandon_ship)
else
audio_play_persona_line_2d(M23_personas.cyrus.persona_id, M23_dialog_lines.cyrus_taunts[M23_runtime.cyrus_taunt_idx])
end
-- Fire at the target player indefinitly, until this thread gets killed
while(true) do
helicopter_shoot_human(M23_groups.cyrus_vtol.vtol, target_player, true, error_radius)
delay(refire_delay)
end
end
-- Helper thread to process Cyrus doing a sweep shot at a random player
--
-- refire_delay: Time delay between shots (in seconds)
function m23_cyrus_boss_sweep_fire_at_player_thread(refire_delay)
delay(2.0)
-- Figure out the target player (%50/%50 split, for either player in coop)
local target_player = LOCAL_PLAYER
if (coop_is_active() and rand_int(1, 2) == 2) then
target_player = REMOTE_PLAYER
end
-- Register the enemy player
ai_add_enemy_target(M23_groups.cyrus_vtol.cyrus, target_player, ATTACK_NOW_NEVER_LOSE, true)
if (M23_runtime.cyrus_taunt_idx == 4) then
audio_play_persona_line_2d(M23_personas.interrogation.persona_id, M23_dialog_lines.abandon_ship)
else
audio_play_persona_line_2d(M23_personas.cyrus.persona_id, M23_dialog_lines.cyrus_taunts[M23_runtime.cyrus_taunt_idx])
end
-- Process the sweep shot indefinitely, until this thread gets killed
local x_offset = 0.0
while(true) do
helicopter_shoot_human(M23_groups.cyrus_vtol.vtol, target_player, false, 1.0, false)
--x_offset = cyrus_boss_ai_sweep_shot(M23_groups.cyrus_vtol.vtol, target_player, 5.0, x_offset, 1.0, 15.0)
delay(refire_delay)
end
end
-- Thread to process Cyrus's strafe attack behavior
function m23_cyrus_boss_ai_strafe_thread()
--message("strafe")
-- Pick a random strafe attack definition
local strafe_attack_idx = rand_int(1, #(M23_cyrus_boss_ai.strafe_attacks))
local strafe_attack_info = M23_cyrus_boss_ai.strafe_attacks[strafe_attack_idx]
local path_name = ""
-- Do the intro path
m23_cyrus_pathfind(strafe_attack_info.paths.left_to_right.intro, strafe_attack_info.paths.right_to_left.intro, -1)
-- Start the thread to process Cyrus's shooting behavior
M23_threads.cyrus_fire_weapon_thread = thread_new("m23_cyrus_boss_fire_at_player_thread", strafe_attack_info.refire_delay, strafe_attack_info.error_radius)
-- Set the max bank angle, and give Cyrus perfect aim
helicopter_set_max_bank_angle(M23_groups.cyrus_vtol.vtol, 15)
set_perfect_aim(M23_groups.cyrus_vtol.cyrus, true)
-- Start up the middle path
m23_cyrus_pathfind(strafe_attack_info.paths.left_to_right.middle, strafe_attack_info.paths.right_to_left.middle, strafe_attack_info.vtol_speed)
-- Kill the thread processing Cyrus's shooting behavior
if (thread_check_done(M23_threads.cyrus_fire_weapon_thread) == false) then
thread_kill(M23_threads.cyrus_fire_weapon_thread)
end
-- Clear the max bank angle setting, and remove perfect aim
helicopter_clear_max_bank_angle(M23_groups.cyrus_vtol.vtol)
set_perfect_aim(M23_groups.cyrus_vtol.cyrus, false)
--helicopter_set_path_orientation(M23_groups.cyrus_vtol.vtol, HELI_PF_FACE_ALONG_PATH)
-- Do the outro path
m23_cyrus_pathfind(strafe_attack_info.paths.left_to_right.outro, strafe_attack_info.paths.right_to_left.outro, -1)
end
-- Thread to process Cyrus's sweep attack behavior
function m23_cyrus_boss_ai_sweep_thread()
--message("sweep")
-- Pick a random strafe attack definition
local sweep_attack_idx = rand_int(1, #(M23_cyrus_boss_ai.sweep_attacks))
local sweep_attack_info = M23_cyrus_boss_ai.sweep_attacks[sweep_attack_idx]
local path_name = ""
-- Do the intro path
m23_cyrus_pathfind(sweep_attack_info.paths.left_to_right.intro, sweep_attack_info.paths.right_to_left.intro, -1)
-- Start a thread to process the sweep shooting behavior
M23_threads.cyrus_fire_weapon_thread = thread_new("m23_cyrus_boss_sweep_fire_at_player_thread", sweep_attack_info.refire_delay)
-- Start up the middle path
m23_cyrus_pathfind(sweep_attack_info.paths.left_to_right.middle, sweep_attack_info.paths.right_to_left.middle, sweep_attack_info.vtol_speed)
helicopter_set_max_bank_angle(M23_groups.cyrus_vtol.vtol, 15)
-- Kill the thread processing Cyrus's shooting behavior
if (thread_check_done(M23_threads.cyrus_fire_weapon_thread) == false) then
thread_kill(M23_threads.cyrus_fire_weapon_thread)
end
-- Do the outro path
m23_cyrus_pathfind(sweep_attack_info.paths.left_to_right.outro, sweep_attack_info.paths.right_to_left.outro, -1)
end
function m23_cyrus_boss_ai_reinforcement_thread()
--message("reinforcements")
-- Spawn some reinforcements somewhere on the deck. Start by releasing any dudes
-- that might still be alive from the previous spawn.
continuous_spawn_stop(M23_spawn_groups.offensive_01)
continuous_spawn_stop(M23_spawn_groups.offensive_02)
continuous_spawn_start(M23_spawn_groups.offensive_01, 1)
continuous_spawn_start(M23_spawn_groups.offensive_02, 1)
-- Pick a random reinforcement attack definition
local reinforcement_idx = rand_int(1, #(M23_cyrus_boss_ai.reinforcement_attacks))
local reinforcement_info = M23_cyrus_boss_ai.reinforcement_attacks[reinforcement_idx]
local path_name = ""
-- Create (or recreate) the transport script group
if (group_is_loaded(M23_groups.transport_vtol_01.name) == true) then
-- Check to see if any of the guys are still alive
for i, char in pairs(M23_groups.transport_vtol_01.soldiers) do
if (character_is_dead(char) == false) then
-- Don't continue with this action, since there is a live enemy on the Daedalus deck.
-- Since we didn't actually fly anywhere, then reset the left-to-right flag.
M23_flags.cyrus_fly_left_to_right = not M23_flags.cyrus_fly_left_to_right
vehicle_set_vulnerable(M23_groups.cyrus_vtol.vtol)
-- If this is the final phase, allow Cyrus' VTOL to death spiral again.
if M23_flags.cyrus_final_phase == true then
helicopter_set_dont_death_spiral(M23_groups.cyrus_vtol.vtol, false)
end
return
end
end
release_to_world(M23_groups.transport_vtol_01.name)
group_destroy(M23_groups.transport_vtol_01.name)
thread_yield()
end
group_create(M23_groups.transport_vtol_01.name, true)
-- Kick off a thread to process the transport VTOL
if (thread_check_done(M23_threads.cyrus_transport_thread) == false) then
thread_kill(M23_threads.cyrus_transport_thread)
thread_yield()
end
local transport_path_idx = rand_int(1, #reinforcement_info.transport_paths)
local transport_path = reinforcement_info.transport_paths[transport_path_idx]
M23_threads.cyrus_transport_thread = thread_new("m23_cyrus_transport_drop_off_thread", M23_groups.transport_vtol_01, transport_path)
-- Do Cyrus's pathfind
m23_cyrus_pathfind(reinforcement_info.cyrus_paths.left_to_right, reinforcement_info.cyrus_paths.right_to_left, -1)
-- Cyrus is made invulnerable until we can start a reinforcement action; so make him vulnerable again when we're done.
vehicle_set_vulnerable(M23_groups.cyrus_vtol.vtol)
-- If this is the final phase, allow Cyrus' VTOL to death spiral again.
if M23_flags.cyrus_final_phase == true then
helicopter_set_dont_death_spiral(M23_groups.cyrus_vtol.vtol, false)
end
end
-- Process a VTOL transport drop-off
--
-- transport_table: VTOL transport Lua table (as defined in M23_groups::transport_vtol_01)
-- paths: Paths the transport should follow to drop the soldiers off and then exit
function m23_cyrus_transport_drop_off_thread(transport_table, paths)
-- Put the characters into the VTOL
local seat_idx = 0
for i, char in pairs(transport_table.pilots) do
-- Put the pilots into the vehicle
vehicle_enter_teleport(char, transport_table.vtol, seat_idx, true)
follower_remain_in_car(char, true)
seat_idx = seat_idx + 1
end
-- Make sure we don't put a solider in a co-pilot seat, if it hasn't been filled
if (seat_idx < 2) then
seat_idx = 2
end
for j, char in pairs(transport_table.soldiers) do
-- Put the soldiers into the vehicle
vehicle_enter_teleport(char, transport_table.vtol, seat_idx, true)
seat_idx = seat_idx + 1
end
delay(1.5)
audio_play_persona_line_2d(M23_personas.cyrus.persona_id, M23_dialog_lines.cyrus_call_backup)
-- Do the approach pathfind.
helicopter_fly_to_direct(transport_table.vtol, -1, paths.approach_path)
-- Tell the soldiers to exit the transport VTOL
--[[
vehicle_exit_group_teleport(transport_table.soldiers) -- hack: teleport the dudees out until we get exit animations.
delay(3.0)
--]]
vehicle_exit_group(transport_table.soldiers, true) -- unenterable to make sure the soldiers don't try to get back in the transport
-- Wait for everyone to get out.
for j, char in pairs(transport_table.soldiers) do
if character_is_in_vehicle(char, transport_table.vtol) then
thread_yield()
break
end
end
-- Force all the soldiers that got dropped off to take cover
for j, char in pairs(transport_table.soldiers) do
if (character_is_dead(char) == false) then
ai_do_scripted_take_cover(char)
end
end
-- Do the exit pathfind.
helicopter_fly_to_direct(transport_table.vtol, -1, paths.exit_path)
-- All drop offs are complete, so tell it to retreat
helicopter_enter_retreat(transport_table.vtol)
end
-- Thread function to execute the chase behavior for Cyrus's boss AI
--
-- duration: Amount of time (in seconds) this attack should last
function m23_cyrus_boss_ai_chase_thread(duration)
-- Pick a random reinforcement attack definition
local chase_idx = rand_int(1, #(M23_cyrus_boss_ai.chase_attacks))
local chase_info = M23_cyrus_boss_ai.chase_attacks[chase_idx]
-- Move to the start
helicopter_fly_to_direct(M23_groups.cyrus_vtol.vtol, -1, chase_info.paths.intro)
-- Start chasing the player
vehicle_chase(M23_groups.cyrus_vtol.vtol, LOCAL_PLAYER, false, false, true, false)
delay(3.0)
if (M23_runtime.cyrus_taunt_idx == 4) then
audio_play_persona_line_2d(M23_personas.interrogation.persona_id, M23_dialog_lines.abandon_ship)
else
audio_play_persona_line_2d(M23_personas.cyrus.persona_id, M23_dialog_lines.cyrus_taunts[M23_runtime.cyrus_taunt_idx])
end
delay(chase_info.duration)
-- Move to the next locatoin
helicopter_fly_to_direct(M23_groups.cyrus_vtol.vtol, -1, chase_info.paths.outro)
end
--[[
-- Thread function to execute the peak behavior for Cyrus's boss AI
--
-- duration: Amount of time (in seconds) this attack should last
function m23_cyrus_boss_ai_peak_thread(duration)
-- Pick a random peak location
local peak_idx = rand_int(1, #(M23_navpoints.cyrus_boss_ai.peak.start))
-- Move the VTOL to the start position
helicopter_fly_to_do(M23_groups.cyrus_vtol.vtol, -1, true, "", false, M23_navpoints.cyrus_boss_ai.peak.start[peak_idx], 0.0)
helicopter_fly_to_set_goal_direction(M23_groups.cyrus_vtol.vtol, LOCAL_PLAYER, false)
helicopter_go_to_set_target(M23_groups.cyrus_vtol.vtol, LOCAL_PLAYER)
ai_add_enemy_target(M23_groups.cyrus_vtol.cyrus, LOCAL_PLAYER, ATTACK_NOW_NEVER_LOSE, true)
while(vehicle_pathfind_check_done(M23_groups.cyrus_vtol.vtol) == 0) do
thread_yield()
end
-- Move to the peak pos
helicopter_fly_to_do(M23_groups.cyrus_vtol.vtol, -1, true, "", false, M23_navpoints.cyrus_boss_ai.peak.stop[peak_idx], 0.0)
helicopter_fly_to_set_goal_direction(M23_groups.cyrus_vtol.vtol, LOCAL_PLAYER, false)
helicopter_go_to_set_target(M23_groups.cyrus_vtol.vtol, LOCAL_PLAYER)
while(vehicle_pathfind_check_done(M23_groups.cyrus_vtol.vtol) == 0) do
thread_yield()
end
ai_add_enemy_target(M23_groups.cyrus_vtol.cyrus, LOCAL_PLAYER, ATTACK_NOW_NEVER_LOSE, true)
delay(duration)
-- Move back down to the start pos
helicopter_fly_to_direct(M23_groups.cyrus_vtol.vtol, -1, M23_navpoints.cyrus_boss_ai.peak.start[peak_idx])
end
-- Thread function to execute the hover behavior for Cyrus's boss AI
--
-- duration: Amount of time (in seconds) this attack should last
function m23_cyrus_boss_ai_attack_thread(duration)
-- Pick a random hover location
local hover_idx = rand_int(1, #(M23_navpoints.cyrus_boss_ai.hover.start))
-- Move to the start
helicopter_fly_to_direct(M23_groups.cyrus_vtol.vtol, -1, M23_navpoints.cyrus_boss_ai.hover.start[hover_idx])
helicopter_fly_to_set_goal_direction(M23_groups.cyrus_vtol.vtol, LOCAL_PLAYER, false)
helicopter_go_to_set_target(M23_groups.cyrus_vtol.vtol, LOCAL_PLAYER)
ai_add_enemy_target(M23_groups.cyrus_vtol.cyrus, LOCAL_PLAYER, ATTACK_NOW_NEVER_LOSE, true)
-- Move to the hover location
helicopter_fly_to_direct(M23_groups.cyrus_vtol.vtol, -1, M23_navpoints.cyrus_boss_ai.hover.stop[hover_idx])
helicopter_fly_to_set_goal_direction(M23_groups.cyrus_vtol.vtol, LOCAL_PLAYER, false)
helicopter_go_to_set_target(M23_groups.cyrus_vtol.vtol, LOCAL_PLAYER)
ai_add_enemy_target(M23_groups.cyrus_vtol.cyrus, LOCAL_PLAYER, ATTACK_NOW_NEVER_LOSE, true)
delay(2.0)
helicopter_shoot_human(M23_groups.cyrus_vtol.vtol, LOCAL_PLAYER, false)
delay(duration)
-- Pick a position to move to
local move_to_idx = rand_int(1, #(M23_navpoints.cyrus_boss_ai.hover.start))
if (move_to_idx == hover_idx) then
-- Make sure this is a different point than we started at
move_to_idx = move_to_idx + 1
if (move_to_idx > #(M23_navpoints.cyrus_boss_ai.hover.start)) then
move_to_idx = 1
end
end
-- Move to the next locatoin
helicopter_go_to_set_target(M23_groups.cyrus_vtol.vtol, LOCAL_PLAYER)
helicopter_fly_to_direct_dont_stop(M23_groups.cyrus_vtol.vtol, -1, M23_navpoints.cyrus_boss_ai.hover.stop[move_to_idx])
helicopter_shoot_human(M23_groups.cyrus_vtol.vtol, LOCAL_PLAYER, false)
delay(2.2)
helicopter_fly_to_direct(M23_groups.cyrus_vtol.vtol, -1, M23_navpoints.cyrus_boss_ai.hover.start[move_to_idx])
helicopter_go_to_set_target(M23_groups.cyrus_vtol.vtol, LOCAL_PLAYER)
end
--]]