5086 lines
164 KiB
Python
Executable File
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
|
|
|