--[[
m02.lua
SR3 Mission Script
DATE: 06/07/10
AUTHOR: Matt.Gawalek
]]--
-- Debug flags --
-- Checkpoints --
M02_checkpoints = {
start = MISSION_START_CHECKPOINT, -- defined in ug_lib.lua
free_falling = "m02_checkpoint_free_fall",
final_dive = "m02_checkpoint_final_dive",
debug_checkpoints = {
first_dive = "m02_debug_checkpoint_first_dive",
final_qte = "m02_debug_checkpoint_final_qte",
},
}
-- Tweakable Parameters --
M02_tweak_values = {
goon_hitpoint_modifier = 0.6,
player_downward_dive_speed = 12.0, -- this doesn't set the player's speed, just used for reference in the script
plane_interior_camera_shake = "cargo_plane_interior",
-- Weapon stuff
skydiving_pistol = "Pistol-Gang",
skydiving_smg = "SMG-Gang",
-- Final inventory load-out after completing the mission
mission_success_inventory = {
{
name = "Pistol-Gang",
reserve_ammo = 16,
},
{
name = "SMG-Gang",
reserve_ammo = 39,
},
},
-- Cooldown for the min/max time between cloud "puff" effects
skydive_cloud_min_cooldown_time = 15, -- seconds
skydive_cloud_max_cooldown_time = 25, -- seconds
-- Hitpoints for the cargo plane windshield
cargo_plane_windshield_hp = 60,
-- Car whoosh start/end trigger distanced (end should be larger than start)
car_whoosh_start_dist = 10.0,
car_whoosh_end_dist = 50.0,
music_trigger_shuandi_catch_time_01 = 13.12,
music_trigger_shuandi_catch_time_02 = 12.8,
}
-- Groups --
M02_groups = {
-- Script item props for the cargo plane interior
cargo_plane_props = {
name = "Group_cargo_plane_items 001",
},
-- Shaundi in the plane
homies_01 = {
name = "Group_homies 001",
chars = { "char_shaundi" },
shaundi = "char_shaundi",
},
-- Shuandi skydiving
homies_skydive_01 = {
name = "Group_homies_skydive 001",
chars = { "char_shaundi_skydive 001" },
shaundi = "char_shaundi_skydive 001",
},
-- Initial bad guys, in the room the player starts
cargo_plane_goons_01 = {
name = "Group_cargo_plane_goons 001",
chars = { "char_goon_cargo_plane_01_ 001", "char_goon_cargo_plane_01_ 002", "char_goon_cargo_plane_01_ 003" },
},
-- Bad guys in the second room of the cargo plane
cargo_plane_goons_02 = {
name = "Group_cargo_plane_goons 002",
chars = { "char_goon_cargo_plane_02_ 001", "char_goon_cargo_plane_02_ 002" },
},
-- Bad guys in the cargo section of the plane
cargo_plane_goons_03 = {
name = "Group_cargo_plane_goons 003",
chars = { "char_goon_cargo_plane_03_ 001", "char_goon_cargo_plane_03_ 002", "char_goon_cargo_plane_03_ 003", "char_goon_cargo_plane_03_ 004", "char_goon_cargo_plane_03_ 005",
"char_goon_cargo_plane_03_ 006", "char_goon_cargo_plane_03_ 007", "char_goon_cargo_plane_03_ 008", "char_goon_cargo_plane_03_ 009", "char_goon_cargo_plane_03_ 010",
"char_goon_cargo_plane_03_ 011", "char_goon_cargo_plane_03_ 012", "char_goon_cargo_plane_03_ 013", "char_goon_cargo_plane_03_ 014" },
move_up_chars = { "char_goon_cargo_plane_03_ 001" },
},
-- Vehicles in the cargo plane interior (not falling)
cargo_vehicles = {
name = "Group_cargo_vehicles 002",
vehicles = { "cargo_vehicle 001", "cargo_vehicle 002", "cargo_vehicle 003", "cargo_vehicle 004", "cargo_vehicle 005", "cargo_vehicle 006" },
car_alarm_vehicle = "cargo_vehicle 005",
},
-- The cargo plane that attempts to ram the player
plane = {
name = "Group_cargo_plane 001",
chars = { "char_cargo_plane_pilot 001" },
vehicle = "veh_cargo_plane 001",
windshield_component = "windshield",
circling_path = "path_plane_circle 001",
ram_start_pos = "nav_plane_ram_start_pos 001",
plane_ram_path = "path_cargo_plane_ram 001",
path_idx = 1,
},
-- Vehicles that are falling
debris_vehicles = {
name = "Group_debris_vehicles 001",
vehicles = {
{ name = "debris_vehicle 001", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle 002", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle 003", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle 004", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle 005", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle 006", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle 007", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle 008", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle 009", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle 010", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle 011", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle 012", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
}
},
debris_vehicles_dive_01 = {
name = "Group_debris_vehicle_dive 001",
vehicles = {
{ name = "debris_vehicle_dive_01_ 001", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle_dive_01_ 002", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle_dive_01_ 003", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle_dive_01_ 004", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle_dive_01_ 005", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle_dive_01_ 006", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle_dive_01_ 007", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle_dive_01_ 008", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle_dive_01_ 009", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle_dive_01_ 010", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle_dive_01_ 011", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle_dive_01_ 012", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
},
},
debris_vehicles_dive_02 = {
name = "Group_debris_vehicle_dive 002",
vehicles = {
{ name = "debris_vehicle_dive_02_ 001", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle_dive_02_ 002", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle_dive_02_ 003", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
{ name = "debris_vehicle_dive_02_ 004", whoosh_event = INVALID_MESSAGE_HANDLE, fire_handle = -1, exp_handle = -1 },
},
},
-- Initial group of enemies while skydiving (includes the guy that grabs the player in the first QTE)
sky_goons_01 = {
name = "Group_sky_goons 001",
chars = {
{ name = "char_goon_sky_01_ 002", dest_nav = "nav_char_goon_sky_01_ 002", forced_dive = false, delay_marker = false, streamers = false, vfx_handles = {} },
{ name = "char_goon_sky_01_ 003", dest_nav = "nav_char_goon_sky_01_ 003", forced_dive = false, delay_marker = false, streamers = false, vfx_handles = {} },
{ name = "char_goon_sky_01_ 004", dest_nav = "nav_char_goon_sky_01_ 004", forced_dive = false, delay_marker = false, streamers = false, vfx_handles = {} },
},
grapple_enemy = "char_goon_sky_01_ 001",
},
-- Secondary group that is spawned in coop so that the coop player has someone to grapple with in the first QTE
sky_goons_01_a = {
name = "Group_sky_goons_A 001",
chars = { "char_goon_sky_01A_ 001" },
grapple_enemy = "char_goon_sky_01A_ 001",
},
-- First wave of reinforcements (spawns below the player)
sky_goons_02 = {
name = "Group_sky_goons 002",
chars = {
{ name = "char_goon_sky_02_ 001", dest_nav = "nav_char_goon_sky_02_ 001", forced_dive = true, delay_marker = false, streamers = false, vfx_handles = {} },
{ name = "char_goon_sky_02_ 002", dest_nav = "nav_char_goon_sky_02_ 002", forced_dive = true, delay_marker = false, streamers = false, vfx_handles = {} },
},
},
-- Next wave of reinforcement (spawns above the player, once they flip down)
sky_goons_03 = {
name = "Group_sky_goons 003",
chars = {
{ name = "char_goon_sky_03_ 001", dest_nav = "nav_char_goon_sky_03_ 001", forced_dive = true, delay_marker = false, streamers = false, vfx_handles = {} },
{ name = "char_goon_sky_03_ 002", dest_nav = "nav_char_goon_sky_03_ 002", forced_dive = true, delay_marker = false, streamers = false, vfx_handles = {} },
{ name = "char_goon_sky_03_ 003", dest_nav = "nav_char_goon_sky_03_ 003", forced_dive = true, delay_marker = false, streamers = false, vfx_handles = {} },
{ name = "char_goon_sky_03_ 004", dest_nav = "nav_char_goon_sky_03_ 004", forced_dive = true, delay_marker = false, streamers = false, vfx_handles = {} },
},
},
-- Group of guys that spawn immediatly after the player fly through the cargo plane
sky_goons_05 = {
name = "Group_sky_goons 005",
chars = {
{ name = "char_goon_sky_05_ 001", dest_nav = "nav_char_goon_sky_05_ 001", forced_dive = true, delay_marker = false, streamers = false, vfx_handles = {} },
{ name = "char_goon_sky_05_ 002", dest_nav = "nav_char_goon_sky_05_ 002", forced_dive = true, delay_marker = false, streamers = false, vfx_handles = {} },
{ name = "char_goon_sky_05_ 003", dest_nav = "nav_char_goon_sky_05_ 003", forced_dive = true, delay_marker = false, streamers = false, vfx_handles = {} },
{ name = "char_goon_sky_05_ 004", dest_nav = "nav_char_goon_sky_05_ 004", forced_dive = true, delay_marker = false, streamers = false, vfx_handles = {} },
{ name = "char_goon_sky_05_ 005", dest_nav = "nav_char_goon_sky_05_ 005", forced_dive = true, delay_marker = false, streamers = false, vfx_handles = {} },
{ name = "char_goon_sky_05_ 006", dest_nav = "nav_char_goon_sky_05_ 006", forced_dive = false, delay_marker = false, streamers = false, vfx_handles = {} },
{ name = "char_goon_sky_05_ 007", dest_nav = "nav_char_goon_sky_05_ 007", forced_dive = false, delay_marker = false, streamers = false, vfx_handles = {} },
},
},
-- Group for the guy that grabs the local player during the final QTE sequence
sky_goons_07 = {
name = "Group_sky_goons 007",
chars = { "char_goon_sky_07_ 001" },
grapple_enemy = "char_goon_sky_07_ 001",
},
-- Secondar group for the guy the grabs the coop player (only spawns in coop)
sky_goons_07_A = {
name = "Group_sky_goons_A 007",
chars = { "char_goon_sky_07A_ 001" },
grapple_enemy = "char_goon_sky_07A_ 001",
}
}
-- Navpoints --
M02_navpoints = {
-- Player start positions
player_start_chk_01 = { "nav_player_start_local_chk 001", "nav_player_start_remote_chk 001" },
player_start_chk_02 = { "nav_player_start_local_chk 002", "nav_player_start_remote_chk 002" },
player_start_chk_03 = { "nav_player_start_local_chk 003", "nav_player_start_remote_chk 003" },
player_end = { "nav_player_local_end 001", "nav_player_remote_end 001" },
-- Turbulance direction navpoints
turbulance_direction_01 = "nav_turbulance_direction 001",
turbulance_direction_02 = "nav_turbulance_direction 002",
-- Locations for the AI to approach while investigating the player at the start of the mission
ai_investigation_01 = "nav_plane_ai_investigation 001",
ai_investigation_02 = "nav_plane_ai_investigation 002",
-- When the cargo bay doors are open, this is the direction humans get tossed if the enter the region
plane_rear_ejection_point = "nav_plane_ejection_point 001",
-- Position of the debris emitters for the junk falling around the player
debris_emitter_pos_01 = "debris_emitter_pos 001", -- default while the player is skydiving (debris goes up)
debris_emitter_pos_02 = "debris_emitter_pos 002", -- special emitter while the player's chute is open (debris goes down)
-- Shaundi's position during the free fall section
shaundi_skydive_pos = "nav_shandi_skydive_pos 001",
remote_player_catch_pos = "nav_remote_player_catch_pos",
player_grapple_pos = { "nav_player_grapple_pos 001", "nav_player_grapple_pos 002" },
player_camera_focus = "nav_player_camera_focus 001",
cargo_plane_fly_away_start = "nav_plane_fly_away_start 001",
cargo_plane_fly_away_end = "nav_plane_fly_away_end 001",
}
-- Triggers --
M02_triggers = {
-- Triggers for picking up the parachutes
turbulance_01 = "tgr_turbulance 001",
turbulance_02 = "tgr_turbulance 002",
--stop_gat_music = "tgr_cargo_plane_stop_music 001",
cargo_plane_parachute_01 = "tgr_cargo_plane_exit 001",
gun_grapple = "tgr_gun_grapple 001",
stop_dive_debris = "tgr_dive_debris_flow_stop 001",
clean_up_debris_above_shaundi = "tgr_cleanup_shaundi_debris 001",
cargo_plane_ram_trigger_01 = "tgr_cargo_plane_ram 001",
cargo_plane_ram_trigger_02 = "tgr_cargo_plane_ram 002",
-- Trigger region to end the vertical dive sequences of the mission (should be centered on Shaundi's position)
final_dive_01 = "tgr_vertical_dive 001",
}
-- Interiors --
M02_interiors = {
cargo_plane = "int_cargo_plane 001",
--vfx = "int_vfx 001",
}
-- Characters --
-- Conversations --
M02_conversations = {
leave_gat_behind = {
name = "m02_convo_1",
load_direct = false,
queued = false,
started = false,
completed = false,
ended = false,
},
gat_takes_control = {
name = "m02_johnny_message_1",
load_direct = false,
queued = false,
started = false,
completed = false,
ended = false,
},
gat_turbulence = {
name = "m02_turbulence",
load_direct = true,
queued = false,
started = false,
completed = false,
ended = false,
},
door_unlocked = {
name = "m02_cargo_doors",
load_direct = false,
queued = false,
started = false,
completed = false,
ended = false,
},
gat_update = {
name = "m02_convo_4",
load_direct = true,
queued = false,
started = false,
completed = false,
ended = false,
},
shaundi_catch_1 = {
name = "m02_pull_chute_1",
load_direct = false,
queued = false,
started = false,
completed = false,
ended = false,
},
notice_cargo_plane = {
name = "m02_plane",
load_direct = false,
queued = false,
started = false,
completed = false,
ended = false,
},
shaundi_catch_2 = {
name = "m02_pull_chute_2",
load_direct = false,
queued = false,
started = false,
completed = false,
ended = false,
},
}
M02_dialog_lines = {
shaundi_nags = {
"m02_get_moving_01",
"m02_get_moving_02",
"m02_get_moving_03",
"m02_get_moving_04",
},
gat_warning = "m02 gat warning",
free_fall_start = "m02_free_fall_03",
sky_goons = "m02_falling_goons",
push_shaundi_away = "m02_shaundi_pushed_away",
shoot_plane_window = "m02_shoot_plane_window",
window_shot = "m02_window_shot",
flew_through_plane = "m02_flew_through_the_plane",
falling_shit = "m02_falling_shit",
}
-- Vehicles --
-- Mesh Movers --
M02_movers = {
cargo_plane_doors = {
lounge1_utility1 = {
name = "mvr_cargo_plane_door 005",
locked = true,
open = false,
},
lounge2_utility1 = {
name = "mvr_cargo_plane_door 001",
locked = true,
open = false,
},
seating1_utility2 = {
name = "mvr_cargo_plane_door 002",
locked = true,
open = false,
},
seating1_utility3 = {
name = "mvr_cargo_plane_door 003",
locked = true,
open = false,
},
cargo1_utility3 = {
name = "mvr_cargo_plane_door 004",
locked = true,
open = false,
},
cargo_bay = {
name = "mvr_cargo_bay_door 001",
locked = true,
open = false,
},
},
debris_box_list = {
"mvr_debris_box 001", "mvr_debris_box 002", "mvr_debris_box 003", "mvr_debris_box 004", "mvr_debris_box 005",
"mvr_debris_box 006", "mvr_debris_box 007", "mvr_debris_box 008", "mvr_debris_box 009", "mvr_debris_box 010",
"mvr_debris_box 011", "mvr_debris_box 012", "mvr_debris_box 013", "mvr_debris_box 014", "mvr_debris_box 015",
"mvr_debris_box 016", "mvr_debris_box 017", "mvr_debris_box 018", "mvr_debris_box 019", "mvr_debris_box 020",
"mvr_debris_box 021", "mvr_debris_box 022", "mvr_debris_box 023", "mvr_debris_box 024", "mvr_debris_box 025",
"mvr_debris_box 026", "mvr_debris_box 027", "mvr_debris_box 028", "mvr_debris_box 029", "mvr_debris_box 030",
"mvr_debris_box 031", "mvr_debris_box 032",
"mvr_drug_stack 001", "mvr_drug_stack 002", "mvr_drug_stack 003", "mvr_drug_stack 004",
"mvr_money_stack 001", "mvr_money_stack 002", "mvr_money_stack 003", "mvr_money_stack 004", "mvr_money_stack 005", "mvr_money_stack 006",
},
debris_pallet_list = {
"mvr_debris_pallet 001", "mvr_debris_pallet 002", "mvr_debris_pallet 003", "mvr_debris_pallet 004", "mvr_debris_pallet 005", "mvr_debris_pallet 006",
"mvr_debris_pallet 007", "mvr_debris_pallet 008", "mvr_debris_pallet 009", "mvr_debris_pallet 010", "mvr_debris_pallet 011", "mvr_debris_pallet 012",
},
debris_dive_boxes = {
"mvr_drug_stack_dive_01_ 001", "mvr_drug_stack_dive_01_ 002", "mvr_drug_stack_dive_01_ 003", "mvr_drug_stack_dive_01_ 004", "mvr_drug_stack_dive_01_ 005", "mvr_drug_stack_dive_01_ 006",
"mvr_drug_stack_dive_01_ 007", "mvr_drug_stack_dive_01_ 008", "mvr_drug_stack_dive_01_ 009", "mvr_drug_stack_dive_01_ 010", "mvr_drug_stack_dive_01_ 011", "mvr_drug_stack_dive_01_ 012",
"mvr_money_stack_dive_01_ 001", "mvr_money_stack_dive_01_ 002", "mvr_money_stack_dive_01_ 003", "mvr_money_stack_dive_01_ 004", "mvr_money_stack_dive_01_ 005", "mvr_money_stack_dive_01_ 006",
"mvr_money_stack_dive_01_ 007", "mvr_money_stack_dive_01_ 008", "mvr_money_stack_dive_01_ 009", "mvr_money_stack_dive_01_ 010",
"mvr_debris_box_dive_01_ 001", "mvr_debris_box_dive_01_ 002", "mvr_debris_box_dive_01_ 003", "mvr_debris_box_dive_01_ 004", "mvr_debris_box_dive_01_ 005", "mvr_debris_box_dive_01_ 006",
"mvr_debris_box_dive_01_ 007", "mvr_debris_box_dive_01_ 008", "mvr_debris_box_dive_01_ 009", "mvr_debris_box_dive_01_ 010", "mvr_debris_box_dive_01_ 011", "mvr_debris_box_dive_01_ 012",
"mvr_debris_box_dive_01_ 013", "mvr_debris_box_dive_01_ 014", "mvr_debris_box_dive_01_ 015", "mvr_debris_box_dive_01_ 016",
},
debris_dive_pallets = {
"mvr_pallet_dive_01_ 001", "mvr_pallet_dive_01_ 002", "mvr_pallet_dive_01_ 003", "mvr_pallet_dive_01_ 004", "mvr_pallet_dive_01_ 005", "mvr_pallet_dive_01_ 006",
"mvr_pallet_dive_01_ 007", "mvr_pallet_dive_01_ 008", "mvr_pallet_dive_01_ 009", "mvr_pallet_dive_01_ 010", "mvr_pallet_dive_01_ 011", "mvr_pallet_dive_01_ 012",
"mvr_pallet_dive_01_ 013", "mvr_pallet_dive_01_ 014", "mvr_pallet_dive_01_ 015", "mvr_pallet_dive_01_ 016",
},
debris_dive_boxes_02 = {
"mvr_drug_stack_dive_02_ 001", "mvr_drug_stack_dive_02_ 002", "mvr_drug_stack_dive_02_ 003", "mvr_drug_stack_dive_02_ 004",
"mvr_money_stack_dive_02_ 001", "mvr_money_stack_dive_02_ 002",
"mvr_debris_box_dive_02_ 001", "mvr_debris_box_dive_02_ 002", "mvr_debris_box_dive_02_ 003", "mvr_debris_box_dive_02_ 004", "mvr_debris_box_dive_02_ 005",
},
debris_dive_pallets_02 = {
"mvr_pallet_dive_02_ 001", "mvr_pallet_dive_02_ 002", "mvr_pallet_dive_02_ 003", "mvr_pallet_dive_02_ 004", "mvr_pallet_dive_02_ 005", "mvr_pallet_dive_02_ 006",
},
}
M02_debris_flows = {
{
vehicle_group = M02_groups.debris_vehicles,
boxes = M02_movers.debris_box_list,
pallets = M02_movers.debris_pallet_list,
handle = 0,
},
{
vehicle_group = M02_groups.debris_vehicles_dive_01,
boxes = M02_movers.debris_dive_boxes,
pallets = M02_movers.debris_dive_pallets,
handle = 0,
},
{
vehicle_group = M02_groups.debris_vehicles_dive_02,
boxes = M02_movers.debris_dive_boxes_02,
pallets = M02_movers.debris_dive_pallets_02,
handle = 0,
}
}
-- Text --
M02_strings = {
help_melee_punch = "HELP_TEXT_THROW_PUNCH",
help_melee_block = "HELP_TEXT_MELEE_BLOCK",
help_melee_charged_attack = "HELP_TEXT_CHARGED_MELEE_ATTACK",
help_melee_lock = "HELP_TEXT_MELEE_LOCK",
help_skydive_move = "HELP_TEXT_SKYDIVE_MOVE_CONTROL",
help_skydive_steer = "HELP_TEXT_SKYDIVE_STEER_CONTROL",
help_skydive_flip = "HELP_TEXT_SKYDIVE_FLIP",
objective_escape = "M02_OBJ_ESCAPE",
objective_kill_guards = "M02_OBJ_KILL_GUARDS",
objective_open_doors = "M02_OBJ_OPEN_DOORS",
objective_find_parachute = "M02_OBJ_FIND_PARACHUTE",
objective_kill_pursuers = "M02_OBJ_KILL_PURSUERS",
objective_catch_shaundi = "M02_OBJ_CATCH_SHAUNDI",
objective_shoot_plane = "M02_OBJ_SHOOT_PLANE",
failure_shaundi_died = "m02_failure_shaundi_died",
failure_player_ejected = "m02_failure_player_ejected"
}
-- Threads --
M02_threads = {
process_convo = -1,
shaundi_nag_vo = -1,
cue_plane_music = -1,
cargo_door_open = -1,
cargo_bay_vo = -1,
cargo_plane_flying = -1,
cloud_generator = -1,
object_whoosh = -1,
catach_shaundi_audio = -1,
cargo_plane_retreat = -1,
clear_area_around_shaundi = -1,
kill_fallen_players = -1,
}
-- Audio Events --
M02_audio_events = {
cargo_bay_door_end = INVALID_MESSAGE_HANDLE,
car_whoosh = {
switch_groups = { "Mission_Number", "Mission_02" },
switches = { "Mission_02", "Mid_Air_Car_Whoosh" },
},
}
-- Audio Emitters --
M02_audio_emitters = {
plane_sounds = { "M02_Cargo_Rattle_Chains", "M02_Cargo_Rattles_ Large", "M02_Cargo_Rattles_ Small", "M02_DSP_Cabin_Reverb",
"M02_DSP_Cargo_Bay", "M02_Muffled_Gun_Battle", "M02_Plane_Cabin_Interior", "M02_Plane_Cargo_Interior",
"M02_TV_Static_01", "M02_TV_Static_02", "M02_TV_Static_03", "M02_TV_Static_04", "M02_TV_Static_05_Capsule_Test",
"M02_Wind_Through_Cargo_Door"},
plane_music = "M02_Plane_Cabin_Music",
cargo_bay_door = "M02_Cargo_Door_Open",
cargo_bay_looping = {
wind = "M02_Wind_Through_Cargo_Door",
paper = "M02_Paper_Flying_VFX",
alarm = "M02_Plane_Cargo_Alarm",
},
}
-- Cutscenes --
M02_cutscenes = {
mission_intro = "02_in",
mission_outro = "02_out",
plane_flythrough = "mission2_plane_flythrough"
}
-- Zscenes --
M02_zscenes = {
bailout = "02_z01",
fly_through = "02_z02",
}
-- Other --
M02_flags = {
-- Flags for the cargo plane interior sequence of events
opened_cargo_plane_rear_doors = false,
-- Flag for when the parachute is picked up by a player
reached_parachutes = false,
-- Flag for when the first QTE sequence is finished
local_gun_grapple_started = false,
remote_gun_grapple_started = false,
local_gun_grapple_finished = false,
remote_gun_grapple_finished = false,
-- Flag for when the cargo plane has reached the end of the ram path
cargo_plane_reached_ram_dest_01 = false,
cargo_plane_reached_ram_dest_02 = false,
-- Flag for when the final QTE sequence is finished
local_final_grapple_finished = false,
remote_final_grapple_finished = false,
-- Flags for various section of the skydiving sequence of events
triggered_final_dive_01 = false,
pull_parachute_finished = false,
}
M02_hud_states = {
hide_minimap = -1,
hide_hud = -1,
}
M02_runtime = {
sky_goons_alive = 0,
shaundi_nag_idx = 1,
convo_queue = {
playing = {
thread_id = -1,
conv_handle = INVALID_CONVERSATION_HANDLE,
convo_table = nil,
},
list = {},
size = 0,
},
stop_streamers = false,
shaundi_caught_by = LOCAL_PLAYER,
}
M02_personas = {
gat = {
name = "Gat",
persona_id = -1,
},
phillipe = {
name = "Phillipe",
persona_id = -1,
},
mstar_wm_01 = {
name = "Morningstar_WM_01",
persona_id = -1,
},
mstar_wm_02 = {
name = "Morningstar_WM_02",
persona_id = -1,
},
interrogation = {
name = "Interrogation",
persona_id = -1,
},
}
--VFX
M02_vfx = {
skydive_city = {
nav = "vfx_city_texture 001",
handle = -1,
},
skydive_clouds_below = {
nav = "vfx_bottom_clouds 001",
handle = -1,
},
skydive_player_clouds = {
nav = "vfx_clouds 001",
handles = { -1, -1 },
},
skydive_cloud_puff = {
nav = "vfx_random_clouds 001",
handle = -1,
},
skydive_player_streamers = {
nav = "vfx_cloud_streamers 001",
attach_points = { "l-foot", "r-foot", "l-hand", "r-hand" },
},
skydive_player_slow_clouds = {
nav = "vfx_slow_clouds 001",
handles = { -1, -1 },
},
debris_fire = {
name = "vfx_debris_fire 001",
box_handles = { },
dive_box_handles = { },
},
cargo_bay_wind = {
name = "vfx_cargo_bay_wind 001",
handle = -1,
},
cargo_dust = {
navs = { "vfx_dust_drop 001", "vfx_dust_drop 002", "vfx_dust_drop 003", "vfx_dust_drop 004", "vfx_dust_drop 005",
"vfx_dust_drop 006", "vfx_dust_drop 007", "vfx_dust_drop 008", "vfx_dust_drop 009", "vfx_dust_drop 010" },
},
-- Not hooked up
vehicle_explosion = {
name = "GP_Mission02_CarExp"
},
}
local M02_CONVO_PLAY_LAST = 0
local M02_CONVO_PLAY_NEXT = 1
local M02_CONVO_PLAY_IMMEDIATE = 2
-- *************************
--
-- 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
--
-- m02_checkpoint: The checkpoint the mission should begin at
-- is_restart: TRUE if the mission is restarting, FALSE otherwise
--
function m02_start(m02_checkpoint, is_restart)
mission_start_fade_out(0.0)
city_zone_swap("m2", true)
city_zone_swap("int_m2", true)
-- turn off grenade drops
npc_drop_grenades(false)
-- Check if this mission starting from the beginning
local playing_cutscene = false
if (m02_checkpoint == M02_checkpoints.start) then
if (is_restart == false) then
-- First time playing mission, play the intro cutscene
if (M02_cutscenes.mission_intro ~= "") then
cutscene_play(M02_cutscenes.mission_intro, nil, M02_navpoints.player_start_chk_01, false)
playing_cutscene = true
end
end
end
-- Handle mission initialization for the current checkpoint
m02_initialize(m02_checkpoint, playing_cutscene)
-- Run the mission from the current checkpoint
m02_run(m02_checkpoint)
end
-- This is the primary function responsible for cleaning up the entire mission
-- CALLED FROM CODE (+++MUST RETURN IMMEDIATLY+++)
--
function m02_cleanup()
-- This should not get called on checkpoint restarts
-- Stop the music and any mission audio
--audio_object_post_event("M02_Stop_All_Mission_Audio", nil, nil, LOCAL_PLAYER)
--audio_object_post_event("M02_Score_Stop", nil, nil, LOCAL_PLAYER)
-- Exit the special camera mode
camera_script_disable()
-- Kill the conversation thread
m02_helper_conversation_kill_all()
if (thread_check_done(M02_threads.process_convo) == false) then
thread_kill(M02_threads.process_convo)
M02_threads.process_convo = -1
end
-- Call the individual clean-up functions for the first checkpoint
m02_cleanup_basic_melee_tutorial()
m02_cleanup_advanced_melee_tutorial()
m02_cleanup_guy_with_gun()
m02_cleanup_go_to_cargo_hold()
m02_cleanup_grab_parachute()
m02_cleanup_cargo_plane_ejection_scene()
-- Call the individual clean-up functions for the second checkpoint
m02_cleanup_free_falling_initial_dive()
m02_cleanup_kill_sky_goons_01()
m02_cleanup_kill_sky_goons_02_03()
m02_cleanup_dive_to_shaundi()
m02_cleanup_catch_shaundi_first_time()
m02_cleanup_ram_player_with_plane()
m02_cleanup_fly_through_plane_scene()
-- Call the individual clean-up functions for the third checkpoint
m02_cleanup_kill_sky_goons_05()
m02_cleanup_final_dive_qte()
m02_cleanup_final_dive()
m02_cleanup_catch_shaundi_again()
-- Stop the players from skydiving
character_set_skydiving(LOCAL_PLAYER, false)
action_stop_custom(LOCAL_PLAYER)
player_parachute_wear_backpack(LOCAL_PLAYER, false)
character_set_never_fall(LOCAL_PLAYER, false)
character_parachute_detach(LOCAL_PLAYER)
character_set_damage_multiplier(LOCAL_PLAYER, 1.0)
character_set_never_catch_fire(LOCAL_PLAYER, false)
if (coop_is_active() == true) then
character_set_skydiving(REMOTE_PLAYER, false)
action_stop_custom(REMOTE_PLAYER)
player_parachute_wear_backpack(REMOTE_PLAYER, false)
character_set_never_fall(REMOTE_PLAYER, false)
character_parachute_detach(REMOTE_PLAYER)
character_set_damage_multiplier(REMOTE_PLAYER, 1.0)
character_set_never_catch_fire(REMOTE_PLAYER, false)
end
-- Remove all temporary weapons and re-enable the weapon radial
inv_weapon_remove_temporary(LOCAL_PLAYER, M02_tweak_values.skydiving_pistol)
inv_weapon_remove_temporary(LOCAL_PLAYER, M02_tweak_values.skydiving_smg)
inv_weapon_remove_temporary(REMOTE_PLAYER, M02_tweak_values.skydiving_pistol)
inv_weapon_remove_temporary(REMOTE_PLAYER, M02_tweak_values.skydiving_smg)
inv_weapon_disable_all_slots(false) -- enable
-- Make sure there aren't any QTEs or camera look-throughs active
qte_cleanup()
-- Stop any debris flow
for i, debris_flow in pairs(M02_debris_flows) do
if (debris_flow.handle ~= 0) then
debris_flow_destroy(debris_flow.handle)
debris_flow.handle = 0
end
end
-- Reset the wind
wind_override_clear()
-- Clean-up the debris flow vehicles
for i, vehicle in pairs(M02_groups.debris_vehicles.vehicles) do
m02_helper_cleanup_debris_vehicle(vehicle)
end
for i, vehicle in pairs(M02_groups.debris_vehicles_dive_01.vehicles) do
m02_helper_cleanup_debris_vehicle(vehicle)
end
for i, vehicle in pairs(M02_groups.debris_vehicles_dive_02.vehicles) do
m02_helper_cleanup_debris_vehicle(vehicle)
end
-- Clean-up all the VFX
m02_helper_destroy_global_effects()
-- Clean-up any loaded groups
for i, group in pairs(M02_groups) do
if (group_is_loaded(group.name)) then
group_destroy(group.name)
end
end
-- Clean-up any active threads
for i, thread in pairs(M02_threads) do
if (thread ~= -1) then
thread_kill(thread)
thread = -1
end
end
-- Clean up any boxes that may be on fire
for i, handle in pairs(M02_vfx.debris_fire.box_handles) do
effect_stop(handle)
end
for i, handle in pairs(M02_vfx.debris_fire.dive_box_handles) do
effect_stop(handle)
end
-- Reset notoriety
notoriety_force_no_spawn("Morningstar", false)
-- Re-eanble automatic vehicle corpse clean-up
vehicle_forced_corpse_removal_enabled(true)
-- Make sure non of the prop weapons still remain in the player's hands from the QTEs
character_remove_child_item_by_name(LOCAL_PLAYER, "SMG-Gang")
-- Restore the HUD
for i, hud_state in pairs(M02_hud_states) do
if (hud_state ~= -1) then
hud_display_remove_state(hud_state)
hud_state = -1
end
end
light_group_set_intensity("Script_Light_Group_pit<001>", 0.0)
light_group_detach_from_camera("Script_Light_Group_pit<001>")
-- Turn peds, vehicles, and action nodes back on
spawning_pedestrians(true)
spawning_vehicles(true)
action_nodes_enable(true)
-- Disable the mission02 interiors
city_zone_swap("m2", false)
city_zone_swap("int_m2", false)
city_zone_swap("m2fx", false)
mission_cleanup_maybe_reenable_player_controls()
crib_weapon_add_enable()
-- customization_item_wear disables some stores, so we need to revert that
customization_item_revert( )
end
-- Called when the mission has ended with success
-- CALLED FROM CODE (+++MUST RETURN IMMEDIATLY+++)
--
function m02_success()
-- Give the players their starting inventory for Stealport
inv_item_remove_all(LOCAL_PLAYER)
if (coop_is_active() == true) then
inv_item_remove_all(REMOTE_PLAYER)
end
for i, weapon in pairs(M02_tweak_values.mission_success_inventory) do
inv_item_add(weapon.name, weapon.reserve_ammo, LOCAL_PLAYER)
if (coop_is_active() == true) then
inv_item_add(weapon.name, weapon.reserve_ammo, REMOTE_PLAYER)
end
end
end
-- *************************
--
-- Local functions
--
-- *************************
-- ***************************************************
-- m02_initialize Helper Functions
-- ***************************************************
-- Initialize the mission for the specified checkpoint
--
-- checkpoint: Checkpoint to initialize the mission to
--
function m02_initialize(checkpoint, playing_cutscene)
-- Set the mission author
set_mission_author("Matt.Gawalek")
-- Common initialization
m02_initialize_common()
-- Checkpoint specific initialization
m02_initialize_checkpoint(checkpoint, playing_cutscene)
-- Start fading in
mission_start_fade_in()
end
-- Handle any common initialization
--
function m02_initialize_common()
-- Hide all the debris objects
m02_helper_hide_debris()
-- No notoriety spawning
notoriety_force_no_spawn("Morningstar", true)
-- special script camera for the airplane interior
camera_script_enable()
-- Disable crib weapons
crib_weapon_add_disable()
-- Start the conversation thread
M02_threads.process_convo = thread_new("m02_thread_process_conversations")
-- Set the cancel warp locations
mission_set_cancel_warp_location(M02_navpoints.player_end[1], M02_navpoints.player_end[2])
-- Reduce the amount of damage the player takes
character_set_damage_multiplier(LOCAL_PLAYER, 0.72)
if (coop_is_active() == true) then
character_set_damage_multiplier(REMOTE_PLAYER, 0.72)
end
-- Turn off peds, vehicles, and action nodes
spawning_pedestrians(false)
spawning_vehicles(false)
action_nodes_enable(false)
-- Create all the debris flows we will use while skydiving
for i, debris_flow in pairs(M02_debris_flows) do
m02_helper_create_debris_flow(debris_flow, (i ~= 1))
end
end
-- Checkpoint specific initialization
--
-- checkpoint: The checkpoint to be initialized
function m02_initialize_checkpoint(checkpoint, playing_cutscene)
if (checkpoint == M02_checkpoints.start) then
m02_initialize_cargo_plane(playing_cutscene)
elseif (checkpoint == M02_checkpoints.free_falling) then
m02_run_setup_free_falling_pre(true)
m02_run_setup_free_falling()
-- Move the players into place
teleport_coop(M02_navpoints.player_start_chk_02[1], M02_navpoints.player_start_chk_02[2])
delay(1.0)
m02_run_setup_free_falling_post(checkpoint)
if (coop_is_active() == true) then
delay(1.5)
end
-- Start the music at the correct spot
audio_object_post_event("M02_Score_Checkpoint_01", nil, nil, LOCAL_PLAYER)
elseif (checkpoint == M02_checkpoints.debug_checkpoints.first_dive) then
m02_run_setup_free_falling_pre(true)
m02_run_setup_free_falling()
-- Move the players into place
teleport_coop(M02_navpoints.player_start_chk_02[1], M02_navpoints.player_start_chk_02[2])
m02_run_setup_free_falling_post(checkpoint)
-- Teleport Shaundi down below
teleport(M02_groups.homies_skydive_01.shaundi, M02_navpoints.shaundi_skydive_pos)
delay(1.0)
-- Give the player(s) the SMG, equip it, and disable all other inventory slots
inv_weapon_remove_temporary(LOCAL_PLAYER, M02_tweak_values.skydiving_smg)
inv_weapon_add_temporary(LOCAL_PLAYER, M02_tweak_values.skydiving_smg, 1, true, true, true) -- SMG has unlimited ammo and is equipped immediatly, and dual weidling
if (coop_is_active() == true) then
inv_weapon_remove_temporary(REMOTE_PLAYER, M02_tweak_values.skydiving_smg)
inv_weapon_add_temporary(REMOTE_PLAYER, M02_tweak_values.skydiving_smg, 1, true, true, true) -- SMG has unlimited ammo and is equipped immediatly, and dual weidling
end
inv_weapon_disable_all_but_this_slot(WEAPON_SLOT_SMG)
skydive_set_player_dive_fall_speed(-1.0)
-- Start the global wind effects
m02_helper_create_global_effects(true, true, true, false)
-- Start the music at the correct spot
audio_object_post_event("M02_Score_Checkpoint_01", nil, nil, LOCAL_PLAYER)
elseif (checkpoint == M02_checkpoints.final_dive or checkpoint == M02_checkpoints.debug_checkpoints.final_qte) then
-- Make sure they have the default parachute backpack
customization_item_wear("basicparachute", "cm_pack_basicparachute.cmeshx", "Basic Parachute")
-- Make sure the player(s) are skydiving in the normal state state
character_set_skydiving(LOCAL_PLAYER, true)
player_parachute_wear_backpack(LOCAL_PLAYER, true)
if (coop_is_active() == true) then
character_set_skydiving(REMOTE_PLAYER, true)
player_parachute_wear_backpack(REMOTE_PLAYER, true)
end
-- Move the players into place
teleport_coop(M02_navpoints.player_start_chk_03[1], M02_navpoints.player_start_chk_03[2])
m02_helper_setup_skydiving_common()
m02_run_setup_final_dive()
skydive_set_player_dive_fall_speed(-1.0)
if (coop_is_active() == true) then
delay(1.5)
end
-- Start the music at the correct spot
audio_object_post_event("M02_Score_Checkpoint_02", nil, nil, LOCAL_PLAYER)
end
end
-- ***************************************************
-- m02_run Helper Functions
-- ***************************************************
-- 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 m02_run(first_checkpoint)
local current_checkpoint = first_checkpoint
-- Run the mission from the beginning, in the cargo plane
if (current_checkpoint == M02_checkpoints.start) then
m02_run_basic_melee_tutorial()
m02_run_advanced_melee_tutorial()
m02_run_guy_with_gun()
m02_run_go_to_cargo_hold()
m02_run_grab_parachute()
m02_run_cargo_plane_ejection_scene()
m02_run_setup_free_falling()
m02_run_setup_free_falling_post(M02_checkpoints.free_falling)
delay(0.2) -- add a short delay to allow the debris to fade in
if (coop_is_active() == true) then
delay(1.5)
end
mission_start_fade_in()
-- Set the checkpoint
current_checkpoint = M02_checkpoints.free_falling
mission_set_checkpoint(M02_checkpoints.free_falling)
end
-- Run the mission starting once the player is skydiving
if (current_checkpoint == M02_checkpoints.free_falling) then
m02_run_free_failling_initial_dive()
m02_run_kill_sky_goons_01()
m02_run_kill_sky_goons_02_03()
current_checkpoint = M02_checkpoints.debug_checkpoints.first_dive
end
-- Debug checkpoint for first dive
if (current_checkpoint == M02_checkpoints.debug_checkpoints.first_dive) then
m02_run_dive_to_shaundi()
m02_run_catch_shaundi_first_time()
m02_run_ram_player_with_plane()
m02_run_fly_through_plane_scene()
m02_run_setup_final_dive()
current_checkpoint = M02_checkpoints.final_dive
mission_set_checkpoint(M02_checkpoints.final_dive)
end
-- Run the mission starting after the player has flown threw the cargo plane
if (current_checkpoint == M02_checkpoints.final_dive) then
m02_run_kill_sky_goons_05()
current_checkpoint = M02_checkpoints.debug_checkpoints.final_qte
end
-- Debug checkpoint for final QTE
if (current_checkpoint == M02_checkpoints.debug_checkpoints.final_qte) then
m02_run_final_dive_qte()
m02_run_final_dive()
m02_run_catch_shaundi_again()
end
delay(6.0)
--[[
Do some immediate clean-up
--]]
-- Start fading out now
fade_out(1.0)
fade_out_block()
-- Stop the catch Shaundi sequence
skydive_set_catching_shaundi(false, false)
-- Reset the wind (do this before the player teleports to fix some effects still having upward wind)
wind_override_clear()
-- Clean-up the debris flow vehicles
for i, vehicle in pairs(M02_groups.debris_vehicles.vehicles) do
m02_helper_cleanup_debris_vehicle(vehicle)
end
for i, vehicle in pairs(M02_groups.debris_vehicles_dive_01.vehicles) do
m02_helper_cleanup_debris_vehicle(vehicle)
end
for i, vehicle in pairs(M02_groups.debris_vehicles_dive_02.vehicles) do
m02_helper_cleanup_debris_vehicle(vehicle)
end
-- Stop the music and any mission audio
audio_object_post_event("M02_Stop_All_Mission_Audio", nil, nil, LOCAL_PLAYER)
-- Make sure we hide the debris so they don't fall in the cutscene
for i, debris_flow in pairs(M02_debris_flows) do
if (debris_flow.handle ~= 0) then
debris_flow_set_inactive(debris_flow.handle)
end
end
-- Remove the player's parachute
player_parachute_wear_backpack(LOCAL_PLAYER, false)
character_parachute_detach(LOCAL_PLAYER)
if (coop_is_active()) then
player_parachute_wear_backpack(REMOTE_PLAYER, false)
character_parachute_detach(REMOTE_PLAYER)
end
-- Clean-up the VFX
m02_helper_destroy_global_effects()
-- Clean up any boxes that may be on fire
for i, handle in pairs(M02_vfx.debris_fire.box_handles) do
effect_stop(handle)
end
for i, handle in pairs(M02_vfx.debris_fire.dive_box_handles) do
effect_stop(handle)
end
-- Call mission success??
mission_end_success("m02", M02_cutscenes.mission_outro, M02_navpoints.player_end)
end
-- Setup the cargo plane interior for the first checkpoint
function m02_initialize_cargo_plane(playing_cutscene)
inv_item_remove_all(LOCAL_PLAYER)
inv_item_remove_all(REMOTE_PLAYER)
-- Set the ragdoll restore location, so characters don't get teleported out of the cargo plane
character_ragdoll_set_last_resort_position(M02_navpoints.player_start_chk_01[1])
character_ragdoll_set_last_valid_position(LOCAL_PLAYER, M02_navpoints.player_start_chk_01[1], true)
if (coop_is_active() == true) then
character_ragdoll_set_last_valid_position(REMOTE_PLAYER, M02_navpoints.player_start_chk_01[2], true)
end
-- Set the player's starting animation state
set_animation_state(LOCAL_PLAYER, "M02 Player Mission Start Idle")
-- Create all the necessary groups
group_create(M02_groups.cargo_plane_props.name)
group_create(M02_groups.homies_01.name)
group_create(M02_groups.cargo_plane_goons_01.name)
-- Move the players back to the mission starting location (if the cutscene wasn't played)
if (playing_cutscene == false) then
teleport_coop(M02_navpoints.player_start_chk_01[1], M02_navpoints.player_start_chk_01[2])
end
group_create_check_done_loop(M02_groups.cargo_plane_props.name)
group_create_check_done_loop(M02_groups.homies_01.name)
group_create_check_done_loop(M02_groups.cargo_plane_goons_01.name)
-- Setup Shaundi
party_add(M02_groups.homies_01.shaundi, LOCAL_PLAYER)
set_never_turn_on_player(M02_groups.homies_01.shaundi, true)
on_death("m02_cb_failure_shaundi_died", M02_groups.homies_01.shaundi)
-- Setup the guys in the cargo hold
set_force_combat_ready_team("Morningstar")
-- Setup the doors to their initial states
for i, door in pairs (M02_movers.cargo_plane_doors) do
if (door.open == true) then
door_open(door.name, false, false) -- don't open reverse, don't animate
else
door_close(door.name, false) -- don't animate
end
door_lock(door.name, door.locked)
end
-- Turn on all the audio emitters
for i, emitter in pairs(M02_audio_emitters.plane_sounds) do
audio_ambient_emitter_start(emitter)
end
-- Start the camera shake for the cargo plane interior
camera_shake_play_looping(M02_tweak_values.plane_interior_camera_shake)
-- Set the global wind override (we want this in the cargo plane as well, since side-ways winds doesn't look that great for "turbulance")
wind_override_set(0.0, 1.0, 0.0, 0.075)
-- Load the audio personas for Gat and Phillipe (they don't have any characters in the world)
M02_personas.gat.persona_id = audio_persona_load_2d(M02_personas.gat.name)
M02_personas.phillipe.persona_id = audio_persona_load_2d(M02_personas.phillipe.name)
M02_personas.mstar_wm_01.persona_id = audio_persona_load_2d(M02_personas.mstar_wm_01.name)
M02_personas.mstar_wm_02.persona_id = audio_persona_load_2d(M02_personas.mstar_wm_02.name)
M02_personas.interrogation.persona_id = audio_persona_load_2d(M02_personas.interrogation.name)
-- Prep the next z-scene
zscene_prep(M02_zscenes.bailout)
end
-- Run the initial melee tutorial, which takes place in the first room of the cargo plane interior
function m02_run_basic_melee_tutorial()
-- Queue up the first conversations
m02_helper_conversation_play(M02_conversations.leave_gat_behind, M02_CONVO_PLAY_LAST, 0.5)
m02_helper_conversation_play(M02_conversations.gat_takes_control, M02_CONVO_PLAY_LAST, 1.0)
-- Kick of some threads to process stuff
M02_threads.shaundi_nag_vo = thread_new("m02_thread_process_shaundi_nag", 60.0, 40.0)
M02_threads.cue_plane_music = thread_new("m02_thread_process_plane_music")
-- Display the melee tutorial
tutorial_start("combat", 0, true)
-- Start creating the rest of the enemies
group_create(M02_groups.cargo_plane_goons_02.name)
group_create(M02_groups.cargo_plane_goons_03.name)
group_create(M02_groups.cargo_vehicles.name)
thread_new("m02_play_player_start_anim_thread")
-- Wait a bit, so it the AI doesn't appear to react immediatly
delay(0.3)
-- Unleash the Shaundi!
npc_leash_remove(M02_groups.homies_01.shaundi)
-- Add a new objective to kill the guards
objective_text(0, M02_strings.objective_kill_guards, nil, nil, SYNC_ALL, OI_ASSET_KILL)
-- Setup the enemies
for i, char in pairs(M02_groups.cargo_plane_goons_01.chars) do
-- Remove the enemy leashes, and tell them not to flee
npc_leash_remove(char)
set_cant_flee_flag(char, true)
end
-- Wait until all the guys have been killed
process_enemy_set(M02_groups.cargo_plane_goons_01.chars)
-- Wait for the next group of enemies to be created
group_create_check_done_loop(M02_groups.cargo_plane_goons_02.name)
-- Call the clean-up for this section
m02_cleanup_basic_melee_tutorial()
end
-- Thread to process the player's start animation, so it doesn't block the main function
function m02_play_player_start_anim_thread()
-- Play the player's start animation
clear_animation_state(LOCAL_PLAYER)
action_play(LOCAL_PLAYER, "M02 Player Mission Start")
end
-- Function to process when to trigger the music in the cargo plane
function m02_thread_process_plane_music()
while(m02_helper_conversation_check_done(M02_conversations.gat_takes_control) == false) do
thread_yield()
end
if (M02_conversations.gat_takes_control.completed == true) then
audio_ambient_emitter_start(M02_audio_emitters.plane_music)
--[[
trigger_enable(M02_triggers.stop_gat_music, true)
on_trigger("m02_cb_stop_plane_music", M02_triggers.stop_gat_music)
--]]
end
end
--[[
function m02_cb_stop_plane_music()
m02_helper_disable_trigger(M02_triggers.stop_gat_music)
local event_id = audio_object_post_event("M02_Stop_Plane_Cabin_Music", nil, nil, M02_audio_emitters.plane_music)
end
--]]
-- Clean-up function for the basic melee tutorial
function m02_cleanup_basic_melee_tutorial()
if (thread_check_done(M02_threads.shaundi_nag_vo) == false) then
thread_kill(M02_threads.shaundi_nag_vo)
M02_threads.shaundi_nag_vo = -1
end
end
-- Run the advanced melee tutorial, which takes place int he second room of the cargo plan interior
function m02_run_advanced_melee_tutorial()
-- Update the obejctive to escape from the cargo plane
objective_text(0, M02_strings.objective_escape, nil, nil, SYNC_ALL, OI_ASSET_DEFEND)
M02_threads.shaundi_nag_vo = thread_new("m02_thread_process_shaundi_nag", 60.0, 40.0)
delay(2.0)
-- Unlock and open the door leading to the next room of the mission
door_lock(M02_movers.cargo_plane_doors.seating1_utility2.name, false)
door_open(M02_movers.cargo_plane_doors.seating1_utility2.name)
-- Send in the first enemy to "investigate"
npc_leash_remove(M02_groups.cargo_plane_goons_02.chars[1])
ai_add_enemy_target(M02_groups.cargo_plane_goons_02.chars[1], CLOSEST_PLAYER, ATTACK_ON_SIGHT)
ai_do_scripted_move(M02_groups.cargo_plane_goons_02.chars[1], M02_navpoints.ai_investigation_01, true, true)
set_cant_flee_flag(M02_groups.cargo_plane_goons_02.chars[1], true)
marker_add(M02_groups.cargo_plane_goons_02.chars[1], MINIMAP_ICON_KILL, OI_ASSET_KILL, OI_FLAGS_DEFAULT, SYNC_ALL)
-- Loop over all the guys in the first wave of goons, and make them attack the player
for i, enemy in pairs(M02_groups.cargo_plane_goons_02.chars) do
if (character_is_dead(enemy) == false) then
npc_leash_remove(enemy)
ai_add_enemy_target(enemy, CLOSEST_PLAYER, ATTACK_NOW)
set_cant_flee_flag(enemy, true)
end
end
-- Wait until all the guards have been killed
objective_text(0, M02_strings.objective_kill_guards, nil, nil, SYNC_ALL, OI_ASSET_KILL)
process_enemy_set(M02_groups.cargo_plane_goons_02.chars)
-- Wait just a sec
delay(1.0)
-- Make sure the next group of guys has been created
group_create_check_done_loop(M02_groups.cargo_plane_goons_03.name)
for i, char in pairs (M02_groups.cargo_plane_goons_03.chars) do
ai_do_scripted_take_cover(char)
set_cant_flee_flag(char, true)
set_ignore_ai_flag(char, false)
end
-- Clean-up this section
m02_cleanup_advanced_melee_tutorial()
end
-- Clean-up the advanced melee tutorial
function m02_cleanup_advanced_melee_tutorial()
if (thread_check_done(M02_threads.shaundi_nag_vo) == false) then
thread_kill(M02_threads.shaundi_nag_vo)
M02_threads.shaundi_nag_vo = -1
end
end
-- Process Gat opening the rear cargo bay doors
function m02_run_guy_with_gun()
-- Update the objective
objective_text(0, M02_strings.objective_escape, nil, nil, SYNC_ALL, OI_ASSET_DEFEND)
M02_threads.shaundi_nag_vo = thread_new("m02_thread_process_shaundi_nag", 40.0, 30.0)
-- Unlock and open the door leading between utility room 3 and the seating room 1
door_lock(M02_movers.cargo_plane_doors.seating1_utility3.name, false)
door_open(M02_movers.cargo_plane_doors.seating1_utility3.name, true) -- open reversed so it goes away from the AI guy
-- Move an enemy into the second room of the cargo plane
npc_leash_remove(M02_groups.cargo_plane_goons_03.move_up_chars[1])
ai_add_enemy_target(M02_groups.cargo_plane_goons_03.move_up_chars[1], CLOSEST_PLAYER, ATTACK_NOW)
ai_do_scripted_move(M02_groups.cargo_plane_goons_03.move_up_chars[1], M02_navpoints.ai_investigation_02, true, true)
set_cant_flee_flag(M02_groups.cargo_plane_goons_03.move_up_chars[1], true)
ai_set_action_delay(M02_groups.cargo_plane_goons_03.move_up_chars[1], "cover take def", 10000)
ai_set_action_delay(M02_groups.cargo_plane_goons_03.move_up_chars[1], "cover take off", 10000)
-- Update the objective
objective_text(0, M02_strings.objective_kill_guards, nil, nil, SYNC_ALL, OI_ASSET_KILL)
-- Enable the trigger region
trigger_enable(M02_triggers.turbulance_01, true)
on_trigger("m02_cb_triggered_turbulance_01", M02_triggers.turbulance_01)
-- Wait until all the guards have been killed
process_enemy_set(M02_groups.cargo_plane_goons_03.move_up_chars)
-- Give Shaundi a gun
inv_item_add(M02_tweak_values.skydiving_pistol, 1, M02_groups.homies_01.shaundi, true)
-- Make sure all the vehicles in the cargo hold have been created
group_create_check_done_loop(M02_groups.cargo_vehicles.name)
for i, vehicle in pairs(M02_groups.cargo_vehicles.vehicles) do
vehicle_car_alarm_enable(vehicle, true)
end
on_damage("m02_cb_trigger_car_alarm", M02_groups.cargo_vehicles.car_alarm_vehicle, 0.98)
turn_vulnerable(M02_groups.cargo_vehicles.car_alarm_vehicle)
vehicle_car_alarm_enable(M02_groups.cargo_vehicles.car_alarm_vehicle, false)
-- Clean-up this section
m02_cleanup_guy_with_gun()
end
-- Callback for when a player enters the trigger region that causes the first turbulance event on the plane
function m02_cb_triggered_turbulance_01()
-- Disable the trigger
m02_helper_disable_trigger(M02_triggers.turbulance_01)
-- Wait until none of the player's are playing a combat mobe
m02_helper_turbulance_wait_for_anims()
-- Make sure the goon is still alive
if (character_is_dead(M02_groups.cargo_plane_goons_03.move_up_chars[1]) == true) then
return
end
-- Do the stumbles (can't call non-blocking becasue it will never give control back to the player (which is stupid)
thread_new("m02_thread_do_play_stumble_animation", LOCAL_PLAYER, M02_navpoints.turbulance_direction_01, false)
if (coop_is_active() == true) then
thread_new("m02_thread_do_play_stumble_animation", REMOTE_PLAYER, M02_navpoints.turbulance_direction_01, false)
end
-- Play stumble on the morning star guy
ai_clear_scripted_action(M02_groups.cargo_plane_goons_03.move_up_chars[1])
action_play_non_blocking(M02_groups.cargo_plane_goons_03.move_up_chars[1], "Plane Cabin Stumble")
-- Play Shaundi's stumble
if (m02_helper_human_can_play_anim(M02_groups.homies_01.shaundi) == true) then
--character_release_human_shield(M02_groups.homies_01.shaundi)
action_play_non_blocking(M02_groups.homies_01.shaundi, "Plane Cabin Stumble")
end
-- Play Gat's conversation
m02_helper_conversation_play(M02_conversations.gat_turbulence, M02_CONVO_PLAY_NEXT, 0.5)
-- Do common turbulance stuff
m02_helper_turbulance_do_common()
end
-- Clean-up for opening up the rear cargo bay doors in the plane
function m02_cleanup_guy_with_gun()
if (thread_check_done(M02_threads.shaundi_nag_vo) == false) then
thread_kill(M02_threads.shaundi_nag_vo)
M02_threads.shaundi_nag_vo = -1
end
m02_helper_disable_trigger(M02_triggers.turbulance_01)
end
-- Run opening hte door to the cargo hold
function m02_run_go_to_cargo_hold()
-- Update the objective
objective_text(0, M02_strings.objective_escape, nil, nil, SYNC_ALL, OI_ASSET_DEFEND)
M02_threads.shaundi_nag_vo = thread_new("m02_thread_process_shaundi_nag", 40.0, 20.0)
-- Unlock the door leading leading between utility room 3 and the cargo hold
M02_flags.door_to_cargo_bay_opened = false
on_door_opened("m02_door_to_cargo_bay_opened_cb", M02_movers.cargo_plane_doors.cargo1_utility3.name)
door_lock(M02_movers.cargo_plane_doors.cargo1_utility3.name, false)
-- Wait until the door has been opened
while (M02_flags.door_to_cargo_bay_opened == false) do
thread_yield()
end
-- Clean-up
m02_cleanup_go_to_cargo_hold()
end
-- Cleanup for going to the cargo hold
function m02_cleanup_go_to_cargo_hold()
-- Remove the calback on the door
on_door_opened("", M02_movers.cargo_plane_doors.cargo1_utility3.name)
if (thread_check_done(M02_threads.shaundi_nag_vo) == false) then
thread_kill(M02_threads.shaundi_nag_vo)
M02_threads.shaundi_nag_vo = -1
end
--m02_helper_disable_trigger(M02_triggers.stop_gat_music)
--audio_ambient_emitter_stop(M02_audio_emitters.plane_music)
end
-- Callback for when the cargo bay door is opened
function m02_door_to_cargo_bay_opened_cb()
M02_flags.door_to_cargo_bay_opened = true
on_door_opened("", M02_movers.cargo_plane_doors.cargo1_utility3.name)
end
-- Wait for the player to reach the parachutes
function m02_run_grab_parachute()
-- Add an obejctive to find the parachute
objective_text(0, M02_strings.objective_find_parachute, nil, nil, SYNC_ALL, OI_ASSET_LOCATION)
M02_threads.shaundi_nag_vo = thread_new("m02_thread_process_shaundi_nag", 120.0, 35.0)
-- Stop the music
audio_object_post_event("M02_Stop_Plane_Cabin_Music", nil, nil, M02_audio_emitters.plane_music)
-- Time to preload the skydiving groups
m02_run_setup_free_falling_pre()
delay(1.0)
-- Loop over all the guys in the goons in the cargo hold, and make them attack the player
for i, char in pairs(M02_groups.cargo_plane_goons_03.chars) do
if (character_is_dead(char) == false) then
on_detection("m02_cb_plane_goon_detected_player", char)
on_take_damage("m02_cb_plane_goon_damaged", char)
end
end
M02_threads.cargo_door_open = thread_new("m02_thread_process_cargo_bay_door")
M02_threads.cargo_bay_vo = thread_new("m02_thread_process_cargo_bay_vo_lines")
-- Start the global wind effects
m02_helper_create_global_effects(false, true, false, false)
-- Enable the trigger regions for the turbulance 02 area
trigger_enable(M02_triggers.turbulance_02, true)
on_trigger("m02_cb_triggered_turbulance_02", M02_triggers.turbulance_02)
-- Enable the trigger regions for the parchute pickup
M02_flags.reached_parachutes = false
trigger_enable(M02_triggers.cargo_plane_parachute_01, true)
on_trigger("m02_cb_triggered_grab_parachute", M02_triggers.cargo_plane_parachute_01)
marker_add_trigger(M02_triggers.cargo_plane_parachute_01, MINIMAP_ICON_LOCATION, INGAME_EFFECT_LOCATION, OI_ASSET_LOCATION, OI_FLAGS_LOCATION, SYNC_ALL)
-- Loop until the parachute has been reached by a player
while(M02_flags.reached_parachutes == false) do
thread_yield()
end
-- Post an audio event to fade out all the cargo plane sounds
audio_object_post_event("M02_Zscene_01_Transition", nil, nil, LOCAL_PLAYER)
delay(0.25)
-- Set the player's last valid ragdoll position
character_ragdoll_set_last_valid_position(LOCAL_PLAYER, M02_navpoints.player_start_chk_02[1], true)
if (coop_is_active() == true) then
character_ragdoll_set_last_valid_position(REMOTE_PLAYER, M02_navpoints.player_start_chk_02[2], true)
end
m02_cleanup_grab_parachute()
end
-- Callback for when a player enters the trigger region that causes the first turbulance event on the plane
function m02_cb_triggered_turbulance_02()
-- Disable the trigger
m02_helper_disable_trigger(M02_triggers.turbulance_02)
-- Wait until none of the player's are playing a combat mobe
m02_helper_turbulance_wait_for_anims()
-- Don't do this event if the player doesn't have the pistol equipped (we don't have animations for those states)
if (inv_item_get_equipped_item(LOCAL_PLAYER) ~= M02_tweak_values.skydiving_pistol) then
return
end
if (coop_is_active() == true) then
if (inv_item_get_equipped_item(REMOTE_PLAYER) ~= M02_tweak_values.skydiving_pistol) then
return
end
end
-- Do the player stumbles (can't call non-blocking becasue it will never give control back to the player (which is stupid)
thread_new("m02_thread_do_play_stumble_animation", LOCAL_PLAYER, M02_navpoints.turbulance_direction_02, true)
if (coop_is_active() == true) then
thread_new("m02_thread_do_play_stumble_animation", REMOTE_PLAYER, M02_navpoints.turbulance_direction_02, true)
end
-- Do Shaundi's stumble
if (m02_helper_human_can_play_anim(M02_groups.homies_01.shaundi) == true) then
--character_release_human_shield(M02_groups.homies_01.shaundi)
action_play_directional_stumble_do(M02_groups.homies_01.shaundi, M02_navpoints.turbulance_direction_02)
end
-- Play a stumble on all the enemies
for i, char in pairs(M02_groups.cargo_plane_goons_03.chars) do
if (m02_helper_human_can_play_anim(char) == true and inv_item_is_equipped(M02_tweak_values.skydiving_pistol, char)) then
--character_release_human_shield(char)
action_play_directional_stumble_do(char, M02_navpoints.turbulance_direction_02)
end
end
-- Do common turbulance stuff
m02_helper_turbulance_do_common()
end
-- Thread to process opening the cargo bay door
function m02_thread_process_cargo_bay_door()
-- Open the cargo bay door
door_lock(M02_movers.cargo_plane_doors.cargo_bay.name, false)
door_open(M02_movers.cargo_plane_doors.cargo_bay.name)
-- Start the wind VFX
M02_vfx.cargo_bay_wind.handle = effect_play(M02_vfx.cargo_bay_wind.name, true)
-- Start all the looping cargo bay audio emitters
for i, emitter in pairs(M02_audio_emitters.cargo_bay_looping) do
audio_ambient_emitter_start(emitter)
end
-- Start the open audio event, and wait for the door to finish opening
audio_ambient_emitter_start(M02_audio_emitters.cargo_bay_door)
delay(30)
M02_audio_events.cargo_bay_door_end = audio_object_post_event("M02_Cargo_Door_Open_Finished", nil, nil, M02_audio_emitters.cargo_bay_door)
while(audio_is_playing(M02_audio_events.cargo_bay_door_end) == true) do
thread_yield()
end
audio_ambient_emitter_stop(M02_audio_emitters.cargo_bay_door)
end
-- Thread to process the cargo bay VO lines
function m02_thread_process_cargo_bay_vo_lines()
m02_helper_conversation_play(M02_conversations.door_unlocked, M02_CONVO_PLAY_IMMEDIATE, 1.0)
while(m02_helper_conversation_check_done(M02_conversations.door_unlocked) == false) do
thread_yield()
end
delay(3.0)
local audio_id = audio_play_persona_line_2d(M02_personas.gat.persona_id, M02_dialog_lines.gat_warning)
while(audio_is_playing(audio_id) == true) do
thread_yield()
end
delay(2.0)
m02_helper_conversation_play(M02_conversations.gat_update, M02_CONVO_PLAY_LAST, 2.0)
end
-- Clean-up for grabbing the player parachutes (final clean-up for checkpoint 01 which included general clean-up for the cargo plane)
function m02_cleanup_grab_parachute()
m02_helper_conversation_kill_all()
character_ragdoll_clear_last_resort_position()
if (thread_check_done(M02_threads.shaundi_nag_vo) == false) then
thread_kill(M02_threads.shaundi_nag_vo)
M02_threads.shaundi_nag_vo = -1
end
if (thread_check_done(M02_threads.cue_plane_music) == false) then
thread_kill(M02_threads.cue_plane_music)
M02_threads.cue_plane_music = -1
end
-- Remove all the active triggers
m02_helper_disable_trigger(M02_triggers.cargo_plane_parachute_01)
m02_helper_disable_trigger(M02_triggers.turbulance_02)
-- Clean-up Shaundi
if (group_is_loaded(M02_groups.homies_01.name)) then
party_dismiss(M02_groups.homies_01.shaundi)
end
on_death("", M02_groups.homies_01.shaundi)
-- Clean-up all the groups we created for this section
if (group_is_loaded(M02_groups.cargo_plane_props.name)) then
group_destroy(M02_groups.cargo_plane_props.name)
end
if (group_is_loaded(M02_groups.homies_01.name)) then
group_destroy(M02_groups.homies_01.name)
end
if (group_is_loaded(M02_groups.cargo_plane_goons_01.name)) then
group_destroy(M02_groups.cargo_plane_goons_01.name)
end
if (group_is_loaded(M02_groups.cargo_plane_goons_02.name)) then
group_destroy(M02_groups.cargo_plane_goons_02.name)
end
if (group_is_loaded(M02_groups.cargo_plane_goons_03.name)) then
group_destroy(M02_groups.cargo_plane_goons_03.name)
end
-- Setup the guys in the cargo hold
for i, char in pairs (M02_groups.cargo_plane_goons_03.chars) do
on_detection("", char)
on_take_damage("", char)
end
-- Clean up any personas we loaded
if (M02_personas.gat.persona_id ~= -1) then
audio_persona_remove_2d(M02_personas.gat.persona_id)
M02_personas.gat.persona_id = -1
end
if (M02_personas.phillipe.persona_id ~= -1) then
audio_persona_remove_2d(M02_personas.phillipe.persona_id)
M02_personas.phillipe.persona_id = -1
end
if (M02_personas.mstar_wm_01.persona_id ~= -1) then
audio_persona_remove_2d(M02_personas.mstar_wm_01.persona_id)
M02_personas.mstar_wm_01.persona_id = -1
end
if (M02_personas.mstar_wm_02.persona_id ~= -1) then
audio_persona_remove_2d(M02_personas.mstar_wm_02.persona_id)
M02_personas.mstar_wm_02.persona_id = -1
end
if (M02_personas.interrogation.persona_id ~= -1) then
audio_persona_remove_2d(M02_personas.interrogation.persona_id)
M02_personas.interrogation.persona_id = -1
end
on_damage("", M02_groups.cargo_vehicles.car_alarm_vehicle)
-- Stop the camera shake
camera_shake_stop()
-- Reset the wind
wind_override_clear()
-- Clean up the audio and thread for the cargo bay doors
audio_stop(M02_audio_events.cargo_bay_door_end)
if (thread_check_done(M02_threads.cargo_door_open) == false) then
thread_kill(M02_threads.cargo_door_open)
M02_threads.cargo_door_open = -1
end
if (thread_check_done(M02_threads.cargo_bay_vo) == false) then
thread_kill(M02_threads.cargo_bay_vo)
M02_threads.cargo_bay_vo = -1
end
-- Remove the callback for the vehicle alarm
on_take_damage("", M02_groups.cargo_vehicles.car_alarm_vehicle)
end
-- The Z-scene of the player(s) and Shaundi getting tossed out the back of the plane
function m02_run_cargo_plane_ejection_scene()
-- Kick off the music
audio_object_post_event("M02_Score_Start", nil, nil, LOCAL_PLAYER)
-- Play the Z-scene
cutscene_play(M02_zscenes.bailout, nil, M02_navpoints.player_start_chk_02, false)
-- Handle if the cutscene was skipped
if (cutscene_was_skipped() == true) then
audio_object_post_event("M02_Score_Skip_Z02_01", nil, nil, LOCAL_PLAYER)
end
-- Do clean-up
m02_cleanup_cargo_plane_ejection_scene()
end
-- Clean-up for the Z-scene
function m02_cleanup_cargo_plane_ejection_scene()
-- Turn off all the audio emitters in the cargo plane
for i, emitter in pairs(M02_audio_emitters.plane_sounds) do
audio_ambient_emitter_stop(emitter)
end
-- Turn off all the looping cargo bay emitters
for i, emitter in pairs(M02_audio_emitters.cargo_bay_looping) do
audio_ambient_emitter_stop(emitter)
end
-- Make sure the music has stopped
audio_ambient_emitter_stop(M02_audio_emitters.plane_music)
-- Stop the cargo bay effect
if (M02_vfx.cargo_bay_wind.handle ~= -1) then
effect_stop(M02_vfx.cargo_bay_wind.handle)
M02_vfx.cargo_bay_wind.handle = -1
end
end
-- Pre initialize free falling (before the Z-scene)
function m02_run_setup_free_falling_pre(immediate)
group_create_hidden(M02_groups.homies_skydive_01.name)
group_create_hidden(M02_groups.plane.name)
group_create_hidden(M02_groups.sky_goons_01.name)
group_create_hidden(M02_groups.sky_goons_01_a.name)
if (immediate == true) then
group_create_check_done_loop(M02_groups.homies_skydive_01.name)
group_create_check_done_loop(M02_groups.plane.name)
group_create_check_done_loop(M02_groups.sky_goons_01.name)
group_create_check_done_loop(M02_groups.sky_goons_01_a.name)
end
-- Prevent both player's from falling, otherwise the client might enter the fall state in the time it takes to
-- process the packets telling them to start skydiving after the z-scene.
character_set_never_fall(LOCAL_PLAYER, true)
if (coop_is_active() == true) then
character_set_never_fall(REMOTE_PLAYER, true)
end
end
-- Setup the player free falling (before the teleport)
function m02_run_setup_free_falling()
-- Make sure they have the default parachute backpack
customization_item_wear("basicparachute", "cm_pack_basicparachute.cmeshx", "Basic Parachute")
-- Have the player(s) start skydiving
skydive_set_player_dive_fall_speed(0.0)
character_set_skydiving(LOCAL_PLAYER, true)
player_parachute_wear_backpack(LOCAL_PLAYER, true)
if (coop_is_active()) then
character_set_skydiving(REMOTE_PLAYER, true)
player_parachute_wear_backpack(REMOTE_PLAYER, true)
end
-- Give the players a pistol, equip it, and disable all other inventory slots
inv_weapon_add_temporary(LOCAL_PLAYER, M02_tweak_values.skydiving_smg, 0, true, true, false) -- Pistol has unlimited ammo and is equipped immediatly, and dual weidling
inv_weapon_add_temporary(REMOTE_PLAYER, M02_tweak_values.skydiving_smg, 0, true, true, false) -- Pistol has unlimited ammo and is equipped immediatly, and dual weidling
inv_weapon_disable_all_but_this_slot(WEAPON_SLOT_SMG)
-- Start the cargo plane pathfinding thread to process the circling
group_show(M02_groups.plane.name)
vehicle_max_speed(M02_groups.plane.vehicle, 120.0)
set_min_hit_points(M02_groups.plane.vehicle, (get_max_hit_points(M02_groups.plane.vehicle) * 0.5))
M02_threads.cargo_plane_flying = thread_new("m02_thread_process_cargo_plane_circling")
-- Disable automatic vehicle corpse clean-up (we don't want the debris vehicles getting deleted on us)
vehicle_forced_corpse_removal_enabled(false)
end
-- Setup the player free falling (after the teleport)
function m02_run_setup_free_falling_post(checkpoint_name)
if (checkpoint_name == M02_checkpoints.free_falling) then
-- Enable the trigger that kicks of the gun grapple
trigger_enable(M02_triggers.gun_grapple, true)
on_trigger("m02_cb_gun_grapple_triggered", M02_triggers.gun_grapple)
end
-- Do common skydiving setup
m02_helper_setup_skydiving_common()
-- Move into the non-combat dive
character_set_skydiving_state(LOCAL_PLAYER, 6, true) -- lock him in the "non combat dive" state
if (coop_is_active()) then
character_set_skydiving_state(REMOTE_PLAYER, 6, true) -- lock him in the "non combat dive" state
end
-- Enable exterior rendering for both the interior volumes, so we can see the city
for i, interior in pairs(M02_interiors) do
interior_disable_exterior(interior, true)
end
-- Start some thund-dah
audio_object_post_event("Mission", { "Mission_Number", "Mission_02" }, { "Mission_02", "Thunder_Rumble" }, LOCAL_PLAYER)
-- And... some wind
audio_object_post_event("M02_Back_To_FreeFall_Wind", nil, nil, LOCAL_PLAYER)
-- Start the global wind effects
m02_helper_create_global_effects(false, true, true, false)
-- Attack some lights to the camera
light_group_set_intensity("Script_Light_Group_pit<001>", 1.0)
light_group_attach_to_camera("Script_Light_Group_pit<001>")
-- Preload the plane flythrough zscene
zscene_prep(M02_zscenes.fly_through)
end
-- Process the short tutorial on the skydiving controls
function m02_run_free_failling_initial_dive()
-- Add the objective text to catch Shaundi
objective_text(0, M02_strings.objective_catch_shaundi, nil, nil, SYNC_ALL, OI_ASSET_LOCATION)
marker_add(M02_groups.homies_skydive_01.shaundi, MINIMAP_ICON_LOCATION, OI_ASSET_LOCATION, OI_FLAGS_DEFAULT, SYNC_ALL)
skydive_set_player_dive_fall_speed(-1.0)
-- Kick off the first QTE
M02_flags.local_gun_grapple_started = false
M02_flags.remote_gun_grapple_started = false
M02_flags.local_gun_grapple_finished = false
M02_flags.remote_gun_grapple_finished = false
-- Set up the grapple enemies
character_set_skydiving(M02_groups.sky_goons_01.grapple_enemy, true)
character_set_skydiving_state(M02_groups.sky_goons_01.grapple_enemy, 1, true)
character_show(M02_groups.sky_goons_01.grapple_enemy)
human_skydive_create_npc_backpack(M02_groups.sky_goons_01.grapple_enemy)
set_ignore_ai_flag(M02_groups.sky_goons_01.grapple_enemy, true)
character_prevent_flinching(M02_groups.sky_goons_01.grapple_enemy, true)
character_allow_ragdoll(M02_groups.sky_goons_01.grapple_enemy, false)
turn_invulnerable(M02_groups.sky_goons_01.grapple_enemy)
if (coop_is_active()) then
character_set_skydiving(M02_groups.sky_goons_01_a.grapple_enemy, true)
character_set_skydiving_state(M02_groups.sky_goons_01_a.grapple_enemy, 1, true)
character_show(M02_groups.sky_goons_01_a.grapple_enemy)
human_skydive_create_npc_backpack(M02_groups.sky_goons_01_a.grapple_enemy)
set_ignore_ai_flag(M02_groups.sky_goons_01_a.grapple_enemy, true)
character_prevent_flinching(M02_groups.sky_goons_01_a.grapple_enemy, true)
character_allow_ragdoll(M02_groups.sky_goons_01_a.grapple_enemy, false)
turn_invulnerable(M02_groups.sky_goons_01_a.grapple_enemy)
end
-- MLG 07/04: This line is cut
--[[
delay(1.0)
audio_play_persona_line(LOCAL_PLAYER, M02_dialog_lines.free_fall_start)
--]]
-- Wait until one player has finished the QTE(s)
while (M02_flags.local_gun_grapple_finished == false and M02_flags.remote_gun_grapple_finished == false) do
thread_yield()
end
m02_cleanup_free_falling_initial_dive()
end
-- Callback when the player reaches the gun grapple trigger region
function m02_cb_gun_grapple_triggered(player_name, trigger_name)
local grapple_enemy, callback_func, grapple_pos
if (player_name == LOCAL_PLAYER) then
if (M02_flags.local_gun_grapple_started == true) then
return
end
M02_flags.local_gun_grapple_started = true
grapple_enemy = M02_groups.sky_goons_01.grapple_enemy
callback_func = "m02_cb_local_gun_grapple_finished"
grapple_pos = M02_navpoints.player_grapple_pos[1]
else
if (M02_flags.remote_gun_grapple_started == true) then
return
end
M02_flags.remote_gun_grapple_started = true
grapple_enemy = M02_groups.sky_goons_01_a.grapple_enemy
callback_func = "m02_cb_remote_gun_grapple_finished"
grapple_pos = M02_navpoints.player_grapple_pos[2]
end
-- Remove the marker on Shaundi
marker_remove(M02_groups.homies_skydive_01.shaundi)
-- Stop streamers
M02_runtime.stop_streamers = true
-- Move the grapple enemy into position
teleport_to_object(grapple_enemy, player_name, false, false, 0.0, 5.0, 0.0)
delay(1.0)
qte_start_m02_skyqte_01(player_name, callback_func, grapple_enemy, grapple_pos)
-- If this is the Local player, player a reaction line
if (player_name == LOCAL_PLAYER) then
delay(1.5)
audio_play_persona_line(player_name, "M02_Air_Flight_01")
end
end
-- Clean-up the free falling tutorial
function m02_cleanup_free_falling_initial_dive()
skydive_set_player_dive_fall_speed(-1.0)
-- Remove the marker on Shaundi
marker_remove(M02_groups.homies_skydive_01.shaundi)
end
-- Setup the player to kill the guys from above
function m02_run_kill_sky_goons_01()
-- Update the objective text to kill all the pursuers
objective_text(0, M02_strings.objective_kill_pursuers, nil, nil, SYNC_ALL, OI_ASSET_KILL)
-- Pre-create the next groups of enemies
group_create_hidden(M02_groups.sky_goons_02.name)
group_create_hidden(M02_groups.sky_goons_03.name)
-- Show the skydiving enemies
M02_runtime.sky_goons_alive = 0
skydive_set_elev_angles_deg(60, 85) --was 50, 75
m02_helper_set_sky_goon_group(M02_groups.sky_goons_01, true)
delay(2.5)
audio_play_persona_line(LOCAL_PLAYER, M02_dialog_lines.sky_goons)
-- Start the global wind effects
m02_helper_create_global_effects(true, true, true, false)
-- Wait until all the enemies have been killed
while(M02_runtime.sky_goons_alive > 0) do
thread_yield()
end
-- Wait until the QTE(s) has finished
while (M02_flags.local_gun_grapple_finished == false or (coop_is_active() == true and M02_flags.remote_gun_grapple_finished == false)) do
thread_yield()
end
-- Teleport Shaundi down below
teleport(M02_groups.homies_skydive_01.shaundi, M02_navpoints.shaundi_skydive_pos)
m02_cleanup_kill_sky_goons_01()
end
-- Clean-up killing sky goons 01
function m02_cleanup_kill_sky_goons_01()
m02_helper_cleanup_sky_goon_group(M02_groups.sky_goons_01)
-- Remove all fun grapple trigger
m02_helper_disable_trigger(M02_triggers.gun_grapple)
end
-- Setup the player to kill the guys from below
function m02_run_kill_sky_goons_02_03()
skydive_set_elev_angles_deg(-30, -5) -- was -50,-25
-- Set all the enemeies skydiving
M02_runtime.sky_goons_alive = 0
group_show(M02_groups.sky_goons_02.name)
m02_helper_set_sky_goon_group(M02_groups.sky_goons_02, true)
-- Kill the human shield, and set the player(s) into the inverted skydiving state
thread_new("m02_thread_process_grapple_enemy_release", LOCAL_PLAYER, M02_groups.sky_goons_01.grapple_enemy)
if (coop_is_active()) then
delay(0.5)
thread_new("m02_thread_process_grapple_enemy_release", REMOTE_PLAYER, M02_groups.sky_goons_01_a.grapple_enemy)
end
-- Start streamers again
M02_runtime.stop_streamers = false
delay(5.0)
-- Display tutorial message
if game_is_active_input_gamepad() then
tutorial_start("mis_02_skydive_flip", 0, true, false, true)
else
tutorial_start("mis_02_skydive_flip_pc", 0, true, false, true)
end
-- Wait until one of the players has flipped
while(get_char_skydiving_state(LOCAL_PLAYER) ~= 0 and (coop_is_active() == false or get_char_skydiving_state(REMOTE_PLAYER) ~= 0)) do
thread_yield()
end
-- Clear the tutorial message
tutorial_stop("mis_02_skydive_flip")
tutorial_stop("mis_02_skydive_flip_pc")
-- Wait until at lesat one guy as been killed
while(M02_runtime.sky_goons_alive == #(M02_groups.sky_goons_02.chars)) do
thread_yield()
end
-- Set all the enemeies skydiving
group_show(M02_groups.sky_goons_03.name)
m02_helper_set_sky_goon_group(M02_groups.sky_goons_03, true)
-- Wait until group 02 & 03 are all dead
while(M02_runtime.sky_goons_alive > 0) do
thread_yield()
end
delay(2.0)
m02_cleanup_kill_sky_goons_02_03()
end
-- Thread to process the Upside down HS release animation
function m02_thread_process_grapple_enemy_release(player, enemy)
character_parachute_open(enemy, true, "Free Fall")
set_animation_state(enemy, "Chute fall Dead")
character_parachute_play_action(enemy, "freefall HS upside release B")
action_play_synced_do(player, enemy, "FreeFalling_HS_UpsDwn_Release")
human_gravity_enable(enemy, false)
while(action_play_synced_is_finished(player, enemy) == false) do
thread_yield()
end
character_set_skydiving(enemy, true)
set_ignore_ai_flag(enemy, true)
character_set_skydiving_state(player, 2, false)
end
-- Clean-up killing sky goons 02 & 03
function m02_cleanup_kill_sky_goons_02_03()
-- Clean-up all the enemies in group 02 & 03
m02_helper_cleanup_sky_goon_group(M02_groups.sky_goons_02)
m02_helper_cleanup_sky_goon_group(M02_groups.sky_goons_03)
end
-- Process the first vertical dive where the player(s) reach Shaundi
function m02_run_dive_to_shaundi()
-- Add the objective text to catch Shaundi
objective_text(0, M02_strings.objective_catch_shaundi, nil, nil, SYNC_ALL, OI_ASSET_LOCATION)
marker_add(M02_groups.homies_skydive_01.shaundi, MINIMAP_ICON_LOCATION, OI_ASSET_LOCATION, OI_FLAGS_DEFAULT, SYNC_ALL)
-- Enable the trigger to create the second wave of debris
trigger_enable(M02_triggers.stop_dive_debris, true)
on_trigger("m02_cd_triggered_start_secondary_debris", M02_triggers.stop_dive_debris)
-- Start the second flow
debris_flow_set_active(M02_debris_flows[1].handle, M02_navpoints.debris_emitter_pos_01, 0.0, 25.0, 250.0, LOCAL_PLAYER, 1.0, true)
debris_flow_set_active(M02_debris_flows[2].handle, M02_navpoints.debris_emitter_pos_01, 0.0, 20.0, 250.0, LOCAL_PLAYER, 1.0, true)
-- Make sure the player's are skydiving in the vertical dive state
character_set_skydiving_state(LOCAL_PLAYER, 1, true)
on_take_damage("m02_player_damaged_while_diving_cb", LOCAL_PLAYER)
if (coop_is_active()) then
character_set_skydiving_state(REMOTE_PLAYER, 1, true)
on_take_damage("m02_player_damaged_while_diving_cb", REMOTE_PLAYER)
end
-- Setup the first trigger region, and just wait until the player get's there
M02_flags.triggered_final_dive_01 = false
on_trigger("m02_cb_triggered_final_dive_01", M02_triggers.final_dive_01)
trigger_enable(M02_triggers.final_dive_01, true)
-- Kick off a thread to figure out when to trigger the music transition for cataching Shaundi
M02_threads.catach_shaundi_audio = thread_new("m02_thread_process_catch_shaundi_music", "M02_Score_Transition_01", M02_tweak_values.music_trigger_shuandi_catch_time_01)
-- Wait until the player reaches the trigger region by Shaundi
while(M02_flags.triggered_final_dive_01 == false) do
thread_yield()
end
m02_cleanup_dive_to_shaundi()
end
-- Clean-up the dive to Shaundi
function m02_cleanup_dive_to_shaundi()
-- Remove the marker from Shaundi
marker_remove(M02_groups.homies_skydive_01.shaundi)
trigger_enable(M02_triggers.stop_dive_debris, false)
on_trigger("", M02_triggers.stop_dive_debris)
on_take_damage("", LOCAL_PLAYER)
if (coop_is_active()) then
on_take_damage("", REMOTE_PLAYER)
end
end
-- Show the enemy the player grapples with
function m02_run_catch_shaundi_first_time()
objective_text_clear(0)
M02_threads.clear_area_around_shaundi = thread_new("m02_thread_cleanup_area_around_shaundi")
-- The player's should be invulnerable to any enemy fire during this sequence (their camera is locked ont he plane)
turn_invulnerable(LOCAL_PLAYER)
if (coop_is_active()) then
turn_invulnerable(REMOTE_PLAYER)
end
-- Stop streamers
M02_runtime.stop_streamers = true
-- Kill the thead processing the plane circling, and teleport it to it's new ram path
if (M02_threads.cargo_plane_flying ~= -1) then
thread_kill(M02_threads.cargo_plane_flying)
end
-- Tell code to start the Shaundi catching sequence
skydive_set_catching_shaundi(true, true)
-- Wait for the animation to start in code
while(action_play_synced_is_finished(LOCAL_PLAYER, M02_groups.homies_skydive_01.shaundi, 1.0) == true) do
thread_yield()
end
-- Hide the HUD
m02_helper_hide_hud(true)
while (action_play_synced_is_finished(LOCAL_PLAYER, M02_groups.homies_skydive_01.shaundi, 0.08148) == false) do
thread_yield()
end
-- Start the plane approach audio
thread_new("m02_thread_process_plane_approach_audio")
while (action_play_synced_is_finished(LOCAL_PLAYER, M02_groups.homies_skydive_01.shaundi, 0.11) == false) do
thread_yield()
end
-- Change the wind audio
audio_object_post_event("M02_Parachute_Open_Wind_Change", nil, nil, LOCAL_PLAYER)
-- Stop the cloud particle effects
m02_helper_create_global_effects(true, true, false, true)
-- Start a new downward debris flow (since the player now has their parachute open)
-- NOTE: (MLG) This debris flow must cleanup objects behind the emitter, otherwise we're at risk of objects going
-- outside the broadphase, which is bad.
debris_flow_set_active(M02_debris_flows[1].handle, M02_navpoints.debris_emitter_pos_02, 20.0, 40.0, 375.0, LOCAL_PLAYER, 2.0, true, true)
debris_flow_set_active(M02_debris_flows[2].handle, M02_navpoints.debris_emitter_pos_02, 20.0, 40.0, 375.0, LOCAL_PLAYER, 2.0, true, true)
debris_flow_set_active(M02_debris_flows[3].handle, M02_navpoints.debris_emitter_pos_02, 20.0, 40.0, 375.0, LOCAL_PLAYER, 2.0, true, true)
m02_cleanup_catch_shaundi_first_time()
end
-- Clean-up catching Shaundi and pulling opening the parachute
function m02_cleanup_catch_shaundi_first_time()
-- NOTHING TO DO
end
function m02_thread_process_plane_approach_audio()
audio_object_post_event("Mission", { "Mission_Number", "Mission_02" }, { "Mission_02", "Plane_Approach" }, LOCAL_PLAYER)
thread_yield()
-- Wait until the plane has reached it's first destination
while(vehicle_anim_finished(M02_groups.plane.vehicle, 0.763) == false) do
thread_yield()
end
audio_object_post_event("M02_Score_Plane_Approach", nil, nil, LOCAL_PLAYER)
end
-- Ram the cargo plane into the player
function m02_run_ram_player_with_plane()
-- Wait for the animation to get to the point where Shaundi should screem
while (action_play_synced_is_finished(LOCAL_PLAYER, M02_groups.homies_skydive_01.shaundi, 0.93) == false) do
thread_yield()
end
camera_restrict_set_limits(-3, 3, -3, 3)
camera_restrict_target_object(M02_groups.plane.vehicle, 0, nil, -4.0, -48.0, 0.0)
camera_restrict_set_active(true)
-- Wait for the synced animation to stop
while (action_play_synced_is_finished(LOCAL_PLAYER, M02_groups.homies_skydive_01.shaundi, 0.96) == false) do
thread_yield()
end
m02_helper_hide_hud(false)
delay(1.0)
-- Update the objective text, and add a marker on the plane
marker_add(M02_groups.plane.vehicle, MINIMAP_ICON_KILL, OI_ASSET_KILL, OI_FLAGS_DEFAULT, SYNC_ALL)
objective_text(0, M02_strings.objective_shoot_plane, nil, nil, SYNC_ALL, OI_ASSET_KILL)
-- Register a callback for the cargo plane, so we know when the player shoots the windshield
on_take_damage("m02_cargo_plane_damaged_cb", M02_groups.plane.vehicle)
-- Wait until the plane has reached it's first destination
while(vehicle_anim_finished(M02_groups.plane.vehicle, 0.995) == false) do
thread_yield()
end
-- Clear the Shaundi catching sequence in code
skydive_set_catching_shaundi(false, false)
-- Check for failure
if (vehicle_component_is_shattered(M02_groups.plane.vehicle, M02_groups.plane.windshield_component) == false) then
-- Play the failure animation
action_play_do(LOCAL_PLAYER, "m02 shootqte end a")
if (coop_is_active()) then
action_play_do(REMOTE_PLAYER, "m02 shootqte end a")
end
-- Wait a bit, then kill the players
if (coop_is_active()) then
character_parachute_detach(REMOTE_PLAYER)
character_kill(REMOTE_PLAYER, true)
end
character_parachute_detach(LOCAL_PLAYER)
character_kill(LOCAL_PLAYER, true)
-- Spin the mission script, until the mission ends due to the player's death
while (true) do
thread_yield()
end
end
-- Put the player back into the normal skydiving state
character_set_skydiving_state(LOCAL_PLAYER, 0, false)
if (coop_is_active() == true) then
character_set_skydiving_state(REMOTE_PLAYER, 0, false)
end
m02_cleanup_ram_player_with_plane()
end
-- Callback for when the cargo plane is damaged
--
-- damaged_obj: Object that was damaged (the cargo plane)
-- attacker: Human that caused the damage
-- hitpoint_pct: Remaining hitpoint percentage after the damage was applied
function m02_cargo_plane_damaged_cb(damaged_obj, attacker, hitpoint_pct)
if (attacker == LOCAL_PLAYER or attacker == REMOTE_PLAYER) then
-- If we've gone over the windshield hitpoints, then detach the windshield (make it shatter
if ((1.0 - hitpoint_pct) * get_max_hit_points(M02_groups.plane.vehicle) >= M02_tweak_values.cargo_plane_windshield_hp) then
vehicle_component_shatter_glass(M02_groups.plane.vehicle, M02_groups.plane.windshield_component)
on_take_damage("", M02_groups.plane.vehicle)
end
end
end
-- Clean-up ramming the player with the cargo plane
function m02_cleanup_ram_player_with_plane()
m02_helper_disable_trigger(M02_triggers.final_dive_01)
-- Remove the marker on the cargo plane, and remove the damage callback
marker_remove(M02_groups.plane.vehicle)
-- Remove the restricted camera
camera_restrict_set_active(false)
skydive_set_catching_shaundi(false, false)
-- Make the player(s) vulnerable again
turn_vulnerable(LOCAL_PLAYER)
player_script_controlled_clear(LOCAL_PLAYER)
character_parachute_detach(LOCAL_PLAYER)
if (coop_is_active()) then
turn_vulnerable(REMOTE_PLAYER)
player_script_controlled_clear(REMOTE_PLAYER)
character_parachute_detach(REMOTE_PLAYER)
end
-- Remove the on damage callback for the cargo plane
on_take_damage("", M02_groups.plane.vehicle)
if (group_is_loaded(M02_groups.homies_skydive_01.name) == true) then
group_hide(M02_groups.homies_skydive_01.name)
end
thread_kill(M02_threads.clear_area_around_shaundi)
end
-- Play the Z-scene of the player(s) flying through the cargo plane
function m02_run_fly_through_plane_scene()
-- Post an event to duck all other audio effects
audio_object_post_event("M02_Zscene_02_Stop_All_Audio", nil, nil, LOCAL_PLAYER)
-- Start playing the cargo bay wind VFX
M02_vfx.cargo_bay_wind.handle = effect_play(M02_vfx.cargo_bay_wind.name, true)
thread_new("m02_thread_hide_stuff_for_plane_fly_through")
-- Remove player(s) parachute backpack
player_parachute_wear_backpack(LOCAL_PLAYER, false)
if (coop_is_active() == true) then
player_parachute_wear_backpack(REMOTE_PLAYER, false)
end
-- Play the bink (wait two frames, one for the effects to stop, and another for the effect objects to get destroyed)
cutscene_play(M02_zscenes.fly_through, nil, M02_navpoints.player_start_chk_03)
-- Handle if the cutscene was skipped
if (cutscene_was_skipped() == true) then
audio_object_post_event("M02_Score_Skip_Z02_02", nil, nil, LOCAL_PLAYER)
end
-- Turn back on the freefall wind
audio_object_post_event("M02_Back_To_FreeFall_Wind", nil, nil, LOCAL_PLAYER)
m02_cleanup_fly_through_plane_scene()
end
function m02_thread_hide_stuff_for_plane_fly_through()
delay(0.1)
-- Destroy the cargo plane
group_destroy(M02_groups.plane.name)
-- Stop any debris flow
for i, debris_flow in pairs(M02_debris_flows) do
if (debris_flow.handle ~= 0) then
debris_flow_set_inactive(debris_flow.handle)
end
end
end
-- Cleanup for the free falling section of the mission
function m02_cleanup_fly_through_plane_scene()
if (M02_vfx.cargo_bay_wind.handle ~= -1) then
effect_stop(M02_vfx.cargo_bay_wind.handle)
M02_vfx.cargo_bay_wind.handle = -1
end
-- Clean-up some groups
if (group_is_loaded(M02_groups.sky_goons_01.name)) then
group_destroy(M02_groups.sky_goons_01.name)
end
if (group_is_loaded(M02_groups.sky_goons_01_a.name)) then
group_destroy(M02_groups.sky_goons_01_a.name)
end
if (group_is_loaded(M02_groups.sky_goons_02.name)) then
group_destroy(M02_groups.sky_goons_02.name)
end
if (group_is_loaded(M02_groups.sky_goons_03.name)) then
group_destroy(M02_groups.sky_goons_03.name)
end
-- Kill any threads that might still be active
if (M02_threads.cargo_plane_flying ~= -1) then
thread_kill(M02_threads.cargo_plane_flying)
M02_threads.cargo_plane_flying = -1
end
end
-- Setup for the final dive checkpoint
function m02_run_setup_final_dive()
-- Make sure the player(s) are skydiving in the normal state state
character_set_skydiving(LOCAL_PLAYER, true)
character_set_skydiving_state(LOCAL_PLAYER, 0, false)
player_parachute_wear_backpack(LOCAL_PLAYER, true)
if (coop_is_active() == true) then
character_set_skydiving(REMOTE_PLAYER, true)
character_set_skydiving_state(REMOTE_PLAYER, 0, false)
player_parachute_wear_backpack(REMOTE_PLAYER, true)
end
-- Give the players the SMG, equip it, and disable all other inventory slots
inv_weapon_remove_temporary(LOCAL_PLAYER, M02_tweak_values.skydiving_smg)
inv_weapon_add_temporary(LOCAL_PLAYER, M02_tweak_values.skydiving_smg, 1, true, true, true) -- SMG has unlimited ammo and is equipped immediatly, and dual weidling
inv_weapon_remove_temporary(REMOTE_PLAYER, M02_tweak_values.skydiving_smg)
inv_weapon_add_temporary(REMOTE_PLAYER, M02_tweak_values.skydiving_smg, 1, true, true, true) -- SMG has unlimited ammo and is equipped immediatly, and dual weidling
inv_weapon_disable_all_but_this_slot(WEAPON_SLOT_SMG)
-- Do common skydiving setup
m02_helper_setup_skydiving_common()
-- Shaundi has already been created, just make sure she's in the correct position
teleport(M02_groups.homies_skydive_01.shaundi, M02_navpoints.shaundi_skydive_pos)
-- Set the boxes on fire
for i, box in pairs(M02_movers.debris_box_list) do
if (rand_int(1, 10) < 2) then
M02_vfx.debris_fire.box_handles[i] = effect_play_on_script_object(M02_vfx.debris_fire.name, box, nil, true)
end
end
-- Start the global VFX
m02_helper_create_global_effects(false, true, true, false)
-- Start some BIG thund-dah
audio_object_post_event("M02_Thunder_BIG", nil, nil, LOCAL_PLAYER)
skydive_set_elev_angles_deg(-30, -5) -- was -50,-25
-- Setup the cargo plane
if (group_is_loaded(M02_groups.plane.name) == false) then
group_create(M02_groups.plane.name, true)
vehicle_set_special_override_never_ghost(M02_groups.plane.vehicle, true)
turn_invulnerable(M02_groups.plane.vehicle)
end
group_show(M02_groups.plane.name)
vehicle_set_keyframed_physics(M02_groups.plane.vehicle, false) -- make sure the vehicle isn't still set as keyframed physics
vehicle_max_speed(M02_groups.plane.vehicle, 500.0)
vehicle_enter_teleport(M02_groups.plane.chars[1], M02_groups.plane.vehicle, 0, true)
teleport_vehicle(M02_groups.plane.vehicle, M02_navpoints.cargo_plane_fly_away_start)
set_min_hit_points(M02_groups.plane.vehicle, (get_max_hit_points(M02_groups.plane.vehicle) * 0.5))
M02_threads.cargo_plane_retreat = thread_new("m02_thread_process_cargo_plane_retreat")
-- Enable exterior rendering for both the interior volumes, so we can see the city
for i, interior in pairs(M02_interiors) do
interior_disable_exterior(interior, false)
end
end
-- Wait for the player(s) to kill the enemies in group 05
function m02_run_kill_sky_goons_05()
-- Update the objective text to kill all the pursuers
objective_text(0, M02_strings.objective_kill_pursuers, nil, nil, SYNC_ALL, OI_ASSET_KILL)
-- Start streamers again
M02_runtime.stop_streamers = false
-- Create and setup the group of guys immediatly after the player is rammed by the cargo plane
group_create_hidden(M02_groups.sky_goons_05.name, true)
delay(0.5)
-- Process the set of enemies
M02_runtime.sky_goons_alive = 0
group_show(M02_groups.sky_goons_05.name)
m02_helper_set_sky_goon_group(M02_groups.sky_goons_05, true, 2.0)
delay(2.5)
audio_play_persona_line(LOCAL_PLAYER, M02_dialog_lines.flew_through_plane)
while(M02_runtime.sky_goons_alive > 0) do
thread_yield()
end
-- Clean-up
m02_cleanup_kill_sky_goons_05()
end
-- Clean-up for killing sky goons 05
function m02_cleanup_kill_sky_goons_05()
m02_helper_cleanup_sky_goon_group(M02_groups.sky_goons_05)
end
-- Run the final QTE, just before the second (final) vertical dive
function m02_run_final_dive_qte()
-- Create the enemies, and set them skydiving
group_create_hidden(M02_groups.sky_goons_07.name, true)
for i, char in pairs (M02_groups.sky_goons_07.chars) do
character_set_never_fall(char, true)
character_set_skydiving(char, true)
end
if (coop_is_active() == true) then
-- Setup the final QTE for the remote player
group_create_hidden(M02_groups.sky_goons_07_A.name, true)
for i, char in pairs (M02_groups.sky_goons_07_A.chars) do
character_set_never_fall(char, true)
character_set_skydiving(char, true)
end
end
-- Add the objective text to catch Shaundi
objective_text(0, M02_strings.objective_catch_shaundi, nil, nil, SYNC_ALL, OI_ASSET_LOCATION)
marker_add(M02_groups.homies_skydive_01.shaundi, MINIMAP_ICON_LOCATION, OI_ASSET_LOCATION, OI_FLAGS_DEFAULT, SYNC_ALL)
delay(1.5)
-- Move into the non-combat dive
character_set_skydiving_state(LOCAL_PLAYER, 6, true) -- lock him in the "non combat dive" state
if (coop_is_active() == true) then
character_set_skydiving_state(REMOTE_PLAYER, 6, true) -- lock him in the "non combat dive" state
end
-- Wait a bit so that the players have flipped over before showing the QTE enemies
delay(2.0)
group_show(M02_groups.sky_goons_07.name)
if (coop_is_active() == true) then
group_show(M02_groups.sky_goons_07_A.name)
end
-- Wait just a tad longer
delay(1.0)
-- Stop streamers
M02_runtime.stop_streamers = true
-- Setup for the final QTE
M02_flags.local_final_grapple_finished = false
M02_flags.remote_final_grapple_finished = false
if (coop_is_active()) then
M02_flags.remote_final_grapple_finished = false
set_ignore_ai_flag(M02_groups.sky_goons_07_A.grapple_enemy, true)
human_skydive_create_npc_backpack(M02_groups.sky_goons_07_A.grapple_enemy)
teleport_to_object(M02_groups.sky_goons_07_A.grapple_enemy, REMOTE_PLAYER, false, false, 0.0, 5.0, 0.0)
character_set_skydiving_state(REMOTE_PLAYER, 1, true, nil, true)
qte_start(REMOTE_PLAYER, "Mission02_SKYQTE3", "m02_cb_remote_final_grapple_finished", M02_groups.sky_goons_07_A.grapple_enemy)
end
-- Setup the final QTE for the local player
set_ignore_ai_flag(M02_groups.sky_goons_07.grapple_enemy, true)
human_skydive_create_npc_backpack(M02_groups.sky_goons_07.grapple_enemy)
teleport_to_object(M02_groups.sky_goons_07.grapple_enemy, LOCAL_PLAYER, false, false, 0.0, 5.0, 0.0)
character_set_skydiving_state(LOCAL_PLAYER, 1, true, nil, true)
qte_start(LOCAL_PLAYER, "Mission02_SKYQTE3", "m02_cb_local_final_grapple_finished", M02_groups.sky_goons_07.grapple_enemy)
delay(1.0)
audio_play_persona_line(LOCAL_PLAYER, "M02_Air_Flight_02")
-- Wait until the QTE(s) have finished
while(M02_flags.local_final_grapple_finished == false and M02_flags.remote_final_grapple_finished == false) do
thread_yield()
end
-- Start streamers again
M02_runtime.stop_streamers = false
m02_cleanup_final_dive_qte()
end
-- Clean-up for the last QTE before the final dive
function m02_cleanup_final_dive_qte()
-- NOTHING TO DO HERE, AS OF NOW
end
-- Do the final dive to catch up to Shaundi
function m02_run_final_dive()
-- Kick off a thread to figure out when to trigger the music transition for cataching Shaundi
M02_threads.catach_shaundi_audio = thread_new("m02_thread_process_catch_shaundi_music", "M02_Score_End", M02_tweak_values.music_trigger_shuandi_catch_time_02)
-- Enable the trigger to create the second wave of debris
trigger_enable(M02_triggers.stop_dive_debris, true)
on_trigger("m02_cd_triggered_start_secondary_debris", M02_triggers.stop_dive_debris)
-- Create the dive debris flow, and set some of the boxes ablaze
debris_flow_set_active(M02_debris_flows[1].handle, M02_navpoints.debris_emitter_pos_01, 0.0, 25.0, 250.0, LOCAL_PLAYER, 1.0, true)
debris_flow_set_active(M02_debris_flows[2].handle, M02_navpoints.debris_emitter_pos_01, 0.0, 20.0, 250.0, LOCAL_PLAYER, 1.0, true)
-- Add fire to some of the boxes
for i, box in pairs(M02_movers.debris_dive_boxes) do
if (rand_int(1, 10) < 4) then
M02_vfx.debris_fire.dive_box_handles[i] = effect_play_on_script_object(M02_vfx.debris_fire.name, box, nil, true)
end
end
-- Force the player into a vertical dive
character_set_skydiving_state(LOCAL_PLAYER, 1, true)
on_take_damage("m02_player_damaged_while_diving_cb", LOCAL_PLAYER)
if (coop_is_active()) then
character_set_skydiving_state(REMOTE_PLAYER, 1, true)
on_take_damage("m02_player_damaged_while_diving_cb", REMOTE_PLAYER)
end
-- Setup the first trigger region, and just wait until the player get there
M02_flags.triggered_final_dive_01 = false
on_trigger("m02_cb_triggered_final_dive_01", M02_triggers.final_dive_01)
trigger_enable(M02_triggers.final_dive_01, true)
delay(4.0)
audio_play_persona_line(LOCAL_PLAYER, M02_dialog_lines.falling_shit)
-- Hide the cargo plane
vehicle_hide(M02_groups.plane.vehicle, true)
while(M02_flags.triggered_final_dive_01 == false) do
thread_yield()
end
m02_cleanup_final_dive()
end
-- Clean-up for the final dive (last clean-up function called for the third checkpoint, and general clean-up for this checkpoint happens here)
function m02_cleanup_final_dive()
-- Remove the marker from Shaundi
marker_remove(M02_groups.homies_skydive_01.shaundi)
-- Disable the final dive trigger
trigger_enable(M02_triggers.stop_dive_debris, false)
on_trigger("", M02_triggers.stop_dive_debris)
on_take_damage("", LOCAL_PLAYER)
if (coop_is_active()) then
on_take_damage("", REMOTE_PLAYER)
end
end
-- Show the enemy the player grapples with
function m02_run_catch_shaundi_again()
objective_text_clear(0)
M02_threads.clear_area_around_shaundi = thread_new("m02_thread_cleanup_area_around_shaundi")
-- The player's should be invulnerable to any enemy fire during this sequence (their camera is locked ont he plane)
turn_invulnerable(LOCAL_PLAYER)
if (coop_is_active()) then
turn_invulnerable(REMOTE_PLAYER)
end
-- Stop streamers
M02_runtime.stop_streamers = true
character_set_skydiving_state(M02_runtime.shaundi_caught_by, 0, true)
-- Tell code to start the Shaundi catching sequence
skydive_set_catching_shaundi(true, false)
-- Wait for the animation to start in code
while(action_play_synced_is_finished(LOCAL_PLAYER, M02_groups.homies_skydive_01.shaundi, 1.0) == true) do
thread_yield()
end
m02_helper_hide_hud(true)
while (action_play_synced_is_finished(LOCAL_PLAYER, M02_groups.homies_skydive_01.shaundi, 0.11) == false) do
thread_yield()
end
-- Change the wind audio
audio_object_post_event("M02_Parachute_Open_Wind_Change", nil, nil, LOCAL_PLAYER)
delay(0.2)
-- Start a new downward debris flow (since the player now has their parachute open)
-- NOTE: (MLG) This debris flow must cleanup objects behind the emitter, otherwise we're at risk of objects going
-- outside the broadphase, which is bad.
debris_flow_set_active(M02_debris_flows[1].handle, M02_navpoints.debris_emitter_pos_02, 20.0, 40.0, 300.0, LOCAL_PLAYER, 2.0, true, true)
debris_flow_set_active(M02_debris_flows[2].handle, M02_navpoints.debris_emitter_pos_02, 20.0, 40.0, 300.0, LOCAL_PLAYER, 2.0, true, true)
debris_flow_set_active(M02_debris_flows[3].handle, M02_navpoints.debris_emitter_pos_02, 20.0, 40.0, 300.0, LOCAL_PLAYER, 2.0, true, true)
m02_cleanup_catch_shaundi_again()
end
-- Clean-up catching Shaundi and pulling opening the parachute
function m02_cleanup_catch_shaundi_again()
thread_kill(M02_threads.clear_area_around_shaundi)
end
-- ***************************************************
-- Miscellaneous m02 Helper Funcrtions
-- ***************************************************
-- Disable a trigger
--
-- trigger_name: Name of the trigger to be disabled
function m02_helper_disable_trigger(trigger_name)
on_trigger("", trigger_name)
trigger_enable(trigger_name, false)
marker_remove_trigger(trigger_name, SYNC_ALL)
end
function m02_helper_cleanup_sky_goon_group(group_table)
for i, char in pairs(group_table.chars) do
on_death("", char.name)
marker_remove(char.name)
for i, vfx_handle in pairs(char.vfx_handles) do
effect_stop(vfx_handle)
end
end
end
-- Hide or unhide the hud
--
-- hide: TRUE to hide the HUD, FALSE to unhide it
function m02_helper_hide_hud(hide)
if (hide == true) then
if (M02_hud_states.hide_hud == -1) then
M02_hud_states.hide_hud = hud_display_create_state()
hud_display_set_element(M02_hud_states.hide_hud, HUD_ALL_ELEM, HUD_FADE_HIDDEN)
hud_display_commit_state(M02_hud_states.hide_hud)
end
else
if (M02_hud_states.hide_hud ~= -1) then
hud_display_remove_state(M02_hud_states.hide_hud)
M02_hud_states.hide_hud = -1
end
end
end
-- Common setup for the skydiving sections
function m02_helper_setup_skydiving_common(do_teleport)
-- Clear the objective text
objective_text_clear(0)
-- Set the global wind override
wind_override_set(0.0, 1.0, 0.0, 1.0)
-- Start the first debris field flowing
debris_flow_set_active(M02_debris_flows[1].handle, M02_navpoints.debris_emitter_pos_01, 10.0, 25.0, 400.0, LOCAL_PLAYER, 1.0, false, true)
-- Player's can never catch on fire while skydiving
character_set_never_catch_fire(LOCAL_PLAYER, true)
if (coop_is_active() == true) then
character_set_never_catch_fire(REMOTE_PLAYER, true)
end
-- Create the new homie group, for Shaundi
if (group_is_loaded(M02_groups.homies_skydive_01.name) == false) then
group_create_hidden(M02_groups.homies_skydive_01.name, true)
party_add(M02_groups.homies_skydive_01.shaundi, LOCAL_PLAYER)
set_never_turn_on_player(M02_groups.homies_skydive_01.shaundi, true)
follower_make_independent(M02_groups.homies_skydive_01.shaundi, true)
end
group_show(M02_groups.homies_skydive_01.name)
character_set_skydiving(M02_groups.homies_skydive_01.shaundi, true)
character_set_skydiving_state(M02_groups.homies_skydive_01.shaundi, 7, true)
character_set_never_catch_fire(M02_groups.homies_skydive_01.shaundi, true)
character_prevent_flinching(M02_groups.homies_skydive_01.shaundi, true)
set_dont_attack_me_on_sight_flag(M02_groups.homies_skydive_01.shaundi, true)
inv_item_remove_all(M02_groups.homies_skydive_01.shaundi)
set_ignore_ai_flag(M02_groups.homies_skydive_01.shaundi, true)
-- Start a thread to process the vehicle audio whooshes
if (M02_threads.object_whoosh == -1) then
M02_threads.object_whoosh = thread_new("m02_thread_process_skydive_object_whoosh")
end
-- Hide the minimap
if (M02_hud_states.hide_minimap == -1) then
M02_hud_states.hide_minimap = hud_display_create_state()
hud_display_set_element(M02_hud_states.hide_minimap, HUD_ELEM_MINIMAP, HUD_FADE_HIDDEN)
hud_display_commit_state(M02_hud_states.hide_minimap)
end
M02_threads.kill_fallen_players = thread_new("m02_thread_kill_players_that_fall_too_far")
end
function m02_helper_create_debris_flow(debris_flow_table, hidden)
-- Create a new flow
debris_flow_table.handle = debris_flow_create()
-- Create the vehicle group
if (group_is_loaded(debris_flow_table.vehicle_group.name) == false) then
if (hidden == true) then
group_create_hidden(debris_flow_table.vehicle_group.name, true)
else
group_create(debris_flow_table.vehicle_group.name, true)
end
end
-- Add the vehicle debris to the flow
local vehicle_min_speed = 10.0
local vehicle_max_speed = 14.0
local vehicle_max_ang_vel = 4.0
for i, vehicle in pairs(debris_flow_table.vehicle_group.vehicles) do
-- Setup the vehicle
vehicle_disable_explosion_and_damage_vfx(vehicle.name, true)
vehicle_skydiving_start(vehicle.name)
on_damage("m02_cb_debris_vehicle_damaged", vehicle.name, 0.2)
on_vehicle_destroyed("m02_cb_debris_vehicle_destroyed", vehicle.name)
on_vehicle_debris_flow_recycled("m02_cb_debris_vehicle_recycled", vehicle.name)
-- Add the vehicle to the debris flow
debris_flow_add_object(debris_flow_table.handle, vehicle.name, vehicle_min_speed, vehicle_max_speed, vehicle_max_ang_vel, true, true)
-- Turn the headlights on
vehicle_lights_on(vehicle.name, true)
end
-- Set all the boxes floating
local box_min_speed = 14.0
local box_max_speed = 18.0
local box_max_ang_vel = 3.0
for i, box in pairs(debris_flow_table.boxes) do
--mesh_mover_reset(box)
debris_flow_add_object(debris_flow_table.handle, box, box_min_speed, box_max_speed, box_max_ang_vel, true, true)
end
for i, pallet in pairs(debris_flow_table.pallets) do
--mesh_mover_reset(pallet)
debris_flow_add_object(debris_flow_table.handle, pallet, box_min_speed, box_max_speed, box_max_ang_vel, true, true)
end
-- Add Shaundi to the avoid object list
debris_flow_add_avoid_object(debris_flow_table.handle, M02_groups.homies_skydive_01.shaundi)
end
function m02_cd_triggered_start_secondary_debris()
m02_helper_disable_trigger(M02_triggers.stop_dive_debris)
-- Reset all the movers in the 3rd flow
for i, box in pairs(M02_debris_flows[3].boxes) do
mesh_mover_reset(box)
end
for i, pallet in pairs(M02_debris_flows[3].pallets) do
mesh_mover_reset(pallet)
end
debris_flow_set_active(M02_debris_flows[3].handle, M02_navpoints.debris_emitter_pos_01, 0.0, 15.0, 250.0, LOCAL_PLAYER, 1.0, true)
end
-- Function to create all of the global skydiving effects
--
-- show_city: TRUE to show the city texture, FALSE otherwise
-- show_clouds: TRUE to show the clouds above the city texture, FALSE otherwise
-- show_player_vfx: TRUE to show the VFX that attach to the players, false otherwise
-- show_slow_clouds: TRUE to show the slow clouds
function m02_helper_create_global_effects(show_city, show_clouds, show_player_vfx, show_slow_clouds)
-- Start the global wind effects
if (M02_vfx.skydive_city.handle == -1) then
if (show_city == true) then
M02_vfx.skydive_city.handle = effect_play(M02_vfx.skydive_city.nav, true, SYNC_ALL)
end
else
if (show_city == false) then
effect_stop(M02_vfx.skydive_city.handle)
M02_vfx.skydive_city.handle = -1
end
end
if (M02_vfx.skydive_clouds_below.handle == -1) then
if (show_clouds == true) then
M02_vfx.skydive_clouds_below.handle = effect_play(M02_vfx.skydive_clouds_below.nav, true, SYNC_ALL)
end
else
if (show_clouds == false) then
effect_stop(M02_vfx.skydive_clouds_below.handle)
M02_vfx.skydive_clouds_below.handle = -1
end
end
-- Play debris effects on the players
if (M02_vfx.skydive_player_clouds.handles[1] == -1) then
if (show_player_vfx == true) then
M02_vfx.skydive_player_clouds.handles[1] = effect_play_on_human(M02_vfx.skydive_player_clouds.nav, LOCAL_PLAYER, nil, true, SYNC_LOCAL)
end
else
if (show_player_vfx == false) then
effect_stop(M02_vfx.skydive_player_clouds.handles[1])
M02_vfx.skydive_player_clouds.handles[1] = -1
end
end
if (coop_is_active() == true and M02_vfx.skydive_player_clouds.handles[2] == -1) then
if (show_player_vfx == true) then
M02_vfx.skydive_player_clouds.handles[2] = effect_play_on_human(M02_vfx.skydive_player_clouds.nav, REMOTE_PLAYER, nil, true, SYNC_REMOTE)
end
else
if (show_player_vfx == false) then
effect_stop(M02_vfx.skydive_player_clouds.handles[2])
M02_vfx.skydive_player_clouds.handles[2] = -1
end
end
-- Play slow debris effects on the players
if (M02_vfx.skydive_player_slow_clouds.handles[1] == -1) then
if (show_slow_clouds == true) then
M02_vfx.skydive_player_slow_clouds.handles[1] = effect_play_on_human(M02_vfx.skydive_player_slow_clouds.nav, LOCAL_PLAYER, nil, true, SYNC_LOCAL)
end
else
if (show_slow_clouds == false) then
effect_stop(M02_vfx.skydive_player_slow_clouds.handles[1])
M02_vfx.skydive_player_slow_clouds.handles[1] = -1
end
end
if (coop_is_active() == true and M02_vfx.skydive_player_slow_clouds.handles[2] == -1) then
if (show_slow_clouds == true) then
M02_vfx.skydive_player_slow_clouds.handles[2] = effect_play_on_human(M02_vfx.skydive_player_slow_clouds.nav, REMOTE_PLAYER, nil, true, SYNC_REMOTE)
end
else
if (show_slow_clouds == false) then
effect_stop(M02_vfx.skydive_player_slow_clouds.handles[2])
M02_vfx.skydive_player_slow_clouds.handles[2] = -1
end
end
-- Kickoff the thread to generate the random clouds
if (M02_threads.cloud_generator == -1) then
if (show_player_vfx == true) then
M02_threads.cloud_generator = thread_new("m02_thread_generate_clouds")
end
else
if (show_player_vfx == false) then
thread_kill(M02_threads.cloud_generator)
M02_threads.cloud_generator = -1
end
end
end
-- Function to destroy all of the global skydiving effects
function m02_helper_destroy_global_effects()
-- Stop the looping effects
if (M02_vfx.skydive_city.handle ~= -1) then
effect_stop(M02_vfx.skydive_city.handle)
M02_vfx.skydive_city.handle = -1
end
if (M02_vfx.skydive_clouds_below.handle ~= -1) then
effect_stop(M02_vfx.skydive_clouds_below.handle)
M02_vfx.skydive_clouds_below.handle = -1
end
if (M02_vfx.skydive_cloud_puff.handle ~= -1) then
effect_stop(M02_vfx.skydive_cloud_puff.handle)
M02_vfx.skydive_cloud_puff.handle = -1
end
-- Stop debris effects on the players
if (M02_vfx.skydive_player_clouds.handles[1] ~= -1) then
effect_stop(M02_vfx.skydive_player_clouds.handles[1], SYNC_LOCAL)
end
if (coop_is_active() == true and M02_vfx.skydive_player_clouds.handles[2] ~= -1) then
effect_stop(M02_vfx.skydive_player_clouds.handles[2], SYNC_REMOTE)
end
-- Stop the slow clouds
if (M02_vfx.skydive_player_slow_clouds.handles[1] ~= -1) then
effect_stop(M02_vfx.skydive_player_slow_clouds.handles[1], SYNC_LOCAL)
end
if (coop_is_active() == true and M02_vfx.skydive_player_slow_clouds.handles[2] ~= -1) then
effect_stop(M02_vfx.skydive_player_slow_clouds.handles[2], SYNC_REMOTE)
end
-- Kill the cloud generator thread
if (M02_threads.cloud_generator ~= -1) then
thread_kill(M02_threads.cloud_generator)
M02_threads.cloud_generator = -1
end
end
-- Helper to setup a group of enemy skydivers
--
-- Group_table: Lua table defining the group of enemy skydivers (see M02_groups::sky_goons_01 for formatting example)
-- show_character: TRUE if the character should be unhidden
-- leash_dist: (optional) Set the leash distance (defaults to 3.0)
function m02_helper_set_sky_goon_group(Group_table, show_characters, leash_dist)
if (leash_dist == nil) then
leash_dist = 2.0
end
for i, char in pairs(Group_table.chars) do
on_death("m02_cb_sky_goon_killed", char.name)
M02_runtime.sky_goons_alive = M02_runtime.sky_goons_alive + 1
character_set_skydiving(char.name, true)
if (show_characters == true) then
character_show(char.name)
end
-- Give them a parachute
human_skydive_create_npc_backpack(char.name)
set_max_hit_points(char.name, (get_current_hit_points(char.name) * M02_tweak_values.goon_hitpoint_modifier) )
ai_add_enemy_target(char.name, CLOSEST_PLAYER, ATTACK_NOW_NEVER_LOSE, true)
-- Do we need to do a pathfind?
if (char.dest_nav ~= nil) then
npc_leash_to_nav(char.name, char.dest_nav, leash_dist)
thread_new("m02_thread_process_sky_goon_move", char)
end
-- Do we add the marker now or later?
if (char.delay_marker == false) then
marker_add(char.name, MINIMAP_ICON_KILL, OI_ASSET_KILL, OI_FLAGS_DEFAULT, SYNC_ALL)
end
end
end
-- Quick thread to process a single skydiving enemies pathfinding (so we can add a marker when it finished)
--
-- char: Human Lua table
function m02_thread_process_sky_goon_move(char)
if (char.streamers == true) then
m02_helper_create_character_streamers(char.name, true, char.vfx_handles)
end
-- Do the move to
skydive_move_to_do(char.name, char.dest_nav, false, 0, true, char.forced_dive)
while(skydive_move_to_check_done(char.name, char.dest_nav, false, 0) == false) do
thread_yield()
end
if (char.show_marker == true) then
if (character_is_dead(char.name) == false) then
marker_add(char, MINIMAP_ICON_KILL, OI_ASSET_KILL, OI_FLAGS_DEFAULT, SYNC_ALL)
end
end
for i, vfx_handle in pairs(char.vfx_handles) do
effect_stop(vfx_handle)
end
end
-- Check if a particular conversation is done
--
-- convo_table: Conversation Lua table (see M02_conversations)
function m02_helper_conversation_check_done(convo_table)
if (convo_table.queued == false) then
return true
else
return convo_table.ended
end
end
-- Helper to play conversations, by adding them to the cue
--
-- convo_table: Conversation Lua table (see M02_conversations)
-- priority: Desired priority for the conversation
-- delay: Amonut of time to dalya after this conversation before playing the next conversation (in seconds)
function m02_helper_conversation_play(convo_table, priority, delay)
convo_table.queued = true
convo_table.delay = delay
if (priority == M02_CONVO_PLAY_LAST) then
-- Insert this entry at the end of hte lsit
M02_runtime.convo_queue.list[M02_runtime.convo_queue.size] = convo_table
M02_runtime.convo_queue.size = M02_runtime.convo_queue.size + 1
elseif (priority == M02_CONVO_PLAY_NEXT) then
-- Shift the list to the right
for i = M02_runtime.convo_queue.size - 1, 0, -1 do
M02_runtime.convo_queue.list[i + 1] = M02_runtime.convo_queue.list[i]
end
M02_runtime.convo_queue.list[0] = convo_table
M02_runtime.convo_queue.size = M02_runtime.convo_queue.size + 1
elseif (priority == M02_CONVO_PLAY_IMMEDIATE) then
-- Stop any actively playing conversations, and start this one immediatly
m02_helper_conversation_cleanup_playing(true)
M02_runtime.convo_queue.playing.thread_id = thread_new("m02_thread_play_convo", convo_table)
end
end
-- Kill any active conversation, and clear out the conversation queue
function m02_helper_conversation_kill_all()
-- Stop any active conversation
m02_helper_conversation_cleanup_playing(false)
-- Clear out the queue
for i = 0, M02_runtime.convo_queue.size - 1 do
M02_runtime.convo_queue.list[i].ended = true
end
M02_runtime.convo_queue.size = 0
end
-- Cleanup any playing conversation
function m02_helper_conversation_cleanup_playing(wait_for_end)
-- Stop the thread processing a playing conversation
if (M02_runtime.convo_queue.playing.thread_id ~= -1) then
thread_kill(M02_runtime.convo_queue.playing.thread_id)
M02_runtime.convo_queue.playing.thread_id = -1
end
-- Mark the playing conversation as ended
if (M02_runtime.convo_queue.playing.convo_table ~= nil) then
M02_runtime.convo_queue.playing.convo_table.ended = true
end
-- Cleanup and conversation data for a playing conversation
if (M02_runtime.convo_queue.playing.conv_handle ~= INVALID_CONVERSATION_HANDLE) then
audio_conversation_end(M02_runtime.convo_queue.playing.conv_handle)
if (wait_for_end == true) then
audio_conversation_wait_for_end(M02_runtime.convo_queue.playing.conv_handle)
end
M02_runtime.convo_queue.playing.conv_handle = INVALID_CONVERSATION_HANDLE
end
end
-- Thread to process playing a single converation
function m02_thread_play_convo(convo_table)
-- Add this conversatin to the playing conversation data
M02_runtime.convo_queue.playing.convo_table = convo_table
convo_table.started = true -- it has now started
-- Load the conversation data
if (convo_table.load_direct == false) then
M02_runtime.convo_queue.playing.conv_handle = audio_conversation_load(convo_table.name)
else
M02_runtime.convo_queue.playing.conv_handle = audio_conversation_load_direct(convo_table.name)
end
-- Play the conversation and wait for it to end
audio_conversation_play(M02_runtime.convo_queue.playing.conv_handle)
audio_conversation_wait_for_end(M02_runtime.convo_queue.playing.conv_handle)
audio_conversation_end(M02_runtime.convo_queue.playing.conv_handle)
convo_table.completed = true
-- Delay the desired amount
delay(convo_table.delay)
-- Do some book-keeping
M02_runtime.convo_queue.playing.conv_handle = INVALID_CONVERSATION_HANDLE
convo_table.ended = true
end
-- Process the conversation queue (should be running the duration of the mission)
function m02_thread_process_conversations()
-- Loop forever
while(true) do
-- Wait until something is added to the queue
while(M02_runtime.convo_queue.size == 0 or M02_runtime.convo_queue.playing.conv_handle ~= INVALID_CONVERSATION_HANDLE) do
thread_yield()
end
-- Play the first conversation from the queue, and then remove it from the qeue
M02_runtime.convo_queue.playing.thread_id = thread_new("m02_thread_play_convo", M02_runtime.convo_queue.list[0])
for i = 1, M02_runtime.convo_queue.size do
M02_runtime.convo_queue.list[i - 1] = M02_runtime.convo_queue.list[i]
end
M02_runtime.convo_queue.size = M02_runtime.convo_queue.size - 1
-- Wait for the active conversation thread to end
while(thread_check_done(M02_runtime.convo_queue.playing.thread_id) == false) do
thread_yield()
end
end
end
-- Helper to check if a human is ready to play a custom animation
--
-- char_name: Name of the characer
--
-- returns: TRUE if the character is ready to play an animation, FALSE otherwise
function m02_helper_human_can_play_anim(char_name)
-- Check if they're ready (checks that they aren't dead, ragdolled
if (character_is_ready(char_name) == false) then
return false
end
if (character_playing_combat_move(char_name) == true) then
return false
end
if (character_is_jumping(char_name) == true) then
return false
end
return true
end
-- Wait until both players are not playing a synced animation
function m02_helper_turbulance_wait_for_anims()
-- Wait until none of the player's are playing a combat mobe
local playing_anim = true
local add_small_delay = false
while(playing_anim == true) do
playing_anim = false
if (m02_helper_human_can_play_anim(LOCAL_PLAYER) == false) then
playing_anim = true
add_small_delay = true
elseif(coop_is_active() == true and (m02_helper_human_can_play_anim(REMOTE_PLAYER) == false)) then
playing_anim = true
add_small_delay = true
end
if (playing_anim == false and add_small_delay == true) then
delay(0.7)
playing_anim = true
add_small_delay = false
end
thread_yield()
end
end
-- Helper for common turbulance stuff
function m02_helper_turbulance_do_common()
-- Hide the HUD
m02_helper_hide_hud(true)
-- Trigger the turbulance VFX
local idx = rand_int(1, #(M02_vfx.cargo_dust.navs))
for i, nav in pairs(M02_vfx.cargo_dust.navs) do
effect_play(M02_vfx.cargo_dust.navs[idx])
delay(0.07)
idx = idx + 1
if (idx > #(M02_vfx.cargo_dust.navs)) then
idx = 1
end
end
-- Increase the wind
wind_override_set(0.0, 1.0, 0.0, 1.0)
-- Wait until the stumble has finished
while(action_play_is_finished(LOCAL_PLAYER) == false) do
thread_yield()
end
delay(0.1)
-- Back to normal
wind_override_set(0.0, 1.0, 0.0, 0.075)
m02_helper_hide_hud(false)
end
-- Search for a debris vehicle Lua table, by vehicle name
function m02_helper_find_debris_vehicle(vehicle_name)
-- Find this vehicle in the debris vehicle lists
for i, vehicle in pairs(M02_groups.debris_vehicles.vehicles) do
if (vehicle.name == vehicle_name) then
-- We found the vehicle
return vehicle
end
end
for i, vehicle in pairs(M02_groups.debris_vehicles_dive_01.vehicles) do
if (vehicle.name == vehicle_name) then
-- We found the vehicle
return vehicle
end
end
for i, vehicle in pairs(M02_groups.debris_vehicles_dive_02.vehicles) do
if (vehicle.name == vehicle_name) then
-- We found the vehicle
return vehicle
end
end
return nil
end
-- Helper to clean-up a debris vehicle
--
-- vehicle_table: Lua table defining the vehicle
function m02_helper_cleanup_debris_vehicle(vehicle_table)
-- Clean-up any audio events
if (vehicle_table.whoosh_event ~= INVALID_MESSAGE_HANDLE) then
audio_stop(vehicle_table.whoosh_event)
vehicle_table.whoosh_event = INVALID_MESSAGE_HANDLE
end
-- Clean up any fire effects
if (vehicle_table.fire_handle ~= -1) then
effect_stop(vehicle_table.fire_handle)
vehicle_table.fire_handle = -1
end
-- Clean up any explosion VFX
if (vehicle_table.exp_handle ~= -1) then
effect_stop(vehicle_table.exp_handle)
vehicle_table.exp_handle = -1
end
-- Un-register any callbacks
on_vehicle_destroyed("", vehicle_table.name)
on_damage("", vehicle_table.name)
on_vehicle_debris_flow_recycled("", vehicle_table.name)
end
-- Create some streamers
--
-- character_name: Name of the character
function m02_helper_create_character_streamers(character_name, looping, vfx_handles)
for i, attach_point in pairs (M02_vfx.skydive_player_streamers.attach_points) do
local handle = effect_play_on_human(M02_vfx.skydive_player_streamers.nav, character_name, attach_point, looping, SYNC_LOCAL)
if (vfx_handles ~= nil) then
vfx_handles[i] = handle
end
end
end
-- Hide debris
function m02_helper_hide_debris()
--[[
-- Hide all the debris objects
for i, box in pairs(M02_movers.debris_box_list) do
mesh_mover_hide(box)
end
for i, pallet in pairs(M02_movers.debris_pallet_list) do
mesh_mover_hide(pallet)
end
--]]
for i, box in pairs(M02_movers.debris_dive_boxes) do
mesh_mover_hide(box)
end
for i, pallet in pairs(M02_movers.debris_dive_pallets) do
mesh_mover_hide(pallet)
end
for i, box in pairs(M02_movers.debris_dive_boxes_02) do
mesh_mover_hide(box)
end
for i, pallet in pairs(M02_movers.debris_dive_pallets_02) do
mesh_mover_hide(pallet)
end
end
-- *************************
--
-- Callback functions
--
-- *************************
-- Callback for when Shaundi is killed
function m02_cb_failure_shaundi_died()
mission_end_failure("m02", M02_strings.failure_shaundi_died)
end
--[[
-- Callback when the player is damaged, to show the melee "block" tutorial
function m02_cb_show_block_tutorial(player)
message(M02_strings.help_melee_block, 5.0)
on_take_damage("", LOCAL_PLAYER)
if (coop_is_active()) then
on_take_damage("", REMOTE_PLAYER)
end
end
--]]
--[[
-- Callback when the player damages an enemey, to show the melee "charged attack" tutorial
function m02_cb_show_charged_attack_tutorial(enemy)
message(M02_strings.help_melee_charged_attack)
-- Remove the on damage callback from each of the enemies
for i, char in pairs(M02_groups.cargo_plane_goons_01.chars) do
on_damage("", char, 0.0)
end
end
--]]
--[[
-- Callback when a vehicle in the cargo plane gets damaged
function m02_cb_cargo_vehicle_damaged(vehicle)
local alarm_chance = rand_int(1, 10)
if (alarm_chance <= 2) then
end
end
--]]
-- Callback when an enemy on the plane detects the player
function m02_cb_plane_goon_detected_player(detector, player)
-- Remove their leash
npc_leash_remove(detector)
-- Clear any callbacks
on_detection("", detector)
on_take_damage("", detector)
end
-- Callback when an enemy on the plane is damaged
function m02_cb_plane_goon_damaged(victim)
-- Remove their leash
npc_leash_remove(victim)
-- Clear any callbacks
on_detection("", victim)
on_take_damage("", victim)
end
-- Trigger for when the player enteres the parachtute location
function m02_cb_triggered_grab_parachute(player_name, trigger_name)
if(trigger_name == M02_triggers.cargo_plane_parachute_01) then
--m02_helper_disable_trigger(trigger_name)
M02_flags.reached_parachutes = true
end
end
-- Callback for when the local gun grapple QTE has finished (QTE1)
function m02_cb_local_gun_grapple_finished()
-- Unequip the enemy
--inv_item_remove_all(M02_groups.sky_goons_01.grapple_enemy)
-- Lock the player into the inverted skydiving state
character_set_skydiving_state(LOCAL_PLAYER, 3, true, M02_groups.sky_goons_01.grapple_enemy, true)
character_set_skydiving(M02_groups.sky_goons_01.grapple_enemy, true)
character_prevent_flinching(M02_groups.sky_goons_01.grapple_enemy, true)
character_allow_ragdoll(M02_groups.sky_goons_01.grapple_enemy, false)
M02_flags.local_gun_grapple_finished = true
-- Give the player the SMG, equip it, and disable all other inventory slots
inv_weapon_remove_temporary(LOCAL_PLAYER, M02_tweak_values.skydiving_smg)
inv_weapon_add_temporary(LOCAL_PLAYER, M02_tweak_values.skydiving_smg, 1, true, true, true) -- SMG has unlimited ammo and is equipped immediatly, and dual weidling
inv_weapon_disable_all_but_this_slot(WEAPON_SLOT_SMG, false, SYNC_LOCAL)
end
-- Callback for when the remote gun grapple QTE has finished (QTE2)
function m02_cb_remote_gun_grapple_finished()
-- Unequip the enemy
--inv_item_remove_all(M02_groups.sky_goons_01_a.grapple_enemy)
-- Lock the player into the inverted skydiving state
character_set_skydiving_state(REMOTE_PLAYER, 3, true, M02_groups.sky_goons_01_a.grapple_enemy, true)
character_set_skydiving(M02_groups.sky_goons_01_a.grapple_enemy, true)
character_prevent_flinching(M02_groups.sky_goons_01_a.grapple_enemy, true)
character_allow_ragdoll(M02_groups.sky_goons_01_a.grapple_enemy, false)
M02_flags.remote_gun_grapple_finished = true
-- Give the player the SMG, equip it, and disable all other inventory slots
inv_weapon_remove_temporary(REMOTE_PLAYER, M02_tweak_values.skydiving_smg)
inv_weapon_add_temporary(REMOTE_PLAYER, M02_tweak_values.skydiving_smg, 1, true, true, true) -- SMG has unlimited ammo and is equipped immediatly, and dual weidling
inv_weapon_disable_all_but_this_slot(WEAPON_SLOT_SMG, false, SYNC_REMOTE)
end
--[[
-- Callback for when the cargo plane has entered the ramming destination trigger
function m02_cb_cargo_plane_at_ram_dest_01(character_name, trigger_name)
if(trigger_name == M02_triggers.cargo_plane_ram_trigger_01) then
if (character_name == M02_groups.plane.chars[1]) then
m02_helper_disable_trigger(trigger_name)
M02_flags.cargo_plane_reached_ram_dest_01 = true
end
end
end
-- Callback for when the cargo plane has entered the ramming destination trigger
function m02_cb_cargo_plane_at_ram_dest_02(character_name, trigger_name)
if(trigger_name == M02_triggers.cargo_plane_ram_trigger_02) then
if (character_name == M02_groups.plane.chars[1]) then
m02_helper_disable_trigger(trigger_name)
M02_flags.cargo_plane_reached_ram_dest_02 = true
end
end
end
--]]
-- Callback for when a player is damage while diving to catch Shaundi
function m02_player_damaged_while_diving_cb(player_name, attacker, damage_amt, from_exp)
-- If this isn't from an explosion, then play the collision animation on the player (since this must have been from hitting debris)
if (from_exp == false) then
delay(0.1)
if (action_play_is_finished(player_name)) then
audio_object_post_event("object_impact", "object_type", "generic_medium_wood", player_name)
audio_play_persona_line(player_name, "voc_pain")
action_play(player_name, "FreeFall Collision")
end
end
end
-- Callback for when the player reaches the trigger at the end of the first dive to Shaundi (he has caught Shaundi)
function m02_cb_triggered_final_dive_01(player_name, trigger_name)
-- Remove the trigger callback
on_trigger("", M02_triggers.final_dive_01)
-- Keep track of who triggered the Shaundi catch sequence
M02_runtime.shaundi_caught_by = player_name
M02_runtime.shaundi_other_player = LOCAL_PLAYER
if (player_name == LOCAL_PLAYER) then
M02_runtime.shaundi_other_player = REMOTE_PLAYER
end
M02_flags.triggered_final_dive_01 = true
end
-- Callback for when an enemy in Group 02 or Group 03 is killed
function m02_cb_sky_goon_killed(enemy)
marker_remove(enemy)
on_death("",enemy)
M02_runtime.sky_goons_alive = M02_runtime.sky_goons_alive - 1
end
-- Callback when the local player finishes the last grapple QTE (QTE3)
function m02_cb_local_final_grapple_finished()
-- Lock the player into the diving state again
character_set_skydiving_state(LOCAL_PLAYER, 1, true, nil, true)
character_kill(M02_groups.sky_goons_07.grapple_enemy)
M02_flags.local_final_grapple_finished = true
end
-- Callback when the remote player finishes the last grapple QTE (QTE3)
function m02_cb_remote_final_grapple_finished()
-- Lock the player into the diving state again
character_set_skydiving_state(REMOTE_PLAYER, 1, true, nil, true)
character_kill(M02_groups.sky_goons_07_A.grapple_enemy)
M02_flags.remote_final_grapple_finished = true
end
-- Callback when one of the debris vehicles gets damaged
--
-- vehicle_name: Name of the vehicle that was damaged
function m02_cb_debris_vehicle_damaged(vehicle_name)
-- Remove the on_damage callback
on_damage("", vehicle_name)
-- Find the vehicle, and turn it on fire
local vehicle_table = m02_helper_find_debris_vehicle(vehicle_name)
if (vehicle_table ~= nil) then
if (vehicle_table.fire_handle == -1) then
-- We found the vehicle, spawn some fire on it
vehicle_table.fire_handle = effect_play_on_script_object(M02_vfx.debris_fire.name, vehicle_table.name, nil, true)
end
end
end
-- Callback when one of the debris vehicles gets destroyed
--
-- vehicle_name: Name of the vehicle that was destroyed
function m02_cb_debris_vehicle_destroyed(vehicle_name)
-- Remove the callback and trigger an explosion
on_vehicle_destroyed("", vehicle_name)
-- Find the vehicle and turn it on fire
local vehicle_table = m02_helper_find_debris_vehicle(vehicle_name)
if (vehicle_table ~= nil) then
if (vehicle_table.fire_handle == -1) then
-- We found the vehicle, spawn some fire on it
vehicle_table.fire_handle = effect_play_on_script_object(M02_vfx.debris_fire.name, vehicle_table.name, nil, true)
end
vehicle_table.exp_handle = explosion_create("Mission02 Car Exp", vehicle_table.name, nil, true, "vfx_car_explosion 001")
end
end
-- Callback when one of the debris vehicles gets recycled by the debris flow
--
-- vehicle_name: Name of the vehicle that was recycled
function m02_cb_debris_vehicle_recycled(vehicle_name)
local vehicle_table = m02_helper_find_debris_vehicle(vehicle_name)
if (vehicle_table ~= nil) then
-- We found the vehicle, stop any fire effect and setup the vehicle again
if (vehicle_table.fire_handle ~= -1) then
effect_stop(vehicle_table.fire_handle)
vehicle_table.fire_handle = -1
end
if (vehicle_table.exp_handle ~= -1) then
effect_stop(vehicle_table.exp_handle)
vehicle_table.exp_handle = -1
end
-- Turn of damage VFX and re-register for vehicle damaged and destroyed callbacks
vehicle_disable_explosion_and_damage_vfx(vehicle_table.name, true)
on_damage("m02_cb_debris_vehicle_damaged", vehicle_table.name, 0.2)
on_vehicle_destroyed("m02_cb_debris_vehicle_destroyed", vehicle_table.name)
end
end
-- Callback when a vehicle with a forced car alarm get's damaged
function m02_cb_trigger_car_alarm(vehicle_name)
on_damage("", vehicle_name)
turn_invulnerable(M02_groups.cargo_vehicles.car_alarm_vehicle)
audio_object_post_event("Vehicle_Alarm", "Vehicle_Model", "car_4dr_luxery06", vehicle_name)
end
-- *************************
--
-- Thread functions
--
-- *************************
-- Function to process shaundi's nags to the player to get moving
--
-- initial_delay: Amount of time (in seconds) to delay before first nagging the player
-- repeat_delay: Amount fo time (in seconds) to delay between nags
function m02_thread_process_shaundi_nag(initial_delay, repeat_delay)
delay(initial_delay)
-- Loop forever until the thread gets killed
while(true) do
audio_play_persona_line(M02_groups.homies_01.shaundi, M02_dialog_lines.shaundi_nags[M02_runtime.shaundi_nag_idx])
M02_runtime.shaundi_nag_idx = M02_runtime.shaundi_nag_idx + 1
if (M02_runtime.shaundi_nag_idx > #(M02_dialog_lines.shaundi_nags)) then
M02_runtime.shaundi_nag_idx = 1
end
delay(repeat_delay)
thread_yield()
end
end
-- Thread to process the circling path of the cargo plane, while it waits to ram the player
function m02_thread_process_cargo_plane_circling()
vehicle_set_special_override_never_ghost(M02_groups.plane.vehicle, true)
turn_invulnerable(M02_groups.plane.vehicle)
vehicle_enter_teleport(M02_groups.plane.chars[1], M02_groups.plane.vehicle, 0, true)
helicopter_set_max_bank_angle(M02_groups.plane.vehicle, 15.0)
while (true) do
helicopter_fly_to_do(M02_groups.plane.vehicle, 120.0, true, "", true, M02_groups.plane.circling_path)
-- Wait until the engine has started, so we can override the spin up value on the rotors
while (vehicle_engine_check_running(M02_groups.plane.vehicle) == false) do
thread_yield()
end
vehicle_engine_start(M02_groups.plane.vehicle, false)
while (vehicle_pathfind_check_done(M02_groups.plane.vehicle) == 0) do
thread_yield()
end
thread_yield()
end
end
-- Thread to process randomly generated cloud VFX
function m02_thread_generate_clouds()
-- loop forever
while(true) do
delay(rand_int(M02_tweak_values.skydive_cloud_min_cooldown_time, M02_tweak_values.skydive_cloud_max_cooldown_time))
-- Make sure we clean up the old effect first
if (M02_vfx.skydive_cloud_puff.handle ~= -1) then
effect_stop(M02_vfx.skydive_cloud_puff.handle)
thread_yield()
end
-- Play the cloud "puff" effect
M02_vfx.skydive_cloud_puff.handle = effect_play_on_human(M02_vfx.skydive_cloud_puff.nav, LOCAL_PLAYER, nil, false)
delay(1)
if (M02_runtime.stop_streamers == false) then
m02_helper_create_character_streamers(LOCAL_PLAYER, false)
end
end
end
function m02_helper_do_vehicle_group_whooses(vehicle_group_table)
-- Loop over all the vehicles, and update their whooshes
for i, vehicle in pairs(vehicle_group_table.vehicles) do
if (vehicle_hidden(vehicle.name) == false) then
local dist = get_dist(vehicle.name, LOCAL_PLAYER)
if (vehicle.whoosh_event == INVALID_MESSAGE_HANDLE) then
-- Check if we should turn on the whoosh
if (dist < M02_tweak_values.car_whoosh_start_dist) then
vehicle.whoosh_event = audio_object_post_event("Mission", M02_audio_events.car_whoosh.switch_groups, M02_audio_events.car_whoosh.switches, vehicle.name)
end
else
-- Check if we should stop the whoosh
if (dist > M02_tweak_values.car_whoosh_end_dist) then
audio_stop(vehicle.whoosh_event)
vehicle.whoosh_event = INVALID_MESSAGE_HANDLE
end
end
end
end
end
-- Thread to process the dynamic whoosh audio for nearby objects while skydiving
function m02_thread_process_skydive_object_whoosh()
-- Loop forever
while(true) do
m02_helper_do_vehicle_group_whooses(M02_groups.debris_vehicles)
if (group_is_loaded(M02_groups.debris_vehicles_dive_01.name) == true) then
m02_helper_do_vehicle_group_whooses(M02_groups.debris_vehicles_dive_01)
end
if (group_is_loaded(M02_groups.debris_vehicles_dive_02.name) == true) then
m02_helper_do_vehicle_group_whooses(M02_groups.debris_vehicles_dive_02)
end
thread_yield()
end
end
-- Thread to process player stumble animations (we can't just call the non-blocking version since some cleanup for the player happens in the complete check)
--
-- player: Player to play the stumble animation for
-- stumble_dir: Navpoint indicating the direction to stumble
-- use_directional: TRUE if the player should play the direction stumble
function m02_thread_do_play_stumble_animation(player, stumble_dir, use_directional)
if (character_has_human_shield(player) == true) then
return
end
character_clear_combat_move(player)
if (use_directional == true and inv_item_get_equipped_item(player) == M02_tweak_values.skydiving_pistol) then
action_play_directional_stumble(player, stumble_dir)
else
action_play(player, "Plane Cabin Stumble")
end
end
-- Process the cargo plane retreat path
function m02_thread_process_cargo_plane_retreat()
-- Fly to the end of the path, then destroy the cargo plane group
if (helicopter_fly_to_do(M02_groups.plane.vehicle, 150.0, true, "", true, M02_navpoints.cargo_plane_fly_away_end, 0.0, false, false, true)) then
-- Wait until the engine has started, so we can override the spin up value on the rotors
while (vehicle_engine_check_running(M02_groups.plane.vehicle) == false) do
thread_yield()
end
vehicle_engine_start(M02_groups.plane.vehicle, false)
vehicle_pathfind_check_done(M02_groups.plane.vehicle)
while (vehicle_pathfind_check_done(M02_groups.plane.vehicle)) do
thread_yield()
end
end
group_destroy(M02_groups.plane.name)
end
-- Process when to transition the music for the Shaundi catch
--
-- event_name: Name of the audio event to play
-- time_unti: The time until the player cataches Shaundi when the music transition should be triggered
function m02_thread_process_catch_shaundi_music(event_name, time_until)
while(true) do
-- Wait until the player is 5.34s away from catching Shaundi
local current_dist = get_dist(LOCAL_PLAYER, M02_groups.homies_skydive_01.shaundi)
if ((current_dist / M02_tweak_values.player_downward_dive_speed) <= time_until) then
audio_object_post_event(event_name, nil, nil, LOCAL_PLAYER)
return
end
thread_yield()
end
end
-- Thread to clear the area around Shaundi from debris
--
function m02_thread_cleanup_area_around_shaundi()
while(true) do
for i, debris_flow in pairs(M02_debris_flows) do
-- Handle all the debris dive debris
if (group_is_loaded(debris_flow.vehicle_group.name) == true) then
for i, vehicle in pairs(debris_flow.vehicle_group.vehicles) do
if (object_is_in_trigger(M02_triggers.clean_up_debris_above_shaundi, vehicle.name) == true) then
debris_flow_recycle_object(debris_flow.handle, vehicle.name)
end
end
end
for i, box in pairs(debris_flow.boxes) do
if (object_is_in_trigger(M02_triggers.clean_up_debris_above_shaundi, box) == true) then
debris_flow_recycle_object(debris_flow.handle, box)
end
end
for i, pallet in pairs(debris_flow.pallets) do
if (object_is_in_trigger(M02_triggers.clean_up_debris_above_shaundi, pallet) == true) then
debris_flow_recycle_object(debris_flow.handle, pallet)
end
end
end
thread_yield()
end
end
-- Thread to kill any players that end up below Shaundi (last ditch fail safe)
--
function m02_thread_kill_players_that_fall_too_far()
while(true) do
local x, y, z = get_object_pos(LOCAL_PLAYER)
if (y < 915.0) then
character_kill(LOCAL_PLAYER, true)
end
if (coop_is_active() == true) then
x, y, z = get_object_pos(REMOTE_PLAYER)
if (y < 915.0) then
character_kill(REMOTE_PLAYER, true)
end
end
thread_yield()
end
end