--[[
	m19.lua
	SR3 Mission Script
	DATE: 08-17-2010
	AUTHOR:	Neil Ryan and Craig Williams
]]--


-- Debug flags --
	M19_enable_binks        = true
	M19_chem_moved_distance = 1.0

-- Tweakable Parameters --
	M19_ped_density = .50
	M19_ped_density_in_truck = 0.8
	M19_ped_density_fix_mask = 0.35
	M19_ped_density_at_rimjobs = 0.15
	M19_player_sprint_ms = 3500 -- Amount of time a full sprint bar will last
	M19_player_sprint_recharge_ms = 10000 -- Amount of time it takes to fully recharge the sprint bar
	M19_player_sprint_recharge_delay_ms = 2500 -- Amount of time to wait before starting to recharge the sprint bar

-- Groups --

	M19_group = {
		start_car = {
			name = "start_car"
		},
		
		stag_guard = {
			name = "stag_guard",
			members = { "npc_stag_guard 001", "npc_stag_guard 002" },
		},
		
		chem_01 = {
			name = "chem_01_group"
		},
		
		chem_02 = {
			name = "chem_02_group"
		},
		
		chem_02_brute = {
			name = "chem_02_brute_group",
			brute = "chem_02_brute",
			detection_callback = "m19_brute_02_cb",
			death_callback = "m19_first_brute_dead_cb",
		},
		
		chem_03 = {
			name = "chem_03_group"
		},
		
		chem_03_brute = {
			name = "chem_03_brute_group",
			brute = "chem_03_brute",
			detection_callback = "m19_brute_03_cb",
			death_callback = "m19_first_brute_dead_cb",
		},
		
		chem_final = {
			name = "chem_final_group"
		},
		
		chem_truck = {
			name = "chem_truck_group",
			vehicle = "chem_truck",
		},
		
		spawn_small_a = {
			name = "continuous_small_a"
		},
		
		spawn_small_b = {
			name = "continuous_small_b"
		},
		
		spawn_small_c = {
			name = "continuous_small_c"
		},
		
		spawn_small_d = {
			name = "continuous_small_d"
		},
		
		spawn_small_e = {
			name = "continuous_small_e"
		},
		
		start_homies = {
			name = "start_homies",
			suko = "Suko_start"
		},
		
		inside_homies = {
			name = "inside_homies",
			suko = "Suko_inside"
		},
		
		checkpoint_homies = {
			name = "checkpoint_homies",
			suko = "Suko_checkpoint"
		},
		
		viola_attack_zombies = {
			name = "attack_zombies",
			-- car = "m19_viola_car",
			members	= 	{ "m19_viola_zombie 001", "m19_viola_zombie 002", "m19_viola_zombie 003", "m19_viola_zombie 004"	},
		},
		
		rim_jobs_spawns_01 = {
			spawns = {"mask_spawn_a", "mask_spawn_b"},
		},
		
		rim_jobs_spawns_02 = {
			spawns = {"mask_spawn_c"},
		},
		
		rim_jobs_spawns_03 = {
			spawns = {"mask_spawn_d"},
		},
		
		rim_jobs_spawns_all = {
			spawns = {"mask_spawn_a", "mask_spawn_b", "mask_spawn_c", "mask_spawn_d"},
		},
		
		flaming_brute = {
			name = "flaming_brute_group",
			brute = "m19_flaming_brute",
		},
		
		cte_zombie_group = {
			name = "m19_cte_zombies_01",
		},
		
		cte_suko_group = {
			name = "m19_cte_suko_01",
		},
		
	}
	
	M19_cte = {
		name = "m19_cte_01",
	
	}
	
	--we need to not stop and start the same spawns at each level or we get extra spawns
	M19_c_spawn = {
		level_1 = { M19_group.spawn_small_a.name, M19_group.spawn_small_b.name },
		stop_level2 = { M19_group.spawn_small_a.name },
		start_level2 = { M19_group.spawn_small_c.name },
		stop_level3 = { M19_group.spawn_small_b.name },
		start_level3 = { M19_group.spawn_small_d.name, M19_group.spawn_small_e.name },
	}

-- Spawn Regions --
	M19_spawn_regions = {
		"Script_Spawn_Region 001",
		"Script_Spawn_Region 002",
		"Script_Spawn_Region 003",
		"Script_Spawn_Region 004",
		"Script_Spawn_Region 005",
		"Script_Spawn_Region 006",
		"Script_Spawn_Region 007",
		"Script_Spawn_Region 008",
		"Script_Spawn_Region 009",
		"Script_Spawn_Region 010"
	}
	
	M19_mask_spawn_regions = {
		"Mask_Spawn_Region 01",
		"Mask_Spawn_Region 02",
		"Mask_Spawn_Region 03",
		"Mask_Spawn_Region 04",
	}
	
-- Navpoints --
	M19_nav = {
		escape_here1 = {
			name = "Escape_here_nav 001"
		},
		
		neutralize_here = {
			name = "neutralize_chem_truck_nav"
		},
		
		mission_end = { "m19_end_nav_local", "m19_end_nav_coop" },
		
		zombie_attack_leash_point = {
			name = "zombie_attack_nav1"
		},
		
		mask_fix_point = {
			name = "Mask_fix_navpoint"
		},
		
		rimjobs_door_open_point = {
			name = "M19_OpenDoor_Offset"
		},
		
		mask_search_point = {
			name = "viola_mask_search_nav"
		},
	}
	
-- Triggers --
	M19_trigger = {
		doors = {
			name = "doors_trigger",
			hit = false,
		},
		
		freeze = {
			name = "m19_trigger_vehicle_freeze",
			hit = false,
		},
		
		escape1 = {
			name = "Escape_trigger 001",
			hit = false,
		},
		
		escape2 = {
			name = "Neutralize_audio_trigger",
			hit = false,
		},
		
		dumpster = {
			name = "m19_trigger_dumpster",
			hit = false,
		},
		
		roof = {
			name = "m19_trigger_roof",
			hit = false,
		},
		
		rim_jobs = {
			name = "mask_fix_trigger",
			hit = false,
			inside = 0,
		},
		
		rim_jobs_door = {
			name = "m19_trigger_rimjobs_door",
		},
		
		traffic = {
			name = "m19_disable_traffic",
		},
		
		island = {
			name = "Trigger_island_area",
			hit = false,
		},
	}
		

-- Characters --

-- Vehicles --

-- Mesh Movers --
	M19_chemical = {
		container1 = {
			name = "Chemicalcontainer B 001",
			script_mover_name = "Chem 001",
			start_nav = "Chem_start 001",			-- navpoint under the chem container used to see if the container has been moved
			end_nav = "Chem_end 001",
			vfx = { nav = "vfx_zombie_fog 001", handle = -1 },
			zombies = M19_group.chem_01.name,
			brute_group = "",
			moved = false,
			complete = false,
			setup = false,
			spawn_trigger = "Chem_01_spawn_trigger",
			spawn_callback = "m19_chem_01_spawn_cb",
		},
		
		container2 = {
			name = "Chemicalcontainer B 002",
			script_mover_name = "Chem 002",
			start_nav = "Chem_start 002",			-- navpoint under the chem container used to see if the container has been moved
			end_nav = "Chem_end 002",
			vfx = { nav = "vfx_zombie_fog 002", handle = -1 },
			zombies = M19_group.chem_02.name,
			brute_group = M19_group.chem_02_brute,
			moved = false,
			complete = false,
			setup = false,
			spawn_trigger = "Chem_02_spawn_trigger",
			spawn_callback = "m19_chem_02_spawn_cb",
		},
		
		container3 = {
			name = "Chemicalcontainer B 003",
			script_mover_name = "Chem 003",
			start_nav = "Chem_start 003",			-- navpoint under the chem container used to see if the container has been moved
			end_nav = "Chem_end 003",
			vfx = { nav = "vfx_zombie_fog 003", handle = -1 },
			zombies = M19_group.chem_03.name,
			brute_group = M19_group.chem_03_brute,
			moved = false,
			complete = false,
			setup = false,
			spawn_trigger = "Chem_03_spawn_trigger",
			spawn_callback = "m19_chem_03_spawn_cb",
		},
		
	}
			
-- Text --

-- Threads --
	M19_closest_container_thread = INVALID_THREAD_HANDLE
	M19_moved_or_found_thread = INVALID_THREAD_HANDLE
	M19_out_of_mask_trigger_thread = INVALID_THREAD_HANDLE
	M19_brute_flame_thread = INVALID_THREAD_HANDLE
	M19_local_player_health_thread = INVALID_THREAD_HANDLE
	M19_remote_player_health_thread = INVALID_THREAD_HANDLE

-- Checkpoints --
	M19_INSIDE_DOOR1 = "doors_start_nav 001"
	
	M19_warp_points = {
		bridge = {
			player1 = "player1_bridge_nav",
			player2 = "player2_bridge_nav",
			viola = "viola_bridge_nav",
		},
		
		rimjobs = {
			player1 = "player1_rimjobs_nav",
			player2 = "player2_rimjobs_nav",
			viola = "viola_rimjobs_nav",
		},
	
	}
	
	M19_checkpoint = {
		start = {
			name = MISSION_START_CHECKPOINT,
			nav = { "start_nav 001", "start_nav 002" },
		},
		
		doors = {
			name = "m19_doors",
			nav1 = "doors_start_nav 001",
			nav2 = "doors_start_nav 002",
		},
		
		-- checkpoints for any 1 container
		chem_01 = { name = "m19_chem_01" },
		chem_02 = { name = "m19_chem_02" },
		chem_03 = { name = "m19_chem_03" },
		
		-- checkpoints for 1 container and viola rescued
		chem_01_rescue = { name = "m19_chem_01_rescue" },
		chem_02_rescue = { name = "m19_chem_02_rescue" },
		chem_03_rescue = { name = "m19_chem_03_rescue" },
		
		-- checkpoints for any 2 containers
		chem_12 = { name = "m19_chem_12" },
		chem_23 = { name = "m19_chem_23" },
		chem_13 = { name = "m19_chem_13" },
		
		-- checkpoints for any 2 containers and mask fixed
		chem_12_fixed = { name = "m19_chem_12_fixed" },
		chem_23_fixed = { name = "m19_chem_23_fixed" },
		chem_13_fixed = { name = "m19_chem_13_fixed" },
		
		all_no_mask = { name = "m19_no_mask" },
		
		final = {
			name = "m19_final",
			nav1 = "doors_start_nav 001",
			nav2 = "doors_start_nav 002",
		}
	}
	
-- Cutscenes --
	CUTSCENE_MISSION_INTRO = "19_in"
	CUTSCENE_MISSION_OUTRO = "19_out"

