--[[ 
 
	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