//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