-- Conversations --
	M19_convo = {
		mission_start = {--
			files = {
				{name = "M19_Mission_Start", delay = 2.0, phonecall = false},
				{name = "M19_Drive_01", delay = 0.0, phonecall = false},
			},
			thread = INVALID_THREAD_HANDLE,
		},
		
		help_me_call = {--
			files = {
				{name = "M19_Help_Me_Call", delay = 0.0, phonecall = true, recieveing = false},
			},
			thread = INVALID_THREAD_HANDLE,
		},
		
		viola_rescued = {--
			files = {
				{name = "M19_viola_Rescued", delay = 0.0, phonecall = false},
			},
			thread = INVALID_THREAD_HANDLE,
		},
		
		zombie_killing_01 = {--
			files = {
				{name = "", delay = 15.0, phonecall = false},
				{name = "M19_Zombie_Killing_01", delay = 8.0, phonecall = false},
				{name = "M19_More_Zombie_Killing", delay = 10.0, phonecall = false},
				{name = "M19_More_Zombie_Killing_2", delay = 0.0, phonecall = false},
			},
			thread = INVALID_THREAD_HANDLE,
		},
		
		broken_mask = {--
			files = {
				{name = "M19_Broken_Mask", delay = 0.0, phonecall = false},
			},
			thread = INVALID_THREAD_HANDLE,
		},
		
		rim_jobs_start = {--
			files = {
				{name = "M19_Hold_Off_Zombies_RJ", delay = 10.0, phonecall = false},
				{name = "M19_Hurry_Up_in_Rim_Jobs", delay = 10.0, phonecall = false},
				{name = "M19_Hurry_Up_in_Rim_Jobs_2", delay = 9.0, phonecall = false},
				{name = "M19_Found_Hose_In_Rim_Jobs", delay = 0.0, phonecall = false},
			},
			thread = INVALID_THREAD_HANDLE,
		},
		-- zombie_killing_02 = {
		-- 	files = {
		-- 		{name = "", delay = 15.0},
		-- 		{name = "M19_Zombie_Killing_02", delay = 0.0},
		-- 	},
		--	thread = INVALID_THREAD_HANDLE,
		-- },
		
		zombie_killing_03 = {--
			files = {
				{name = "", delay = 15.0, phonecall = false},
				{name = "M19_Zombie_Killing_03", delay = 10.0, phonecall = false},
				{name = "M19_Zombie_Killing_04", delay = 0.0, phonecall = false},
			},
			thread = INVALID_THREAD_HANDLE,
		},
		
		take_sample = {--
			files = {
				{name = "M19_Finish_The_Job_Call", delay = 5.0, phonecall = true, recieveing = true},
				{name = "M19_Take_Sample_Call", delay = 0.0, phonecall = true, recieveing = true},
			},
			thread = INVALID_THREAD_HANDLE,
		},
		
		left_with_truck = {--
			files = {
				{name = "M19_Flatbed_Truck", delay = 0.0, phonecall = false},
			},
			thread = INVALID_THREAD_HANDLE,
		},
		
		jump_truck = {--
			files = {
				{name = "M19_Jump_Truck", delay = 0.0, phonecall = false},
			},
			thread = INVALID_THREAD_HANDLE,
		},
	}
	
	M19_personas = {
		{ name = "Oleg", handle = INVALID_PERSONA_HANDLE },
		{ name = "Suko", handle = INVALID_PERSONA_HANDLE },
		{ name = "Burt", handle = INVALID_PERSONA_HANDLE },
	}

	M19_persona_line = {
		easy_enough	= { name = "M19_First_Container_Done_01", 	character = LOCAL_PLAYER, play_2d = false},--
		blasting_containers = { name = "M19_Blasting_Containers", character = LOCAL_PLAYER, play_2d = false},--
		see_zombie = { name = "M19_More_Zombie_Brute_01", character = LOCAL_PLAYER, play_2d = false},
		
		-- Character set by m19_setup_crew()
		one_more = { name = "M19_Third_Container_Done", character = "", play_2d = false},--
		brute_on_fire = { name = "M19_Brute_On_Fire", character = LOCAL_PLAYER, play_2d = false},
		brute_on_fire_dead = { name = "M19_Brute_On_Fire_Dead", character = LOCAL_PLAYER, play_2d = false},
	}
	
	M19_island_emitter = "M19_amb_zombie_island"
	
	M19_emitters = {
		"M19_em_crashed_plane_groans",
	}
		
-- Other --
	M19_SONIC_GUN = "Special-SonicGun"
	
	M19_PLAYER_LOADOUT = { M19_SONIC_GUN }
	
	M19_containers_disturbed = 0
	M19_containers_found = 0
	M19_spawn_level = 0
	M19_doors_started = false
	
	-- record decision
	M19_decision = {
		took_chem_truck = false,
		chem_truck_in_water = false,
	}
	
	M19_NOT_CLOSE_DISTANCE = 5000.0
	
	M19_waypoint_target = ""
	
	M19_active_convo = ""

	M19_rim_jobs_old_door = "m19_rimjobs_door_mover"
	M19_rim_jobs_new_door = "rim_jobs_zombie_door_mover"
	
	M19_container_splash_effect = "Wtr Splash Zombie"
	
	M19_mask_finished = false
	M19_initial_brute_down = false
	
	Current_Suko = ""
	
	M19_phonecall_finished = false
	M19_first_brute_dead = false

-- *************************
--
-- 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
--
-- m19_checkpoint:	The checkpoint the mission should begin at
-- is_restart:					TRUE if the mission is restarting, FALSE otherwise
--
function m19_start(m19_checkpoint, is_restart)

	-- Check if this mission starting from the beginning
	if (m19_checkpoint == M19_checkpoint.start.name) then
		if (is_restart == false) then
			-- First time playing mission
			
			-- Play an intro cutscene???
			if (CUTSCENE_MISSION_INTRO ~= "") then
				cutscene_play(CUTSCENE_MISSION_INTRO, nil, M19_checkpoint.start.nav, false)
			elseif M19_enable_binks then
				bink_play("19_IN_v2")
			end
		end
		fade_out(0)
	end

	-- Handle mission initialization for the current checkpoint
	m19_initialize(m19_checkpoint)

	-- Run the mission from the current checkpoint
	m19_run(m19_checkpoint)
	
end

-- This is the primary function responsible for running the entire mission from start to finish.
--
-- first_checkpoint:	The first checkpoint to begin running the mission at
--
function m19_run(first_checkpoint)
	local current_checkpoint = first_checkpoint
	local ignore_vehicles = true
	local ignore_notoriety = true
	spawn_override_change_hood_alternate_spawning("HOOD_ZOMBIE", true)
	
	-- post voice event to mute non-modulated voice
	audio_object_post_event("Mission_VO_Override")

	--set the cancel navpoints
	mission_set_cancel_warp_location(M19_checkpoint.start.nav[1], M19_checkpoint.start.nav[2])
	
	-- Run the mission from the beginning
	if current_checkpoint == M19_checkpoint.start.name then
		objective_text( 0, "M19_OBJ_GO_TO_ENTRANCE", "", "", SYNC_ALL, OI_ASSET_LOCATION )	
		m19_go_to_entrance()
		
		m19_guards_scene()	
		tutorial_start("weap_charge_weapon", 0, true, true) 
		
		current_checkpoint = M19_checkpoint.doors.name
		mission_set_checkpoint(M19_checkpoint.doors.name, ignore_vehicles, ignore_notoriety)
	end
	
	spawn_region_max_spawn_dist(150.0)
	notoriety_force_no_spawn("morningstar", true)
	notoriety_set_min("morningstar", 0)
	notoriety_set_max("morningstar", 0)
	notoriety_force_no_spawn("luchadores", true)
	notoriety_set_min("luchadores", 0)
	notoriety_set_max("luchadores", 0)
	notoriety_force_no_spawn("deckers", true)
	notoriety_set_min("deckers", 0)
	notoriety_set_max("deckers", 0)
	notoriety_force_no_spawn("police", true)
	notoriety_force_no_spawn("stag", true)
	
	--start audio emitters
	for i, emitter in pairs(M19_emitters) do
		audio_ambient_emitter_start(emitter)
	end
	
	
	--start health monitoring
	M19_local_player_health_thread = thread_new("m19_player_health_thread", LOCAL_PLAYER)
	if (coop_is_active()) then
		M19_remote_player_health_thread = thread_new("m19_player_health_thread", REMOTE_PLAYER)
	end
	
	m19_setup_containers_for_checkpoint(current_checkpoint)
	
	-- determine which checkpoint is hit
	if(m19_get_num_destroyed_containers() < 1) then
		current_checkpoint = m19_do_containers_checkpoints(current_checkpoint, 1)
		mission_set_checkpoint(current_checkpoint, ignore_vehicles, ignore_notoriety)
	end
	
	if (current_checkpoint == M19_checkpoint.chem_01.name or current_checkpoint == M19_checkpoint.chem_02.name or current_checkpoint == M19_checkpoint.chem_03.name) then
		current_checkpoint = m19_do_rescue()
		m19_start_convo_thread(M19_convo.zombie_killing_01)
	
	end 
	-- from this poitn on viola should be invulnerable
	--mute viola's normal persona lines
	npc_suppress_persona(Current_Suko, true)
	
	if(m19_get_num_destroyed_containers() < 2) then
		current_checkpoint = m19_do_containers_checkpoints(current_checkpoint, 2)
		mission_set_checkpoint(current_checkpoint, ignore_vehicles, ignore_notoriety)
	end
	
	if (current_checkpoint == M19_checkpoint.chem_12.name or current_checkpoint == M19_checkpoint.chem_13.name or current_checkpoint == M19_checkpoint.chem_23.name or current_checkpoint == M19_checkpoint.all_no_mask.name) then
		current_checkpoint = m19_do_mask()
		m19_start_convo_thread(M19_convo.zombie_killing_03)
	
	end
	
	
	if(m19_get_num_destroyed_containers() < 3) then
		current_checkpoint = m19_do_containers_checkpoints(current_checkpoint, 3)
		mission_set_checkpoint(current_checkpoint, ignore_vehicles, ignore_notoriety)
	end
	
	if current_checkpoint == M19_checkpoint.final.name then
		m19_take_or_destroy_chem_truck()
	end

	-- if (CUTSCENE_MISSION_OUTRO ~= "") then
	-- 	cutscene_play(CUTSCENE_MISSION_OUTRO)
	-- elseif M19_enable_binks then
	-- 	bink_play("19_Out_v2")
	-- end
	
	-- fade_out(0.5)
	-- fade_out_block()
	-- teleport_coop(M19_nav.mission_end_local.name, M19_nav.mission_end_coop.name, true)
	-- fade_in(0.5)
	-- fade_in_block()
	
	mission_end_success("m19", CUTSCENE_MISSION_OUTRO, M19_nav.mission_end)
	
end

