--[[
m12.lua
SR3 Mission Script
DATE: 07-02-2010
AUTHOR: Craig Williams
]]--
-- Debug flags --
-- Tweakable Parameters --
M12_WEAPON_UNEQUIP_TIME = 10000 -- Time in miliseconds that the player has to unequip their weapon
M12_DISTANCE_TO_JOSH_FOR_ZSCENE = 14.0 -- How close you can get to Josh before the ZScene starts
M12_MIN_SPAWN_DIST = 21.0 -- Minimum distance a player must be from a spawn point, in order for that spawn point to be used.
M12_DELAY_AFTER_GRAB_JOSH_ZSCENE = 2.0
M12_ELEVATOR_OPEN_PROXIMITY = 6.0
M12_GARAGE_MIN_NOTORIETY = 4
M12_GARAGE_MAX_NOTORIETY = 5
M12_NO_COPS_TIME = 120.0 -- Time that no cops spawn ambiently and police notoriety is capped at zero.
M12_ESCAPE_VEHICLE = "Script_Vehicle 004"
M12_TEMP_PISTOL_NAME = "Pistol-Gang"
-- Characters --
NPC_SUKO = "npc_suko_start" -- Part of group "homies_start_group"
NPC_JOSH = "npc_josh" -- Part of group "josh_group"
-- Groups --
-- List of all the groups in the mission
M12_groups = {
vehicle_start = {name = "vehicle_start_group"},
homies = {name = "homies_start_group"},
josh = {name = "josh_group", char = "npc_josh" },
workers_lobby = {
name = "workers_lobby_group",
members = { "worker_lobby_001", "worker_lobby_002", "worker_lobby_003", "worker_lobby_004",
"worker_lobby_005", "worker_lobby_006" } ,
second_floor_receptionist = "worker_lobby_006",
},
workers_cubical = {name = "workers_cubical_group"},
workers_studio = {
name = "workers_studio_group",
members = { "worker_studio_001", "worker_studio_002", "worker_studio_003", "worker_studio_004",
"worker_studio_005", "worker_studio_006", "worker_studio_007" },
},
stag_lobby = {
name = "stag_lobby_group",
members = { "stag_lobby_001", "stag_lobby_002", "stag_lobby_003", "stag_lobby_004", "stag_lobby_005", "stag_lobby_006", "stag_lobby_007",}
},
stag_cubical = {name = "stag_cubical_group"},
stag_studio = {
name = "stag_studio_group",
members = { "stag_studio_001", "stag_studio_002", "stag_studio_003", "stag_studio_004", "stag_studio_005" }
},
lobby_escape = {name = "lobby_escape_group"},
garage_cars = {
name = "temp_car_group",
vehicles = {"Script_Vehicle 003", "Script_Vehicle 004", "Script_Vehicle 005", "Script_Vehicle 006",
"Script_Vehicle 008", "Script_Vehicle 009", },
},
stag_window_rush = {
name = "stag_window_rush_group",
chars = {
{name = "stag_window_rush_01", nav = "nav_win_rush 01", behavior = "advance"},
--{name = "stag_window_rush_02", nav = "nav_win_rush 02", behavior = "advance"},
},
triggers = {"trigger_window_rush"},
},
stag_studio_rush_01 = {
name = "stag_studio_rush_group 001",
chars = {
{ name = "stag_studio_rush_01 001", nav = "nav_studio_rush_01 001", behavior = "advance" },
{ name = "stag_studio_rush_01 002", nav = "nav_studio_rush_01 002", behavior = "advance" },
},
triggers = { "trigger_stag_studio_rush 001" },
},
stag_studio_rush_02 = {
name = "stag_studio_rush_group 002",
chars = {
{ name = "stag_studio_rush_02 001", nav = "nav_studio_rush_02 001", behavior = "take_cover" },
{ name = "stag_studio_rush_02 002", nav = "nav_studio_rush_02 002", behavior = "take_cover" },
{ name = "stag_studio_rush_02 003", nav = "nav_studio_rush_02 003", behavior = "take_cover" },
},
triggers = { "trigger_stag_studio_rush 002" },
},
stag_outside_garage = {
name = "stag_outside_garage_group",
vehicle_paths = {
{
vehicle = "vehicle_outside_garage <001>",
members = {"stag_outside_garage <001>",},
path = "outside_garage_path <001>",
thread = INVALID_THREAD_HANDLE,
},
{
vehicle = "vehicle_outside_garage <002>",
members = {"stag_outside_garage <002>",},
path = "outside_garage_path <002>",
thread = INVALID_THREAD_HANDLE,
},
},
},
stag_chasers_001 = {
name = "stag_chase_group 001",
vehicles = {
{
vehicle = "vehicle_chaser 001",
driver = "stag_chaser_driver 001",
gunner = "stag_chaser_gunner 001",
},
{
vehicle = "vehicle_chaser 002",
driver = "stag_chaser_driver 002",
gunner = "stag_chaser_gunner 002",
},
},
},
stag_chasers_002 = {
name = "stag_chase_group 002",
vehicles = {
{
vehicle = "vehicle_chaser 003",
driver = "stag_chaser_driver 003",
gunner = "stag_chaser_gunner 003",
},
{
vehicle = "vehicle_chaser 004",
driver = "stag_chaser_driver 004",
gunner = "stag_chaser_gunner 004",
},
},
},
fleeing_peds_001 = {
name = "fleeing_ped_group 001",
members = {"fleeing_ped <001>", "fleeing_ped <002>",},
navs = {"nav_ped_flee <001>", "nav_ped_flee <002>",},
},
}
-- list of spawn groups and their associated spawn regions.
-- See m12_setup_spawn_group()
M12_spawn_groups = {
cubical_wave_1 = {
regions = {"cubical_region 002"},
groups = {"stag_spawn_group 004"},
},
cubical_wave_2 = {
regions = {"cubical_region 001", "cubical_region 002"},
groups = {"stag_spawn_group 005"},
},
lobby_escape = {
regions = {"lobby_region 001", "lobby_region 002"},
groups = {"lobby_spawn_group 001", "lobby_spawn_group 002"},
}
}
-- Map between group members and the nav to call ai_do_scripted_move() on.
-- This should be used with m12_move_members()
M12_scripted_move = {
stag_cubical = {
members = { "stag_cubical_001", "stag_cubical_002", "stag_cubical_003", "stag_cubical_004",
"stag_cubical_005", "stag_cubical_006", "stag_cubical_007", "stag_cubical_008" },
navs = { "cubical_nav 001", "cubical_nav 002", "cubical_nav 003", "cubical_nav 004",
"cubical_nav 005", "cubical_nav 006", "cubical_nav 007", "cubical_nav 008" }
}
}
-- Navpoints --
M12_nav = {
player_car_start = {
"player_car_start_local", "player_car_start_remote",
},
lobby_desk = "nav_lobby_desk_ped",
-- List of nav points for Josh to flee to if he escapes
josh_flee_to = {
"nav_josh_flee_to 001", "nav_josh_flee_to 002", "nav_josh_flee_to 003", "nav_josh_flee_to 004", "nav_josh_flee_to 005",
"nav_josh_flee_to 006", "nav_josh_flee_to 007", "nav_josh_flee_to 008", "nav_josh_flee_to 009",
},
-- Lists of nav points to teleport the player and Josh to if Josh is dropped while over the player's shoulder.
josh_dropped = {
floor_3 = {
"nav_josh_dropped_3f_1", "nav_josh_dropped_3f_2", "nav_josh_dropped_3f_3",
},
floor_2 = {
"nav_josh_dropped_2f_1", "nav_josh_dropped_2f_2",
},
lobby = {
"nav_josh_dropped_1f_1", "nav_josh_dropped_1f_2", "nav_josh_dropped_1f_3", "nav_josh_dropped_1f_4",
"nav_josh_dropped_1f_5", "nav_josh_dropped_1f_6", "nav_josh_dropped_1f_7", "nav_josh_dropped_1f_8",
},
garage = {
"nav_josh_dropped_garage",
},
},
-- Location to teleport the player to after a mission completion/failure
saints_hq_local = "nav_saints_hq_local",
saints_hq_remote = "nav_saints_hq_remote",
-- Mission objective nav points
pr_center_enterance = "nav_pr_center_entrance",
elevator_to_studio = "nav_elevator_to_studio",
stairs_to_cubical = "nav_stairs_to_cubical", -- On the studio level
stairs_to_studio = "nav_stairs_to_studio", -- On the cubical level
elevator_to_lobby = "nav_elevator_to_lobby",
elevator_to_garage = "nav_elevator_to_garage",
saints_hq_enterance = "nav_saints_hq_enterance",
load_josh = "nav_load_josh",
-- Ambient life nav points
stag_pace = { "nav_stag_pace_001", "nav_stag_pace_002", "nav_stag_pace_003" },
}
-- Teleportation Navpoints --
-- Teleportation Navpoint tables. The following tables map to
-- [1] - Local Player
-- [2] - Remote Player (if M12_in_coop)
-- [3] - Suko
-- [4] - Josh
-- See the helper function m12_teleport_team_to()
M12_teleport = {
start = {"nav_start_local", "nav_start_remote", "nav_start_suko", "nav_studio_josh"},
lobby = {"nav_lobby_local", "nav_lobby_remote", "nav_lobby_suko", "nav_studio_josh"},
studio = {"nav_studio_local", "nav_studio_remote", "nav_studio_suko", "nav_studio_josh"},
have_josh = {"nav_have_josh_local", "nav_have_josh_remote", "nav_have_josh_suko", "nav_have_josh_josh"},
cubical = {"nav_cubical_local", "nav_cubical_remote", "nav_cubical_suko", "nav_cubical_josh"},
lobby_elevator = {"nav_lobby_elevator_local", "nav_lobby_elevator_remote", "nav_lobby_elevator_suko", "nav_lobby_elevator_josh"},
nav_garage = {"nav_garage_local", "nav_garage_remote", "nav_garage_suko", "nav_garage_josh"},
garage_outside = {"nav_garage_outside_local", "nav_garage_outside_remote", "nav_garage_outside_suko", "nav_garage_outside_josh"},
}
-- Triggers --
M12_trigger = {
pr_center_front = "trigger_pr_center_front",
pr_center_enterance = "trigger_pr_center_enterance",
reception = "trigger_reception",
door_to_josh = "trigger_door_to_josh",
-- elev_conv_01 = "trigger_elev_conv 01",
elev_conv_02 = "trigger_elev_conv 02",
studio_01 = "trigger_studio 001",
studio_02 = "trigger_studio 002",
studio_03 = "trigger_studio 003",
studio_door_to_stairs = "trigger_studio_door_to_stairs",
stairs_marker = "trigger_stairs_marker",
cubicle_01 = "trigger_cubicle 001",
start_spawning = "trigger_start_spawning",
lobby_spawn = "trigger_lobby_spawn",
garage_door = "trigger_garage_door",
saints_hq = "trigger_saints_hq",
saints_hq_freeze = "trigger_saints_hq_freeze",
lobby_middle = "trigger_lobby_middle",
lobby_floor = "trigger_lobby_floor",
lobby_top = "trigger_lobby_top",
lobby_enter_1 = "trigger_lobby_enter 001",
lobby_enter_2 = "trigger_lobby_enter 002",
lobby_exit = "trigger_lobby_exit 001",
stag_outside_garage = "trigger_stag_outside_garage",
chasers_1 = "trigger_start_chasers 001",
chasers_2 = "trigger_start_chasers 002",
fleeing_peds_1 = "trigger_fleeing_peds 001",
load_josh = "trigger_load_josh",
block_traffic = "tgr_block_traffic 001",
}
-- Elevator Door Movers --
M12_elevator = {
lobby_lower = {
left = {name = "door_lobby_lower_left", trigger = "trigger_lobby_lower_left", add_marker = false,},
right = {name = "door_lobby_lower_right", trigger = "trigger_lobby_lower_right", add_marker = true,},
},
lobby_upper = {
left = {name = "door_lobby_upper_left", trigger = "trigger_lobby_upper_left", add_marker = true,},
right = {name = "door_lobby_upper_right", trigger = "trigger_lobby_upper_right", add_marker = false,},
},
cubical = {
left = {name = "door_cubical_left", trigger = "trigger_cubical_left", add_marker = true,},
--right = {name = "door_cubical_right", trigger = "trigger_cubical_right", add_marker = false,},
},
studio = {
left = {name = "door_studio_left", trigger = "trigger_studio_left", add_marker = false,},
},
}
M12_front_doors = {
"lobby_front_door 001", "lobby_front_door 003",
}
-- Trigger Flags --
M12_in_coop = false
M12_weapon_equipped = false
-- m12_goto_a_trigger() flags
M12_trigger_reached = false
M12_fleeing_peds_triggered = false
-- m12_goto_a_elevator() flags
M12_player_in_elevator = false -- Local or remote player is in a elevator
M12_local_in_elevator = false
M12_remote_in_elevator = false
M12_josh_in_elevator = false
-- Text --
M12_OBJ_UNEQUIP_YOUR_WEAPON = {name = "m12_obj_unequip_your_weapon", icon = OI_ASSET_USE,}
M12_OBJ_GO_TO_THE_PR_CENTER = {name = "m12_obj_go_to_the_pr_center", icon = OI_ASSET_LOCATION,}
M12_OBJ_ENTER_THE_PR_CENTER = {name = "m12_obj_enter_the_pr_center", icon = OI_ASSET_LOCATION,}
M12_OBJ_ENTER_THE_ELEVATOR = {name = "m12_obj_enter_the_elevator", icon = OI_ASSET_LOCATION,}
M12_OBJ_GO_TO_RECEPTION = {name = "m12_obj_go_to_reception_desk", icon = OI_ASSET_LOCATION,}
M12_OBJ_FIND_JOSH_BIRK = {name = "m12_obj_find_josh_birk", icon = OI_ASSET_LOCATION,}
M12_OBJ_GRAB_JOSH_BIRK = {name = "m12_obj_grab_josh_birk", icon = OI_ASSET_USE,}
M12_OBJ_ESCAPE_THE_STAG_BUILDING = {name = "m12_obj_escape_the_stag_building",icon = OI_ASSET_LOCATION,}
M12_OBJ_GET_TO_THE_ELEVATORS = {name = "m12_obj_get_to_the_elevators", icon = OI_ASSET_LOCATION,}
M12_OBJ_GET_TO_THE_GARAGE = {name = "m12_obj_get_to_the_garage", icon = OI_ASSET_LOCATION,}
M12_OBJ_STEAL_A_VEHICLE = {name = "m12_obj_steal_a_vehicle", icon = OI_ASSET_USE,}
M12_OBJ_PICKUP_JOSH_BIRK = {name = "m12_pickup_josh_birk", icon = OI_ASSET_USE,}
M12_OBJ_ESCAPE_TO_THE_SAINTS_HQ = {name = "m12_obj_escape_to_the_saints_hq", icon = OI_ASSET_LOCATION,}
M12_FAILURE_SUKO_DIED = "m12_failure_suko_died"
M12_FAILURE_JOSH_DIED = "m12_failure_josh_died"
M12_FAILURE_COVER_BLOWN = "m12_failure_cover_blown"
M12_FAILURE_JOSH_ABANDONED = "m12_failure_josh_abandoned"
M12_FAILURE_SUKO_ABANDONED = "m12_failure_suko_abandoned"
-- Threads --
M12_thread = {
no_cops = INVALID_THREAD_HANDLE,
unequip_weapon = INVALID_THREAD_HANDLE,
hs_check = INVALID_THREAD_HANDLE,
pickup_josh = INVALID_THREAD_HANDLE,
josh_shield_check = INVALID_THREAD_HANDLE,
stop_player_car = INVALID_THREAD_HANDLE,
play_line = INVALID_THREAD_HANDLE,
}
-- Checkpoints --
M12_CHECKPOINT_START = MISSION_START_CHECKPOINT -- defined in ug_lib.lua
M12_CHECKPOINT_GRAB_JOSH = "m12_checkpoint_grab_josh"
M12_CHECKPOINT_CUBICAL_BATTLE = "m12_checkpoint_cubical_battle"
M12_CHECKPOINT_ESCAPE = "m12_checkpoint_escape"
-- Cutscenes --
CUTSCENE_MISSION_INTRO = "12_In"
CUTSCENE_GRAB_JOSH = "12_z01"
CUTSCENE_MISSION_OUTRO = "12_Out"
-- Music
M12_music = {
josh_grabbed = "M12_music_escape_PR_center",
josh_loaded_into_car = "M12_music_escape_PR_center_end",
garage_exited = "M12_music_drive_to_Saints_HQ",
arrived_at_saints_hq = "M12_music_drive_to_Saints_HQ_end",
restart_drive_to_hq = "M12_music_Saints_HQ_Checkpoint",
restart_pr_center = "M12_music_escape_PR_Checkpoint",
stop = "M12_music_STOP",
}
-- Personas --
M12_personas = {
{ name = "Suko", handle = INVALID_PERSONA_HANDLE },
{ name = "Interrogation", handle = INVALID_PERSONA_HANDLE },
{ name = "Josh", handle = INVALID_PERSONA_HANDLE },
}
M12_persona_line = {
hide_guns = { name = "M12_Hide_Guns", character = LOCAL_PLAYER, play_2d = false },
up_on_floor_7 = { name = "M12_Elevator", character = "worker_lobby_006", play_2d = false },
thats_him = { name = "M12_Found_Josh", character = "Suko", play_2d = true },
secure_building = { name = "M12_Secure_Building", character = "", play_2d = false },
stunned_01 = { name = "M12_Josh_Stunned_01", character = NPC_JOSH, play_2d = false },
stunned_02 = { name = "M12_Josh_Stunned_02", character = NPC_JOSH, play_2d = false },
stunned_03 = { name = "M12_Josh_Stunned_03", character = NPC_JOSH, play_2d = false },
stunned_04 = { name = "M12_Josh_Stunned_04", character = NPC_JOSH, play_2d = false },
stunned_05 = { name = "M12_Josh_Stunned_05", character = NPC_JOSH, play_2d = false },
stunned_06 = { name = "M12_Josh_Stunned_06", character = NPC_JOSH, play_2d = false },
stunned_07 = { name = "M12_Josh_Stunned_07", character = NPC_JOSH, play_2d = false },
stunned_08 = { name = "M12_Josh_Stunned_08", character = NPC_JOSH, play_2d = false },
stunned_09 = { name = "M12_Josh_Stunned_09", character = NPC_JOSH, play_2d = false },
stunned_10 = { name = "M12_Josh_Stunned_10", character = NPC_JOSH, play_2d = false },
ele_to_lobby = { name = "M12_See_Elevator_To_Lobby", character = LOCAL_PLAYER, play_2d = false },
ele_to_garage = { name = "M12_See_Elevator_To_Garage", character = "Suko", play_2d = true },
get_in = { name = "M12_Get_In_Car", character = LOCAL_PLAYER, play_2d = false },
}
-- Conversations --
-- Table of audio conversations to play. Either a actual audio file can be specifed, or a message can be used in its place.
-- Expected Table Members:
-- name - Name of the audio file to play
-- delay - Amount of time (in seconds) to delay after a audio file has played
-- message - Supplementary message to display if no audio file is provided
M12_convo = {
-- Stars along with the mission
to_pr_center = {
files = {
{name = "m12_convo_1", delay = 15.0,},
{name = "m12_begin_drive_to_center", delay = 15.0,},
{name = "m12_drive_to_center", delay = 0.0,},
},
thread = INVALID_THREAD_HANDLE,
},
-- Starts when you enter the PR Center lobby
to_studio = {
files = {
{name = "m12_arrive_at_center", delay = 4.0,},
{name = "m12_convo_3", delay = 0.0,},
},
thread = INVALID_THREAD_HANDLE,
},
-- Starts when Josh is taken hostage, after the Z-Scene
escaping_01 = {
files = {
{name = "m12_convo_5", delay = 2.0,},
{name = M12_persona_line.stunned_01, delay = 6.0,},
{name = "m12_office_combat", delay = 6.0,},
{name = M12_persona_line.stunned_02, delay = 15.0,},
{name = M12_persona_line.stunned_03, delay = 15.0,},
{name = M12_persona_line.stunned_04, delay = 15.0,},
{name = M12_persona_line.stunned_05, delay = 15.0,},
},
thread = INVALID_THREAD_HANDLE,
},
-- Starts when the player enters the cubicle area
escaping_02 = {
files = {
{name = M12_persona_line.stunned_06, delay = 10.0,},
{name = M12_persona_line.stunned_07, delay = 6.0,},
{name = "m12_lobby_combat", delay = 6.0,},
{name = M12_persona_line.stunned_08, delay = 12.0,},
{name = M12_persona_line.stunned_09, delay = 13.0,},
{name = M12_persona_line.stunned_10, delay = 14.0,},
},
thread = INVALID_THREAD_HANDLE,
},
-- Starts when player tries to take upper elevators after taking Josh
elevator = {
files = {
{name = "m12_elevator_blocked", delay = 0.0,},
},
thread = INVALID_THREAD_HANDLE,
},
-- Starts when the player enters the lobby with Josh.
lobby_escape = {
files = {
{name = "m12_enter_lobby", delay = 10.0,},
},
thread = INVALID_THREAD_HANDLE,
},
-- Played while the player gets into the garage.
flee_to_hq = {
files = {
{name = "M12_Escape_To_HQ", delay = 0.0,},
},
thread = INVALID_THREAD_HANDLE,
},
-- Played when the player gets into the car.
flee_to_hq_nightblade = {
files = {
{name = "M12_Escape_To_HQ_Nightblade", delay = 10.0,},
},
thread = INVALID_THREAD_HANDLE,
},
}
M12_runtime = {
ambush_groups = { }
}
-- Other --
M12_STAG_NOTORIETY = "police"
-- Variables --
M12_active_triggers = {}
M12_current_obj = ""
M12_secondary_obj = ""
M12_josh_flee_to_nav = false
M12_begin_load_josh = false
M12_escape_vehicle_entered = false
M12_inv_restricted_for_carrying_josh = false
M12_cur_josh_dropped_nav_list = {}
M12_active_convo = ""
-- *************************
--
-- 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
--
-- m12_checkpoint: The checkpoint the mission should begin at
-- is_restart: TRUE if the mission is restarting, FALSE otherwise
--
function m12_start(m12_checkpoint, is_restart)
M12_in_coop = coop_is_active()
-- Check if this mission starting from the beginning
if (m12_checkpoint == M12_CHECKPOINT_START) then
if (is_restart == false) then
-- First time playing mission
cutscene_play(CUTSCENE_MISSION_INTRO, nil, {M12_teleport.start[1], M12_teleport.start[2]}, false)
end
end
-- Handle mission initialization for the current checkpoint
m12_initialize(m12_checkpoint, is_restart)
-- Run the mission from the current checkpoint
m12_run(m12_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 m12_run(first_checkpoint)
local current_checkpoint = first_checkpoint
-- Warp to the Saints HQ if the player cancels.
mission_set_cancel_warp_location(M12_nav.saints_hq_local, M12_nav.saints_hq_remote)
-- Run the mission from the beginning
if current_checkpoint == M12_CHECKPOINT_START then
m12_start_convo_thread(M12_convo.to_pr_center)
m12_goto_a_trigger(M12_trigger.pr_center_front, M12_OBJ_GO_TO_THE_PR_CENTER, true)
m12_cleanup_start_world()
m12_setup_start_lobby()
m12_enter_stealth_section()
m12_dont_allow_weapons()
m12_unload_all_convos()
m12_goto_a_trigger(M12_trigger.pr_center_enterance, M12_OBJ_ENTER_THE_PR_CENTER, false)
m12_goto_a_trigger(M12_trigger.reception, M12_OBJ_GO_TO_RECEPTION, false)
m12_start_convo_thread(M12_convo.to_studio)
delay(6.0)
ai_do_scripted_move(M12_groups.workers_lobby.second_floor_receptionist, M12_nav.lobby_desk)
-- Additional breadcrumbing. Re-add if people have trouble getting to the upper elevators.
m12_goto_a_trigger(M12_trigger.lobby_middle, M12_OBJ_ENTER_THE_ELEVATOR, false, M12_trigger.lobby_enter_1)
m12_goto_a_trigger(M12_trigger.lobby_top, M12_OBJ_ENTER_THE_ELEVATOR, false, M12_trigger.lobby_enter_2)
-- m12_setup_trigger(M12_trigger.elev_conv_01, "m12_elev_persona_cb", "")
m12_goto_a_elevator(M12_elevator.lobby_upper, M12_OBJ_ENTER_THE_ELEVATOR, false)
--turn_to(M12_groups.workers_lobby.second_floor_receptionist, LOCAL_PLAYER)
m12_unload_all_convos()
m12_cleanup_start_lobby()
m12_teleport_team_to(M12_teleport.studio)
current_checkpoint = M12_CHECKPOINT_GRAB_JOSH
mission_set_checkpoint(current_checkpoint, true)
end
if current_checkpoint == M12_CHECKPOINT_GRAB_JOSH then
character_ragdoll_set_last_resort_position(M12_teleport.studio[1])
zscene_prep(CUTSCENE_GRAB_JOSH)
m12_setup_grab_josh()
m12_enter_stealth_section()
m12_dont_allow_weapons()
m12_play_persona_line(M12_persona_line.thats_him)
M12_cur_josh_dropped_nav_list = M12_nav.josh_dropped.floor_3
m12_goto_a_trigger(M12_trigger.door_to_josh, M12_OBJ_FIND_JOSH_BIRK, false)
m12_set_objective(M12_OBJ_GRAB_JOSH_BIRK)
m12_wait_until_josh_is_in_range()
m12_allow_weapons() -- Re-enable weapons before the cutscene so the player doesn't fail the mission while it is playing
m12_play_z_scene()
m12_teleport_team_to(M12_teleport.have_josh, true, true, true)
door_lock(M12_elevator.studio.left.name, true)
m12_unload_all_convos()
group_create_hidden(M12_groups.stag_window_rush.name)
group_create_hidden(M12_groups.stag_studio_rush_01.name)
group_create_hidden(M12_groups.stag_studio_rush_02.name)
m12_setup_trigger(M12_trigger.elev_conv_02, "m12_start_convo_elevator", "")
m12_setup_trigger(M12_trigger.fleeing_peds_1, "m12_start_fleeing_peds_1", "")
-- Kick off some music.
m12_post_music_event(M12_music.josh_grabbed)
--m12_studio_window_rush group. After z-scene these guys rush past the window in front of the player
group_show(M12_groups.stag_window_rush.name)
m12_helper_setup_ambush_group(M12_groups.stag_window_rush)
-- Allow the patroling guys to enter combat now.
npc_combat_enable(M12_groups.stag_studio.members[1], true)
npc_combat_enable(M12_groups.stag_studio.members[4], true)
--Too many enemies are drawing the player the wrong way
object_destroy(M12_groups.stag_studio.members[2])
object_destroy(M12_groups.stag_studio.members[3])
-- Give the player a brief amount of time to adjust to the new situation before we
-- let the STAG attack.
-- delay(M12_DELAY_AFTER_GRAB_JOSH_ZSCENE)
-- Tell the NPCs to attack the player(s)
for key, npc in pairs(M12_groups.stag_studio.members) do
set_attack_enemies_flag(npc, true)
end
for i, npc in pairs(M12_groups.workers_studio.members) do
flee(npc, LOCAL_PLAYER)
end
m12_exit_stealth_section()
m12_start_convo_thread(M12_convo.escaping_01)
m12_goto_a_trigger(M12_trigger.studio_01, M12_OBJ_ESCAPE_THE_STAG_BUILDING, false)
group_show(M12_groups.stag_studio_rush_01.name)
m12_helper_setup_ambush_group(M12_groups.stag_studio_rush_01)
m12_goto_a_trigger(M12_trigger.studio_02, M12_OBJ_ESCAPE_THE_STAG_BUILDING, false)
group_show(M12_groups.stag_studio_rush_02.name)
m12_helper_setup_ambush_group(M12_groups.stag_studio_rush_02)
M12_thread.play_line = thread_new("m12_find_nearest_guy_and_play_line", M12_groups.stag_studio_rush_02, M12_persona_line.secure_building, 15.0, 1.25)
m12_goto_a_trigger(M12_trigger.studio_03, M12_OBJ_ESCAPE_THE_STAG_BUILDING, false)
m12_goto_a_trigger(M12_trigger.studio_door_to_stairs, M12_OBJ_ESCAPE_THE_STAG_BUILDING, false, M12_trigger.stairs_marker)
-- Don't clean up the convos since they can continue into the cubical scene
m12_cleanup_grab_josh()
current_checkpoint = M12_CHECKPOINT_CUBICAL_BATTLE
mission_set_checkpoint(current_checkpoint, true)
end
if current_checkpoint == M12_CHECKPOINT_CUBICAL_BATTLE then
character_ragdoll_set_last_resort_position(M12_teleport.cubical[1])
M12_cur_josh_dropped_nav_list = M12_nav.josh_dropped.floor_2
m12_setup_cubical_battle()
m12_start_convo_thread(M12_convo.escaping_02)
m12_goto_a_trigger(M12_trigger.cubicle_01, M12_OBJ_ESCAPE_THE_STAG_BUILDING, false)
m12_goto_a_elevator(M12_elevator.cubical, M12_OBJ_ENTER_THE_ELEVATOR, true)
m12_cleanup_cubical_battle()
-- Set the next set of recovery navs before teleporting in case something happens.
M12_cur_josh_dropped_nav_list = M12_nav.josh_dropped.lobby
m12_teleport_team_to(M12_teleport.lobby_elevator, true, true, true)
current_checkpoint = M12_CHECKPOINT_ESCAPE
mission_set_checkpoint(current_checkpoint, true)
end
if current_checkpoint == M12_CHECKPOINT_ESCAPE then
character_ragdoll_set_last_resort_position(M12_teleport.lobby_elevator[1])
M12_cur_josh_dropped_nav_list = M12_nav.josh_dropped.lobby
M12_josh_flee_to_nav = false
m12_setup_escape_lobby()
m12_goto_a_trigger(M12_trigger.lobby_middle, M12_OBJ_ESCAPE_THE_STAG_BUILDING, false, M12_trigger.lobby_exit)
m12_goto_a_elevator(M12_elevator.lobby_lower, M12_OBJ_ENTER_THE_ELEVATOR, true)
m12_cleanup_escape_lobby()
M12_cur_josh_dropped_nav_list = M12_nav.josh_dropped.garage
m12_teleport_team_to(M12_teleport.nav_garage, true, true, true)
character_ragdoll_set_last_resort_position(M12_teleport.nav_garage[1])
m12_load_josh_into_car()
m12_setup_escape_garage()
M12_thread.pickup_josh = thread_new("m12_thread_pickup_josh_marker")
--m12_start_convo_thread(M12_convo.flee_to_hq)
m12_goto_a_trigger(M12_trigger.garage_door, M12_OBJ_ESCAPE_TO_THE_SAINTS_HQ, false)
-- Stop using the mission specified ragdoll get up last resort.
character_ragdoll_clear_last_resort_position()
-- Progress the musics.
m12_post_music_event(M12_music.garage_exited)
-- Use the innards of m12_goto_a_trigger so we can play the conversation after setting up the waypoint.
--m12_goto_a_trigger(M12_trigger.saints_hq, M12_OBJ_ESCAPE_TO_THE_SAINTS_HQ, true)
m12_goto_a_trigger_setup(M12_trigger.saints_hq, M12_OBJ_ESCAPE_TO_THE_SAINTS_HQ, true, nil)
trigger_enable(M12_trigger.saints_hq_freeze, true)
on_trigger("m12_hq_freeze_cb", M12_trigger.saints_hq_freeze)
delay(15.0)
m12_start_convo_thread(M12_convo.flee_to_hq_nightblade)
while not M12_trigger_reached do
thread_yield()
end
m12_goto_a_trigger_cleanup(M12_trigger.saints_hq, true, nil)
-- Progress the musics.
m12_post_music_event(M12_music.arrived_at_saints_hq)
m12_cleanup_escape_garage()
-- M12_thread.stop_player_car = thread_new("m12_stop_player_car_thread")
end
mission_end_success("m12", CUTSCENE_MISSION_OUTRO, {M12_nav.saints_hq_local, M12_nav.saints_hq_remote})
end
-- This is the primary function responsible for cleaning up the entire mission
-- CALLED FROM CODE (+++MUST RETURN IMMEDIATLY+++)
--
function m12_cleanup()
--[[ INSERT ANY MISSION SPECIFIC CLEAN-UP ]]--
-- The mission may have failed due to a remote player disconnecting. Double check to see if they are connected.
if M12_in_coop then
M12_in_coop = coop_is_active()
end
m12_clear_seconodary_objective()
m12_clear_objective()
spawn_region_min_spawn_dist_reset()
m12_exit_stealth_section()
m12_allow_weapons()
m12_unload_all_threads()
m12_unload_personas()
m12_cleanup_all_spawn_groups()
-- Cleanup all checkpoints
m12_cleanup_start_world()
m12_cleanup_start_lobby()
m12_cleanup_grab_josh()
m12_cleanup_cubical_battle()
m12_cleanup_escape_lobby()
m12_cleanup_escape_garage()
m12_cleanup_load_josh_into_car()
m12_cleanup_josh()
m12_cleanup_suko()
-- Ensure all triggers are disabled
for i, trigger in pairs(M12_trigger) do
m12_cleanup_trigger(M12_trigger)
end
-- Cleanup any active elevators
for i, elevators in pairs(M12_elevator) do
m12_goto_a_elevator_cleanup(elevators)
end
if group_is_loaded(M12_groups.garage_cars.name) then
group_destroy(M12_groups.garage_cars.name)
end
-- Remove the Player(s) costumes
customization_item_revert()
-- Reenable party recruiting
party_set_recruitable(true)
-- Allow ambient cops again.
if M12_thread.no_cops ~= INVALID_THREAD_HANDLE then
thread_kill(M12_thread.no_cops)
M12_thread.no_cops = INVALID_THREAD_HANDLE
end
spawning_allow_cop_ambient_spawns(true)
notoriety_reset("police")
thread_kill(M12_thread.stop_player_car)
-- Undo anything we did to set up the player to carry Josh over the shoulder.
m12_cleanup_player_carrying_josh(true)
inv_weapon_disable_all_slots(false, SYNC_LOCAL)
M12_inv_restricted_for_carrying_josh = false
-- Stop the music.
m12_post_music_event(M12_music.stop)
-- Stop using the mission specified ragdoll get up last resort.
character_ragdoll_clear_last_resort_position()
end
-- Called when the mission has ended with success
-- CALLED FROM CODE (+++MUST RETURN IMMEDIATLY+++)
--
function m12_success()
--[[ INSERT ANY MISSION SPECIFIC SUCCESS STUFF ]]--
end
-- *************************
--
-- Local functions
--
-- *************************
-- Initialize the mission for the specified checkpoint
--
-- checkpoint: Checkpoint to initialize the mission to
--
function m12_initialize(checkpoint, is_restart)
-- Make sure the screen is completly faded out
mission_start_fade_out(0.0)
-- Set the mission author
set_mission_author("Craig Williams")
-- Common initialization
m12_initialize_common()
-- Checkpoint specific initialization
m12_initialize_checkpoint(checkpoint, is_restart)
-- Start fading in
mission_start_fade_in()
end
-- ***************************************************
-- m12_run Helper Functions
-- ***************************************************
-- Posts a music progression event.
--
function m12_post_music_event(event_name)
audio_object_post_event(event_name, nil, nil, LOCAL_PLAYER)
end
-- Sets up the player to carry Josh over the shoulder.
-- Should be called after grabbing Josh.
--
function m12_setup_player_to_carry_josh(start_shield_check_thread)
-- Don't allow the player to ragdoll or flinch, we don't
-- want to break out of the human shield animation.
character_allow_ragdoll(LOCAL_PLAYER, false)
character_prevent_flinching(LOCAL_PLAYER, true)
character_prevent_explosion_fling(LOCAL_PLAYER, true)
character_set_no_explosive_panic(LOCAL_PLAYER, true)
character_set_never_catch_fire(LOCAL_PLAYER, true)
character_allow_ragdoll(NPC_JOSH, false)
character_prevent_flinching(NPC_JOSH, true)
character_prevent_explosion_fling(NPC_JOSH, true)
character_set_no_explosive_panic(NPC_JOSH, true)
character_set_never_catch_fire(NPC_JOSH, true)
turn_invulnerable(NPC_JOSH)
-- The OTS animations are only set up for the pistol, smg, and rifle.
if M12_inv_restricted_for_carrying_josh == false then
inv_weapon_disable_all_slots(true, SYNC_LOCAL)
inv_weapon_disable_slot(WEAPON_SLOT_PISTOL, false, SYNC_LOCAL)
inv_weapon_disable_slot(WEAPON_SLOT_SMG, false, SYNC_LOCAL)
inv_weapon_disable_slot(WEAPON_SLOT_RIFLE, false, SYNC_LOCAL)
-- Equip the pistol for starters.
inv_item_equip_slot("best", LOCAL_PLAYER)
M12_inv_restricted_for_carrying_josh = true
end
-- Prevent the player from leaving the human shield by pressing Y.
player_human_shield_lock(LOCAL_PLAYER, true)
-- Can't throw grenades.
player_grenades_disable()
-- If at any time Josh becomes un-human shielded, he dies and the mission fails.
if start_shield_check_thread then
M12_thread.josh_shield_check = thread_new("m12_handle_josh_dropped_thread")
end
end
-- Cleans up settings for carrying Josh over the shoulder.
-- Should be called before dropping Josh.
--
function m12_cleanup_player_carrying_josh(end_shield_check_thread)
if group_is_loaded(M12_groups.josh) then
ai_clear_scripted_action(NPC_JOSH)
character_allow_ragdoll(NPC_JOSH, true)
character_prevent_flinching(NPC_JOSH, false)
character_prevent_explosion_fling(NPC_JOSH, false)
character_set_no_explosive_panic(NPC_JOSH, false)
character_set_never_catch_fire(NPC_JOSH, false)
turn_vulnerable(NPC_JOSH)
end
player_release_human_shield(LOCAL_PLAYER, true)
character_allow_ragdoll(LOCAL_PLAYER, true)
character_prevent_flinching(LOCAL_PLAYER, false)
character_prevent_explosion_fling(LOCAL_PLAYER, false)
character_set_no_explosive_panic(LOCAL_PLAYER, false)
character_set_never_catch_fire(LOCAL_PLAYER, false)
player_human_shield_lock(LOCAL_PLAYER, false)
player_grenades_enable()
if end_shield_check_thread then
thread_kill(M12_thread.josh_shield_check)
M12_thread.josh_shield_check = INVALID_THREAD_HANDLE
end
end
-- Open and unlocks the front doors of the PR Center
--
function m12_open_front_doors()
for key, door in pairs(M12_front_doors) do
door_lock(door, false)
door_open(door)
end
end
-- Closes and locks the front doors of the PR Center
--
function m12_lock_front_doors()
for key, door in pairs(M12_front_doors) do
door_close(door)
door_lock(door, true)
end
end
-- Unlocks the front doors of the PR Center
--
function m12_unlock_front_doors()
for key, door in pairs(M12_front_doors) do
door_lock(door, false)
end
end
-- Waits until a player is in range of Josh
--
function m12_wait_until_josh_is_in_range()
marker_add(NPC_JOSH, MINIMAP_ICON_USE, OI_ASSET_USE, OI_FLAGS_DEFAULT, SYNC_ALL)
if M12_in_coop then
while get_dist(LOCAL_PLAYER, NPC_JOSH) > M12_DISTANCE_TO_JOSH_FOR_ZSCENE and
get_dist(REMOTE_PLAYER, NPC_JOSH) > M12_DISTANCE_TO_JOSH_FOR_ZSCENE do
thread_yield()
end
else
while get_dist(LOCAL_PLAYER, NPC_JOSH) > M12_DISTANCE_TO_JOSH_FOR_ZSCENE do
thread_yield()
end
end
marker_remove(NPC_JOSH, SYNC_ALL)
end
-- Teleports the team to the specified nav points. This function will also
-- put Josh into a human shield state if necessary
--
-- nav_table - Nav points to teleport the group to
-- tele_josh - Should Josh be teleported?
-- josh_over_shoulder - Whether or not Josh should be put over the player's shoulder.
-- do_fade_out - Whether or not we should fade out/in
--
function m12_teleport_team_to(nav_table, tele_josh, josh_over_shoulder, do_fade_out)
local ignore_vehicle = true
if do_fade_out == nil or do_fade_out == true then
if fade_get_percent() == 0.0 then
fade_out(0.5)
fade_out_block()
end
end
-- Give Josh a reprieve during teleporting; don't kill him while
-- he's not a human shield.
m12_cleanup_player_carrying_josh(true)
teleport_coop(nav_table[1], nav_table[2], ignore_vehicle)
-- If anyone is downed, revive them. Some weird crap happens once in a blue moon
-- if someone is ragdolled while teleporting them between interiors.
if human_is_downed(NPC_SUKO) then
npc_revive(NPC_SUKO)
delay(3.0)
end
while character_is_ragdolled(NPC_SUKO) do
delay(0.25)
end
teleport(NPC_SUKO, nav_table[3], true)
if tele_josh then
teleport(NPC_JOSH, nav_table[4], ignore_vehicle)
end
if josh_over_shoulder and m12_who_is_holding_josh() == "" then
-- Wait for Josh to be ready if necessary, but only wait so long.
local delay_handle = thread_new("delay", 5.0)
while character_is_ready(NPC_JOSH) == false and thread_check_done(delay_handle) == false do
thread_yield()
end
player_take_human_shield(LOCAL_PLAYER, NPC_JOSH)
m12_setup_player_to_carry_josh(true)
end
if do_fade_out == nil or do_fade_out == true then
fade_in(0.5)
fade_in_block()
end
end
-- Iterates through a table of npcs and instructs them to move to the specified destination
--
-- group - table with the following memebers:
-- members - List of NPCs in the group
-- navs - List of nav points the npcs will move to
--
function m12_move_members(group)
local n = #group.members
for i = 1, n do
ai_do_scripted_move(group.members[i], group.navs[i], true, false)
end
end
-- Setup a callback to detect notoriety events (mission failure) and setup
-- the players party to not be attacked by STAG members.
--
function m12_enter_stealth_section()
-- Register a callback to check to for anything that would blow the players cover
on_notoriety_event("m12_notoriety_increased", LOCAL_PLAYER)
-- Prevent the STAG from attacking the player and Suko
set_player_undercover_team(LOCAL_PLAYER, "STAG")
set_team(NPC_SUKO, "STAG")
if M12_in_coop then
on_notoriety_event("m12_notoriety_increased", REMOTE_PLAYER)
set_player_undercover_team(REMOTE_PLAYER, "STAG")
end
npc_combat_enable(NPC_SUKO, false)
notoriety_set_max(M12_STAG_NOTORIETY, 0)
end
-- Removes all stealth related callbacks and setting setup by
-- m12_enter_stealth_section()
--
function m12_exit_stealth_section()
hud_timer_stop(1)
hud_timer_hide(1, true)
on_notoriety_event("", LOCAL_PLAYER)
set_player_undercover_team(LOCAL_PLAYER, "None")
if M12_in_coop then
on_notoriety_event("", REMOTE_PLAYER)
set_player_undercover_team(REMOTE_PLAYER, "None")
end
npc_combat_enable(NPC_SUKO, true)
inv_item_remove_all(NPC_SUKO)
inv_item_add("Pistol-Gang", 1, NPC_SUKO, true)
set_team(NPC_SUKO, "Playas")
notoriety_set_max(M12_STAG_NOTORIETY, M12_GARAGE_MAX_NOTORIETY)
notoriety_set(M12_STAG_NOTORIETY, 1)
notoriety_set_min(M12_STAG_NOTORIETY, 1)
-- Clean up the on_take_damage callbacks that we registered on dudes.
if group_is_loaded(M12_groups.stag_studio.name) then
for i, stag in pairs(M12_groups.stag_studio.members) do
on_take_damage("", stag)
end
end
end
-- Unequips all weapons and then starts to thread to make sure the player(s)
-- do not blow their cover by having some guns out in the open for to long.
--
function m12_dont_allow_weapons()
if M12_thread.unequip_weapon == INVALID_THREAD_HANDLE then
-- Unequip all weapons from each player
inv_item_remove_all(NPC_SUKO)
inv_item_equip(nil, LOCAL_PLAYER)
on_weapon_equip("m12_weapon_was_equipped", LOCAL_PLAYER)
on_attack_performed("m12_player_fired_weapon", LOCAL_PLAYER)
-- We'll need to unequip the player's weapon again when they exit a vehicle.
if character_is_in_vehicle(LOCAL_PLAYER) then
on_vehicle_exit("m12_remove_weapon_on_vehicle_exit", LOCAL_PLAYER)
end
if M12_in_coop then
inv_item_equip(nil, REMOTE_PLAYER)
on_weapon_equip("m12_weapon_was_equipped", REMOTE_PLAYER)
on_attack_performed("m12_player_fired_weapon", REMOTE_PLAYER)
-- We'll need to unequip the player's weapon again when they exit a vehicle.
if character_is_in_vehicle(REMOTE_PLAYER) then
on_vehicle_exit("m12_remove_weapon_on_vehicle_exit", REMOTE_PLAYER)
end
end
M12_thread.unequip_weapon = thread_new("m12_thread_unequip_your_weapon")
M12_thread.hs_check = thread_new("m12_thread_human_shield_check")
end
end
-- Allows the player(s) to use weapons again
--
function m12_allow_weapons()
if M12_thread.unequip_weapon ~= INVALID_THREAD_HANDLE then
thread_kill(M12_thread.unequip_weapon)
thread_kill(M12_thread.hs_check)
on_weapon_equip("", LOCAL_PLAYER)
--Set player to his best weapon after the cutscene.
--inv_item_equip_slot( "best", LOCAL_PLAYER )
on_attack_performed("", LOCAL_PLAYER)
on_vehicle_exit("", LOCAL_PLAYER)
if M12_in_coop then
on_weapon_equip("", REMOTE_PLAYER)
on_attack_performed("", REMOTE_PLAYER)
on_vehicle_exit("", REMOTE_PLAYER)
end
hud_timer_stop(1)
m12_clear_seconodary_objective()
M12_thread.unequip_weapon = INVALID_THREAD_HANDLE
M12_thread.hs_check = INVALID_THREAD_HANDLE
end
end
-- Plays the cutscene where Josh is initially taken as a hostage
--
function m12_play_z_scene()
cutscene_play(CUTSCENE_GRAB_JOSH, nil, nil, false)
clear_animation_state(M12_groups.josh.char)
m12_set_objective(M12_OBJ_ESCAPE_THE_STAG_BUILDING)
end
-- Instructs the player(s) to goto a set of elevators.
--
-- elevators - Table of elevators with the following members
-- name - Name of the script mover that is linked to a door helper
-- trigger - Name of a trigger inside the elevator. Used to detect entry/exit.
-- obj_text - Objective text to display
-- require_josh - Does josh have to be inside an elevator as well?
--
function m12_goto_a_elevator(elevators, obj_text, require_josh)
m12_goto_a_elevator_setup(elevators, obj_text)
-- If we require Josh to be in the elevator, really we only care if the local player is in there, since
-- only the local player can carry Josh.
while not M12_player_in_elevator or (require_josh and not M12_local_in_elevator) do
m12_elevator_proximity_check(elevators)
thread_yield()
end
m12_goto_a_elevator_cleanup(elevators)
end
-- Initializes all global variables used by m12_goto_a_elevator() and enable all
-- elevator triggers to detect human entry/exit
--
-- elevator_set - Table of elevators to enable
-- obj_text - Objective text to display
--
function m12_goto_a_elevator_setup(elevator_set, obj_text)
M12_player_in_elevator = false
M12_local_in_elevator = false
M12_remote_in_elevator = false
M12_josh_in_elevator = false
M12_active_triggers = {}
for i, elevator in pairs(elevator_set) do
if elevator.add_marker then
local trigger = elevator.trigger
M12_active_triggers[i] = trigger
m12_setup_trigger(trigger, "m12_elevator_enter_cback", "m12_elevator_exit_cback")
-- Make sure the door is actually closed
door_close(elevator.name, false)
end
end
m12_set_objective(obj_text)
end
-- Disables all elevators setup by m12_goto_a_elevator_setup()
--
-- elevator_set - Table of elevators to disable
--
function m12_goto_a_elevator_cleanup(elevator_set)
for i, elevator in pairs(elevator_set) do
local trigger = elevator.trigger
m12_cleanup_trigger(trigger)
door_close(elevator.name, false)
end
m12_remove_icons_from_active_triggers()
M12_active_triggers = {}
end
function m12_add_icons_to_active_triggers()
for i, trigger in pairs(M12_active_triggers) do
marker_add_trigger(trigger, MINIMAP_ICON_LOCATION, INGAME_EFFECT_CHECKPOINT, OI_ASSET_LOCATION, OI_FLAGS_LOCATION, SYNC_ALL)
end
end
function m12_remove_icons_from_active_triggers()
for i, trigger in pairs(M12_active_triggers) do
marker_remove_trigger(trigger)
end
end
-- Checks to see if there is a nearby human. If so, open the elevator, otherwise close it.
--
-- elevator_set - List of elevators to check if they need to be opened.
--
function m12_elevator_proximity_check(elevator_set)
for i, elevator in pairs(elevator_set) do
-- Use the elevator.trigger since they are inside the elevator, rather elevator.name (door helper) which
-- are next to the elevator on the wall
local dist_to_npc, npc = get_closest_npc_to_object(elevator.trigger, M12_ELEVATOR_OPEN_PROXIMITY)
local should_open_npc = false
local should_open_player = false
if npc ~= "" then
should_open_npc = true
else
-- A script NPC is not in range. Check to see if a player is nearby.
local dist = get_dist(LOCAL_PLAYER, elevator.trigger)
if dist < M12_ELEVATOR_OPEN_PROXIMITY then
should_open_player = true
end
if M12_in_coop == true then
dist = get_dist(REMOTE_PLAYER, elevator.trigger)
if dist < M12_ELEVATOR_OPEN_PROXIMITY then
should_open_player = true
end
end
end
if should_open_player then
delay(0.5)
door_open(elevator.name, false)
elseif should_open_npc then
delay(2.0)
door_open(elevator.name, false)
else
door_close(elevator.name, true)
end
end
end
-- General task. Instructs the player to go to the specific trigger.
--
-- trigger - Name of the trigger to activate and go to.
-- obj_text - Objective text to display
-- gps - Should a gps waypoint be added?
-- marker_trigger - Alternative trigger to add a marker to. If one is not specified, the param trigger will be used instead.
--
function m12_goto_a_trigger(trigger, obj_text, gps, marker_trigger)
if gps == nil then
gps = false
end
m12_goto_a_trigger_setup(trigger, obj_text, gps, marker_trigger)
while not M12_trigger_reached do
thread_yield()
end
m12_goto_a_trigger_cleanup(trigger, gps, marker_trigger)
end
-- Set the objective text and sets up the trigger associated with the objective
--
-- trigger - Name of the trigger to activate and go to.
-- obj_text - Objective text to display
-- gps - Should a gps waypoint be added?
-- marker_trigger - Alternative trigger to add a marker to. If one is not specified, the param trigger will be used instead.
--
function m12_goto_a_trigger_setup(trigger, obj_text, gps, marker_trigger)
-- Teleportation and navagation related functions don't play nice so give pathfinding
-- and similar a frame to finialize everything.
thread_yield()
if marker_trigger ~= nil and marker_trigger ~= "" then
trigger_enable(marker_trigger, true) -- Needs to be enabled for a minimap icon to attach to it.
M12_active_triggers = {marker_trigger}
else
M12_active_triggers = {trigger}
end
-- Set up the trigger before setting the objective. The
-- minimap icon won't appear on a trigger that's disabled.
m12_setup_trigger(trigger, "m12_trigger_cback", "")
m12_set_objective(obj_text)
if gps then
waypoint_add(trigger)
end
M12_trigger_reached = false
end
-- Cleans up the trigger and any HUD elements setup by m12_goto_a_trigger_setup()
--
-- trigger - Name of the trigger that was passed to m12_goto_a_trigger_setup()
-- gps - Was a gps route requested?
--
function m12_goto_a_trigger_cleanup(trigger, gps, marker_trigger)
-- Cleanup the task.
m12_cleanup_trigger(trigger)
m12_cleanup_trigger(marker_trigger)
m12_remove_icons_from_active_triggers()
M12_active_triggers = {}
if gps then
waypoint_remove()
end
-- To be on the safe side, give pathfinding a similar a full frame to remove the nav points and such
thread_yield()
end
-- Checks to see if a player is holding Josh and sets M12_player_holding_josh to the name of the player holding Josh.
--
-- returns: (string) name of the player that is holding Josh, otherwise ""
function m12_who_is_holding_josh()
if character_has_specific_human_shield(LOCAL_PLAYER, NPC_JOSH) then
return LOCAL_PLAYER
end
if M12_in_coop then
if character_has_specific_human_shield(REMOTE_PLAYER, NPC_JOSH) then
return REMOTE_PLAYER
end
end
return ""
end
-- ***************************************************
-- m12_initialize Helper Functions
-- ***************************************************
-- Handle any common initialization
--
function m12_initialize_common()
--[[ INSERT ANY COMMON INITIALIZATION CODE HERE ]]--
spawn_region_min_spawn_dist(M12_MIN_SPAWN_DIST)
-- Forcefully set the min notoriety to level 0 so restricted zones (includes the PR Center)
-- will not overwrite the script set levels
notoriety_set_min(M12_STAG_NOTORIETY, 0)
notoriety_set_max(M12_STAG_NOTORIETY, M12_GARAGE_MAX_NOTORIETY)
notoriety_force_no_spawn(M12_STAG_NOTORIETY, true)
notoriety_force_no_spawn("stag", true)
-- Boot everyone out of the player's party and disable recruiting.
party_dismiss_all()
party_set_recruitable(false)
-- Suko should always exists
m12_setup_suko()
m12_load_personas()
customization_outfit_wear("Cardinal")
--customization_item_wear("cardinal hat", "cm_hat_cardinal.cmeshx", "Cardinal Hat")
--customization_item_wear("cardinal glasses", "cm_eye_cardinal.cmeshx", "Cardinal Glasses")
--customization_item_wear("cm_suit_cardinal", "cm_suit_cardinal.cmeshx", "Cardinal Robe")
end
-- Checkpoint specific initialization
--
-- checkpoint: The checkpoint to be initialized
function m12_initialize_checkpoint(checkpoint, is_restart)
if checkpoint == M12_CHECKPOINT_START then
m12_setup_start_world(is_restart)
-- Get the players' vehicles out of the way.
teleport_player_vehicles(M12_nav.player_car_start[1], M12_nav.player_car_start[2])
elseif checkpoint == M12_CHECKPOINT_GRAB_JOSH then
teleport_coop(M12_teleport.studio[1], M12_teleport.studio[2], true)
teleport(NPC_SUKO, M12_teleport.studio[3], true)
-- Set a fake team so suko and the player arn't attacked by the STAG or civillians
m12_enter_stealth_section()
m12_dont_allow_weapons()
elseif checkpoint == M12_CHECKPOINT_CUBICAL_BATTLE then
m12_setup_josh()
M12_cur_josh_dropped_nav_list = M12_nav.josh_dropped.floor_2
m12_teleport_team_to(M12_teleport.cubical, true, true, false)
-- Kick off some music.
m12_post_music_event(M12_music.restart_pr_center)
elseif checkpoint == M12_CHECKPOINT_ESCAPE then
m12_setup_josh()
M12_cur_josh_dropped_nav_list = M12_nav.josh_dropped.lobby
m12_teleport_team_to(M12_teleport.lobby_elevator, true, true, false)
-- Kick off some music.
m12_post_music_event(M12_music.restart_pr_center)
end
party_add(NPC_SUKO, LOCAL_PLAYER)
end
-- Teleports the player(s) to their start location and unequip any weapons since they are
-- supposed to sneak into the PR Center
--
function m12_setup_start_world(is_restart)
if is_restart == true then
teleport_coop(M12_teleport.start[1], M12_teleport.start[2], true)
end
-- Unequip all weapons from each player
inv_item_equip(nil, LOCAL_PLAYER)
inv_item_equip(nil, NPC_SUKO)
if M12_in_coop then
inv_item_equip(nil, REMOTE_PLAYER)
end
group_create(M12_groups.vehicle_start.name, true)
tutorial_start("mis_undercover", 0, true, false) --SP 7/12: Added tutorial, feel free to move it if it's in a bad place
-- Disable cop spawns and prevent the player from getting police notoriety.
-- This prevents an annoying mission fail when you get to the PR center and
-- have the guards attack you.
spawning_allow_cop_ambient_spawns(false)
notoriety_set_max("police", 0)
-- Start a timer to reset police spawning. We don't want the player to exploit
-- the fact that there are no cops for too long.
M12_thread.no_cops = thread_new("m23_reset_cop_spawning_thread")
end
function m12_cleanup_start_world()
-- Don't cleanup the start vehicle until we get into the studio level, since the player
-- will be able to see it from the lobby
end
-- This thread waits for a while then resets police notoriety and allows ambient cop spawning again.
function m23_reset_cop_spawning_thread()
-- Wait a bit.
delay(M12_NO_COPS_TIME)
-- Allow ambient cops again.
spawning_allow_cop_ambient_spawns(true)
notoriety_reset("police")
M12_thread.no_cops = INVALID_THREAD_HANDLE
end
-- Spawns the lobby guard and tell the ambient NPCs to wander around
--
function m12_setup_start_lobby()
group_create(M12_groups.stag_lobby.name, true)
--ai_do_scripted_move(M12_groups.stag_lobby.members[5], M12_nav.stag_pace[1], false, false)
group_create(M12_groups.workers_lobby.name, true)
for k, v in pairs (M12_groups.workers_lobby.members) do
wander_start(M12_groups.workers_lobby.members[k])
--wander_stop(M12_groups.workers_lobby.second_floor_receptionist)
end
for i, stag in pairs(M12_groups.stag_lobby.members) do
on_take_damage("m12_npc_damaged_fail", stag)
end
m12_play_persona_line(M12_persona_line.hide_guns)
end
function m12_cleanup_start_lobby()
-- Setup in m12_setup_start_world(). Cleanuped up here since the player can still see
-- the vehicle from the lobby.
if group_is_loaded(M12_groups.stag_lobby.name) then
for i, stag in pairs(M12_groups.stag_lobby.members) do
on_take_damage("", stag)
end
end
if group_is_loaded(M12_groups.vehicle_start.name) then
group_destroy(M12_groups.vehicle_start.name)
end
if group_is_loaded(M12_groups.stag_lobby.name) then
group_destroy(M12_groups.stag_lobby.name)
end
if group_is_loaded(M12_groups.workers_lobby.name) then
group_destroy(M12_groups.workers_lobby.name)
end
end
-- Move the player(s) to their proper location, spawn Josh, spawn a few ambient NPCs, and
-- setup the intial STAG guard
--
function m12_setup_grab_josh()
group_create(M12_groups.stag_studio.name, true)
ai_do_scripted_move(M12_groups.stag_studio.members[1], M12_nav.stag_pace[2], false, false)
ai_do_scripted_move(M12_groups.stag_studio.members[4], M12_nav.stag_pace[3], false, false)
-- Prevent the pacing guys from going all combat-ready hunkery.
npc_combat_enable(M12_groups.stag_studio.members[1], false)
npc_combat_enable(M12_groups.stag_studio.members[4], false)
group_create(M12_groups.workers_studio.name, true)
group_create(M12_groups.workers_cubical.name, true)
m12_setup_josh()
thread_yield()
set_animation_state(M12_groups.josh.char, "Josh Warmup")
for i, stag in pairs(M12_groups.stag_studio.members) do
on_take_damage("m12_npc_damaged_fail", stag)
end
end
function m12_cleanup_grab_josh()
-- Clean up the groups in m12_cleanup_cubical_battle() since the player can
-- travel between the cubical and studio without any transition
thread_kill(M12_thread.play_line)
M12_thread.play_line = INVALID_THREAD_HANDLE
end
-- Spawn the statically placed STAG, and setup some continuious spawn groups to keep
-- some pressure on the plyaer.
--
function m12_setup_cubical_battle()
group_create(M12_groups.stag_cubical.name, true)
-- Have the stag members appear like they just arrived, and move them to their intial positions
m12_move_members(M12_scripted_move.stag_cubical)
ai_do_scripted_take_cover(M12_scripted_move.stag_cubical.members[1])
-- m12_setup_spawn_group(M12_spawn_groups.cubical_wave_1, 15.0, false)
m12_setup_trigger(M12_trigger.start_spawning, "m12_start_spawning", "")
end
function m12_cleanup_cubical_battle()
-- Groups from m12_setup_grab_josh(). Clean these up here since the player can go between the
-- cubical and studio without transition.
if group_is_loaded(M12_groups.stag_studio.name) then
group_destroy(M12_groups.stag_studio.name)
end
if group_is_loaded(M12_groups.workers_studio.name) then
group_destroy(M12_groups.workers_studio.name)
end
if group_is_loaded(M12_groups.workers_cubical.name) then
group_destroy(M12_groups.workers_cubical.name)
end
-- Groups from m12_setup_cubical_battle()
if group_is_loaded(M12_groups.stag_cubical.name) then
group_destroy(M12_groups.stag_cubical.name)
end
m12_helper_cleanup_ambush_group(M12_groups.stag_window_rush)
release_to_world(M12_groups.stag_window_rush.name)
m12_helper_cleanup_ambush_group(M12_groups.stag_studio_rush_01)
release_to_world(M12_groups.stag_studio_rush_01.name)
m12_helper_cleanup_ambush_group(M12_groups.stag_studio_rush_02)
release_to_world(M12_groups.stag_studio_rush_02.name)
if group_is_loaded(M12_groups.fleeing_peds_001.name) then
group_destroy(M12_groups.fleeing_peds_001.name)
end
m12_cleanup_all_spawn_groups()
m12_cleanup_trigger(M12_trigger.start_spawning)
m12_cleanup_trigger(M12_trigger.elev_conv_02)
m12_cleanup_trigger(M12_trigger.studio_rush)
m12_cleanup_trigger(M12_trigger.fleeing_peds_1)
end
-- Spawns the baracade blocking the enterance and setup all the attacking STAG in the
-- lobby area.
--
function m12_setup_escape_lobby()
group_create(M12_groups.lobby_escape.name, true)
group_create(M12_groups.garage_cars.name, true)
traffic_disable_lanes(M12_trigger.block_traffic, true)
m12_lock_front_doors()
m12_setup_trigger(M12_trigger.lobby_spawn, "m12_lobby_spawn_cb", "")
m12_start_convo_thread(M12_convo.lobby_escape)
-- Stop spawning action nodes while in the lobby. This prevents a flood of STAG.
action_nodes_enable(false)
end
function m12_cleanup_escape_lobby()
if group_is_loaded(M12_groups.lobby_escape.name) then
group_destroy(M12_groups.lobby_escape.name)
end
m12_unlock_front_doors()
m12_cleanup_all_spawn_groups()
-- Re-enable action nodes.
action_nodes_enable(true)
end
-- Objective to load josh into the car.
--
function m12_load_josh_into_car()
-- Lock all the cars in the garage
for i, car in pairs(M12_groups.garage_cars.vehicles) do
set_unjackable_flag(car, true)
end
m12_play_persona_line(M12_persona_line.get_in)
-- Add the objective to load Josh into the car
m12_set_objective(M12_OBJ_STEAL_A_VEHICLE)
marker_add_trigger(M12_trigger.load_josh, MINIMAP_ICON_USE, INGAME_EFFECT_LOCATION, OI_ASSET_USE, OI_FLAGS_FULL, SYNC_LOCAL)
m12_setup_trigger(M12_trigger.load_josh, "m12_on_load_josh", "", SYNC_LOCAL)
-- Wait for the player to use the trigger
while M12_begin_load_josh == false do
thread_yield()
end
-- Progress the musics.
m12_post_music_event(M12_music.josh_loaded_into_car)
-- Prevent Josh from exiting the vehicle until the player gets in.
vehicle_suppress_npc_exit(M12_ESCAPE_VEHICLE, true)
-- Unlock the escape vehicle.
set_unjackable_flag(M12_ESCAPE_VEHICLE, false)
on_vehicle_enter("m12_escape_vehicle_entered", M12_ESCAPE_VEHICLE)
player_controls_disable(LOCAL_PLAYER)
-- Set the animation state to keep the transition between actions smooth.
set_animation_state(LOCAL_PLAYER, "m12_plym_overshoulder_exittocar_C")
set_animation_state(NPC_JOSH, "m12_josh_overshoulder_exittocar_C")
-- Make sure the player has a pistol for this animation.
local pistol_name = inv_item_in_slot(LOCAL_PLAYER, "pistol")
if pistol_name == "none" or pistol_name == nil then
-- Give them a temporary pistol.
inv_weapon_add_temporary(LOCAL_PLAYER, M12_TEMP_PISTOL_NAME)
end
-- Make weapon slot match the animation.
inv_item_equip_slot("pistol", LOCAL_PLAYER)
-- Play the animation to drop Josh off the player's shoulder.
m12_cleanup_player_carrying_josh(true)
action_play_synced(LOCAL_PLAYER, NPC_JOSH, "OS_Exit_To_Car", M12_nav.load_josh)
-- Have Josh enter the car and play the action for the player threatening him.
vehicle_suppress_npc_exit(M12_ESCAPE_VEHICLE, true)
vehicle_enter(NPC_JOSH, M12_ESCAPE_VEHICLE, 2, false, false, false, "m12_josh_overshoulder_exittocar_b")
action_play(LOCAL_PLAYER, "m12_plym_overshoulder_exittocar_b")
-- Clear the animation states
clear_animation_state(LOCAL_PLAYER)
clear_animation_state(NPC_JOSH)
player_controls_enable(LOCAL_PLAYER)
-- Let the player use all their weapons now that they no longer have Josh.
inv_weapon_disable_all_slots(false, SYNC_LOCAL)
M12_inv_restricted_for_carrying_josh = false
-- Add Josh to the player's party at this point to prevent Viola from pulling
-- him out of the car when the player enters.
party_add(NPC_JOSH, LOCAL_PLAYER)
-- Wait for the player to get into the car.
marker_add(M12_ESCAPE_VEHICLE, MINIMAP_ICON_USE, OI_ASSET_USE, OI_FLAGS_FULL)
while M12_escape_vehicle_entered == false do
thread_yield()
end
marker_remove(M12_ESCAPE_VEHICLE)
vehicle_suppress_npc_exit(M12_ESCAPE_VEHICLE, false)
-- Remove the temporary pistol if we gave one to the player.
inv_weapon_remove_temporary(LOCAL_PLAYER, M12_TEMP_PISTOL_NAME)
-- Unlock the rest of the cars in the garage.
for i, car in pairs(M12_groups.garage_cars.vehicles) do
set_unjackable_flag(car, false)
end
m12_start_convo_thread(M12_convo.flee_to_hq)
m12_cleanup_load_josh_into_car()
end
-- Cleans up the objective to load josh into the car.
--
function m12_cleanup_load_josh_into_car()
player_controls_enable(LOCAL_PLAYER)
marker_remove_trigger(M12_trigger.load_josh)
m12_cleanup_trigger(M12_trigger.load_josh)
on_vehicle_enter("", M12_ESCAPE_VEHICLE)
clear_animation_state(LOCAL_PLAYER)
if group_is_loaded(M12_groups.josh) then
clear_animation_state(NPC_JOSH)
end
M12_begin_load_josh = false
M12_escape_vehicle_entered = false
end
-- Callback for when the player uses the trigger to load Josh into the car.
--
function m12_on_load_josh(character, trigger)
-- Only the local player can do this.
if character ~= LOCAL_PLAYER then
return
end
marker_remove_trigger(M12_trigger.load_josh)
m12_cleanup_trigger(M12_trigger.load_josh)
M12_begin_load_josh = true
end
-- Callback for the player entering the escape vehicle.
--
function m12_escape_vehicle_entered(character, vehicle, seat_idx)
if character ~= LOCAL_PLAYER then
return
end
-- Let Josh get out if the player does.
vehicle_suppress_npc_exit(M12_ESCAPE_VEHICLE, false)
on_vehicle_enter("", vehicle)
M12_escape_vehicle_entered = true
end
-- Spawn some cars for the player to escape in
--
function m12_setup_escape_garage()
notoriety_set_min(M12_STAG_NOTORIETY, M12_GARAGE_MIN_NOTORIETY)
set_ped_density(1.0)
set_traffic_density(1.0)
npc_combat_enable(NPC_JOSH, false)
follower_set_can_abandon(NPC_JOSH, true)
on_dismiss("m12_party_member_dismissed", NPC_JOSH)
group_create(M12_groups.stag_outside_garage.name, true)
m12_setup_trigger(M12_trigger.stag_outside_garage, "m12_block_garage_ramp_cb", "")
m12_setup_chaser_group(M12_groups.stag_chasers_001)
m12_setup_trigger(M12_trigger.chasers_1, "m12_start_chasers_1_cb", "")
m12_setup_chaser_group(M12_groups.stag_chasers_002)
m12_setup_trigger(M12_trigger.chasers_2, "m12_start_chasers_2_cb", "")
end
function m12_cleanup_escape_garage()
-- Don't cleanup M12_groups.garage_cars.name here since we don't want to destory the
-- players vehicle until we fade out (in m12_cleanup())
notoriety_set(M12_STAG_NOTORIETY, 0)
notoriety_set_min(M12_STAG_NOTORIETY, 0)
notoriety_set_max(M12_STAG_NOTORIETY, 5)
on_vehicle_enter("", LOCAL_PLAYER)
traffic_disable_lanes(M12_trigger.block_traffic, false)
end
-- Spawns and populate a group of chaser vehicles.
--
function m12_setup_chaser_group(group_table)
group_create(group_table.name)
for key, vehicle_table in pairs(group_table.vehicles) do
local vehicle = vehicle_table.vehicle
local driver = vehicle_table.driver
local gunner = vehicle_table.gunner
vehicle_suppress_npc_exit(vehicle)
vehicle_enter_teleport(driver, vehicle, 0, true)
if gunner ~= nil and gunner ~= "" then
vehicle_enter_teleport(gunner, vehicle, 4, true)
end
end
end
function m12_hq_freeze_cb()
on_trigger("", M12_trigger.saints_hq_freeze)
trigger_enable(M12_trigger.saints_hq_freeze, false)
M12_thread.stop_player_car = thread_new("m12_stop_player_car_thread")
end
-- Spawns and sets up Josh's behavior
--
function m12_setup_josh()
group_create(M12_groups.josh.name, true)
-- make sure Josh has the proper persona
character_set_persona(NPC_JOSH, "Josh")
-- Josh is important to the mission, so check to make sure he does not die.
on_death("m12_on_death", NPC_JOSH)
-- Prevent Josh from attacking anyone
set_attack_enemies_flag(NPC_JOSH, false)
set_attack_peds_flag(NPC_JOSH, false)
set_attack_player_flag(NPC_JOSH, false)
-- Prevent Josh from being attacked
set_dont_attack_me_on_sight_flag(NPC_JOSH, true)
-- Mark Josh as a Over the Shoulder human shield
character_set_over_the_shoulder(NPC_JOSH, true)
-- Only allow Josh to be grabbed by the player
character_set_only_player_grabs(NPC_JOSH, true)
-- Make sure Josh doesn't have any weapons and attempt to attack someone
inv_item_remove_all(NPC_JOSH)
-- Do not disable all combat actions since fleeing/cowerig is considered a combat action
--npc_combat_enable(NPC_JOSH, false)
-- Wait until Josh is active; we can sometimes fail to take him as a human shield if
-- he isn't activated before we get to that point.
local delay_handle = thread_new("delay", 2.0)
while character_is_ready(NPC_JOSH) == false and thread_check_done(delay_handle) == false do
thread_yield()
end
-- Prevent the player from accidentally pwning Josh.
set_suppress_synced_melee_attacks_flag(NPC_JOSH, true)
end
function m12_cleanup_josh()
if not group_is_loaded(M12_groups.josh.name) then
return
end
marker_remove(NPC_JOSH, SYNC_ALL)
on_death("", NPC_JOSH)
on_vehicle_enter("", NPC_JOSH)
on_vehicle_exit("", NPC_JOSH)
on_dismiss("", NPC_JOSH)
party_dismiss(NPC_JOSH)
group_destroy(M12_groups.josh.name)
end
-- Spawns and sets up Suko.
--
function m12_setup_suko()
group_create(M12_groups.homies.name, true)
follower_set_can_abandon(NPC_SUKO, true)
on_death("m12_on_death", NPC_SUKO)
on_dismiss("m12_party_member_dismissed", NPC_SUKO)
end
function m12_cleanup_suko()
on_death("", NPC_SUKO)
on_dismiss("", NPC_SUKO)
party_dismiss(NPC_SUKO)
-- Suko should always be loaded
group_destroy(M12_groups.homies.name)
end
-- ***************************************************
-- Miscellaneous m12 Helper Functions
-- ***************************************************
-- Enables the specified trigger
--
-- trigger - Name of the trigger to enable
-- on_enter - call back function to call when the trigger is triggered.
-- on_exit - call back function to call when the trigger is exited
-- sync_type - (optional) how to sync the trigger_enable call.
function m12_setup_trigger(trigger, on_enter, on_exit, sync_type)
trigger_enable(trigger, true, sync_type)
on_trigger(on_enter, trigger)
on_trigger_exit(on_exit, trigger)
end
-- Disables the specified trigger
--
-- trigger - Name of the trigger to disable
function m12_cleanup_trigger(trigger)
if trigger == nil or trigger == "" then
return
end
if type(trigger) == "table" then
if trigger.name ~= nil then
m12_cleanup_trigger(trigger.name)
else
for key, val in pairs(trigger) do
m12_cleanup_trigger(val)
end
end
return
end
on_trigger("", trigger)
on_trigger_exit("", trigger)
trigger_enable(trigger, false)
end
-- Sets up a spawn region, and starts spawning the specified groups.
--
-- group - Table with the following member tables
-- group.regions - Table of script region names
-- group.groups - Table of group names to spawn
-- respawn_delay - How long to wait (in seconds) to respawn a group after it was destroyed
-- check_los - false to disable line of sight checkes. Defaults to true (perform los checks)
function m12_setup_spawn_group(group, respawn_delay, check_los)
-- If a dealy is not provided, assume 10 seconds
respawn_delay = respawn_delay or 10.0
-- Enable the spawn region
for i = 1, #group.regions do
spawn_region_enable(group.regions[i], true)
end
-- Disable line of sight checks if requested.
if check_los == false then
for i = 1, #group.regions do
spawn_region_enable_los(group.regions[i], false)
end
end
-- Start spawning the actual groups.
for i = 1, #group.groups do
continuous_spawn_start(group.groups[i], 0, respawn_delay)
end
end
-- Disables the specified spawn groups and spawn regions
--
-- group - Spawn group table (see m12_setup_spawn_group()) to disable
function m12_cleanup_spawn_group(group)
-- Stop spawning the actual groups.
for i = 1, #group.groups do
continuous_spawn_stop(group.groups[i])
end
-- disable the spawn region
for i = 1, #group.regions do
local region = group.regions[i]
spawn_region_enable(region, false)
spawn_region_enable_los(region, true)
end
end
-- Disables all spawn groups and regions.
--
function m12_cleanup_all_spawn_groups()
for group in pairs(M12_spawn_groups) do
m12_cleanup_spawn_group(M12_spawn_groups[group])
end
end
function m12_set_objective(obj)
M12_current_obj = obj
if M12_secondary_obj == "" then
objective_text(0, obj.name, "", "", SYNC_ALL, obj.icon)
m12_add_icons_to_active_triggers()
end
end
function m12_clear_objective()
M12_current_obj = ""
if M12_secondary_obj == "" then
objective_text_clear(0)
end
m12_remove_icons_from_active_triggers()
end
function m12_set_seconodary_objective(obj)
M12_secondary_obj = obj
objective_text(0, obj.name, "", "", SYNC_ALL, obj.icon)
m12_remove_icons_from_active_triggers()
end
function m12_clear_seconodary_objective()
M12_secondary_obj = ""
if M12_current_obj ~= "" then
objective_text(0, M12_current_obj.name, "", "", SYNC_ALL, M12_current_obj.icon)
m12_add_icons_to_active_triggers()
else
objective_text_clear(0)
end
end
-- Setup an ambush group
--
-- group_table: Lua table defining the ambush group
function m12_helper_setup_ambush_group(group_table)
-- Register this ambush group
M12_runtime.ambush_groups[#M12_runtime.ambush_groups + 1] = group_table
-- Enable the trigger(s)
for i, trigger in pairs(group_table.triggers) do
trigger_enable(trigger, true)
on_trigger("m12_ambush_triggered_cb", trigger)
end
end
-- Cleanup an ambush group
function m12_helper_cleanup_ambush_group(group_table, destroy)
-- Find and un-register this ambush group
for i, group in pairs(M12_runtime.ambush_groups) do
if (M12_runtime.ambush_groups[i].name == group_table.name) then
M12_runtime.ambush_groups[i] = M12_runtime.ambush_groups[#M12_runtime.ambush_groups]
M12_runtime.ambush_groups[#M12_runtime.ambush_groups] = nil
break
end
end
-- Disabled the trigger(s)
for i, trigger in pairs(group_table.triggers) do
trigger_enable(trigger, false)
on_trigger("", trigger)
end
end
-- Callback when the player enters an ambush trigger
--
-- triggerer: Play that entered the trigger
-- trigger: Name of the trigger that was entered
function m12_ambush_triggered_cb(triggerer, trigger)
-- Look-up the script group for this trigger volume
local group_table = nil
for i, ambush_group in pairs(M12_runtime.ambush_groups) do
for j, ambush_trigger in pairs(ambush_group.triggers) do
if (ambush_trigger == trigger) then
group_table = ambush_group
end
end
end
if (group_table ~= nil) then
for i, char in pairs(group_table.chars) do
if (char.behavior == "advance") then
npc_leash_remove(char.name)
ai_add_enemy_target(char.name, CLOSEST_PLAYER, ATTACK_ON_SIGHT, false)
ai_do_scripted_move(char.name, char.nav, true, false)
elseif (char.behavior == "attack_region") then
npc_leash_remove(char.name)
ai_add_enemy_target(char.name, CLOSEST_PLAYER, ATTACK_ON_SIGHT, false)
ai_attack_region(char.name, char.nav, char.radius)
elseif (char.behavior == "take_cover") then
npc_leash_remove(char.name)
ai_add_enemy_target(char.name, CLOSEST_PLAYER, ATTACK_ON_SIGHT, false)
ai_do_scripted_take_cover(char.name)
end
end
end
end
-- *************************
--
-- Callback functions
--
-- *************************
-- Generic trigger callback. Whenever the trigger is triggered by the player, the
-- M12_trigger_reached flag is set.
--
-- human - Who triggered the trigger
-- trigger - Name of the triggered trigger.
function m12_trigger_cback(human, trigger)
if human == LOCAL_PLAYER or human == REMOTE_PLAYER then
M12_trigger_reached = true
end
end
-- function m12_elev_persona_cb(human, trigger)
-- m12_play_persona_line(M12_persona_line.up_on_floor_7)
-- end
-- Conversation that plays when the player tries to take upper elevators
--
function m12_start_convo_elevator(human, trigger)
m12_cleanup_trigger(trigger)
m12_start_convo_thread(M12_convo.elevator)
end
-- Have some peds flee from a side room down a flight of stairs
--
function m12_start_fleeing_peds_1(human, trigger)
-- Only do this once.
if M12_fleeing_peds_triggered then
return
end
M12_fleeing_peds_triggered = true
m12_cleanup_trigger(trigger)
group_create(M12_groups.fleeing_peds_001.name)
m12_move_members(M12_groups.fleeing_peds_001)
end
-- Called when a human enters a elevator.
--
function m12_elevator_enter_cback(human, trigger)
if human == LOCAL_PLAYER then
M12_local_in_elevator = true
elseif human == REMOTE_PLAYER then
M12_remote_in_elevator = true
elseif human == NPC_JOSH then
M12_josh_in_elevator = true
end
M12_player_in_elevator = M12_local_in_elevator or M12_remote_in_elevator
end
-- Called when a human exits a elevator.
--
function m12_elevator_exit_cback(human, trigger)
if human == LOCAL_PLAYER then
M12_local_in_elevator = false
elseif human == REMOTE_PLAYER then
M12_remote_in_elevator = false
elseif human == NPC_JOSH then
M12_josh_in_elevator = false
end
M12_player_in_elevator = M12_local_in_elevator or M12_remote_in_elevator
end
-- Ends the mission if an important human is killed.
--
-- victim - Who was killed
-- killer - The killer...
function m12_on_death(victim, killer)
if victim == NPC_SUKO then
mission_end_failure("m12", M12_FAILURE_SUKO_DIED)
elseif victim == NPC_JOSH then
mission_end_failure("m12", M12_FAILURE_JOSH_DIED)
end
end
function m12_party_member_dismissed(npc, leader)
if npc == NPC_SUKO then
mission_end_failure("m12", M12_FAILURE_SUKO_ABANDONED)
elseif npc == NPC_JOSH then
mission_end_failure("m12", M12_FAILURE_JOSH_ABANDONED)
end
end
-- Cubical battle callback. When this trigger is reached, start spawning additional
-- spawn groups. This is done to help balance the number of npcs and prevent the player
-- from being overrun.
--
-- human - Who triggered the callback
-- trigger - Name of the triggered trigger.
function m12_start_spawning(human, trigger)
if human == LOCAL_PLAYER or human == REMOTE_PLAYER then
-- Cleanup this trigger since it should only fire once.
m12_cleanup_trigger(M12_trigger.start_spawning)
-- Start spawning the additional waves.
m12_setup_spawn_group(M12_spawn_groups.cubical_wave_1, 10.0, true)
m12_setup_spawn_group(M12_spawn_groups.cubical_wave_2, 25.0, true)
end
end
-- Continuous spawn callback for the lobby area.
function m12_lobby_spawn_cb()
m12_cleanup_trigger(M12_trigger.lobby_spawn)
m12_play_persona_line(M12_persona_line.ele_to_garage)
m12_setup_spawn_group(M12_spawn_groups.lobby_escape, 25.0, true)
end
-- Called when a player equips a weapon, and sets M12_weapon_equipped to reflect
-- the players weapon state.
--
function m12_weapon_was_equipped(human, weapon)
if human == LOCAL_PLAYER or human == REMOTE_PLAYER then
if weapon ~= "Fist" then
M12_weapon_equipped = true
else
M12_weapon_equipped = false
end
end
end
function m12_player_fired_weapon(human, weapon, attack_type)
if weapon ~= "unarmed" and weapon ~= "Fist" then
m12_exit_stealth_section()
mission_end_failure("m12", M12_FAILURE_COVER_BLOWN)
end
end
-- The player reverts to whatever weapon they had equipped when they entered the vehicle,
-- so we need to re-unequip it when they exit the vehicle after the "hide the guns" line plays.
--
function m12_remove_weapon_on_vehicle_exit(character)
-- Only need to do this once.
on_vehicle_exit("", character)
-- Unequip their weapon again.
inv_item_equip(nil, character)
end
-- Called when any notoriety based event is generated. Fail the mission on any
-- NPC deaths or assaults.
--
-- event - Name of the event that occured
-- police - Increase in police notoriety
-- gange - Increase in gane notoriety
-- team - Name of the notoriety team that was effected
function m12_notoriety_increased(event, police, gange, team)
if event == "civilian kill" or event == "gang kill" or event == "police kill" or
event == "civilian assault" or event == "gang assault" or event == "police assault" then
m12_exit_stealth_section()
mission_end_failure("m12", M12_FAILURE_COVER_BLOWN)
end
end
-- Callback for when a mission critical NPC takes damage during the stealth section; fails the mission.
--
function m12_npc_damaged_fail()
m12_exit_stealth_section()
mission_end_failure("m12", M12_FAILURE_COVER_BLOWN)
end
-- Called when the player is leaving the garage. Have a few vehicles partly block the exit.
--
function m12_block_garage_ramp_cb(human, trigger)
m12_cleanup_trigger(trigger)
for key, vehicle_table in pairs(M12_groups.stag_outside_garage.vehicle_paths) do
vehicle_table.thread = thread_new("m12_thread_vehicle_pathfind_thread", vehicle_table)
end
notoriety_force_no_spawn(M12_STAG_NOTORIETY, false)
notoriety_force_no_spawn("stag", false)
end
-- Called when the player passes a group of STAG vehicles sitting on the side of the road.
-- Have them follow and attempt to ram the player.
--
function m12_start_chasers_1_cb(human, trigger)
m12_cleanup_trigger(trigger)
for key, vehicle_table in pairs(M12_groups.stag_chasers_001.vehicles) do
local vehicle = vehicle_table.vehicle
local exit_on_stop = false
local allow_ram = true
vehicle_chase(vehicle, human, exit_on_stop, allow_ram)
vehicle_set_crazy(vehicle, true)
end
end
function m12_start_chasers_2_cb(human, trigger)
m12_cleanup_trigger(trigger)
for key, vehicle_table in pairs(M12_groups.stag_chasers_002.vehicles) do
local vehicle = vehicle_table.vehicle
local exit_on_stop = false
local allow_ram = true
vehicle_chase(vehicle, human, exit_on_stop, allow_ram)
vehicle_set_crazy(vehicle, true)
end
end
-- *************************
--
-- Thread functions
--
-- *************************
-- Finds the member of a group nearest to the player and plays a line after a duration.
--
function m12_find_nearest_guy_and_play_line(group, line, max_dist, delay_time)
delay(delay_time)
local all_dead = false
while true do
if group_is_loaded(group) == false then
return
end
local nearest_dist = 999999
local nearest_dude = ""
for i, npc in pairs(group.chars) do
if character_is_dead(npc.name) == false then
local dist = get_dist(npc.name, LOCAL_PLAYER)
if dist < nearest_dist then
nearest_dist = dist
nearest_dude = npc.name
end
end
end
if nearest_dude == "" then
-- Couldn't find anyone, we're done.
return
end
if max_dist > 0 and nearest_dist < max_dist then
audio_play_persona_line(nearest_dude, M12_persona_line.secure_building.name)
return
end
delay(0.25)
end
end
-- Stops the player's car.
--
function m12_stop_player_car_thread()
-- Josh should be in the same car as the local player.
if character_is_in_vehicle(LOCAL_PLAYER) then
vehicle_max_speed(LOCAL_PLAYER, 0.0)
while true do
-- Continue stopping until we kill the thread in mission cleanup.
vehicle_stop_do(LOCAL_PLAYER)
thread_yield()
end
end
end
-- This function kills Josh if he is ever dropped as a human shield, failing the mission.
--
function m12_handle_josh_dropped_thread()
while true do
-- Keep checking until this thread is killed.
while character_has_specific_human_shield(LOCAL_PLAYER, NPC_JOSH) do
thread_yield()
end
if character_is_dead(NPC_JOSH) then
-- Well, that's it for Josh...
return
end
-- If the host is incapacitated he can't take Josh as a human shield.
-- Let's just kill Josh then; we don't have much other choice.
if human_is_downed(LOCAL_PLAYER) then
character_kill(NPC_JOSH, true)
return
end
-- Woops! Find the nearest nav in the current list and teleport the player and Josh there.
local nearest_nav = nil
local nearest_dist = nil
local cur_dist = nil
for i, cur_nav in pairs(M12_cur_josh_dropped_nav_list) do
cur_dist = get_dist(LOCAL_PLAYER, cur_nav)
if nearest_dist == nil or nearest_dist > cur_dist then
nearest_nav = cur_nav
nearest_dist = cur_dist
end
end
if nearest_nav == nil then
-- Well, somehow we failed to find a place to teleport to. Josh dies as a result... poor Josh.
character_kill(NPC_JOSH, true)
return
end
fade_out(0.5, {0, 0, 0}, SYNC_LOCAL)
fade_out_block()
m12_cleanup_player_carrying_josh(false)
teleport(LOCAL_PLAYER, nearest_nav)
teleport(NPC_JOSH, nearest_nav)
while teleport_check_done(LOCAL_PLAYER) == false do
thread_yield()
end
while teleport_check_done(NPC_JOSH) == false do
thread_yield()
end
-- Wait for Josh to be ready if necessary, but only wait so long.
local wait_time = 0.0
while (character_is_ready(NPC_JOSH) == false) and (wait_time < 5.0) do
wait_time = wait_time + get_frame_time()
thread_yield()
end
if character_is_ready(NPC_JOSH) then
player_take_human_shield(LOCAL_PLAYER, NPC_JOSH)
m12_setup_player_to_carry_josh(false)
else
character_kill(NPC_JOSH, true)
return
end
fade_in(0.5, SYNC_LOCAL)
fade_in_block()
end
end
-- Populate a vehicle, have it follow a path, and then have all the npcs exit the vehicle.
--
function m12_thread_vehicle_pathfind_thread(vehicle_table)
vehicle_enter_group_teleport(vehicle_table.members, vehicle_table.vehicle)
vehicle_pathfind_to(vehicle_table.vehicle, vehicle_table.path, true, true)
vehicle_exit_group(vehicle_table.members)
vehicle_table.thread = INVALID_THREAD_HANDLE
end
-- Instruct the player to remove their weapon, or fail the mission if they keep it out for to long
--
function m12_thread_unequip_your_weapon()
while true do
while not M12_weapon_equipped do
thread_yield()
end
-- Setup the HUD to display how much time the player has to unequip their weapon
hud_timer_set(1, M12_WEAPON_UNEQUIP_TIME)
m12_set_seconodary_objective(M12_OBJ_UNEQUIP_YOUR_WEAPON)
while M12_weapon_equipped do
if hud_timer_get_remainder(1) <= 0 then
hud_timer_stop(1)
m12_exit_stealth_section()
mission_end_failure("m12", M12_FAILURE_COVER_BLOWN)
end
thread_yield()
end
hud_timer_stop(1)
m12_clear_seconodary_objective()
end
end
-- Taking a human shield does not generate a notoriety event. If a player takes a human sheild,
-- instantly fail the mission.
--
function m12_thread_human_shield_check()
while true do
if character_has_human_shield(LOCAL_PLAYER) then
m12_exit_stealth_section()
mission_end_failure("m12", M12_FAILURE_COVER_BLOWN)
end
if M12_in_coop then
if character_has_human_shield(REMOTE_PLAYER) then
m12_exit_stealth_section()
mission_end_failure("m12", M12_FAILURE_COVER_BLOWN)
end
end
thread_yield()
end
end
-- Add a marker to pickup Josh whenever necessary
--
function m12_thread_pickup_josh_marker()
while true do
while not character_is_in_vehicle(LOCAL_PLAYER) do
thread_yield()
end
if not character_is_in_vehicle(NPC_JOSH) then
m12_set_seconodary_objective(M12_OBJ_PICKUP_JOSH_BIRK)
marker_add(NPC_JOSH, MINIMAP_ICON_USE, OI_ASSET_USE, OI_FLAGS_DEFAULT, SYNC_ALL)
while character_is_in_vehicle(LOCAL_PLAYER) and not character_is_in_vehicle(NPC_JOSH) do
thread_yield()
end
marker_remove(NPC_JOSH, SYNC_ALL)
m12_clear_seconodary_objective()
end
thread_yield()
end
end
-- *************************
--
-- Thread helper functions
--
-- *************************
-- Kill all active threads.
--
function m12_unload_all_threads()
m12_unload_all_convos()
if M12_thread.pickup_josh ~= INVALID_THREAD_HANDLE then
thread_kill(M12_thread.pickup_josh)
M12_thread.pickup_josh = INVALID_THREAD_HANDLE
end
for key, vehicle_table in pairs(M12_groups.stag_outside_garage.vehicle_paths) do
if vehicle_table.thread ~= INVALID_THREAD_HANDLE then
thread_kill(vehicle_table.thread)
vehicle_table.thread = INVALID_THREAD_HANDLE
end
end
end
-- *************************
--
-- Conversation functions
--
-- *************************
-- Plays a conversation
--
-- convo: Conversation to play
function m12_play_convo(convo)
if convo.name ~= "" and convo.name ~= nil then
if type(convo.name) == "table" then
m12_play_persona_line(convo.name)
else
debug_print("MAD", "Playing Convo: " .. convo.name)
convo.handle = audio_conversation_load(convo.name)
audio_conversation_play(convo.handle)
audio_conversation_wait_for_end(convo.handle)
convo.handle = nil
debug_print("MAD", "Ended Convo: " .. convo.name)
end
end
if convo.delay ~= nil and convo.delay > 0.01 then
delay(convo.delay)
end
end
-- Unloads a conversation
--
-- convo: Conversation to unload
function m12_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 m12_unload_all_convos()
for convo_key, convo_set in pairs(M12_convo) do
m12_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 m12_play_convo_set(convo_set)
for key, convo in pairs(convo_set.files) do
m12_play_convo(convo)
end
end
-- Unloads a convo table (array)
--
-- convo_set Table of conversations to unload
--
function m12_unload_convo_set(convo_set)
for key, convo in pairs(convo_set.files) do
m12_unload_convo(convo)
end
end
-- Starts an audio conversation as a thread
--
-- convo_table: Conversation to play
--
function m12_start_convo_thread(convo_table)
if M12_active_convo ~= "" then
m12_unload_convo_set(M12_active_convo)
M12_active_convo = ""
delay(0.25)
end
convo_table.thread = thread_new("m12_audio_thread", convo_table)
end
-- Loads the necessary 2D personas for the heli rail sequence
--
function m12_load_personas()
for key, persona in pairs(M12_personas) do
persona.handle = audio_persona_load_2d(persona.name)
end
end
-- Unloads all 2d personas
--
function m12_unload_personas()
for key, persona in pairs(M12_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 m12_get_persona_handle(line)
for key, persona in pairs(M12_personas) do
if persona.name == line.character then
return persona.handle
end
end
return nil
end
-- Plays a persona line.
--
function m12_play_persona_line(line)
debug_print("MAD", "Playing Persona Line: " .. line.name)
if line.play_2d then
audio_play_persona_line_2d(m12_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 m12_audio_thread(convo_table)
M12_active_convo = convo_table
m12_play_convo_set(convo_table)
convo_table.thread = INVALID_THREAD_HANDLE
M12_active_convo = ""
end