-- ss11.lua -- SR2 mission script -- 3/28/07 -- Mission help table text tags HT_DIDNT_MAKE_IT_TO_AMBUSH_IN_TIME = "ss11_didnt_make_it_to_ambush_in_time" HT_SHAUNDI_ABANDONED = "ss11_shaundi_abandoned" HT_GET_INTO_POSITION = "ss11_get_into_position" HT_GO_AFTER_GENERAL = "ss11_go_after_general" HT_KILL_THE_GENERAL = "ss11_kill_the_general" HT_GET_TO_AMBUSH = "ss11_get_to_ambush" HT_SHAUNDI_DIED = "ss11_shaundi_died" HT_DIDNT_MAKE_IT_TO_AMBUSH_IN_TIME = "ss11_didnt_make_it_to_ambush_in_time" HT_FINISH_OFF_ESCORTS = "ss11_finish_off_escorts" HT_X_OF_Y_ESCORT_MEMBERS_KILLED = "ss11_x_of_y_escort_members_killed" HT_TRACK_DOWN_GENERAL = "ss11_track_down_general" HT_GENERALS_RIDE_HP = "ss11_generals_bulldog_health" HT_GENERALS_HP = "ss11_generals_health" HT_TEMP_SHAUNDI_DROPOFF_ATV_LINE = "ss11_temp_shaundi_dropoff_atv_line" -- Mission states MS_TRAVEL_TO_AMBUSH_LOCATION = 1 MS_CUTSCENE = 2 MS_ABOVEGROUND_FIREFIGHT = 3 MS_GENERAL_ESCAPING_UNDERGROUND = 4 MS_THROUGH_THE_MALL_FIREFIGHT = 5 MS_GENERAL_BOSS_BATTLE_DRIVING_PART = 6 MS_GENERAL_BOSS_BATTLE_ON_FOOT_PART = 7 -- Groups, NPCs, vehicles, navpoints, and other names -- Mission constant names MISSION_NAME = "ss11" -- ( Mission Prefix ) MP = MISSION_NAME.."_$" ENEMY_GANG = "Samedi" UNDERGROUND_MALL_HOOD = "sr2_ul_mall01" -- These are defined in highend.lua: ENTER_UG_MALL_FUNCTION = "highend_ug_mall_elevator_out" -- Defined in neighborhoods.xtbl: UNDERGROUND_MALL_OWNER = "Ultor" -- Checkpoints REACHED_AMBUSH_LOCATION_CHECKPOINT = "Reached_Ambush_Location" GENERAL_FIGHT_CHECKPOINT = "General_Boss_Fight" -- Weapons THE_GENERALS_SAW = "AR200" -- NPC names SHAUNDI_NAME = MP.."Shaundi" PIERCE_NAME = MP.."Pierce" THE_GENERAL_NAME = MP.."The_General" BODYGUARD_GROUP_MEMBERS = { MP.."General_Bodyguard1", MP.."General_Bodyguard2", MP.."General_Bodyguard3" } FIRST_DEFENDER_GROUP_MEMBERS = { MP.."Limo_DefenderG1_01", MP.."Limo_DefenderG1_02", MP.."Limo_DefenderG1_03", MP.."Limo_DefenderG1_04" } SECOND_DEFENDER_GROUP_MEMBERS = { MP.."Limo_DefenderG2_01", MP.."Limo_DefenderG2_02", MP.."Limo_DefenderG2_03", MP.."Limo_DefenderG2_04" } BUS_DRIVER_NAME = MP.."Bus_Driver" MALL_SQUAD_MEMBERS = { { MP.."Squad1_01", MP.."Squad1_02", MP.."Squad1_03", MP.."Squad1_04", MP.."Squad1_05", MP.."Squad1_06" }, { MP.."Squad2_01", MP.."Squad2_02", MP.."Squad2_03", MP.."Squad2_04", MP.."Squad2_05" }, { MP.."Squad3_01", MP.."Squad3_02", MP.."Squad3_03", MP.."Squad3_04", MP.."Squad3_05", MP.."Squad3_06" }, { MP.."Squad4_01", MP.."Squad4_02", MP.."Squad4_03", MP.."Squad4_04", MP.."Squad4_05", MP.."Squad4_06", MP.."Squad4_07", MP.."Squad4_08" } } MALL_SQUAD_LEASHED_MEMBERS = { { MP.."Squad1_01", MP.."Squad1_02", MP.."Squad1_05", MP.."Squad1_06" }, { MP.."Squad2_01", MP.."Squad2_02", MP.."Squad2_03" }, { }, { } } MALL_SQUAD_THREE_LEFT_SIDE_MEMBERS = { MALL_SQUAD_MEMBERS[3][1], MALL_SQUAD_MEMBERS[3][2], MALL_SQUAD_MEMBERS[3][3], MALL_SQUAD_MEMBERS[3][4] } MALL_SQUAD_THREE_RIGHT_SIDE_MEMBERS = { MALL_SQUAD_MEMBERS[3][5], MALL_SQUAD_MEMBERS[3][6] } GENERALS_RIDE_DRIVER_NAME = MP.."Generals_Driver" GENERALS_RIDE_AK_GUYS = { MP.."Rear_AK_Guy1", MP.."Rear_AK_Guy2" } -- Groups SHAUNDI_GROUP = MP.."Shaundi" STARTER_CAR_GROUP = MP.."Starter_Car" STARTER_CAR_COOP_GROUP = MP.."Starter_Car_Coop" BUS_GROUP = MP.."Bus" GENERALS_LIMO_GROUP = MP.."Generals_Limo" THE_GENERAL_GROUP = MP.."The_General" LIMO_DEFENDERS_GROUP = MP.."Limo_Defenders" AMBUSHER_SAINTS_GROUP = MP.."Ambusher_Saints" MALL_SAMEDI = MP.."Mall_Samedi" MALL_SQUAD_THREE_GROUP = MP.."Mall_Squad3" MALL_SQUAD_FOUR_GROUP = MP.."Mall_Squad4" GENERALS_RIDE_GROUP = MP.."Generals_Ride" SHAUNDI_DROPOFF_ATV_GROUP = MP.."Shaundi_Dropoff_ATV" PIERCE_DROPOFF_ATV_GROUP = MP.."Pierce_Dropoff_ATV" DROPOFF_ATV_GROUPS = { [SHAUNDI_NAME] = SHAUNDI_DROPOFF_ATV_GROUP, [PIERCE_NAME] = PIERCE_DROPOFF_ATV_GROUP } VEHICLE_SHOW_GROUPS = { MP.."Vehicle_Show01", MP.."Vehicle_Show02", MP.."Vehicle_Show03", MP.."Vehicle_Show04", MP.."Vehicle_Show05", MP.."Vehicle_Show06" } -- Vehicle names BUS = MP.."Bus" GENERALS_LIMO = MP.."Generals_Limo" FIRST_DEFENDER_GROUP_VEHICLE = MP.."Defender_V1" SECOND_DEFENDER_GROUP_VEHICLE = MP.."Defender_V2" GENERALS_RIDE = MP.."Generals_Ride_02" SHAUNDI_DROPOFF_ATV = MP.."Shaundi_Dropoff_ATV" PIERCE_DROPOFF_ATV = MP.."Pierce_Dropoff_ATV" DROPOFF_ATVS = { [SHAUNDI_NAME] = SHAUNDI_DROPOFF_ATV, [PIERCE_NAME] = PIERCE_DROPOFF_ATV } VEHICLE_SHOW_VEHICLES = { MP.."ATV_01", MP.."ATV_02", MP.."ATV_03", MP.."ATV_04", MP.."ATV_05", MP.."ATV_06" } -- Navpoints and paths LOCAL_PLAYER_START = MP.."Local_Player_Start" REMOTE_PLAYER_START = MP.."Remote_Player_Start" LOCAL_PLAYER_AMBUSH_LOCATION = MP.."Ambush_Location" REMOTE_PLAYER_AMBUSH_LOCATION = MP.."Remote_Player_Ambush" FIRST_DEFENDER_GROUP_DROPOFF = MP.."Defender_V1_Dropoff" SECOND_DEFENDER_GROUP_DROPOFF = MP.."Defender_V2_Dropoff" GENERALS_RIDE_PATHS_CW = { MP.."Boss_Path1", MP.."Boss_Path2", MP.."Boss_Path3", MP.."Boss_Path4" } GENERALS_RIDE_PATHS_CCW = { MP.."Boss_Path1c", MP.."Boss_Path2c", MP.."Boss_Path3c", MP.."Boss_Path4c" } GENERALS_RIDE_PATHS_DIRECTIONS = { GENERALS_RIDE_PATHS_CW, GENERALS_RIDE_PATHS_CCW } GENERALS_RIDE_PATH_STARTS_CW = { MP.."Path1_Start", MP.."Path2_Start", MP.."Path3_Start", MP.."Path4_Start" } GENERALS_RIDE_PATH_STARTS_CCW = { MP.."Path1c_Start", MP.."Path2c_Start", MP.."Path3c_Start", MP.."Path4c_Start" } GENERALS_RIDE_PATH_STARTS_DIRECTIONS = { GENERALS_RIDE_PATH_STARTS_CW, GENERALS_RIDE_PATH_STARTS_CCW } UNDERGROUND_MALL_ENTRANCE = MP.."UG_Mall_Entrance" GENERAL_BATTLE_REMOTE_PLAYER_WARP = MP.."General_Battle_Remote_Player_Warp" GENERAL_BATTLE_LOCAL_PLAYER_WARP = MP.."General_Battle_Local_Player_Warp" GENERAL_BEHIND_PILLAR = MP.."General_Behind_Pillar_Spawn" NEAR_PILLAR_STOP = MP.."Near_Pillar_Stop" SHAUNDI_DROPOFF_ATV_PATH = MP.."Shaundi_Dropoff_ATV_Path" PIERCE_DROPOFF_ATV_PATH = MP.."Pierce_Dropoff_ATV_Path" SHAUNDI_LEAVE_PATH = MP.."Shaundi_Leave_Path" PIERCE_LEAVE_PATH = MP.."Pierce_Leave_Path" DROPOFF_LEAVE_PATHS = { [SHAUNDI_NAME] = SHAUNDI_LEAVE_PATH, [PIERCE_NAME] = PIERCE_LEAVE_PATH } SHAUNDI_NEAR_ATV_WARP = MP.."Shaundi_Near_ATV_Warp" DROPOFF_ATV_PATHS = { [SHAUNDI_NAME] = SHAUNDI_DROPOFF_ATV_PATH, [PIERCE_NAME] = PIERCE_DROPOFF_ATV_PATH } MALL_PATH_INITIAL = MP.."Mall_Path_Initial" GENERALS_RIDE_MALL_AROUND_THE_MALL_PATHS = { MP.."Generals_Ride_Mall_Path01", MP.."Generals_Ride_Mall_Path02", MP.."Generals_Ride_Mall_Path03", MP.."Generals_Ride_Mall_Path04" } -- Triggers SAINTS_PARKING_LOT_AREA_TRIGGER = MP.."Saints_Parking_Lot_Area" AMBUSH_LOCATION_TRIGGER = MP.."Ambush_Location" ENTER_UG_MALL_TELEPORT_TRIGGER = MP.."UG_Mall_Teleport_Entrance" -- The in-game enter and leave underground mall triggers. Not used by this mission- -- we disable them. DEFAULT_ENTER_UG_MALL_TRIGGER = "highend_$t-ug-mall-elevator-out" DEFAULT_LEAVE_UG_MALL_TRIGGER = "highend_$t-ug-mall-elevator-in" PARKING_GARAGE_UG_MALL_TRIGGER = MP.."UG_Mall_PG_Entrance" SQUAD_ONE_ATTACK_TRIGGER = MP.."Squad1_Attack" PAST_SQUAD_ONE_TRIGGER = MP.."Past_Squad1" PAST_SQUAD_TWO_TRIGGER = MP.."Past_Squad2" GENERAL_BATTLE_TRIGGER = MP.."General_Battle" GENERAL_BATTLE_ATTACK_POINTS = { MP.."Attack_Point1", MP.."Attack_Point2" } GENERAL_BATTLE_AREA_TRIGGER = MP.."In_Arena" -- Movers CHAIRS = { MP.."Chair01", MP.."Chair02", MP.."Chair03", MP.."Chair04", MP.."Chair05", MP.."Chair06", MP.."Chair07", MP.."Chair08", MP.."Chair09", MP.."Chair10", MP.."Chair11", MP.."Chair12", MP.."Chair13", MP.."Chair14", MP.."Chair15", MP.."Chair16", MP.."Chair17", MP.."Chair18", MP.."Chair19", MP.."Chair20", MP.."Chair21", MP.."Chair22", MP.."Chair23", MP.."Chair24", MP.."Chair25", MP.."Chair26", MP.."Chair27", MP.."Chair28", MP.."Chair29", MP.."Chair30", MP.."Chair31", MP.."Chair32", MP.."Chair33", MP.."Chair34", MP.."Chair35", MP.."Chair36", MP.."Chair37", MP.."Chair38", MP.."Chair39", MP.."Chair40", MP.."Chair41", MP.."Chair42" } TABLES = { MP.."Table01", MP.."Table02", MP.."Table03", MP.."Table04", MP.."Table05", MP.."Table06", MP.."Table07", MP.."Table08", MP.."Table09", MP.."Table10", MP.."Table11", MP.."Table12", MP.."Table13", MP.."Table14" } SPEED_LIMIT_20_TRIGGERS = { MP.."Speed_Limit_20_01", MP.."Speed_Limit_20_02", MP.."Speed_Limit_20_03", MP.."Speed_Limit_20_04" } SPEED_LIMIT_25_TRIGGERS = { MP.."Speed_Limit_25_01", MP.."Speed_Limit_25_02", MP.."Speed_Limit_25_03", MP.."Speed_Limit_25_04", MP.."Speed_Limit_25_05", MP.."Speed_Limit_25_06", MP.."Speed_Limit_25_07", MP.."Speed_Limit_25_08" } SPEED_LIMIT_30_TRIGGERS = { MP.."Speed_Limit_30_01", MP.."Speed_Limit_30_02", MP.."Speed_Limit_30_03", MP.."Speed_Limit_30_04" } SPEED_LIMIT_35_TRIGGERS = { MP.."Speed_Limit_35_01" } SPEED_LIMIT_40_TRIGGERS = { MP.."Speed_Limit_40_01", MP.."Speed_Limit_40_02", MP.."Speed_Limit_40_03", MP.."Speed_Limit_40_04" } -- Effects and animation states -- Cutscenes CUTSCENE_NAME = "ig_ss11_scene1" CT_INTRO = "ss11-01" CT_OUTRO = "ss11-02" CT_COLLISION = "ss11-collision" -- Sound -- Persona overrides THE_GENERAL_PERSONA_OVERRIDES = { { "threat - alert (solo attack)", "GENERAL_SOS11_ATTACK" }, { "threat - alert (group attack)", "GENERAL_SOS11_ATTACK" }, { "threat - damage received (firearm)", "GENERAL_SOS11_TAKEDAM" }, { "threat - damage received (melee)", "GENERAL_SOS11_TAKEDAM" } } SHAUNDI_PERSONA_OVERRIDES = { { "hostage - barters", "SOS11_SHAUNDI_BARTER" }, { "misc - respond to player taunt w/taunt", "SOS11_SHAUNDI_TAUNT" }, { "observe - praised by pc", "SOS11_SHAUNDI_PRAISED" }, { "threat - alert (group attack)", "SOS11_SHAUNDI_ATTACK" }, { "threat - alert (solo attack)", "SOS11_SHAUNDI_ATTACK" }, { "threat - damage received (firearm)", "SOS11_SHAUNDI_TAKEDAM" }, { "threat - damage received (melee)", "SOS11_SHAUNDI_TAKEDAM" } } -- Lines/Dialog Stream INTRO_DIALOG_STREAM = { { "SOS11_INTRO_L1", SHAUNDI_NAME, 0 }, { "PLAYER_SOS11_INTRO_L2", LOCAL_PLAYER, 0 } } TAKE_OUT_GENERAL_DIALOG_STREAM = { { "PLAYER_SOS11_AMBUSH_01", LOCAL_PLAYER, 0 } } SHAUNDI_GET_READY_FOR_LIMO_LINE = "SOS11_SHAUNDI_SEEGENERAL_1" SHAUNDI_LIMO_BUILT_LIKE_TANK_LINE = "SOS11_SHAUNDI_ATTACKLIMO_1" SHAUNDI_GENERAL_IS_ESCAPING_LINE = "SHAUNDI_SOS11_GENERALFLEE_2" PLAYER_TAKE_THEM_OUT_LINE = "PLAYER_SOS11_AMBUSH_01" -- Sound effects -- Distances -- Percents and multipliers GENERALS_RIDE_HP_MULTIPLIER = 3 GENERAL_HP_MULTIPLIER = 5 HURRY_UP_PROMPT_PERCENT_OF_TIME = 0.75 GENERALS_RIDE_START_MALL_LOOP_DAMAGE_PERCENT = .75 -- Time values MISSION_START_FADED_TIME_SECONDS = 3.0 GET_TO_AMBUSH_TIME_MS = 110000 BODYGUARD_LIMO_EXIT_DELAY_SECONDS = 10 LIMO_CATCH_FIRE_DELAY_SECONDS = 30 BUS_DRIVER_EXIT_DELAY_SECONDS = 7 GENERAL_EXIT_LIMO_DELAY_SECONDS = 2 LIMO_EXPLODE_DELAY_SECONDS = 4 GENERAL_MALL_CHECKPOINT_DELAY_SECONDS = 2.0 DRIVER_GRENADE_THROW_INTERVAL_SECONDS = 10 ATTACK_THE_PLAYER_TIME_SECONDS = 15 GENERALS_RIDE_PAUSE_DELAY_SECONDS = 12 BETWEEN_GENERAL_ATTACKS_TIME_SECONDS = 12 BETWEEN_GENERAL_PAUSES_DELAY_SECONDS = 6 VS_VEHICLE_RESPAWN_DELAY_SECONDS = 30 -- How long elapses between the left-hand and right-hand groups of squad three -- attacking the player BETWEEN_SQUAD_THREE_GROUPS_ATTACK_SECONDS = 3.0 -- How many members of squad three must die before we spawn squad 4 SQUAD_THREE_KILL_BEFORE_SPAWN_COUNT = 4 -- How many members of squad four must be killed before we bump up Samedi notoriety SQUAD_FOUR_KILL_BEFORE_NOTORIETY_INCREASE = sizeof_table( MALL_SQUAD_MEMBERS[4] ) -- Speeds ATV_DROPOFF_SPEED_MPH = 25 GENERALS_RIDE_PATH_START_SPEED_MPH = 20 -- Chunks MALL_CHUNK_NAME = "sr2_ug_chunk159_MallA" -- Constant values and counts NUM_ESCORT_MEMBERS = sizeof_table( BODYGUARD_GROUP_MEMBERS ) + sizeof_table( FIRST_DEFENDER_GROUP_MEMBERS ) + sizeof_table( SECOND_DEFENDER_GROUP_MEMBERS ) -- Global variables Generals_ride_pathing_thread_handle = INVALID_THREAD_HANDLE Generals_ride_driver_throw_grenades_thread_handle = INVALID_THREAD_HANDLE Generals_ride_stop_cycle_thread_handle = INVALID_THREAD_HANDLE Generals_ride_attack_dispatcher_thread_handle = INVALID_THREAD_HANDLE Generals_ride_attacking_player_outside_of_arena_thread_handle = INVALID_THREAD_HANDLE Generals_ride_attacking_player = false Generals_ride_stopped = false Players_in_arena = { [LOCAL_PLAYER] = false, [REMOTE_PLAYER] = false } Players_went_underground = { [LOCAL_PLAYER] = false, [REMOTE_PLAYER] = false } Num_escort_members_killed = 0 Num_mall_squad_three_members_killed = 0 Num_mall_squad_four_members_killed = 0 Hurry_up_prompt_thread_handle = INVALID_THREAD_HANDLE Built_like_tank_line_played = false Current_path_direction = 0 Controls_disabled = false Went_underground_state_switched = false -- Returns true if both players are in the center of the food court, false otherwise. -- function ss11_both_players_in_arena() if ( Players_in_arena[LOCAL_PLAYER] ) then if ( coop_is_active() ) then if ( Players_in_arena[REMOTE_PLAYER] ) then return true end else return true end end return false end function ss11_create_mall_groups( blocking ) if ( blocking == nil ) then blocking = false end if ( group_is_loaded( SHAUNDI_DROPOFF_ATV_GROUP ) == false ) then group_create_hidden( SHAUNDI_DROPOFF_ATV_GROUP, blocking ) end if ( group_is_loaded( PIERCE_DROPOFF_ATV_GROUP ) == false ) then group_create_hidden( PIERCE_DROPOFF_ATV_GROUP, blocking ) end for index, group_name in pairs( VEHICLE_SHOW_GROUPS ) do if ( group_is_loaded( group_name ) == false ) then group_create_hidden( group_name, blocking ) end end end -- Starts and sets up the mission. Also handles setting up the mission -- correctly for checkpoints. -- -- checkpoint_name: Name of checkpoint to resume the mission from. -- function ss11_start( checkpoint_name, is_restart ) -- Start trigger is hit...the activate button was hit set_mission_author("Mark G. and Brad Johnson") character_slots_cap_for_team( "ronin", 1 ) character_slots_cap_for_team( "police", 1 ) chainsaw_disable() fade_out( 0 ) local next_state player_controls_disable( LOCAL_PLAYER ) if ( coop_is_active() ) then player_controls_disable( REMOTE_PLAYER ) end if ( checkpoint_name == MISSION_START_CHECKPOINT ) then -- The starter groups are this in single player local start_groups = { SHAUNDI_GROUP, STARTER_CAR_GROUP, AMBUSHER_SAINTS_GROUP } -- If we're running in coop, append an extra group to the end if ( coop_is_active() ) then start_groups = { SHAUNDI_GROUP, STARTER_CAR_GROUP, AMBUSHER_SAINTS_GROUP, STARTER_CAR_COOP_GROUP } end if (not is_restart) then cutscene_play( CT_INTRO, start_groups, { LOCAL_PLAYER_START, REMOTE_PLAYER_START }, false ) for index, group_name in pairs( start_groups ) do group_show( group_name ) end else for index, group_name in pairs( start_groups ) do group_create( group_name, true ) end -- Teleport the player last - helps with load times teleport_coop( LOCAL_PLAYER_START, REMOTE_PLAYER_START ) end end -- Set up failure conditions for Shaundi dying and being abandoned on_death( "ss11_follower_died", SHAUNDI_NAME ) on_dismiss( "ss11_follower_dismissed", SHAUNDI_NAME ) if ( checkpoint_name == MISSION_START_CHECKPOINT ) then for index, override in pairs( SHAUNDI_PERSONA_OVERRIDES ) do persona_override_character_start( SHAUNDI_NAME, override[1], override[2] ) end party_add( SHAUNDI_NAME, LOCAL_PLAYER ) -- Set up failure conditions for Shaundi dying and being abandoned on_death( "ss11_follower_died", SHAUNDI_NAME ) on_dismiss( "ss11_follower_dismissed", SHAUNDI_NAME ) next_state = MS_TRAVEL_TO_AMBUSH_LOCATION elseif ( checkpoint_name == REACHED_AMBUSH_LOCATION_CHECKPOINT ) then group_create( AMBUSHER_SAINTS_GROUP, true ) if ( group_is_loaded( SHAUNDI_GROUP ) == false ) then group_create( SHAUNDI_GROUP, true ) end local disable_controls_required = false ss11_setup_ambush( disable_controls_required ) next_state = "Puerto Rico" next_state = MS_ABOVEGROUND_FIREFIGHT elseif ( checkpoint_name == GENERAL_FIGHT_CHECKPOINT ) then -- We need the mall groups immediately at this point, so load them blocking ss11_create_mall_groups( true ) if ( group_is_loaded( SHAUNDI_GROUP ) == false ) then group_create( SHAUNDI_GROUP, true ) end local resuming_from_checkpoint = true -- Setup the ambient vehicles for the vehicle show ss11_setup_vehicle_show() ss11_hide_food_court_tables_and_chairs() ss11_setup_generals_ride_fight( resuming_from_checkpoint ) end player_controls_enable( LOCAL_PLAYER ) if ( coop_is_active() ) then player_controls_enable( REMOTE_PLAYER ) end notoriety_allow_indoor_gang_spawning(true) fade_in( MISSION_START_FADED_TIME_SECONDS ) if ( checkpoint_name == MISSION_START_CHECKPOINT or checkpoint_name == REACHED_AMBUSH_LOCATION_CHECKPOINT ) then -- Create groups that will be used in the mall non-blocking since they have time to -- stream in ss11_create_mall_groups( false ) end -- Don't let the player go underground using these triggers. He should -- only be able to go underground when we enable the mission-specific trigger, -- which isn't use-activated like these are. trigger_enable( DEFAULT_ENTER_UG_MALL_TRIGGER, false ) trigger_enable( DEFAULT_LEAVE_UG_MALL_TRIGGER, false ) ss11_switch_state( next_state ) end -- Called when one of the Samedi defending the General outside the mall -- gets killed. They are required kills - when all are delt with, the -- General runs away and the mission proceeds with the player giving -- chase. -- function ss11_general_outside_defender_killed( defender_name ) marker_remove_npc( defender_name, SYNC_ALL ) Num_escort_members_killed = Num_escort_members_killed + 1 objective_text( 0, HT_X_OF_Y_ESCORT_MEMBERS_KILLED, Num_escort_members_killed, NUM_ESCORT_MEMBERS, SYNC_ALL ) if ( Num_escort_members_killed == NUM_ESCORT_MEMBERS ) then ss11_switch_state( MS_GENERAL_ESCAPING_UNDERGROUND ) end end function ss11_generals_limo_hit( name, attacker ) if ( Built_like_tank_line_played == false and ( attacker == LOCAL_PLAYER or attacker == REMOTE_PLAYER ) ) then audio_play_for_character( SHAUNDI_LIMO_BUILT_LIKE_TANK_LINE, SHAUNDI_NAME, "voice", false, false ) Built_like_tank_line_played = true end end -- Sets up the ambush. The Saints are ambushing the General and have -- people surrounding his expected stop point. -- -- disable_controls_required: (optional) Whether or not we need to disable the player's -- controls. Defaults to true. function ss11_setup_ambush( disable_controls_required ) if ( disable_controls_required == nil ) then disable_controls_required = true end spawning_vehicles( false ) thread_yield() -- Clear out the starter cars so they don't mess with the spawning if ( group_is_loaded( STARTER_CAR_GROUP ) ) then group_destroy( STARTER_CAR_GROUP ) end if ( group_is_loaded( STARTER_CAR_COOP_GROUP ) ) then group_destroy( STARTER_CAR_COOP_GROUP ) end -- Disable control during the ambush if ( disable_controls_required ) then player_controls_disable( LOCAL_PLAYER ) if ( coop_is_active() ) then player_controls_disable( REMOTE_PLAYER ) end Controls_disabled = true end teleport_coop( LOCAL_PLAYER_AMBUSH_LOCATION, REMOTE_PLAYER_AMBUSH_LOCATION, true ) teleport( SHAUNDI_NAME, LOCAL_PLAYER_AMBUSH_LOCATION ) -- Create the Samedi groups group_create( THE_GENERAL_GROUP, true ) group_create( GENERALS_LIMO_GROUP, true ) group_create( LIMO_DEFENDERS_GROUP, true ) group_create( BUS_GROUP, true ) -- The General shouldn't be affected by or affect the fight. turn_invulnerable( THE_GENERAL_NAME ) inv_item_remove_all( THE_GENERAL_NAME ) for index, override in pairs( THE_GENERAL_PERSONA_OVERRIDES ) do persona_override_character_start( THE_GENERAL_NAME, override[1], override[2] ) end turn_invulnerable( GENERALS_LIMO ) on_take_damage( "ss11_generals_limo_hit", GENERALS_LIMO ) vehicle_enter_teleport( BODYGUARD_GROUP_MEMBERS[1], GENERALS_LIMO, 0, true ) vehicle_enter_teleport( THE_GENERAL_NAME, GENERALS_LIMO, 2, true ) vehicle_suppress_npc_exit( GENERALS_LIMO, true ) vehicle_prevent_explosion_fling( GENERALS_LIMO, true ) vehicle_infinite_mass( GENERALS_LIMO, true ) vehicle_set_bulletproof_glass( GENERALS_LIMO, true ) vehicle_speed_override( GENERALS_LIMO, 0 ) vehicle_enter_teleport( BUS_DRIVER_NAME, BUS, 0, true ) vehicle_suppress_npc_exit( BUS, true ) set_unjackable_flag( GENERALS_LIMO, true ) vehicle_speed_override( BUS, 0 ) vehicle_enter_group_teleport( FIRST_DEFENDER_GROUP_MEMBERS, FIRST_DEFENDER_GROUP_VEHICLE ) vehicle_enter_group_teleport( SECOND_DEFENDER_GROUP_MEMBERS, SECOND_DEFENDER_GROUP_VEHICLE ) ss11_setup_required_kill_defenders_group( FIRST_DEFENDER_GROUP_MEMBERS ) ss11_setup_required_kill_defenders_group( SECOND_DEFENDER_GROUP_MEMBERS ) ss11_setup_required_kill_defenders_group( BODYGUARD_GROUP_MEMBERS ) -- Give the players control if ( disable_controls_required ) then player_controls_enable( LOCAL_PLAYER ) if ( coop_is_active() ) then player_controls_enable( REMOTE_PLAYER ) end Controls_disabled = false end spawning_vehicles( true ) end function ss11_prompt_hurry_up() delay( HURRY_UP_PROMPT_PERCENT_OF_TIME * ( GET_TO_AMBUSH_TIME_MS / 1000.0 ) ) end function ss11_setup_required_kill_defenders_group( group_members ) for index, name in ipairs( group_members ) do mission_debug( "markering member "..index..": "..name, 15 ) marker_add_npc( name, MINIMAP_ICON_KILL, INGAME_EFFECT_KILL, SYNC_ALL ) on_death( "ss11_general_outside_defender_killed", name ) end end -- Failure function for critical follower dying -- function ss11_follower_died( follower_name ) if ( follower_name == SHAUNDI_NAME ) then mission_end_failure( MISSION_NAME, HT_SHAUNDI_DIED ) end end -- Failure function for critical follower being dismissed or being abandoned -- function ss11_follower_dismissed( follower_name ) if ( follower_name == SHAUNDI_NAME ) then mission_end_failure( MISSION_NAME, HT_SHAUNDI_ABANDONED ) end end -- Cleans up the mission and restores the game to an acceptable state -- function ss11_cleanup() -- Cleanup mission here -- Restore the in-game enter-leave triggers for the UG mall now that -- the mission is over. trigger_enable( DEFAULT_ENTER_UG_MALL_TRIGGER, true ) trigger_enable( DEFAULT_LEAVE_UG_MALL_TRIGGER, true ) notoriety_set_min( ENEMY_GANG, 0 ) notoriety_set( ENEMY_GANG, 0 ) notoriety_set_min( "police", 0 ) notoriety_set( "police", 0 ) chainsaw_enable() spawning_allow_gang_team_ambient_spawns( HUMAN_TEAM_SAMEDI, true ) notoriety_allow_indoor_gang_spawning(false) spawning_vehicles( true ) spawning_pedestrians( true, true ) on_vehicle_enter( "", LOCAL_PLAYER ) if ( coop_is_active() ) then on_vehicle_enter( "", REMOTE_PLAYER ) end if ( Controls_disabled ) then player_controls_enable( LOCAL_PLAYER ) if ( coop_is_active() ) then player_controls_enable( REMOTE_PLAYER ) end end hud_timer_stop( 0 ) spawning_vehicles( true ) character_slots_clear_caps() end function ss11_take_out_general_dialog() audio_play_conversation( TAKE_OUT_GENERAL_DIALOG_STREAM, NOT_CALL ) end function ss11_shaundi_blah( triggerer_name, trigger_name ) trigger_enable( trigger_name, false ) delay( 20 ) audio_play_conversation( INTRO_DIALOG_STREAM, NOT_CALL ) end -- Switches the mission to a new state. -- -- new_state: Name of the new state to switch to. -- function ss11_switch_state( new_state ) if ( new_state == MS_TRAVEL_TO_AMBUSH_LOCATION ) then -- Add callback for the player getting a phone call from Shaundi trigger_enable( SAINTS_PARKING_LOT_AREA_TRIGGER, true ) on_trigger_exit( "ss11_shaundi_blah", SAINTS_PARKING_LOT_AREA_TRIGGER ) hud_timer_set( 0, GET_TO_AMBUSH_TIME_MS, "ss11_missed_ambush" ) Hurry_up_prompt_thread_handle = thread_new( "ss11_prompt_hurry_up" ) -- Enable trigger trigger_enable( AMBUSH_LOCATION_TRIGGER, true ) on_trigger( "ss11_reached_ambush_location", AMBUSH_LOCATION_TRIGGER ) -- Let the player know what he should be doing -- Prompt mission_help_table( HT_GET_TO_AMBUSH ) -- Location marker and GPS path marker_add_trigger( AMBUSH_LOCATION_TRIGGER, MINIMAP_ICON_LOCATION, INGAME_EFFECT_LOCATION, SYNC_ALL ) waypoint_add( AMBUSH_LOCATION_TRIGGER, SYNC_ALL ) elseif ( new_state == MS_CUTSCENE ) then -- Play the cutscene cutscene_play( CT_COLLISION ) fade_out( 0 ) -- Setup the ambush groups ss11_setup_ambush() fade_in( 1.0 ) release_to_world( AMBUSHER_SAINTS_GROUP ) mission_set_checkpoint( REACHED_AMBUSH_LOCATION_CHECKPOINT ) ss11_switch_state( MS_ABOVEGROUND_FIREFIGHT ) -- Set the checkpoint elseif ( new_state == MS_ABOVEGROUND_FIREFIGHT ) then thread_new( "ss11_take_out_general_dialog" ) notoriety_set_min( ENEMY_GANG, 3 ) -- Spawn threads for the general's bodyguards getting out, the general -- getting out, the bus driver getting out, and the defender cars coming to help thread_new( "ss11_generals_bodyguards_get_out" ) thread_new( "ss11_bus_driver_get_out" ) thread_new( "ss11_defender_cars_come_help" ) delay( 5.0 ) mission_help_table( HT_FINISH_OFF_ESCORTS ) objective_text( 0, HT_X_OF_Y_ESCORT_MEMBERS_KILLED, 0, NUM_ESCORT_MEMBERS, SYNC_ALL ) elseif ( new_state == MS_GENERAL_ESCAPING_UNDERGROUND ) then -- Set the limo on fire, have the general get out, and have him run to -- the UG mall entrance turn_vulnerable( GENERALS_LIMO ) --vehicle_set_smoke_and_fire_state( GENERALS_LIMO, true, true ) delay( GENERAL_EXIT_LIMO_DELAY_SECONDS ) ss11_setup_general_for_escape() local general_escape_thread_handle = thread_new( "ss11_general_escape" ) delay( LIMO_EXPLODE_DELAY_SECONDS ) vehicle_infinite_mass( GENERALS_LIMO, false ) vehicle_prevent_explosion_fling( GENERALS_LIMO, false ) vehicle_detonate( GENERALS_LIMO ) if ( thread_check_done( general_escape_thread_handle ) == false ) then delay( 5.0 ) if ( thread_check_done( general_escape_thread_handle ) == false ) then thread_kill( general_escape_thread_handle ) ss11_guide_player_to_chase_the_general() end end ss11_general_clear_escape_flags() elseif ( new_state == MS_THROUGH_THE_MALL_FIREFIGHT ) then -- Load the Samedi groups - make it blocking to be safer fade_out( 0 ) player_controls_disable( LOCAL_PLAYER ) if ( coop_is_active() ) then player_controls_disable( REMOTE_PLAYER ) end if ( group_is_loaded( MALL_SAMEDI ) == false ) then group_create( MALL_SAMEDI, true ) end group_create_hidden( MALL_SQUAD_THREE_GROUP, true ) group_create_hidden( MALL_SQUAD_FOUR_GROUP, true ) -- Setup the ambient vehicles for the vehicle show ss11_setup_vehicle_show() notoriety_set ( "police", 0 ) notoriety_set_max ( "police", 0 ) player_controls_enable( LOCAL_PLAYER ) if ( coop_is_active() ) then player_controls_enable( REMOTE_PLAYER ) end fade_in( 0 ) -- Let the player know what he needs to do in the mall mission_help_table( HT_TRACK_DOWN_GENERAL ) trigger_enable( SQUAD_ONE_ATTACK_TRIGGER, true ) on_trigger( "ss11_squad_one_attack", SQUAD_ONE_ATTACK_TRIGGER ) trigger_enable( PAST_SQUAD_ONE_TRIGGER, true ) on_trigger( "ss11_passed_squad_one", PAST_SQUAD_ONE_TRIGGER ) trigger_enable( PAST_SQUAD_TWO_TRIGGER, true ) on_trigger( "ss11_passed_squad_two", PAST_SQUAD_TWO_TRIGGER ) for member_index, member_name in pairs( MALL_SQUAD_MEMBERS[3] ) do on_death( "ss11_mall_squad_three_member_died", member_name ) end for member_index, member_name in pairs( MALL_SQUAD_MEMBERS[4] ) do on_death( "ss11_mall_squad_four_member_died", member_name ) end trigger_enable( GENERAL_BATTLE_TRIGGER, true ) marker_add_trigger( GENERAL_BATTLE_TRIGGER, MINIMAP_ICON_LOCATION, "", SYNC_ALL ) on_trigger( "ss11_start_battle", GENERAL_BATTLE_TRIGGER ) -- make ambient peds flee thread_new("ss11_ambient_npcs_flee") ss11_hide_food_court_tables_and_chairs() elseif ( new_state == MS_GENERAL_BOSS_BATTLE_DRIVING_PART ) then -- Clear on-death callbacks for the squads because they shouldn't change mission behavior -- after this point for member_index, member_name in pairs( MALL_SQUAD_MEMBERS[3] ) do if ( character_is_dead( member_name ) == false ) then on_death( "", member_name ) end end for member_index, member_name in pairs( MALL_SQUAD_MEMBERS[4] ) do if ( character_is_dead( member_name ) == false ) then on_death( "", member_name ) end end -- Setup the fight ss11_setup_generals_ride_fight() elseif ( new_state == MS_GENERAL_BOSS_BATTLE_ON_FOOT_PART ) then end end function ss11_get_vs_vehicle_index_from_name( vehicle_to_find ) for index, vehicle_name in pairs( VEHICLE_SHOW_VEHICLES ) do if ( vehicle_name == vehicle_to_find ) then return index end end return -1 end function ss11_respawn_vehicle_group( vehicle_index ) release_to_world( VEHICLE_SHOW_GROUPS[vehicle_index] ) group_create_hidden( VEHICLE_SHOW_GROUPS[vehicle_index] ) delay( VS_VEHICLE_RESPAWN_DELAY_SECONDS ) local vehicle_in_view = true repeat thread_yield() vehicle_in_view = navpoint_in_player_fov( VEHICLE_SHOW_VEHICLES[vehicle_index], LOCAL_PLAYER ) if ( coop_is_active() and vehicle_in_view == false ) then vehicle_in_view = navpoint_in_player_fov( VEHICLE_SHOW_VEHICLES[vehicle_index], REMOTE_PLAYER ) end until ( vehicle_in_view == false ) group_show( VEHICLE_SHOW_GROUPS[vehicle_index] ) end function ss11_entered_vs_vehicle( player_name, vehicle_name ) local vehicle_index = ss11_get_vs_vehicle_index_from_name( vehicle_name ) thread_new( "ss11_respawn_vehicle_group", vehicle_index ) end function ss11_setup_vehicle_show() -- Show the vehicle show groups for index, group_name in pairs( VEHICLE_SHOW_GROUPS ) do group_show( group_name ) on_vehicle_enter( "ss11_entered_vs_vehicle", VEHICLE_SHOW_VEHICLES[index] ) end end -- Causes all the members of the first mall squad to attack the player when he -- gets close enough. -- function ss11_squad_one_attack( triggerer_name, trigger_name ) trigger_enable( trigger_name, false ) for member_index, member_name in pairs( MALL_SQUAD_MEMBERS[1] ) do attack_safe( member_name, triggerer_name ) end end -- Called when the player passes the first squad. Unleashes any squad members that -- are still alive and leashed, and tells the second squad to attack the player. -- function ss11_passed_squad_one( triggerer_name, trigger_name ) trigger_enable( trigger_name, false ) for member_index, member_name in pairs( MALL_SQUAD_LEASHED_MEMBERS[1] ) do if ( character_is_dead( member_name ) == false ) then npc_leash_remove( member_name ) end end for member_index, member_name in pairs( MALL_SQUAD_MEMBERS[2] ) do attack_safe( member_name, triggerer_name ) end end -- Called when the player passes the second squad. Unleashes any squad members that -- are still alive and leashed, and unhides the third squad and has it attack the -- player. -- function ss11_passed_squad_two( triggerer_name, trigger_name ) trigger_enable( trigger_name, false ) -- Remove the leashes from Squad 2 members that we've passed for member_index, member_name in pairs( MALL_SQUAD_LEASHED_MEMBERS[2] ) do if ( character_is_dead( member_name ) == false ) then npc_leash_remove( member_name ) end end -- Show the third squad group_show( MALL_SQUAD_THREE_GROUP ) -- Have the left-hand portion of it attack for member_index, member_name in pairs( MALL_SQUAD_THREE_LEFT_SIDE_MEMBERS ) do attack_safe( member_name, triggerer_name ) end -- Delay, then have the right-hand portion of it attack delay( BETWEEN_SQUAD_THREE_GROUPS_ATTACK_SECONDS ) for member_index, member_name in pairs( MALL_SQUAD_THREE_RIGHT_SIDE_MEMBERS ) do local distance, player_name = get_dist_closest_player_to_object( member_name ) attack_safe( member_name, player_name ) end end -- Death tracking function for squad three. If enough members die before the -- fight with the General, group four is spawned. -- function ss11_mall_squad_three_member_died() Num_mall_squad_three_members_killed = Num_mall_squad_three_members_killed + 1 if ( Num_mall_squad_three_members_killed == SQUAD_THREE_KILL_BEFORE_SPAWN_COUNT ) then group_show( MALL_SQUAD_FOUR_GROUP ) for member_index, member_name in pairs( MALL_SQUAD_MEMBERS[4] ) do local distance, player_name = get_dist_closest_player_to_object( member_name ) attack_safe( member_name, player_name ) end end end -- Death tracking function for squad four. If enough members die before the -- fight with the General, Samedi notoriety is maxed out. -- function ss11_mall_squad_four_member_died() Num_mall_squad_four_members_killed = Num_mall_squad_four_members_killed + 1 if ( Num_mall_squad_four_members_killed == SQUAD_FOUR_KILL_BEFORE_NOTORIETY_INCREASE ) then notoriety_set_min( ENEMY_GANG, MAX_NOTORIETY_LEVEL ) end end function ss11_assure_entry_success( character_name, vehicle_name, seat_index ) repeat vehicle_enter_teleport( character_name, vehicle_name, seat_index, false ) delay( 0.25 ) until ( character_is_in_vehicle( character_name, vehicle_name ) ) end function ss11_setup_generals_ride_fight( resuming_from_checkpoint ) if ( resuming_from_checkpoint == nil ) then resuming_from_checkpoint = false end -- Remove Shaundi from the player's party on_dismiss( "", SHAUNDI_NAME ) on_death( "", SHAUNDI_NAME ) party_dismiss( SHAUNDI_NAME ) group_hide( SHAUNDI_GROUP ) group_destroy( SHAUNDI_GROUP ) group_create_hidden( SHAUNDI_GROUP, true ) notoriety_set ( "police", 0 ) notoriety_set_max ( "police", 0 ) script_profiler_reset() -- If we're supposed to play the cutscene, set it up and do it if ( resuming_from_checkpoint == false ) then cutscene_play( CUTSCENE_NAME, "", { GENERAL_BATTLE_LOCAL_PLAYER_WARP, GENERAL_BATTLE_REMOTE_PLAYER_WARP }, false ) script_profiler_start_section( "Post CT: Part 1" ) spawning_vehicles( false ) spawning_pedestrians( false, true ) player_controls_disable( LOCAL_PLAYER ) if ( coop_is_active() ) then player_controls_disable( REMOTE_PLAYER ) end Controls_disabled = true group_hide( "ss11_$GCTE" ) group_destroy( "ss11_$GCTE" ) group_create( GENERALS_RIDE_GROUP, true ) teleport( THE_GENERAL_NAME, GENERAL_BEHIND_PILLAR ) character_show( THE_GENERAL_NAME ) vehicle_suppress_npc_exit( GENERALS_RIDE, true ) script_profiler_end_section( "Post CT: Part 1" ) -- Otherwise, we're doing a checkpoint, so just create the groups -- we don't have else spawning_vehicles( false ) spawning_pedestrians( false, true ) group_create( THE_GENERAL_GROUP, true ) group_create( GENERALS_RIDE_GROUP, true ) vehicle_suppress_npc_exit( GENERALS_RIDE, true ) end spawning_vehicles( true ) spawning_pedestrians( true, true ) -- Tell the players to kill the General! marker_add_npc( THE_GENERAL_NAME, MINIMAP_ICON_KILL, INGAME_EFFECT_KILL, SYNC_ALL ) -- Load up the ride script_profiler_start_section( "Post CT: Teleport Entry" ) thread_new( "ss11_assure_entry_success", GENERALS_RIDE_DRIVER_NAME, GENERALS_RIDE, 0 ) thread_new( "ss11_assure_entry_success", THE_GENERAL_NAME, GENERALS_RIDE, 1 ) thread_new( "ss11_assure_entry_success", GENERALS_RIDE_AK_GUYS[1], GENERALS_RIDE, 2 ) thread_new( "ss11_assure_entry_success", GENERALS_RIDE_AK_GUYS[2], GENERALS_RIDE, 3 ) delay( .2 ) script_profiler_end_section( "Post CT: Teleport Entry" ) script_profiler_start_section( "Post CT: Misc" ) -- Setup the people riding with the General to allow for a proper boss battle turn_invulnerable( GENERALS_RIDE_AK_GUYS[1] ) turn_invulnerable( GENERALS_RIDE_AK_GUYS[2] ) turn_invulnerable( GENERALS_RIDE_DRIVER_NAME ) turn_invulnerable( THE_GENERAL_NAME ) set_seatbelt_flag( GENERALS_RIDE_DRIVER_NAME, true ) set_seatbelt_flag( THE_GENERAL_NAME, true ) set_seatbelt_flag( GENERALS_RIDE_AK_GUYS[1], true ) set_seatbelt_flag( GENERALS_RIDE_AK_GUYS[2], true ) -- Prevent the General's ride from flipping over - it makes the battle too easy vehicle_suppress_flipping( GENERALS_RIDE, true ) -- Setup the General's ride for a proper boss battle local standard_hit_points = get_max_hit_points(GENERALS_RIDE) local generals_ride_hp = GENERALS_RIDE_HP_MULTIPLIER * standard_hit_points set_max_hit_points( GENERALS_RIDE, generals_ride_hp ) set_current_hit_points(GENERALS_RIDE, generals_ride_hp ) set_unjackable_flag( GENERALS_RIDE, true ) -- Start the AI mode for the General's car local distance, closest_player = get_dist_closest_player_to_object( GENERALS_RIDE ) Generals_ride_attacking_player_outside_of_arena_thread_handle = thread_new( "ss11_generals_ride_chase_attack_player", closest_player ) Generals_ride_driver_throw_grenades_thread_handle = thread_new( "ss11_generals_ride_driver_throw_grenades" ) on_vehicle_destroyed( "ss11_generals_ride_destroyed", GENERALS_RIDE ) damage_indicator_on( 0, GENERALS_RIDE, 0, HT_GENERALS_RIDE_HP, generals_ride_hp ) -- Trigger stuff used by the boss battle AI trigger_enable( GENERAL_BATTLE_AREA_TRIGGER, true ) on_trigger( "ss11_entered_battle_area", GENERAL_BATTLE_AREA_TRIGGER ) on_trigger_exit( "ss11_left_battle_area", GENERAL_BATTLE_AREA_TRIGGER ) -- Switch to the "loop around the mall" state if he's damaged enough on_damage( "ss11_generals_ride_start_around_the_mall_path", GENERALS_RIDE, GENERALS_RIDE_START_MALL_LOOP_DAMAGE_PERCENT ) script_profiler_end_section( "Post CT: Misc" ) -- Only enable the player's controls and fade in if the opposite things were done above if ( resuming_from_checkpoint == false ) then script_profiler_start_section( "Post CT: Reenable" ) player_controls_enable( LOCAL_PLAYER ) if ( coop_is_active() ) then player_controls_enable( REMOTE_PLAYER ) end Controls_disabled = false script_profiler_end_section( "Post CT: Reenable" ) fade_in( 1.0 ) mission_set_checkpoint( GENERAL_FIGHT_CHECKPOINT ) end script_profiler_do_printout() mission_help_table( HT_KILL_THE_GENERAL ) end -- Called when the player doesn't reach it to the ambush location on time. -- function ss11_missed_ambush() mission_end_failure( MISSION_NAME, HT_DIDNT_MAKE_IT_TO_AMBUSH_IN_TIME ) end function ss11_maybe_kill_thread( thread_handle ) if ( thread_handle ~= INVALID_THREAD_HANDLE ) then thread_kill( thread_handle ) end end function ss11_maybe_speed_override( triggerer_name, speed_mph ) if ( triggerer_name ~= nil ) then if ( character_is_in_vehicle( triggerer_name, GENERALS_RIDE ) ) then vehicle_speed_override( GENERALS_RIDE, speed_mph ) end end end function ss11_speed_20( triggerer_name, trigger_name ) ss11_maybe_speed_override( triggerer_name, 20 ) end function ss11_speed_25( triggerer_name, trigger_name ) ss11_maybe_speed_override( triggerer_name, 25 ) end function ss11_speed_30( triggerer_name, trigger_name ) ss11_maybe_speed_override( triggerer_name, 30 ) end function ss11_speed_35( triggerer_name, trigger_name ) ss11_maybe_speed_override( triggerer_name, 35 ) end function ss11_speed_40( triggerer_name, trigger_name ) ss11_maybe_speed_override( triggerer_name, 40 ) end function ss11_setup_speed_limit_triggers() for index, name in pairs( SPEED_LIMIT_20_TRIGGERS ) do trigger_enable( name, true ) on_trigger( "ss11_speed_20", name ) end for index, name in pairs( SPEED_LIMIT_25_TRIGGERS ) do trigger_enable( name, true ) on_trigger( "ss11_speed_25", name ) end for index, name in pairs( SPEED_LIMIT_30_TRIGGERS ) do trigger_enable( name, true ) on_trigger( "ss11_speed_30", name ) end for index, name in pairs( SPEED_LIMIT_35_TRIGGERS ) do trigger_enable( name, true ) on_trigger( "ss11_speed_35", name ) end for index, name in pairs( SPEED_LIMIT_40_TRIGGERS ) do trigger_enable( name, true ) on_trigger( "ss11_speed_40", name ) end end function ss11_generals_ride_start_around_the_mall_path() mission_debug( "starting around-the-mall path" ) -- Stop the "ride around the food court" attack pattern. ss11_stop_circling_attack() thread_new( "ss11_generals_ride_do_mall_loop" ) thread_new( "ss11_shaundi_and_maybe_pierce_appear" ) end function ss11_generals_ride_do_mall_loop() -- Make sure we're not going crazy vehicle_speed_override( GENERALS_RIDE, GENERALS_RIDE_PATH_START_SPEED_MPH ) vehicle_disable_chase( GENERALS_RIDE, true ) -- Go to the path start vehicle_pathfind_to( GENERALS_RIDE, MALL_PATH_INITIAL, true, true ) -- Enable the speed limits ss11_setup_speed_limit_triggers() -- Have the vehicle pathing around the mall continually ( until it's destroyed ) while ( vehicle_is_destroyed( GENERALS_RIDE ) == false ) do for index, path_name in pairs( GENERALS_RIDE_MALL_AROUND_THE_MALL_PATHS ) do vehicle_pathfind_to( GENERALS_RIDE, path_name, true, false ) end thread_yield() end end function ss11_shaundi_and_maybe_pierce_appear() group_show( SHAUNDI_GROUP ) turn_invulnerable( SHAUNDI_NAME ) teleport( SHAUNDI_NAME, SHAUNDI_NEAR_ATV_WARP ) thread_new( "ss11_drop_off_atv", SHAUNDI_NAME ) if ( coop_is_active() ) then thread_new( "ss11_drop_off_atv", PIERCE_NAME ) end end function ss11_drop_off_atv( follower_name ) -- Create the ATV group_show( DROPOFF_ATV_GROUPS[follower_name] ) set_ignore_ai_flag( follower_name, true ) character_prevent_flinching( follower_name, true ) character_prevent_explosion_fling( follower_name, true ) character_prevent_kneecapping( follower_name, true ) turn_invulnerable( follower_name ) -- Have the follower get in it vehicle_enter_teleport( follower_name, DROPOFF_ATVS[follower_name] ) vehicle_speed_override( DROPOFF_ATVS[follower_name], ATV_DROPOFF_SPEED_MPH ) -- Have it pathfind to the dropoff and have the follower get out vehicle_pathfind_to( DROPOFF_ATVS[follower_name], DROPOFF_ATV_PATHS[follower_name], true, true ) vehicle_exit( follower_name ) --mission_help_table( HT_TEMP_SHAUNDI_DROPOFF_ATV_LINE ) delay( 2.0 ) on_vehicle_enter( "ss11_entered_vehicle_during_chase", LOCAL_PLAYER ) if ( coop_is_active() ) then on_vehicle_enter( "ss11_entered_vehicle_during_chase", REMOTE_PLAYER ) end on_vehicle_enter( "ss11_entered_vehicle_during_chase", DROPOFF_ATVS[follower_name] ) marker_add_vehicle( DROPOFF_ATVS[follower_name], MINIMAP_ICON_LOCATION, INGAME_EFFECT_VEHICLE_INTERACT, SYNC_ALL ) -- Finally, have the follower run away mission_debug( "moving "..follower_name.." along "..DROPOFF_LEAVE_PATHS[follower_name], 15 ) move_to( follower_name, DROPOFF_LEAVE_PATHS[follower_name], 2, true, false ) character_hide( follower_name ) end function ss11_entered_vehicle_during_chase() for index, atv_name in pairs( DROPOFF_ATVS ) do if ( vehicle_is_destroyed( atv_name ) == false ) then marker_remove_vehicle( atv_name, SYNC_ALL ) end end end -- Stops the "ride around the food court" attack pattern. -- function ss11_stop_circling_attack() ss11_maybe_kill_thread( Generals_ride_pathing_thread_handle ) Generals_ride_pathing_thread_handle = INVALID_THREAD_HANDLE ss11_maybe_kill_thread( Generals_ride_driver_throw_grenades_thread_handle ) Generals_ride_driver_throw_grenades_thread_handle = INVALID_THREAD_HANDLE ss11_maybe_kill_thread( Generals_ride_stop_cycle_thread_handle ) Generals_ride_stop_cycle_thread_handle = INVALID_THREAD_HANDLE ss11_maybe_kill_thread( Generals_ride_attack_dispatcher_thread_handle ) Generals_ride_attack_dispatcher_thread_handle = INVALID_THREAD_HANDLE ss11_maybe_kill_thread( Generals_ride_attacking_player_outside_of_arena_thread_handle ) Generals_ride_attacking_player_outside_of_arena_thread_handle = INVALID_THREAD_HANDLE trigger_enable( GENERAL_BATTLE_AREA_TRIGGER, false ) end -- Called when a player enters the center of the food court. -- Keeps track of where the players are and calls appropriate logic -- for the corresponding state. -- -- triggerer_name: Name of the player that triggered the trigger. -- trigger_name: Name of the trigger that was triggered. -- function ss11_entered_battle_area( triggerer_name, trigger_name ) mission_debug( triggerer_name.." entered battle arena" ) -- Update the player location tracking Players_in_arena[triggerer_name] = true -- If both players are inside, then have the general's ride start circling. if ( ss11_both_players_in_arena() ) then if ( Generals_ride_attacking_player_outside_of_arena_thread_handle ~= INVALID_THREAD_HANDLE ) then thread_kill( Generals_ride_attacking_player_outside_of_arena_thread_handle ) Generals_ride_attacking_player_outside_of_arena_thread_handle = INVALID_THREAD_HANDLE end Generals_ride_pathing_thread_handle = thread_new( "ss11_generals_ride_do_circle_pathing" ) Generals_ride_stop_cycle_thread_handle = thread_new( "ss11_generals_ride_stop_cycle" ) Generals_ride_attack_dispatcher_thread_handle = thread_new( "ss11_generals_ride_attack_dispatcher" ) end end -- Called when a player leaves the center of the food court. -- Keeps track of where the players are and calls appropriate logic -- for the corresponding state. -- -- triggerer_name: Name of the player that left the trigger. -- trigger_name: Name of the trigger that was left. -- function ss11_left_battle_area( triggerer_name, trigger_name ) mission_debug( triggerer_name.." left battle arena" ) -- Update the player location tracking Players_in_arena[triggerer_name] = false -- Cancel any currently running AI threads for the vehicle and have it -- attack the player that ventured outside thread_kill( Generals_ride_pathing_thread_handle ) Generals_ride_pathing_thread_handle = INVALID_THREAD_HANDLE --thread_kill( Generals_ride_stop_cycle_thread_handle ) --Generals_ride_stop_cycle_thread_handle = INVALID_THREAD_HANDLE thread_kill( Generals_ride_attack_dispatcher_thread_handle ) Generals_ride_attack_dispatcher_thread_handle = INVALID_THREAD_HANDLE Generals_ride_attacking_player_outside_of_arena_thread_handle = thread_new( "ss11_generals_ride_chase_attack_player", triggerer_name ) end function ss11_generals_ride_chase_attack_player( player_to_chase ) vehicle_chase( GENERALS_RIDE, player_to_chase, false, true, false ) end -- Deals with the General's ride being destroyed, including killing the -- passengers and making sure that the General can get out and still -- be a viable fighter for the next part of the mission. -- function ss11_generals_ride_destroyed() damage_indicator_off( 0 ) turn_vulnerable( GENERALS_RIDE_AK_GUYS[1] ) turn_vulnerable( GENERALS_RIDE_AK_GUYS[2] ) turn_vulnerable( GENERALS_RIDE_DRIVER_NAME ) turn_vulnerable( THE_GENERAL_NAME ) vehicle_suppress_npc_exit( GENERALS_RIDE, false ) set_seatbelt_flag( GENERALS_RIDE_DRIVER_NAME, false ) set_seatbelt_flag( THE_GENERAL_NAME, false ) set_seatbelt_flag( GENERALS_RIDE_AK_GUYS[1], false ) set_seatbelt_flag( GENERALS_RIDE_AK_GUYS[2], false ) character_kill( GENERALS_RIDE_AK_GUYS[1] ) character_kill( GENERALS_RIDE_AK_GUYS[2] ) character_kill( GENERALS_RIDE_DRIVER_NAME ) character_kill( THE_GENERAL_NAME ) thread_kill( Generals_ride_driver_throw_grenades_thread_handle ) if ( Generals_ride_pathing_thread_handle ~= INVALID_THREAD_HANDLE ) then thread_kill( Generals_ride_pathing_thread_handle ) Generals_ride_pathing_thread_handle = INVALID_THREAD_HANDLE end character_kill( GENERALS_RIDE_DRIVER_NAME ) character_kill( THE_GENERAL_NAME ) mission_end_success( MISSION_NAME, CT_OUTRO ) --[[ while ( character_is_ragdolled( THE_GENERAL_NAME ) ) do thread_yield() end -- Make the General more difficult turn_vulnerable( THE_GENERAL_NAME ) character_prevent_flinching( THE_GENERAL_NAME, true ) character_prevent_explosion_fling( THE_GENERAL_NAME, true ) character_prevent_kneecapping( THE_GENERAL_NAME, true ) on_death( "ss11_general_killed", THE_GENERAL_NAME ) local player_max_hp = get_max_hit_points( LOCAL_PLAYER ) local general_max_hp = player_max_hp * GENERAL_HP_MULTIPLIER inv_item_add( THE_GENERALS_SAW, 1, THE_GENERAL_NAME ) inv_item_equip( THE_GENERALS_SAW, THE_GENERAL_NAME ) set_max_hit_points( THE_GENERAL_NAME, general_max_hp ) set_current_hit_points( THE_GENERAL_NAME, general_max_hp ) damage_indicator_on( 0, THE_GENERAL_NAME, 0, HT_GENERALS_HP, general_max_hp ) ]] end -- This function causes a passenger in the general's ride to throw grenades at a -- random player every set number of seconds. -- function ss11_generals_ride_driver_throw_grenades() -- Only throw grenades every so many seconds while ( true and ( character_is_dead( GENERALS_RIDE_DRIVER_NAME ) == false ) ) do delay( DRIVER_GRENADE_THROW_INTERVAL_SECONDS ) -- By default, attack the local player local target = LOCAL_PLAYER -- but if coop is on, then possibly target the remote player if ( coop_is_active() ) then local which_player = rand_int( 1, 2 ) if ( which_player == 2 ) then target = REMOTE_PLAYER end end force_throw_char( GENERALS_RIDE_DRIVER_NAME, target ) end end function ss11_find_closest_nav_index_in_list_from_char( character_name, navpoints ) -- Find the nearest path start local closest_index = 1 local closest_distance = get_dist_char_to_nav( character_name, navpoints[1] ) for index, name in navpoints do local cur_dist = get_dist_char_to_nav( character_name, name ) if ( cur_dist < closest_distance ) then closest_distance = cur_dist closest_index = index end end return closest_index end -- Continually pathing around the center of the food court -- function ss11_generals_ride_do_circle_pathing() -- Rollover to the next path direction ( this starts at 0, so 1 will be the first path. ) -- The directions are counterclockwise or clockwise Current_path_direction = Current_path_direction + 1 if ( Current_path_direction > sizeof_table( GENERALS_RIDE_PATHS_DIRECTIONS ) ) then Current_path_direction = 1 end -- Find the list of path starts for this direction local current_path_starts = GENERALS_RIDE_PATH_STARTS_DIRECTIONS[Current_path_direction] -- Find the index of the path in this direction to path along local path_index = ss11_find_closest_nav_index_in_list_from_char( THE_GENERAL_NAME, current_path_starts ) -- Find the actual path we're using local current_path = GENERALS_RIDE_PATHS_DIRECTIONS[Current_path_direction][path_index] -- Path along this path local rode_to_a_stop = false while ( true ) do -- Pathfinding with looping support if ( Generals_ride_stopped == false ) then vehicle_pathfind_to( GENERALS_RIDE, current_path, true, false, true ) rode_to_a_stop = false -- If we haven't already stopped, go to a stop -- We'll be waiting until "Generals_ride_stopped" is true again to resume pathing elseif ( rode_to_a_stop == false ) then vehicle_pathfind_to( GENERALS_RIDE, current_path, true, true, true ) rode_to_a_stop = true end thread_yield() end end -- This function's purpose is to have the General's ride attack the -- player ever so often, then resume circling the food court. -- function ss11_generals_ride_attack_dispatcher() -- Continue attacking indefinitely while ( true ) do -- Only attack every so often delay( BETWEEN_GENERAL_ATTACKS_TIME_SECONDS ) -- When it's time to do an attack, check to see if things are set up correctly for it if ( Generals_ride_stopped == false and Generals_ride_attacking_player == false ) then -- Stop circle pathing if ( Generals_ride_pathing_thread_handle ~= INVALID_THREAD_HANDLE ) then thread_kill( Generals_ride_pathing_thread_handle ) Generals_ride_pathing_thread_handle = INVALID_THREAD_HANDLE end Generals_ride_attacking_player = true mission_debug( "attack!" ) ss11_generals_ride_do_attack() Generals_ride_attacking_player = false Generals_ride_pathing_thread_handle = thread_new( "ss11_generals_ride_do_circle_pathing" ) end end end -- This function causes the General's ride to stop circling ever so often. -- function ss11_generals_ride_stop_cycle() while ( true ) do delay( BETWEEN_GENERAL_PAUSES_DELAY_SECONDS ) -- When it's time to pause, check to see if this is a good time if ( Generals_ride_stopped == false and Generals_ride_attacking_player == false ) then -- Generals_ride_stopped = true delay( GENERALS_RIDE_PAUSE_DELAY_SECONDS ) Generals_ride_stopped = false end end end function ss11_generals_ride_do_attack() -- Attack local distance, player_name = get_dist_closest_player_to_object( GENERALS_RIDE ) local attack_the_player_thread_handle = thread_new( "ss11_generals_ride_chase_attack_player", player_name ) delay( ATTACK_THE_PLAYER_TIME_SECONDS ) thread_kill( attack_the_player_thread_handle ) end -- Begins the battle with the General when the player reaches the correct location -- -- triggerer_name: The name of the player that triggered the battle -- trigger_name: The name of the battle-triggering trigger -- function ss11_start_battle( triggerer_name, trigger_name ) -- No more trigger required trigger_enable( trigger_name, false ) marker_remove_trigger( trigger_name, SYNC_ALL ) -- Start the battle ss11_switch_state( MS_GENERAL_BOSS_BATTLE_DRIVING_PART ) end -- Function that makes the bodyguards of the General get out of his wrecked limo. -- function ss11_generals_bodyguards_get_out() delay( BODYGUARD_LIMO_EXIT_DELAY_SECONDS ) for index, name in pairs( BODYGUARD_GROUP_MEMBERS ) do vehicle_exit( name ) end end function ss11_setup_general_for_escape() set_unjackable_flag( GENERALS_LIMO, false ) vehicle_suppress_npc_exit( GENERALS_LIMO, false ) vehicle_exit( THE_GENERAL_NAME ) character_prevent_flinching( THE_GENERAL_NAME, true ) character_prevent_explosion_fling( THE_GENERAL_NAME, true ) character_allow_ragdoll( THE_GENERAL_NAME, false ) character_prevent_kneecapping( THE_GENERAL_NAME, true ) set_ignore_ai_flag( THE_GENERAL_NAME, true ) marker_add_npc( THE_GENERAL_NAME, MINIMAP_ICON_KILL, INGAME_EFFECT_KILL, SYNC_ALL ) end -- Basic pathing function that hides the character after it paths -- function ss11_general_escape() audio_play_for_character( SHAUNDI_GENERAL_IS_ESCAPING_LINE, SHAUNDI_NAME, "voice", false, false ) move_to_safe( THE_GENERAL_NAME, UNDERGROUND_MALL_ENTRANCE, 3, true, false ) ss11_guide_player_to_chase_the_general() end function ss11_guide_player_to_chase_the_general() marker_remove_npc( THE_GENERAL_NAME ) character_hide( THE_GENERAL_NAME ) trigger_enable( ENTER_UG_MALL_TELEPORT_TRIGGER, true ) marker_add_trigger( ENTER_UG_MALL_TELEPORT_TRIGGER, MINIMAP_ICON_LOCATION, INGAME_EFFECT_LOCATION, SYNC_ALL ) on_trigger( "ss11_player_went_underground", ENTER_UG_MALL_TELEPORT_TRIGGER ) trigger_enable( PARKING_GARAGE_UG_MALL_TRIGGER, true ) on_trigger( "ss11_player_went_underground", PARKING_GARAGE_UG_MALL_TRIGGER ) mission_help_table( HT_GO_AFTER_GENERAL, "", "", SYNC_ALL ) objective_text_clear( 0 ) end -- Sets the General to "escaping" state, or deactivates said state. -- function ss11_general_clear_escape_flags() character_prevent_flinching( THE_GENERAL_NAME, false ) character_prevent_explosion_fling( THE_GENERAL_NAME, false ) character_allow_ragdoll( THE_GENERAL_NAME, true ) character_prevent_kneecapping( THE_GENERAL_NAME, false ) set_ignore_ai_flag( THE_GENERAL_NAME, false ) end function ss11_all_players_went_underground() if ( Players_went_underground[LOCAL_PLAYER] == true ) then if ( coop_is_active() ) then if ( Players_went_underground[REMOTE_PLAYER] == true ) then return true end else return true end end return false end -- Callback on player entering the underground trigger -- function ss11_player_went_underground( triggerer_name, trigger_name ) -- Disallow Samedi gang spawning while players are in the mall - it causes issues. spawning_allow_gang_team_ambient_spawns( HUMAN_TEAM_SAMEDI, false ) if ( Players_went_underground[triggerer_name] == false ) then -- Remove the trigger's markers local sync_type = sync_from_player( triggerer_name ) marker_remove_trigger( trigger_name, sync_type ) -- If everyone's gone underground, disable all the go underground triggers Players_went_underground[triggerer_name] = true if ( ss11_all_players_went_underground() ) then trigger_enable( ENTER_UG_MALL_TELEPORT_TRIGGER, false ) trigger_enable( PARKING_GARAGE_UG_MALL_TRIGGER, false ) end thread_yield() if ( Went_underground_state_switched == false ) then Went_underground_state_switched = true ss11_switch_state( MS_THROUGH_THE_MALL_FIREFIGHT ) end -- Only teleport the player if he used the elevator ( rather than walking in through the -- parking garage ) if ( trigger_name == ENTER_UG_MALL_TELEPORT_TRIGGER ) then teleport( triggerer_name, DEFAULT_LEAVE_UG_MALL_TRIGGER ) end -- If this was the local player, then Shaundi was teleported. Have her go idle so she -- doesn't attack people she can't possibly reach. if ( triggerer_name == LOCAL_PLAYER ) then npc_go_idle( SHAUNDI_NAME ) end end end -- Hides all the tables and chairs in the food court so that they -- don't get in the way of the battle. -- function ss11_hide_food_court_tables_and_chairs() -- Hide all the chairs and tables for index, name in pairs( CHAIRS ) do mesh_mover_hide( name ) end for index, name in pairs( TABLES ) do mesh_mover_hide( name ) end end -- The bus driver needs a little while to recover from the bus crash, -- so he doesn't get out immediately. -- function ss11_bus_driver_get_out() delay( BUS_DRIVER_EXIT_DELAY_SECONDS ) vehicle_exit( BUS_DRIVER_NAME ) end -- force ambient pedestrians to flee the mall -- function ss11_ambient_npcs_flee() while(1) do thread_yield() city_chunk_set_all_civilians_fleeing(MALL_CHUNK_NAME) delay(1) end end -- Have the two escorts of the General's limo pathfind and try to defend the General against the ambush function ss11_defender_cars_come_help() thread_new( "ss11_defender_car_one_come_help" ) thread_new( "ss11_defender_car_two_come_help" ) end function ss11_defender_car_one_come_help() vehicle_pathfind_to( FIRST_DEFENDER_GROUP_VEHICLE, FIRST_DEFENDER_GROUP_DROPOFF, true, true ) end function ss11_defender_car_two_come_help() vehicle_pathfind_to( SECOND_DEFENDER_GROUP_VEHICLE, SECOND_DEFENDER_GROUP_DROPOFF, true, true ) end -- Starts the ambush -- function ss11_reached_ambush_location() hud_timer_stop( 0 ) -- We've reached the ambush location, so don't tell the player he needs to hurry up if ( Hurry_up_prompt_thread_handle ~= INVALID_THREAD_HANDLE ) then thread_kill( Hurry_up_prompt_thread_handle ) Hurry_up_prompt_thread_handle = INVALID_THREAD_HANDLE end --audio_play_for_character( SHAUNDI_GET_READY_FOR_LIMO_LINE, SHAUNDI_NAME, "voice", false, true ) --delay( GENERAL_MALL_CHECKPOINT_DELAY_SECONDS ) marker_remove_trigger( AMBUSH_LOCATION_TRIGGER, SYNC_ALL ) trigger_enable( AMBUSH_LOCATION_TRIGGER, false ) waypoint_remove( SYNC_ALL ) ss11_switch_state( MS_CUTSCENE ) end function ss11_success() -- Called when the mission has ended with success -- Unlock story arc complete rewards spawning_pedestrians( false ) spawning_pedestrians( true ) end