-- This is the primary function responsible for cleaning up the entire mission
-- CALLED FROM CODE (+++MUST RETURN IMMEDIATLY+++)
--
function m19_cleanup()

	-- stop muting voice
	audio_object_post_event("Mission_VO_Override_Stop")
	audio_object_post_event("M19_Music_STOP", nil, nil, LOCAL_PLAYER)

	-- Unload any audio
	m19_unload_all_convos()
	m19_unload_personas()
	
	-- re-enable traffic
	traffic_disable_lanes(M19_trigger.traffic.name, false)
	sidewalk_disable_nodes(M19_trigger.island.name, true)
	
	--fix rimjobs door
	mesh_mover_hide(M19_rim_jobs_new_door)
	mesh_mover_show(M19_rim_jobs_old_door)
	
	spawn_region_max_spawn_dist_reset()
	-- shut down continuous spawns
	m19_stop_continuous_spawns()
	for i,spawn in pairs(M19_group.rim_jobs_spawns_all.spawns) do
		continuous_spawn_stop(spawn)
	end
	continuous_spawn_regions_enable(M19_spawn_regions, false)
	continuous_spawn_regions_enable(M19_mask_spawn_regions, false)
	-- clear tod settings
	m19_clear_tod()

	-- remove temp weapons
	inv_remove_temp_loadout(M19_PLAYER_LOADOUT)
	
	-- kill the water check	
	if M19_moved_or_found_thread ~= INVALID_THREAD_HANDLE then
		thread_kill(M19_moved_or_found_thread)
		M19_moved_or_found_thread = INVALID_THREAD_HANDLE
	end
	if M19_closest_container_thread ~= INVALID_THREAD_HANDLE then
		thread_kill(M19_closest_container_thread)
		M19_closest_container_thread = INVALID_THREAD_HANDLE
	end
	if M19_brute_flame_thread ~= INVALID_THREAD_HANDLE then
		thread_kill(M19_brute_flame_thread)
		M19_brute_flame_thread = INVALID_THREAD_HANDLE
	end
	
	if M19_local_player_health_thread ~= INVALID_THREAD_HANDLE then
		thread_kill(M19_local_player_health_thread)
		M19_local_player_health_thread = INVALID_THREAD_HANDLE
	end
	if M19_remote_player_health_thread ~= INVALID_THREAD_HANDLE then
		thread_kill(M19_remote_player_health_thread)
		M19_remote_player_health_thread = INVALID_THREAD_HANDLE
	end
	if M19_out_of_mask_trigger_thread ~= INVALID_THREAD_HANDLE then
		thread_kill(M19_out_of_mask_trigger_thread)
		M19_out_of_mask_trigger_thread = INVALID_THREAD_HANDLE
	end
	
	-- clear container markers
	m19_clear_container(M19_chemical.container1)
	m19_clear_container(M19_chemical.container2)
	m19_clear_container(M19_chemical.container3)

	-- cleanup truck
	on_vehicle_destroyed("", M19_group.chem_truck.vehicle)
	
	--cleanup zombies
	for i, zombie in pairs(M19_group.viola_attack_zombies.members) do
		on_death("", zombie)
	end
	
	--cleanup first brutes
	on_detection("", M19_group.chem_02_brute.brute)
	on_detection("", M19_group.chem_03_brute.brute)
	on_death("", M19_group.chem_02_brute.brute)
	on_death("", M19_group.chem_03_brute.brute)
	
	-- Cleanup the decision scene
	m19_cleanup_decision()
	
	-- clear triggers
	for key, trigger in pairs(M19_trigger) do
		m19_clear_trigger(trigger.name)
	end
	
	-- cleanup groups
	m19_cleanup_groups()
	m19_cleanup_crew(M19_group.start_homies)
	m19_cleanup_crew(M19_group.inside_homies)
	m19_cleanup_crew(M19_group.checkpoint_homies)
	
	notoriety_set_max("luchadores", 5)
	notoriety_set_max("deckers", 5)
	notoriety_set_max("morningstar", 5)
	notoriety_force_no_spawn("luchadores", false)
	notoriety_force_no_spawn("deckers", false)
	notoriety_force_no_spawn("morningstar", false)
	notoriety_force_no_spawn("police", false)
	notoriety_force_no_spawn("stag", false)

	-- Remove any sprint restrictions
	player_override_clear_sprint()
	player_override_clear_sprint_delay()
	player_override_clear_sprint_recharge()
	
	-- cleanup timer callback
	hud_timer_set(1, 0, "")
	hud_timer_stop(1)
	hud_timer_hide(1, true)
	
	-- do spawning again
	spawning_vehicles(true)

	-- takeoff player mask
	customization_item_revert()
	audio_object_post_event("m19_sfx_gas_mask_STOP", nil, nil, LOCAL_PLAYER)
	if (coop_is_active()) then
		audio_object_post_event("m19_sfx_gas_mask_STOP", nil, nil, REMOTE_PLAYER)
	end
	audio_object_post_event("m19_amb_PC_poisoned_STOP")
	
	--stop audio emitters
	for i, emitter in pairs(M19_emitters) do
		audio_ambient_emitter_stop(emitter)
	end
	audio_ambient_emitter_stop(M19_island_emitter)
	
	--kill any phonecalls that may be occuring
	
	audio_remove_mission_cellphone("M19_Help_Me_Call")
	audio_remove_mission_cellphone("M19_Finish_The_Job_Call")
	audio_remove_mission_cellphone("M19_Take_Sample_Call")
	
	
	--kill all other mission audio
	audio_play("M19_unload")
	
	--clear drug effects
	drug_effect_clear_override_values()
	refraction_situation_clear()
	
end

-- Called when the mission has ended with success
-- CALLED FROM CODE (+++MUST RETURN IMMEDIATLY+++)
--
function m19_success()
end


-- *************************
--
-- Local functions
--
-- *************************

-- Initialize the mission for the specified checkpoint
--
-- checkpoint:		Checkpoint to initialize the mission to
--
function m19_initialize(checkpoint)
	-- Zone swap
	-- city_zone_swap("chemcrash")
	city_zone_swap("zombies")
	city_zone_swap("mayoroff")
	mesh_mover_hide(M19_rim_jobs_old_door)
	mesh_mover_show(M19_rim_jobs_new_door)
	-- city_zone_swap("burn")
	traffic_disable_lanes(M19_trigger.traffic.name, true)
	sidewalk_disable_nodes(M19_trigger.island.name, false)
	
	-- Make sure the screen is completly faded out
	mission_start_fade_out(0.0)

	-- Set the mission author
	set_mission_author("Neil Ryan")

	-- Common initialization
	m19_initialize_common()

	-- Checkpoint specific initialization
	m19_initialize_checkpoint(checkpoint)

	-- Start fading in 
	mission_start_fade_in()

end

-- Go to the entrance of the zombie area
--
function m19_go_to_entrance()
	m19_setup_crew(M19_group.start_homies)
	group_create(M19_group.stag_guard)
	for i, npc in pairs(M19_group.stag_guard.members) do
		npc_detection_enable(npc, false)
	end
	
	m19_set_trigger(M19_trigger.island.name, "m19_island_entrance_cb", false)
	
	m19_set_trigger(M19_trigger.doors.name, "m19_doors_cb")
	mission_waypoint_add(M19_trigger.doors.name, SYNC_ALL)
	marker_add(M19_trigger.doors.name, MINIMAP_ICON_LOCATION, OI_ASSET_LOCATION, OI_FLAGS_LOCATION, SYNC_ALL)
	
	m19_start_convo_thread(M19_convo.mission_start)

	m19_set_trigger(M19_trigger.freeze.name, "m19_doors_freeze_cb", false)
	
	set_ped_density(M19_ped_density)
	while not M19_trigger.doors.hit do
		thread_yield()
	end
	
	if character_is_in_vehicle(LOCAL_PLAYER) then
		vehicle_speed_override(LOCAL_PLAYER, 0)
	end
	if character_is_in_vehicle(REMOTE_PLAYER) then
		vehicle_speed_override(REMOTE_PLAYER, 0)
	end
	--start the zombie island emitter
	audio_ambient_emitter_start(M19_island_emitter)
	
end

-- Z-scene of guards letting you into the infected area
function m19_guards_scene()

	if M19_trigger.island.hit == true then
		for i, container in pairs(M19_chemical) do
			mesh_mover_show(container.script_mover_name)
		end
	end
	m19_clear_trigger(M19_trigger.island.name)

	--fade_out(0.5)
	--fade_out_block()
	cutscene_in()
	group_create_hidden( M19_group.cte_suko_group.name, true )
	group_create_hidden( M19_group.cte_zombie_group.name, true )
	customization_item_wear("cm_gasmask", "cm_mask_gasmask.cmeshx", "cm_mask_gasmask", true, SYNC_ALL, true)
	character_add_prop("m19_cte_suko", "gasmask") 
	cutscene_play( M19_cte.name, nil, {M19_checkpoint.doors.nav1, M19_checkpoint.doors.nav2} )
	--cutscene_play( M19_cte.name)
	group_destroy( M19_group.cte_zombie_group.name )
	group_destroy( M19_group.cte_suko_group.name )

	M19_doors_started = true
	
	-- teleport players into the bad area
	--local exit_vehicle = true
	--teleport_coop(M19_checkpoint.doors.nav1, M19_checkpoint.doors.nav2, exit_vehicle)
	--delay(5)
	
	m19_cleanup_crew(M19_group.start_homies)
	
	-- if group_is_loaded(M19_group.start_car.name) then
	-- 	group_destroy(M19_group.start_car.name)
	-- end
	
	if group_is_loaded(M19_group.stag_guard.name) then
		group_destroy(M19_group.stag_guard.name)
	end
	
	--m19_setup_crew(M19_group.inside_homies)
	spawning_vehicles(false)
	m19_restrict_players_sprint()
	inv_item_equip("Special-SonicGun", LOCAL_PLAYER)
	if (coop_is_active()) then
		inv_item_equip("Special-SonicGun", REMOTE_PLAYER)
	end
	
	customization_item_wear("cm_gasmask", "cm_mask_gasmask.cmeshx", "cm_mask_gasmask", true, SYNC_ALL, true)
	--play gasmask sounds
	audio_object_post_event("m19_sfx_gas_mask_normal", nil, nil, LOCAL_PLAYER)
	if (coop_is_active()) then
		audio_object_post_event("m19_sfx_gas_mask_normal", nil, nil, REMOTE_PLAYER)
	end
	
	--setup the exit truck here while we can be sure the players aren't in a custom car
	m19_setup_chemical_truck()
	
	cutscene_out()
	--fade_in(0.5)
	--fade_in_block()
end

function m19_setup_containers_for_checkpoint(current_checkpoint)
	local do_1 = false
	local do_2 = false
	local do_3 = false
	local checkpoint_found = true
	
	-- figure out which containers we have left for the checkpoint
	if current_checkpoint == M19_checkpoint.doors.name then
		-- just got here, still need to do all of the containers
		do_1 = true
		do_2 = true
		do_3 = true
	elseif (current_checkpoint == M19_checkpoint.chem_01_rescue.name or
		    current_checkpoint == M19_checkpoint.chem_01.name) then
		do_2 = true
		do_3 = true
	elseif (current_checkpoint == M19_checkpoint.chem_02_rescue.name  or
			current_checkpoint == M19_checkpoint.chem_02.name) then
		do_1 = true
		do_3 = true
	elseif (current_checkpoint == M19_checkpoint.chem_03_rescue.name or
			current_checkpoint == M19_checkpoint.chem_03.name) then
		do_1 = true
		do_2 = true
	elseif (current_checkpoint == M19_checkpoint.chem_12_fixed.name or
			current_checkpoint == M19_checkpoint.chem_12.name) then
		do_3 = true
	elseif (current_checkpoint == M19_checkpoint.chem_23_fixed.name or
			current_checkpoint == M19_checkpoint.chem_23.name) then
		do_1 = true
	elseif (current_checkpoint == M19_checkpoint.chem_13_fixed.name or
			current_checkpoint == M19_checkpoint.chem_13.name) then
		do_2 = true
	end
		
	continuous_spawn_regions_enable(M19_spawn_regions, true)
	
	-- setup or destroy container
	if do_1 then
		m19_setup_container(M19_chemical.container1)
	else
		m19_checkpoint_complete_container(M19_chemical.container1)
	end
	
	if do_2 then
		m19_setup_container(M19_chemical.container2)
	else
		m19_checkpoint_complete_container(M19_chemical.container2)
	end
	
	if do_3 then
		m19_setup_container(M19_chemical.container3)
	else
		m19_checkpoint_complete_container(M19_chemical.container3)
	end

