Files
2025-09-29 00:52:08 +02:00

5086 lines
164 KiB
Python
Executable File

//Compile out Title Update changes to header functions.
//Must be before includes.
//CONST_INT USE_TU_CHANGES 0 // Removed by Kenneth R.
USING "rage_builtins.sch"
USING "globals.sch"
USING "cutscene_public.sch"
USING "commands_entity.sch"
USING "commands_script.sch"
USING "script_player.sch"
USING "randomChar_public.sch"
USING "script_ped.sch"
USING "dialogue_public.sch"
USING "comms_control_public.sch"
USING "event_public.sch"
USING "commands_shapetest.sch"
USING "RC_Helper_Functions.sch"
USING "CompletionPercentage_public.sch"
USING "initial_scenes_Nigel.sch"
USING "RC_Threat_public.sch"
USING "chase_hint_cam.sch"
USING "commands_recording.sch"
USING "mission_titles_private.sch"
#IF IS_DEBUG_BUILD
USING "select_mission_stage.sch"
#ENDIF
// *****************************************************************************************
// *****************************************************************************************
// *****************************************************************************************
//
// MISSION NAME : Nigel3.sc
// AUTHOR : Tom Waters
// DESCRIPTION : Trevor deals with the kidnapped celeb for Nigel & Mrs Thornhill
//
// *****************************************************************************************
// *****************************************************************************************
// *****************************************************************************************
// The Random Character
// The Random Character - sRCLauncherDataLocal.pedID[0]
g_structRCScriptArgs sRCLauncherDataLocal
//*************************************************************************************************************************************************
// :ENUMS:
//*************************************************************************************************************************************************
// Mission stage progession
ENUM MISSION_STAGE
MS_INIT,
MS_LEADIN,
MS_INTRO_CUTSCENE,
MS_GET_IN_CAR,
MS_DRIVING,
MS_PLAYER_EXITS_CAR,
MS_RAM_TRAIN,
MS_MISSED_RAM,
MS_RELEASE_CUTSCENE,
MS_OUTRO_CUTSCENE,
MS_ENDING_PHONE_CALL,
MS_LOSE_COPS,
MS_WRECK_PASS,
MS_FAIL_FADE,
MS_DEBUG_CRASHES_FOREVER
ENDENUM
// Progression within a stage
ENUM STAGE_PROGRESS
SP_SETUP,
SP_RUNNING,
SP_CLEANUP
ENDENUM
// Nigel and Mrs Thornhill state tracking
ENUM NAGGING_CONVERSATION
NAG_INITIAL,
NAG_WAITING,
NAG_CONVERSATION_ONGOING,
NAG_CONVERSATION_ONGOING_SPECIAL,
NAG_FINISHED,
NAG_SPENT
ENDENUM
// Nigel and Mrs Thornhill cleanup state
ENUM NMT_CLEANUP_STATE
NMTCS_PLAYER_LEFT,
NMTCS_PLAYER_SCARED_STALKERS,
NMTCS_NIGEL_INJURED,
NMTCS_NIGEL_DEAD,
NMTCS_MRS_THORNHILL_INJURED,
NMTCS_MRS_THORNHILL_DEAD
ENDENUM
// Progress through conversation/objective etc.
ENUM CELEB_CONV_CYCLE
CCC_WAIT_FOR_DISTANCE,
CCC_SETUP_CONVERSATION,
CCC_PLAY_THUMPS,
CCC_START_CONVERSATION,
CCC_WAIT_TO_FINISH,
CCC_DELAY_NEXT_CONVERSATION,
CCC_CONVERSATIONS_DONE
ENDENUM
// Track where we are with damage conversations
ENUM DAMAGE_CONV_CYCLE
DCC_WAIT_FOR_DAMAGE,
DCC_START_COMMENT,
DCC_WAIT_FOR_COMMENT,
DCC_REPEAT_DELAY
ENDENUM
// What state are we in when the player exits car?
ENUM CAR_EXIT_CONDITIONS
CEC_TOO_SOON,
CEC_NEAR_COPS,
CEC_OBSTRUCTED_BOOT,
CEC_UNEVEN_GROUND,
CEC_CAR_MOVING,
CEC_OK_TO_RELEASE,
CEC_TOO_LATE
ENDENUM
// Did player release celeb after exiting car?
ENUM RELEASE_CHOICE
RCH_GOT_BACK_IN,
RCH_RELEASED,
RCH_RAN_OFF
ENDENUM
//*************************************************************************************************************************************************
// :STRUCTS:
//*************************************************************************************************************************************************
// Stores info needed for spawning a vehicle
STRUCT MISSION_VEHICLE
VEHICLE_INDEX vehicle
MODEL_NAMES model
VECTOR location
FLOAT heading
PED_INDEX driver
MODEL_NAMES driverModel
ENDSTRUCT
// Stores a non-axially aligned volume
STRUCT NAA_VOLUME
VECTOR vEnds[2]
FLOAT fWidth
ENDSTRUCT
// Holds converstaion info
STRUCT CONVERSATION_INFO
STRING txtBlock
STRING rootBlock
FLOAT triggerDistance
VECTOR debugCoords
FLOAT debugHeading
INT completedReward
ENDSTRUCT
//*************************************************************************************************************************************************
// :CONSTANTS:
//*************************************************************************************************************************************************
// Skip stuff
CONST_INT SKIP_FORWARD 0
CONST_INT SKIP_BACKWARD 1
// How many distinct conversations during journey?
CONST_INT NUM_BEGGING_CONVS 4
// How many nagging conversations when player isn't starting?
CONST_INT NUM_NAGGING_CONVS 3
// How many nagging lines after those conversations have finished?
CONST_INT NUM_NAGGING_LINES 6
// Nagging conversation timing delays
CONST_INT NAGGING_CONVERSATION_GAP 20000
// Enforced minimum time between begging conversations
CONST_INT CONVERSATION_GAP 10000
// Number of cameras used in outro cutscenes
CONST_INT NUM_OUTRO_CAMERAS 4
// Used in detecting player's proximity to car boot if they want to release the celeb
CONST_FLOAT TRUNK_DETECT_RADIUS 0.8//1.6
// Train speed variance limits and timespan
CONST_FLOAT TRAIN_INITIAL_SPEED 25.0
CONST_FLOAT TRAIN_TARGET_SPEED 75.0
CONST_INT TRAIN_SPEEDUP_TIME 11000 //15000
// Car abandon
CONST_FLOAT CAR_ABANDON_DISTANCE 200.0
CONST_FLOAT CAR_ABANDON_WARN_DIST 50.0
// Conversation ped indexes
CONST_INT CONVPED_TREV 2 // Trevor/player
CONST_INT CONVPED_MRST 3 // Mrs Thornhill
CONST_INT CONVPED_NIGE 4 // Nigel
CONST_INT CONVPED_ALDN 5 // Al DiNapoli
// Vehicle restriction
CONST_INT RESTRICTION_NIGELCAR 0
// Scenario blocking areas
CONST_INT NUM_BLOCKING_AREAS 3
//*************************************************************************************************************************************************
// :VARIABLES:
//*************************************************************************************************************************************************
// Progression stuff
MISSION_STAGE mStage = MS_INIT // track what mission stage we are at
STAGE_PROGRESS sProgress = SP_SETUP // used to track what bit of the current mission stage we're at
RELEASE_CHOICE releaseChoice
CELEB_CONV_CYCLE cccProgress = CCC_SETUP_CONVERSATION
INT iDestObjDisp = 0 // Don't show the drive to destination objective more than twice
BOOL bBackInObjDisplayed = FALSE // Has the get-back-in-vehicle objective been shown
INT iBackInDelayTimer
CONST_INT BACK_IN_DELAY 5000
// GP counter
INT iCount
// N & Mrs T's car, used by player
MISSION_VEHICLE mvPlayerCar
// Mission car stuck check stuff
BOOL bVehicleOnRoofLastFrame
INT iStuckTimer
// The celeb, Al Di Napoli
PED_INDEX pedDiNapoli
// Nigel and Mrs Thornhill
PED_INDEX pedNigel
PED_INDEX pedMrsThornhill
OBJECT_INDEX oiHandbag
STRING sNMTAnimDict = "rcmnigel3_idles"
STRING sNigelAnim = "base_nig"
BOOL bNotSetUpNigelAnims
STRING sMrsTAnim = "base_mst"
BOOL bNotSetUpMrsTAnims
VECTOR vecBagOffset = << 0.198, 0.074, 0.358 >>
VECTOR vecBagAngles = << 12.24, -173.52, 130.32 >>
BOOL bPlayerSpookedStalkers = FALSE // Whether player has done any mucking about to make them run off, e.g. shooting near them
BOOL bRequestedStalkerAnims // Did we request their idle anims
REL_GROUP_HASH relGroupFriendly // Friendly group to add stalkers
BOOL bRelGroupExists = FALSE
// Lockup door
OBJECT_INDEX oiDoor
// Used to save start location
VECTOR vecStartLocation = << -44.7500, -1288.6747, 28.2104 >>
// Scenario blocking areas
SCENARIO_BLOCKING_INDEX scenarioBlockers[NUM_BLOCKING_AREAS]
VECTOR vecScenario[NUM_BLOCKING_AREAS][2]
// Release handling and car exit bits
VECTOR vecBootOffset = << 0.0, -2.2, 0.5 >> // << 0.0, -3.0, 3.0 >>
FLOAT fDodgyRollAngle = 8.0 // Car tilted past this is considered to be on a dodgy slope for cutscene purposes
FLOAT fDodgyPitchAngle = 9.0 // Car tilted past this is considered to be on a dodgy slope for cutscene purposes
SHAPETEST_INDEX stiBootArea[2]
VECTOR vecBootBlockOffset[4] //= << 0.0, -3.45, 0.8 >> //<< 0.0, -3.35, 0.8 >>
VECTOR vecBootBlockDimensions[4] //= << 2.3, 2.0, 2.0 >>
INT iShapetestResult[2]
VECTOR vecShapetestResult[2][2]
ENTITY_INDEX hitEntity[2]
CAR_EXIT_CONDITIONS cecExitConditions
// Release cutscene
BOOL bReleaseConvStarted = FALSE
BOOL bCutsceneSkipped
BOOL bCheckedFinalFloater
STRING sReleaseAnimDict = "rcmnigel3_trunk"
VECTOR vecScenePosition
VECTOR vecSceneRotation
INT iSceneID
CAMERA_INDEX camReleaseCamera
MODEL_NAMES mCash = P_BankNote_S
OBJECT_INDEX oCash
// The destination
BLIP_INDEX biGotoBlip
VECTOR vecDestination = << 2611.0215, 1655.5, 26.6356 >> // << 2611.0215, 1642.1946, 26.8556 >>
// Train track tracker box
NAA_VOLUME naaTrainTrackBox
NAA_VOLUME naaTrackWidthBox
// Track if we've turned trains off yet
BOOL bTrainsAreActive = TRUE
// Begging conversations
CONVERSATION_INFO ciConvos[NUM_BEGGING_CONVS]
BOOL bBeggingConvActive = FALSE
INT iConvoCount
structPedsForConversation mConversationStruct
BOOL bNotThumping // Used to track whether we are playing boot thumps
INT iThumpID // Grab the thump sound ID
INT iConversationTimer // Timer for gaps between conversations
// Boot bouncing with Al's thumps - variables for the triggered handler system
STAGE_PROGRESS spBouncyBoot // Progress tracker for triggered boot thumps
INT iBounceLimit // Max bounces in this event
INT iBounceCount // Bounces so far in this event
INT iBounceTimer // Time since last bounce in this event
INT iBounceDelay // Time to wait before next bounce iis triggered
INT iBounceForce // Z-component of bounce force vector
FLOAT fBounceOffSet // left-right offset across the boot area
// Proc that triggers the thumps preiodically if player is on foot
STAGE_PROGRESS spAlThumps // Progress tracker for time-delayed boot thumps
INT iTriggerThumpDelay // Delay timer for triggering the next thump event
// Have we shown the enter the car objective
BOOL bEnterCarObjectiveDone = FALSE
// Tracks firing of running off warning if player exits car.
BOOL bWarnedPlayerNotToRunOff
// Tracks whether release is valid
BOOL bReleaseIsValid = FALSE // Are we allowed to release?
BOOL bReleaseObjectiveShown = FALSE // Has release hint been shown?
// Tracks whether we've shown how-to-release-Al help
BOOL bShowedTrunkHelp = FALSE
// Used for cleaning up celeb model correctly on mission fail
BOOL bCelebWasReleased = FALSE
// Damage conversations
DAMAGE_CONV_CYCLE damConvCycle = DCC_WAIT_FOR_DAMAGE
BOOL bDamageConvActive = FALSE
INT iTimerDamageCommentDelay
// Shooting damage conversations
DAMAGE_CONV_CYCLE shootConvCycle = DCC_WAIT_FOR_DAMAGE
INT iShootCommentDelay // Timer for having a gap between shooting comments
// Outro cutscenes stuff
BOOL bCutsceneLoop
BOOL bOutroWasSkipped
BOOL bSwitchedCameras
BOOL bExplodedVehicle
BOOL bFPFlashNeeded
VECTOR vecFireLocator
INT iCutsceneTimer
BOOL bUseShortCam
CAMERA_INDEX csOutroCameras[4]
INT iCameraDuration[2]
FLOAT fRealTime
MISSION_VEHICLE mvTrain
// Stuff to deal with Nigel and Mrs Thornhill hanging around
NAGGING_CONVERSATION nagConv = NAG_INITIAL
STAGE_PROGRESS spRandomCharHandler = SP_SETUP
NMT_CLEANUP_STATE NMTCleanupState = NMTCS_PLAYER_LEFT
BOOL bRandomCharsStillLoaded = TRUE
CONVERSATION_INFO ciNaggingConv[NUM_NAGGING_CONVS]
INT iNagCount = 0
INT iStalkerCommentsTimer
STRING sNagLineLabels[NUM_NAGGING_LINES]
INT iNagLineCount = 0
RC_CONV_RESTORE_STATE stateRestoreConversation
TEXT_LABEL_23 tSavedConversationRoot
TEXT_LABEL_23 tSavedConversationLabel
// Track progress/objectives in end section
BOOL bPlayerNotInCar // The player has exited the car during the initial part of the ram stage
BOOL bRamStageStarted = FALSE
BOOL bMurderCommentStarted // Whether Trevor has commented that he's definitely killing Al
STAGE_PROGRESS spAlPanic // Al boot panic conversation tracking
INT iAlPanicTimer // Spaces out Al panic utterances
BOOL bTrainIsSpawned
BOOL bRequestedTrainModels
BOOL bTrainRamSpeedUpStarted = FALSE
INT iRamSpeedTimer
BOOL bTrainObjectiveDone = FALSE
BOOL bBailOutObjShown = FALSE // Bail out objective
FLOAT fBailDistanceTrack = 300 // Will be constantly updated for as long as the player is in the car
INT iSkinOfTeethTimer // Track time between bail and train impact
INT iSkinOfTeethLimit = 500 // Time limit for bailout in milliseconds
BOOL bSkinOfTeethTimerStarted // Prevents the timer being started multiple times
BOOL bSkinOfTeethStatHappened = FALSE // Used by Video Editor
// Save the status of a bunch of stuff from last frame because ENTITY_TOUCHING_ENTITY sometimes doesn't report contact the same frame
FLOAT fTrainRamSavedSpeed[2]
FLOAT fCarRamSavedSpeed[2]
BOOL bTrackCarOnRails[2]
BOOL bTrackPlayerTouchingCar[2]
BOOL bGlancedTrain // Used in picking a miss train comment
BOOL bPlayerMissedInCar // Used in picking a miss train comment
// Train section sound effect stuff
INT iCrashID
//INT iScrapeID
INT iTrainComing
//BOOL bScrapeStarted
//BOOL bScrapeStopped
// Train cinematic cam
CHASE_HINT_CAM_STRUCT localChaseHintCamStruct
BOOL bShowedTrainCamHelp // Has the help message for the train cam been shown
CAMERA_INDEX camTrainCinCam // Follow cam for viewing player vehicle from train POV
NAA_VOLUME naaTrainCamBox[4] // Camera cannot be active unless car is in general rails area
// Has Nigel been added back to conversation struct
BOOL bAddedNigelForPhoneConv
// Count conversation lines to see if the phone call was interrupted before it began
INT iCallLine
// Fail reason
STRING sFailReason
// -----------------------
//
// CHECKPOINTS/SKIP
//
// -----------------------
CONST_INT CP_TRAIN 1
CONST_INT CP_OUTRO_CALL 2 // Shitskip only
#IF IS_DEBUG_BUILD
// Skippable mission points
ENUM MISSION_SKIP_STAGE
MSS_INTRO,
MSS_RESTART,
MSS_TRAIN,
MSS_OUTRO_CALL
ENDENUM
MISSION_SKIP_STAGE eTargetStage
CONST_INT MAX_SKIP_MENU_LENGTH 4
MissionStageMenuTextStruct mSkipMenu[MAX_SKIP_MENU_LENGTH]
// Debug widgets
BOOl bDebugToggleInvuln = FALSE
BOOL bDebugDrawTrainCamZones = FALSE
BOOL bDebugDrawTrainCamZonesLastFrame = FALSE // DO NOT ADD WIDGET FOR THIS, it's toggled by previous one
BOOL bDebugShowBailDistanceOnCustscene = FALSE
BOOL bDebugEventTTY = FALSE
BOOL bDebugTrainTTY = FALSE
BOOL bDebugReattach
WIDGET_GROUP_ID widgetGroup
#ENDIF
//*************************************************************************************************************************************************
// :UTILITY PROCS AND FUNCS:
//*************************************************************************************************************************************************
#IF IS_DEBUG_BUILD
// Draw debug box for non-axis aligned locate
PROC DRAW_DEBUG_LOCATE_SPECIAL(VECTOR vec1, VECTOR vec2, FLOAT width)
VECTOR vBottom[2]
VECTOR vTop[2]
vBottom[0] = vec1
vBottom[1] = vec2
vTop[0] = vec1
vTop[1] = vec2
IF vec1.z > vec2.z
vBottom[0].z = vec2.z
vBottom[1].z = vec2.z
vTop[0].z = vec1.z
vTop[1].z = vec1.z
ELSE
vBottom[0].z = vec1.z
vBottom[1].z = vec1.z
vTop[0].z = vec2.z
vTop[1].z = vec2.z
ENDIF
VECTOR fwd = NORMALISE_VECTOR(vBottom[1] - vBottom[0]) // normalize to get distance
VECTOR side = <<-fwd.y, fwd.x, fwd.z>>
VECTOR w = side * (width / 2.0)
// Bottom points
VECTOR c1 = vBottom[0] - w // base left
VECTOR c2 = vBottom[0] + w // base right
VECTOR c3 = vBottom[1] + w // top rt
VECTOR c4 = vBottom[1] - w // top lt
// Top points
VECTOR d1 = vTop[0] - w // base left
VECTOR d2 = vTop[0] + w // base right
VECTOR d3 = vTop[1] + w // top rt
VECTOR d4 = vTop[1] - w // top lt
// Draw bottom lines
DRAW_DEBUG_LINE(c1, c2, 128, 0, 128)
DRAW_DEBUG_LINE(c2, c3, 128, 0, 128)
DRAW_DEBUG_LINE(c3, c4, 128, 0, 128)
DRAW_DEBUG_LINE(c4, c1, 128, 0, 128)
// Draw top lines
DRAW_DEBUG_LINE(d1, d2, 128, 0, 128)
DRAW_DEBUG_LINE(d2, d3, 128, 0, 128)
DRAW_DEBUG_LINE(d3, d4, 128, 0, 128)
DRAW_DEBUG_LINE(d4, d1, 128, 0, 128)
// Draw uprights
DRAW_DEBUG_LINE(c1, d1, 128, 0, 128)
DRAW_DEBUG_LINE(c2, d2, 128, 0, 128)
DRAW_DEBUG_LINE(c3, d3, 128, 0, 128)
DRAW_DEBUG_LINE(c4, d4, 128, 0, 128)
ENDPROC
#ENDIF
/// PURPOSE:
/// Toggle all the car mod shops in one go
/// PARAMS:
/// bToggleFlag - Pass TRUE to toggle off, FALSE to toggle back on
PROC TOGGLE_CAR_MOD_SHOPS_UNAVAILABLE(BOOL bToggleFlag)
SET_SHOP_IS_TEMPORARILY_UNAVAILABLE(CARMOD_SHOP_01_AP, bToggleFlag)
SET_SHOP_IS_TEMPORARILY_UNAVAILABLE(CARMOD_SHOP_05_ID2, bToggleFlag)
SET_SHOP_IS_TEMPORARILY_UNAVAILABLE(CARMOD_SHOP_06_BT1, bToggleFlag)
SET_SHOP_IS_TEMPORARILY_UNAVAILABLE(CARMOD_SHOP_07_CS1, bToggleFlag)
SET_SHOP_IS_TEMPORARILY_UNAVAILABLE(CARMOD_SHOP_08_CS6, bToggleFlag)
SET_SHOP_IS_TEMPORARILY_UNAVAILABLE(CARMOD_SHOP_SUPERMOD, bToggleFlag)
ENDPROC
/// PURPOSE:
/// Create (or delete) the scenario blocking areas
/// PARAMS:
/// bCreate - TRUE to create, FALSE to delete
PROC CREATE_SCENARIO_BLOCKING_AREAS(BOOL bCreate)
INT iSCount
REPEAT NUM_BLOCKING_AREAS iSCount
IF bCreate
scenarioBlockers[iSCount] = ADD_SCENARIO_BLOCKING_AREA(vecScenario[iSCount][0], vecScenario[iSCount][1])
ELSE
REMOVE_SCENARIO_BLOCKING_AREA(scenarioBlockers[iSCount])
ENDIF
ENDREPEAT
ENDPROC
/// PURPOSE:
/// Fills in the structs used to store mission vehicles
/// PARAMS:
/// missionVehicle - Struct that will get filled in
/// modelName - model that the vehicle should use
/// startPos - initial spawn point
/// startHead - initial spawn heading
PROC SETUP_MISSION_VEHICLE(MISSION_VEHICLE &missionVehicle, MODEL_NAMES modelName, VECTOR startPos, FLOAT startHead)
missionVehicle.model = modelName
missionVehicle.location = startPos
missionVehicle.heading = startHead
ENDPROC
/// PURPOSE:
/// Fills in the conversation info structs for the conversations where Al begs Trevor for his life
PROC SETUP_BEGGING_CONVERSATION_INFO()
ciConvos[0].txtBlock = "NIGE3AU"
ciConvos[0].rootBlock = "NIGEL3_C1"
ciConvos[0].triggerDistance = 200
ciConvos[0].completedReward = 3000
ciConvos[1].txtBlock = "NIGE3AU"
ciConvos[1].rootBlock = "NIGEL3_C2"
ciConvos[1].triggerDistance = 1000
ciConvos[1].completedReward = 5000
ciConvos[2].txtBlock = "NIGE3AU"
ciConvos[2].rootBlock = "NIGEL3_C3"
ciConvos[2].triggerDistance = 1800
ciConvos[2].completedReward = 10000
ciConvos[3].txtBlock = "NIGE3AU"
ciConvos[3].rootBlock = "NIGEL3_C4"
ciConvos[3].triggerDistance = 2250
ciConvos[3].completedReward = 10000
ciConvos[0].debugCoords = << 165.8198, -1241.9022, 37.1128 >>
ciConvos[0].debugHeading = 287.4437
ciConvos[1].debugCoords = << 997.1073, -1193.7147, 53.6550 >>
ciConvos[1].debugHeading = 273.9958
ciConvos[2].debugCoords = << 1880.1357, -780.5549, 80.6259 >>
ciConvos[2].debugHeading = 304.6608
ciConvos[3].debugCoords = << 2483.9358, 946.1223, 85.4867 >>
ciConvos[3].debugHeading = 38.84
ENDPROC
/// PURPOSE:
/// Checks whether the vehicle is stuck
/// RETURNS:
/// TRUE if the vehicle is stuck
FUNC BOOL VEHICLE_IS_STUCK()
BOOL bVehicleStuck
bVehicleStuck = FALSE
// Keep track of vehicle on roof
IF IS_VEHICLE_STUCK_ON_ROOF(mvPlayerCar.vehicle)
IF bVehicleOnRoofLastFrame
IF GET_GAME_TIMER() - iStuckTimer > ROOF_TIME
bVehicleStuck = TRUE
ENDIF
ELSE
CPRINTLN(DEBUG_MISSION, "Nigel3::: Vehicle is now stuck on roof!")
bVehicleOnRoofLastFrame = TRUE
iStuckTimer = GET_GAME_TIMER()
ENDIF
ELSE
IF bVehicleOnRoofLastFrame
CPRINTLN(DEBUG_MISSION, "Nigel3::: Vehicle is no longer stuck on roof.")
ENDIF
bVehicleOnRoofLastFrame = FALSE
ENDIF
RETURN bVehicleStuck
ENDFUNC
/// PURPOSE:
/// Fail mission if player dead, car is trashed/stuck, celeb stalkers are scared/injured/dead
/// RETURNS:
/// TRUE if things have gone pear shaped
FUNC BOOL MISSION_FAIL_CHECKS()
// Basic check - dead player
IF NOT IS_ENTITY_ALIVE(PLAYER_PED_ID())
// Player dead fail
CPRINTLN(DEBUG_MISSION, "MISSION_FAIL_CHECKS: Trevor died")
sFailReason = "DEFAULT"
mStage = MS_FAIL_FADE
sProgress = SP_SETUP
RETURN TRUE
ENDIF
// Player vehicle checks if relevant
IF mStage != MS_INIT
AND mStage != MS_LEADIN
AND mStage != MS_INTRO_CUTSCENE
AND mStage != MS_OUTRO_CUTSCENE
AND mStage != MS_MISSED_RAM
AND mStage != MS_ENDING_PHONE_CALL
AND mStage != MS_DEBUG_CRASHES_FOREVER
// Basic dead car check
IF NOT IS_ENTITY_ALIVE(mvPlayerCar.vehicle)
// Car wreck pass
CPRINTLN(DEBUG_MISSION, "MISSION_FAIL_CHECKS: The car is dead but this is no longer a fail")
mStage = MS_WRECK_PASS
sProgress = SP_SETUP
RETURN FALSE
// Removed undriveable check - can still potentially release/kill Al and pass
// Apparently you can kill Al by shooting the trunk even when he's not visible? B*2017887
ELIF DOES_ENTITY_EXIST(pedDiNapoli)
AND NOT IS_ENTITY_ALIVE(pedDiNapoli)
// Shot Al pass
CPRINTLN(DEBUG_MISSION, "MISSION_FAIL_CHECKS: The car is dead but this is no longer a fail")
mStage = MS_WRECK_PASS
sProgress = SP_SETUP
RETURN FALSE
// Car not dead - do the stuck check
ELIF VEHICLE_IS_STUCK()
// Stuck vehicle fail
CPRINTLN(DEBUG_MISSION, "MISSION_FAIL_CHECKS: The car is stuck")
sFailReason = "N3STUCK"
mStage = MS_FAIL_FADE
sProgress = SP_SETUP
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Find out if there are cops near the player's position
/// RETURNS:
/// Returns TRUE if player is near a cop or wanted
FUNC BOOL IS_PLAYER_NEAR_COP()
IF IS_COP_PED_IN_AREA_3D(GET_ENTITY_COORDS(PLAYER_PED_ID()) - << 40.0, 40.0, 40.0 >>, GET_ENTITY_COORDS(PLAYER_PED_ID()) + << 40.0, 40.0, 40.0 >>)
OR GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) > 0
RETURN TRUE
ENDIF
// If we get here, no cops found
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Start a shape test on an offset from the vehicle boot
PROC START_BOOT_SHAPE_TEST()
IF IS_ENTITY_ALIVE(mvPlayerCar.vehicle)
stiBootArea[0] = START_SHAPE_TEST_BOX(GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvPlayerCar.vehicle, vecBootBlockOffset[0]), vecBootBlockDimensions[0], GET_ENTITY_ROTATION(mvPlayerCar.vehicle), DEFAULT, SCRIPT_INCLUDE_MOVER|SCRIPT_INCLUDE_OBJECT|SCRIPT_INCLUDE_VEHICLE)
stiBootArea[1] = START_SHAPE_TEST_BOX(GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvPlayerCar.vehicle, vecBootBlockOffset[1]), vecBootBlockDimensions[1], GET_ENTITY_ROTATION(mvPlayerCar.vehicle), DEFAULT, SCRIPT_INCLUDE_MOVER|SCRIPT_INCLUDE_OBJECT|SCRIPT_INCLUDE_VEHICLE)
ENDIF
ENDPROC
/// PURPOSE:
/// Checks whether the car is parked on a slope
/// RETURNS:
/// TRUE if tilted past fDodgyPitchAngle/fDodgyRollAngle degrees pitch/roll
FUNC BOOL CAR_ON_DODGY_SLOPE()
IF IS_ENTITY_ALIVE(mvPlayerCar.vehicle)
FLOAT fCarRoll = WRAP(GET_ENTITY_ROLL(mvPlayerCar.vehicle), 0, 360)
FLOAT fCarPitch = WRAP(GET_ENTITY_PITCH(mvPlayerCar.vehicle), 0, 360)
CPRINTLN(DEBUG_MISSION, "Clamped 0-360 car roll is ", fCarRoll, ", pitch is ", fCarPitch)
IF (fCarRoll > fDodgyRollAngle AND fCarRoll < (360-fDodgyRollAngle))
OR (fCarPitch > fDodgyPitchAngle AND fCarPitch < (360-fDodgyPitchAngle))
CPRINTLN(DEBUG_MISSION, "Slope rating: DODGY.")
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Create or recreate the player car
PROC CREATE_MISSION_VEHICLE(VECTOR mvLocation, FLOAT mvHeading, BOOL bIsItLocked = FALSE)
IF HAS_MODEL_LOADED(mvPlayerCar.model)
CREATE_NIGEL_VEHICLE(mvPlayerCar.vehicle, mvLocation, mvHeading, bIsItLocked, FALSE)
SET_VEHICLE_ON_GROUND_PROPERLY(mvPlayerCar.vehicle)
SET_VEHICLE_MODEL_IS_SUPPRESSED(mvPlayerCar.model, TRUE)
SET_VEHICLE_CAN_LEAK_OIL(mvPlayerCar.vehicle, FALSE)
SET_VEHICLE_CAN_LEAK_PETROL(mvPlayerCar.vehicle, FALSE)
// SET_VEHICLE_CAN_DEFORM_WHEELS(mvPlayerCar.vehicle, FALSE)
SET_VEHICLE_HAS_STRONG_AXLES(mvPlayerCar.vehicle, TRUE)
SET_CAN_AUTO_VAULT_ON_ENTITY(mvPlayerCar.vehicle, FALSE)
SET_VEHICLE_AS_RESTRICTED(mvPlayerCar.vehicle, RESTRICTION_NIGELCAR)
ENDIF
ENDPROC
/// PURPOSE:
/// Create the celeb in the boot
PROC SPAWN_CELEB_IN_BOOT()
CPRINTLN(DEBUG_MISSION, "Nigel3::: SPAWN_CELEB_IN_BOOT trying to make Al... ")
IF HAS_MODEL_LOADED(U_M_M_ALDINAPOLI)
IF IS_ENTITY_ALIVE(mvPlayerCar.vehicle)
// Create ped
pedDiNapoli = CREATE_PED(PEDTYPE_MISSION, U_M_M_ALDINAPOLI, << -59.7094, -1330.1289, 32.1963 >>)
SET_PED_NAME_DEBUG(pedDiNapoli, "NIGEL3_CELEB")
SET_PED_COMPONENT_VARIATION(pedDiNapoli, PED_COMP_LEG, 1, 0)
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(pedDiNapoli, TRUE)
// Get car boot positions
VECTOR vCarBoot
vCarBoot = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvPlayerCar.vehicle, <<-0.2000, -1.8000, -1.0000>>)
VECTOR vCarBootAngles
FLOAT fCarBootHeading
fCarBootHeading = -90 + GET_ENTITY_HEADING(mvPlayerCar.vehicle)
vCarBootAngles = << 0.0, 0.0, fCarBootHeading>>
// Put ped in boot
SET_ENTITY_COORDS_NO_OFFSET(pedDiNapoli, vCarBoot)
SET_ENTITY_ROTATION(pedDiNapoli, vCarBootAngles)
ATTACH_ENTITY_TO_ENTITY(pedDiNapoli, mvPlayerCar.vehicle, 0, << 0.2, -1.8000, 0.0 >>, << 0, 0, 90 >>, FALSE)
SET_ENTITY_VISIBLE(pedDiNapoli, FALSE)
ADD_PED_FOR_DIALOGUE(mConversationStruct, CONVPED_ALDN, pedDiNapoli, "DINAPOLI")
// Al's thump action in idle state
spBouncyBoot = SP_CLEANUP
#IF IS_DEBUG_BUILD
ELSE
CPRINTLN(DEBUG_MISSION, "Nigel3::: SPAWN_CELEB_IN_BOOT - there is no car!") #ENDIF
ENDIF
ENDIF
ENDPROC
// Play sounds for Al in the car Boot
PROC PLAY_BOOT_THUMP()
IF IS_ENTITY_ALIVE(pedDiNapoli)
PLAY_SOUND_FROM_ENTITY(iThumpID, "TRUNK_THUMPS", pedDiNapoli)
CPRINTLN(DEBUG_MISSION, "Nigel3: Playing sound from pedDiNapoli.")
spBouncyBoot = SP_SETUP
ELSE
CPRINTLN(DEBUG_MISSION, "Nigel3: mvPlayerCar.vehicle apparently not OK for playing a sound!")
ENDIF
ENDPROC
/// PURPOSE:
/// Set the random bits of the bouncy boot force
PROC SET_FORCE_VARIABLES()
iBounceForce = GET_RANDOM_INT_IN_RANGE(-220,-120)
fBounceOffSet = 0.5*TO_FLOAT(GET_RANDOM_INT_IN_RANGE(-2,3))
ENDPROC
/// PURPOSE:
/// When set to active by the PLAY_BOOT_THUMP proc this will make the car wobble about as Al struggles for a few seconds
PROC BOUNCY_BOOT()
IF IS_ENTITY_ALIVE(mvPlayerCar.vehicle)
SWITCH spBouncyBoot
CASE SP_SETUP
// Do an initial bounce
IF GET_ENTITY_SPEED(mvPlayerCar.vehicle) < 0.5 // Only apply bouncy if car is nearly stopped
CPRINTLN(DEBUG_MISSION, "BOUNCY_BOOT: Car not moving (much): Bouncy! 0")
SET_FORCE_VARIABLES()
APPLY_FORCE_TO_ENTITY(mvPlayerCar.vehicle, APPLY_TYPE_IMPULSE, << 0.0, 0.0, iBounceForce >>, << fBounceOffSet, -1.5, 0.0 >>, 0, TRUE, TRUE, FALSE, TRUE)
ELSE
CPRINTLN(DEBUG_MISSION, "BOUNCY_BOOT: Car moving: No bouncy!")
ENDIF
// Set up counters for more bounces
iBounceLimit = GET_RANDOM_INT_IN_RANGE(3,7)
iBounceCount = 1
iBounceTimer = GET_GAME_TIMER()
iBounceDelay = GET_RANDOM_INT_IN_RANGE(100,250)
spBouncyBoot = SP_RUNNING
BREAK
CASE SP_RUNNING
// Repeat bounces until we've done the required amount
IF iBounceCount < iBounceLimit
// Check whether it's time to do another bounce
IF GET_GAME_TIMER() - iBounceTimer > iBounceDelay
IF GET_ENTITY_SPEED(mvPlayerCar.vehicle) < 0.5 // Only apply bouncy if car is nearly stopped
CPRINTLN(DEBUG_MISSION, "BOUNCY_BOOT: Car not moving (much): Bouncy! ", iBounceCount)
SET_FORCE_VARIABLES()
APPLY_FORCE_TO_ENTITY(mvPlayerCar.vehicle, APPLY_TYPE_IMPULSE, << 0.0, 0.0, iBounceForce >>, << fBounceOffSet, -1.5, 0.0 >>, 0, TRUE, TRUE, FALSE, TRUE)
ELSE
CPRINTLN(DEBUG_MISSION, "BOUNCY_BOOT: Car moving: No bouncy!")
ENDIF
iBounceTimer = GET_GAME_TIMER()
iBounceDelay = GET_RANDOM_INT_IN_RANGE(100,250)
iBounceCount++
ENDIF
ELSE
spBouncyBoot = SP_CLEANUP
ENDIF
BREAK
CASE SP_CLEANUP
// This is the inert state for waiting until called again
BREAK
ENDSWITCH
ENDIF
ENDPROC
/// PURPOSE:
/// Checks the status of the damage complaint conversations
PROC CHECK_DAMAGE_COMPLAINT_CONV()
// Only start conversations if player in car
IF IS_ENTITY_ALIVE(mvPlayerCar.vehicle)
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), mvPlayerCar.vehicle)
// Everything will go here
SWITCH damConvCycle
CASE DCC_WAIT_FOR_DAMAGE
// Don't start unless Trevor's embarkation comment has played
IF HAS_ENTITY_COLLIDED_WITH_ANYTHING(mvPlayerCar.vehicle)
AND NOT IS_THIS_PRINT_BEING_DISPLAYED("N3NORTH")
AND NOT IS_THIS_PRINT_BEING_DISPLAYED("N3RAM")
damConvCycle = DCC_START_COMMENT
ENDIF
BREAK
CASE DCC_START_COMMENT
// Wait until the conversation has initialised
IF CREATE_CONVERSATION(mConversationStruct, "NIGE3AU", "NIGEL3_FRND", CONV_PRIORITY_MEDIUM)
PLAY_BOOT_THUMP()
bDamageConvActive = TRUE
damConvCycle = DCC_WAIT_FOR_COMMENT
ENDIF
BREAK
CASE DCC_WAIT_FOR_COMMENT
IF NOT IS_SCRIPTED_CONVERSATION_ONGOING()
CPRINTLN(DEBUG_MISSION, "NIGEL3: detected damage complain conversation completed")
bDamageConvActive = FALSE
// Reset delay timer
iTimerDamageCommentDelay = GET_GAME_TIMER()
damConvCycle = DCC_REPEAT_DELAY
ENDIF
BREAK
CASE DCC_REPEAT_DELAY
// must be at least 8 seconds before he can bitch again
IF GET_GAME_TIMER() - iTimerDamageCommentDelay > 8000
damConvCycle = DCC_WAIT_FOR_DAMAGE
ENDIF
BREAK
ENDSWITCH
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Checks the status of the car being shot complaint conversations
/// These will actually be triggered by ANY damage the player causes to the vehicle when they are not in it
PROC CHECK_SHOOTING_COMPLAINT_CONV()
// Only start conversations if player not in car
IF NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), mvPlayerCar.vehicle)
SWITCH shootConvCycle
CASE DCC_WAIT_FOR_DAMAGE
// Check for damage...
IF HAS_ENTITY_BEEN_DAMAGED_BY_ENTITY(mvPlayerCar.vehicle, PLAYER_PED_ID())
// Don't start unless Trevor's embarkation comment has played, and player is close to vehicle
CPRINTLN(DEBUG_MISSION, "CHECK_SHOOTING_COMPLAINT_CONV: Player has damaged vehicle!")
IF IS_ENTITY_IN_RANGE_ENTITY(mvPlayerCar.vehicle, PLAYER_PED_ID(), 20)
shootConvCycle = DCC_START_COMMENT
// Reset random timed boot thump proc
spAlThumps = SP_SETUP
ENDIF
CLEAR_ENTITY_LAST_DAMAGE_ENTITY(mvPlayerCar.vehicle)
ENDIF
BREAK
CASE DCC_START_COMMENT
// Wait until the conversation has initialised
IF CREATE_CONVERSATION(mConversationStruct, "NIGE3AU", "NIGEL3_SCRND", CONV_PRIORITY_MEDIUM)
// Reset random timed boot thump proc
spAlThumps = SP_SETUP
// Play a sound
PLAY_BOOT_THUMP()
shootConvCycle = DCC_WAIT_FOR_COMMENT
ENDIF
BREAK
CASE DCC_WAIT_FOR_COMMENT
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
CPRINTLN(DEBUG_MISSION, "NIGEL3: detected shoot/damage complain conversation completed")
// Reset delay timer
iShootCommentDelay = GET_GAME_TIMER() + 3000
// Reset random timed boot thump proc
spAlThumps = SP_SETUP
shootConvCycle = DCC_REPEAT_DELAY
ENDIF
BREAK
CASE DCC_REPEAT_DELAY
// must be at least 3 seconds before he can bitch again
IF GET_GAME_TIMER() > iShootCommentDelay
shootConvCycle = DCC_WAIT_FOR_DAMAGE
CLEAR_ENTITY_LAST_DAMAGE_ENTITY(mvPlayerCar.vehicle)
ENDIF
BREAK
ENDSWITCH
ENDIF
ENDPROC
/// PURPOSE:
/// Al repeatedly plays panic comments and shakes the car boot until the train hits
PROC AL_TRAIN_PANIC()
// Tick the boot wobbler
BOUNCY_BOOT()
SWITCH spAlPanic
CASE SP_SETUP
// Wait for Trevor's gonnakillya comment to be done
IF bMurderCommentStarted
AND NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
iAlPanicTimer = GET_GAME_TIMER()
spAlPanic = SP_RUNNING
ENDIF
BREAK
CASE SP_RUNNING
IF IS_ENTITY_ALIVE(pedDiNapoli)
AND GET_GAME_TIMER() > iAlPanicTimer
IF IS_THIS_PRINT_BEING_DISPLAYED("N3BAIL")
IF CREATE_CONVERSATION(mConversationStruct, "NIGE3AU", "NIGEL3_PNC", CONV_PRIORITY_MEDIUM, DO_NOT_DISPLAY_SUBTITLES)
PLAY_BOOT_THUMP()
spAlPanic = SP_CLEANUP
ENDIF
ELSE
IF CREATE_CONVERSATION(mConversationStruct, "NIGE3AU", "NIGEL3_PNC", CONV_PRIORITY_MEDIUM)
PLAY_BOOT_THUMP()
spAlPanic = SP_CLEANUP
ENDIF
ENDIF
ENDIF
BREAK
CASE SP_CLEANUP
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
iAlPanicTimer = GET_GAME_TIMER() + GET_RANDOM_INT_IN_RANGE(1000,2500)
spAlPanic = SP_RUNNING
ENDIF
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Handles the en route conversations
/// Should be called in blocks where the player will be in the car
PROC DO_CONVERSATION_CYCLE()
// Tick the boot wobbler
BOUNCY_BOOT()
// Handle the conversation cycle
SWITCH cccProgress
CASE CCC_WAIT_FOR_DISTANCE
// Are we far enough from the start to trigger next conversation
IF IS_ENTITY_ALIVE(mvPlayerCar.vehicle)
IF NOT IS_ENTITY_IN_RANGE_COORDS(mvPlayerCar.vehicle, vecStartLocation, ciConvos[iConvoCount].triggerDistance)
AND NOT bDamageConvActive
cccProgress = CCC_START_CONVERSATION
IF iConvoCount < NUM_BEGGING_CONVS-1
PLAY_BOOT_THUMP()
bNotThumping = FALSE
ELSE
bNotThumping = TRUE
ENDIF
ELSE
// Progress the damage complaint conversation
CHECK_DAMAGE_COMPLAINT_CONV()
ENDIF
ENDIF
BREAK
CASE CCC_START_CONVERSATION
// Wait for thumps
IF bNotThumping = FALSE
bNotThumping = HAS_SOUND_FINISHED(iThumpID)
ENDIF
// Check conversation counter is in range
IF iConvoCount < NUM_BEGGING_CONVS
AND bNotThumping
// Make sure there isn't a damage complaint going
IF NOT bDamageConvActive
// If we're on the last conversation, don't start it if we're too close to the end
IF iConvoCount = NUM_BEGGING_CONVS-1
IF NOT IS_ENTITY_IN_RANGE_COORDS(PLAYER_PED_ID(), vecDestination, 150)
IF CREATE_CONVERSATION(mConversationStruct, ciConvos[iConvoCount].txtBlock, ciConvos[iConvoCount].rootBlock, CONV_PRIORITY_MEDIUM)
IF DOES_ENTITY_EXIST(pedDiNapoli)
ADD_PED_TO_CONVERSATION(CONVPED_ALDN, pedDiNapoli, "DINAPOLI")
ENDIF
// last conversation initiated by Trevor - so not playing a thump here is deliberate
IF iConvoCount = 0
REPLAY_RECORD_BACK_FOR_TIME(2.0, 5.0, REPLAY_IMPORTANCE_LOWEST)
ENDIF
cccProgress = CCC_WAIT_TO_FINISH
ENDIF
ENDIF
ELSE
// Go to next step once conversation's been started OK
IF CREATE_CONVERSATION(mConversationStruct, ciConvos[iConvoCount].txtBlock, ciConvos[iConvoCount].rootBlock, CONV_PRIORITY_MEDIUM)
IF DOES_ENTITY_EXIST(pedDiNapoli)
ADD_PED_TO_CONVERSATION(CONVPED_ALDN, pedDiNapoli, "DINAPOLI")
ENDIF
// Play a sound
PLAY_BOOT_THUMP()
cccProgress = CCC_WAIT_TO_FINISH
ENDIF
ENDIF
ELSE
// Progress the damage complaint conversation
CHECK_DAMAGE_COMPLAINT_CONV()
ENDIF
ELSE
IF iConvoCount >= NUM_BEGGING_CONVS
CPRINTLN(DEBUG_MISSION, "DO_CONVERSATION_CYCLE: Got into CCC_START_CONVERSATION with iConvoCount >= NUM_BEGGING_CONVS, this shouldn't happen.")
cccProgress = CCC_CONVERSATIONS_DONE
#IF IS_DEBUG_BUILD
ELSE
CPRINTLN(DEBUG_MISSION, "DO_CONVERSATION_CYCLE: Waiting for boot thumps to finish...") #ENDIF
ENDIF
ENDIF
BREAK
CASE CCC_WAIT_TO_FINISH
// Check whether player is out of vehicle
IF IS_ENTITY_ALIVE(mvPlayerCar.vehicle)
IF NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), mvPlayerCar.vehicle)
IF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), mvPlayerCar.vehicle, 15)
// CPRINTLN(DEBUG_MISSION, "DO_CONVERSATION_CYCLE: CCC_WAIT_TO_FINISH: Killing conversation because the player too far from car.")
KILL_FACE_TO_FACE_CONVERSATION_DO_NOT_FINISH_LAST_LINE()
ENDIF
ENDIF
// Check whether conversation is finished
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
bReleaseIsValid = TRUE
// Don't trigger next conversation until player is in car
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), mvPlayerCar.vehicle)
iConversationTimer = GET_GAME_TIMER()
cccProgress = CCC_DELAY_NEXT_CONVERSATION
// Increment conversation counter
iConvoCount++
// #IF IS_DEBUG_BUILD
// ELSE
// CPRINTLN(DEBUG_MISSION, "DO_CONVERSATION_CYCLE: CCC_WAIT_TO_FINISH: Waiting to progress to another begging conversation but player isn't in car.") #ENDIF
ENDIF
ENDIF
ENDIF
// Handle conversation subtitle and objective conflicts
HANDLE_CONVERSATION_AND_OBJECTIVE_TEXT_CONFLICT(stateRestoreConversation, mConversationStruct, "NIGE3AU", tSavedConversationRoot, tSavedConversationLabel)
BREAK
CASE CCC_DELAY_NEXT_CONVERSATION
// Wait for help popup to time out
IF GET_GAME_TIMER() - iConversationTimer < DEFAULT_GOD_TEXT_TIME
IF NOT bReleaseObjectiveShown
PRINT_HELP("N3REL1") // If you want to release Al, stop the car and get out.
bReleaseObjectiveShown = TRUE
ENDIF
// Or wait for the minimum time between conversations
ELIF GET_GAME_TIMER() - iConversationTimer > CONVERSATION_GAP
// Is there another valid conversation?
IF iConvoCount < NUM_BEGGING_CONVS
cccProgress = CCC_WAIT_FOR_DISTANCE
ELSE
// No valid next conversation, conversations are done
cccProgress = CCC_CONVERSATIONS_DONE
ENDIF
ELSE
// Progress the damage complaint conversation
CHECK_DAMAGE_COMPLAINT_CONV()
ENDIF
BREAK
CASE CCC_CONVERSATIONS_DONE
// Nothing else to do except check damage conversations
CHECK_DAMAGE_COMPLAINT_CONV()
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// This tidies up any conversation stuff when the player gets out of the car
/// makes it easier to resume cleanly if player gets back in
PROC CONVERSATION_KILLER()
SWITCH cccProgress
CASE CCC_WAIT_FOR_DISTANCE
// Do nothing: Safe to leave it in this state
BREAK
CASE CCC_START_CONVERSATION
// This state should only exist for a few frames, but if we're in it we want to come back to it
// Nobble any conversation just in case
IF IS_SCRIPTED_CONVERSATION_ONGOING()
AND bBeggingConvActive
STOP_CHECKING_CONVERSATION_AND_OBJECTIVE_TEXT_CONFLICT(stateRestoreConversation)
KILL_FACE_TO_FACE_CONVERSATION()
ENDIF
BREAK
CASE CCC_WAIT_TO_FINISH
// Kill the conversation
IF IS_SCRIPTED_CONVERSATION_ONGOING()
AND bBeggingConvActive
STOP_CHECKING_CONVERSATION_AND_OBJECTIVE_TEXT_CONFLICT(stateRestoreConversation)
KILL_FACE_TO_FACE_CONVERSATION()
ENDIF
BREAK
CASE CCC_DELAY_NEXT_CONVERSATION
// Do nothing: Safe to leave in this state
BREAK
CASE CCC_CONVERSATIONS_DONE
// Do nothing: Conversations are done!
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Fills in the conversation struct for when Nigel and Mrs Thornhill are waiting for Trevor to start driving
PROC SETUP_NAGGING_CONVERSATION_INFO()
ciNaggingConv[0].txtBlock = "NIGE3AU"
ciNaggingConv[0].rootBlock = "NIGEL3_N1"
ciNaggingConv[1].txtBlock = "NIGE3AU"
ciNaggingConv[1].rootBlock = "NIGEL3_N2"
ciNaggingConv[2].txtBlock = "NIGE3AU"
ciNaggingConv[2].rootBlock = "NIGEL3_N3"
sNagLineLabels[0] = "NIGEL3_DAWD_1"
sNagLineLabels[1] = "NIGEL3_DAWD_3"
sNagLineLabels[2] = "NIGEL3_DAWD_5"
sNagLineLabels[3] = "NIGEL3_DAWD_7"
sNagLineLabels[4] = "NIGEL3_DAWD_9"
sNagLineLabels[5] = "NIGEL3_DAWD_11"
ENDPROC
/// PURPOSE:
/// Loads the anim dictionaries needed by the stalkers' idles
PROC REQUEST_STALKER_ANIMS()
REQUEST_ANIM_DICT(sNMTAnimDict)
bNotSetUpMrsTAnims = TRUE
bNotSetUpNigelAnims = TRUE
ENDPROC
/// PURPOSE:
/// Checks whether the anim dictionaries for the stalkers have loaded
/// RETURNS:
/// TRUE once both required dictionaries are loaded
FUNC BOOL STALKER_ANIM_DICTS_LOADED()
IF NOT HAS_ANIM_DICT_LOADED(sNMTAnimDict)
RETURN FALSE
ELSE
RETURN TRUE
ENDIF
ENDFUNC
/// PURPOSE:
/// Sets up anims on Nigel and Mrs Thornhill
PROC SETUP_NIGEL_ANIMS()
IF IS_ENTITY_ALIVE(pedNigel)
CPRINTLN(DEBUG_MISSION, "SETUP_NIGEL_ANIMS adding anim tasks")
SET_ENTITY_COORDS_NO_OFFSET(pedNigel, << -44.83, -1289.11, 29.17 >>)//<<-44.833633,-1289.034424,29.166584>>)// << -44.828876, -1289.113281, 29.166151 >>)
TASK_PLAY_ANIM(pedNigel, sNMTAnimDict, sNigelAnim, INSTANT_BLEND_IN, FAST_BLEND_OUT, -1, AF_LOOPING)
FORCE_PED_AI_AND_ANIMATION_UPDATE(pedNigel, TRUE)
TASK_LOOK_AT_ENTITY(pedNigel, PLAYER_PED_ID(), -1)
SET_PED_KEEP_TASK(pedNigel, TRUE)
bNotSetUpNigelAnims = FALSE
ENDIF
ENDPROC
/// PURPOSE:
/// Sets up anims on Nigel and Mrs Thornhill
PROC SETUP_MRST_ANIMS()
IF IS_ENTITY_ALIVE(pedMrsThornhill)
CPRINTLN(DEBUG_MISSION, "SETUP_MRST_ANIMS adding anim tasks")
SET_ENTITY_COORDS_NO_OFFSET(pedMrsThornhill, <<-45.00, -1289.83, 29.177>>)//<<-44.991505,-1289.909546,29.17750>>)// << -44.999081, -1289.767700, 29.18 >>)
TASK_PLAY_ANIM(pedMrsThornhill, sNMTAnimDict, sMrsTAnim, INSTANT_BLEND_IN, FAST_BLEND_OUT, -1, AF_LOOPING)
FORCE_PED_AI_AND_ANIMATION_UPDATE(pedMrsThornhill, TRUE)
TASK_LOOK_AT_ENTITY(pedMrsThornhill, PLAYER_PED_ID(), -1)
SET_PED_KEEP_TASK(pedMrsThornhill, TRUE)
oiHandbag = CREATE_OBJECT(PROP_LD_HANDBAG_S, <<-45.00, -1289.83, 49.177>>)
ATTACH_ENTITY_TO_ENTITY(oiHandbag, pedMrsThornhill, GET_PED_BONE_INDEX(pedMrsThornhill, BONETAG_L_CLAVICLE), vecBagOffset, vecBagAngles)
bNotSetUpMrsTAnims = FALSE
ENDIF
ENDPROC
/// PURPOSE:
/// Remove anim dictionary when it's no longer needed
PROC CLEAR_NMT_ANIMS()
REMOVE_ANIM_DICT(sNMTAnimDict)
ENDPROC
/// PURPOSE:
/// Checks that the player hasn't knackered Nigel or Mrs Thornhill
/// Sets correct cleanup state for RANDOM_CHAR_HANDLER to fail mission if celeb is injured
FUNC BOOL RANDOM_CHAR_HEALTH_CHECK()
// Note that we do not use IS_ENTITY_ALIVE, if the stalkers have been deleted/released, that is fine.
// We only have a problem if they are currently spawned, and either dead, or hurt by the player.
// Health check Nigel
IF DOES_ENTITY_EXIST(pedNigel)
IF IS_ENTITY_DEAD(pedNigel)
NMTCleanupState = NMTCS_NIGEL_DEAD
RETURN FALSE
ELIF HAS_ENTITY_BEEN_DAMAGED_BY_ENTITY(pedNigel, PLAYER_PED_ID())
NMTCleanupState = NMTCS_NIGEL_INJURED
RETURN FALSE
ENDIF
ELSE
NMTCleanupState = NMTCS_NIGEL_INJURED
RETURN FALSE
ENDIF
// Health check Mrs Thornhill
IF DOES_ENTITY_EXIST(pedMrsThornhill)
IF IS_ENTITY_DEAD(pedMrsThornhill)
NMTCleanupState = NMTCS_MRS_THORNHILL_DEAD
RETURN FALSE
ELIF HAS_ENTITY_BEEN_DAMAGED_BY_ENTITY(pedMrsThornhill, PLAYER_PED_ID())
NMTCleanupState = NMTCS_MRS_THORNHILL_INJURED
RETURN FALSE
ENDIF
ELSE
NMTCleanupState = NMTCS_MRS_THORNHILL_INJURED
RETURN FALSE
ENDIF
// If we reach the end both characters are OK
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Handles loading and behaviour of Nigel and Mrs Thornhill
PROC RANDOM_CHAR_HANDLER()
// Check on Nigel and Mrs Thornhill
IF bRandomCharsStillLoaded
AND mStage != MS_DEBUG_CRASHES_FOREVER
SWITCH spRandomCharHandler
CASE SP_SETUP
// Wait until we're in a stage where we need to start managing
IF mStage != MS_INIT AND mStage != MS_INTRO_CUTSCENE
// Cutscene or skip procs will have set up random chars
IF IS_ENTITY_ALIVE(pedNigel)
AND IS_ENTITY_ALIVE(pedMrsThornhill)
// Characters have loaded, add to conversation struct
ADD_PED_FOR_DIALOGUE(mConversationStruct, CONVPED_NIGE, pedNigel, "NIGEL")
SET_PED_CONFIG_FLAG(pedNigel, PCF_UseKinematicModeWhenStationary, TRUE)
SET_PED_CAN_RAGDOLL_FROM_PLAYER_IMPACT(pedNigel, FALSE)
TASK_LOOK_AT_ENTITY(pedNigel, PLAYER_PED_ID(), -1)
SET_PED_KEEP_TASK(pedNigel, TRUE)
ADD_PED_FOR_DIALOGUE(mConversationStruct, CONVPED_MRST, pedMrsThornhill, "MRSTHORNHILL")
SET_PED_CONFIG_FLAG(pedMrsThornhill, PCF_UseKinematicModeWhenStationary, TRUE)
SET_PED_CAN_RAGDOLL_FROM_PLAYER_IMPACT(pedMrsThornhill, FALSE)
// Progress to waiting for Trevor to do something
iStalkerCommentsTimer = GET_GAME_TIMER()
// Set up friendly group for stalkers
ADD_RELATIONSHIP_GROUP("FRIENDLIES", relGroupFriendly)
SET_RELATIONSHIP_BETWEEN_GROUPS(ACQUAINTANCE_TYPE_PED_LIKE, relGroupFriendly, RELGROUPHASH_PLAYER)
SET_RELATIONSHIP_BETWEEN_GROUPS(ACQUAINTANCE_TYPE_PED_LIKE, RELGROUPHASH_PLAYER, relGroupFriendly)
IF IS_PED_IN_GROUP(pedNigel)
REMOVE_PED_FROM_GROUP(pedNigel)
ENDIF
SET_PED_RELATIONSHIP_GROUP_HASH(pedNigel, relGroupFriendly)
IF IS_PED_IN_GROUP(pedMrsThornhill)
REMOVE_PED_FROM_GROUP(pedMrsThornhill)
ENDIF
SET_PED_RELATIONSHIP_GROUP_HASH(pedMrsThornhill, relGroupFriendly)
bRelGroupExists = TRUE
// Init conversation trackers
iNagCount = 0
iNagLineCount = 0
spRandomCharHandler = SP_RUNNING
ENDIF
ENDIF
BREAK
CASE SP_RUNNING
// Check player hasn't nobbled the celeb stalkers
IF NOT RANDOM_CHAR_HEALTH_CHECK()
spRandomCharHandler = SP_CLEANUP
// Check player isn't far enough away to despawn stalkers
ELIF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedNigel, 100)
AND NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedMrsThornhill, 100)
NMTCleanupState = NMTCS_PLAYER_LEFT
spRandomCharHandler = SP_CLEANUP
// If the stalkers get spooked all their other behaviours won't happen
ELIF NOT bPlayerSpookedStalkers
// Check player isn't shooting near random chars
IF HAS_PLAYER_THREATENED_PED(pedNigel)
OR HAS_PLAYER_THREATENED_PED(pedMrsThornhill)
CPRINTLN(DEBUG_MISSION, "RANDOM_CHAR_HANDLER: Threatened ped!")
CLEAR_PED_TASKS(pedNigel)
TASK_SMART_FLEE_PED(pedNigel, PLAYER_PED_ID(), 120, -1)
SET_PED_KEEP_TASK(pedNigel, TRUE)
// CLEAR_PED_TASKS(pedMrsThornhill)
// TASK_SMART_FLEE_PED(pedMrsThornhill, PLAYER_PED_ID(), 120, -1)
TASK_PLAY_ANIM(pedMrsThornhill, sNMTAnimDict, "Cower_Enter_MsT", FAST_BLEND_IN, FAST_BLEND_OUT, -1, AF_DEFAULT)
SET_PED_KEEP_TASK(pedMrsThornhill, TRUE)
// Stop conversation
IF nagConv = NAG_CONVERSATION_ONGOING
AND IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
CPRINTLN(DEBUG_MISSION, "RANDOM_CHAR_HANDLER: SHUT THE HELL UP AND RUN!")
KILL_FACE_TO_FACE_CONVERSATION_DO_NOT_FINISH_LAST_LINE()
ENDIF
NMTCleanupState = NMTCS_PLAYER_SCARED_STALKERS
spRandomCharHandler = SP_CLEANUP
bPlayerSpookedStalkers = TRUE
BREAK
// Check whether player has nobbled the stalkers with car
ELIF IS_ENTITY_TOUCHING_ENTITY(pedNigel, mvPlayerCar.vehicle)
IF IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
// Interrupt conversation (immediately if Nigel or Mrs Thornhill is speaking)
IF GET_SPEAKER_INT_FOR_CURRENT_STANDARD_CONVERSATION_LINE() = CONVPED_MRST
OR GET_SPEAKER_INT_FOR_CURRENT_STANDARD_CONVERSATION_LINE() = CONVPED_NIGE
KILL_FACE_TO_FACE_CONVERSATION_DO_NOT_FINISH_LAST_LINE()
ELSE
KILL_FACE_TO_FACE_CONVERSATION()
ENDIF
ENDIF
SET_PED_TO_RAGDOLL(pedNigel, 750, 2000, TASK_NM_SCRIPT)
TASK_SMART_FLEE_PED(pedNigel, PLAYER_PED_ID(), 120, -1)
TASK_SMART_FLEE_PED(pedMrsThornhill, PLAYER_PED_ID(), 120, -1)
NMTCleanupState = NMTCS_PLAYER_SCARED_STALKERS
spRandomCharHandler = SP_CLEANUP
ELIF IS_ENTITY_TOUCHING_ENTITY(pedMrsThornhill, mvPlayerCar.vehicle)
IF IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
// Interrupt conversation (immediately if Nigel or Mrs Thornhill is speaking)
IF GET_SPEAKER_INT_FOR_CURRENT_STANDARD_CONVERSATION_LINE() = CONVPED_MRST
OR GET_SPEAKER_INT_FOR_CURRENT_STANDARD_CONVERSATION_LINE() = CONVPED_NIGE
KILL_FACE_TO_FACE_CONVERSATION_DO_NOT_FINISH_LAST_LINE()
ELSE
KILL_FACE_TO_FACE_CONVERSATION()
ENDIF
ENDIF
SET_PED_TO_RAGDOLL(pedMrsThornhill, 750, 2000, TASK_NM_SCRIPT)
TASK_SMART_FLEE_PED(pedNigel, PLAYER_PED_ID(), 120, -1)
TASK_SMART_FLEE_PED(pedMrsThornhill, PLAYER_PED_ID(), 120, -1)
NMTCleanupState = NMTCS_PLAYER_SCARED_STALKERS
spRandomCharHandler = SP_CLEANUP
ELSE
// Checks whether it's time for Nigel and Mrs Thornhill to comment on player if they're lurking
SWITCH nagConv
CASE NAG_INITIAL
IF IS_ENTITY_ALIVE(pedMrsThornhill)
// Initial comment explaining celeb is in car boot
// Same time as objective so don't subtitle
IF CREATE_CONVERSATION(mConversationStruct, "NIGE3AU", "NIGEL3_N0", CONV_PRIORITY_MEDIUM, DO_NOT_DISPLAY_SUBTITLES)
nagConv = NAG_WAITING
ENDIF
ENDIF
BREAK
CASE NAG_WAITING
IF iNagCount < NUM_NAGGING_CONVS
// Play conversation from sequence
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
AND IS_ENTITY_ALIVE(PLAYER_PED_ID())
IF GET_GAME_TIMER() - iStalkerCommentsTimer > NAGGING_CONVERSATION_GAP
AND IS_ENTITY_IN_RANGE_COORDS(PLAYER_PED_ID(), vecStartLocation, 30)
IF IS_THIS_PRINT_BEING_DISPLAYED("N3DRIVE")
OR IS_THIS_PRINT_BEING_DISPLAYED("N3RTCAR")
CPRINTLN(DEBUG_MISSION, "RANDOM_CHAR_HANDLER: Starting nag conversation ", iNagCount, " without subtitles")
IF CREATE_CONVERSATION(mConversationStruct, ciNaggingConv[iNagCount].txtBlock, ciNaggingConv[iNagCount].rootBlock, CONV_PRIORITY_MEDIUM, DO_NOT_DISPLAY_SUBTITLES)
nagConv = NAG_CONVERSATION_ONGOING
ENDIF
ELSE
CPRINTLN(DEBUG_MISSION, "RANDOM_CHAR_HANDLER: Starting nag conversation ", iNagCount, " with subtitles")
IF CREATE_CONVERSATION(mConversationStruct, ciNaggingConv[iNagCount].txtBlock, ciNaggingConv[iNagCount].rootBlock, CONV_PRIORITY_MEDIUM)
nagConv = NAG_CONVERSATION_ONGOING
ENDIF
ENDIF
ENDIF
ENDIF
ELSE
// Out of standard conversations, play sequenced single lines
IF iNagLineCount < NUM_NAGGING_LINES
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
AND IS_ENTITY_ALIVE(PLAYER_PED_ID())
IF GET_GAME_TIMER() - iStalkerCommentsTimer > NAGGING_CONVERSATION_GAP
AND IS_ENTITY_IN_RANGE_COORDS(PLAYER_PED_ID(), vecStartLocation, 30)
AND (GET_PROFILE_SETTING(PROFILE_DISPLAY_SUBTITLES) = 0 OR NOT IS_THIS_PRINT_BEING_DISPLAYED("N3DRIVE"))
IF PLAY_SINGLE_LINE_FROM_CONVERSATION(mConversationStruct, "NIGE3AU", "NIGEL3_DAWD", sNagLineLabels[iNagLineCount], CONV_PRIORITY_MEDIUM, DISPLAY_SUBTITLES)
CPRINTLN(DEBUG_MISSION, "RANDOM_CHAR_HANDLER: Played nag line ", iNagLineCount)
iNagLineCount++
nagConv = NAG_CONVERSATION_ONGOING
ENDIF
ENDIF
ENDIF
ELSE
CPRINTLN(DEBUG_MISSION, "RANDOM_CHAR_HANDLER: Ran out of nag conversations AND sequenced lines, banter is over")
nagConv = NAG_SPENT
ENDIF
ENDIF
BREAK
CASE NAG_CONVERSATION_ONGOING
// Range kill conversations - B*1098737
IF NOT IS_ENTITY_IN_RANGE_COORDS(PLAYER_PED_ID(), vecStartLocation, 35)
CPRINTLN(DEBUG_MISSION, "RANDOM_CHAR_HANDLER: NAG_CONVERSATION_ONGOING: Detected player out of range, killing conversation")
STOP_CHECKING_CONVERSATION_AND_OBJECTIVE_TEXT_CONFLICT(stateRestoreConversation)
KILL_FACE_TO_FACE_CONVERSATION()
nagConv = NAG_FINISHED
ELSE
HANDLE_CONVERSATION_AND_OBJECTIVE_TEXT_CONFLICT(stateRestoreConversation, mConversationStruct, "NIGE3AU", tSavedConversationRoot, tSavedConversationLabel)
ENDIF
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
CPRINTLN(DEBUG_MISSION, "RANDOM_CHAR_HANDLER: NAG_CONVERSATION_ONGOING: Detected conversation over")
nagConv = NAG_FINISHED
ENDIF
BREAK
CASE NAG_FINISHED
iStalkerCommentsTimer = GET_GAME_TIMER()
iNagCount++
nagConv = NAG_WAITING
BREAK
CASE NAG_SPENT
// Nothing else to do
BREAK
ENDSWITCH
ENDIF
ENDIF
BREAK
CASE SP_CLEANUP
CLEAR_NMT_ANIMS()
SWITCH NMTCleanupState
CASE NMTCS_MRS_THORNHILL_DEAD
// Mrs Thornhill dead - FAIL
sFailReason = "N3MRSTDEAD" // Mrs Thornhill died.
mStage = MS_FAIL_FADE
sProgress = SP_SETUP
BREAK
CASE NMTCS_MRS_THORNHILL_INJURED
// Mrs Thornhill was injured - FAIL
sFailReason = "N3MRSTHURT" // Mrs Thornhill was injured
mStage = MS_FAIL_FADE
sProgress = SP_SETUP
BREAK
CASE NMTCS_NIGEL_DEAD
// Nigel dead - FAIL
sFailReason = "N3NIGEDEAD" // Nigel died.
mStage = MS_FAIL_FADE
sProgress = SP_SETUP
BREAK
CASE NMTCS_NIGEL_INJURED
// Nigel was injured - FAIL
sFailReason = "N3NIGEHURT" // Nigel was injured.
mStage = MS_FAIL_FADE
sProgress = SP_SETUP
BREAK
CASE NMTCS_PLAYER_SCARED_STALKERS
// Player scared stalkers - FAIL
sFailReason = "N3SCARED" // You scared Nigel and Mrs Thornhill.
mStage = MS_FAIL_FADE
sProgress = SP_SETUP
BREAK
CASE NMTCS_PLAYER_LEFT
// Player not near stalkers any more, clean them up
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
AND bRandomCharsStillLoaded
// Player has left proximity of Nigel and Mrs Thornhill, delete them
bRandomCharsStillLoaded = FALSE
REMOVE_PED_FOR_DIALOGUE(mConversationStruct, CONVPED_NIGE) // Nigel
REMOVE_PED_FOR_DIALOGUE(mConversationStruct, CONVPED_MRST) // Mrs Thornhill
IF bRelGroupExists
REMOVE_RELATIONSHIP_GROUP(relGroupFriendly)
bRelGroupExists = FALSE
ENDIF
SAFE_DELETE_PED(pedNigel)
SAFE_DELETE_OBJECT(oiHandbag)
SAFE_DELETE_PED(pedMrsThornhill)
ENDIF
BREAK
ENDSWITCH
BREAK
ENDSWITCH
ENDIF
ENDPROC
/// PURPOSE:
/// Resolve the cutscene area
PROC CLEAR_NIGEL3_CUTSCENE_AREA()
// Cutscene area
RESOLVE_VEHICLES_INSIDE_ANGLED_AREA_WITH_SIZE_LIMIT(<<-40.749416,-1294.584229,27.251148>>, <<-40.964180,-1284.256592,35.233482>>, 9.5, << -49.09, -1260.03, 29.26 >>, -87.89, << 5.0, 14.0, 5.0 >>)
// Car here is shit for gameplay
RESOLVE_VEHICLES_INSIDE_ANGLED_AREA_WITH_SIZE_LIMIT(<<-40.749416,-1294.584229,27.251148>>, <<-40.681786,-1302.860107,35.009792>>, 9.5, << -42.69, -1314.36, 28.06 >>, 0.9, << 5.0, 14.0, 5.0 >>)
SET_MISSION_START_VEHICLE_AS_VEHICLE_GEN(<<0,0,0>>, 0, TRUE, CHAR_TREVOR)
// Delete luminous prostitutes - B*1477445
CLEAR_AREA_OF_PEDS(<<-39.5317, -1257.1841, 28.2361>>, 8.0)
ENDPROC
/// PURPOSE:
/// Will make Al thump and rock the boot periodically when the player isn't in the car
PROC AL_THUMP_HANDLER()
// Check Al exists
IF IS_ENTITY_ALIVE(pedDiNapoli)
// Trigger limited to player not in mission car
IF NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), mvPlayerCar.vehicle)
SWITCH spAlThumps
CASE SP_SETUP
// If we are in setup EITHER: Player has just exited vehicle/we just finished intro cutscene/we just finished a thump event/conversation handler called a reset
// Set a random delay for a thump incident
iTriggerThumpDelay = GET_GAME_TIMER() + GET_RANDOM_INT_IN_RANGE(6000,12000)
spAlThumps = SP_RUNNING
BREAK
CASE SP_RUNNING
// If the timer's up and we're not interrupting an existing conversation, thumps
IF GET_GAME_TIMER() > iTriggerThumpDelay
AND NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
PLAY_BOOT_THUMP()
spAlThumps = SP_CLEANUP
ENDIF
BREAK
CASE SP_CLEANUP
// Wait until bouncy boot has gone inert again
IF spBouncyBoot = SP_CLEANUP
// Go back to timeout for another trunk thump
spAlThumps = SP_SETUP
ENDIF
BREAK
ENDSWITCH
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Handles loading all the models needed and spawning the train
/// RETURNS:
/// TRUE once the train exists
FUNC BOOL SPAWN_TRAIN()
IF NOT bRequestedTrainModels
// Request the models
REQUEST_MODEL(mvTrain.model)
REQUEST_MODEL(FREIGHT)
REQUEST_MODEL(FREIGHTCAR)
REQUEST_MODEL(FREIGHTCONT1)
REQUEST_MODEL(FREIGHTCONT2)
REQUEST_MODEL(mvTrain.driverModel)
bRequestedTrainModels = TRUE
CPRINTLN(DEBUG_MISSION, "SPAWN_TRAIN: Requested models")
RETURN FALSE
ELIF NOT DOES_ENTITY_EXIST(mvTrain.vehicle)
// If models are loaded create train
IF HAS_MODEL_LOADED(mvTrain.model)
AND HAS_MODEL_LOADED(FREIGHT)
AND HAS_MODEL_LOADED(FREIGHTCAR)
AND HAS_MODEL_LOADED(FREIGHTCONT1)
AND HAS_MODEL_LOADED(FREIGHTCONT2)
mvTrain.vehicle = CREATE_MISSION_TRAIN(10, mvTrain.location, TRUE)
SET_ENTITY_INVINCIBLE(mvTrain.vehicle, TRUE)
SET_TRAIN_SPEED(mvTrain.vehicle, TRAIN_INITIAL_SPEED)
SET_MODEL_AS_NO_LONGER_NEEDED(mvTrain.model)
SET_MODEL_AS_NO_LONGER_NEEDED(FREIGHT)
SET_MODEL_AS_NO_LONGER_NEEDED(FREIGHTCAR)
SET_MODEL_AS_NO_LONGER_NEEDED(FREIGHTCONT1)
SET_MODEL_AS_NO_LONGER_NEEDED(FREIGHTCONT2)
CPRINTLN(DEBUG_MISSION, "SPAWN_TRAIN: Spawned train")
ENDIF
RETURN FALSE
ELIF NOT DOES_ENTITY_EXIST(mvTrain.driver)
// We have a train but no driver
IF HAS_MODEL_LOADED(mvTrain.driverModel)
AND IS_ENTITY_ALIVE(mvTrain.vehicle)
mvTrain.driver = CREATE_PED_INSIDE_VEHICLE(mvTrain.vehicle, PEDTYPE_MISSION, mvTrain.driverModel)
SET_PED_COMPONENT_VARIATION(mvTrain.driver, PED_COMP_TORSO, 1, 0)
SET_PED_COMPONENT_VARIATION(mvTrain.driver, PED_COMP_LEG, 0, 0)
SET_PED_COMPONENT_VARIATION(mvTrain.driver, PED_COMP_DECL, 0, 0)
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(mvTrain.driver, TRUE)
SET_MODEL_AS_NO_LONGER_NEEDED(S_M_M_Trucker_01)
CPRINTLN(DEBUG_MISSION, "SPAWN_TRAIN: Spawned train driver")
RETURN TRUE
ELSE
RETURN FALSE
ENDIF
ELSE
// Everything exists now
RETURN TRUE
ENDIF
ENDFUNC
/// PURPOSE:
/// Creates the cinematic cam when player input should enable it
PROC CREATE_TRAIN_CINEMATIC_CAM()
IF NOT DOES_CAM_EXIST(camTrainCinCam)
camTrainCinCam = CREATE_CAM("DEFAULT_SCRIPTED_CAMERA", FALSE)
SET_CAM_FOV(camTrainCinCam, 40.68)
SHAKE_CAM(camTrainCinCam, "HAND_SHAKE", 1.0)
ATTACH_CAM_TO_ENTITY(camTrainCinCam, mvTrain.vehicle, << 3.32, -6.08, 4.28 >>)
SET_CAM_ACTIVE(camTrainCinCam, TRUE)
RENDER_SCRIPT_CAMS(TRUE, FALSE)
ENDIF
ENDPROC
/// PURPOSE:
/// Sets the cinematic cam off and destroys it
PROC REMOVE_TRAIN_CINEMATIC_CAM()
IF DOES_CAM_EXIST(camTrainCinCam)
IF IS_CAM_ACTIVE(camTrainCinCam)
SET_CAM_ACTIVE(camTrainCinCam, FALSE)
RENDER_SCRIPT_CAMS(FALSE, FALSE)
ENDIF
DESTROY_CAM(camTrainCinCam)
ENDIF
ENDPROC
/// PURPOSE:
/// Controls the cinematic cam
PROC TRAIN_CINEMATIC_CAM(BOOL bForceCleanup = FALSE)
IF bForceCleanup
REMOVE_TRAIN_CINEMATIC_CAM()
CLEAR_HELP(TRUE)
ELSE
// Cam cannot be used if player is not in car
IF NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), mvPlayerCar.vehicle)
IF bShowedTrainCamHelp
AND IS_HELP_MESSAGE_BEING_DISPLAYED()
CLEAR_HELP (TRUE)
ENDIF
REMOVE_TRAIN_CINEMATIC_CAM()
ELSE
// Check car is in valid area for train cam to be active
IF IS_ENTITY_IN_RANGE_ENTITY(mvPlayerCar.vehicle, mvTrain.vehicle, 350.0)
IF IS_ENTITY_IN_ANGLED_AREA(mvPlayerCar.vehicle, naaTrainCamBox[0].vEnds[0], naaTrainCamBox[0].vEnds[1], naaTrainCamBox[0].fWidth)
OR IS_ENTITY_IN_ANGLED_AREA(mvPlayerCar.vehicle, naaTrainCamBox[1].vEnds[0], naaTrainCamBox[1].vEnds[1], naaTrainCamBox[1].fWidth)
OR IS_ENTITY_IN_ANGLED_AREA(mvPlayerCar.vehicle, naaTrainCamBox[2].vEnds[0], naaTrainCamBox[2].vEnds[1], naaTrainCamBox[2].fWidth)
OR IS_ENTITY_IN_ANGLED_AREA(mvPlayerCar.vehicle, naaTrainCamBox[3].vEnds[0], naaTrainCamBox[3].vEnds[1], naaTrainCamBox[3].fWidth)
// Check to show help
IF NOT bShowedTrainCamHelp
PRINT_HELP("N3TRAINCAM")
bShowedTrainCamHelp = TRUE
ENDIF
// Call standardised hint cam input checks
IF SHOULD_CONTROL_CHASE_HINT_CAM(localChaseHintCamStruct)
CREATE_TRAIN_CINEMATIC_CAM()
IF DOES_CAM_EXIST(camTrainCinCam)
POINT_CAM_AT_ENTITY(camTrainCinCam, mvPlayerCar.vehicle, << 0.0, 0.0, 2.0 >>)
ENDIF
ELSE
REMOVE_TRAIN_CINEMATIC_CAM()
ENDIF
ENDIF
ELSE
// Car nowhere near tracks - disable camera
REMOVE_TRAIN_CINEMATIC_CAM()
ENDIF
ENDIF
ENDIF
#IF IS_DEBUG_BUILD
IF bDebugDrawTrainCamZones
FOR iCount = 0 TO 3
DRAW_DEBUG_LOCATE_SPECIAL(naaTrainCamBox[iCount].vEnds[0], naaTrainCamBox[iCount].vEnds[1], naaTrainCamBox[iCount].fWidth)
ENDFOR
ENDIF
#ENDIF
ENDPROC
/// PURPOSE:
/// Increases the train speed as it passes a certain point
PROC RAM_SPEED_ADJUSTER()
// No need to do anything if we've not reached the ram stage yet
IF bRamStageStarted
// See if the train's been spawned yet
IF IS_ENTITY_ALIVE(mvTrain.vehicle)
// Find out where train is
VECTOR vecTrainCoords
vecTrainCoords = GET_ENTITY_COORDS(mvTrain.vehicle)
// Check if ram speedup needs starting
IF NOT bTrainRamSpeedUpStarted
// If train is near crossing, start speedup
IF vecTrainCoords.y < 1824.36
CPRINTLN(DEBUG_MISSION, "Train past speedup point - RAMMING SPEED!")
iRamSpeedTimer = GET_GAME_TIMER()
bTrainRamSpeedUpStarted = TRUE
// If driver is dead start speedup sooner
ELIF (DOES_ENTITY_EXIST(mvTrain.driver) AND IS_ENTITY_DEAD(mvTrain.driver))
CPRINTLN(DEBUG_MISSION, "Train driver was killed - RAMMING SPEED!")
iRamSpeedTimer = GET_GAME_TIMER()
bTrainRamSpeedUpStarted = TRUE
ENDIF
ENDIF
// If speed-up has started, adjust train speed
IF bTrainRamSpeedUpStarted
// Adjust train speed in proportion to time passed since train past speedup point
// Extent of speed variance
FLOAT fSpeedEnvelope
fSpeedEnvelope = TRAIN_TARGET_SPEED - TRAIN_INITIAL_SPEED
// Figure out how much to apply
FLOAT fTimeProportion
INT iTimePassed
iTimePassed = GET_GAME_TIMER() - iRamSpeedTimer
IF iTimePassed < TRAIN_SPEEDUP_TIME
fTimeProportion = TO_FLOAT(iTimePassed) / TO_FLOAT(TRAIN_SPEEDUP_TIME)
ELSE
fTimeProportion = 1.0
ENDIF
// Update speed
SET_TRAIN_SPEED(mvTrain.vehicle, TRAIN_INITIAL_SPEED + (fSpeedEnvelope * fTimeProportion))
#IF IS_DEBUG_BUILD IF bDebugTrainTTY CPRINTLN(DEBUG_MISSION, "--- Train speed ", GET_ENTITY_SPEED(mvTrain.vehicle), " ---") ENDIF #ENDIF
ELSE
SET_TRAIN_SPEED(mvTrain.vehicle, TRAIN_INITIAL_SPEED)
ENDIF
ENDIF
ENDIF
ENDPROC
// Checks whether the car is properly over the rails when it hits
FUNC BOOL PLAYER_CAR_IS_PROPERLY_ON_RAILS()
IF IS_ENTITY_ALIVE(mvPlayerCar.vehicle)
AND IS_ENTITY_ALIVE(mvTrain.vehicle)
// Do the easy check first
IF IS_ENTITY_IN_ANGLED_AREA(mvPlayerCar.vehicle, naaTrackWidthBox.vEnds[0], naaTrackWidthBox.vEnds[1], naaTrackWidthBox.fWidth)
#IF IS_DEBUG_BUILD IF bDebugTrainTTY CPRINTLN(DEBUG_MISSION, "PLAYER_CAR_IS_PROPERLY_ON_RAILS: Found car over rails with check on static angled area") ENDIF #ENDIF
RETURN TRUE
ENDIF
// Second easiest check is based on car origin in train centre box
VECTOR vTrainCentreBox[2]
vTrainCentreBox[0] = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvTrain.vehicle, << 0.0, 8.0, -4.0>>)
vTrainCentreBox[1] = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvTrain.vehicle, << 0.0, 16.0, 8.0>>)
IF IS_ENTITY_IN_ANGLED_AREA(mvPlayerCar.vehicle, vTrainCentreBox[0], vTrainCentreBox[1], naaTrackWidthBox.fWidth)
#IF IS_DEBUG_BUILD IF bDebugTrainTTY CPRINTLN(DEBUG_MISSION, "PLAYER_CAR_IS_PROPERLY_ON_RAILS: Found car over rails with train centre box") ENDIF #ENDIF
RETURN TRUE
ENDIF
// Approx values are adequate for this - we need to determine a reasonably solid hit
FLOAT fCarWidth
FLOAT fCarLength
fCarWidth = 1.6
fCarLength = 4.2
// Set up points to test for each corner of the car
VECTOR carCorners[4]
carCorners[0] = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvPlayerCar.vehicle, << -fCarWidth/2, fCarLength/2, 0 >>)
carCorners[1] = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvPlayerCar.vehicle, << fCarWidth/2, fCarLength/2, 0 >>)
carCorners[2] = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvPlayerCar.vehicle, << -fCarWidth/2, -fCarLength/2, 0 >>)
carCorners[3] = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvPlayerCar.vehicle, << fCarWidth/2, -fCarLength/2, 0 >>)
// Set up the train's L/R detection boxes for this frame
// VECTOR vTrainLeftBox[2], vTrainRightBox[2]
// FLOAT fTrainBoxWidth, fTrainOffset
// fTrainBoxWidth = 5.0
// fTrainOffset = (naaTrackWidthBox.fWidth + fTrainBoxWidth)/2
// vTrainLeftBox[0] = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvTrain.vehicle, << -fTrainOffset, 8.2, -4.0>>)
// vTrainLeftBox[1] = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvTrain.vehicle, << -fTrainOffset, 16.2, 8.0>>)
// vTrainRightBox[0] = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvTrain.vehicle, << fTrainOffset, 8.2, -4.0>>)
// vTrainRightBox[1] = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvTrain.vehicle, << fTrainOffset, 16.2, 8.0>>)
// INT iCornersLeft
// INT iCornersRight
// iCornersLeft = 0
// iCornersRight = 0
// Test each corner to see if it's in between the rails according to centre box
FOR iCount = 0 TO 3
IF IS_POINT_IN_ANGLED_AREA(carCorners[iCount], vTrainCentreBox[0], vTrainCentreBox[1], naaTrackWidthBox.fWidth)
#IF IS_DEBUG_BUILD IF bDebugTrainTTY CPRINTLN(DEBUG_MISSION, "PLAYER_CAR_IS_PROPERLY_ON_RAILS: Found car corner ", iCount," in train centre box") ENDIF #ENDIF
RETURN TRUE
ENDIF
ENDFOR
/* // Test each corner to see which side of the rails it's on - the rails are directly N-S aligned in the area we are checking
FOR iCount = 0 TO 3
// Test if this corner is off to train's left
IF IS_POINT_IN_ANGLED_AREA(carCorners[iCount], vTrainLeftBox[0], vTrainLeftBox[1], fTrainBoxWidth)
iCornersLeft++
ENDIF
// Test if this corner is off to the train's right
IF IS_POINT_IN_ANGLED_AREA(carCorners[iCount], vTrainRightBox[0], vTrainRightBox[1], fTrainBoxWidth)
iCornersRight++
ENDIF
ENDFOR
// In any case where some of the corner points are on one side of a rail and some on the other,
// then we know the car is at least partly on the actual rails
IF (iCornersLeft > 0 AND iCornersRight < 4)
OR (iCornersRight > 0 AND iCornersLeft < 4)
#IF IS_DEBUG_BUILD IF bDebugTrainTTY CPRINTLN(DEBUG_MISSION, "Found car over rails with corner tests: Left ", iCornersLeft, ", Right ", iCornersRight, ".") ENDIF #ENDIF
RETURN TRUE
ENDIF
#IF IS_DEBUG_BUILD IF bDebugTrainTTY CPRINTLN(DEBUG_MISSION, "Didn't detect car over rails with corner tests: Left ", iCornersLeft, ", Right ", iCornersRight, ".") ENDIF #ENDIF
*/
ENDIF
#IF IS_DEBUG_BUILD IF bDebugTrainTTY CPRINTLN(DEBUG_MISSION, "PLAYER_CAR_IS_PROPERLY_ON_RAILS: Didn't find anything with any checks") ENDIF #ENDIF
RETURN FALSE
ENDFUNC
/*
/// PURPOSE:
/// Check if player is on the railway lines
/// RETURNS:
/// TRUE if player is mucking about on the railway lines
FUNC BOOL PLAYING_ON_RAILWAY_LINES()
VECTOR vTmpCar, vTmpTrackBox[2]
vTmpCar = GET_ENTITY_COORDS(mvPlayerCar.vehicle)
vTmpTrackBox[0] = << 2611.0, vTmpCar.y-2, 30.0 >>
vTmpTrackBox[1] = << 2611.0, vTmpCar.y-30, 20.0 >>
IF IS_ENTITY_IN_ANGLED_AREA(PLAYER_PED_ID(), vTmpTrackBox[0], vTmpTrackBox[1], 1.8)
RETURN TRUE
CPRINTLN(DEBUG_MISSION, "Trevor is playing on the rails!")
ENDIF
RETURN FALSE
ENDFUNC
*/
/// PURPOSE:
/// Checks for a fringe case of the player being on the car roof that isn't correctly detected by the standard improper bail-out checks
/// RETURNS:
/// TRUE if on car roof
FUNC BOOL PLAYER_ON_CAR_ROOF()
VECTOR vPlayerLoc, vCarLoc
FLOAT fDist
vPlayerLoc = GET_ENTITY_COORDS(PLAYER_PED_ID())
vCarLoc = GET_ENTITY_COORDS(mvPlayerCar.vehicle)
fDist = GET_DISTANCE_BETWEEN_COORDS(vCarLoc, vPlayerLoc)
IF IS_ENTITY_TOUCHING_ENTITY(mvPlayerCar.vehicle, PLAYER_PED_ID())
AND vPlayerLoc.z - vCarLoc.z > 0.5
CPRINTLN(DEBUG_MISSION, "Player appears to be on car roof - entities touching")
RETURN TRUE
ELIF fDist < 3
AND vPlayerLoc.z - vCarLoc.z > 0.5
CPRINTLN(DEBUG_MISSION, "Player appears to be on car roof - distance check")
RETURN TRUE
ELSE
CPRINTLN(DEBUG_MISSION, "Player does not appear to be on car roof, car Z ", vCarLoc.z, " - player Z ", vPlayerLoc.z, " - dist ", fDist)
RETURN FALSE
ENDIF
ENDFUNC
/// PURPOSE:
/// Check whether the player is "safe" in bail out stage
/// RETURNS:
/// TRUE if they ought to be OK
FUNC BOOL PLAYER_BAILED_OUT()
// Check invulnerability first - it's a get-out clause for being in the car
IF GET_PLAYER_INVINCIBLE(PLAYER_ID())
CPRINTLN(DEBUG_MISSION, "PLAYER_BAILED_OUT: Player ped is invincible - not continuing with rest of checks")
RETURN TRUE
ENDIF
// Check if player's on car roof
IF PLAYER_ON_CAR_ROOF() // B*1037515
CPRINTLN(DEBUG_MISSION, "PLAYER_BAILED_OUT: Player ped is invincible - not continuing with rest of checks")
RETURN FALSE
ENDIF
IF NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), mvPlayerCar.vehicle, TRUE)
AND NOT (bPlayerNotInCar AND bTrackPlayerTouchingCar[1]) // Handle fringe cases where player is getting back in
CPRINTLN(DEBUG_MISSION, "PLAYER_BAILED_OUT: Player detected as not in car")
RETURN TRUE
ENDIF
// Player must still be in car an not invulnerable
CPRINTLN(DEBUG_MISSION, "PLAYER_BAILED_OUT: Player hasn't bailed out")
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Store and update the speeds for the last few frames, whether the car was on rails, whether player was touching car
PROC TRACK_RAM_SPEEDS(FLOAT fTrainSpeed, FLOAT fCarSpeed)
fTrainRamSavedSpeed[0] = fTrainRamSavedSpeed[1]
fTrainRamSavedSpeed[1] = fTrainSpeed
fCarRamSavedSpeed[0] = fCarRamSavedSpeed[1]
fCarRamSavedSpeed[1] = fCarSpeed
bTrackCarOnRails[0] = bTrackCarOnRails[1]
bTrackCarOnRails[1] = PLAYER_CAR_IS_PROPERLY_ON_RAILS()
bTrackPlayerTouchingCar[0] = bTrackPlayerTouchingCar[1]
bTrackPlayerTouchingCar[1] = IS_ENTITY_TOUCHING_ENTITY(PLAYER_PED_ID(), mvPlayerCar.vehicle)
ENDPROC
/// PURPOSE:
/// Updates a float that determines how far the player was when they were last in the car and moving at an acceptable speed
/// Starts a timer for when the player was last in the car
PROC UPDATE_PLAYER_BAIL_DISTANCE()
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), mvPlayerCar.vehicle)
IF GET_ENTITY_SPEED(mvPlayerCar.vehicle) > 2.0
AND PLAYER_CAR_IS_PROPERLY_ON_RAILS()
FLOAT fTmpDist
fTmpDist = GET_DISTANCE_BETWEEN_ENTITIES(mvPlayerCar.vehicle, mvTrain.vehicle)
IF fTmpDist < fBailDistanceTrack
fBailDistanceTrack = fTmpDist
ENDIF
ENDIF
ELIF NOT bSkinOfTeethTimerStarted
IF GET_ENTITY_SPEED(mvPlayerCar.vehicle) > 2.0
AND PLAYER_CAR_IS_PROPERLY_ON_RAILS()
CPRINTLN(DEBUG_MISSION, "STARTING SKIN OF YOUR TEETH TIMER")
iSkinOfTeethTimer = GET_GAME_TIMER()
bSkinOfTeethTimerStarted = TRUE
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Stops the car on rails sound effect
//PROC STOP_SCRAPE()
// IF bScrapeStarted
// AND NOT bScrapeStopped
// STOP_SOUND(iScrapeID)
// bScrapeStopped = TRUE
// ENDIF
//ENDPROC
// Create blip at boot offset
// Might need this again at some point
//PROC CREATE_BOOT_BLIP()
//
// SAFE_REMOVE_BLIP(biGotoBlip)
// biGotoBlip = CREATE_COORD_BLIP(GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvPlayerCar.vehicle, vecBootOffset, FALSE))
// SET_BLIP_COLOUR(biGotoBlip, BLIP_COLOUR_BLUE)
//
//ENDPROC
//*************************************************************************************************************************************************
//
// :CHECKPOINT PROCS:
//
//*************************************************************************************************************************************************
#IF IS_DEBUG_BUILD
// SKIP PROC ONLY
// Replay the intro cutscene
PROC SKIP_INTRO_CUTSCENE()
REQUEST_MODEL(mvPlayerCar.model)
RC_REQUEST_CUTSCENE("NMT_3_RCM")
REQUEST_STALKER_ANIMS()
// Progression stuff
mStage = MS_INTRO_CUTSCENE // track what mission stage we are at
sProgress = SP_SETUP // used to track what bit of the current mission stage we're at
cccProgress = CCC_WAIT_FOR_DISTANCE
iDestObjDisp = 0 // Don't show the drive to destination objective more than twice
bBackInObjDisplayed = FALSE // Has the get-back-in-vehicle objective been shown
IF IS_AUDIO_SCENE_ACTIVE("NIGEL_03_MIX_SETTINGS")
STOP_AUDIO_SCENE("NIGEL_03_MIX_SETTINGS")
ENDIF
IF NOT IS_AUDIO_SCENE_ACTIVE("NIGEL_03_JOURNEY_MIX")
START_AUDIO_SCENE("NIGEL_03_JOURNEY_MIX")
ENDIF
// Respot player
IF NOT IS_ENTITY_DEAD(PLAYER_PED_ID())
SET_ENTITY_COORDS(PLAYER_PED_ID(), <<-43.6338, -1289.3225, 28.0753>>)
SET_ENTITY_HEADING(PLAYER_PED_ID(), 87.6522)
ENDIF
// Set up the peds
IF bRelGroupExists
REMOVE_RELATIONSHIP_GROUP(relGroupFriendly)
ENDIF
SAFE_DELETE_PED(pedNigel)
// WHILE NOT RC_CREATE_NPC_PED(pedNigel, CHAR_NIGEL, << -44.76, -1289.19, 29.22 >>, -109.3, "RC_NIGEL")
// CPRINTLN(DEBUG_MISSION, "SKIP_INTRO_CUTSCENE: Waiting for Nigel ped to spawn...") ENDIF
// WAIT(0)
// ENDWHILE
// SET_ENTITY_COORDS_NO_OFFSET(pedNigel, << -44.76, -1289.19, 29.22 >>)
SAFE_DELETE_OBJECT(oiHandbag)
SAFE_DELETE_PED(pedMrsThornhill)
bPlayerSpookedStalkers = FALSE
// Remake the vehicle
RC_CleanupScene_VEHICLES(sRCLauncherDataLocal, TRUE)
SAFE_DELETE_PED(pedDiNapoli)
IF DOES_ENTITY_EXIST(mvPlayerCar.vehicle)
REMOVE_VEHICLE_UPSIDEDOWN_CHECK(mvPlayerCar.vehicle)
SAFE_DELETE_VEHICLE(mvPlayerCar.vehicle)
ENDIF
REQUEST_MODEL(mvPlayerCar.model)
WHILE NOT HAS_MODEL_LOADED(mvPlayerCar.model)
WAIT(0)
ENDWHILE
WHILE NOT DOES_ENTITY_EXIST(mvPlayerCar.vehicle)
CPRINTLN(DEBUG_MISSION, "SKIP_INTRO_CUTSCENE: Waiting for car to spawn...")
CREATE_MISSION_VEHICLE(mvPlayerCar.location, mvPlayerCar.heading, FALSE)
ENDWHILE
//Ambient trains should start on
bTrainsAreActive = TRUE
// Begging conversations
bBeggingConvActive = FALSE
iConvoCount = 0
bEnterCarObjectiveDone = FALSE // Has enter car objective fired
bWarnedPlayerNotToRunOff = FALSE // Tracks firing of running off warning if player exits car.
bReleaseIsValid = FALSE // Player is OK to release celeb
bReleaseObjectiveShown = FALSE // Tracks whether player has had release objective
bCelebWasReleased = FALSE // Used for cleaning up celeb model correctly on mission fail
// Release conversations
bReleaseConvStarted = FALSE
// Damage conversations
bDamageConvActive = FALSE
// Stuff to deal with Nigel and Mrs Thornhill hanging around
nagConv = NAG_INITIAL
spRandomCharHandler = SP_SETUP
NMTCleanupState = NMTCS_PLAYER_LEFT
bRandomCharsStillLoaded = TRUE
// Ending section objectives and stuff
bRamStageStarted = FALSE
bTrainRamSpeedUpStarted = FALSE
bTrainObjectiveDone = FALSE
bBailOutObjShown = FALSE
// Reset ending call stuff
bAddedNigelForPhoneConv = FALSE
WAIT(0)
ENDPROC
#ENDIF
// CHECKPOINT 0 - CP_AFTER_INTRO
// Starts after the beginning of the mission, at the point equivalent to when the intro cutscene would have finished
PROC CHECKPOINT_AFTER_INTRO(BOOL bIsACheckpoint = FALSE)
RC_START_Z_SKIP()
IF bIsACheckpoint
START_REPLAY_SETUP(<<-43.6338, -1289.3225, 28.0753>>, 87.6522)
ELSE
// Respot player
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
SET_ENTITY_COORDS(PLAYER_PED_ID(), <<-43.6338, -1289.3225, 28.0753>>)
SET_ENTITY_HEADING(PLAYER_PED_ID(), 87.6522)
ENDIF
ENDIF
REQUEST_MODEL(mvPlayerCar.model)
REQUEST_STALKER_ANIMS()
// Progression stuff
mStage = MS_GET_IN_CAR // track what mission stage we are at
sProgress = SP_SETUP // used to track what bit of the current mission stage we're at
cccProgress = CCC_WAIT_FOR_DISTANCE
iDestObjDisp = 0 // Don't show the drive to destination objective more than twice
bBackInObjDisplayed = FALSE // Has the get-back-in-vehicle objective been shown
IF IS_AUDIO_SCENE_ACTIVE("NIGEL_03_MIX_SETTINGS")
STOP_AUDIO_SCENE("NIGEL_03_MIX_SETTINGS")
ENDIF
IF NOT IS_AUDIO_SCENE_ACTIVE("NIGEL_03_JOURNEY_MIX")
START_AUDIO_SCENE("NIGEL_03_JOURNEY_MIX")
ENDIF
// Set up the peds
IF bRelGroupExists
REMOVE_RELATIONSHIP_GROUP(relGroupFriendly)
ENDIF
RC_CleanupScene_PEDS(sRCLauncherDataLocal, TRUE)
IF NOT IS_ENTITY_ALIVE(pedNigel)
WHILE NOT RC_CREATE_NPC_PED(pedNigel, CHAR_NIGEL, << -44.76, -1289.19, 29.22 >>, -109.3, "RC_NIGEL")
CPRINTLN(DEBUG_MISSION, "CHECKPOINT_AFTER_INTRO: Waiting for Nigel ped to spawn...")
WAIT(0)
ENDWHILE
ENDIF
SET_ENTITY_COORDS_NO_OFFSET(pedNigel, << -44.76, -1289.19, 29.22 >>, FALSE) // Always position, will not be on ground if newly spawned
SET_ENTITY_HEADING(pedNigel, -109.3)
IF NOT IS_ENTITY_ALIVE(pedMrsThornhill)
WHILE NOT RC_CREATE_NPC_PED(pedMrsThornhill, CHAR_MRS_THORNHILL, << -45.11, -1289.77, 29.24 >>, -96.8, "RC_MRS_THORNHILL")
CPRINTLN(DEBUG_MISSION, "CHECKPOINT_AFTER_INTRO: Waiting for Mrs Thornhill ped to spawn...")
WAIT(0)
ENDWHILE
ENDIF
SET_ENTITY_COORDS_NO_OFFSET(pedMrsThornhill, << -45.11, -1289.77, 29.24 >>) // Always position, will not be on ground if newly spawned
SET_ENTITY_HEADING(pedMrsThornhill, -96.8)
bPlayerSpookedStalkers = FALSE
// Anims
WHILE NOT STALKER_ANIM_DICTS_LOADED()
WAIT(0)
ENDWHILE
IF IS_ENTITY_ALIVE(pedNigel) AND IS_ENTITY_ALIVE(pedMrsThornhill)
SETUP_NIGEL_ANIMS()
SETUP_MRST_ANIMS()
ENDIF
//CLEAR_NMT_ANIMS()
// Remake the vehicle
RC_CleanupScene_VEHICLES(sRCLauncherDataLocal, TRUE)
SAFE_DELETE_PED(pedDiNapoli)
SAFE_DELETE_VEHICLE(mvPlayerCar.vehicle)
WHILE NOT HAS_MODEL_LOADED(mvPlayerCar.model)
WAIT(0)
ENDWHILE
WHILE NOT DOES_ENTITY_EXIST(mvPlayerCar.vehicle)
CPRINTLN(DEBUG_MISSION, "CHECKPOINT_AFTER_INTRO: Waiting for car to spawn...")
CREATE_MISSION_VEHICLE(mvPlayerCar.location, mvPlayerCar.heading, FALSE)
ENDWHILE
// Reset camera behind player
SET_GAMEPLAY_CAM_RELATIVE_PITCH(0.0)
SET_GAMEPLAY_CAM_RELATIVE_HEADING(0.0)
// Start vehicle upsidedown check
IF IS_ENTITY_ALIVE(mvPlayerCar.vehicle)
ADD_VEHICLE_UPSIDEDOWN_CHECK(mvPlayerCar.vehicle)
ENDIF
//Ambient trains should start on
bTrainsAreActive = TRUE
// Begging conversations
bBeggingConvActive = FALSE
iConvoCount = 0
bEnterCarObjectiveDone = FALSE // Has enter car objective fired
bWarnedPlayerNotToRunOff = FALSE // Tracks firing of running off warning if player exits car.
bReleaseIsValid = FALSE // Player is OK to release celeb
bReleaseObjectiveShown = FALSE // Tracks whether player has had release objective
bCelebWasReleased = FALSE // Used for cleaning up celeb model correctly on mission fail
// Release conversations
bReleaseConvStarted = FALSE
// Damage conversations
bDamageConvActive = FALSE
// Stuff to deal with Nigel and Mrs Thornhill hanging around
nagConv = NAG_INITIAL
spRandomCharHandler = SP_SETUP
NMTCleanupState = NMTCS_PLAYER_LEFT
bRandomCharsStillLoaded = TRUE
// Ending section objectives and stuff
bRamStageStarted = FALSE
bTrainRamSpeedUpStarted = FALSE
bTrainObjectiveDone = FALSE
bBailOutObjShown = FALSE
// Reset ending call stuff
bAddedNigelForPhoneConv = FALSE
WAIT(0)
IF bIsACheckpoint
WHILE NOT HAS_ADDITIONAL_TEXT_LOADED(MISSION_TEXT_SLOT)
WAIT(0)
ENDWHILE
END_REPLAY_SETUP()
ENDIF
RC_END_Z_SKIP()
ENDPROC
// CHECKPOINT 1 - CP_TRAIN
// Sets car on rails facing towards bridge
PROC CHECKPOINT_TRAIN(BOOL bIsACheckpoint = FALSE)
RC_START_Z_SKIP()
// Clear up trains BEFORE anything that needs waits - B*980179
// Ambient trains should have been set off prior to reaching this point
SAFE_DELETE_PED(mvTrain.driver)
IF DOES_ENTITY_EXIST(mvTrain.vehicle)
DELETE_MISSION_TRAIN(mvTrain.vehicle)
ENDIF
SET_RANDOM_TRAINS(FALSE)
DELETE_ALL_TRAINS()
bTrainsAreActive = FALSE
// Now safe to respawn player
IF bIsACheckpoint
START_REPLAY_SETUP(vecDestination, 0.0)
ELSE
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
SET_ENTITY_COORDS(PLAYER_PED_ID(), vecDestination)
ENDIF
ENDIF
// Remove unnecessary launcher peds
IF bRelGroupExists
REMOVE_RELATIONSHIP_GROUP(relGroupFriendly)
ENDIF
RC_CleanupScene_PEDS(sRCLauncherDataLocal, TRUE)
SAFE_DELETE_PED(pedNigel)
SAFE_DELETE_OBJECT(oiHandbag)
SAFE_DELETE_PED(pedMrsThornhill)
SET_MODEL_AS_NO_LONGER_NEEDED(IG_NIGEL)
SET_MODEL_AS_NO_LONGER_NEEDED(IG_MRS_THORNHILL)
bRandomCharsStillLoaded = FALSE
// Recreate the car in the right place
SAFE_DELETE_PED(pedDiNapoli)
RC_CleanupScene_VEHICLES(sRCLauncherDataLocal, TRUE)
SAFE_DELETE_VEHICLE(mvPlayerCar.vehicle)
REQUEST_MODEL(mvPlayerCar.model)
WHILE NOT DOES_ENTITY_EXIST(mvPlayerCar.vehicle)
CREATE_MISSION_VEHICLE(vecDestination, 0.0)
WAIT(0)
ENDWHILE
IF IS_ENTITY_ALIVE(mvPlayerCar.vehicle)
ADD_VEHICLE_UPSIDEDOWN_CHECK(mvPlayerCar.vehicle)
SET_VEHICLE_ENGINE_ON(mvPlayerCar.vehicle, TRUE, TRUE)
ENDIF
// Handle Al
WHILE NOT DOES_ENTITY_EXIST(pedDiNapoli)
SPAWN_CELEB_IN_BOOT()
WAIT(0)
ENDWHILE
// Request additional train models
bRequestedTrainModels = FALSE
iConvoCount = 2 // Set en-route conversations as all completed
cccProgress = CCC_CONVERSATIONS_DONE
bEnterCarObjectiveDone = TRUE // Has enter car objective fired
// Ending section objectives
bRamStageStarted = FALSE
bTrainRamSpeedUpStarted = FALSE
bTrainObjectiveDone = FALSE
bBailOutObjShown = FALSE
// Reset ending call stuff
bAddedNigelForPhoneConv = FALSE
// Mission progress
mStage = MS_RAM_TRAIN
sProgress = SP_SETUP
IF bIsACheckpoint
WHILE NOT HAS_ADDITIONAL_TEXT_LOADED(MISSION_TEXT_SLOT)
WAIT(0)
ENDWHILE
END_REPLAY_SETUP(mvPlayerCar.vehicle, VS_DRIVER, FALSE)
ELSE
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
AND IS_ENTITY_ALIVE(mvPlayerCar.vehicle)
SAFE_SET_PED_INTO_VEHICLE(PLAYER_PED_ID(), mvPlayerCar.vehicle)
ENDIF
WAIT_FOR_WORLD_TO_LOAD(vecDestination)
ENDIF
IF IS_ENTITY_ALIVE(mvPlayerCar.vehicle)
SET_VEHICLE_FORWARD_SPEED(mvPlayerCar.vehicle, 10.0)
ENDIF
RC_END_Z_SKIP()
ENDPROC
// CHECKPOINT 2 - CP_OUTRO_CALL
// Set up as if successfully rammed train
PROC CHECKPOINT_OUTRO_CALL(BOOL bIsACheckpoint = FALSE)
RC_START_Z_SKIP()
// Respawn the player
IF bIsACheckpoint
START_REPLAY_SETUP(<<2605.4436, 1890.1893, 26.1751>>, 196.5)
ELSE
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
SET_ENTITY_COORDS(PLAYER_PED_ID(), <<2605.4436, 1890.1893, 26.1751>>)
SET_ENTITY_HEADING(PLAYER_PED_ID(), 196.5)
ENDIF
ENDIF
IF IS_AUDIO_SCENE_ACTIVE("NIGEL_03_MIX_SETTINGS")
STOP_AUDIO_SCENE("NIGEL_03_MIX_SETTINGS")
ENDIF
IF NOT IS_AUDIO_SCENE_ACTIVE("NIGEL_03_JOURNEY_MIX")
START_AUDIO_SCENE("NIGEL_03_JOURNEY_MIX")
ENDIF
SET_MODEL_AS_NO_LONGER_NEEDED(IG_NIGEL)
SET_MODEL_AS_NO_LONGER_NEEDED(IG_MRS_THORNHILL)
// Recreate the car in the right place
REQUEST_MODEL(mvPlayerCar.model)
WHILE NOT DOES_ENTITY_EXIST(mvPlayerCar.vehicle)
CREATE_MISSION_VEHICLE(<<2621.2988, 1868.0214, 26.4687>>, 60.4)
WAIT(0)
ENDWHILE
IF IS_ENTITY_ALIVE(mvPlayerCar.vehicle)
EXPLODE_VEHICLE_IN_CUTSCENE(mvPlayerCar.vehicle)
ENDIF
//Ambient trains should have been set off prior to reaching this point
SET_RANDOM_TRAINS(FALSE)
SAFE_DELETE_PED(mvTrain.driver)
DELETE_ALL_TRAINS()
bTrainsAreActive = FALSE
// Ending call stuff
bAddedNigelForPhoneConv = FALSE
// Mission progress
mStage = MS_ENDING_PHONE_CALL
sProgress = SP_SETUP
IF bIsACheckpoint
WHILE NOT HAS_ADDITIONAL_TEXT_LOADED(MISSION_TEXT_SLOT)
WAIT(0)
ENDWHILE
END_REPLAY_SETUP()
ELSE
WAIT_FOR_WORLD_TO_LOAD(<<2605.4436, 1890.1893, 26.1751>>)
ENDIF
WAIT(1000) // Wait for car explosion to finish
RC_END_Z_SKIP()
ENDPROC
//*************************************************************************************************************************************************
//
// :SKIP PROCS:
//
//*************************************************************************************************************************************************
// Used to get rid of any prints
PROC CLEAR_ALL_MISSION_OBJECTIVES()
CLEAR_PRINTS()
CLEAR_HELP(TRUE)
ENDPROC
#IF IS_DEBUG_BUILD
// Clear wanted
PROC SKIP_CLEAR_WANTED()
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
IF GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) > 0
SET_PLAYER_WANTED_LEVEL(PLAYER_ID(), 0)
SET_PLAYER_WANTED_LEVEL_NOW(PLAYER_ID())
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Fake the hit state
PROC SKIP_HIT_TRAIN_CLEANUP()
SAFE_REMOVE_BLIP(biGotoBlip)
CLEAR_PRINTS()
// Delete train if it exists
IF DOES_ENTITY_EXIST(mvTrain.vehicle)
DELETE_MISSION_TRAIN(mvTrain.vehicle)
ENDIF
// Handle Al
SAFE_DELETE_PED(pedDiNapoli)
// Remove Trevor from car, delete it if it exists
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
SET_ENTITY_COORDS(PLAYER_PED_ID(), << 2622.4365, 1879.8334, 26.6709 >>)
SET_ENTITY_HEADING(PLAYER_PED_ID(), 358.4)
ENDIF
IF DOES_ENTITY_EXIST(mvPlayerCar.vehicle)
REMOVE_VEHICLE_UPSIDEDOWN_CHECK(mvPlayerCar.vehicle)
ENDIF
SAFE_DELETE_VEHICLE(mvPlayerCar.vehicle)
iConvoCount = 2 // Set en-route conversations as all completed
bEnterCarObjectiveDone = TRUE // Has enter car objective fired
// Ending section objectives
bRamStageStarted = TRUE
bTrainRamSpeedUpStarted = TRUE
bTrainObjectiveDone = TRUE
bBailOutObjShown = TRUE
bReleaseObjectiveShown = TRUE // Stops God text from momentarily popping up
// Reset ending call stuff
bAddedNigelForPhoneConv = FALSE
SET_REPLAY_MID_MISSION_STAGE_WITH_NAME(CP_TRAIN, "Train section", TRUE)
ENDPROC
// This launches all the skip procs
PROC JUMP_TO_STAGE(MISSION_SKIP_STAGE targetStage)
RC_START_Z_SKIP()
// Force train cam cleanup
TRAIN_CINEMATIC_CAM(TRUE)
// Clean up any active cutscene stuff - scripted
IF mStage = MS_RELEASE_CUTSCENE
OR mStage = MS_OUTRO_CUTSCENE
OR mStage = MS_DEBUG_CRASHES_FOREVER
// Clean up the cameras etc. and restore HUD elements
RENDER_SCRIPT_CAMS(FALSE, FALSE)
RC_END_CUTSCENE_MODE()
ENDIF
// Stop mocap cutscene
IF IS_CUTSCENE_ACTIVE()
STOP_CUTSCENE()
WHILE IS_CUTSCENE_ACTIVE()
WAIT(0)
ENDWHILE
// Return player control
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
SET_PLAYER_CONTROL(PLAYER_ID(), TRUE)
ENDIF
ENDIF
// Kill conversations/phone
IF IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
KILL_FACE_TO_FACE_CONVERSATION_DO_NOT_FINISH_LAST_LINE()
ENDIF
IF IS_PHONE_ONSCREEN()
HANG_UP_AND_PUT_AWAY_PHONE()
ENDIF
SKIP_CLEAR_WANTED()
// STOP_SCRAPE()
SWITCH targetStage
CASE MSS_INTRO
SKIP_INTRO_CUTSCENE()
BREAK
CASE MSS_RESTART
CHECKPOINT_AFTER_INTRO()
BREAK
CASE MSS_TRAIN
CHECKPOINT_TRAIN()
BREAK
CASE MSS_OUTRO_CALL
mStage = MS_ENDING_PHONE_CALL
sProgress = SP_SETUP
cccProgress = CCC_CONVERSATIONS_DONE
SKIP_HIT_TRAIN_CLEANUP()
BREAK
ENDSWITCH
RC_END_Z_SKIP()
ENDPROC
// Handles skipping forward/backward
PROC DO_A_SKIP(INT iSkipType)
SWITCH iSkipType
CASE SKIP_FORWARD
SWITCH mStage
CASE MS_LEADIN
JUMP_TO_STAGE(MSS_RESTART)
BREAK
CASE MS_INTRO_CUTSCENE
IF IS_CUTSCENE_ACTIVE()
WAIT_FOR_CUTSCENE_TO_STOP()
ELSE
JUMP_TO_STAGE(MSS_RESTART)
ENDIF
BREAK
CASE MS_GET_IN_CAR
CASE MS_DRIVING
CASE MS_PLAYER_EXITS_CAR
JUMP_TO_STAGE(MSS_TRAIN)
BREAK
CASE MS_RAM_TRAIN
JUMP_TO_STAGE(MSS_OUTRO_CALL)
BREAK
CASE MS_LOSE_COPS
SKIP_CLEAR_WANTED()
BREAK
ENDSWITCH
BREAK
CASE SKIP_BACKWARD
SWITCH mStage
CASE MS_LEADIN
CASE MS_INTRO_CUTSCENE
// Nothing
BREAK
CASE MS_GET_IN_CAR
JUMP_TO_STAGE(MSS_INTRO)
BREAK
CASE MS_DRIVING
JUMP_TO_STAGE(MSS_RESTART)
BREAK
CASE MS_PLAYER_EXITS_CAR
IF bRamStageStarted
JUMP_TO_STAGE(MSS_TRAIN)
ELSE
JUMP_TO_STAGE(MSS_RESTART)
ENDIF
BREAK
CASE MS_RAM_TRAIN
IF IS_ENTITY_IN_RANGE_COORDS(mvPlayerCar.vehicle, vecDestination, 12.0)
// AND NOT bTrainObjectiveDone
JUMP_TO_STAGE(MSS_RESTART)
ELSE
JUMP_TO_STAGE(MSS_TRAIN)
ENDIF
BREAK
CASE MS_MISSED_RAM
CASE MS_ENDING_PHONE_CALL
JUMP_TO_STAGE(MSS_TRAIN)
BREAK
CASE MS_LOSE_COPS
SKIP_CLEAR_WANTED()
BREAK
ENDSWITCH
BREAK
ENDSWITCH
ENDPROC
#ENDIF
// ===========================================================================================================
// Termination
// ===========================================================================================================
// -----------------------------------------------------------------------------------------------------------
// Script Cleanup
// -----------------------------------------------------------------------------------------------------------
PROC Script_Cleanup()
// Ensure launcher is cleaned up
RC_CLEANUP_LAUNCHER()
// If the mission was triggered then additional mission cleanup will be required.
IF (Random_Character_Cleanup_If_Triggered())
CPRINTLN(DEBUG_MISSION, "...Random Character Script was triggered so additional cleanup required")
ENDIF
//Cleanup the scene created by the launcher
RC_CleanupSceneEntities(sRCLauncherDataLocal)
IF DOES_ENTITY_EXIST(mvPlayerCar.vehicle)
REMOVE_VEHICLE_UPSIDEDOWN_CHECK(mvPlayerCar.vehicle)
SET_VEHICLE_AS_NO_LONGER_NEEDED(mvPlayerCar.vehicle)
ENDIF
IF IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
STOP_SCRIPTED_CONVERSATION(TRUE)
ENDIF
// Check that random trains are on
SET_RANDOM_TRAINS(TRUE)
// Get rid of any scenario blockers
CREATE_SCENARIO_BLOCKING_AREAS(FALSE)
// Blip purge
SAFE_REMOVE_BLIP(biGotoBlip)
// Remove Al if he is still in the boot to handle force cleanup
IF DOES_ENTITY_EXIST(pedDiNapoli)
AND NOT bCelebWasReleased
SAFE_DELETE_PED(pedDiNapoli)
ENDIF
SET_PED_MODEL_IS_SUPPRESSED(IG_NIGEL, FALSE)
SET_PED_MODEL_IS_SUPPRESSED(IG_MRS_THORNHILL, FALSE)
SET_PED_MODEL_IS_SUPPRESSED(U_M_M_ALDINAPOLI, FALSE)
// Get rid of stalker anims
IF bRequestedStalkerAnims
REMOVE_ANIM_DICT("amb@world_human_stand_impatient@female@no_sign@idle_a")
REMOVE_ANIM_DICT("amb@world_human_stand_impatient@male@no_sign@idle_a")
ENDIF
SAFE_RELEASE_OBJECT(oiDoor)
// Reenable fire service
ENABLE_DISPATCH_SERVICE(DT_FIRE_DEPARTMENT, TRUE)
IF bRelGroupExists
REMOVE_RELATIONSHIP_GROUP(relGroupFriendly)
ENDIF
#IF IS_DEBUG_BUILD
// Widgets
IF DOES_WIDGET_GROUP_EXIST(widgetGroup)
DELETE_WIDGET_GROUP(widgetGroup)
ENDIF
#ENDIF
TOGGLE_CAR_MOD_SHOPS_UNAVAILABLE(FALSE)
SET_CINEMATIC_BUTTON_ACTIVE(TRUE)
SET_INSTANCE_PRIORITY_HINT(INSTANCE_HINT_NONE)
TERMINATE_THIS_THREAD()
ENDPROC
// -----------------------------------------------------------------------------------------------------------
// Script Pass
// -----------------------------------------------------------------------------------------------------------
PROC Script_Passed()
IF NOT bCelebWasReleased
SAFE_DELETE_PED(pedDiNapoli)
SET_MISSION_FLOW_FLAG_STATE(FLOWFLAG_NIGEL3_AL_DI_NAPOLI_KILLED, TRUE)
STAT_SET_BOOL(SP_KILLED_AL, TRUE)
ENDIF
IF NOT bTrainsAreActive
SET_RANDOM_TRAINS(TRUE)
ENDIF
// Mark train as not needed
IF DOES_ENTITY_EXIST(mvTrain.vehicle)
SAFE_RELEASE_PED(mvTrain.driver)
SET_MISSION_TRAIN_AS_NO_LONGER_NEEDED(mvTrain.vehicle)
ENDIF
Random_Character_Passed(CP_RAND_C_NIG3)
Script_Cleanup()
ENDPROC
//*************************************************************************************************************************************************
//
// :DATA INIT:
//
//*************************************************************************************************************************************************
PROC DATA_INIT()
CPRINTLN(DEBUG_MISSION, "DATA_INIT()")
SETUP_MISSION_VEHICLE(mvPlayerCar, GET_NIGEL_VEHICLE_MODEL(), <<-39.9698, -1285.9698,28.8324>>, -178.98)
SETUP_MISSION_VEHICLE(mvTrain, FREIGHT, <<2574.2910, 2173.5510, 31.4003>>, 210.2)
mvTrain.driverModel = S_M_M_LSMETRO_01
//SETUP_MISSION_VEHICLE(mvTrain, FREIGHT, << 2611.0730, 2060.6868, 31.2598 >>, 180)
REQUEST_MODEL(IG_NIGEL)
REQUEST_MODEL(IG_MRS_THORNHILL)
REQUEST_MODEL(mvPlayerCar.model) // This HAS to be requested now, and should be loaded anyway
REQUEST_MODEL(U_M_M_ALDINAPOLI) // Celeb model
REQUEST_MODEL(PROP_LD_HANDBAG_S)
SET_PED_MODEL_IS_SUPPRESSED(IG_NIGEL, TRUE)
SET_PED_MODEL_IS_SUPPRESSED(IG_MRS_THORNHILL, TRUE)
SET_PED_MODEL_IS_SUPPRESSED(U_M_M_ALDINAPOLI, TRUE)
REQUEST_ADDITIONAL_TEXT("NIGEL3", MISSION_TEXT_SLOT)
// Silently add Nigel as contact in case we've launched from debug menu
ADD_CONTACT_TO_PHONEBOOK(CHAR_NIGEL, TREVOR_BOOK, FALSE)
#IF IS_DEBUG_BUILD
// Z menu text labels
mSkipMenu[0].sTxtLabel = "Intro cutscene"
mSkipMenu[1].sTxtLabel = "After cutscene (CP1)"
mSkipMenu[2].sTxtLabel = "Train (CP2)"
mSkipMenu[3].sTxtLabel = "Post-train phone call"
IF NOT DOES_WIDGET_GROUP_EXIST(widgetGroup)
CPRINTLN(DEBUG_MISSION, "Creating widget group - look for Nigel 3 widgets under Scripts")
widgetGroup = START_WIDGET_GROUP("Nigel 3")
ADD_WIDGET_BOOL("Toggle scripted invulnerability", bDebugToggleInvuln)
ADD_WIDGET_BOOL("Debug draw all the train safe zone boxes", bDebugDrawTrainCamZones)
ADD_WIDGET_BOOL("Show the bail-out distance/time on cutscene", bDebugShowBailDistanceOnCustscene)
ADD_WIDGET_INT_SLIDER("Change the time limit for Skin Of Your Teeth stat", iSkinOfTeethLimit, 250, 5000, 50)
ADD_WIDGET_BOOL("TTY Toggle - En-route event setups debug info", bDebugEventTTY)
ADD_WIDGET_BOOL("TTY Toggle - Train debug info", bDebugTrainTTY)
ADD_WIDGET_FLOAT_SLIDER("Dodgy slope incline if pitch greater than:", fDodgyPitchAngle, 0, 45, 0.5)
ADD_WIDGET_FLOAT_SLIDER("Dodgy slope incline if roll greater than:", fDodgyRollAngle, 0, 45, 0.5)
ADD_WIDGET_BOOL("Reattach Thornhill bag", bDebugReattach)
ADD_WIDGET_VECTOR_SLIDER("Bag attachment offset", vecBagOffset, -1, 1, 0.01)
ADD_WIDGET_VECTOR_SLIDER("Bag attachment rotation", vecBagAngles, -180, 180, 0.1)
STOP_WIDGET_GROUP()
ENDIF
#ENDIF
CLEAR_BIT(g_savedGlobals.sRandomChars.g_iWebsiteQueryBit, ENUM_TO_INT(RC_NWS_NGL3_DINAPOLI_NOT_KILLED_BY_TRAIN)) // ensure this bit is initially clear when starting the mission
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
ADD_PED_FOR_DIALOGUE(mConversationStruct, CONVPED_TREV, PLAYER_PED_ID(), "TREVOR")
ENDIF
// Prep conversations structs
SETUP_NAGGING_CONVERSATION_INFO()
SETUP_BEGGING_CONVERSATION_INFO()
iConvoCount = 0
// Get sounds
REQUEST_SCRIPT_AUDIO_BANK("CAR_TRUNK_THUMPS")
iThumpID = GET_SOUND_ID()
iCrashID = GET_SOUND_ID()
iTrainComing = GET_SOUND_ID()
// Scenario blocking areas
vecScenario[0][0] = << -92, -1271, 25 >> // Start area prostitutes and car that sometimes drives a scenario backwards
vecScenario[0][1] = << -27, -1247, 30 >>
vecScenario[1][0] = << 2649.08, 1643.26, 20 >> // Parking scenarios at power plant (N)
vecScenario[1][1] = << 2688.73, 1675.07, 30 >>
vecScenario[2][0] = << 2649.08, 1608.60, 20 >> // Parking scenarios at power plant (S)
vecScenario[2][1] = << 2692.73, 1640.40, 30 >>
// Store the destination box size
naaTrainTrackBox.vEnds[0] = << 2611.25, 1800.09, 25.40 >>
naaTrainTrackBox.vEnds[1] = << 2611.25, 1950.31, 29.68 >>
naaTrainTrackBox.fWidth = 3.0
// Store train track detection box size
naaTrackWidthBox.vEnds[0] = << 2611.00, 1630.00, 24.61 >>
naaTrackWidthBox.vEnds[1] = << 2611.28, 1856.65, 30.73 >>
naaTrackWidthBox.fWidth = 1.75
// Store train cinematic cam validation volumes - these are ordered so that the check that uses them
naaTrainCamBox[0].vEnds[0] = << 2611.00, 1601.95, 31.48 >> // First box is skinny, right up rails one
naaTrainCamBox[0].vEnds[1] = << 2611.00, 2050.00, 24.36 >>
naaTrainCamBox[0].fWidth = 6.5
naaTrainCamBox[1].vEnds[0] = << 2611.00, 1786.85, 34.44 >> // Second box is wider one cover either side of rails once over bridge
naaTrainCamBox[1].vEnds[1] = << 2611.00, 2050.00, 22.22 >>
naaTrainCamBox[1].fWidth = 30.0
naaTrainCamBox[2].vEnds[0] = << 2625.47, 1707.78, 29.52 >> // Third box covers side route over bridge
naaTrainCamBox[2].vEnds[1] = << 2611.65, 1749.99 , 23.39>>
naaTrainCamBox[2].fWidth = 8.0
naaTrainCamBox[3].vEnds[0] = << 2617.39, 1709.61, 29.56 >> // Fourth box covers bit between the two tracks on the bridge
naaTrainCamBox[3].vEnds[1] = << 2611.65, 1749.99, 23.39>>
naaTrainCamBox[3].fWidth = 8.0
// keeping the original shapetest values for reference
//vecBootBlockOffset = << 0.0, -3.45, 0.8 >> //<< 0.0, -3.35, 0.8 >>
//vecBootBlockDimensions = << 2.3, 2.0, 2.0 >>
// Boot blocking shapetest stuff
vecBootBlockOffset[0] = << 0.0, -3.45, 1.15 >> //<< 0.0, -3.35, 0.8 >>
vecBootBlockOffset[1] = << 0.0, -3.45, 0.15 >> //<< 0.0, -3.35, 0.8 >>
vecBootBlockDimensions[0] = << 2.0, 2.0, 1.3 >>
vecBootBlockDimensions[1] = << 1.6, 2.0, 0.7 >>
// Boot block tests to see if we can use full camera
vecBootBlockOffset[2] = << 0.0, -5.45, 1.15 >>
vecBootBlockOffset[3] = << 0.0, -5.45, 0.15 >>
vecBootBlockDimensions[2] = << 1.5, 6.0, 1.3 >>
vecBootBlockDimensions[3] = << 1.2, 6.0, 0.7 >>
// Track loading of stalker anims
bRequestedStalkerAnims = FALSE
TOGGLE_CAR_MOD_SHOPS_UNAVAILABLE(TRUE) // Toggle off all car mod shops
CREATE_SCENARIO_BLOCKING_AREAS(TRUE)
sFailReason = "DEFAULT"
bRequestedTrainModels = FALSE
ENDPROC
//*************************************************************************************************************************************************
//
// :STAGE PROCS:
//
//*************************************************************************************************************************************************
/// PURPOSE:
/// Mission stage PROC
/// Required in this script to ensure we load the cutscene on a debug skip to the beginning
PROC STAGE_INIT()
IF IS_AUDIO_SCENE_ACTIVE("NIGEL_03_MIX_SETTINGS")
STOP_AUDIO_SCENE("NIGEL_03_MIX_SETTINGS")
ENDIF
IF NOT IS_AUDIO_SCENE_ACTIVE("NIGEL_03_JOURNEY_MIX")
START_AUDIO_SCENE("NIGEL_03_JOURNEY_MIX")
ENDIF
REQUEST_STALKER_ANIMS()
REQUEST_CUTSCENE("NMT_3_RCM")
// IF bDoneCheckpoints = TRUE
// OR NOT Is_Replay_In_Progress()
// OR IS_REPEAT_PLAY_ACTIVE()
// RC_REQUEST_CUTSCENE("NMT_3_RCM")
// ENDIF
RC_PLAYER_TRIGGER_SCENE_LOCK_IN()
mStage = MS_LEADIN
sProgress = SP_SETUP
CPRINTLN(DEBUG_MISSION, "STAGE_INIT go to STAGE_LEADIN")
ENDPROC
/// PURPOSE:
/// MISSION STAGE PROC
/// Waits for Nigel's anim to reach end of loop
PROC STAGE_LEADIN()
RC_PLAYER_TRIGGER_SCENE_LOCK_IN()
IF IS_ENTITY_ALIVE(pedNigel)
IF IS_ENTITY_PLAYING_ANIM(pedNigel, "rcmnigel3", "base")
FLOAT fPhase
fPhase = GET_ENTITY_ANIM_CURRENT_TIME(pedNigel, "rcmnigel3", "base")
IF fPhase > 0.97 OR fPhase < 0.03
CPRINTLN(DEBUG_MISSION, "STAGE_LEADIN launching cutscene on phase synch")
mStage = MS_INTRO_CUTSCENE
sProgress = SP_SETUP
ELIF IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedNigel, 2.5)
CPRINTLN(DEBUG_MISSION, "STAGE_LEADIN launching cutscene on player proximity")
mStage = MS_INTRO_CUTSCENE
sProgress = SP_SETUP
ENDIF
ELIF IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedNigel, 5.0)
CPRINTLN(DEBUG_MISSION, "STAGE_LEADIN launching cutscene on increased player proximity range, Nigel wasn't playing an anim")
mStage = MS_INTRO_CUTSCENE
sProgress = SP_SETUP
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Mission stage PROC
/// Load/play intro cutscene
PROC STAGE_INTRO_CUTSCENE()
SWITCH sProgress
CASE SP_SETUP
RC_REQUEST_CUTSCENE("NMT_3_RCM")
IF RC_IS_CUTSCENE_OK_TO_START()
//Block mission title on cutscene start
BLOCK_MISSION_TITLE(TRUE)
IF IS_REPEAT_PLAY_ACTIVE()
RC_END_Z_SKIP()
ENDIF
IF IS_ENTITY_ALIVE(pedNigel)
REGISTER_ENTITY_FOR_CUTSCENE(pedNigel, "Nigel", CU_ANIMATE_EXISTING_SCRIPT_ENTITY)
CPRINTLN(DEBUG_MISSION, "STAGE_INTRO_CUTSCENE: SP_SETUP: Nigel existed at start of cutscene, registering")
ELSE
REGISTER_ENTITY_FOR_CUTSCENE(pedNigel, "Nigel", CU_CREATE_AND_ANIMATE_NEW_SCRIPT_ENTITY, IG_NIGEL)
CPRINTLN(DEBUG_MISSION, "STAGE_INTRO_CUTSCENE: SP_SETUP: Nigel set to be spawned by cutscene")
ENDIF
IF IS_ENTITY_ALIVE(pedMrsThornhill)
REGISTER_ENTITY_FOR_CUTSCENE(pedMrsThornhill, "MRS_Thornhill", CU_ANIMATE_EXISTING_SCRIPT_ENTITY, IG_MRS_THORNHILL)
CPRINTLN(DEBUG_MISSION, "STAGE_INTRO_CUTSCENE: SP_SETUP: Mrs Thornhill existed at start of cutscene, registering")
ELSE
REGISTER_ENTITY_FOR_CUTSCENE(pedMrsThornhill, "MRS_Thornhill", CU_CREATE_AND_ANIMATE_NEW_SCRIPT_ENTITY, IG_MRS_THORNHILL)
CPRINTLN(DEBUG_MISSION, "STAGE_INTRO_CUTSCENE: SP_SETUP: Mrs Thornhill set to be spawned by cutscene")
ENDIF
// Get hold of the door
IF NOT DOES_ENTITY_EXIST(oiDoor)
oiDoor = GET_CLOSEST_OBJECT_OF_TYPE(<< -45.93, -1290.67, 29.67 >>, 10.0, v_ilev_gc_door01)
ENDIF
// Cleanup launcher to remove lead-in blip
RC_CLEANUP_LAUNCHER()
REPLAY_START_EVENT(REPLAY_IMPORTANCE_LOW)
// Start mocap scene
START_CUTSCENE()
WAIT(0)
CLEAR_NIGEL3_CUTSCENE_AREA()
RC_START_CUTSCENE_MODE(<< -44.75, -1288.67, 28.21 >>, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT)
// Additional clear area for excessive parked cars in alleyway
CLEAR_AREA_OF_VEHICLES(<< 20.6534, -1302.8673, 28.0513 >>, 15.0)
// Remake or get hold of the player car from script
IF DOES_ENTITY_EXIST(sRCLauncherDataLocal.vehID[0])
CPRINTLN(DEBUG_MISSION, "Deleting and recreating car...")
SAFE_DELETE_VEHICLE(sRCLauncherDataLocal.vehID[0])
STOP_FIRE_IN_RANGE(mvPlayerCar.location, 10.0)
ENDIF
IF NOT DOES_ENTITY_EXIST(mvPlayerCar.vehicle)
CREATE_MISSION_VEHICLE(mvPlayerCar.location, mvPlayerCar.heading, TRUE)
ENDIF
SET_CAN_AUTO_VAULT_ON_ENTITY(mvPlayerCar.vehicle, FALSE)
// Hide the door
IF IS_ENTITY_ALIVE(oiDoor)
SET_ENTITY_VISIBLE(oiDoor, FALSE)
ENDIF
// Clear up launcher entities
RC_CleanupSceneEntities(sRCLauncherDataLocal, FALSE)
// Progress to waiting for cutscene to finish
sProgress = SP_RUNNING
ENDIF
BREAK
CASE SP_RUNNING
// Do Mrs Thornhills setup, exit state etc.
IF NOT DOES_ENTITY_EXIST(pedMrsThornhill)
IF DOES_ENTITY_EXIST(GET_ENTITY_INDEX_OF_REGISTERED_ENTITY("MRS_Thornhill"))
pedMrsThornhill = GET_PED_INDEX_FROM_ENTITY_INDEX(GET_ENTITY_INDEX_OF_REGISTERED_ENTITY("MRS_Thornhill"))
CPRINTLN(DEBUG_MISSION, "STAGE_INTRO_CUTSCENE: SP_RUNNING: Mrs Thornhill was grabbed while cutscene was running")
ENDIF
ELIF bNotSetUpMrsTAnims
AND HAS_ANIM_DICT_LOADED(sNMTAnimDict)
IF CAN_SET_EXIT_STATE_FOR_REGISTERED_ENTITY("MRS_Thornhill", IG_MRS_THORNHILL)
SETUP_MRST_ANIMS()
ENDIF
ENDIF
// Do Nigel's setup, exit state etc.
IF NOT DOES_ENTITY_EXIST(pedNigel)
IF DOES_ENTITY_EXIST(GET_ENTITY_INDEX_OF_REGISTERED_ENTITY("Nigel"))
pedNigel = GET_PED_INDEX_FROM_ENTITY_INDEX(GET_ENTITY_INDEX_OF_REGISTERED_ENTITY("Nigel"))
CPRINTLN(DEBUG_MISSION, "STAGE_INTRO_CUTSCENE: SP_RUNNING: Nigel was grabbed while cutscene was running")
ENDIF
ELIF bNotSetUpNigelAnims
AND HAS_ANIM_DICT_LOADED(sNMTAnimDict)
IF CAN_SET_EXIT_STATE_FOR_REGISTERED_ENTITY("Nigel", IG_NIGEL)
SETUP_NIGEL_ANIMS()
ENDIF
ENDIF
// Player exit state
IF DOES_ENTITY_EXIST(PLAYER_PED_ID())
IF CAN_SET_EXIT_STATE_FOR_REGISTERED_ENTITY("Trevor", PLAYER_TWO)
SET_ENTITY_COORDS(PLAYER_PED_ID(), <<-43.6328, -1289.2937, 28.0752>>, FALSE)
ENDIF
ENDIF
IF CAN_SET_EXIT_STATE_FOR_CAMERA()
REPLAY_STOP_EVENT()
// Reset camera behind player
SET_GAMEPLAY_CAM_RELATIVE_PITCH(0.0)
SET_GAMEPLAY_CAM_RELATIVE_HEADING(0.0)
ENDIF
IF HAS_CUTSCENE_FINISHED()
// Unhide the door
IF IS_ENTITY_ALIVE(oiDoor)
SET_ENTITY_VISIBLE(oiDoor, TRUE)
SAFE_RELEASE_OBJECT(oiDoor)
ENDIF
// Return script systems to normal.
RC_END_CUTSCENE_MODE()
RC_SET_ENTITY_PROOFS_FOR_CUTSCENE(sRCLauncherDataLocal, FALSE)
// Cleanup
sProgress = SP_CLEANUP
ELSE
IF IS_ENTITY_ALIVE(oiDoor)
AND GET_CUTSCENE_TIME() > 45000
SET_ENTITY_VISIBLE(oiDoor, TRUE)
SAFE_RELEASE_OBJECT(oiDoor)
ENDIF
ENDIF
BREAK
CASE SP_CLEANUP
// Unlock Nigel's car, add upside down check
IF IS_ENTITY_ALIVE(mvPlayerCar.vehicle)
SET_VEHICLE_DOORS_LOCKED(mvPlayerCar.vehicle, VEHICLELOCK_UNLOCKED)
ADD_VEHICLE_UPSIDEDOWN_CHECK(mvPlayerCar.vehicle)
ENDIF
SET_MODEL_AS_NO_LONGER_NEEDED(IG_NIGEL)
SET_MODEL_AS_NO_LONGER_NEEDED(IG_MRS_THORNHILL)
cccProgress = CCC_WAIT_FOR_DISTANCE
sProgress = SP_SETUP
mStage = MS_GET_IN_CAR
//Allow displaying mission title now
BLOCK_MISSION_TITLE(FALSE)
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Mission stage PROC
/// Get into car
PROC STAGE_GET_IN_CAR()
SWITCH sProgress
CASE SP_SETUP
// Spawn celeb
IF NOT DOES_ENTITY_EXIST(pedDiNapoli)
SPAWN_CELEB_IN_BOOT()
ENDIF
IF HAS_ADDITIONAL_TEXT_LOADED(MISSION_TEXT_SLOT)
// Show objective, blip car
IF NOT bEnterCarObjectiveDone
REPLAY_RECORD_BACK_FOR_TIME(0.0, 10.0, REPLAY_IMPORTANCE_LOW)
PRINT_NOW("N3GETCAR", DEFAULT_GOD_TEXT_TIME, 1) // Get in the car
bEnterCarObjectiveDone = TRUE
ENDIF
SAFE_REMOVE_BLIP(biGotoBlip)
IF IS_ENTITY_ALIVE(mvPlayerCar.vehicle)
biGotoBlip = CREATE_VEHICLE_BLIP(mvPlayerCar.vehicle)
ENDIF
// Initialise timed Al thumps
spAlThumps = SP_SETUP
sProgress = SP_RUNNING
ENDIF
BREAK
CASE SP_RUNNING
// Spawn celeb
IF NOT DOES_ENTITY_EXIST(pedDiNapoli)
SPAWN_CELEB_IN_BOOT()
ELSE
AL_THUMP_HANDLER()
BOUNCY_BOOT()
ENDIF
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), mvPlayerCar.vehicle)
// Player now in the car - change states
sProgress = SP_CLEANUP
ELIF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), mvPlayerCar.vehicle, CAR_ABANDON_DISTANCE)
// Player abandoned vehicle - change states
sProgress = SP_CLEANUP
ELIF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), mvPlayerCar.vehicle, CAR_ABANDON_WARN_DIST)
// Player far from vehicle - may need to warn them
IF NOT bWarnedPlayerNotToRunOff
PRINT_NOW("N3LEAVECAR", DEFAULT_GOD_TEXT_TIME, 1)
bWarnedPlayerNotToRunOff = TRUE
ENDIF
ENDIF
BREAK
CASE SP_CLEANUP
// Clear objective and blip
CLEAR_PRINTS()
SAFE_REMOVE_BLIP(biGotoBlip)
// Spawn celeb
IF NOT DOES_ENTITY_EXIST(pedDiNapoli)
SPAWN_CELEB_IN_BOOT()
ENDIF
// Go to new stage
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), mvPlayerCar.vehicle)
// Going to start driving
SET_INSTANCE_PRIORITY_HINT(INSTANCE_HINT_DRIVING)
// Drop into delivery or wanted state
IF GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) > 0
// Player wanted - lose cops
mStage = MS_LOSE_COPS
ELSE
// Player not wanted - deliver Al to his doom
mStage = MS_DRIVING
ENDIF
ELIF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), mvPlayerCar.vehicle, CAR_ABANDON_DISTANCE)
// Player abandoned vehicle - change states
mStage = MS_FAIL_FADE
sFailReason = "N3FLEFT" // You abandoned the vehicle.
ENDIF
sProgress = SP_SETUP
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Mission stage PROC
/// Main driving stage - main part of script
PROC STAGE_DRIVING()
SWITCH sProgress
CASE SP_SETUP
// Make sure DiNapoli has spawned and player is in car before adding destination
IF NOT DOES_ENTITY_EXIST(pedDiNapoli)
SPAWN_CELEB_IN_BOOT()
ELIF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), mvPlayerCar.vehicle)
CPRINTLN(DEBUG_MISSION, "STAGE_DRIVING: SP_SETUP: Going on to SP_RUNNING")
SAFE_REMOVE_BLIP(biGotoBlip)
// If we aren't about to drop into wanted, add blip and do objective
IF GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) < 1
biGotoBlip = CREATE_COORD_BLIP(vecDestination)
ENDIF
sProgress = SP_RUNNING
ENDIF
BREAK
CASE SP_RUNNING
// Player is now in car
// See if we're due the objective
IF iDestObjDisp < 1
PRINT_NOW("N3DRIVE", DEFAULT_GOD_TEXT_TIME, 1) // Go to the destination.
CHECK_CONVERSATION_AND_OBJECTIVE_TEXT_CONFLICT_NOW(stateRestoreConversation)
iDestObjDisp ++
ENDIF
// Trevor comment done, OK to trigger other conversations
DO_CONVERSATION_CYCLE()
// Keep track of player being in car
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), mvPlayerCar.vehicle)
// Check for wanted
IF GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) > 0
// Wanted - Change states
sProgress = SP_CLEANUP
// Check for arriving
ELIF IS_ENTITY_AT_COORD(mvPlayerCar.vehicle, vecDestination, <<6.0, 6.0, LOCATE_SIZE_HEIGHT>>, TRUE)
// Arrived - change states
sProgress = SP_CLEANUP
// Check for stopping trains
ELIF bTrainsAreActive
IF IS_ENTITY_IN_RANGE_COORDS(mvPlayerCar.vehicle, vecDestination, 300)
CPRINTLN(DEBUG_MISSION, "STAGE_DRIVING: SP_RUNNING: Switching off train spawning.")
SET_RANDOM_TRAINS(FALSE)
bTrainsAreActive = FALSE
ENDIF
ENDIF
ELSE
// Player exited vehicle - change states
sProgress = SP_CLEANUP
ENDIF
BREAK
CASE SP_CLEANUP
CLEAR_PRINTS()
SAFE_REMOVE_BLIP(biGotoBlip)
// Change states
IF NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), mvPlayerCar.vehicle)
CPRINTLN(DEBUG_MISSION, "STAGE_DRIVING: SP_CLEANUP: Player got out of car, go to STAGE_PLAYER_EXITS_CAR")
mStage = MS_PLAYER_EXITS_CAR
// Stop conversations
CONVERSATION_KILLER()
// Initialise a shape test area by the car boot
START_BOOT_SHAPE_TEST()
// Clear entity damage flags to prevent bogus damage dialogue - B*1256589
IF HAS_ENTITY_BEEN_DAMAGED_BY_ENTITY(mvPlayerCar.vehicle, PLAYER_PED_ID())
CPRINTLN(DEBUG_MISSION, "EXITING VEHICLE - Clearing entity damage on car")
CLEAR_ENTITY_LAST_DAMAGE_ENTITY(mvPlayerCar.vehicle)
#IF IS_DEBUG_BUILD ELSE CPRINTLN(DEBUG_MISSION, "EXITING VEHICLE - No entity damage to clear on car") #ENDIF
ENDIF
ELIF GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) > 0
CPRINTLN(DEBUG_MISSION, "STAGE_DRIVING: SP_CLEANUP: Player wanted, go to STAGE_LOSE_COPS")
mStage = MS_LOSE_COPS
ELIF IS_ENTITY_AT_COORD(mvPlayerCar.vehicle, vecDestination, <<6.0, 6.0, LOCATE_SIZE_HEIGHT>>, FALSE)
CPRINTLN(DEBUG_MISSION, "STAGE_DRIVING: SP_CLEANUP: Going on to STAGE_RAM_TRAIN")
mStage = MS_RAM_TRAIN
REQUEST_MODEL(FREIGHTCAR)
REQUEST_MODEL(FREIGHTCONT1)
REQUEST_MODEL(FREIGHTCONT2)
REQUEST_MODEL(S_M_M_Trucker_01)
// Make sure celeb ped is invisible - skip can fail to set this up if fired during intro mocap
SET_ENTITY_VISIBLE(pedDiNapoli, FALSE)
ENDIF
sProgress = SP_SETUP
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Mission stage PROC
/// Lose your wanted level!
PROC STAGE_LOSE_COPS()
SWITCH sProgress
CASE SP_SETUP
// Confirm we're still wanted
IF GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) > 0
// Do wanted objective
PRINT_NOW("N3LOSEWANT", DEFAULT_GOD_TEXT_TIME, 1)
CHECK_CONVERSATION_AND_OBJECTIVE_TEXT_CONFLICT_NOW(stateRestoreConversation)
sProgress = SP_RUNNING
ELSE
// Not wanted - change states
sProgress = SP_CLEANUP
ENDIF
BREAK
CASE SP_RUNNING
// Is player no longer wanted?
IF GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) < 1
// Player no longer wanted - change state
sProgress = SP_CLEANUP
CPRINTLN(DEBUG_MISSION, "Nigel3::: Game reported to script that player no longer wanted")
ELIF NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), mvPlayerCar.vehicle)
// Player got out of car - change state
sProgress = SP_CLEANUP
ENDIF
// Check for damage conversations
CHECK_DAMAGE_COMPLAINT_CONV()
// Do we need to check on the begging conversations?
IF cccProgress = CCC_WAIT_TO_FINISH
DO_CONVERSATION_CYCLE()
ENDIF
BREAK
CASE SP_CLEANUP
CLEAR_PRINTS()
SAFE_REMOVE_BLIP(biGotoBlip)
// Go back to appropriate state
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), mvPlayerCar.vehicle)
// Player in car - get on with driving
mStage = MS_DRIVING
ELSE
// Player not in car - get in
START_BOOT_SHAPE_TEST()
mStage = MS_PLAYER_EXITS_CAR
ENDIF
sProgress = SP_SETUP
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Mission stage PROC
/// Player exits car - will they free the celeb?
/// Also handles car exit before release objective given, and after point of no return
PROC STAGE_PLAYER_EXITS_CAR()
SWITCH sProgress
CASE SP_SETUP
// The setup section mainly determines whether letting Al go is currently allowed so we know what objectives/checks are needed
// Request the synched scene release anims
REQUEST_ANIM_DICT(sReleaseAnimDict)
REQUEST_MODEL(mCash)
// Check whether the car is static
IF GET_ENTITY_SPEED(mvPlayerCar.vehicle) >= 0.025
CPRINTLN(DEBUG_MISSION, "STAGE_PLAYER_EXITS_CAR: SP_SETUP: Car is in motion, wait for it to slow")
cecExitConditions = CEC_CAR_MOVING
sProgress = SP_RUNNING
ELSE
CPRINTLN(DEBUG_MISSION, "STAGE_PLAYER_EXITS_CAR: SP_SETUP: Car stopped, check other conditions")
// Check whether we should be priming the back in objective
IF bReleaseObjectiveShown
IF NOT bBackInObjDisplayed
iBackInDelayTimer = GET_GAME_TIMER()
// PRINT_NOW("N3RTCAR", DEFAULT_GOD_TEXT_TIME, 1) // Get back in the car.
// bBackInObjDisplayed = TRUE
ENDIF
ENDIF
IF NOT DOES_BLIP_EXIST(biGotoBlip)
biGotoBlip = CREATE_VEHICLE_BLIP(mvPlayerCar.vehicle)
ENDIF
// Detect and handle the particular case we are in
// Check whether releasing the celeb is a valid option yet
IF NOT bReleaseIsValid
CPRINTLN(DEBUG_MISSION, "STAGE_PLAYER_EXITS_CAR: SP_SETUP: Cannot release yet!")
// Conversation progress not sufficient for release yet - can only get back in
// Set state and go on to loop
cecExitConditions = CEC_TOO_SOON
sProgress = SP_RUNNING
// Check whether we have reached the point of no return
ELIF bRamStageStarted
CPRINTLN(DEBUG_MISSION, "STAGE_PLAYER_EXITS_CAR: SP_SETUP: Reached the ram stage, release not allowed any more")
// Player has reached ramming stage - Trevor's mind is made up, get back in
// Set state and go on to loop
cecExitConditions = CEC_TOO_LATE
sProgress = SP_RUNNING
ELIF IS_PLAYER_NEAR_COP()
CPRINTLN(DEBUG_MISSION, "STAGE_PLAYER_EXITS_CAR: SP_SETUP: Near cops or wanted, cannot release")
// Can't release Al near to cops
PRINT_HELP("N3NRCOP") // You can't release Al while cops are nearby
cecExitConditions = CEC_NEAR_COPS
sProgress = SP_RUNNING
// Check whether boot area is blocked
ELIF GET_SHAPE_TEST_RESULT(stiBootArea[0], iShapetestResult[0], vecShapetestResult[0][0], vecShapetestResult[0][1], hitEntity[0]) != SHAPETEST_STATUS_RESULTS_NOTREADY
AND GET_SHAPE_TEST_RESULT(stiBootArea[1], iShapetestResult[1], vecShapetestResult[1][0], vecShapetestResult[1][1], hitEntity[0]) != SHAPETEST_STATUS_RESULTS_NOTREADY
CPRINTLN(DEBUG_MISSION, "STAGE_PLAYER_EXITS_CAR: SP_SETUP: Checking shapetest result")
IF iShapetestResult[0] = 1
OR iShapetestResult[1] = 1
CPRINTLN(DEBUG_MISSION, "STAGE_PLAYER_EXITS_CAR: SP_SETUP: Boot is blocked, player must move car")
PRINT_HELP("N3TRUNK") // If you want to release Al, park so the trunk is not blocked.
cecExitConditions = CEC_OBSTRUCTED_BOOT
sProgress = SP_RUNNING
ELIF CAR_ON_DODGY_SLOPE()
CPRINTLN(DEBUG_MISSION, "STAGE_PLAYER_EXITS_CAR: SP_SETUP: Boot is not blocked, car on a slope, player must move car")
PRINT_HELP("N3WONKY") // If you want to release Al, park so the trunk is not blocked.
cecExitConditions = CEC_UNEVEN_GROUND
sProgress = SP_RUNNING
ELSE
CPRINTLN(DEBUG_MISSION, "STAGE_PLAYER_EXITS_CAR: SP_SETUP: We are OK to release Al if desired, shapetest results [0]:", iShapetestResult[0], " [1]:", iShapetestResult[1])
IF NOT bShowedTrunkHelp
PRINT_HELP("N3FTRUNK") // Walk to the trunk if you want to release Al.
bShowedTrunkHelp = TRUE
ENDIF
cecExitConditions = CEC_OK_TO_RELEASE
sProgress = SP_RUNNING
ENDIF
ELSE
CPRINTLN(DEBUG_MISSION, "STAGE_PLAYER_EXITS_CAR: SP_SETUP: Waiting for shape test to start up...")
ENDIF
ENDIF
// Tick bouncy boot for ongoing wobbling
BOUNCY_BOOT()
// Initialise timed Al thumps
spAlThumps = SP_SETUP
BREAK
CASE SP_RUNNING
// Check whether we need to play shooting damage conversations
CHECK_SHOOTING_COMPLAINT_CONV()
// Do we need to check on the begging conversations?
IF cccProgress = CCC_WAIT_TO_FINISH
DO_CONVERSATION_CYCLE()
ENDIF
AL_THUMP_HANDLER()
BOUNCY_BOOT()
// Check if player got back in car
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), mvPlayerCar.vehicle)
CPRINTLN(DEBUG_MISSION, "NIGEL3::: STAGE_PLAYER_EXITS_CAR::: SP_RUNNING::: Detected player back in car")
// Player got back in - change states
sProgress = SP_CLEANUP
releaseChoice = RCH_GOT_BACK_IN
ELIF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), mvPlayerCar.vehicle, 250)
// Player ran off - change states
sProgress = SP_CLEANUP
releaseChoice = RCH_RAN_OFF
ELIF (NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), mvPlayerCar.vehicle, 50) AND (NOT bWarnedPlayerNotToRunOff))
// Warn if wandering off
PRINT_NOW("N3LEAVECAR", DEFAULT_GOD_TEXT_TIME, 1) // Don't leave the car behind
bWarnedPlayerNotToRunOff = TRUE
ELSE
SWITCH cecExitConditions
CASE CEC_TOO_SOON
// Check for back in objective
IF NOT bBackInObjDisplayed
PRINT_NOW("N3RTCAR", DEFAULT_GOD_TEXT_TIME, 1) // Get back in the car.
bBackInObjDisplayed = TRUE
ENDIF
// Check whether the conversation's progressed
IF bReleaseIsValid
// The first conversation has finished, go back to setup to evaluate release state.
CPRINTLN(DEBUG_MISSION, "STAGE_PLAYER_EXITS_CAR: SP_RUNNING: CEC_TOO_SOON: Conversation progressed to allow release, going back to setup to reevaluate state")
START_BOOT_SHAPE_TEST()
sProgress = SP_SETUP
ENDIF
BREAK
CASE CEC_NEAR_COPS
CASE CEC_OBSTRUCTED_BOOT
CASE CEC_UNEVEN_GROUND
CASE CEC_TOO_LATE
// Currently nothing extra to handle
BREAK
CASE CEC_OK_TO_RELEASE
// Check whether we've delayed the objective long enough
IF NOT bBackInObjDisplayed
AND GET_GAME_TIMER() - iBackInDelayTimer > BACK_IN_DELAY
PRINT_NOW("N3RTCAR", DEFAULT_GOD_TEXT_TIME, 1) // Get back in the car.
CHECK_CONVERSATION_AND_OBJECTIVE_TEXT_CONFLICT_NOW(stateRestoreConversation)
bBackInObjDisplayed = TRUE
ENDIF
IF IS_ENTITY_IN_RANGE_COORDS(PLAYER_PED_ID(), GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvPlayerCar.vehicle, vecBootOffset), TRUNK_DETECT_RADIUS)
AND IS_PED_ON_FOOT(PLAYER_PED_ID())
AND NOT (IS_PED_RAGDOLL(PLAYER_PED_ID()) OR IS_PED_FALLING(PLAYER_PED_ID()) OR IS_PED_GETTING_UP(PLAYER_PED_ID()))
// Player in release trigger area - change states
sProgress = SP_CLEANUP
releaseChoice = RCH_RELEASED
// Prevent the boot thumps from doing stuff to the cutscene - force idle
spBouncyBoot = SP_CLEANUP
ENDIF
// Encourage ambient traffic to not drive into the car
SET_VEHICLE_WILL_FORCE_OTHER_VEHICLES_TO_STOP(mvPlayerCar.vehicle, TRUE)
BREAK
CASE CEC_CAR_MOVING
// Check if the car has now stopped moving
IF GET_ENTITY_SPEED(mvPlayerCar.vehicle) < 0.025
// Car nearly stopped - reevaluate state
START_BOOT_SHAPE_TEST()
sProgress = SP_SETUP
ENDIF
BREAK
ENDSWITCH
ENDIF
BREAK
CASE SP_CLEANUP
CLEAR_PRINTS()
CLEAR_HELP()
SAFE_REMOVE_BLIP(biGotoBlip)
SWITCH releaseChoice
CASE RCH_GOT_BACK_IN
CPRINTLN(DEBUG_MISSION, "Player got back in.")
IF bRamStageStarted
mStage = MS_RAM_TRAIN
ELSE
// Check wanted level
IF GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) > 0
mStage = MS_LOSE_COPS
ELSE
mStage = MS_DRIVING
ENDIF
ENDIF
BREAK
CASE RCH_RELEASED
// Player released the celeb
CPRINTLN(DEBUG_MISSION, "Player releasing celeb.")
stiBootArea[0] = START_SHAPE_TEST_BOX(GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvPlayerCar.vehicle, vecBootBlockOffset[2]), vecBootBlockDimensions[2], GET_ENTITY_ROTATION(mvPlayerCar.vehicle), DEFAULT, SCRIPT_INCLUDE_MOVER|SCRIPT_INCLUDE_OBJECT|SCRIPT_INCLUDE_VEHICLE)
stiBootArea[1] = START_SHAPE_TEST_BOX(GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvPlayerCar.vehicle, vecBootBlockOffset[3]), vecBootBlockDimensions[3], GET_ENTITY_ROTATION(mvPlayerCar.vehicle), DEFAULT, SCRIPT_INCLUDE_MOVER|SCRIPT_INCLUDE_OBJECT|SCRIPT_INCLUDE_VEHICLE)
mStage = MS_RELEASE_CUTSCENE
BREAK
CASE RCH_RAN_OFF
// Player abandoned the car, mission failed.
CPRINTLN(DEBUG_MISSION, "NIGEL3::: Player ran away.")
sFailReason = "N3FLEFT" // You left the car behind
mStage = MS_FAIL_FADE
BREAK
ENDSWITCH
sProgress = SP_SETUP
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Mission stage PROC
/// When player has nearly reached destination, guide them to ram train
PROC STAGE_RAM_TRAIN()
SWITCH sProgress
CASE SP_SETUP
// This is used to visualise the car's corner coords as used in the over rails check - might need to set this up again if car gets changed
/* IF ENTITY_OK_TO_USE(mvPlayerCar.vehicle)
FLOAT fCarWidth
FLOAT fCarLength
fCarWidth = 1.6
fCarLength = 4.2
VECTOR offset_FL
VECTOR offset_FR
VECTOR offset_BL
VECTOR offset_BR
offset_FL = << -fCarWidth/2, fCarLength/2, 0 >>
offset_FR = << fCarWidth/2, fCarLength/2, 0 >>
offset_BL = << -fCarWidth/2, -fCarLength/2, 0 >>
offset_BR = << fCarWidth/2, -fCarLength/2, 0 >>
DRAW_DEBUG_SPHERE(GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvPlayerCar.vehicle, offset_FL), 0.025)
DRAW_DEBUG_SPHERE(GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvPlayerCar.vehicle, offset_FR), 0.025)
DRAW_DEBUG_SPHERE(GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvPlayerCar.vehicle, offset_BL), 0.025)
DRAW_DEBUG_SPHERE(GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvPlayerCar.vehicle, offset_BR), 0.025)
ENDIF */
IF NOT bRamStageStarted
// Kill any ongoing damage comment
IF IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
KILL_FACE_TO_FACE_CONVERSATION()
ENDIF
bRamStageStarted = TRUE // So we know if the train ram section has been reached if the player gets out.
bPlayerNotInCar = FALSE // This will get checked next
bMurderCommentStarted = FALSE
bTrainIsSpawned = FALSE
SET_REPLAY_MID_MISSION_STAGE_WITH_NAME(CP_TRAIN, "Train section", TRUE)
ENDIF
// Keep track of player being in/out of car
// Not changing anything based off this now, it's needed for the running stage though
IF bPlayerNotInCar
// Player is out of car: Check for them getting back in
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), mvPlayerCar.vehicle)
CPRINTLN(DEBUG_MISSION, "STAGE_RAM_TRAIN: SP_SETUP: Player got back in car")
CLEAR_PRINTS()
SAFE_REMOVE_BLIP(biGotoBlip)
bPlayerNotInCar = FALSE
ENDIF
ELSE
// Player is in car: Check for them getting out
IF NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), mvPlayerCar.vehicle)
CPRINTLN(DEBUG_MISSION, "STAGE_RAM_TRAIN: SP_SETUP: Player got out of car")
CLEAR_PRINTS()
SAFE_REMOVE_BLIP(biGotoBlip)
biGotoBlip = CREATE_VEHICLE_BLIP(mvPlayerCar.vehicle)
IF NOT bBackInObjDisplayed
PRINT_NOW("N3RTCAR", DEFAULT_GOD_TEXT_TIME, 1)
bBackInObjDisplayed = TRUE
ENDIF
bPlayerNotInCar = TRUE
ENDIF
ENDIF
// Start conversation - Trevor has decided not to let Al go
IF CREATE_CONVERSATION(mConversationStruct, "NIGE3AU", "NIGEL3_CX", CONV_PRIORITY_MEDIUM, DO_NOT_DISPLAY_SUBTITLES)
SAFE_REMOVE_BLIP(biGotoBlip)
bMurderCommentStarted = TRUE
spAlPanic = SP_SETUP
ENDIF
// Attempt to spawn train
IF NOT bTrainIsSpawned
AND SPAWN_TRAIN()
bTrainIsSpawned = TRUE
ENDIF
IF bMurderCommentStarted
AND bTrainIsSpawned
// Do the objective if the player is in the car
IF bPlayerNotInCar = FALSE
PRINT_NOW("N3RAM", DEFAULT_GOD_TEXT_TIME, 1) // Drive towards the train.
bTrainObjectiveDone = TRUE
// Update blip
SAFE_REMOVE_BLIP(biGotoBlip)
// IF IS_ENTITY_ALIVE(mvTrain.vehicle)
// biGotoBlip = CREATE_VEHICLE_BLIP(mvTrain.vehicle, FALSE)
// ENDIF
sProgress = SP_RUNNING
ENDIF
REPLAY_RECORD_BACK_FOR_TIME(10.0, 6.0, REPLAY_IMPORTANCE_LOW)
bGlancedTrain = FALSE
bSkinOfTeethTimerStarted = FALSE
bShowedTrainCamHelp = FALSE
sProgress = SP_RUNNING
ENDIF
BREAK
CASE SP_RUNNING
// Checks for when player not in car
IF bPlayerNotInCar
// Player got back in
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), mvPlayerCar.vehicle, TRUE)
CPRINTLN(DEBUG_MISSION, "STAGE_RAM_TRAIN: SP_SETUP: Player got back in car")
CLEAR_PRINTS()
SAFE_REMOVE_BLIP(biGotoBlip)
// biGotoBlip = CREATE_VEHICLE_BLIP(mvTrain.vehicle, FALSE)
IF NOT bTrainObjectiveDone
PRINT_NOW("N3RAM", DEFAULT_GOD_TEXT_TIME, 1)
bTrainObjectiveDone = TRUE
ENDIF
bPlayerNotInCar = FALSE
// Check for abandon in case player got out earlier
ELIF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), mvPlayerCar.vehicle, CAR_ABANDON_DISTANCE)
mStage = MS_FAIL_FADE
sProgress = SP_SETUP
sFailReason = "N3FLEFT"
BREAK
ENDIF
ELSE
// Check for clearing the N3RAM objective - Currently this says leave the car on the rails
IF NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), mvPlayerCar.vehicle)
IF IS_THIS_PRINT_BEING_DISPLAYED("N3RAM")
CLEAR_PRINTS()
ENDIF
bPlayerNotInCar = TRUE
ENDIF
ENDIF
// Train blip and audio scene mix setup
IF IS_ENTITY_ALIVE(mvTrain.vehicle)
IF IS_AUDIO_SCENE_ACTIVE("NIGEL_03_JOURNEY_MIX")
STOP_AUDIO_SCENE("NIGEL_03_JOURNEY_MIX")
ENDIF
IF NOT IS_AUDIO_SCENE_ACTIVE("NIGEL_03_MIX_SETTINGS")
ADD_ENTITY_TO_AUDIO_MIX_GROUP(mvTrain.vehicle,"NIGEL_03_MIX_GROUP")
START_AUDIO_SCENE("NIGEL_03_MIX_SETTINGS")
START_VEHICLE_HORN(mvTrain.vehicle, 30000, GET_HASH_KEY("HELDDOWN"))
PLAY_SOUND_FROM_ENTITY(iTrainComing, "TRAIN_COMING", mvTrain.vehicle, "NIGEL_03_SOUNDSET")
ENDIF
// Train cinematic cam
TRAIN_CINEMATIC_CAM()
ENDIF
// Check for player being on the train tracks
IF IS_ENTITY_IN_ANGLED_AREA(mvPlayerCar.vehicle, naaTrainTrackBox.vEnds[0], naaTrainTrackBox.vEnds[1], naaTrainTrackBox.fWidth)
// See if we need to show bail out objective
IF GET_DISTANCE_BETWEEN_ENTITIES(mvPlayerCar.vehicle, mvTrain.vehicle) < 120
AND NOT bBailOutObjShown
AND IS_PED_IN_VEHICLE(PLAYER_PED_ID(), mvPlayerCar.vehicle)
IF GET_ENTITY_SPEED(mvPlayerCar.vehicle) > 0.25
PRINT_NOW("N3BAIL", DEFAULT_GOD_TEXT_TIME, 1)
ELSE
PRINT_NOW("N3EXIT", DEFAULT_GOD_TEXT_TIME, 1)
ENDIF
bBailOutObjShown = TRUE
ENDIF
ELSE
// Remove objectives if player not on rails
IF bBailOutObjShown
CLEAR_THIS_PRINT("N3BAIL")
CLEAR_THIS_PRINT("N3EXIT")
ENDIF
ENDIF
// Remove objectives if player not in car
IF bBailOutObjShown
AND IS_PED_ON_FOOT(PLAYER_PED_ID())
CLEAR_THIS_PRINT("N3BAIL")
CLEAR_THIS_PRINT("N3EXIT")
ENDIF
// Monitor Train/car hit, get relative speed
VECTOR vecTmpTrainSpeed
VECTOR vecTmpCarSpeed
vecTmpTrainSpeed = GET_ENTITY_SPEED_VECTOR(mvTrain.vehicle)
vecTmpCarSpeed = GET_ENTITY_SPEED_VECTOR(mvPlayerCar.vehicle)
// See if train has hit car
IF IS_ENTITY_TOUCHING_ENTITY(mvPlayerCar.vehicle, mvTrain.vehicle)
CPRINTLN(DEBUG_MISSION, "STAGE_RAM_TRAIN: SP_RUNNING: Train touching car")
// Check relative impact speed is significant - Use saved speeds as it may take a couple of frames for entities to register as touching
IF (fTrainRamSavedSpeed[0] + fCarRamSavedSpeed[0]) > 14 // 14m/s ~ 30mph
CPRINTLN(DEBUG_MISSION, "STAGE_RAM_TRAIN: SP_RUNNING: Adequate speed for crash")
// Adequate speed, do we have a solid hit or just a glance - based on whether car was on rails last frame
IF bTrackCarOnRails[1]
// Solid hit - see if player has bailed out
IF PLAYER_BAILED_OUT()
// Player not in car, go to outro cutscene
// Check stats
IF GET_PLAYER_INVINCIBLE(PLAYER_ID())
CPRINTLN(DEBUG_MISSION, "Checking mission stats on ram hit - player is invulnerable so not allowing!")
ELSE
// Locomotivation - player did train ending so allowed!
CPRINTLN(DEBUG_MISSION, "Setting the NI3_REVERSED_TO_KILL stat")
INFORM_STAT_SYSTEM_OF_BOOL_STAT_HAPPENED(NI3_REVERSED_TO_KILL)
// Evaluate bail out stat now
IF bSkinOfTeethTimerStarted
iSkinOfTeethTimer = GET_GAME_TIMER() - iSkinOfTeethTimer
CPRINTLN(DEBUG_MISSION, "SKIN OF YOUR TEETH - bailed in ", iSkinOfTeethTimer, "milliseconds against ", iSkinOfTeethLimit, " millisecond limit.")
IF iSkinOfTeethTimer < iSkinOfTeethLimit
CPRINTLN(DEBUG_MISSION, "Setting the NI3_BAILED_AT_LAST_MOMENT stat")
bSkinOfTeethStatHappened = TRUE
INFORM_STAT_SYSTEM_OF_BOOL_STAT_HAPPENED(NI3_BAILED_AT_LAST_MOMENT)
ENDIF
ENDIF
ENDIF
CLEAR_PRINTS()
SAFE_REMOVE_BLIP(biGotoBlip)
TRAIN_CINEMATIC_CAM(TRUE)
SET_VEHICLE_UNDRIVEABLE(mvPlayerCar.vehicle, TRUE) // Should prevent engine repair game after mission complete.
sProgress = SP_SETUP
mStage = MS_OUTRO_CUTSCENE
CPRINTLN(DEBUG_MISSION, "STAGE_RAM_TRAIN: SP_RUNNING:: Car hit train, player bailed out or invulnerable, relative speed ", (fTrainRamSavedSpeed[0] + fCarRamSavedSpeed[0]))
BREAK
ELSE
// Player fucked up and is still in car
SAFE_DELETE_PED(pedDiNapoli)
IF IS_SPECIAL_ABILITY_ACTIVE(PLAYER_ID())
SPECIAL_ABILITY_DEACTIVATE(PLAYER_ID())
ENDIF
EXPLODE_VEHICLE(mvPlayerCar.vehicle)
EXPLODE_PED_HEAD(PLAYER_PED_ID()) // Reinstated per B*1037337. Wrap in an IS_PED_IN_VEHICLE if the PLAYING_ON_RAILWAY_LINES check goes back in
CPRINTLN(DEBUG_MISSION, "STAGE_RAM_TRAIN: SP_RUNNING: Car hit train with player still in it, relative speed ", (fTrainRamSavedSpeed[0] + fCarRamSavedSpeed[0]))
ENDIF
ELSE
CPRINTLN(DEBUG_MISSION, "STAGE_RAM_TRAIN: SP_RUNNING: Car not on rails - treat as miss")
// Glancing hit - treat as miss
CLEAR_PRINTS()
CLEAR_HELP()
TRAIN_CINEMATIC_CAM(TRUE)
SAFE_REMOVE_BLIP(biGotoBlip)
mStage = MS_MISSED_RAM
sProgress = SP_SETUP
bGlancedTrain = TRUE
// Was player in car?
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), mvPlayerCar.vehicle, TRUE)
bPlayerMissedInCar = TRUE
ELSE
bPlayerMissedInCar = FALSE
ENDIF
// Mark train as not needed
IF DOES_ENTITY_EXIST(mvTrain.vehicle)
SET_MISSION_TRAIN_AS_NO_LONGER_NEEDED(mvTrain.vehicle)
ENDIF
ENDIF
#IF IS_DEBUG_BUILD
ELSE
CPRINTLN(DEBUG_MISSION, "STAGE_RAM_TRAIN: SP_RUNNING: Car hit train but relative speed only ", (fTrainRamSavedSpeed[0] + fCarRamSavedSpeed[0])) #ENDIF
ENDIF
ELSE
// Monitor train distance from car and time since bailout for stat
UPDATE_PLAYER_BAIL_DISTANCE()
ENDIF
// Save the speeds for next frame
TRACK_RAM_SPEEDS(-vecTmpTrainSpeed.y, vecTmpCarSpeed.y)
// Check for train going past player
IF DOES_ENTITY_EXIST(mvTrain.vehicle)
VECTOR vecPlayerCarPos
VECTOR vecTrainPos
vecPlayerCarPos = GET_ENTITY_COORDS(mvPlayerCar.vehicle)
vecTrainPos = GET_ENTITY_COORDS(mvTrain.vehicle, FALSE)
// Check if train is further south than player
IF vecPlayerCarPos.y > vecTrainPos.y
// Player missed train
CLEAR_PRINTS()
SAFE_REMOVE_BLIP(biGotoBlip)
mStage = MS_MISSED_RAM
sProgress = SP_SETUP
ELSE
// See if we need to adjust train speed
RAM_SPEED_ADJUSTER()
ENDIF
ENDIF
// Tick Al's screaming
AL_TRAIN_PANIC()
BREAK
CASE SP_CLEANUP
// Player goes directly to cutscene from successful ram detection now - B*951059
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Mission stage PROC
/// What happens when player misses train
PROC STAGE_MISSED_RAM()
SWITCH sProgress
CASE SP_SETUP
CPRINTLN(DEBUG_MISSION, "STAGE_MISSED_RAM: SP_SETUP")
IF IS_AUDIO_SCENE_ACTIVE("NIGEL_03_MIX_SETTINGS")
STOP_AUDIO_SCENE("NIGEL_03_MIX_SETTINGS")
ENDIF
// Start right comment playing depending on what happened
IF bGlancedTrain
// Make sure glancing hit hasn't killed the car after the fact
IF NOT IS_ENTITY_ALIVE(mvPlayerCar.vehicle)
IF IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
KILL_FACE_TO_FACE_CONVERSATION_DO_NOT_FINISH_LAST_LINE()
ENDIF
sProgress = SP_SETUP
mStage = MS_ENDING_PHONE_CALL
ELSE
IF bPlayerMissedInCar
// Can't even drive straight
IF CREATE_CONVERSATION(mConversationStruct, "NIGE3AU", "NIGEL3_X1", CONV_PRIORITY_MEDIUM)
sProgress = SP_RUNNING
ENDIF
ELSE
// Need to be right on the rails
IF CREATE_CONVERSATION(mConversationStruct, "NIGE3AU", "NIGEL3_X2", CONV_PRIORITY_MEDIUM)
sProgress = SP_RUNNING
ENDIF
ENDIF
ENDIF
ELSE
// It's like I wasn't even trying
IF CREATE_CONVERSATION(mConversationStruct, "NIGE3AU", "NIGEL3_X3", CONV_PRIORITY_MEDIUM)
sProgress = SP_RUNNING
ENDIF
ENDIF
BREAK
CASE SP_RUNNING
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
sProgress = SP_CLEANUP
ENDIF
BREAK
CASE SP_CLEANUP
// Mission failed - missed train
sFailReason = "N3FMISST"
mStage = MS_FAIL_FADE
sProgress = SP_SETUP
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Handle the slowmo effect
PROC MANAGE_TIME_SCALING()
FLOAT fCurrentScale = 1.0
IF fRealTime < 0.225
fCurrentScale = 1.0
ELIF fRealTime < 0.75
fCurrentScale = 1.0 - (0.8*((fRealTime-0.225)/(0.75-0.225)))
ELIF fRealTime < 3.0
fCurrentScale = 0.2
ELIF fRealTime < 4.0
fCurrentScale = 0.2 + (0.8*((fRealTime-3.0)/(4.0-3.0)))
ENDIF
fRealTime = fRealTime +@ (1/fCurrentScale)
// CPRINTLN(DEBUG_MISSION, "TIMESCALE ", fCurrentScale, " - TIME ", fRealTime)
SET_TIME_SCALE(fCurrentScale)
ENDPROC
/// PURPOSE:
/// Mission stage PROC
/// Train hits car before Trevor calls Nigel to complete the mission
PROC STAGE_OUTRO_CUTSCENE()
SWITCH sProgress
CASE SP_SETUP
// Ditch the blips
SAFE_REMOVE_BLIP(biGotoBlip)
// Disable phone etc.
RC_START_CUTSCENE_MODE(<<0.0,0.0,0.0>>)
// Set up deathbox and cams
IF IS_ENTITY_ALIVE(mvTrain.vehicle)
VECTOR vecDeathBox1, vecDeathBox2
// Allow player to be damaged in this cutscene if they are in a dumb place in front of the train
vecDeathBox1 = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvTrain.vehicle, << 0.0, 8.0, -4.0>>)
vecDeathBox2 = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvTrain.vehicle, << 0.0, 60.0, 10.0>>)
IF IS_ENTITY_IN_ANGLED_AREA(PLAYER_PED_ID(), vecDeathBox1, vecDeathBox2, 3.0)
CPRINTLN(DEBUG_MISSION, "STAGE_OUTRO_CUTSCENE: Player is in a bad place, may be mucking about. Invulnerability OFF.")
SET_PLAYER_CONTROL(PLAYER_ID(), FALSE, SPC_ALLOW_PLAYER_DAMAGE)
ENDIF
// Create cameras
csOutroCameras[0] = CREATE_CAMERA(CAMTYPE_SCRIPTED, TRUE)
SET_CAM_FOV(csOutroCameras[0], 25.3209)
ATTACH_CAM_TO_ENTITY(csOutroCameras[0], mvTrain.vehicle, <<-3.4384, 42.1476, 0.1772>>)
POINT_CAM_AT_ENTITY(csOutroCameras[0], mvTrain.vehicle, <<-2.9318, 39.1938, 0.3114>>)
SHAKE_CAM(csOutroCameras[0], "HAND_SHAKE", 0.166)
csOutroCameras[1] = CREATE_CAMERA(CAMTYPE_SCRIPTED, TRUE)
SET_CAM_FOV(csOutroCameras[1], 25.3209)
ATTACH_CAM_TO_ENTITY(csOutroCameras[1], mvTrain.vehicle, <<-3.6622, 13.9852, -0.5222>>)
POINT_CAM_AT_ENTITY(csOutroCameras[1], mvTrain.vehicle, <<-2.2737, 11.3714, -0.0324>>)
SHAKE_CAM(csOutroCameras[1], "HAND_SHAKE", 0.166)
ENDIF
// Camera durations (ms)
iCameraDuration[0] = 4000
iCameraDuration[1] = 3000
fRealTime = 0
// Disable fire service
ENABLE_DISPATCH_SERVICE(DT_FIRE_DEPARTMENT, FALSE)
// Set vehicle on fire
IF IS_ENTITY_ALIVE(mvPlayerCar.vehicle)
IF GET_VEHICLE_PETROL_TANK_HEALTH(mvPlayerCar.vehicle) > -100
SET_VEHICLE_PETROL_TANK_HEALTH(mvPlayerCar.vehicle, -100)
// Save the position so we can extinguish the flames
vecFireLocator = GET_ENTITY_COORDS(mvPlayerCar.vehicle)
ENDIF
ENDIF
// Immortal driver
IF IS_ENTITY_ALIVE(mvTrain.driver)
SET_ENTITY_INVINCIBLE(mvTrain.driver, TRUE)
ENDIF
// Activate camera
SET_CAM_ACTIVE_WITH_INTERP(csOutroCameras[1], csOutroCameras[0], iCameraDuration[0], GRAPH_TYPE_LINEAR, GRAPH_TYPE_LINEAR)
RENDER_SCRIPT_CAMS(TRUE, FALSE)
REPLAY_START_EVENT(REPLAY_IMPORTANCE_LOW)
// Init loop/progress trackers
bCutsceneLoop = TRUE
bOutroWasSkipped = FALSE
bSwitchedCameras = FALSE
bExplodedVehicle = FALSE
// Do we need the first person flash?
IF IS_PLAYER_IN_FIRST_PERSON_CAMERA()
bFPFlashNeeded = TRUE
ELSE
bFPFlashNeeded = FALSE
ENDIF
sProgress = SP_RUNNING
BREAK
CASE SP_RUNNING
#IF IS_DEBUG_BUILD
IF bDebugShowBailDistanceOnCustscene
DISPLAY_TEXT_WITH_FLOAT(0.125, 0.7, "DM_NUM", fBailDistanceTrack, 3)
IF bSkinOfTeethTimerStarted
DISPLAY_TEXT_WITH_FLOAT(0.125, 0.85, "DM_NUM", TO_FLOAT(iSkinOfTeethTimer)/1000, 3)
ENDIF
ENDIF
#ENDIF
MANAGE_TIME_SCALING()
IF fRealTime >= 7.0
// Cutscene finished
bCutsceneLoop = FALSE
// Check whether it's time to switch cameras
ELIF NOT bSwitchedCameras
AND fRealTime >= 4.0
CPRINTLN(DEBUG_MISSION, "Switched cameras at ", fRealTime)
IF IS_ENTITY_ALIVE(mvTrain.vehicle)
csOutroCameras[2] = CREATE_CAMERA(CAMTYPE_SCRIPTED, TRUE)
SET_CAM_FOV(csOutroCameras[2], 38.4841)
SET_CAM_COORD(csOutroCameras[2], GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvTrain.vehicle, <<-0.1473, -9.8601, 5.1181>>))
POINT_CAM_AT_COORD(csOutroCameras[2], GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvTrain.vehicle, <<-0.1893, -6.9636, 4.3379>>))
csOutroCameras[3] = CREATE_CAMERA(CAMTYPE_SCRIPTED, TRUE)
SET_CAM_FOV(csOutroCameras[3], 38.4841)
SET_CAM_COORD(csOutroCameras[3], GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvTrain.vehicle, <<-0.1892, -9.8601, 6.0113>>))
POINT_CAM_AT_COORD(csOutroCameras[3], GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvTrain.vehicle, <<-0.2225, -7.1434, 5.7790>>))
SET_CAM_ACTIVE_WITH_INTERP(csOutroCameras[3], csOutroCameras[2], iCameraDuration[1], GRAPH_TYPE_DECEL, GRAPH_TYPE_DECEL)
bSwitchedCameras = TRUE
// Set Trevor facing train - B*1516195
IF IS_ENTITY_ALIVE(mvTrain.vehicle)
AND NOT IS_ENTITY_ON_FIRE(PLAYER_PED_ID())
// Handle Trevor being on floor after bailing out etc. by respotting
IF IS_PED_RAGDOLL(PLAYER_PED_ID())
OR IS_PED_GETTING_UP(PLAYER_PED_ID())
OR IS_ENTITY_IN_AIR(PLAYER_PED_ID())
FLOAT fGroundZ
VECTOR vTmpTrev
vTmpTrev = GET_ENTITY_COORDS(PLAYER_PED_ID())
vTmpTrev.z = vTmpTrev.z + 1.0
IF GET_GROUND_Z_FOR_3D_COORD(vTmpTrev, fGroundZ)
vTmpTrev.z = fGroundZ
SET_ENTITY_COORDS(PLAYER_PED_ID(), vTmpTrev)
SET_ENTITY_FACING(PLAYER_PED_ID(), GET_ENTITY_COORDS(mvTrain.vehicle))
FORCE_PED_AI_AND_ANIMATION_UPDATE(PLAYER_PED_ID())
ENDIF
ELSE
SET_ENTITY_FACING(PLAYER_PED_ID(), GET_ENTITY_COORDS(mvTrain.vehicle))
FORCE_PED_AI_AND_ANIMATION_UPDATE(PLAYER_PED_ID())
ENDIF
ENDIF
ENDIF
ELIF NOT bExplodedVehicle
AND fRealTime >= 0.25
IF IS_ENTITY_ALIVE(mvPlayerCar.vehicle)
SHAKE_CAM(csOutroCameras[0], "MEDIUM_EXPLOSION_SHAKE", 0.1666)
EXPLODE_VEHICLE_IN_CUTSCENE(mvPlayerCar.vehicle)
PLAY_SOUND_FROM_ENTITY(iCrashID, "CRASH", mvPlayerCar.vehicle, "NIGEL_03_SOUNDSET")
bExplodedVehicle = TRUE
ENDIF
ENDIF
IF bCutsceneLoop
AND bFPFlashNeeded
AND fRealTime >= 6.7
CPRINTLN(DEBUG_MISSION, "REQUESTED FPS FLASH")
ANIMPOSTFX_PLAY("CamPushInNeutral", 0, FALSE)
PLAY_SOUND_FRONTEND(-1, "1st_Person_Transition", "PLAYER_SWITCH_CUSTOM_SOUNDSET")
bFPFlashNeeded = FALSE
ENDIF
// Break the loop and mark as skipped if button pressed and CS isn't already exiting
IF IS_CUTSCENE_SKIP_BUTTON_JUST_PRESSED_WITH_DELAY()
AND bCutsceneLoop
bCutsceneLoop = FALSE
bOutroWasSkipped = TRUE
ENDIF
IF NOT bCutsceneLoop
// Cutscene completed or was skipped
SET_TIME_SCALE(1.0)
sProgress = SP_CLEANUP
ENDIF
BREAK
CASE SP_CLEANUP
REPLAY_STOP_EVENT()
// See if cutscene was skipped
IF bOutroWasSkipped
// If the car is "OK" we didn't get far enough for it to be done in so explode it now
IF IS_ENTITY_ALIVE(mvPlayerCar.vehicle)
// SET_ENTITY_COORDS(mvPlayerCar.vehicle, << 2615.9702, 1878.9554, 26.4716 >>)
// SET_ENTITY_HEADING(mvPlayerCar.vehicle, 284.47)
EXPLODE_VEHICLE_IN_CUTSCENE(mvPlayerCar.vehicle, TRUE)
ENDIF
ENDIF
// Back to normal cams
STOP_SCRIPT_GLOBAL_SHAKING(TRUE)
RENDER_SCRIPT_CAMS(FALSE, FALSE)
// Restore hud etc. + Enable phone etc.
RC_END_CUTSCENE_MODE()
SET_GAMEPLAY_CAM_RELATIVE_HEADING()
FOR iCount = 0 TO NUM_OUTRO_CAMERAS-1
DESTROY_CAM(csOutroCameras[iCount])
ENDFOR
// Put petrol tank fire out - unless car is within range still, don't want to put vehicle fire out
IF DOES_ENTITY_EXIST(mvPlayerCar.vehicle)
IF GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(mvPlayerCar.vehicle, FALSE), vecFireLocator) > 25
STOP_FIRE_IN_RANGE(vecFireLocator, 20)
ENDIF
ENDIF
IF IS_AUDIO_SCENE_ACTIVE("NIGEL_03_MIX_SETTINGS")
STOP_AUDIO_SCENE("NIGEL_03_MIX_SETTINGS")
ENDIF
// VIDEO EDITOR
FLOAT fRecordTime
fRecordTime = (TO_FLOAT(iCameraDuration[0]+iCameraDuration[1]))/1000
IF bSkinOfTeethStatHappened
// Record a back a bit further if player thrillingly bailed out
REPLAY_RECORD_BACK_FOR_TIME(fRecordTime+2.5, 2.0, REPLAY_IMPORTANCE_LOWEST)
ELSE
// Just record cutscene
REPLAY_RECORD_BACK_FOR_TIME(fRecordTime, 2.0, REPLAY_IMPORTANCE_LOWEST)
ENDIF
sProgress = SP_SETUP
mStage = MS_ENDING_PHONE_CALL
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Mission stage PROC
/// Trevor calls Nigel after reaching whatever sort of conclusion
PROC STAGE_ENDING_PHONE_CALL()
IF NOT bAddedNigelForPhoneConv
CPRINTLN(DEBUG_MISSION, "NIGEL3::: Adding Nigel back in for ending call.")
ADD_PED_FOR_DIALOGUE(mConversationStruct, CONVPED_NIGE, NULL, "NIGEL")
bAddedNigelForPhoneConv = TRUE
ENDIF
SWITCH sProgress
CASE SP_SETUP
IF NOT IS_PED_RAGDOLL(PLAYER_PED_ID())
AND NOT IS_PED_GETTING_UP(PLAYER_PED_ID())
AND NOT IS_ENTITY_ON_FIRE(PLAYER_PED_ID())
AND NOT IS_ENTITY_IN_AIR(PLAYER_PED_ID())
IF PLAYER_CALL_CHAR_CELLPHONE(mConversationStruct, CHAR_NIGEL, "NIGE3AU", "NIGEL3_ZA", CONV_PRIORITY_MEDIUM)
iCallLine = -1
sProgress = SP_RUNNING
ENDIF
SET_CINEMATIC_BUTTON_ACTIVE(TRUE)
ENDIF
BREAK
CASE SP_RUNNING
// Wait for conversation to finish
IF IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
// Monitor phone call progress
IF GET_CURRENT_SCRIPTED_CONVERSATION_LINE() > iCallLine
iCallLine = GET_CURRENT_SCRIPTED_CONVERSATION_LINE()
CPRINTLN(DEBUG_MISSION, "STAGE_ENDING_PHONE_CALL: SP_RUNNING: Call progressed to line ", iCallLine)
ENDIF
ELSE
// Check how the call was ended
IF HAS_CELLPHONE_CALL_FINISHED()
IF HAS_CELLPHONE_JUST_BEEN_FORCED_AWAY()
// Player
IF iCallLine >= 0
CPRINTLN(DEBUG_MISSION, "STAGE_ENDING_PHONE_CALL: SP_RUNNING: Call interrupted after it had started correctly, line ", iCallLine, ", going to cleanup")
sProgress = SP_CLEANUP
ELSE
CPRINTLN(DEBUG_MISSION, "STAGE_ENDING_PHONE_CALL: SP_RUNNING: Call interrupted before any lines had played, restarting")
sProgress = SP_SETUP
ENDIF
ELSE
CPRINTLN(DEBUG_MISSION, "STAGE_ENDING_PHONE_CALL: SP_RUNNING: Player hung up phone deliberately, going to cleanup")
sProgress = SP_CLEANUP
ENDIF
ENDIF
ENDIF
BREAK
CASE SP_CLEANUP
// Don't let mission pass if player is burning to death they will restart at checkpoint - B*1053595
// Updated to include ragdoll/get-up/air checks after seeing ped re-ignite immediately when getting up after extinguishing
IF NOT IS_PED_RAGDOLL(PLAYER_PED_ID())
AND NOT IS_PED_GETTING_UP(PLAYER_PED_ID())
AND NOT IS_ENTITY_ON_FIRE(PLAYER_PED_ID())
AND NOT IS_ENTITY_IN_AIR(PLAYER_PED_ID())
SAFE_RELEASE_VEHICLE(mvPlayerCar.vehicle)
bCelebWasReleased = FALSE
Script_Passed()
ENDIF
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Mission stage PROC
/// If the player blows up the car at an arbitrary point
PROC STAGE_WRECK_PASS()
bCelebWasReleased = FALSE
IF NOT IS_BIT_SET(g_savedGlobals.sRandomChars.g_iWebsiteQueryBit, ENUM_TO_INT(RC_NWS_NGL3_DINAPOLI_NOT_KILLED_BY_TRAIN))
SET_BIT(g_savedGlobals.sRandomChars.g_iWebsiteQueryBit, ENUM_TO_INT(RC_NWS_NGL3_DINAPOLI_NOT_KILLED_BY_TRAIN))
CPRINTLN(DEBUG_INTERNET, GET_THIS_SCRIPT_NAME(), " g_savedGlobals.sRandomChars.g_iWebsiteQueryBit, RC_NWS_NGL3_DINAPOLI_NOT_KILLED_BY_TRAIN) set")
ENDIF
IF DOES_ENTITY_EXIST(pedNigel)
IF NOT IS_ENTITY_ON_SCREEN(pedNigel)
OR IS_ENTITY_OCCLUDED(pedNigel)
SAFE_DELETE_PED(pedNigel)
ENDIF
ENDIF
IF DOES_ENTITY_EXIST(pedMrsThornhill)
IF NOT IS_ENTITY_ON_SCREEN(pedMrsThornhill)
OR IS_ENTITY_OCCLUDED(pedMrsThornhill)
SAFE_DELETE_OBJECT(oiHandbag)
SAFE_DELETE_PED(pedMrsThornhill)
ENDIF
ENDIF
Script_Passed()
ENDPROC
/// PURPOSE:
/// Mission stage PROC
/// If the player releases Al
PROC STAGE_RELEASE_CUTSCENE()
SWITCH sProgress
CASE SP_SETUP
REQUEST_ANIM_DICT(sReleaseAnimDict)
IF GET_SHAPE_TEST_RESULT(stiBootArea[0], iShapetestResult[0], vecShapetestResult[0][0], vecShapetestResult[0][1], hitEntity[0]) != SHAPETEST_STATUS_RESULTS_NOTREADY
AND GET_SHAPE_TEST_RESULT(stiBootArea[1], iShapetestResult[1], vecShapetestResult[1][0], vecShapetestResult[1][1], hitEntity[0]) != SHAPETEST_STATUS_RESULTS_NOTREADY
IF iShapetestResult[0] = 1
OR iShapetestResult[1] = 1
bUseShortCam = TRUE
ELSE
bUseShortCam = FALSE
ENDIF
ELSE
bUseShortCam = TRUE
ENDIF
IF HAS_ANIM_DICT_LOADED(sReleaseAnimDict)
AND HAS_MODEL_LOADED(mCash)
AND IS_ENTITY_ALIVE(mvPlayerCar.vehicle)
RC_START_CUTSCENE_MODE(GET_ENTITY_COORDS(mvPlayerCar.vehicle))
iCutsceneTimer = GET_GAME_TIMER()
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
AND IS_ENTITY_ALIVE(pedDiNapoli)
SET_INSTANCE_PRIORITY_HINT(INSTANCE_HINT_NONE)
KILL_FACE_TO_FACE_CONVERSATION_DO_NOT_FINISH_LAST_LINE()
SET_VEHICLE_WILL_FORCE_OTHER_VEHICLES_TO_STOP(mvPlayerCar.vehicle, TRUE)
DETACH_ENTITY(pedDiNapoli, FALSE, TRUE)
SET_ENTITY_VISIBLE(pedDiNapoli, TRUE)
FREEZE_ENTITY_POSITION(mvPlayerCar.vehicle, TRUE)
vecScenePosition = GET_ENTITY_COORDS(mvPlayerCar.vehicle)
vecSceneRotation = GET_ENTITY_ROTATION(mvPlayerCar.vehicle)
iSceneID = CREATE_SYNCHRONIZED_SCENE(vecScenePosition, vecSceneRotation)
camReleaseCamera = CREATE_CAM("DEFAULT_ANIMATED_CAMERA", FALSE)
IF bUseShortCam
PLAY_SYNCHRONIZED_CAM_ANIM(camReleaseCamera, iSceneID, "out_trunk_cam_alt", sReleaseAnimDict)
ELSE
PLAY_SYNCHRONIZED_CAM_ANIM(camReleaseCamera, iSceneID, "out_trunk_cam", sReleaseAnimDict)
ENDIF
SET_CAM_ACTIVE(camReleaseCamera, TRUE)
RENDER_SCRIPT_CAMS(TRUE, FALSE)
oCash = CREATE_OBJECT(mCash, vecScenePosition)
TASK_SYNCHRONIZED_SCENE(PLAYER_PED_ID(),iSceneID, sReleaseAnimDict, "out_trunk_trevor" , INSTANT_BLEND_IN, REALLY_SLOW_BLEND_OUT, SYNCED_SCENE_TAG_SYNC_OUT) //, RBF_NONE, INSTANT_BLEND_IN, IK_CONTROL_FLAGS)
TASK_SYNCHRONIZED_SCENE(pedDiNapoli, iSceneID, sReleaseAnimDict, "out_trunk_al" , INSTANT_BLEND_IN, REALLY_SLOW_BLEND_OUT)
PLAY_SYNCHRONIZED_ENTITY_ANIM(mvPlayerCar.vehicle, iSceneID, "out_trunk_car", sReleaseAnimDict, INSTANT_BLEND_IN)
PLAY_SYNCHRONIZED_ENTITY_ANIM(oCash, iSceneID, "out_trunk_cash", sReleaseAnimDict, INSTANT_BLEND_IN)
SET_SYNCHRONIZED_SCENE_HOLD_LAST_FRAME(iSceneID, FALSE)
// Clear the area
CLEAR_AREA_OF_VEHICLES(GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvPlayerCar.vehicle, << 0.0, -3.0, -0.47 >>), 10, TRUE) // Increased value - B*1072486
CLEAR_AREA_OF_VEHICLES(GET_ENTITY_COORDS(mvPlayerCar.vehicle), 10, TRUE) // Extra clear - B*1072486
// B*1931290
IF bTrainsAreActive
CPRINTLN(DEBUG_MISSION, "STAGE_RELEASE_CUTSCENE: SET_RANDOM_TRAINS(FALSE), DELETE_ALL_TRAINS()")
SET_RANDOM_TRAINS(FALSE)
DELETE_ALL_TRAINS()
bTrainsAreActive = FALSE
ENDIF
REPLAY_START_EVENT(REPLAY_IMPORTANCE_LOW)
bCutsceneSkipped = FALSE
bCheckedFinalFloater = FALSE
sProgress = SP_RUNNING
ENDIF
ENDIF
BREAK
CASE SP_RUNNING
IF IS_ENTITY_ALIVE(mvPlayerCar.vehicle)
SET_VEHICLE_WILL_FORCE_OTHER_VEHICLES_TO_STOP(mvPlayerCar.vehicle, TRUE)
ENDIF
// Check for cutscene skip
IF IS_CUTSCENE_SKIP_BUTTON_JUST_PRESSED_WITH_DELAY()
bCutsceneSkipped = TRUE
IF IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
KILL_FACE_TO_FACE_CONVERSATION_DO_NOT_FINISH_LAST_LINE()
ENDIF
ENDIF
// Initial conversation start delay
IF NOT bReleaseConvStarted
AND GET_GAME_TIMER() - iCutsceneTimer > 500
AND NOT bCutsceneSkipped
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
AND IS_ENTITY_ALIVE(mvPlayerCar.vehicle)
// Start the conversation
IF CREATE_CONVERSATION(mConversationStruct, "NIGE3AU", "NIGEL3_R1", CONV_PRIORITY_MEDIUM)
bReleaseConvStarted = TRUE
ENDIF
ENDIF
ENDIF
// Check whether we need an early out to stop Trevor dropping to the ground
IF GET_SYNCHRONIZED_SCENE_PHASE(iSceneID) >= 0.852 // Camera cut
AND NOT bCheckedFinalFloater
BOOL bKeepOnWalkin
bKeepOnWalkin = TRUE
VECTOR vTemp1, vTemp2
FLOAT fTmp1, fTmp2
vTemp1 = GET_ANIM_INITIAL_OFFSET_POSITION(sReleaseAnimDict, "out_trunk_trevor", vecScenePosition, vecSceneRotation, 0.852)
vTemp2 = GET_ANIM_INITIAL_OFFSET_POSITION(sReleaseAnimDict, "out_trunk_trevor", vecScenePosition, vecSceneRotation, 1)
IF GET_GROUND_Z_FOR_3D_COORD(vTemp1 ,fTmp1)
AND GET_GROUND_Z_FOR_3D_COORD(vTemp2 ,fTmp2)
IF fTmp1 - fTmp2 > 0.25 // Drop
// OR fTmp2 - fTmp1 > 0.1 // Bump
CPRINTLN(DEBUG_MISSION, "Outro early out to stop the drop -- Boot loc Z: ", fTmp1, " -- Side loc Z: ", fTmp2, " -- Diff: ", fTmp1 - fTmp2, " -- Phase: ", GET_SYNCHRONIZED_SCENE_PHASE(iSceneID))
CLEAR_PED_TASKS(PLAYER_PED_ID())
bCutsceneSkipped = FALSE // Reset this because it would set player to end location
sProgress = SP_CLEANUP
bKeepOnWalkin = FALSE
ENDIF
// Set coord z
vTemp1.z = fTmp1
ELSE
CPRINTLN(DEBUG_MISSION, "Couldn't get the ground heights for some reason")
ENDIF
bCheckedFinalFloater = TRUE
IF bKeepOnWalkin
CPRINTLN(DEBUG_MISSION, "Swapping Trevor out to a non-sync anim now")
vTemp2 = GET_ANIM_INITIAL_OFFSET_ROTATION(sReleaseAnimDict, "out_trunk_trevor", vecScenePosition, vecSceneRotation, GET_SYNCHRONIZED_SCENE_PHASE(iSceneID))
STOP_SYNCHRONIZED_ENTITY_ANIM(PLAYER_PED_ID(), INSTANT_BLEND_OUT, TRUE)
SET_ENTITY_HEADING(PLAYER_PED_ID(), vTemp2.z)
SET_ENTITY_COORDS(PLAYER_PED_ID(), vTemp1, FALSE)
TASK_PLAY_ANIM(PLAYER_PED_ID(), sReleaseAnimDict, "out_trunk_trevor", INSTANT_BLEND_IN, REALLY_SLOW_BLEND_OUT, -1, AF_DEFAULT, GET_SYNCHRONIZED_SCENE_PHASE(iSceneID))
FORCE_PED_AI_AND_ANIMATION_UPDATE(PLAYER_PED_ID())
ENDIF
// Remove cash object - no longer needed and goes out of whack if walk is active
SAFE_DELETE_OBJECT(oCash)
// Check whether it's time for normal out
ELIF GET_SYNCHRONIZED_SCENE_PHASE(iSceneID) > 0.99
OR bCutsceneSkipped
CPRINTLN(DEBUG_MISSION, "Outro going out on phase ", GET_SYNCHRONIZED_SCENE_PHASE(iSceneID))
sProgress = SP_CLEANUP
ENDIF
BREAK
CASE SP_CLEANUP
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
AND IS_ENTITY_ALIVE(pedDiNapoli)
AND IS_ENTITY_ALIVE(mvPlayerCar.vehicle)
IF bCutsceneSkipped
KILL_FACE_TO_FACE_CONVERSATION_DO_NOT_FINISH_LAST_LINE()
IF GET_SYNCHRONIZED_SCENE_PHASE(iSceneID) < 0.99
SET_SYNCHRONIZED_SCENE_PHASE(iSceneID, 0.99)
ENDIF
ENDIF
STOP_SYNCHRONIZED_ENTITY_ANIM(mvPlayerCar.vehicle, INSTANT_BLEND_OUT, TRUE)
FORCE_PED_MOTION_STATE(PLAYER_PED_ID(), MS_ON_FOOT_IDLE, TRUE, FAUS_DEFAULT, TRUE)
SET_VEHICLE_DOOR_SHUT(mvPlayerCar.vehicle, SC_DOOR_BOOT, TRUE)
// SET_ENTITY_COORDS_NO_OFFSET(pedDiNapoli, GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvPlayerCar.vehicle, vecAlSpawnOffset))//<< -1.0, -3.05, -0.47 >>)) // << -1.5, -2.75, 0.0 >>))
// SET_ENTITY_HEADING(pedDiNapoli, GET_ENTITY_HEADING(mvPlayerCar.vehicle)+180)
CLEAR_PED_TASKS(pedDiNapoli)
TASK_SMART_FLEE_PED(pedDiNapoli, PLAYER_PED_ID(), 3000, -1)
SET_PED_KEEP_TASK(pedDiNapoli, TRUE)
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(pedDiNapoli, TRUE)
IF DOES_CAM_EXIST(camReleaseCamera)
RENDER_SCRIPT_CAMS(FALSE, FALSE)
SET_CAM_ACTIVE(camReleaseCamera, FALSE)
DESTROY_CAM(camReleaseCamera)
ENDIF
IF bCutsceneSkipped
SET_GAMEPLAY_CAM_RELATIVE_HEADING()
ELSE
STOP_RENDERING_SCRIPT_CAMS_USING_CATCH_UP()
ENDIF
CLEAR_PED_TASKS(PLAYER_PED_ID())
SAFE_DELETE_OBJECT(oCash)
// If we played the short cam, Al may be somewhere dodgy
IF bUseShortCam
SAFE_DELETE_PED(pedDiNapoli)
ENDIF
RC_END_CUTSCENE_MODE()
// Give Trevor money
IF iConvoCount = 0
// Bump the conversation counter if the player was really keen on the release
iConvoCount = 1
ENDIF
CREDIT_BANK_ACCOUNT(GET_CURRENT_PLAYER_PED_ENUM(), BAAC_UNLOGGED_SMALL_ACTION, ciConvos[iConvoCount-1].completedReward)
REPLAY_STOP_EVENT()
// End the mission
bCelebWasReleased = TRUE
Script_Passed()
ENDIF
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Mission stage PROC
/// Handles stupid stuff happening during our wait to fail the mission
PROC STAGE_FAIL_FADE()
SWITCH sProgress
CASE SP_SETUP
SAFE_REMOVE_BLIP(biGotoBlip)
CLEAR_PRINTS()
IF IS_STRING_NULL_OR_EMPTY(sFailReason)
// Guard against null string case
SCRIPT_ASSERT("Reached STAGE_FAIL_FADE with NULL/empty fail reason string - Bug must explain what you were doing prior to this message.")
sFailReason = "DEFAULT"
ENDIF
IF ARE_STRINGS_EQUAL(sFailReason, "DEFAULT")
// No fail reason
Random_Character_Failed()
ELSE
Random_Character_Failed_With_Reason(sFailReason)
ENDIF
sProgress = SP_RUNNING
BREAK
CASE SP_RUNNING
IF GET_MISSION_FLOW_SAFE_TO_CLEANUP()
CPRINTLN(DEBUG_MISSION, "NIGEL3::: Fail delay thing is finishing...")
// If the player failed during the ram stage or later, they may be in an unsafe location
// Respawn at power plant bridge
IF bRamStageStarted
MISSION_FLOW_SET_FAIL_WARP_LOCATION(<<2555.9321, 1645.3575, 27.9926>>, 89.0)
ENDIF
KILL_FACE_TO_FACE_CONVERSATION_DO_NOT_FINISH_LAST_LINE()
// Do the brutal cleanup
SAFE_DELETE_PED(pedDiNapoli)
SAFE_DELETE_PED(pedNigel)
SAFE_DELETE_OBJECT(oiHandbag)
SAFE_DELETE_PED(pedMrsThornhill)
IF DOES_ENTITY_EXIST(mvPlayerCar.vehicle)
REMOVE_VEHICLE_UPSIDEDOWN_CHECK(mvPlayerCar.vehicle)
ENDIF
SAFE_DELETE_VEHICLE(mvPlayerCar.vehicle)
IF DOES_ENTITY_EXIST(mvTrain.vehicle)
DELETE_MISSION_TRAIN(mvTrain.vehicle)
ENDIF
Script_Cleanup()
ELSE
// not finished fading out
// you may want to handle dialogue etc here.
ENDIF
BREAK
ENDSWITCH
ENDPROC
// ===========================================================================================================
// DEBUG FUNCTIONS
// ===========================================================================================================
// PURPOSE: Check for Forced Pass or Fail and skips
PROC DO_DEBUG()
#IF IS_DEBUG_BUILD
INT iNewStage
// Check for Pass
IF (IS_KEYBOARD_KEY_JUST_PRESSED(KEY_S))
WAIT_FOR_CUTSCENE_TO_STOP()
CLEAR_ALL_MISSION_OBJECTIVES()
SAFE_DELETE_PED(pedDiNapoli)
Script_Passed()
ELIF (IS_KEYBOARD_KEY_JUST_PRESSED(KEY_F))
WAIT_FOR_CUTSCENE_TO_STOP()
CLEAR_ALL_MISSION_OBJECTIVES()
mStage = MS_FAIL_FADE
sProgress = SP_SETUP
ELIF (IS_KEYBOARD_KEY_JUST_PRESSED(KEY_J))// AND bFinishedSkipping)
DO_A_SKIP(SKIP_FORWARD)
ELIF (IS_KEYBOARD_KEY_JUST_PRESSED(KEY_P))// AND bFinishedSkipping)
DO_A_SKIP(SKIP_BACKWARD)
ELIF LAUNCH_MISSION_STAGE_MENU(mSkipMenu, iNewStage)
eTargetStage = INT_TO_ENUM(MISSION_SKIP_STAGE, iNewStage)
JUMP_TO_STAGE(eTargetStage)
ENDIF
// Toggle debug drawing for debug draw of train camera safe zones
IF bDebugDrawTrainCamZones != bDebugDrawTrainCamZonesLastFrame
bDebugDrawTrainCamZonesLastFrame = bDebugDrawTrainCamZones
IF bDebugDrawTrainCamZones
SET_DEBUG_ACTIVE(TRUE)
SET_DEBUG_LINES_AND_SPHERES_DRAWING_ACTIVE(TRUE)
ELSE
SET_DEBUG_ACTIVE(FALSE)
SET_DEBUG_LINES_AND_SPHERES_DRAWING_ACTIVE(FALSE)
ENDIF
ENDIF
// Toggle script invulnerability
IF bDebugToggleInvuln
IF GET_PLAYER_INVINCIBLE(PLAYER_ID())
SET_PLAYER_INVINCIBLE(PLAYER_ID(), FALSE)
CPRINTLN(DEBUG_MISSION, "*** INVINCIBILITY OFF ***")
ELSE
SET_PLAYER_INVINCIBLE(PLAYER_ID(), TRUE)
CPRINTLN(DEBUG_MISSION, "*** INVINCIBILITY ON ***")
ENDIF
bDebugToggleInvuln = FALSE
ENDIF
// Handbag offset
IF bDebugReattach
IF IS_ENTITY_ALIVE(pedMrsThornhill)
AND DOES_ENTITY_EXIST(oiHandbag)
CPRINTLN(DEBUG_MISSION, "attach radio")
DETACH_ENTITY(oiHandbag, FALSE)
ATTACH_ENTITY_TO_ENTITY(oiHandbag, pedMrsThornhill, GET_PED_BONE_INDEX(pedMrsThornhill, BONETAG_L_CLAVICLE), vecBagOffset, vecBagAngles)
bDebugReattach = FALSE
ENDIF
ENDIF
#ENDIF
ENDPROC
// ===========================================================================================================
// Script Loop
// ===========================================================================================================
SCRIPT(g_structRCScriptArgs sRCLauncherDataIn)
CPRINTLN(DEBUG_MISSION, "NIGEL3::: Script starting... ")
sRCLauncherDataLocal = sRCLauncherDataIn
RC_TakeEntityOwnership(sRCLauncherDataLocal)
SET_MISSION_FLAG(TRUE)
// Setup callback when player is killed, arrested or goes to multiplayer
IF (HAS_FORCE_CLEANUP_OCCURRED(DEFAULT_FORCE_CLEANUP_FLAGS|FORCE_CLEANUP_FLAG_DEBUG_MENU))
PRINT_LAUNCHER_DEBUG("Force cleanup [TERMINATING]")
Random_Character_Failed()
IF mStage = MS_OUTRO_CUTSCENE
// B*1876191 - Trevor dying in cutscene
RC_END_CUTSCENE_MODE(DEFAULT, DEFAULT, TRUE, FALSE)
ENDIF
Script_Cleanup()
ENDIF
// Permanently grab the Nigel ped
IF DOES_ENTITY_EXIST(sRCLauncherDataLocal.pedID[0])
ASSIGN_PED_INDEX(pedNigel, sRCLauncherDataLocal.pedID[0])
ENDIF
// Remove DiNapoli Launcher ped
SAFE_DELETE_PED(sRCLauncherDataLocal.pedID[1])
DATA_INIT()
// Check whether this is a replay
IF Is_Replay_In_Progress()
CPRINTLN(DEBUG_MISSION, "NIGEL3 is in a replay")
INT iReplayStage = GET_REPLAY_MID_MISSION_STAGE()
IF g_bShitskipAccepted
iReplayStage++
ENDIF
SWITCH iReplayStage
CASE 0
CPRINTLN(DEBUG_MISSION, "NIGEL3::: CP_AFTER_INTRO")
CHECKPOINT_AFTER_INTRO(TRUE)
BREAK
CASE CP_TRAIN
CPRINTLN(DEBUG_MISSION, "NIGEL3::: CP_TRAIN")
CHECKPOINT_TRAIN(TRUE)
BREAK
CASE CP_OUTRO_CALL
CPRINTLN(DEBUG_MISSION, "NIGEL3::: CP_OUTRO_CALL")
CHECKPOINT_OUTRO_CALL(TRUE)
BREAK
DEFAULT
SCRIPT_ASSERT("Replay in progress: Unknown checkpoint selected")
BREAK
ENDSWITCH
ENDIF
// Loop within here until the mission passes or fails
WHILE(TRUE)
REPLAY_CHECK_FOR_EVENT_THIS_FRAME("SF_VSTLA")
UPDATE_MISSION_NAME_DISPLAYING(sRCLauncherDataLocal.sIntroCutscene)
IF mStage = MS_FAIL_FADE
STAGE_FAIL_FADE()
ELSE
IF NOT MISSION_FAIL_CHECKS()
RANDOM_CHAR_HANDLER()
// Check debug keys
DO_DEBUG()
SWITCH mStage
CASE MS_INIT
STAGE_INIT()
BREAK
CASE MS_LEADIN
STAGE_LEADIN()
BREAK
CASE MS_INTRO_CUTSCENE
STAGE_INTRO_CUTSCENE()
BREAK
CASE MS_GET_IN_CAR
STAGE_GET_IN_CAR()
BREAK
CASE MS_DRIVING
STAGE_DRIVING()
BREAK
CASE MS_LOSE_COPS
STAGE_LOSE_COPS()
BREAK
CASE MS_PLAYER_EXITS_CAR
STAGE_PLAYER_EXITS_CAR()
BREAK
CASE MS_RAM_TRAIN
STAGE_RAM_TRAIN()
BREAK
CASE MS_MISSED_RAM
STAGE_MISSED_RAM()
BREAK
CASE MS_RELEASE_CUTSCENE
STAGE_RELEASE_CUTSCENE()
BREAK
CASE MS_OUTRO_CUTSCENE
STAGE_OUTRO_CUTSCENE()
BREAK
CASE MS_ENDING_PHONE_CALL
STAGE_ENDING_PHONE_CALL()
BREAK
CASE MS_WRECK_PASS
STAGE_WRECK_PASS()
BREAK
ENDSWITCH
ENDIF
ENDIF
WAIT(0)
ENDWHILE
// Script should never reach here. Always terminate with cleanup function.
ENDSCRIPT