end

-- determine what we have done and what needs to be done
--
-- current_checkpoint:		(string) name of the current checkpoint
--
function m19_do_containers_checkpoints(current_checkpoint, size)

	--had to setup containers, but we've already finished this goal, don't mess with the rest
	if (m19_get_num_destroyed_containers() >= size) then
		return current_checkpoint
	end
		
	-- set objective text to whatever it needs to be
	m19_set_3_way_objective_text()
	
	-- check to see if containers have been moved or found
	M19_moved_or_found_thread = thread_new("m19_container_moved_or_found_thread")
	
	if (size == 1) then
		--this should happen just after we get inside or if we restart at the doors checkpoint
		audio_object_post_event("M19_music_mission_start", nil, nil, LOCAL_PLAYER)
	end
	
	--check all current containers and possibly re-add their OIs
	m19_update_container_markers()
	
	-- keep waypoint on the closest container
	M19_closest_container_thread = thread_new("m19_nav_to_closest_thread")
	
	while (m19_get_num_destroyed_containers() < size) do
		thread_yield()
	end
	
	-- potentially clean up threads
	if M19_moved_or_found_thread ~= INVALID_THREAD_HANDLE then
		thread_kill(M19_moved_or_found_thread)
		M19_moved_or_found_thread = INVALID_THREAD_HANDLE
	end
	if M19_closest_container_thread ~= INVALID_THREAD_HANDLE then
		thread_kill(M19_closest_container_thread)
		M19_closest_container_thread = INVALID_THREAD_HANDLE
	end
	m19_clear_container_markers()
	-- keep track of where we're currently at
	return m19_get_container_checkpoint()		
end

function m19_update_container_markers()
	for i, container in pairs(M19_chemical) do
		if(container.moved and not container.complete) then
			marker_remove(container.script_mover_name)
			marker_add(container.script_mover_name, MINIMAP_ICON_KILL, OI_ASSET_KILL_FULL, OI_FLAGS_FULL, SYNC_ALL)
			waypoint_add(container.end_nav)
			marker_add(container.end_nav, MINIMAP_ICON_LOCATION, OI_ASSET_LOCATION, OI_FLAGS_FULL, SYNC_ALL)
		end
	end
	
end

function m19_clear_container_markers()
	for i, container in pairs(M19_chemical) do
		marker_remove(container.script_mover_name, SYNC_ALL)
		marker_remove(container.end_nav, SYNC_ALL)
	end
end

-- after one container is destroyed, rescue viola
function m19_do_rescue()
	local current_checkpoint_name
	waypoint_remove()
	
	-- set objective text to whatever it needs to be
	m19_set_3_way_objective_text()
	
	m19_start_convo_thread(M19_convo.help_me_call)
	--spawn viola and zombies
	m19_setup_crew(M19_group.inside_homies)
	Current_Suko = M19_group.inside_homies.suko
	character_add_prop(Current_Suko, "gasmask")
	npc_leash_to_nav(Current_Suko, M19_nav.zombie_attack_leash_point.name, 1)
	character_prevent_flinching(Current_Suko, true)
	character_prevent_bumping(Current_Suko, true)
	--mute viola's normal persona lines
	npc_suppress_persona(Current_Suko, true)
	--set_ignore_ai_flag(Current_Suko, true)
	group_create(M19_group.viola_attack_zombies.name, true)
	for i, zombie in pairs(M19_group.viola_attack_zombies.members) do
		turn_invulnerable(zombie)
	end
	
	
	--wait on conversation
	while M19_convo.help_me_call.thread ~= INVALID_THREAD_HANDLE do
		thread_yield()
	end
	
	--guide the player to viola
	m19_set_trigger(M19_trigger.dumpster.name, "m19_dumpster_cb", true)
	objective_text( 0, "M19_OBJ_RESCUE_VIOLA", "", "", SYNC_ALL, OI_ASSET_LOCATION )	
	repeat
		--seriously stay the hell there. wtf. stop it.
		if (get_dist(Current_Suko, M19_nav.zombie_attack_leash_point.name) > 2.0) then
			teleport(Current_Suko, M19_nav.zombie_attack_leash_point.name)
			npc_leash_to_nav(Current_Suko, M19_nav.zombie_attack_leash_point.name, 1)
		end
		thread_yield()
	until (M19_trigger.dumpster.hit)
	
	m19_set_trigger(M19_trigger.roof.name, "m19_roof_cb", true)	
	repeat
		thread_yield()
	until (M19_trigger.roof.hit)
	
	--set waypoint to viola
	marker_add(Current_Suko, MINIMAP_ICON_LOCATION, OI_ASSET_LOCATION, OI_FLAGS_LOCATION)
	--objective_text(0, "get to viola")
	
	
	--set_ignore_ai_flag(Current_Suko, false)
	--wait till you're close
	while (get_dist_closest_player_to_object(Current_Suko) > 25) do
		thread_yield()
	end
	-- once you're there, set goal to kill zombies, make viola not invulnerable
	--objective_text(0, "kill zombies!")
	objective_text( 0, "M19_OBJ_KILL_ZOMBiES", "", "", SYNC_ALL, OI_ASSET_KILL )	
	marker_remove(Current_Suko)
	--follower_set_can_abandon( Current_Suko, true)
	turn_vulnerable(Current_Suko)
	npc_leash_remove(Current_Suko)
	character_prevent_flinching(Current_Suko, false)
	character_prevent_bumping(Current_Suko, false)
	for i, zombie in pairs(M19_group.viola_attack_zombies.members) do
		turn_vulnerable(zombie)
		marker_add(zombie, MINIMAP_ICON_KILL, OI_ASSET_KILL, OI_FLAGS_DEFAULT, SYNC_ALL)
		on_death("m19_viola_zombie_death_cb", zombie)
	end
	
	--while any zombies left wait
	while ( group_members_alive(M19_group.viola_attack_zombies.name) > 0) do
		thread_yield()
	end
	m19_start_convo_thread(M19_convo.viola_rescued)
	npc_leash_remove(Current_Suko)
	marker_remove(Current_Suko)
	set_dont_attack_me_on_sight_flag(Current_Suko, true)
	turn_invulnerable(Current_Suko)
	current_checkpoint_name = m19_get_container_checkpoint(true, false)
	mission_set_checkpoint(current_checkpoint_name, true, true)
	delay(10)
	return current_checkpoint_name
end

function m19_viola_zombie_death_cb(zombie, attacker)
	marker_remove(zombie, SYNC_ALL)
	on_death("", zombie)
end

-- after two containers are destroyed, fix your mask
function m19_do_mask()
	local current_checkpoint_name
	waypoint_remove()
	
	-- set objective text to whatever it needs to be
	m19_set_3_way_objective_text()
	
	--change gasmask sounds
	audio_object_post_event("m19_sfx_gas_mask_panic", nil, nil, LOCAL_PLAYER)
	if (coop_is_active()) then
		audio_object_post_event("m19_sfx_gas_mask_panic", nil, nil, REMOTE_PLAYER)
	end

	audio_object_post_event("m19_amb_PC_poisoned", nil, nil, LOCAL_PLAYER)
	audio_object_post_event( "M19_music_PC_poisoned", nil, nil, LOCAL_PLAYER)
	drug_effect_set_override_values(0.15,0)
	refraction_situation_set("drug weak")
	
	m19_start_convo_thread(M19_convo.broken_mask)
	--wait on conversation
	while M19_convo.broken_mask.thread ~= INVALID_THREAD_HANDLE do
		thread_yield()
	end
	
	-- Turn down peds
	set_ped_density(M19_ped_density_fix_mask)
	
	-- Set waypoint to rim_jobs
	trigger_enable(M19_trigger.rim_jobs.name, true)
	marker_add_trigger(M19_trigger.rim_jobs.name, MINIMAP_ICON_LOCATION, INGAME_EFFECT_CHECKPOINT, OI_ASSET_LOCATION, OI_FLAGS_LOCATION, SYNC_ALL)
	on_trigger("m19_mask_trigger_enter_cb", M19_trigger.rim_jobs.name)
	on_trigger_exit("m19_mask_trigger_exit_cb", M19_trigger.rim_jobs.name)
	
	continuous_spawn_regions_enable(M19_spawn_regions, false)
	continuous_spawn_regions_enable(M19_mask_spawn_regions, true)
	objective_text( 0, "M19_OBJ_GO_TO_RIMJOBS", "", "", SYNC_ALL, OI_ASSET_LOCATION )	
	
	-- Wait till the player gets there
	hud_timer_set(1, 120000, "get_to_rimjobs_cb")
	
	local num_players = 1
	if (coop_is_active()) then
		num_players = 2
	end
	
	repeat
		thread_yield()
	until (M19_trigger.rim_jobs.inside == num_players)
	hud_timer_set(1, 0, "")
	hud_timer_stop(1)
	hud_timer_hide(1, true)
	marker_remove_trigger(M19_trigger.rim_jobs.name, SYNC_ALL)
	
	-- Open the door to rimjobs
	--door_open(M19_rim_jobs_door)
	door_lock(M19_rim_jobs_new_door, true)
	objective_text( 0, "M19_OBJ_OPEN_DOOR", "", "", SYNC_ALL, OI_ASSET_USE )
	marker_add(M19_rim_jobs_new_door, MINIMAP_ICON_USE, OI_ASSET_USE, OI_FLAGS_DEFAULT, SYNC_ALL)
	trigger_enable(M19_trigger.rim_jobs_door.name, true)
	on_trigger("m19_open_rimjobs_cb", M19_trigger.rim_jobs_door.name)
	repeat
		thread_yield()
	until (door_is_open(M19_rim_jobs_new_door))
	--make sure this trigger is cleared
	m19_clear_trigger(M19_trigger.rim_jobs_door.name)
	
	--up the drugged effects
	marker_remove(M19_rim_jobs_new_door, SYNC_ALL)
	drug_effect_set_override_values(0.25,0)
	-- refraction_situation_set("drug medium")
	
	-- Turn down peds
	set_ped_density(M19_ped_density_at_rimjobs)
	
	-- Start spawns/setup fight
	objective_text( 0, "M19_OBJ_PROTECT_VIOLA", "", "", SYNC_ALL, OI_ASSET_DEFEND )
	marker_add(Current_Suko, MINIMAP_ICON_PROTECT_ACQUIRE, OI_ASSET_DEFEND, OI_FLAGS_FULL, SYNC_ALL)
	
	--
	-- marker_add_trigger(M19_trigger.rim_jobs.name, MINIMAP_ICON_USE, INGAME_EFFECT_LOCATION, OI_ASSET_USE, OI_FLAGS_LOCATION)
	
	-- Make viola go to her spot
	local should_run = true
	local should_fire = false
	
	while (get_dist(Current_Suko, M19_nav.mask_search_point.name) > 2.0) do
		ai_do_scripted_move(Current_Suko, M19_nav.mask_search_point.name, should_run, should_fire)
		npc_combat_enable(Current_Suko, false)
		
		while not ai_scripted_action_is_complete(Current_Suko) do
			thread_yield()
		end
	end
	
	-- Start area monitoring thread
	M19_out_of_mask_trigger_thread = thread_new("m19_handle_mask_trigger_thread")
	m19_start_convo_thread(M19_convo.rim_jobs_start)
	hud_timer_set(1, 45000, "")
	-- Put her into searching state
	npc_leash_to_nav(Current_Suko, M19_nav.mask_search_point.name, 10)
	set_animation_state(Current_Suko, "walk investigate")
	
	for i,spawn in pairs(M19_group.rim_jobs_spawns_01.spawns) do
		continuous_spawn_start(spawn, 0, 15)
		continuous_spawn_set_orders(spawn, "attack", "", -1)
	end
	
	-- Spawn a flaming brute
	group_create(M19_group.flaming_brute.name, true)
	character_ignite(M19_group.flaming_brute.brute)
	
	-- Voice line about zombie brutes being weak to fire
	M19_brute_flame_thread = thread_new("m19_flaming_brute_thread")
	
	--up the drugged effects
	drug_effect_set_override_values(0.25,0)
	
	-- Wait on timer, spawn new groups of zombies after intervals
	delay(15.0)
	-- for i,spawn in pairs(M19_group.rim_jobs_spawns_02.spawns) do
	-- 	continuous_spawn_start(spawn)
	-- 	continuous_spawn_set_orders(spawn, "attack", "", -1)
	-- end
	
	--up the drugged effects
	--drug_effect_set_override_values(0.35,0)
	refraction_situation_set("drug medium")
	
	delay(15.0)
	for i,spawn in pairs(M19_group.rim_jobs_spawns_03.spawns) do
		continuous_spawn_start(spawn, 0, 15)
		continuous_spawn_set_orders(spawn, "attack", "", -1)
	end
	
	--up the drugged effects
	drug_effect_set_override_values(0.45,0)
	--refraction_situation_set("drug strongest")
	
	delay(15.0)
	while(M19_trigger.rim_jobs.inside == false) do
		thread_yield()
	end
	
	hud_timer_set(1, 0, "")
	hud_timer_stop(1)
	hud_timer_hide(1, true)
	
	-- Success!
	if (M19_brute_flame_thread ~= INVALID_THREAD_HANDLE) then
		thread_kill(M19_brute_flame_thread)
		M19_brute_flame_thread = INVALID_THREAD_HANDLE
	end
	
	--go to viola to fix your mask
	marker_remove(Current_Suko)
	m19_clear_trigger(M19_trigger.rim_jobs.name)
	marker_add(Current_Suko, MINIMAP_ICON_USE, OI_ASSET_LOCATION, OI_FLAGS_DEFAULT, SYNC_ALL)
	objective_text( 0, "M19_OBJ_GO_TO_VIOLA", "", "", SYNC_ALL, OI_ASSET_LOCATION )
	
	while(get_dist_closest_player_to_object(Current_Suko) > 5.0 or human_is_downed(Current_Suko)) do
		thread_yield()
	end
	
	-- kill the monitoring thread
	if M19_out_of_mask_trigger_thread ~= INVALID_THREAD_HANDLE then
		thread_kill(M19_out_of_mask_trigger_thread)
		hud_critical_timer_stop()
		M19_out_of_mask_trigger_thread = INVALID_THREAD_HANDLE
	end
	m19_clear_trigger(M19_trigger.rim_jobs.name)
	
	--message("your mask has been fixed!")
	npc_combat_enable(Current_Suko, true)
	npc_leash_remove(Current_Suko)
	clear_animation_state(Current_Suko)
	audio_object_post_event("m19_sfx_gas_mask_normal", nil, nil, LOCAL_PLAYER)
	if (coop_is_active()) then
		audio_object_post_event("m19_sfx_gas_mask_normal", nil, nil, REMOTE_PLAYER)
	end
	
	audio_object_post_event("m19_amb_PC_poisoned_STOP", nil, nil, LOCAL_PLAYER)
	audio_object_post_event( "M19_music_zombie_island", nil, nil, LOCAL_PLAYER )
	
	marker_remove(Current_Suko)
	-- minimap_icon_remove_radius(M19_trigger.rim_jobs.name, SYNC_ALL)
	for i,spawn in pairs(M19_group.rim_jobs_spawns_all.spawns) do
		continuous_spawn_stop(spawn)
	end
	drug_effect_set_override_values(0.35,0)
	refraction_situation_set("drug medium")
	delay(1.0)
	drug_effect_set_override_values(0.25,0)
	--refraction_situation_set("drug medium")
	delay(1.0)
	drug_effect_set_override_values(0.15,0)
	refraction_situation_set("drug weak")
	delay(1.0)
	drug_effect_clear_override_values()
	refraction_situation_clear()
	
	-- Restore ped density
	set_ped_density(M19_ped_density)
	continuous_spawn_regions_enable(M19_mask_spawn_regions, false)
	continuous_spawn_regions_enable(M19_spawn_regions, true)
	current_checkpoint_name = m19_get_container_checkpoint(false, true)
	mission_set_checkpoint(current_checkpoint_name, true, true)
	M19_mask_finished = true
	return current_checkpoint_name
end

-- Take the chemical truck or destroy it
--
function m19_take_or_destroy_chem_truck()
	waypoint_remove()
	group_create(M19_group.chem_final.name)
	
	zscene_prep(CUTSCENE_MISSION_OUTRO)	
	vehicle_show(M19_group.chem_truck.vehicle)
	objective_text( 0, "M19_OBJ_GET_IN_TRUCK", "", "", SYNC_ALL, OI_ASSET_USE )
	
	
	-- Allow the player to enter the chemical truck again
	set_unjackable_flag(M19_group.chem_truck.vehicle, false)
	
	-- Register some callbacks to detect what the player has done to the vehicle
	on_vehicle_enter("m19_player_in_chem_truck_cb", M19_group.chem_truck.vehicle)
	on_vehicle_enter_water("m19_chem_truck_in_water", M19_group.chem_truck.vehicle)
	
	-- Point out where the vehicle is
	mission_waypoint_add(M19_group.chem_truck.vehicle)
	marker_add(M19_group.chem_truck.vehicle, MINIMAP_ICON_USE, OI_ASSET_USE, OI_FLAGS_FULL, SYNC_ALL)

	while M19_convo.zombie_killing_03.thread ~= INVALID_THREAD_HANDLE do
		thread_yield()
	end
	m19_start_convo_thread(M19_convo.take_sample)
	
	while not M19_decision.took_chem_truck and not M19_decision.chem_truck_in_water do
		thread_yield()
	end
	
	marker_remove(M19_group.chem_truck.vehicle)
	mission_waypoint_remove()
	
	while M19_convo.take_sample.thread ~= INVALID_THREAD_HANDLE do
		thread_yield()
	end
	
	--player_choice_tut_open("m19")
	-- Increase the ped count to give the player some extra stuff to run over
	set_ped_density(M19_ped_density_in_truck)
	
	
	objective_text( 0, "M19_OBJ_CHOICE", "", "", SYNC_ALL, OI_ASSET_INVALID )
	
	vehicle_infinite_mass(M19_group.chem_truck.vehicle, false)
	vehicle_prevent_explosion_fling(M19_group.chem_truck.vehicle, false)
	vehicle_speed_override(M19_group.chem_truck.vehicle, 45.0)
	
	marker_add(M19_trigger.escape1.name, MINIMAP_ICON_LOCATION, OI_ASSET_LOCATION, OI_FLAGS_DEFAULT, SYNC_ALL)
	marker_add(M19_nav.neutralize_here.name, MINIMAP_ICON_KILL, OI_ASSET_KILL_FULL, OI_FLAGS_FULL, SYNC_ALL)
	m19_set_trigger(M19_trigger.escape1.name, "m19_escape1_cb")
	m19_set_trigger(M19_trigger.escape2.name, "m19_escape2_cb", false)
	object_indicator_add_objective_text(M19_trigger.escape1.name, "PLAYER_CHOICE_M19_OPTION1_TITLE", "PLAYER_CHOICE_M19_OPTION1_BODY")
	object_indicator_add_objective_text(M19_nav.neutralize_here.name, "PLAYER_CHOICE_M19_OPTION2_TITLE", "PLAYER_CHOICE_M19_OPTION2_BODY")

	while true do
		if M19_trigger.escape1.hit or M19_decision.chem_truck_in_water then
			break
		end
	
		-- Nag the player to get back into the truck if they are no longer in it
		local player_in_vehicle = character_is_in_vehicle(LOCAL_PLAYER, M19_group.chem_truck.vehicle)
		if (player_in_vehicle == false) then
			player_in_vehicle = character_is_in_vehicle(REMOTE_PLAYER, M19_group.chem_truck.vehicle)
		end
		if not player_in_vehicle then
			m19_clear_trigger(M19_trigger.escape1.name)
			m19_clear_trigger(M19_trigger.escape2.name)
			marker_remove(M19_trigger.escape1.name)
			marker_remove(M19_nav.neutralize_here.name)
			
			M19_decision.took_chem_truck = false
			
			objective_text( 0, "M19_OBJ_GET_IN_TRUCK", "", "", SYNC_ALL, OI_ASSET_USE )
			marker_add(M19_group.chem_truck.vehicle, MINIMAP_ICON_USE, OI_ASSET_USE, OI_FLAGS_DEFAULT, SYNC_ALL)
			
			while not M19_decision.took_chem_truck and not M19_decision.chem_truck_in_water do
				thread_yield()
			end
			
			marker_remove(M19_group.chem_truck.vehicle)
			
			objective_text( 0, "M19_OBJ_CHOICE", "", "", SYNC_ALL, OI_ASSET_INVALID )
			marker_add(M19_trigger.escape1.name, MINIMAP_ICON_LOCATION, OI_ASSET_LOCATION, OI_FLAGS_DEFAULT, SYNC_ALL)
			marker_add(M19_nav.neutralize_here.name, MINIMAP_ICON_KILL, OI_ASSET_KILL_FULL, OI_FLAGS_FULL, SYNC_ALL)
			m19_set_trigger(M19_trigger.escape1.name, "m19_escape1_cb")
			m19_set_trigger(M19_trigger.escape2.name, "m19_escape2_cb", false)
		end
		
		thread_yield()
	end

	if M19_decision.chem_truck_in_water then
		-- If the chem truck is in the water, we get SWAT instead of zombies.
		-- Exclude the zombie reward
		mission_set_excluded_reward("Homie_Zombie_Backup")
	else
		m19_play_convo_set(M19_convo.left_with_truck)
		mission_set_excluded_reward("Homie_Mayor_Reynolds")
		mission_set_excluded_reward("Homie_SWAT_Backup")
	end
	
	audio_object_post_event( "M19_music_zombie_island_end", nil, nil, LOCAL_PLAYER )
	
	m19_cleanup_decision()
end

-- Cleanup anything setup for the decision scene
function m19_cleanup_decision()
	on_vehicle_enter("", M19_group.chem_truck.vehicle)
	on_vehicle_enter_water("", M19_group.chem_truck.vehicle)
	
	marker_remove(M19_group.chem_truck.vehicle)
	marker_remove(M19_trigger.escape1.name)
	marker_remove(M19_nav.neutralize_here.name)
	m19_clear_trigger(M19_trigger.escape1.name)
end

-- ***************************************************
-- m19_run Helper Functions
-- ***************************************************


-- ***************************************************
-- m19_initialize Helper Functions
-- ***************************************************

-- Handle any common initialization
--
function m19_initialize_common()
	-- We only use fake notoriety, so make sure checkpoint system doesn't screw this up for us
	notoriety_reset("luchadores")
	notoriety_reset("deckers")
	notoriety_reset("morningstar")
	inv_add_temp_loadout(M19_PLAYER_LOADOUT)
			
	for i, container in pairs(M19_chemical) do
		container.setup = false
	end
	M19_mask_finished = false
	M19_initial_brute_down = false
	m19_set_tod()
	m19_load_personas()
end

-- set the time of day for the mission
function m19_set_tod()
	-- make it blood red night
	set_time_of_day(20, 00) -- evening
end

-- clear the time of day for the mission
function m19_clear_tod()
end

-- Checkpoint specific initialization
--
-- checkpoint:		The checkpoint to be initialized
--
function m19_initialize_checkpoint(checkpoint)

	if (checkpoint == M19_checkpoint.start.name) then
		teleport_coop(M19_checkpoint.start.nav[1], M19_checkpoint.start.nav[2], true)
		group_create(M19_group.start_car.name, true)
	else
		--equip the sonic gun
		inv_item_equip("Special-SonicGun", LOCAL_PLAYER)
		if (coop_is_active()) then
			inv_item_equip("Special-SonicGun", REMOTE_PLAYER)
		end
		customization_item_wear("cm_gasmask", "cm_mask_gasmask.cmeshx", "cm_mask_gasmask", true, SYNC_ALL, true)
		audio_object_post_event("m19_sfx_gas_mask_normal", nil, nil, LOCAL_PLAYER)
		audio_ambient_emitter_start(M19_island_emitter)
		
		if (coop_is_active()) then
			audio_object_post_event("m19_sfx_gas_mask_normal", nil, nil, REMOTE_PLAYER)
		end
		if (checkpoint ~= M19_checkpoint.doors.name) then
			--only play this if we're starting after the doors checkpoint
			audio_object_post_event( "M19_music_regular_checkpoint", nil, nil, LOCAL_PLAYER )
		end
		
		if (checkpoint ~= M19_checkpoint.doors.name and 
			checkpoint ~= M19_checkpoint.chem_01.name and 
			checkpoint ~= M19_checkpoint.chem_02.name and 
			checkpoint ~= M19_checkpoint.chem_03.name) then
			--m19_setup_crew(M19_group.inside_homies)
			--Current_Suko = M19_group.inside_homies.suko
			m19_setup_crew(M19_group.checkpoint_homies)
			Current_Suko = M19_group.checkpoint_homies.suko
			character_add_prop(Current_Suko, "gasmask")
		end
		
		--possibly teleport
		if (checkpoint == M19_checkpoint.chem_12.name or
			checkpoint == M19_checkpoint.chem_23.name or
			checkpoint == M19_checkpoint.chem_13.name or
			checkpoint == M19_checkpoint.all_no_mask.name) then
			teleport_coop(M19_warp_points.bridge.player1, M19_warp_points.bridge.player2, true)
			teleport(Current_Suko, M19_warp_points.bridge.viola, true)
			M19_initial_brute_down = true
		elseif (checkpoint == M19_checkpoint.chem_12_fixed.name or
			checkpoint == M19_checkpoint.chem_13_fixed.name or
			checkpoint == M19_checkpoint.chem_23_fixed.name or
			checkpoint == M19_checkpoint.final.name) then
			teleport_coop(M19_warp_points.rimjobs.player1, M19_warp_points.rimjobs.viola, true)
			-- teleport(Current_Suko, M19_warp_points.rimjobs.viola, true)
			M19_mask_finished = true
			M19_initial_brute_down = true
		else	
			-- default is teleporting inside the door
			teleport_coop(M19_checkpoint.doors.nav1, M19_checkpoint.doors.nav2, true)	
		end
		--setup the end truck
		m19_setup_chemical_truck()
		
		spawning_vehicles(false)
		set_ped_density(M19_ped_density)
		m19_restrict_players_sprint()
	end
end

-- Sets up the mission critical chemical truck for the final decision scene
--
function m19_setup_chemical_truck()
	group_create(M19_group.chem_truck.name)
	vehicle_infinite_mass(M19_group.chem_truck.vehicle, true)
	vehicle_prevent_explosion_fling(M19_group.chem_truck.vehicle, true)
	set_unjackable_flag(M19_group.chem_truck.vehicle, true)
	on_vehicle_destroyed("m19_mover_destroyed_cb", M19_group.chem_truck.vehicle)
	vehicle_hide(M19_group.chem_truck.vehicle)
end

-- Add the radius thing to the chem container and setup callbacks and flags.
--
-- setup_container:		(table) container information
--
function m19_setup_container(setup_container)
	if (setup_container.setup == false) then
		while (mesh_mover_ready(setup_container.script_mover_name) == false) do
			thread_yield()
		end
		mesh_mover_reset(setup_container.script_mover_name)
		on_mover_enter_water("m19_container_in_water_cb", setup_container.script_mover_name)
		on_mover_destroyed("m19_mover_destroyed_cb", setup_container.script_mover_name)
		m19_set_trigger(setup_container.spawn_trigger, setup_container.spawn_callback, false)
		-- setup_container.hiss_id = audio_object_post_event("m19_sfx_virus_tank_beep_hiss", nil, nil, setup_container.script_mover_name)
		setup_container.complete = false
		setup_container.moved = false
		setup_container.vfx.handle = effect_play(setup_container.vfx.nav, true, SYNC_ALL)
		setup_container.setup = true
	end
end

-- Mark container as completed and destroy it.
-- This function is for loading from a checkpoint.
-- The normal clearing of a container is handled in the checkpoint.
--
-- complete_container:		(table) container information
--
function m19_checkpoint_complete_container(complete_container)
	mesh_mover_hide(complete_container.script_mover_name)
	if (complete_container.moved ~= true) then
		complete_container.moved = true
		M19_containers_disturbed = M19_containers_disturbed + 1
		M19_containers_found = M19_containers_found + 1
	end
	
	complete_container.complete = true
	m19_adjust_spawning()
end

-- Remove markers/callbacks for a container
--
-- container:		(table) container information
--
function m19_clear_container(container)
	marker_remove(container.script_mover_name, SYNC_ALL)
	marker_remove(container.end_nav, SYNC_ALL)
	on_mover_enter_water("", container.script_mover_name)
	on_mover_destroyed("", container.script_mover_name)
	m19_clear_trigger(container.spawn_trigger)
end


-- ***************************************************
-- Miscellaneous m19 Helper Funcrtions
-- ***************************************************

-- Reduce the player's ability to sprint
--
function m19_restrict_players_sprint()
	-- Change how long a full sprint bar will last. Unlimited sprint is ignored if once this value is set.
	player_override_set_sprint(M19_player_sprint_ms)
	
	-- Change how long it takes to recharge the full sprint bar
	player_override_set_sprint_recharge(M19_player_sprint_recharge_ms)
	
	-- Change the delay before the sprint bar starts to regenerate
	player_override_set_sprint_delay(M19_player_sprint_recharge_delay_ms)
end

-- Spawn and setup homies
--
-- homie_setup:	(table) homie information
--
function m19_setup_crew(homie_setup)
	group_create(homie_setup.name, true)
	
	party_add( homie_setup.suko, LOCAL_PLAYER )
	follower_set_can_abandon( homie_setup.suko, false)
	on_death("m19_failure_suko_died_cb", homie_setup.suko)
	on_dismiss("m19_failure_suko_abandon_cb", homie_setup.suko)
	set_behavior_set(homie_setup.suko, "Viola Zombie Fight")

	M19_persona_line.one_more.character = homie_setup.suko
	
	turn_invulnerable(homie_setup.suko)
	inv_item_remove_all(homie_setup.suko)
	inv_item_add("Shotgun-Police", 1, homie_setup.suko, true)
	inv_item_equip(WEAPON_SLOT_SHOTGUN, homie_setup.suko)
	npc_weapon_pickup_override(homie_setup.suko, false)
end

-- Cleanup homies
--
-- homie_cleanup:	(table) homie information
--
function m19_cleanup_crew(homie_cleanup)
	if group_is_loaded(homie_cleanup.name) then
		on_death("", homie_cleanup.suko)
		on_dismiss("", homie_cleanup.suko)
		group_destroy(homie_cleanup.name)
	end
end

-- Set a trigger enabled with a mission marker
--
-- trigger:					(string) Name of the trigger
-- trigger_callback:		(string) Function name to call when trigger is activated
--
function m19_set_trigger(trigger, trigger_callback, add_marker)
	trigger_enable(trigger, true)
	if add_marker ~= false then
		marker_add_trigger(trigger, MINIMAP_ICON_LOCATION, INGAME_EFFECT_CHECKPOINT, OI_ASSET_LOCATION, OI_FLAGS_LOCATION, SYNC_ALL)
	end
	on_trigger(trigger_callback, trigger)
end

-- Disable trigger
--
-- trigger:			(string) Name of the trigger
--
function m19_clear_trigger(trigger)
	on_trigger("", trigger)
	on_trigger_exit("", trigger)
	trigger_enable(trigger, false)
	marker_remove_trigger(trigger, SYNC_ALL)
	mission_waypoint_remove()
end

-- Cleanup all mission groups
--
function m19_cleanup_groups()
	if group_is_loaded(M19_group.start_car.name) then
		group_destroy(M19_group.start_car.name)
	end
	if group_is_loaded(M19_group.stag_guard.name) then
		group_destroy(M19_group.stag_guard.name)
	end
	if group_is_loaded(M19_group.chem_01.name) then
		group_destroy(M19_group.chem_01.name)
	end
	if group_is_loaded(M19_group.chem_02.name) then
		group_destroy(M19_group.chem_02.name)
	end
	if group_is_loaded(M19_group.chem_03.name) then
		group_destroy(M19_group.chem_03.name)
	end
	if group_is_loaded(M19_group.chem_final.name) then
		group_destroy(M19_group.chem_final.name)
	end
	if group_is_loaded(M19_group.chem_truck.name) then
		group_destroy(M19_group.chem_truck.name)
	end
end

-- start a group of continuous spawns
--
-- spawn_table:	(table) continuous spawns to start
--
function m19_start_continuous_spawns(spawn_table)
	for i,spawn in pairs(spawn_table) do
		continuous_spawn_start(spawn, 0, 15)
	end
end

function m19_stop_continuous_spawn_table(spawn_table)
	for i,spawn in pairs(spawn_table) do
		continuous_spawn_stop(spawn)
	end
end

function m19_stop_continuous_spawns()
	continuous_spawn_stop(M19_group.spawn_small_a.name)
	continuous_spawn_stop(M19_group.spawn_small_b.name)
	continuous_spawn_stop(M19_group.spawn_small_c.name)
	continuous_spawn_stop(M19_group.spawn_small_d.name)
	continuous_spawn_stop(M19_group.spawn_small_e.name)
end

-- adjust the continuous spawning based on the number of containers found
--
function m19_adjust_spawning()
	
	if (M19_containers_disturbed <= M19_spawn_level) then
		return -- spawn rate only goes up
	end
	
	if (M19_spawn_level == 0 and M19_containers_disturbed >= 1) then
		m19_start_continuous_spawns(M19_c_spawn.level_1)
		M19_spawn_level = 1
	end 
	
	if (M19_spawn_level == 1 and M19_containers_disturbed >= 2 and M19_first_brute_dead == true )then
		m19_stop_continuous_spawn_table(M19_c_spawn.stop_level2)
		m19_start_continuous_spawns(M19_c_spawn.start_level2)
		M19_spawn_level = 2
	end
	
	if (M19_spawn_level == 2 and M19_containers_disturbed == 3) then
		m19_stop_continuous_spawn_table(M19_c_spawn.stop_level3)
		m19_start_continuous_spawns(M19_c_spawn.start_level3)
		M19_spawn_level = 3
	end
end

-- Determine how many containers have been destroyed and play appropriate line
function m19_play_container_destroyed_audio()
	local destroyed_count = 0
	
	if M19_chemical.container1.complete then
		destroyed_count = destroyed_count + 1
	end
	if M19_chemical.container2.complete then
		destroyed_count = destroyed_count + 1
	end
	if M19_chemical.container3.complete then
		destroyed_count = destroyed_count + 1
	end
	
	if destroyed_count == 1 then
		m19_play_persona_line(M19_persona_line.easy_enough)
	elseif destroyed_count == 2 then
		-- m19_start_convo_thread(M19_convo.zombie_killing_02)
	elseif destroyed_count == 3 then
		m19_play_persona_line(M19_persona_line.one_more)
	end
end

-- set the objective text based on how many containers are taken care of
function m19_set_3_way_objective_text()
	local destroyed_count = 0
	
	if M19_chemical.container1.complete then
		destroyed_count = destroyed_count + 1
	end
	if M19_chemical.container2.complete then
		destroyed_count = destroyed_count + 1
	end
	if M19_chemical.container3.complete then
		destroyed_count = destroyed_count + 1
	end
	
	if destroyed_count < 3 then
		objective_text( 0, "M19_OBJ_NEUTRALIZE_CONTAINERS", destroyed_count, 3, SYNC_ALL, OI_ASSET_KILL )
	else
		objective_text( 0, "M19_OBJ_GET_IN_TRUCK", "", "", SYNC_ALL, OI_ASSET_USE )
	end
end

-- Determine which checkpoint we just completed
-- rescue and mask are set to true only if we *just* did one of them
function m19_get_container_checkpoint( rescue, mask )
	local did1 = M19_chemical.container1.complete
	local did2 = M19_chemical.container2.complete
	local did3 = M19_chemical.container3.complete
	
	local current_checkpoint
	
	if (rescue) then
		if did1 and did2 and did3 then
			current_checkpoint = M19_checkpoint.all_no_mask.name
		elseif did1 and did2 then
			current_checkpoint = M19_checkpoint.chem_12.name
		elseif did2 and did3 then
			current_checkpoint = M19_checkpoint.chem_23.name
		elseif did1 and did3 then
			current_checkpoint = M19_checkpoint.chem_13.name
		elseif did3 then
			current_checkpoint = M19_checkpoint.chem_03_rescue.name
		elseif did2 then
			current_checkpoint = M19_checkpoint.chem_02_rescue.name
		elseif did1 then
			current_checkpoint = M19_checkpoint.chem_01_rescue.name
		end
	elseif (mask) then
		if did1 and did2 and did3 then
			current_checkpoint = M19_checkpoint.final.name
		elseif did1 and did2 then
			current_checkpoint = M19_checkpoint.chem_12_fixed.name
		elseif did2 and did3 then
			current_checkpoint = M19_checkpoint.chem_23_fixed.name
		elseif did1 and did3 then
			current_checkpoint = M19_checkpoint.chem_13_fixed.name
		end
	else
		if did1 and did2 and did3 and M19_mask_finished then
			current_checkpoint = M19_checkpoint.final.name
		elseif did1 and did2 and did3 then
			current_checkpoint = M19_checkpoint.all_no_mask.name
		elseif did1 and did2 then
			current_checkpoint = M19_checkpoint.chem_12.name
		elseif did2 and did3 then
			current_checkpoint = M19_checkpoint.chem_23.name
		elseif did1 and did3 then
			current_checkpoint = M19_checkpoint.chem_13.name
		elseif did3 then
			current_checkpoint = M19_checkpoint.chem_03.name
		elseif did2 then
			current_checkpoint = M19_checkpoint.chem_02.name
		elseif did1 then
			current_checkpoint = M19_checkpoint.chem_01.name
		end
	end
	return current_checkpoint
end

-- check container to see if the has moved
-- separated from found becasue moved == attack
--
-- container_table:		(table) container to test
--
function m19_moved_container_test(container_table)	
	if (M19_chem_moved_distance < get_dist(container_table.start_nav, container_table.script_mover_name)) then
		container_table.moved = true

		m19_clear_waypoint_target()
		
		-- Ensure there are no makers on the container beforehand
		marker_remove(container_table.script_mover_name)
		marker_add(container_table.script_mover_name, MINIMAP_ICON_KILL, OI_ASSET_KILL_FULL, OI_FLAGS_FULL, SYNC_ALL)
		waypoint_add(container_table.end_nav)
		marker_add(container_table.end_nav, MINIMAP_ICON_LOCATION, OI_ASSET_LOCATION, OI_FLAGS_FULL, SYNC_ALL)

		M19_containers_found = M19_containers_found + 1		
		
		if (container_table.brute_group ~= "" and M19_initial_brute_down == false) then
			--make sure the brute if alive sees you
			if (character_is_dead(container_table.brute_group.brute) == false) then
				set_always_sees_player_flag(container_table.brute_group.brute, true)
			end
		end
		
		--Stop the VFX for this container
		if (container_table.vfx.handle ~= -1) then
			effect_stop(container_table.vfx.handle)
			container_table.vfx.handle = -1
		end
		
		-- if (container_table.hiss_id ~= nil) then
			-- audio_stop(container_table.hiss_id)
		-- end
		
		--play an audio line
		m19_play_persona_line(M19_persona_line.blasting_containers)
		
		-- bump up the continuous spawn
		M19_containers_disturbed = M19_containers_disturbed + 1
		m19_adjust_spawning()
	end	
end

-- Clear any previously set waypoint markers, and then add a marker to the specified container
--
-- container:	(table) container to add a marker to
--
function m19_set_waypoint_target(container)
	-- Ignore the container if we already have a marker on it
	if M19_waypoint_target ~= "" and M19_waypoint_target.script_mover_name == container.script_mover_name then
		return
	end

	-- Clear any old markers
	m19_clear_waypoint_target()
	
	-- Add the new marker
	M19_waypoint_target = container
	marker_add(container.script_mover_name, MINIMAP_ICON_KILL, OI_ASSET_KILL_FULL, OI_FLAGS_FULL, SYNC_ALL)
end

-- Remove any previously set waypoint markers.
--
function m19_clear_waypoint_target()
	if M19_waypoint_target == "" then
		return
	end

	-- Only remove the marker if the container hasn't been touched. Otherwise, we want to leave the marker
	-- on the container.
	if not M19_waypoint_target.moved then
		marker_remove(M19_waypoint_target.script_mover_name)
	end
	
	M19_waypoint_target = ""
end

function m19_get_num_destroyed_containers()
	local destroyed_count = 0
	
	if M19_chemical.container1.complete then
		destroyed_count = destroyed_count + 1
	end
	if M19_chemical.container2.complete then
		destroyed_count = destroyed_count + 1
	end
	if M19_chemical.container3.complete then
		destroyed_count = destroyed_count + 1
	end

	return destroyed_count
	
end

-- *************************
--
-- Callback functions
--
-- *************************

-- Suko died, you lose
function m19_failure_suko_died_cb()
	mission_end_failure("m19", "m19_failure_suko_died")
end

-- Suko abandoned, you lose
function m19_failure_suko_abandon_cb()
	hud_critical_timer_stop()
	mission_end_failure("m19", "m19_failure_suko_abandoned")
end

function m19_island_entrance_cb()
	for i, container in pairs(M19_chemical) do
		mesh_mover_hide(container.script_mover_name)
	end
	M19_trigger.island.hit = true
end

function m19_doors_freeze_cb()
	while M19_doors_started == false do
		if character_is_in_vehicle(LOCAL_PLAYER) then
			vehicle_freeze(LOCAL_PLAYER, true)
		end
		if character_is_in_vehicle(REMOTE_PLAYER) then
			vehicle_freeze(REMOTE_PLAYER, true)
		end
		thread_yield()
	end
	m19_clear_trigger(M19_trigger.freeze.name)
end

-- a player has activiated the doors trigger region
function m19_doors_cb()
	M19_trigger.doors.hit = true
	m19_clear_trigger(M19_trigger.doors.name)
end

-- a player has hit the escape trigger
function m19_escape1_cb()
	M19_trigger.escape1.hit = true
	m19_clear_trigger(M19_trigger.escape1.name)
end

function m19_escape2_cb()
	m19_start_convo_thread(M19_convo.jump_truck)
	m19_clear_trigger(M19_trigger.escape2.name)
end

--spawn the dudes around a specific container
function m19_chem_01_spawn_cb(player, trigger)
	local setup_container = M19_chemical.container1
	if (setup_container.complete == false ) then
		group_create(setup_container.zombies)
		if (setup_container.brute_group ~= "" and M19_initial_brute_down == false) then
			group_create(setup_container.brute_group.name)
			on_detection(setup_container.brute_group.detection_callback, setup_container.brute_group.brute)
			on_death(setup_container.brute_group.death_callback, setup_container.brute_group.brute)
		end
	end
	m19_clear_trigger(trigger)
end

function m19_chem_02_spawn_cb(player, trigger)
	local setup_container = M19_chemical.container2
	if (setup_container.complete == false) then
		group_create(setup_container.zombies)
		if (setup_container.brute_group ~= "" and M19_initial_brute_down == false) then
			group_create(setup_container.brute_group.name)
			on_detection(setup_container.brute_group.detection_callback, setup_container.brute_group.brute)
			on_death(setup_container.brute_group.death_callback, setup_container.brute_group.brute)
		end
	end
	m19_clear_trigger(trigger)
end

function m19_chem_03_spawn_cb(player, trigger)
	local setup_container = M19_chemical.container3
	if (setup_container.complete == false) then
		group_create(setup_container.zombies)
		if (setup_container.brute_group ~= "" and M19_initial_brute_down == false) then
			group_create(setup_container.brute_group.name)
			on_detection(setup_container.brute_group.detection_callback, setup_container.brute_group.brute)
			on_death(setup_container.brute_group.death_callback, setup_container.brute_group.brute)
		end
	end
	m19_clear_trigger(trigger)
end

-- determine a chemical container is in the water
function m19_container_in_water_cb(mover_name)
	-- find the container
	for i, container in pairs(M19_chemical) do
		if container.script_mover_name == mover_name then
			--message("Container neutralized", 10.0)
			container.complete = true
			container.moved = true
			
			--play sound
			--audio_object_post_event("m19_sfx_virus_tank_splash", nil, nil, container.script_mover_name)
			effect_play_on_script_object(M19_container_splash_effect, container.script_mover_name)
			-- clean up any container stuff
			m19_clear_container(container)
			
			-- play amusing audio line
			m19_play_container_destroyed_audio()
			
			-- Remove the callback so m19_mover_destroyed_cb() doesn't get called 
			on_mover_destroyed("", container.script_mover_name)
			marker_remove(container.end_nav, SYNC_ALL)
			
			delay(1)
			
			-- destroy the container mesh
			mesh_mover_set_damaged_by_player_only(container.script_mover_name, false)
			mesh_mover_kill(container.script_mover_name)
		end
	end
end

function m19_mover_destroyed_cb(mover_name)
	mission_end_failure("m19", "m19_failure_container_destroyed")
end

-- Someone has entered the chemical truck
function m19_player_in_chem_truck_cb(player, vehicle)
	-- if player == LOCAL_PLAYER then
		M19_decision.took_chem_truck = true
	-- end
end

-- The chemical truck fell into the water
function m19_chem_truck_in_water()
	M19_decision.chem_truck_in_water = true
	on_vehicle_destroyed("", M19_group.chem_truck.vehicle)
end

--viola rescue callbacks
function m19_dumpster_cb()
	-- Clear the trigger
	m19_clear_trigger(M19_trigger.dumpster.name)
	-- Notify script that we reached the trigger
	M19_trigger.dumpster.hit = true
end

function m19_roof_cb()
	-- Clear the trigger
	m19_clear_trigger(M19_trigger.roof.name)
	-- Notify script that we reached the trigger
	M19_trigger.roof.hit = true
end

function m19_mask_trigger_enter_cb(player)
	M19_trigger.rim_jobs.inside = M19_trigger.rim_jobs.inside + 1
end

function m19_mask_trigger_exit_cb(player)
	M19_trigger.rim_jobs.inside = M19_trigger.rim_jobs.inside - 1
end


function get_to_rimjobs_cb()
	mission_end_failure("m19", "m19_failure_rimjobs_timer")
end

function m19_open_rimjobs_cb(player)
	--ai_do_scripted_move(LOCAL_PLAYER, M19_nav.rimjobs_door_open_point, true)
	door_lock(M19_rim_jobs_new_door, false)
	door_open(M19_rim_jobs_new_door, false, true, player, "M19 Open Garage Door", M19_nav.rimjobs_door_open_point.name)
	--action_play(player, "M19 Open Garage Door")
	m19_clear_trigger(M19_trigger.rim_jobs_door.name)
end


function m19_brute_02_cb()
	--play a line
	delay(5)
	m19_play_persona_line(M19_persona_line.see_zombie)
	--kill other brute
	on_death("", M19_group.chem_03_brute.brute)
	if (character_exists(M19_group.chem_03_brute.brute)) then
		character_kill(M19_group.chem_03_brute.brute)
	end
	--remove callbacks
	on_detection("", M19_group.chem_02_brute.brute)
	on_detection("", M19_group.chem_03_brute.brute)
	M19_initial_brute_down = true
end

function m19_brute_03_cb()
	--play a line
	delay(5)
	m19_play_persona_line(M19_persona_line.see_zombie)
	--kill other brute
	on_death("", M19_group.chem_02_brute.brute)
	if (character_exists(M19_group.chem_02_brute.brute)) then
		character_kill(M19_group.chem_02_brute.brute)
	end
	--remove callbacks
	on_detection("", M19_group.chem_02_brute.brute)
	on_detection("", M19_group.chem_03_brute.brute)
	M19_initial_brute_down = true
end

function m19_first_brute_dead_cb(char)
	m19_adjust_spawning()
	on_death("", char)
end

function m19_finish_phonecall_cb()
	M19_phonecall_finished = true
end
-- *************************
--
-- Thread functions
--
-- *************************

-- Find the closest container or container area and set a waypoint to it
function m19_nav_to_closest_thread()
	while true do
		-- If a container has been moved, but it still hasn't been pushed into the water, the container will have
		-- a marker on it. Wait until the container is neutralized before we point out another container.
		local player_moving_container = true
		while player_moving_container do
			player_moving_container = false
		
			for i, container in pairs(M19_chemical) do
				while not container.complete and container.moved do
					thread_yield()
					player_moving_container = true
				end
			end
		end
		
		-- Find the closest non-moved container
		local closest = ""
		local closest_dist = M19_NOT_CLOSE_DISTANCE
		for i, container in pairs(M19_chemical) do
			if not container.moved then
				local dist = get_dist(LOCAL_PLAYER, container.script_mover_name)
				if dist < closest_dist then
					closest_dist = dist
					closest = container
				end
			end
		end
		
		-- If we did not find a near by container, they have all been found. 
		if closest == "" then
			break
		end

		-- Set the waypoint target to the closest container. This will clear the old marker for us as well.
		m19_set_waypoint_target(closest)

		-- don't update this so often
		delay(1.0)
	end
	
	M19_closest_container_thread = INVALID_THREAD_HANDLE
end

-- determine if a container has been moved or found
-- 
function m19_container_moved_or_found_thread()
	local all_complete = false
	while not all_complete do
		all_complete = true
		for i, container in pairs(M19_chemical) do
			if not container.complete and not container.moved then
				all_complete = false
				thread_yield()	-- test over multiple frames
				m19_moved_container_test(container)
			end
		end

		thread_yield()
	end
	
	M19_moved_or_found_thread = INVALID_THREAD_HANDLE
end

--keep track of players being out of the trigger area and abandoning viola
function m19_handle_mask_trigger_thread()
	local num_players = 1
	if (coop_is_active()) then
		num_players = 2
	end
	local players_inside = true
	while (true) do
		if (players_inside == true) then
			if(M19_trigger.rim_jobs.inside < num_players) then
				players_inside = false
				--start a warning and fail chance
				hud_critical_timer_set(10000, "m19_obj_protect_viola", "m19_failure_suko_abandon_cb")
			end
		else
			if(M19_trigger.rim_jobs.inside == num_players) then
				players_inside = true
				--kill the warning and fail chance
				hud_critical_timer_stop()
			end
		end
		thread_yield()
	end
end

--handle the player being out of the trigger area during the fix the mask section
function m19_out_of_mask_trigger_thread()
	--objective_text(0, "get to back to Viola!")
	m19_set_trigger(M19_trigger.rim_jobs.name, "m19_mask_trigger_return_cb", true)
	--message("Get back to rim jobs!")
	hud_critical_timer_set(10000, "m19_obj_protect_viola")
	
	delay(10.0)
	hud_critical_timer_stop()
	--mission_end_failure("m19", "m19_failure_suko_abandoned")
end

function m19_flaming_brute_thread()
	delay(1)
	m19_play_persona_line(M19_persona_line.brute_on_fire)
	while (get_dist_closest_player_to_object(M19_group.flaming_brute.brute) > 5.0) do
		thread_yield()
	end
	
	--kill the brute
	character_kill(M19_group.flaming_brute.brute)
	m19_play_persona_line(M19_persona_line.brute_on_fire_dead)
	M19_brute_flame_thread = INVALID_THREAD_HANDLE
end

function m19_player_health_thread(player)
	local over_half = true
	local over_quarter = true
	local max_health = get_max_hit_points(player)
	while (true) do
		local current_health = get_current_hit_points(player)
		local ratio = current_health / max_health
		if (ratio > 0.5) then
			--over half health
			if(over_half == false) then
				over_half = true
				over_quarter = true
				audio_object_post_event("m19_sfx_gas_mask_normal", nil, nil, player)
			end
		elseif (ratio > 0.25) then
			if(over_quarter == false or over_half == true) then
				over_half = false
				over_quarter = true
				audio_object_post_event("m19_sfx_gas_mask_sprint", nil, nil, LOCAL_PLAYER)
			end
		else
			if(over_quarter == true or over_half == true) then
				over_half = false
				over_quarter = false
				audio_object_post_event("m19_sfx_gas_mask_panic", nil, nil, LOCAL_PLAYER)
			end
		end
		delay(2)
	end

end




















-- Plays a conversation
--
-- convo: Conversation to play
function m19_play_convo(convo)
	if convo.name ~= "" and convo.name ~= nil then
		--local msg_handle = message("Playing Convo: " .. convo.name, 99999999.0)
		if(convo.phonecall == true) then
			M19_phonecall_finished = false
			convo.handle = INVALID_CONVERSATION_HANDLE
			audio_play_for_mission_cellphone(convo.name , convo.recieveing , true, "", "m19_finish_phonecall_cb")
			while (M19_phonecall_finished == false) do
				thread_yield()
			end
			convo.handle = nil
		else
			convo.handle = audio_conversation_load(convo.name)
			audio_conversation_play(convo.handle)

			audio_conversation_wait_for_end(convo.handle)

			convo.handle = nil
		end
		--message_remove(msg_handle)
	end

	if convo.delay ~= nil and convo.delay > 0.01 then
		delay(convo.delay)
	end
end

-- Unloads a conversation
--
-- convo: Conversation to unload
function m19_unload_convo(convo)
	if convo.handle == nil then
		return
	end

	audio_conversation_end(convo.handle)
	convo.handle = nil
end

-- Unloads all conversations from the convo table
--
function m19_unload_all_convos()
	for convo_key, convo_set in pairs(M19_convo) do
		m19_unload_convo_set(convo_set)

		if convo_set.thread ~= INVALID_THREAD_HANDLE then
			thread_kill(convo_set.thread)
		end
	end
end

-- Iterates through a table of conversations and plays them.
--
-- convo_set: Table of conversations
--
function m19_play_convo_set(convo_set)
	for key, convo in pairs(convo_set.files) do
		m19_play_convo(convo)
	end
end

-- Unloads a convo table (array)
--
-- convo_set Table of conversations to unload
--
function m19_unload_convo_set(convo_set)
	for key, convo in pairs(convo_set.files) do
		m19_unload_convo(convo)
	end
end

-- Starts an audio conversation as a thread
--
-- convo_table: Conversation to play
--
function m19_start_convo_thread(convo_table)
	if M19_active_convo ~= "" then
		m19_unload_convo_set(M19_active_convo)
		M19_active_convo = ""
	end
	convo_table.thread = thread_new("m19_audio_thread", convo_table)
end

-- Loads the necessary 2D personas for the heli rail sequence
--
function m19_load_personas()
	for key, persona in pairs(M19_personas) do
		persona.handle = audio_persona_load_2d(persona.name)
	end
end

-- Unloads all 2d personas
--
function m19_unload_personas()
	for key, persona in pairs(M19_personas) do
		if persona.handle ~= INVALID_PERSONA_HANDLE then
			audio_persona_remove_2d(persona.handle)
			persona.handle = INVALID_PERSONA_HANDLE
		end
	end
end

-- Gets the 2d persona handle from a character name
--
function m19_get_persona_handle(line)
	for key, persona in pairs(M19_personas) do
		if persona.name == line.character then
			return persona.handle
		end
	end

	return nil
end

-- Plays a persona line.
--
function m19_play_persona_line(line)
	local convo_active = true
	while(M19_active_convo ~= "" and convo_active == true) do
		convo_active = false
		for key, convo in pairs(M19_active_convo.files) do
			if (convo.handle ~= nil) then
				convo_active = true
			end
		end
		thread_yield()
	end

	if line.play_2d then
		audio_play_persona_line_2d(m19_get_persona_handle(line), line.name)
	else
		audio_play_persona_line(line.character, line.name)
	end
end



-- Play a audio conversation
--
-- convo_table: Table of the audio conversation files to play
--
function m19_audio_thread(convo_table)
	M19_active_convo = convo_table
	m19_play_convo_set(convo_table)
	convo_table.thread = INVALID_THREAD_HANDLE
	M19_active_convo = ""
end
