Files
gtav-src/script/dev_ng/singleplayer/scripts/Ambient/MichaelEvents/ME_Tracey1.sc
T
2025-09-29 00:52:08 +02:00

6582 lines
231 KiB
Python
Executable File

//Compile out Title Update changes to header functions.
//Must be before includes.
//CONST_INT USE_TU_CHANGES 0 // Removed by Kenneth R.
USING "rage_builtins.sch"
USING "globals.sch"
USING "commands_camera.sch"
USING "commands_pad.sch"
USING "commands_script.sch"
USING "commands_vehicle.sch"
USING "flow_public_core_override.sch"
USING "script_ped.sch"
USING "script_blips.sch"
USING "dialogue_public.sch"
USING "comms_control_public.sch"
USING "chase_hint_cam.sch"
USING "RC_Helper_Functions.sch"
USING "rc_threat_public.sch"
USING "family_public.sch"
USING "buddy_head_track_public.sch"
USING "taxi_functions.sch"
USING "player_scene_schedule.sch"
USING "commands_recording.sch"
#IF IS_DEBUG_BUILD
USING "select_mission_stage.sch"
#ENDIF
// *****************************************************************************************
// *****************************************************************************************
// *****************************************************************************************
//
// MISSION NAME : ME_Tracey1.sc
// AUTHOR : Tom Waters
// DESCRIPTION : Tracey has phoned Michael in a panic - she is being stalked by
// a lunatic/loser as she drives around Los Santos. Michael must
// reach her, find the stalker and deal with him.
//
// *****************************************************************************************
// *****************************************************************************************
// *****************************************************************************************
//*************************************************************************************************************************************************
// :ENUMS:
//*************************************************************************************************************************************************
// Track progress through the mission
ENUM MISSION_STAGE_TRACK
MST_INIT,
MST_DRIVE_TO_TRACEY,
MST_WAIT_FOR_PLAYER,
MST_DRIVE_TO_STALKER,
MST_TRIGGER_STALKER,
MST_CHASE,
MST_STALKER_WRECKED,
MST_DRIVE_HOME,
MST_AT_HOME,
MST_FAIL_CALL,
// MST_FAIL_TRACEY_DRIVE_OFF,
MST_LOSE_WANTED,
MST_FAIL_FADE
ENDENUM
// Progression within a stage
ENUM STAGE_PROGRESS
SP_SETUP,
SP_RUNNING,
SP_CLEANUP
ENDENUM
// Looped route conversation progress track
ENUM CONVERSATION_PROGRESS_TRACK
CPT_WAIT_TO_TRIGGER,
CPT_PICK_CONVERSATION,
CPT_START_CONVERSATION_STALKER,
CPT_START_RANDOM_STALKER, // Specific to stalker on foot chat
CPT_START_CONVERSATION_TRACEY_WTF,
CPT_START_CONVERSATION_TRACEY,
CPT_START_CONVERSATION_RAMARK,
CPT_START_CONVERSATION_STOPPED,
CPT_START_CONVERSATION_ESCAPING,
CPT_START_CONVERSATION,
CPT_WAIT_TO_FINISH,
CPT_SPENT
ENDENUM
// Warning/fail call track
ENUM WARNING_FAIL_CALLS
WFC_SETUP,
WFC_START_WARNING_CALL,
WFC_WAIT_FOR_WARNING,
WFC_CHECK_FOR_FAIL,
WFC_ARRIVED_WAIT,
WFC_ARRIVED_WARN,
WFC_ARRIVED_CHECK_FOR_FAIL,
WFC_DONE
ENDENUM
// Track progress in meeting up with Tracey stage
ENUM TRACEY_CAR_ENTRY
TCE_WAIT_FOR_PLAYER_RANGE,
TCE_WAIT_FOR_CONVERSATION_START,
TCE_WAIT_FOR_PLAYER_IN_CAR
ENDENUM
// Tracks the conversation and destination driving to
ENUM TRACEY_DESTINATION_STATE
TDS_START_CHAT,
TDS_WAITING_FOR_CHAT_TO_START,
TDS_WAITING_FOR_BLIP_LINE,
TDS_WAITING_TO_DELAY_QUEUING_EXTRA_CHAT,
TDS_WAITING_TO_QUEUE_EXTRA_CHAT,
TDS_DRIVING_TO_POINT,
TDS_DO_DELAY,
TDS_LOOK_AROUND
ENDENUM
// Tracks the head IK lookat on the stalker
ENUM STALKER_HEAD_TRACK_CONTROL
SHTC_WAIT_FOR_TRIGGER_EVENT,
SHTC_PROX_LOOKAT,
SHTC_RAM_LOOKAT
ENDENUM
//*************************************************************************************************************************************************
// :STRUCTS:
//*************************************************************************************************************************************************
// Stores info needed for spawning a vehicle
STRUCT MISSION_VEHICLE
VEHICLE_INDEX vehicle
MODEL_NAMES model
BOOL carModelRequestActive
PED_INDEX driver
MODEL_NAMES driverModel
BOOL pedModelRequestActive
VECTOR location
FLOAT heading
FLOAT initialSpeed
BLIP_INDEX blip
ENDSTRUCT
// Objectives
STRUCT MISSION_OBJECTIVE
INT iDisplayMax
INT iDisplayCount
STRING sTextLabel
ENDSTRUCT
// Holds converstaion info
STRUCT CONVERSATION_INFO
INT startDelay
STRING txtBlock
STRING rootBlock
ENDSTRUCT
// Stores a non-axially aligned volume
STRUCT NAA_VOLUME
VECTOR vEnds[2]
FLOAT fWidth
ENDSTRUCT
// Holds road disabler info
STRUCT ROAD_BLOCK
NAA_VOLUME trigBox
NAA_VOLUME blockBox[2]
ENDSTRUCT
// Holds info for the places Tracey wants player to drive to
STRUCT DESTINATION_POINT
VECTOR location
FLOAT range
STRING chatLabel
STRING extraChatLabel
STRING bonkersChatLabel
INT blipUpdateLine
ENDSTRUCT
//*************************************************************************************************************************************************
// :CONSTANTS:
//*************************************************************************************************************************************************
// Checkpoints
CONST_INT CP_MET_TRACEY 2 // Met Tracey at Vinewood plaza
CONST_INT CP_STALKER_APPEARS 3 // Stalker gets spawned
CONST_INT CP_DRIVE_HOME 4 // Stalker scared off, drive Tracey home
CONST_INT CP_DRIVE_HOME_DEAD 5 // Stalker dead, drive Tracey home
// Distances
CONST_FLOAT SAFE_TRIGGER_DISTANCE 200.0 // Close enough that Tracey will actually spawn if we ask
CONST_FLOAT TRIGGER_OFF_DISTANCE 300.0 // Flop back to reenable warning/fail calls at this range
CONST_FLOAT TRACEY_MET_DIST 20.0 // Close enough to Tracey to start mission proper
CONST_FLOAT WARN_DIS_TOO_FAR 60.0 // Getting too far from Tracey
CONST_FLOAT ABANDON_TRACEY_DIST 160.0 // Fail for abandoning Tracey
CONST_FLOAT STALKER_ESCAPE_DIST 220.0 // Stalker car escape range
CONST_FLOAT CHASE_FLASH_DIST 0.75 // Distance that blip should flash at
CONST_FLOAT STALKER_FOOT_ESCAPE_DIST 80.0 // Stalker on-foot escape range
CONST_FLOAT STALKER_FOOT_MUTE_DIST 25.0 // Cut off distance for stalker on-foot chat triggering
// Timings
CONST_INT RENDEZVOUS_TIMER 5 // Time the player has to reach Tracey, in in-game hours
CONST_INT WARNING_TIMER 2 // Time until player gets warning call from Tracey, in in-game hours
CONST_INT STALKER_SPOT_TIMER 8000 // Maximum time it takes Tracey to spot the stalker in milliseconds, if range trigger does not fire first
//CONST_INT TRACEY_BORED_DELAY 20000 // Time delay for Tracey waiting for player to get back in a car
CONST_INT ARRIVED_TIMER_WARN 15000 // How long will Tracey wait in the car park before complaining about the player screwing around on foot
CONST_INT ARRIVED_TIMER_FAIL 120000 // How long will Tracey wait in the car park if the player is screwing around on foot
CONST_INT DIRECTIONS_DELAY 4000 // Time that must pass between Tracey direction clips
CONST_INT BANTER_DELAY 4000 // Time that must pass between banter conversation clips
CONST_INT TOO_FAST_DELAY 4000 // Time that must pass between too fast conversation clips
CONST_INT NOT_GOING_DELAY 18000 // Time that must pass between not moving complaint clips in drive to stalker
CONST_INT NOT_GOING_DELAY_CHASE 6000 // Time that must pass between not moving complaint clips in chase section
CONST_INT CHASE_COMMENT_DELAY 4000 // Time that must pass between chase comment clips
CONST_INT CHASE_PROX_CHECK_TIME 10000 // Time gap between checks on stalker position vs player position in on-foot chase section
CONST_INT CHASE_AREA_CHECK_TIME 2000 // Time gap between checks on player car in front of stalker
// Video Editor timings
CONST_FLOAT SNUFF_TIMER_RETRORECORD 2.5 // How far back from the time of death should we record stalker murder incident (s)
CONST_INT SNUFF_TIMER_TRACEY_SAW 8000 // Wait for Tracey reaction dialogue if she saw the stalker being killed (ms)
CONST_INT SNUFF_TIMER_DIDNT_SEE 2000 // No need to record so much if stalker killed out of sight of Tracey (ms)
//
CONST_INT CT_PROX_CHECK 0
CONST_INT CT_AREA_CHECK 1
// Objective constants - if you add or remove any do not leave gaps and update NUM_MISSION_OBJECTIVES
CONST_INT MO_MET1INITDRV 0 // Go to the Vinewood Plaza.
CONST_INT MO_MET1TCAR 1 // Get in Tracey's car.
CONST_INT MO_MET1WAIT 2 // Wait for Tracey.
CONST_INT MO_MET1WANT 3 // Lose the cops.
CONST_INT MO_MET1CHASE 4 // Stop the stalker's car.
CONST_INT MO_MET1GETBKT 5 // Get back in Tracey's car.
CONST_INT MO_MET1DLT 6 // Return to Tracey.
CONST_INT MO_MET1HOME 7 // Go home.
CONST_INT MO_MET1HOTEL 8 // Go to the hotel.
CONST_INT NUM_MISSION_OBJECTIVES 9
// Array sizes
CONST_INT NUM_MICHAEL_COMMENTS 3 // Number of Michael monologues before meetup
CONST_INT NUM_ROUTE_CONVERSATIONS 3 // Number of Michael/Tracey conversations after meetup
CONST_INT NUM_BANTER_CHUNKS 10 // Max number of banter chunks available during conversations while driving to stalker
CONST_INT NUM_DESTINATIONS 4 // Number of destinations that Tracey suggests to Michael
CONST_INT NUM_CHASE_CONVERSATIONS_S 5 // Number of Michael/Stalker conversations in car chase
CONST_INT NUM_CHASE_CONVERSATIONS_T 4 // Number of Michael/Tracey conversations in car chase
CONST_INT NUM_FOOT_CONVERSATIONS_S 3 // Number of Michael/stalker conversations once stalker is on foot
CONST_INT NUM_FOOT_CONVERSATIONS_T 3 // Number of Michael/Tracey conversations once stalker is on foot
CONST_INT NUM_DRIVEHOME_CONVERSATIONS 9 // Number of Michael/Tracey conversation elements available in drive home
CONST_INT NUM_AUDIOSCENES 8 // Number of different audioscene states
// Audio scenes
CONST_INT AUDIOSCENE_NONE 0 // None
CONST_INT AUDIOSCENE_GO_TO_TRACEY 1 // M_E_TRACEY_GO_TO_TRACEY
CONST_INT AUDIOSCENE_ENTER_TRACEYS_CAR 2 // M_E_TRACEY_ENTER_TRACEYS_CAR
CONST_INT AUDIOSCENE_DRIVE_TO_STALKER 3 // M_E_TRACEY_FIND_STALKER
CONST_INT AUDIOSCENE_CHASE 4 // M_E_TRACEY_STOP_STALKERS_CAR
CONST_INT AUDIOSCENE_CHASE_FOOT 5 // M_E_TRACEY_DEAL_WITH_STALKER
CONST_INT AUDIOSCENE_DRIVE_HOME 6 // M_E_TRACEY_TAKE_TRACEY_HOME
CONST_INT AUDIOSCENE_DONE 7 // None
// Vehicle restriction
CONST_INT RESTRICTION_STALKER 0
CONST_INT RESTRICTION_TRACEY 1
//*************************************************************************************************************************************************
// :VARIABLES:
//*************************************************************************************************************************************************
// Progression stuff
MISSION_STAGE_TRACK msTrack = MST_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
WARNING_FAIL_CALLS wfcProgress // Tracks progression of warning call and fail call
BOOL bWFCOnHold // Suspends warning fail calls when we're within grace distance.
// Conversation struct
structPedsForConversation mConversationStruct // Speaker/Listener IDs: 0 Michael, 3 Tracey, 4 Stalker
// Hint cam struct
CHASE_HINT_CAM_STRUCT localChaseHintCamStruct
// Cars
VEHICLE_INDEX viTraceyCar
BOOL bStuckCheckActive = FALSE
BOOL bVehicleOnRoofLastFrame = FALSE
INT iStuckTimer
MISSION_VEHICLE mvStalkerCar
// Peds
PED_INDEX pedTracey
PED_INDEX pedStalker
MODEL_NAMES mnStalkerModel = A_M_O_ACult_01
BOOL bStalkerModelRequested = FALSE
BOOL bTraceyIsCurrentlySpawned = FALSE
BOOL bRelGroupExists = FALSE
REL_GROUP_HASH relGroupFriendly
STRING sTraceyAnimDict = "rcmme_tracey1"
// Blips
BLIP_INDEX biBlip // General blip
BLIP_INDEX biTraceyBlip // Specific blip for Tracey's car for stalker section onwards
INT iFlashTick // For flashing stalker blip
INT iFlashTimeout // For flashing stalker blip
// In-game clock times that player started the mission at, i.e. "3pm" not "7454568ms"
INT iStartDate
INT iStartHours
INT iStartMinutes
TRACEY_CAR_ENTRY traceyCarEntry // Progression within the substage where player meets Tracey
BOOL bPlayerOnFoot // Track player being on foot.
BOOL bTraceyOnFoot // Track Tracey being on foot.
BOOL bTraceyAttemptingReentry // Track Tracey attempting to get back in car.
INT iFootDawdleTimer // Timer for player dawdling in car park on foot
BOOL bPlayerWanted // Used for tracking player being wanted in drive home step
BOOL bWantedObjectiveNeeded // Have we shown a wanted objective yet on becoming wanted in drive home section
BOOL bMichaelCommentPlayed // Michael comment on getting in car
// For tracking whether the player has driven weirdly with Tracey outside the chase stages
BOOL bBonkersDriving
INT iPedsDamaged
INT iPedDamageTimeOut
FLOAT fTotalTimeOverSpeedLimit
TRACEY_DESTINATION_STATE traceyDestinationState // Control chatter and objective progress
//INT iLookAroundTimer // Timer for waiting at each point before proceeding
//INT iLookAroundDelay = 2000 //4500 // How long they should wait at each point (ms)
// Stalker stuck check
BOOL bStalkerStuckCheckActive = FALSE
BOOL bStalkerOnRoofLastFrame = FALSE
INT iStalkerStuckTimer
// Monitor Tracey's car moving - used to make her comment if player is idle
VECTOR vecTraceyPositionTrack[3]
INT iTraceyPositionTimer // Spaces out position check updates
INT iComplainTimer // Timer for immobile state before a complaint
// Whether stalker knows player is after him
BOOL bPlayerWiseToStalker // Controls when stalker starts to flee
BOOL bStalkerFleeing // Has the stalker started to flee from the player
BOOL bMichaelArrivalLinePlayed // Michael has said his arrival line for hitting the final destination
BOOL bStalkerSpotChatStarted // Tracey has ID'ed the stalker and tells the player
BOOL bStalkerDead // Stalker has been killed
BOOL bTraceySawOutcome = FALSE // Whether Tracey witnessed the stalker's death/escape
//INT iStalkerBlipTimer // Timeout for blipping stalker regardless of whether he's in range
// Video editor event for player killing stalker
BOOL bStalkerSnuffMovieStarted = FALSE // Video Editor - Have we started a recording for the stalker's death?
BOOL bStalkerSnuffMovieFinished = FALSE // Video Editor - Have we ended a recording for the stalker's death?
INT iStalkerSnuffTimer // Timer for video editor movie
// Objective display structure
MISSION_OBJECTIVE moObjectives[NUM_MISSION_OBJECTIVES]
// Tracey muttering to herself in car park
INT iTraceyWaitingChatTimer
INT iTraceyWaitingConversationCount
CONVERSATION_PROGRESS_TRACK cptTraceyWaitingComments = CPT_WAIT_TO_TRIGGER
// Vehicle chase conversation stuff
INT iChaseStalkerChatCount // Stalker's comment counter
INT iChaseTraceyChatCount // Tracey's comment counter
INT iChaseChatTimer
BOOL bStalkerChatting // TRUE while a stalker/Michael conversation is ongoing
BOOL bTraceyChatting // TRUE while a Tracey/Michael conversation is ongoing
BOOL bQuickRamComment // TRUE if a collision has occured
INT iLastCollisionTime // The last time we detected the car colliding with something
INT iTimeSinceCollision // The time elapsed since last colliding with something
CONVERSATION_PROGRESS_TRACK cptChaseTrack
CONVERSATION_INFO ciChaseConversations[NUM_CHASE_CONVERSATIONS_S] // Stalker/Michael chase banter
CONVERSATION_INFO ciTraceyChaseConversations[NUM_CHASE_CONVERSATIONS_T] // Tracey/Michael chase banter
// Monitoring Michael shooting and Tracey complaining about it
BOOL bShootingComment
INT iShootingTimer
INT iShootingComplaintGap
CONST_INT RECENT_SHOOTING_TIMEOUT 4500
CONST_INT SHOOTING_COMPLAINT_GAP 15000
// Stalker on foot chase conversation stuff
BOOL bTraceyOnFootAlertDone // Tracey has said her comment when the stalker gets out of his car
BOOL bTraceyEscapeCommentDone // Tracey has said her comment about whether they are letting the stalker go now he's on foot
INT iFootChaseStalkerChat // Stalker's comment counter
INT iFootChaseTraceyChat // Tracey's comment counter
CONVERSATION_PROGRESS_TRACK cptFootChaseTrack
CONVERSATION_INFO ciFootChaseConversations[NUM_FOOT_CONVERSATIONS_S]
CONVERSATION_INFO ciTraceyFootChaseConversations[NUM_FOOT_CONVERSATIONS_T]
// Conversations on drive home
CONVERSATION_INFO ciDriveHomeChat[NUM_DRIVEHOME_CONVERSATIONS]
INT iDriveHomeChatStep
INT iDriveHomeChatTimer
BOOL bNeedWantedRemark = FALSE
BOOL bNeedCrazyDrivingComment = FALSE
BOOL bConversationKilled = FALSE // Has any ongoing conversation been killed at the point we need to instant the dead stalker reaction.
// Nearly home comment
BOOL bNearlyHomeCommentDone
BOOL bAtHomeCommentDone
// At home cutscene
CAMERA_INDEX ciHomeCam
INT iCutTimer
BOOL bOutroWasSkipped
BOOL bFPFlashNeeded // First person
BOOL bFPFlashTimerStarted // First person
INT iFPFlashTimer // First person
// Locations
VECTOR vecTraceyStartPoint = << 238.1823, -34.5401, 68.7233 >> // Where Tracey's car is spawned in car park
FLOAT fTraceyStartHeading = 160.6
VECTOR vecTraceyPedStart = << 236.23, -34.25, 69.71 >> // Where Tracey ped is spawned in car park
FLOAT fTraceyPedStartHead = 154.19
DESTINATION_POINT dpLocates[NUM_DESTINATIONS] // Locations that Tracey suggests the player drives to
INT iCurrentDestination // The current destination in the array
VECTOR vecHouseLocation = << -825.07, 180.32, 70.52>> // Michael's house
VECTOR vecHotelLocation = <<-1316.5687, 391.5938, 68.7507>> // Back of hotel
VECTOR vecReturnLocation, vecTraceyDest // Destination of Drive home stage / Point tracey walks to in outro cut
BOOL bGoingToHotel = FALSE // Set this to true to go to hotel instead of house
NAA_VOLUME naaCarPark // Disables road nodes in car park where you meet Tracey
SCENARIO_BLOCKING_INDEX sbiCarpark // Disable parking scenario in car park where you meet Tracey
INT iLookAroundTimer // Timer for looking around at each point
INT iLookAroundDelay = 2000 // How long they should wait at each point (ms)
BOOL bDoingDelay // Controls whether conversation delays are needed
// Stalker spawn stuff
VECTOR vecStalkerSpawnPoint[6]
FLOAT fStalkerSpawnHead[6]
// General purpose counter
INT iCount
// Fail reason tracking - will either be an ID or "DEFAULT" if no fail reason set.
STRING sFailReason
// Audio scene handling
INT iCurrentAudioScene
STRING sAudioScenes[NUM_AUDIOSCENES]
// Monitor whether player has trapped stalker car in chase section and general route progress
VECTOR vecStalkerPositionTrack[6]
INT iStalkerPositionTimer // Frequency of position check
BOOL bStalkerCowering // On foot stalker has started cowering
BOOL bStalkerOnPedFleeTask // The stalker has been set to TASK_SMART_FLEE_PED instead of TASK_SMART_FLEE_COORD
INT iChaseTaskTimer[2] // Stalker task check timer in on-foot chase
// Monitor player ramming stalker in chase section
BOOL bStalkerRammedLastFrame
INT iStalkerRamTimer
INT iStalkerRamCount
FLOAT fRamClosingSpeedLastFrame
// Monitor stalker looking at player in chase section
INT iStalkerLookTimer // The timer for controlling look repeats
INT iStalkerLookAtTime // The time for which a given look should last
INT iStalkerRamLookDelay // How long to wait between ram-triggered looks
INT iStalkerProxLookDelay // How long to wait between proximity-triggered looks
STALKER_HEAD_TRACK_CONTROL shtcProgress
// Has Tracey started driving when she gets fed up waiting for player in car park
//BOOL bTDFStarted
// Replay player placement
VECTOR vecReplayPlayerLocation
FLOAT fReplayPlayerHeading
BOOL bReplayInVehicle
VEHICLE_INDEX vehReplayCar
// Replay timer
INT iMeetTimer
BOOL bMeetReplayIsGoing = FALSE
INT iDisableReplayCameraTimer //Fix for bug 2227778
//*************************************************************************************************************************************************
// :DEBUG VARS:
//*************************************************************************************************************************************************
#IF IS_DEBUG_BUILD
// Mission specific z-skip enum
ENUM MISSION_SKIP_STAGE
MSS_RESTART,
MSS_RESTART_CAR,
MSS_MEET_TRACEY,
MSS_STALKER_APPEARS,
MSS_STALKER_WRECK,
MSS_STALKER_STUCK,
MSS_DRIVE_HOME, // Driving home if stalker escaped on foot
MSS_DRIVE_HOME_DEAD, // Driving home if stalker dead
MSS_ARRIVE_HOME
ENDENUM
CONST_INT MAX_SKIP_MENU_LENGTH 9
//BOOL bFinishedSkipping
MISSION_SKIP_STAGE eTargetStage
MissionStageMenuTextStruct mSkipMenu[MAX_SKIP_MENU_LENGTH]
// Car model to be spawned by debug skips
MODEL_NAMES debugPlayerCarModel = TAILGATER
// Debug widgets
BOOL bDebugTraceyTTY = FALSE
BOOL bDebugStalkerTTY = FALSE
BOOL bDebugCallTimeDisplay = FALSE
BOOL bDebugFlipStalkerCar = FALSE
WIDGET_GROUP_ID widgetGroup
#ENDIF
//*************************************************************************************************************************************************
// :DEBUG RENDERING PROCS:
//*************************************************************************************************************************************************
#IF IS_DEBUG_BUILD
/// PURPOSE:
/// Draw debug box for non-axis aligned locate, useful for rendering triggers and telling which is which
/// Will only be called if you set the bool for them
/// PARAMS:
/// vec1 - locate vector 1
/// vec2 - locate vector 2
/// width - locate width
/// colR - Red value 0-255
/// colG - Green value 0-255
/// colB - Blue value 0-255
PROC DRAW_DEBUG_LOCATE_SPECIAL(VECTOR vec1, VECTOR vec2, FLOAT width, INT colR, INT colG, INT colB)
IF colR > 255 OR colR < 0 OR colG > 255 OR colG < 0 OR colB > 255 OR colB < 0
SCRIPT_ASSERT("Trying to set an illegal RGB value for locate rendering, so no boxes for you!")
ELSE
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, colR, colG, colB)
DRAW_DEBUG_LINE(c2, c3, colR, colG, colB)
DRAW_DEBUG_LINE(c3, c4, colR, colG, colB)
DRAW_DEBUG_LINE(c4, c1, colR, colG, colB)
// Draw top lines
DRAW_DEBUG_LINE(d1, d2, colR, colG, colB)
DRAW_DEBUG_LINE(d2, d3, colR, colG, colB)
DRAW_DEBUG_LINE(d3, d4, colR, colG, colB)
DRAW_DEBUG_LINE(d4, d1, colR, colG, colB)
// Draw uprights
DRAW_DEBUG_LINE(c1, d1, colR, colG, colB)
DRAW_DEBUG_LINE(c2, d2, colR, colG, colB)
DRAW_DEBUG_LINE(c3, d3, colR, colG, colB)
DRAW_DEBUG_LINE(c4, d4, colR, colG, colB)
ENDIF
ENDPROC
#ENDIF
//*************************************************************************************************************************************************
// :OBJECTIVE FUNCS/PROCS:
//*************************************************************************************************************************************************
/// PURPOSE:
/// Returns a completed struct that holds an objective's info
/// PARAMS:
/// stTextLabel - The name of the text label that will be displayed as God text
/// iDisMax - Maximum number of times it can display. Set to -1 for no limit.
FUNC MISSION_OBJECTIVE CREATE_MISSION_OBJECTIVE(STRING stTextLabel, INT iDisMax = 1)
MISSION_OBJECTIVE tmpMissionObj
tmpMissionObj.sTextLabel = stTextLabel
tmpMissionObj.iDisplayMax = iDisMax
tmpMissionObj.iDisplayCount = 0
RETURN tmpMissionObj
ENDFUNC
/// PURPOSE:
/// Display an objective unless it has already been displayed as many times as allowed
/// PARAMS:
/// iObj - index for the objective (use the CONSTs)
PROC DISPLAY_MISSION_OBJECTIVE(INT iObj)
IF moObjectives[iObj].iDisplayCount < moObjectives[iObj].iDisplayMax
OR moObjectives[iObj].iDisplayMax = -1
PRINT_NOW(moObjectives[iObj].sTextLabel, DEFAULT_GOD_TEXT_TIME, 1)
moObjectives[iObj].iDisplayCount++
ENDIF
ENDPROC
/// PURPOSE:
/// Test whether an objective will display
/// Saves having a seperate bool for blip updates, or lets you see whether you need to clear an objective
/// PARAMS:
/// iObj - index for the objective (use the CONSTs)
/// RETURNS:
/// TRUE if calling DISPLAY_MISSION_OBJECTIVE would trigger a PRINT
FUNC BOOL MISSION_OBJECTIVE_WILL_DISPLAY(INT iObj)
IF moObjectives[iObj].iDisplayCount < moObjectives[iObj].iDisplayMax
OR moObjectives[iObj].iDisplayMax = -1
RETURN TRUE
ELSE
RETURN FALSE
ENDIF
ENDFUNC
/// PURPOSE:
/// Clear a specific objective print that is being displayed
/// PARAMS:
/// iObj - index for the objective (use the CONSTs)
PROC CLEAR_MISSION_OBJECTIVE(INT iObj)
CLEAR_THIS_PRINT(moObjectives[iObj].sTextLabel)
ENDPROC
/// PURPOSE:
/// Are any objectives from this mission currently on screen?
/// RETURNS:
/// TRUE if a PRINT from this mission is on screen
FUNC BOOL MISSION_OBJECTIVES_CURRENTLY_DISPLAYED()
// Check the prints in sequence
FOR iCount = 0 TO NUM_MISSION_OBJECTIVES-1
IF IS_THIS_PRINT_BEING_DISPLAYED(moObjectives[iCount].sTextLabel)
RETURN TRUE
ENDIF
ENDFOR
// Didn't find any prints
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Allows skip procs to reset objective counter
/// PARAMS:
/// iObj - index for the objective (use the CONSTs)
/// iNewCount - optionally set the display count to a specific value
PROC MISSION_OBJECTIVE_RESET(INT iObj, INT iNewCount = 0)
moObjectives[iObj].iDisplayCount = iNewCount
ENDPROC
/// PURPOSE:
/// Allows skip procs to set an objective to act as if it has already displayed
/// PARAMS:
/// iObj - index for the objective (use the CONSTs)
PROC MISSION_OBJECTIVE_EXPIRE(INT iObj)
IF moObjectives[iObj].iDisplayMax = -1
CPRINTLN(DEBUG_MISSION, "Trying to expire an objective that has unlimited repeats - index ", iObj)
ELSE
moObjectives[iObj].iDisplayCount = moObjectives[iObj].iDisplayMax
ENDIF
ENDPROC
/// PURPOSE:
/// Set up all the mission objectives
PROC STORE_MISSION_OBJECTIVE_DATA()
moObjectives[MO_MET1INITDRV] = CREATE_MISSION_OBJECTIVE("MET1INITDRV", 1) // Drive towards Vinewood.
moObjectives[MO_MET1TCAR] = CREATE_MISSION_OBJECTIVE("MET1TCAR", 1) // Get in Tracey's car.
moObjectives[MO_MET1WAIT] = CREATE_MISSION_OBJECTIVE("MET1WAIT", 1) // Wait for Tracey.
moObjectives[MO_MET1WANT] = CREATE_MISSION_OBJECTIVE("MET1WANT", -1) // Lose the cops.
moObjectives[MO_MET1CHASE] = CREATE_MISSION_OBJECTIVE("MET1CHASE", 1) // Stop the stalker's car.
moObjectives[MO_MET1GETBKT] = CREATE_MISSION_OBJECTIVE("MET1GETBKT", 1) // Get back in Tracey's car.
moObjectives[MO_MET1DLT] = CREATE_MISSION_OBJECTIVE("MET1DLT", 1) // Return to Tracey.
moObjectives[MO_MET1HOME] = CREATE_MISSION_OBJECTIVE("MET1HOME", 1) // Take Tracey home.
moObjectives[MO_MET1HOTEL] = CREATE_MISSION_OBJECTIVE("MET1HOTEL", 1) // Take Tracey to the hotel.
ENDPROC
//*************************************************************************************************************************************************
// :UTILITY FUNCS/PROCS:
//*************************************************************************************************************************************************
/// PURPOSE:
/// Are we at a point in flow that means we should go to the hotel instead of the house?
/// RETURNS:
/// TRUE if family are in hiding, otherwise false
FUNC BOOL SHOULD_GO_TO_HOTEL()
// I think we'll need new flowflags for this
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Checks a vehicle to see if it has any busted tyres
/// RETURNS:
/// TRUE if any tyres are busted
FUNC BOOL VEHICLE_TYRE_BUSTED(VEHICLE_INDEX viCheck)
IF IS_VEHICLE_TYRE_BURST(viCheck, SC_WHEEL_CAR_FRONT_LEFT)
OR IS_VEHICLE_TYRE_BURST(viCheck, SC_WHEEL_CAR_FRONT_RIGHT)
OR IS_VEHICLE_TYRE_BURST(viCheck, SC_WHEEL_CAR_REAR_LEFT)
OR IS_VEHICLE_TYRE_BURST(viCheck, SC_WHEEL_CAR_REAR_RIGHT)
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Check whether the stalker car is damaged enough that he needs to abandon it
/// RETURNS:
/// TRUE if it's trashed, stuck or has burst tyres
FUNC BOOL STALKER_CAR_IS_TRASHED()
// Basic trashed checks
IF DOES_ENTITY_EXIST(mvStalkerCar.vehicle)
IF IS_ENTITY_DEAD(mvStalkerCar.vehicle)
OR NOT IS_VEHICLE_DRIVEABLE(mvStalkerCar.vehicle)
RETURN TRUE
ENDIF
// Do player ram counting
IF HAS_PLAYER_RAMMED_ENEMY_ENOUGH(mvStalkerCar.vehicle, bStalkerRammedLastFrame, iStalkerRamTimer, iStalkerRamCount, fRamClosingSpeedLastFrame)
RETURN TRUE
ENDIF
// Trigger a ram comment if bStalkerRammedLastFrame has been set
IF bStalkerRammedLastFrame
bQuickRamComment = TRUE
ENDIF
// Keep track of vehicle on roof
IF IS_VEHICLE_STUCK_ON_ROOF(mvStalkerCar.vehicle)
IF bStalkerOnRoofLastFrame
IF GET_GAME_TIMER() - iStalkerStuckTimer > ROOF_TIME
RETURN TRUE
ENDIF
ELSE
CPRINTLN(DEBUG_MISSION, "Stalker is now stuck on roof!")
bStalkerOnRoofLastFrame = TRUE
iStalkerStuckTimer = GET_GAME_TIMER()
ENDIF
ELSE
IF bStalkerOnRoofLastFrame
CPRINTLN(DEBUG_MISSION, "MET_TRACEY1::: Stalker is no longer stuck on roof.")
ENDIF
bStalkerOnRoofLastFrame = FALSE
ENDIF
// Check for tyre bursts
IF VEHICLE_TYRE_BUSTED(mvStalkerCar.vehicle)
RETURN TRUE
ENDIF
ENDIF
// No problems found:
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Perform location based checks to ensure we aren't trying to set IK when it would look awful
FUNC BOOL MAKE_SURE_THIS_WONT_LOOK_STUPID(BOOL bStrictCheck)
IF bStrictCheck = TRUE OR bStrictCheck = FALSE
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Makes the stalker look at the player after a ram or if he drives right alongside him for a while
PROC STALKER_HEAD_TRACK_CONTROLLER()
IF shtcProgress = SHTC_WAIT_FOR_TRIGGER_EVENT
// Check for ram look
IF bStalkerRammedLastFrame
AND GET_GAME_TIMER() - iStalkerLookTimer > iStalkerRamLookDelay
iStalkerLookTimer = GET_GAME_TIMER()
shtcProgress = SHTC_RAM_LOOKAT
CPRINTLN(DEBUG_MISSION, "STALKER_HEAD_TRACK_CONTROLLER: Go to SHTC_RAM_LOOKAT")
// Check for player cruising alongside look
ELIF GET_GAME_TIMER() - iStalkerLookTimer > iStalkerProxLookDelay
AND IS_ENTITY_IN_ANGLED_AREA(PLAYER_PED_ID(), GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvStalkerCar.vehicle, << 0.0, -0.5, -1.5 >>), GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvStalkerCar.vehicle, << 0.0, 6.0, 2.0 >>), 12.0)
iStalkerLookTimer = GET_GAME_TIMER()
shtcProgress = SHTC_PROX_LOOKAT
CPRINTLN(DEBUG_MISSION, "STALKER_HEAD_TRACK_CONTROLLER: Go to SHTC_PROX_LOOKAT")
ENDIF
ENDIF
IF shtcProgress = SHTC_PROX_LOOKAT
// Check for being rammed last frame
IF bStalkerRammedLastFrame
CPRINTLN(DEBUG_MISSION, "STALKER_HEAD_TRACK_CONTROLLER: Stalker was rammed in SHTC_PROX_LOOKAT- fall through to SHTC_RAM_LOOKAT")
shtcProgress = SHTC_RAM_LOOKAT
// Check if the prox lookat is still good
ELIF GET_GAME_TIMER() - iStalkerLookTimer < iStalkerLookAtTime
AND MAKE_SURE_THIS_WONT_LOOK_STUPID(TRUE)
// Lookat still good to go, keep setting
//SET_PED_CAN_HEAD_IK(pedStalker, FALSE)
SET_IK_TARGET(pedStalker, IK_PART_HEAD, PLAYER_PED_ID(), 0, << 0.0, 0.0, 0.0 >>, ITF_DEFAULT, 300, 300)
ELSE
// Lookat timed out or would look bad, go back to waiting
CPRINTLN(DEBUG_MISSION, "STALKER_HEAD_TRACK_CONTROLLER: SHTC_PROX_LOOKAT timed out or cancelled, back to SHTC_WAIT_FOR_TRIGGER_EVENT after", GET_GAME_TIMER() - iStalkerLookTimer, "ms")
iStalkerLookTimer = GET_GAME_TIMER()
shtcProgress = SHTC_WAIT_FOR_TRIGGER_EVENT
ENDIF
ENDIF
IF shtcProgress = SHTC_RAM_LOOKAT
// Check if the ram lookat is still good
IF GET_GAME_TIMER() - iStalkerLookTimer < iStalkerLookAtTime
AND MAKE_SURE_THIS_WONT_LOOK_STUPID(FALSE)
// Lookat still good to go, keep setting
//SET_PED_CAN_HEAD_IK(pedStalker, FALSE)
SET_IK_TARGET(pedStalker, IK_PART_HEAD, PLAYER_PED_ID(), 0, << 0.0, 0.0, 0.0 >>, ITF_DEFAULT, 150, 400)
ELSE
// Lookat timed out or would look bad, go back to waiting
CPRINTLN(DEBUG_MISSION, "STALKER_HEAD_TRACK_CONTROLLER: SHTC_RAM_LOOKAT timed out or cancelled, back to SHTC_WAIT_FOR_TRIGGER_EVENT after", GET_GAME_TIMER() - iStalkerLookTimer, "ms")
iStalkerLookTimer = GET_GAME_TIMER()
shtcProgress = SHTC_WAIT_FOR_TRIGGER_EVENT
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Checks that it's still relevant whether Tracey's car is driveable, then checks whether it's wrecked
/// RETURNS:
/// TRUE if Tracey's car is spawned and wrecked, and the stalker is not yet fleeing the player. Otherwise FALSE
FUNC BOOL TRACEY_CAR_WRECKED()
IF NOT IS_ENTITY_ALIVE(viTraceyCar)
RETURN TRUE
ENDIF
IF NOT IS_VEHICLE_DRIVEABLE(viTraceyCar)
RETURN TRUE
ENDIF
// Either not trashed, or stalker is already fleeing so we don't care
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Checks that Tracey's car hasn't got stuck
/// RETURNS:
/// TRUE if car is stuck
FUNC BOOL TRACEY_CAR_STUCK()
// Keep track of vehicle on roof
IF IS_VEHICLE_STUCK_ON_ROOF(viTraceyCar)
IF bVehicleOnRoofLastFrame
IF GET_GAME_TIMER() - iStuckTimer > ROOF_TIME
RETURN TRUE
ENDIF
ELSE
CPRINTLN(DEBUG_MISSION, "Vehicle is now stuck on roof!")
bVehicleOnRoofLastFrame = TRUE
iStuckTimer = GET_GAME_TIMER()
ENDIF
ELSE
IF bVehicleOnRoofLastFrame
CPRINTLN(DEBUG_MISSION, "Vehicle is no longer stuck on roof.")
ENDIF
bVehicleOnRoofLastFrame = FALSE
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// A 'safe' check for whether Tracey's car is on its roof.
/// RETURNS:
/// TRUE only if the stuck check is active and the car's on its roof - cannot return TRUE when stuck check isn't active
FUNC BOOL CAR_CURRENTLY_ON_ROOF()
IF bStuckCheckActive
AND IS_VEHICLE_STUCK_ON_ROOF(viTraceyCar)
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Checks the stalker's car's roll to see if it's probably on its roof
/// RETURNS:
/// TRUE if reasonably sure the stalker is upside down
FUNC BOOL STALKER_CAR_UPSIDE_DOWN()
IF IS_ENTITY_ALIVE(mvStalkerCar.vehicle)
FLOAT fStalkRoll = WRAP(GET_ENTITY_ROLL(mvStalkerCar.vehicle), 0, 360)
CPRINTLN(DEBUG_MISSION, "Clamped 0-360 stalker car roll is ", fStalkRoll)
IF fStalkRoll > 150
AND fStalkRoll < 210
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Sets the fail reason, mission stage track and stage progress
/// PARAMS:
/// sFailString -
PROC SET_MISSION_FAILED(STRING sFailString)
sFailReason = sFailString
msTrack = MST_FAIL_FADE
sProgress = SP_SETUP
ENDPROC
/// PURPOSE:
/// Checks for player or Tracey death etc.
/// RETURNS:
/// TRUE if the mission is failed
FUNC BOOL MISSION_FAIL_CHECKS()
IF NOT IS_ENTITY_ALIVE(PLAYER_PED_ID())
// Player dead - FAIL
SET_MISSION_FAILED("DEFAULT")
RETURN TRUE
ELIF bTraceyIsCurrentlySpawned
// Tracey is currently spawned - do health checks etc.
IF NOT IS_ENTITY_ALIVE(pedTracey)
// Tracey dead - FAIL
SET_MISSION_FAILED("MET1TRAKILL") // Tracey was killed.
RETURN TRUE
ELIF TRACEY_CAR_WRECKED()
// Tracey's car was wrecked - Mission Fail
SET_MISSION_FAILED("MET1TWRECK") // Tracey's car was wrecked.
RETURN TRUE
ELIF TRACEY_CAR_STUCK()
// Tracey's car got stuck
SET_MISSION_FAILED("MET1STUCK")
RETURN TRUE
ELSE
// Stop Tracey using inappropriate idles
SET_PED_CAN_PLAY_AMBIENT_BASE_ANIMS(pedTracey, FALSE)
ENDIF
ENDIF
// None of the fail conditions have been met
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// If the player becomes wanted while following Tracey, the stalker is scared off
/// RETURNS:
/// TRUE if the function detects and triggers the wanted fail state
FUNC BOOL CHECK_WANTED_FAIL()
IF GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) > 0
// Player is wanted - mission fail!
sFailReason = "MET1CPSTALK" // The cops have scared off the stalker
msTrack = MST_FAIL_FADE
sProgress = SP_SETUP
RETURN TRUE
ELSE
RETURN FALSE
ENDIF
ENDFUNC
/// PURPOSE:
/// Create friendly group if it doesn't exist, add Tracey to it
PROC DO_FRIENDLY_GROUP_SETUP()
IF NOT bRelGroupExists
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)
bRelGroupExists = TRUE
ENDIF
IF IS_PED_IN_GROUP(pedTracey)
REMOVE_PED_FROM_GROUP(pedTracey)
ENDIF
SET_PED_RELATIONSHIP_GROUP_HASH(pedTracey, relGroupFriendly)
ENDPROC
/// PURPOSE:
/// Checks the distance between two locations is less than a given amount
/// RETURNS:
/// TRUE if in range
FUNC BOOL ARE_PROGRESS_COORDS_IN_RANGE(VECTOR v1, VECTOR v2, FLOAT fRange)
RETURN ( VDIST2(v1, v2) <= fRange*fRange )
ENDFUNC
/// PURPOSE:
/// Keep a set of points for tracking the stalker being stuck in the chase section.
/// Also resets timer to delay the next check
PROC UPDATE_STALKER_SAVED_POSITIONS(BOOL bOnFoot = FALSE)
// Save timer for delaying next check
iStalkerPositionTimer = GET_GAME_TIMER()
// Shuffle stored positions
vecStalkerPositionTrack[0] = vecStalkerPositionTrack[1]
vecStalkerPositionTrack[1] = vecStalkerPositionTrack[2]
vecStalkerPositionTrack[2] = vecStalkerPositionTrack[3]
vecStalkerPositionTrack[3] = vecStalkerPositionTrack[4]
vecStalkerPositionTrack[4] = vecStalkerPositionTrack[5]
IF bOnFoot
vecStalkerPositionTrack[5] = GET_ENTITY_COORDS(pedStalker)
ELSE
vecStalkerPositionTrack[5] = GET_ENTITY_COORDS(mvStalkerCar.vehicle)
ENDIF
ENDPROC
/// PURPOSE:
/// Set some arbitrary values to prevent him cowering instantly - for stuff like B*1115258/1115282
PROC FORCE_INIT_STALKER_FOOT_TRACKING()
VECTOR vTmpSPos
vTmpSPos = GET_ENTITY_COORDS(pedStalker)
vecStalkerPositionTrack[0] = << vTmpSPos.x, vTmpSPos.y, vTmpSPos.z+15 >>
vecStalkerPositionTrack[1] = << vTmpSPos.x, vTmpSPos.y, vTmpSPos.z+12 >>
vecStalkerPositionTrack[2] = << vTmpSPos.x, vTmpSPos.y, vTmpSPos.z+9 >>
vecStalkerPositionTrack[3] = << vTmpSPos.x, vTmpSPos.y, vTmpSPos.z+6 >>
vecStalkerPositionTrack[4] = << vTmpSPos.x, vTmpSPos.y, vTmpSPos.z+3 >>
vecStalkerPositionTrack[5] = vTmpSPos
ENDPROC
/// PURPOSE:
/// Checks whether Tracey's car is in a position directly ahead of or behind the stalker.
/// RETURNS:
/// TRUE if the player is in a position that would mean he'd wedged him in
FUNC BOOL PLAYER_HAS_WEDGED_STALKER_IN()
IF IS_ENTITY_IN_RANGE_COORDS(viTraceyCar, GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvStalkerCar.vehicle, << 0.0, 3.2, 0.5 >>), 3)
OR IS_ENTITY_IN_RANGE_COORDS(viTraceyCar, GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(mvStalkerCar.vehicle, << 0.0, -3.2, 0.5 >>), 3)
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Keep ticking the ped see ped checks for
PROC TICK_TRACEY_OUTCOME_TESTS()
CAN_PED_SEE_PED(pedTracey, PLAYER_PED_ID())
CAN_PED_SEE_PED(pedTracey, pedStalker)
ENDPROC
/// PURPOSE:
/// Check whether Tracey was close to player when stalker died/escaped
/// RETURNS:
/// TRUE if Tracey would reasonably be expected to see what happened
FUNC BOOL DID_TRACEY_SEE_OUTCOME()
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
AND IS_PED_IN_VEHICLE(pedTracey, viTraceyCar)
CPRINTLN(DEBUG_MISSION, "DID_TRACEY_SEE_OUTCOME: Michael is in car with Tracey.")
RETURN TRUE
ELSE
IF bStalkerDead
IF IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedTracey, 10.0)
CPRINTLN(DEBUG_MISSION, "DID_TRACEY_SEE_OUTCOME: Stalker dead, Michael within 10m of Tracey.")
RETURN TRUE
ELIF IS_ENTITY_IN_RANGE_COORDS(pedTracey, GET_ENTITY_COORDS(pedStalker, FALSE), 10.0)
CPRINTLN(DEBUG_MISSION, "DID_TRACEY_SEE_OUTCOME: Stalker dead,Tracey within 10m of stalker.")
RETURN TRUE
ELIF CAN_PED_SEE_PED(pedTracey, PLAYER_PED_ID())
CPRINTLN(DEBUG_MISSION, "DID_TRACEY_SEE_OUTCOME: Stalker dead, Tracey has LOS to Michael.")
RETURN TRUE
ELIF CAN_PED_SEE_PED(pedTracey, pedStalker)
CPRINTLN(DEBUG_MISSION, "DID_TRACEY_SEE_OUTCOME: Stalker dead, Tracey has LOS to stalker.")
RETURN TRUE
ENDIF
ELSE
IF CAN_PED_SEE_PED(pedTracey, PLAYER_PED_ID())
CPRINTLN(DEBUG_MISSION, "DID_TRACEY_SEE_OUTCOME: Stalker alive, Tracey has LOS to Michael.")
RETURN TRUE
ENDIF
ENDIF
ENDIF
CPRINTLN(DEBUG_MISSION, "DID_TRACEY_SEE_OUTCOME: Didn't see outcome")
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Initialise the bonkers driving tracking variables
PROC INIT_BONKERS_DRIVING_CHECK()
bBonkersDriving = FALSE
iPedsDamaged = 0
iPedDamageTimeOut = GET_GAME_TIMER() + 2000
fTotalTimeOverSpeedLimit = 0
ENDPROC
/// PURPOSE:
/// Monitors whether the player is driving crazily outside the chase sections
PROC PLAYER_BONKERS_DRIVING_CHECK()
// Once we have established the player's bonkers credentials, stop checking
IF bBonkersDriving = FALSE
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
AND IS_PED_IN_VEHICLE(pedTracey, viTraceyCar)
AND GET_ENTITY_SPEED(viTraceyCar) > 30
fTotalTimeOverSpeedLimit = fTotalTimeOverSpeedLimit +@ 1.0
ENDIF
IF GET_GAME_TIMER() > iPedDamageTimeOut
AND HAS_PLAYER_DAMAGED_AT_LEAST_ONE_PED(PLAYER_ID())
iPedsDamaged++
CLEAR_PLAYER_HAS_DAMAGED_AT_LEAST_ONE_PED(PLAYER_ID())
iPedDamageTimeOut = GET_GAME_TIMER() + 2000
ENDIF
// Check bonkers rating
IF iPedsDamaged > 2
OR fTotalTimeOverSpeedLimit > 10.0
bBonkersDriving = TRUE
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Checks that Stalker is moving sensibly in car
PROC STALKER_DRIVE_CHECK()
IF IS_ENTITY_ALIVE(mvStalkerCar.vehicle)
// See if it's time to check positions
IF GET_GAME_TIMER() - iStalkerPositionTimer > 3000
UPDATE_STALKER_SAVED_POSITIONS()
IF ARE_PROGRESS_COORDS_IN_RANGE(vecStalkerPositionTrack[3], vecStalkerPositionTrack[5], 2)
AND PLAYER_HAS_WEDGED_STALKER_IN()
CPRINTLN(DEBUG_MISSION, "STALKER_DRIVE_CHECK::: Stalker has moved less than 2m in last 6 seconds and the player has them boxed in")
msTrack = MST_STALKER_WRECKED
sProgress = SP_SETUP
ELIF ARE_PROGRESS_COORDS_IN_RANGE(vecStalkerPositionTrack[0], vecStalkerPositionTrack[5], 2)
CPRINTLN(DEBUG_MISSION, "STALKER_DRIVE_CHECK::: Stalker has moved less than 2m in last 15 seconds")
msTrack = MST_STALKER_WRECKED
sProgress = SP_SETUP
#IF IS_DEBUG_BUILD
ELIF bDebugStalkerTTY
CPRINTLN(DEBUG_MISSION, "STALKER_DRIVE_CHECK::: Stalker OK, moved over 2m during last 6 seconds")
#ENDIF
ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Update the flee task given to the stalker
/// PARAMS:
/// bSetFootFlee - Whether we are setting the ped to flee the player instead of a coord
PROC STALKER_REFRESH_FLEE_TASK(BOOL bSetFootFlee = FALSE, BOOL bForceLongTimer = FALSE)
IF bSetFootFlee = FALSE
// Update flee coordinates
TASK_SMART_FLEE_COORD(pedStalker, GET_ENTITY_COORDS(PLAYER_PED_ID()), 2000.0, -1, FALSE)
iChaseTaskTimer[CT_PROX_CHECK] = GET_GAME_TIMER() + CHASE_PROX_CHECK_TIME
IF bForceLongTimer
iChaseTaskTimer[CT_AREA_CHECK] = GET_GAME_TIMER() + CHASE_PROX_CHECK_TIME
ELSE
iChaseTaskTimer[CT_AREA_CHECK] = GET_GAME_TIMER() + CHASE_AREA_CHECK_TIME
ENDIF
bStalkerOnPedFleeTask = FALSE
ELSE
// Set to flee ped
TASK_SMART_FLEE_PED(pedStalker, PLAYER_PED_ID(), 2000.0, -1, FALSE)
iChaseTaskTimer[CT_PROX_CHECK] = GET_GAME_TIMER() + CHASE_PROX_CHECK_TIME
IF bForceLongTimer
iChaseTaskTimer[CT_AREA_CHECK] = GET_GAME_TIMER() + CHASE_PROX_CHECK_TIME
ELSE
iChaseTaskTimer[CT_AREA_CHECK] = GET_GAME_TIMER() + CHASE_AREA_CHECK_TIME
ENDIF
bStalkerOnPedFleeTask = TRUE
ENDIF
ENDPROC
/// PURPOSE:
/// Checks that Stalker is moving sensibly on foot
PROC STALKER_FLEE_CHECK()
IF NOT bStalkerCowering
// See if it's time to check positions
IF GET_GAME_TIMER() - iStalkerPositionTimer > 3000
// // Check stalker not trapped in car somehow
// IF DOES_ENTITY_EXIST(mvStalkerCar.vehicle)
// AND IS_PED_IN_VEHICLE(pedStalker, mvStalkerCar.vehicle)
// CPRINTLN(DEBUG_MISSION, "STALKER_FLEE_CHECK: Stalker seems to be trapped in vehicle, emergency warp out!")
// SEQUENCE_INDEX siTmpStuck
// OPEN_SEQUENCE_TASK(siTmpStuck)
// TASK_LEAVE_VEHICLE(NULL, mvStalkerCar.vehicle, ECF_WARP_IF_DOOR_IS_BLOCKED)
// TASK_SMART_FLEE_COORD(NULL, GET_ENTITY_COORDS(PLAYER_PED_ID()), 2000.0, -1, FALSE)
// CLOSE_SEQUENCE_TASK(siTmpStuck)
// CLEAR_PED_TASKS(pedStalker)
//
// ELSE
//
UPDATE_STALKER_SAVED_POSITIONS(TRUE)
IF ARE_PROGRESS_COORDS_IN_RANGE(vecStalkerPositionTrack[0], vecStalkerPositionTrack[2], 1)
AND NOT IS_PED_IN_MELEE_COMBAT(pedStalker)
AND NOT IS_PED_RAGDOLL(pedStalker) // Prevents firing if player has knocked him over
AND NOT IS_PED_GETTING_UP(pedStalker)
CPRINTLN(DEBUG_MISSION, "STALKER_FLEE_CHECK::: Stalker has moved less than 1m in last 6 seconds, not fighting")
TASK_COWER(pedStalker)
bStalkerCowering = TRUE
#IF IS_DEBUG_BUILD
ELIF bDebugStalkerTTY
CPRINTLN(DEBUG_MISSION, "STALKER_FLEE_CHECK::: Stalker OK, moved over 1m during last 6 seconds")
#ENDIF
ENDIF
ENDIF
// ENDIF
// Check for stalker direction change if we haven't set him to cower now
IF NOT bStalkerCowering
// Checks for when the player's stayed in the car
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
AND NOT IS_PED_IN_ANY_VEHICLE(pedStalker)
// Periodic check to update flee coordinates if the player car is nearby
IF (GET_GAME_TIMER() > iChaseTaskTimer[CT_PROX_CHECK] AND IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedStalker, 20))
AND NOT IS_PED_RAGDOLL(pedStalker) // Prevents task spamming if player has knocked him over
AND NOT IS_PED_GETTING_UP(pedStalker) // Prevents task spamming if player has knocked him over
CPRINTLN(DEBUG_MISSION, "STALKER_FLEE_CHECK: Player in vehicle near fleeing stalker, periodically refreshing flee task")
STALKER_REFRESH_FLEE_TASK(FALSE)
ELSE
// Periodic angled area check update - solidly timed, won't lurk once valid like the proximity check
IF GET_GAME_TIMER() > iChaseTaskTimer[CT_AREA_CHECK]
IF IS_ENTITY_IN_ANGLED_AREA(PLAYER_PED_ID(), GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(pedStalker, <<0.0, 1.0, -2.0>>), GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(pedStalker, <<0.0, 7.0, 5.0>>), 5)
AND NOT IS_PED_RAGDOLL(pedStalker) // Prevents task spamming if player has knocked him over
AND NOT IS_PED_GETTING_UP(pedStalker) // Prevents task spamming if player has knocked him over
CPRINTLN(DEBUG_MISSION, "STALKER_FLEE_CHECK: Player in vehicle in front of fleeing stalker, doing faster refresh check")
STALKER_REFRESH_FLEE_TASK(FALSE, TRUE)
ELSE
// Reset timer
iChaseTaskTimer[CT_AREA_CHECK] = GET_GAME_TIMER() + CHASE_AREA_CHECK_TIME
ENDIF
// // Touching check
// ELIF IS_ENTITY_TOUCHING_ENTITY(pedStalker, PLAYER_PED_ID())
// AND NOT IS_PED_RAGDOLL(pedStalker) // Prevents task spamming if player has knocked him over
// AND NOT IS_PED_GETTING_UP(pedStalker) // Prevents task spamming if player has knocked him over
// CPRINTLN(DEBUG_MISSION, "STALKER_FLEE_CHECK: Player vehicle touching fleeing stalker, doing faster refresh check")
// STALKER_REFRESH_FLEE_TASK(FALSE, TRUE)
ENDIF
ENDIF
// Check for when the stalker is on foot - leave him on a ped flee task for this
ELIF NOT bStalkerOnPedFleeTask
AND NOT IS_PED_IN_ANY_VEHICLE(pedStalker)
CPRINTLN(DEBUG_MISSION, "STALKER_FLEE_CHECK: Player on foot, set to ped flee task")
STALKER_REFRESH_FLEE_TASK(TRUE, TRUE)
ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Checks whether the player is heading up or down the hill the stalker should spawn on
/// RETURNS:
/// TRUE if downhill
FUNC BOOL PLAYER_IS_DOWNHILL()
VECTOR tmpPlyrPos = GET_ENTITY_COORDS(PLAYER_PED_ID())
IF tmpPlyrPos.z < dpLocates[NUM_DESTINATIONS-1].location.z
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Checks that the stalker car spawn point isn't on screen, and that the player isn't miles away
PROC CHECK_STALKER_SPAWN_OCCLUSION()
INT iTmpSpawnIndex
// Check player is in range of initial spawn point
IF IS_ENTITY_IN_RANGE_COORDS(PLAYER_PED_ID(), dpLocates[NUM_DESTINATIONS-1].location, dpLocates[NUM_DESTINATIONS-1].range)
CPRINTLN(DEBUG_MISSION, "CHECK_STALKER_SPAWN_OCCLUSION: Player in range of standard trigger point")
// If the preferred point is in view of player, spawn opposite
IF WOULD_ENTITY_BE_OCCLUDED(mvStalkerCar.model, vecStalkerSpawnPoint[0], FALSE)
iTmpSpawnIndex = 0
ELSE
iTmpSpawnIndex = 1
ENDIF
// Set the points
mvStalkerCar.location = vecStalkerSpawnPoint[iTmpSpawnIndex]
mvStalkerCar.heading = fStalkerSpawnHead[iTmpSpawnIndex]
mvStalkerCar.initialSpeed = 15.0
ELSE
CPRINTLN(DEBUG_MISSION, "CHECK_STALKER_SPAWN_OCCLUSION: Player OUT OF RANGE of standard trigger point")
// Check whether we should be looking further up or down the hill
IF PLAYER_IS_DOWNHILL()
// Check the downhill spawns
IF WOULD_ENTITY_BE_OCCLUDED(mvStalkerCar.model, vecStalkerSpawnPoint[2], FALSE)
iTmpSpawnIndex = 2
ELSE
iTmpSpawnIndex = 3
ENDIF
ELSE
// Check the uphill spawns
IF WOULD_ENTITY_BE_OCCLUDED(mvStalkerCar.model, vecStalkerSpawnPoint[4], FALSE)
iTmpSpawnIndex = 4
ELSE
iTmpSpawnIndex = 5
ENDIF
ENDIF
// Set the points
mvStalkerCar.location = vecStalkerSpawnPoint[iTmpSpawnIndex]
mvStalkerCar.heading = fStalkerSpawnHead[iTmpSpawnIndex]
mvStalkerCar.initialSpeed = 25.0
ENDIF
CPRINTLN(DEBUG_MISSION, "Stalker spawnpoint set to ", iTmpSpawnIndex)
ENDPROC
/// PURPOSE:
/// Make them look around for the stalker
PROC LOOK_AROUND_FOR_STALKER()
SEQUENCE_INDEX seqLook[2]
VECTOR vLookAt[4]
INT iTimings[2][4]
INT iSeqOrder[2][3]
// Get some points to look at
vLookAt[0] = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(viTraceyCar, << -8.0, 2.0, 0.0 >>) // Left
vLookAt[1] = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(viTraceyCar, << -8.0, 8.0, 0.0 >>) // Left/forward
vLookAt[2] = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(viTraceyCar, << 8.0, 2.0, 0.0 >>) // Right
vLookAt[3] = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(viTraceyCar, << 8.0, 8.0, 0.0 >>) // Right/forward
// Set up Tracey - pick indexes to choose lookat points in the array of points we just set up
// Pick initial point entirely at random
iSeqOrder[0][0] = GET_RANDOM_INT_IN_RANGE(0,3)
// Pick second point on opposite side
IF iSeqOrder[0][0] < 2
iSeqOrder[0][1] = GET_RANDOM_INT_IN_RANGE(2,3)
ELSE
iSeqOrder[0][1] = GET_RANDOM_INT_IN_RANGE(0,1)
ENDIF
// Pick third point on original side
IF iSeqOrder[0][0] > 1
iSeqOrder[0][2] = GET_RANDOM_INT_IN_RANGE(2,3)
ELSE
iSeqOrder[0][2] = GET_RANDOM_INT_IN_RANGE(0,1)
ENDIF
// Set up some timings
iTimings[0][0] = GET_RANDOM_INT_IN_RANGE(300,600)
iTimings[0][1] = GET_RANDOM_INT_IN_RANGE(400,1000)
iTimings[0][2] = GET_RANDOM_INT_IN_RANGE(500,1000)
iTimings[0][3] = GET_RANDOM_INT_IN_RANGE(500,1000)
// Create Tracey's Task sequence
OPEN_SEQUENCE_TASK(seqLook[0])
TASK_PAUSE(NULL, iTimings[0][0])
TASK_LOOK_AT_COORD(NULL, vLookAt[iSeqOrder[0][0]], iTimings[0][1], SLF_WHILE_NOT_IN_FOV)
TASK_LOOK_AT_COORD(NULL, vLookAt[iSeqOrder[0][1]], iTimings[0][2], SLF_WHILE_NOT_IN_FOV)
TASK_LOOK_AT_COORD(NULL, vLookAt[iSeqOrder[0][2]], iTimings[0][3], SLF_WHILE_NOT_IN_FOV)
CLOSE_SEQUENCE_TASK(seqLook[0])
TASK_PERFORM_SEQUENCE(pedTracey, seqLook[0])
CLEAR_SEQUENCE_TASK(seqLook[0])
// Set up Michael - pick indexes to choose lookat points in the array of points we just set up
// Pick initial point entirely at random
iSeqOrder[1][0] = GET_RANDOM_INT_IN_RANGE(0,3)
// Pick again if it exactly matches Tracey's
IF iSeqOrder[0][0] = iSeqOrder[1][0]
iSeqOrder[1][0] = GET_RANDOM_INT_IN_RANGE(0,3)
ENDIF
// Pick second point on opposite side
IF iSeqOrder[1][0] < 2
iSeqOrder[1][1] = GET_RANDOM_INT_IN_RANGE(2,3)
ELSE
iSeqOrder[1][1] = GET_RANDOM_INT_IN_RANGE(0,1)
ENDIF
// Pick third point on original side
IF iSeqOrder[1][0] > 1
iSeqOrder[1][1] = GET_RANDOM_INT_IN_RANGE(2,3)
ELSE
iSeqOrder[1][1] = GET_RANDOM_INT_IN_RANGE(0,1)
ENDIF
// Set up some timings
iTimings[1][0] = 0 // We don't pause the player
iTimings[1][1] = GET_RANDOM_INT_IN_RANGE(500,1000)
iTimings[1][2] = GET_RANDOM_INT_IN_RANGE(500,1000)
iTimings[1][3] = GET_RANDOM_INT_IN_RANGE(500,1000)
// Create Tracey's Task sequence
OPEN_SEQUENCE_TASK(seqLook[1])
TASK_PAUSE(NULL, iTimings[1][0])
TASK_LOOK_AT_COORD(NULL, vLookAt[iSeqOrder[1][0]], iTimings[1][1], SLF_WHILE_NOT_IN_FOV)
TASK_LOOK_AT_COORD(NULL, vLookAt[iSeqOrder[1][1]], iTimings[1][2], SLF_WHILE_NOT_IN_FOV)
TASK_LOOK_AT_COORD(NULL, vLookAt[iSeqOrder[1][2]], iTimings[1][3], SLF_WHILE_NOT_IN_FOV)
CLOSE_SEQUENCE_TASK(seqLook[1])
TASK_PERFORM_SEQUENCE(PLAYER_PED_ID(), seqLook[1])
CLEAR_SEQUENCE_TASK(seqLook[1])
ENDPROC
/// PURPOSE:
/// Sets the audio scene to match the mission stage specified
/// PARAMS:
/// audioScene - index for the audio scene state, use the constants beginning "AUDIOSCENE_"
PROC UPDATE_AUDIO_SCENE(INT iNewAudioScene)
// Check we aren't already using the requested scene
IF iCurrentAudioScene <> iNewAudioScene
// Check for deactivating active scenes
FOR iCount = 0 TO NUM_AUDIOSCENES-1
// See if this is the scene state to change to
IF iCount = iNewAudioScene
IF NOT ARE_STRINGS_EQUAL(sAudioScenes[iCount], "NONE")
CPRINTLN(DEBUG_MISSION, "Starting audioscene ", sAudioScenes[iCount])
START_AUDIO_SCENE(sAudioScenes[iCount])
ENDIF
iCurrentAudioScene = iNewAudioScene
// See if we need to kill an audio scene
ELIF IS_AUDIO_SCENE_ACTIVE(sAudioScenes[iCount])
CPRINTLN(DEBUG_MISSION, "Stopping audioscene ", sAudioScenes[iCount])
STOP_AUDIO_SCENE(sAudioScenes[iCount])
ENDIF
ENDFOR
ENDIF
ENDPROC
/// PURPOSE:
/// Toggles the audio scene overlay for the focus cam on and off
/// PARAMS:
/// bEnable - TRUE to toggle on, FALSE for off
PROC TOGGLE_FOCUS_AUDIO_SCENE(BOOL bEnable = TRUE)
IF bEnable
AND NOT IS_AUDIO_SCENE_ACTIVE("M_E_TRACEY_FOCUS_CAM")
CPRINTLN(DEBUG_MISSION, "Starting audioscene M_E_TRACEY_FOCUS_CAM")
START_AUDIO_SCENE("M_E_TRACEY_FOCUS_CAM")
ELIF bEnable = FALSE
AND IS_AUDIO_SCENE_ACTIVE("M_E_TRACEY_FOCUS_CAM")
CPRINTLN(DEBUG_MISSION, "Stopping audioscene M_E_TRACEY_FOCUS_CAM")
STOP_AUDIO_SCENE("M_E_TRACEY_FOCUS_CAM")
ENDIF
ENDPROC
/// PURPOSE:
/// Video Editor: Mark point when the stalker got killed
PROC START_STALKER_SNUFF_MOVIE()
CPRINTLN(DEBUG_MISSION, "START_STALKER_SNUFF_MOVIE: Marking required start point")
bStalkerSnuffMovieStarted = TRUE
iStalkerSnuffTimer = GET_GAME_TIMER()
ENDPROC
/// PURPOSE:
/// If the stalker is dead and we marked a start point for the movie, this will stop it when appropriate
PROC MONITOR_STALKER_SNUFF_MOVIE()
// If we never started, nothing else to check
IF NOT bStalkerSnuffMovieStarted
EXIT
ENDIF
// If we triggered the recording, nothing else to check
IF bStalkerSnuffMovieFinished
EXIT
ENDIF
// The movie is still running - is it time to stop yet?
// Test to see if Tracey saw the stalker die - note that the outcome was always "death" if the movie started
IF bTraceySawOutcome
AND GET_GAME_TIMER() - iStalkerSnuffTimer > SNUFF_TIMER_TRACEY_SAW
CPRINTLN(DEBUG_MISSION, "MONITOR_STALKER_SNUFF_MOVIE: Kicking in the retro record, bTraceySawOutcome=TRUE and timer>SNUFF_TIMER_TRACEY_SAW")
REPLAY_RECORD_BACK_FOR_TIME(SNUFF_TIMER_RETRORECORD+(SNUFF_TIMER_TRACEY_SAW/1000))
bStalkerSnuffMovieFinished = TRUE
ENDIF
// When Tracey didn't see, ends recording sooner
IF NOT bTraceySawOutcome
AND GET_GAME_TIMER() - iStalkerSnuffTimer > SNUFF_TIMER_DIDNT_SEE
CPRINTLN(DEBUG_MISSION, "MONITOR_STALKER_SNUFF_MOVIE: Kicking in the retro record, bTraceySawOutcome=FALSE and timer>SNUFF_TIMER_DIDNT_SEE")
REPLAY_RECORD_BACK_FOR_TIME(SNUFF_TIMER_RETRORECORD+(SNUFF_TIMER_DIDNT_SEE/1000))
bStalkerSnuffMovieFinished = TRUE
ENDIF
ENDPROC
//*************************************************************************************************************************************************
//
// Data setup procs
//
//*************************************************************************************************************************************************
/// PURPOSE:
/// Store setup for Stalker's car and any other NPC cars that get added
PROC SETUP_NPC_CARS()
mvStalkerCar.model = TORNADO2
mvStalkerCar.carModelRequestActive = FALSE
mvStalkerCar.location = << -58.8963, -191.2678, 51.1096 >>
mvStalkerCar.heading = 66.9
ENDPROC
/// PURPOSE:
/// Stores audio scene names
PROC STORE_AUDIOSCENES()
sAudioScenes[AUDIOSCENE_NONE] = "NONE"
sAudioScenes[AUDIOSCENE_GO_TO_TRACEY] = "M_E_TRACEY_GO_TO_TRACEY"
sAudioScenes[AUDIOSCENE_ENTER_TRACEYS_CAR] = "M_E_TRACEY_ENTER_TRACEYS_CAR"
sAudioScenes[AUDIOSCENE_DRIVE_TO_STALKER] = "M_E_TRACEY_FIND_STALKER"
sAudioScenes[AUDIOSCENE_CHASE] = "M_E_TRACEY_STOP_STALKERS_CAR"
// sAudioScenes[AUDIOSCENE_CHASE_FOCUS_CAM] = "M_E_TRACEY_FOCUS_CAM"
sAudioScenes[AUDIOSCENE_CHASE_FOOT] = "M_E_TRACEY_DEAL_WITH_STALKER"
sAudioScenes[AUDIOSCENE_DRIVE_HOME] = "M_E_TRACEY_TAKE_TRACEY_HOME"
sAudioScenes[AUDIOSCENE_DONE] = "NONE"
ENDPROC
/// PURPOSE:
/// Fills in structs for the destinations that Tracey guides the player to
PROC STORE_DESTINATION_POINTS()
iCount = 0
// Hawaiian Snow/Oeuvre Gallery
dpLocates[iCount].location = <<245.6922, -209.0348, 52.8895>>
dpLocates[iCount].range = 20
dpLocates[iCount].chatLabel = "MET1_LOC1"
dpLocates[iCount].extraChatLabel = "MET1_CHAT1"
dpLocates[iCount].bonkersChatLabel = "NULL"
dpLocates[iCount].blipUpdateLine = 1
iCount++
// Motel
dpLocates[iCount].location = <<52.7660, -283.8273, 46.6203>>
dpLocates[iCount].range = 20
dpLocates[iCount].chatLabel = "MET1_LOC2"
dpLocates[iCount].extraChatLabel = "MET1_CHAT2"
dpLocates[iCount].bonkersChatLabel = "MET1_CHAT2C"
dpLocates[iCount].blipUpdateLine = 2
iCount++
// Cluckin' Bell
dpLocates[iCount].location = <<-129.7824, -259.7662, 42.2812>>
dpLocates[iCount].range = 20
dpLocates[iCount].chatLabel = "MET1_LOC3"
dpLocates[iCount].extraChatLabel = "MET1_CHAT3"
dpLocates[iCount].bonkersChatLabel = "MET1_CHAT3C"
dpLocates[iCount].blipUpdateLine = 2
iCount++
// Rockford past Croq-a-hoop
dpLocates[iCount].location = <<-160.4700, -75.1948, 52.7966>>
dpLocates[iCount].range = 40
dpLocates[iCount].chatLabel = "MET1_LOC4"
dpLocates[iCount].extraChatLabel = "MET1_CHAT4"
dpLocates[iCount].bonkersChatLabel = "MET1_CHAT4C"
dpLocates[iCount].blipUpdateLine = 2
iCount++
iCurrentDestination = 0
ENDPROC
/// PURPOSE:
/// Stores the information needed for a non-axis aligned area
PROC SETUP_NAA_VOLUME(NAA_VOLUME& naaVol, VECTOR v1, VECTOR v2, FLOAT fWidth)
naaVol.vEnds[0] = v1
naaVol.vEnds[1] = v2
naaVol.fWidth = fWidth
ENDPROC
/// PURPOSE:
/// set up the stalker trigger volumes and spawn locations
PROC STORE_STALKER_TRIGGERS()
// Standard spawns
vecStalkerSpawnPoint[0] = <<-224.0988, -62.9169, 48.8994>>
fStalkerSpawnHead[0] = 250.2
vecStalkerSpawnPoint[1] = <<-98.7688, -90.0482, 56.5581>>
fStalkerSpawnHead[1] = 72.7
// Downhill spawns
vecStalkerSpawnPoint[2] = <<-190.0195, -64.3358, 50.6959>>
fStalkerSpawnHead[2] = 70.37
vecStalkerSpawnPoint[3] = <<-254.3389, -16.5055, 48.7322>>
fStalkerSpawnHead[3] = 170.42
// Uphill spawns
vecStalkerSpawnPoint[4] = <<-145.0999, -91.2274, 54.1001>>
fStalkerSpawnHead[4] = 249.33
vecStalkerSpawnPoint[5] = <<-35.9104, -97.4508, 56.3653>>
fStalkerSpawnHead[5] = 163.63
ENDPROC
/// PURPOSE:
/// Stores the data needed for all the conversations that use the CONVERSATION_INFO struct
PROC SETUP_ENROUTE_CHAT()
// Michael/Stalker conversations during the chase
ciChaseConversations[0].startDelay = 0
ciChaseConversations[0].txtBlock = "MET1AUD"
ciChaseConversations[0].rootBlock = "MET1_TRCC1"
ciChaseConversations[1].startDelay = 0
ciChaseConversations[1].txtBlock = "MET1AUD"
ciChaseConversations[1].rootBlock = "MET1_TRCC2"
ciChaseConversations[2].startDelay = 0
ciChaseConversations[2].txtBlock = "MET1AUD"
ciChaseConversations[2].rootBlock = "MET1_TRCC3"
ciChaseConversations[3].startDelay = 0
ciChaseConversations[3].txtBlock = "MET1AUD"
ciChaseConversations[3].rootBlock = "MET1_TRCC4"
ciChaseConversations[4].startDelay = 0
ciChaseConversations[4].txtBlock = "MET1AUD"
ciChaseConversations[4].rootBlock = "MET1_TRCC5"
// Tracey/Michael conversations during the chase
ciTraceyChaseConversations[0].startDelay = 0
ciTraceyChaseConversations[0].txtBlock = "MET1AUD"
ciTraceyChaseConversations[0].rootBlock = "MET1_ST1"
ciTraceyChaseConversations[1].startDelay = 0
ciTraceyChaseConversations[1].txtBlock = "MET1AUD"
ciTraceyChaseConversations[1].rootBlock = "MET1_ST2"
ciTraceyChaseConversations[2].startDelay = 0
ciTraceyChaseConversations[2].txtBlock = "MET1AUD"
ciTraceyChaseConversations[2].rootBlock = "MET1_ST3"
ciTraceyChaseConversations[3].startDelay = 0
ciTraceyChaseConversations[3].txtBlock = "MET1AUD"
ciTraceyChaseConversations[3].rootBlock = "MET1_ST4"
// Michael/stalker conversations once stalker is on foot
ciFootChaseConversations[0].startDelay = 0
ciFootChaseConversations[0].txtBlock = "MET1AUD"
ciFootChaseConversations[0].rootBlock = "MET1_STW1"
ciFootChaseConversations[1].startDelay = 0
ciFootChaseConversations[1].txtBlock = "MET1AUD"
ciFootChaseConversations[1].rootBlock = "MET1_STW2"
ciFootChaseConversations[2].startDelay = 0
ciFootChaseConversations[2].txtBlock = "MET1AUD"
ciFootChaseConversations[2].rootBlock = "MET1_STW3"
// Tracey/Michael conversations once stalker is on foot
ciTraceyFootChaseConversations[0].startDelay = 0
ciTraceyFootChaseConversations[0].txtBlock = "MET1AUD"
ciTraceyFootChaseConversations[0].rootBlock = "MET1_TRFC1"
ciTraceyFootChaseConversations[1].startDelay = 0
ciTraceyFootChaseConversations[1].txtBlock = "MET1AUD"
ciTraceyFootChaseConversations[1].rootBlock = "MET1_TRFC2"
ciTraceyFootChaseConversations[2].startDelay = 0
ciTraceyFootChaseConversations[2].txtBlock = "MET1AUD"
ciTraceyFootChaseConversations[2].rootBlock = "MET1_TRFC3"
// Tracey/Michael conversations during drive home
ciDriveHomeChat[0].startDelay = 0
ciDriveHomeChat[0].txtBlock = "MET1AUD"
ciDriveHomeChat[0].rootBlock = "MET1_TSDEAD1" // Initial dead comment - if Tracey saw
ciDriveHomeChat[1].startDelay = 0
ciDriveHomeChat[1].txtBlock = "MET1AUD"
ciDriveHomeChat[1].rootBlock = "MET1_TSDEAD2" // Initial dead comment - if Tracey didn't see
ciDriveHomeChat[2].startDelay = 0
ciDriveHomeChat[2].txtBlock = "MET1AUD"
ciDriveHomeChat[2].rootBlock = "MET1_TSESCP1" // Initial escape comment - if Tracey didn't see
ciDriveHomeChat[3].startDelay = 5000
ciDriveHomeChat[3].txtBlock = "MET1AUD"
ciDriveHomeChat[3].rootBlock = "MET1_GOHOM1" // Delayed secondary kill comment
ciDriveHomeChat[4].startDelay = 8000
ciDriveHomeChat[4].txtBlock = "MET1AUD"
ciDriveHomeChat[4].rootBlock = "MET1_GOHOM2" // Delayed car trashed comment
ciDriveHomeChat[5].startDelay = 8000
ciDriveHomeChat[5].txtBlock = "MET1AUD"
ciDriveHomeChat[5].rootBlock = "MET1_GOHOM3" // Delayed secondary escape comment
ciDriveHomeChat[6].startDelay = 8000
ciDriveHomeChat[6].txtBlock = "MET1AUD"
ciDriveHomeChat[6].rootBlock = "MET1_GOHOM4" // Tracey asks if anyone's trying to kill Michael
ciDriveHomeChat[7].startDelay = 8000
ciDriveHomeChat[7].txtBlock = "MET1AUD"
ciDriveHomeChat[7].rootBlock = "MET1_GOHOM5" // Michael asks if Tracey has heard from Lazlow
ciDriveHomeChat[8].startDelay = 0
ciDriveHomeChat[8].txtBlock = "MET1AUD"
ciDriveHomeChat[8].rootBlock = "MET1_GOHOMC" // Special comment if player's driving crazily
ENDPROC
//*************************************************************************************************************************************************
//
// Flow/progression procs
//
//*************************************************************************************************************************************************
/// PURPOSE:
/// Initialise the tracking variables needed for checking elapsed in-game clock time
PROC INIT_DRIVE_TIMERS()
iStartDate = GET_CLOCK_DAY_OF_MONTH()
iStartHours = GET_CLOCK_HOURS()
iStartMinutes = GET_CLOCK_MINUTES()
ENDPROC
/// PURPOSE:
/// Checks whether more than a certain amount of in-game clock time has passed. Good for up to 23 hours.
/// PARAMS:
/// hoursAllowed - the elapsed time we are looking for
/// RETURNS:
/// TRUE if the requested amount of time has passed
FUNC BOOL GAMECLOCK_TIMER_EXCEEDED(INT hoursAllowed)
INT iTimePassed//, iTmpStartTime
IF GET_CLOCK_DAY_OF_MONTH() <> iStartDate
iTimePassed = 60*((24 - iStartHours) + GET_CLOCK_HOURS())
iTimePassed = iTimePassed - iStartMinutes + GET_CLOCK_MINUTES()
ELSE
// Can just subtract the minute total for the start time away from the end time
iTimePassed = ((60*GET_CLOCK_HOURS()) + GET_CLOCK_MINUTES()) - (iStartMinutes + (60*iStartHours))
ENDIF
// iMinutesPassed = GET_CLOCK_MINUTES() - iStartMinutes
// IF iMinutesPassed < 0
// iMinutesPassed = iMinutesPassed + 60
// ENDIF
//
// iTimePassed = iTimePassed + iMinutesPassed
#IF IS_DEBUG_BUILD
IF bDebugCallTimeDisplay
DISPLAY_TEXT_WITH_FLOAT(0.9, 0.125, "DM_NUM", TO_FLOAT(iTimePassed), 0)
ENDIF
#ENDIF
IF iTimePassed >= 60*hoursAllowed
RETURN TRUE
ELSE
RETURN FALSE
ENDIF
ENDFUNC
/// PURPOSE:
/// Checks whether the player should fail for taking too long, after triggering a call warning them to hurry up first
/// RETURNS:
/// TRUE if the failure call has been triggered.
FUNC BOOL CHECK_WARNING_CALL()
SWITCH wfcProgress
CASE WFC_SETUP
IF NOT bWFCOnHold
// As progress might have been paused, see if we need to skip on to the fail call
IF GAMECLOCK_TIMER_EXCEEDED(RENDEZVOUS_TIMER)
wfcProgress = WFC_CHECK_FOR_FAIL
// Check for standard warning call
ELIF GAMECLOCK_TIMER_EXCEEDED(WARNING_TIMER)
CPRINTLN(DEBUG_MISSION, "Warning call time!")
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
wfcProgress = WFC_START_WARNING_CALL
ENDIF
ENDIF
ENDIF
BREAK
CASE WFC_START_WARNING_CALL
IF NOT bWFCOnHold
// Player still far enough away to call
REMOVE_PED_FOR_DIALOGUE(mConversationStruct, 3)
ADD_PED_FOR_DIALOGUE(mConversationStruct, 3, NULL, "TRACEY")
IF CHAR_CALL_PLAYER_CELLPHONE_FORCE_ANSWER(mConversationStruct, CHAR_TRACEY, "MET1AUD", "MET1_WARN2", CONV_PRIORITY_MEDIUM, DISPLAY_SUBTITLES)
wfcProgress = WFC_WAIT_FOR_WARNING
ENDIF
ENDIF
BREAK
CASE WFC_WAIT_FOR_WARNING
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
wfcProgress = WFC_CHECK_FOR_FAIL
ENDIF
BREAK
CASE WFC_CHECK_FOR_FAIL
IF NOT bWFCOnHold
IF GAMECLOCK_TIMER_EXCEEDED(RENDEZVOUS_TIMER)
REMOVE_PED_FOR_DIALOGUE(mConversationStruct,3)
ADD_PED_FOR_DIALOGUE(mConversationStruct, 3, NULL, "TRACEY")
msTrack = MST_FAIL_CALL
sProgress = SP_SETUP
RETURN TRUE
ENDIF
ENDIF
BREAK
CASE WFC_ARRIVED_WAIT
IF GET_GAME_TIMER() - iFootDawdleTimer > ARRIVED_TIMER_WARN
REMOVE_PED_FOR_DIALOGUE(mConversationStruct, 3)
ADD_PED_FOR_DIALOGUE(mConversationStruct, 3, pedTracey, "TRACEY")
wfcProgress = WFC_ARRIVED_WARN
ENDIF
BREAK
CASE WFC_ARRIVED_WARN
// Player is close enough to Tracey that a phone call would be ridiculous
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_WARNC", CONV_PRIORITY_MEDIUM)//, DO_NOT_DISPLAY_SUBTITLES)
iFootDawdleTimer = GET_GAME_TIMER()
wfcProgress = WFC_ARRIVED_WAIT
ENDIF
BREAK
// Not currently allowing time out fails once player has reached car park
// CASE WFC_ARRIVED_CHECK_FOR_FAIL
//
// IF GET_GAME_TIMER() - iFootDawdleTimer > ARRIVED_TIMER_FAIL // We'll need to replace with a new timer if this goes back in
// ADD_PED_FOR_DIALOGUE(mConversationStruct, 3, pedTracey, "TRACEY")
// msTrack = MST_FAIL_TRACEY_DRIVE_OFF
// sProgress = SP_SETUP
// RETURN TRUE
// ENDIF
// BREAK
CASE WFC_DONE
// Don't run these checks any more
BREAK
ENDSWITCH
// If we reach the end, no time-up fail yet
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Tracey mutters to herself while waiting for Michael
PROC TRACEY_WAITING_DIALOGUE()
SWITCH cptTraceyWaitingComments
CASE CPT_WAIT_TO_TRIGGER
IF GET_GAME_TIMER() - iTraceyWaitingChatTimer > 5000
AND IS_ENTITY_IN_RANGE_ENTITY(pedTracey, PLAYER_PED_ID(), 25)
CPRINTLN(DEBUG_MISSION, "ME_TRACEY1::: Starting Tracey waiting comment")
cptTraceyWaitingComments = CPT_START_CONVERSATION
ENDIF
BREAK
CASE CPT_START_CONVERSATION
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_PACE", CONV_PRIORITY_MEDIUM)
IF iCurrentAudioScene <> AUDIOSCENE_ENTER_TRACEYS_CAR
UPDATE_AUDIO_SCENE(AUDIOSCENE_ENTER_TRACEYS_CAR)
ENDIF
cptTraceyWaitingComments = CPT_WAIT_TO_FINISH
ENDIF
ENDIF
BREAK
CASE CPT_WAIT_TO_FINISH
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
iTraceyWaitingConversationCount++
IF iTraceyWaitingConversationCount < 4
iTraceyWaitingChatTimer = GET_GAME_TIMER()
cptTraceyWaitingComments = CPT_WAIT_TO_TRIGGER
ELSE
cptTraceyWaitingComments = CPT_SPENT
ENDIF
ENDIF
BREAK
CASE CPT_SPENT
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Tracks Tracey's car moving for purposes of triggering complaint conversations
PROC UPDATE_TRACEYCAR_SAVED_COORDS()
IF GET_GAME_TIMER() - iTraceyPositionTimer > 3000
vecTraceyPositionTrack[0] = vecTraceyPositionTrack[1]
vecTraceyPositionTrack[1] = vecTraceyPositionTrack[2]
vecTraceyPositionTrack[2] = GET_ENTITY_COORDS(viTraceyCar)
iTraceyPositionTimer = GET_GAME_TIMER()
ENDIF
ENDPROC
/// PURPOSE:
/// Checks whether Tracey's car is more or less stationary
/// RETURNS:
/// TRUE if moved less than 2m in 6 seconds
FUNC BOOL NOT_GOING_ANYWHERE()
IF ARE_PROGRESS_COORDS_IN_RANGE(vecTraceyPositionTrack[0], vecTraceyPositionTrack[2], 2)
RETURN TRUE
ENDIF
iComplainTimer = GET_GAME_TIMER()
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Trigger incidental banter during the drive.
PROC BANTER_CONTROLLER()
// If no dialogue playing and situation is generally OK for it, check for Tracey complaint about player idling
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
AND NOT CAR_CURRENTLY_ON_ROOF()
AND IS_PED_IN_VEHICLE(pedTracey, viTraceyCar)
AND IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
// See if we've been sitting still for too long
IF NOT_GOING_ANYWHERE()
AND GET_GAME_TIMER() - iComplainTimer > NOT_GOING_DELAY
CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_NOTGO", CONV_PRIORITY_AMBIENT_HIGH)
iComplainTimer = GET_GAME_TIMER()
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Checks whether the player is shooting
/// RETURNS:
/// TRUE if player has fired a weapon in last few seconds
FUNC BOOL PLAYER_SHOT_RECENTLY()
// Update shooting timer
IF IS_PED_SHOOTING(PLAYER_PED_ID())
iShootingTimer = GET_GAME_TIMER()
RETURN TRUE
// Check if time since we last detected firing is less that timeout
ELSE
INT iTmpTime = GET_GAME_TIMER() - iShootingTimer
IF iTmpTime > 0 AND iTmpTime < RECENT_SHOOTING_TIMEOUT
RETURN TRUE
ENDIF
ENDIF
// Player hasn't shot recently
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Used as part of checks to make Tracey complain if the player rams things that aren't the stalker's car.
/// Safe test to prevent assert on stalker's car if it's dead.
/// (Tracey's car has a death check higher up so not a concern)
/// RETURNS:
/// TRUE for collisions that aren't a live stalker car
FUNC BOOL TRACEY_CAR_COLLISION_CHECK()
IF HAS_ENTITY_COLLIDED_WITH_ANYTHING(viTraceyCar)
IF IS_ENTITY_ALIVE(mvStalkerCar.vehicle)
AND IS_ENTITY_TOUCHING_ENTITY(viTraceyCar, mvStalkerCar.vehicle)
// Bashing the stalker doesn't count as a collision
RETURN FALSE
ENDIF
// Either the stalker's car is dead or it's not what we collided with!
RETURN TRUE
ENDIF
// No collision has occured
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Triggers dialogue in the car chase -
/// - Michael shouting at stalker
/// - Tracey fretting/banter
/// - Tracey commenting if stalker is getting away
/// - Tracey commenting if they ram the stalker's car
PROC STALKER_CHASE_CHAT()
// Check for wait override chat types first - these can short-circuit the switch if no other conversation is running
// Quick ram comment
IF bQuickRamComment = TRUE
AND (cptChaseTrack = CPT_WAIT_TO_TRIGGER OR cptChaseTrack = CPT_PICK_CONVERSATION)
cptChaseTrack = CPT_START_CONVERSATION_RAMARK
ELSE
// Can't trigger a remark just now - reset the flag
bQuickRamComment = FALSE
ENDIF
// Check for other collisions stopping the player car
iTimeSinceCollision = GET_GAME_TIMER() - iLastCollisionTime
IF iTimeSinceCollision > 0 AND iTimeSinceCollision < 250
AND (cptChaseTrack = CPT_WAIT_TO_TRIGGER OR cptChaseTrack = CPT_PICK_CONVERSATION)
AND GET_ENTITY_SPEED(viTraceyCar) < 1.5 // 1.0
AND GET_ENTITY_SPEED(mvStalkerCar.vehicle) > 3.0 // 2.0
// Crashed complaint
CPRINTLN(DEBUG_MISSION, "Tracey car smash - play an escape conversation")
cptChaseTrack = CPT_START_CONVERSATION_ESCAPING
ENDIF
// IF HAS_ENTITY_COLLIDED_WITH_ANYTHING(viTraceyCar)
// AND NOT IS_ENTITY_TOUCHING_ENTITY(viTraceyCar, mvStalkerCar.vehicle)
IF TRACEY_CAR_COLLISION_CHECK()
CPRINTLN(DEBUG_MISSION, "Tracey's car collided with something this frame")
iLastCollisionTime = GET_GAME_TIMER()
ENDIF
// Check for shooting and trigger a complaint if it's been long enough since the last one
// Not inside the case statement so we can keep updating the timer in PLAYER_SHOT_RECENTLY
IF PLAYER_SHOT_RECENTLY()
AND GET_GAME_TIMER() > iShootingComplaintGap
CPRINTLN(DEBUG_MISSION, "Player shot recently and it's been a while since we last scolded them, checking if safe to trigger")
IF cptChaseTrack = CPT_WAIT_TO_TRIGGER
OR cptChaseTrack = CPT_PICK_CONVERSATION
CPRINTLN(DEBUG_MISSION, "Player shot recently and it's been a while since we last scolded them, safe to trigger Tracey complaint")
cptChaseTrack = CPT_START_CONVERSATION_TRACEY_WTF
ENDIF
ENDIF
SWITCH cptChaseTrack
// Wait for a decent time to pass since last comment
CASE CPT_WAIT_TO_TRIGGER
// Wait to trigger conversation
IF GET_GAME_TIMER() - iChaseChatTimer > CHASE_COMMENT_DELAY // ciChaseConversations[iChaseStalkerChatCount].startDelay
AND IS_ENTITY_ALIVE(pedStalker)
AND NOT MISSION_OBJECTIVES_CURRENTLY_DISPLAYED() // So subtitles cannot override chase objective
AND NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED() // We might have only just triggered the stalker spotted exchange
CPRINTLN(DEBUG_MISSION, "Starting chase dialogue")
ADD_PED_FOR_DIALOGUE(mConversationStruct, 4, pedStalker, "Stalker")
cptChaseTrack = CPT_PICK_CONVERSATION
ENDIF
BREAK
// Pick a conversation type
CASE CPT_PICK_CONVERSATION
IF IS_ENTITY_ALIVE(pedStalker)
// Check if Stalker is getting away first
IF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedStalker, STALKER_ESCAPE_DIST*CHASE_FLASH_DIST) // Changed from 100m to be the same as the flash distance
IF IS_PED_IN_ANY_VEHICLE(pedStalker)
// Tracey "getting away" comment
cptChaseTrack = CPT_START_CONVERSATION_ESCAPING
ENDIF
// See if the player car isn't moving
ELIF (NOT_GOING_ANYWHERE() AND GET_GAME_TIMER() - iComplainTimer > NOT_GOING_DELAY_CHASE)
// Immobile complaint
cptChaseTrack = CPT_START_CONVERSATION_STOPPED
ELSE
// Stalker not getting away, see if we're close enough to stalker for Michael to shout at him
IF IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedStalker, 50.0)
// Has there been more stalker+Michael than Michael+Tracey
// Chat should more or less alternate for as long as there is chat left in each set
IF iChaseStalkerChatCount > iChaseTraceyChatCount
// We would prefer to trigger Tracey+Michael banter as the stalker's had most so far...
IF iChaseTraceyChatCount < NUM_CHASE_CONVERSATIONS_T
// Tracey still has banter left, trigger that
cptChaseTrack = CPT_START_CONVERSATION_TRACEY
ELSE
// Tracey+Michael list is empty - check stalker list
IF iChaseStalkerChatCount < NUM_CHASE_CONVERSATIONS_S
// Stalker still has banter left, trigger that
cptChaseTrack = CPT_START_CONVERSATION_STALKER
ELSE
// Neither has any banter left
CPRINTLN(DEBUG_MISSION, "All chase dialogue exhausted - wait to see if we need a warning comment next time")
bStalkerChatting = FALSE
iChaseChatTimer = GET_GAME_TIMER()
cptChaseTrack = CPT_WAIT_TO_TRIGGER
ENDIF
ENDIF
ELSE
// We would prefer to trigger Stalker+Michael banter as either Tracey has had most or scores are tied...
IF iChaseStalkerChatCount < NUM_CHASE_CONVERSATIONS_S
// Stalker still has banter left, trigger that
cptChaseTrack = CPT_START_CONVERSATION_STALKER
ELSE
// Stalker+Michael list is empty - check Tracey list
IF iChaseTraceyChatCount < NUM_CHASE_CONVERSATIONS_T
// Tracey still has banter left, trigger that
cptChaseTrack = CPT_START_CONVERSATION_TRACEY
ELSE
// Neither has any banter left
CPRINTLN(DEBUG_MISSION, "All chase dialogue exhausted - wait to see if we need a warning comment next time")
bTraceyChatting = FALSE
iChaseChatTimer = GET_GAME_TIMER()
cptChaseTrack = CPT_WAIT_TO_TRIGGER
ENDIF
ENDIF
ENDIF
ELSE
// Stalker isn't close enough for Michael to shout at him - see if there's still Tracey dialogue
IF iChaseTraceyChatCount < NUM_CHASE_CONVERSATIONS_T
// Tracey
cptChaseTrack = CPT_START_CONVERSATION_TRACEY
ELSE
// Tracey has no banter left, roll back to beginning
CPRINTLN(DEBUG_MISSION, "Would like to trigger Tracey banter but none left - roll back to wait state")
bTraceyChatting = FALSE
iChaseChatTimer = GET_GAME_TIMER()
cptChaseTrack = CPT_WAIT_TO_TRIGGER
ENDIF
ENDIF
ENDIF
ENDIF
BREAK
// Play a Michael/stalker interaction
CASE CPT_START_CONVERSATION_STALKER
IF IS_THIS_PRINT_BEING_DISPLAYED("MET1KSTKRED") OR IS_THIS_PRINT_BEING_DISPLAYED("MET1KSTKBLU")
IF CREATE_CONVERSATION(mConversationStruct, ciChaseConversations[iChaseStalkerChatCount].txtBlock, ciChaseConversations[iChaseStalkerChatCount].rootBlock, CONV_PRIORITY_MEDIUM, DO_NOT_DISPLAY_SUBTITLES, DO_ADD_TO_BRIEF_SCREEN)
CPRINTLN(DEBUG_MISSION, "Starting chase conversation ", iChaseStalkerChatCount, " with no subtitles.")
bStalkerChatting = TRUE
bTraceyChatting = FALSE
bShootingComment = FALSE
cptChaseTrack = CPT_WAIT_TO_FINISH
ENDIF
ELSE
IF CREATE_CONVERSATION(mConversationStruct, ciChaseConversations[iChaseStalkerChatCount].txtBlock, ciChaseConversations[iChaseStalkerChatCount].rootBlock, CONV_PRIORITY_MEDIUM)
CPRINTLN(DEBUG_MISSION, "Starting chase conversation ", iChaseStalkerChatCount, " with subtitles.")
bStalkerChatting = TRUE
bTraceyChatting = FALSE
bShootingComment = FALSE
cptChaseTrack = CPT_WAIT_TO_FINISH
ENDIF
ENDIF
BREAK
// Play a Tracey/Michael interaction
CASE CPT_START_CONVERSATION_TRACEY
IF IS_THIS_PRINT_BEING_DISPLAYED("MET1KSTKRED") OR IS_THIS_PRINT_BEING_DISPLAYED("MET1KSTKBLU")
IF CREATE_CONVERSATION(mConversationStruct, ciTraceyChaseConversations[iChaseTraceyChatCount].txtBlock, ciTraceyChaseConversations[iChaseTraceyChatCount].rootBlock, CONV_PRIORITY_MEDIUM, DO_NOT_DISPLAY_SUBTITLES, DO_ADD_TO_BRIEF_SCREEN)
CPRINTLN(DEBUG_MISSION, "Starting Tracey chase conversation ", iChaseTraceyChatCount, " with no subtitles.")
bStalkerChatting = FALSE
bTraceyChatting = TRUE
bShootingComment = FALSE
cptChaseTrack = CPT_WAIT_TO_FINISH
ENDIF
ELSE
IF CREATE_CONVERSATION(mConversationStruct, ciTraceyChaseConversations[iChaseTraceyChatCount].txtBlock, ciTraceyChaseConversations[iChaseTraceyChatCount].rootBlock, CONV_PRIORITY_MEDIUM)
CPRINTLN(DEBUG_MISSION, "Starting Tracey chase conversation ", iChaseStalkerChatCount, " with subtitles.")
bStalkerChatting = FALSE
bTraceyChatting = TRUE
bShootingComment = FALSE
cptChaseTrack = CPT_WAIT_TO_FINISH
ENDIF
ENDIF
BREAK
// Tracey warning Michael that stalker is escaping
CASE CPT_START_CONVERSATION_ESCAPING
IF IS_THIS_PRINT_BEING_DISPLAYED("MET1KSTKRED") OR IS_THIS_PRINT_BEING_DISPLAYED("MET1KSTKBLU")
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_GETHIM", CONV_PRIORITY_MEDIUM, DO_NOT_DISPLAY_SUBTITLES)
CPRINTLN(DEBUG_MISSION, "Starting Tracey escape warning conversation with no subtitles.")
bStalkerChatting = FALSE
bTraceyChatting = FALSE
bShootingComment = FALSE
cptChaseTrack = CPT_WAIT_TO_FINISH
ENDIF
ELSE
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_GETHIM", CONV_PRIORITY_MEDIUM)
CPRINTLN(DEBUG_MISSION, "Starting Tracey escape warning conversation with subtitles.")
bStalkerChatting = FALSE
bTraceyChatting = FALSE
bShootingComment = FALSE
cptChaseTrack = CPT_WAIT_TO_FINISH
ENDIF
ENDIF
BREAK
// Tracey remarks on Michael successfully ramming stalker's car
CASE CPT_START_CONVERSATION_RAMARK
IF IS_THIS_PRINT_BEING_DISPLAYED("MET1KSTKRED") OR IS_THIS_PRINT_BEING_DISPLAYED("MET1KSTKBLU")
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_BASH", CONV_PRIORITY_MEDIUM, DO_NOT_DISPLAY_SUBTITLES)
CPRINTLN(DEBUG_MISSION, "Starting Tracey ram comment with no subtitles.")
bStalkerChatting = FALSE
bTraceyChatting = FALSE
bQuickRamComment = FALSE
bShootingComment = FALSE
cptChaseTrack = CPT_WAIT_TO_FINISH
ENDIF
ELSE
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_BASH", CONV_PRIORITY_MEDIUM)
CPRINTLN(DEBUG_MISSION, "Starting Tracey ram comment with subtitles.")
bStalkerChatting = FALSE
bTraceyChatting = FALSE
bQuickRamComment = FALSE
bShootingComment = FALSE
cptChaseTrack = CPT_WAIT_TO_FINISH
ENDIF
ENDIF
BREAK
// Tracey complains if Michael has stopped
CASE CPT_START_CONVERSATION_STOPPED
IF IS_THIS_PRINT_BEING_DISPLAYED("MET1KSTKRED") OR IS_THIS_PRINT_BEING_DISPLAYED("MET1KSTKBLU")
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_STOP", CONV_PRIORITY_MEDIUM, DO_NOT_DISPLAY_SUBTITLES)
CPRINTLN(DEBUG_MISSION, "Starting Tracey stopped complaint with no subtitles.")
iComplainTimer = GET_GAME_TIMER()
bStalkerChatting = FALSE
bTraceyChatting = FALSE
bQuickRamComment = FALSE
bShootingComment = FALSE
cptChaseTrack = CPT_WAIT_TO_FINISH
ENDIF
ELSE
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_STOP", CONV_PRIORITY_MEDIUM)
CPRINTLN(DEBUG_MISSION, "Starting Tracey stopped complaint with subtitles.")
iComplainTimer = GET_GAME_TIMER()
bStalkerChatting = FALSE
bTraceyChatting = FALSE
bQuickRamComment = FALSE
bShootingComment = FALSE
cptChaseTrack = CPT_WAIT_TO_FINISH
ENDIF
ENDIF
BREAK
// Tracey complaint about Michael going psycho
CASE CPT_START_CONVERSATION_TRACEY_WTF
IF IS_THIS_PRINT_BEING_DISPLAYED("MET1KSTKRED") OR IS_THIS_PRINT_BEING_DISPLAYED("MET1KSTKBLU")
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_MESS", CONV_PRIORITY_MEDIUM, DO_NOT_DISPLAY_SUBTITLES)
CPRINTLN(DEBUG_MISSION, "Starting Tracey player psycho complaint with no subtitles.")
iComplainTimer = GET_GAME_TIMER()
bStalkerChatting = FALSE
bTraceyChatting = FALSE
bQuickRamComment = FALSE
bShootingComment = TRUE
cptChaseTrack = CPT_WAIT_TO_FINISH
ENDIF
ELSE
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_MESS", CONV_PRIORITY_MEDIUM)
CPRINTLN(DEBUG_MISSION, "Starting Tracey player psycho complaint with subtitles.")
iComplainTimer = GET_GAME_TIMER()
bStalkerChatting = FALSE
bTraceyChatting = FALSE
bQuickRamComment = FALSE
bShootingComment = TRUE
cptChaseTrack = CPT_WAIT_TO_FINISH
ENDIF
ENDIF
BREAK
// Wait for the current conversation to finish
CASE CPT_WAIT_TO_FINISH
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
// Current conversation completed - increment counter for whoever was yakking
IF bStalkerChatting
iChaseStalkerChatCount++
ELIF bTraceyChatting
iChaseTraceyChatCount++
ENDIF
IF bShootingComment
iShootingComplaintGap = GET_GAME_TIMER() + SHOOTING_COMPLAINT_GAP
ENDIF
iChaseChatTimer = GET_GAME_TIMER()
cptChaseTrack = CPT_WAIT_TO_TRIGGER
ENDIF
BREAK
CASE CPT_SPENT
// Finished
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Stalker/Michael dialogue if the stalker has abandoned his car
PROC STALKER_ON_FOOT_CHAT()
// Check for shooting and trigger a complaint if it's been long enough since the last one
// Not inside the case statement so we can keep updating the timer in PLAYER_SHOT_RECENTLY
IF PLAYER_SHOT_RECENTLY()
AND GET_GAME_TIMER() > iShootingComplaintGap
CPRINTLN(DEBUG_MISSION, "Player shot recently and it's been a while since we last scolded them, checking if safe to trigger")
IF cptChaseTrack = CPT_WAIT_TO_TRIGGER
OR cptChaseTrack = CPT_PICK_CONVERSATION
IF IS_ENTITY_ALIVE(viTraceyCar)
AND (IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar) OR IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), viTraceyCar, 15.0))
CPRINTLN(DEBUG_MISSION, "Player shot recently and it's been a while since we last scolded them, safe to trigger Tracey complaint")
cptChaseTrack = CPT_START_CONVERSATION_TRACEY_WTF
ENDIF
ENDIF
ENDIF
SWITCH cptFootChaseTrack
CASE CPT_WAIT_TO_TRIGGER
// Wait to trigger conversation
IF GET_GAME_TIMER() - iChaseChatTimer > CHASE_COMMENT_DELAY
CPRINTLN(DEBUG_MISSION, "Time to try triggering foot chase dialogue...")
ADD_PED_FOR_DIALOGUE(mConversationStruct, 4, pedStalker, "Stalker")
cptFootChaseTrack = CPT_PICK_CONVERSATION
ENDIF
BREAK
CASE CPT_PICK_CONVERSATION
// Remember whether player is in the car...
BOOL bPlayerInCar
IF IS_ENTITY_ALIVE(viTraceyCar)
AND IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
bPlayerInCar = TRUE
ELSE
// CPRINTLN(DEBUG_MISSION, "Player not currently in Tracey's car, this will limit dialogue options.")
bPlayerInCar = FALSE
ENDIF
IF IS_ENTITY_ALIVE(pedStalker)
// Stalker not getting away, see if we're close enough to stalker for Michael to shout at him
IF IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedStalker, STALKER_FOOT_MUTE_DIST)
// Has there been more stalker+Michael than Michael+Tracey
// Chat should more or less alternate for as long as there is chat left in each set unless player is on foot
IF bPlayerInCar
AND iFootChaseStalkerChat > iFootChaseTraceyChat
// We would prefer to trigger Tracey+Michael banter as the stalker's had most so far...
IF iFootChaseTraceyChat < NUM_FOOT_CONVERSATIONS_T
// Tracey still has banter left, trigger that
cptFootChaseTrack = CPT_START_CONVERSATION_TRACEY
ELSE
// Tracey+Michael list is empty - check stalker list
IF iFootChaseStalkerChat < NUM_FOOT_CONVERSATIONS_S
// Stalker still has banter left, trigger that
cptFootChaseTrack = CPT_START_CONVERSATION_STALKER
ELSE
// Neither has any banter left
CPRINTLN(DEBUG_MISSION, "Sequenced chase dialogue exhausted - trigger a random stalker line")
cptFootChaseTrack = CPT_START_RANDOM_STALKER
// // Neither has any banter left
// CPRINTLN(DEBUG_MISSION, "All valid chase dialogue exhausted - wait to see if we need a warning comment next time")
// bStalkerChatting = FALSE
// iChaseChatTimer = GET_GAME_TIMER()
// cptFootChaseTrack = CPT_WAIT_TO_TRIGGER
ENDIF
ENDIF
ELSE
// We would prefer to trigger Stalker+Michael banter as either Tracey has had most, scores are tied, or player is on foot so Tracey is unavailable...
IF iFootChaseStalkerChat < NUM_FOOT_CONVERSATIONS_S
// Stalker still has banter left, trigger that
cptFootChaseTrack = CPT_START_CONVERSATION_STALKER
ELSE
// Stalker+Michael list is empty - check Tracey list if player in car
IF bPlayerInCar
AND iFootChaseTraceyChat < NUM_FOOT_CONVERSATIONS_T
// Tracey still has banter left, trigger that
cptFootChaseTrack = CPT_START_CONVERSATION_TRACEY
ELSE
// Neither has any banter left
CPRINTLN(DEBUG_MISSION, "Sequenced chase dialogue exhausted - trigger a random stalker line")
cptFootChaseTrack = CPT_START_RANDOM_STALKER
// // Neither has any banter left
// CPRINTLN(DEBUG_MISSION, "All valid chase dialogue exhausted - wait to see if we need a warning comment next time")
// bStalkerChatting = FALSE
// iChaseChatTimer = GET_GAME_TIMER()
// cptFootChaseTrack = CPT_WAIT_TO_TRIGGER
ENDIF
ENDIF
ENDIF
ELSE
// Do Tracey dialogue as long as player's in car
IF bPlayerInCar
// Check whether he's escaping
IF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedStalker, STALKER_FOOT_ESCAPE_DIST*0.75)
IF NOT bTraceyEscapeCommentDone
// Tracey should do escape comment
cptFootChaseTrack = CPT_START_CONVERSATION_ESCAPING
ELSE
// Tracey has done her escape comment - see if she has other dialogue left
IF iFootChaseTraceyChat < NUM_FOOT_CONVERSATIONS_T
// Tracey
cptFootChaseTrack = CPT_START_CONVERSATION_TRACEY
ELSE
// Tracey has no banter left, roll back to beginning
CPRINTLN(DEBUG_MISSION, "Would like to trigger Tracey banter but none left - roll back to wait state")
bTraceyChatting = FALSE
bShootingComment = FALSE
iChaseChatTimer = GET_GAME_TIMER()
cptFootChaseTrack = CPT_WAIT_TO_TRIGGER
ENDIF
ENDIF
ELSE
// Stalker isn't close enough for Michael to shout at him - see if there's still Tracey dialogue
IF iFootChaseTraceyChat < NUM_FOOT_CONVERSATIONS_T
// Tracey
cptFootChaseTrack = CPT_START_CONVERSATION_TRACEY
ELSE
// Tracey has no banter left, roll back to beginning
CPRINTLN(DEBUG_MISSION, "Would like to trigger Tracey banter but none left - roll back to wait state")
bTraceyChatting = FALSE
bShootingComment = FALSE
iChaseChatTimer = GET_GAME_TIMER()
cptFootChaseTrack = CPT_WAIT_TO_TRIGGER
ENDIF
ENDIF
// See if we could do "getting away" chat
ELIF NOT bTraceyEscapeCommentDone
AND NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedStalker, STALKER_FOOT_ESCAPE_DIST*0.5)
AND IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedTracey, 15.0)
// Tracey should do escape comment
cptFootChaseTrack = CPT_START_CONVERSATION_ESCAPING
ENDIF
ENDIF
ENDIF
BREAK
CASE CPT_START_CONVERSATION_STALKER
IF IS_ENTITY_ALIVE(pedStalker)
IF IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedStalker, STALKER_FOOT_MUTE_DIST)
IF IS_THIS_PRINT_BEING_DISPLAYED("MET1KSTKRED") OR IS_THIS_PRINT_BEING_DISPLAYED("MET1KSTKBLU")
IF CREATE_CONVERSATION(mConversationStruct, ciFootChaseConversations[iFootChaseStalkerChat].txtBlock, ciFootChaseConversations[iFootChaseStalkerChat].rootBlock, CONV_PRIORITY_MEDIUM, DO_NOT_DISPLAY_SUBTITLES, DO_ADD_TO_BRIEF_SCREEN)
CPRINTLN(DEBUG_MISSION, "Starting stalker foot chase conversation ", iFootChaseStalkerChat, " with no subtitles.")
bStalkerChatting = TRUE
bTraceyChatting = FALSE
bShootingComment = FALSE
cptFootChaseTrack = CPT_WAIT_TO_FINISH
ENDIF
ELSE
IF CREATE_CONVERSATION(mConversationStruct, ciFootChaseConversations[iFootChaseStalkerChat].txtBlock, ciFootChaseConversations[iFootChaseStalkerChat].rootBlock, CONV_PRIORITY_MEDIUM)
CPRINTLN(DEBUG_MISSION, "Starting stalker foot chase conversation ", iFootChaseStalkerChat)
bStalkerChatting = TRUE
bTraceyChatting = FALSE
bShootingComment = FALSE
cptFootChaseTrack = CPT_WAIT_TO_FINISH
ENDIF
ENDIF
ELSE
CPRINTLN(DEBUG_MISSION, "Stalker wasn't in range for dialogue in CPT_START_CONVERSATION_STALKER, go back to start and wait for another go.")
bStalkerChatting = FALSE
bTraceyChatting = FALSE
bShootingComment = FALSE
iChaseChatTimer = GET_GAME_TIMER()
cptFootChaseTrack = CPT_WAIT_TO_TRIGGER
ENDIF
ENDIF
BREAK
CASE CPT_START_RANDOM_STALKER
IF IS_ENTITY_ALIVE(pedStalker)
IF IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedStalker, STALKER_FOOT_MUTE_DIST)
IF IS_THIS_PRINT_BEING_DISPLAYED("MET1KSTKRED") OR IS_THIS_PRINT_BEING_DISPLAYED("MET1KSTKBLU")
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_STWR", CONV_PRIORITY_MEDIUM, DO_NOT_DISPLAY_SUBTITLES, DO_ADD_TO_BRIEF_SCREEN)
CPRINTLN(DEBUG_MISSION, "Starting stalker foot chase random conversation with no subtitles.")
bStalkerChatting = TRUE
bTraceyChatting = FALSE
bShootingComment = FALSE
cptFootChaseTrack = CPT_WAIT_TO_FINISH
ENDIF
ELSE
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_STWR", CONV_PRIORITY_MEDIUM)
CPRINTLN(DEBUG_MISSION, "Starting stalker foot chase random conversation.")
bStalkerChatting = TRUE
bTraceyChatting = FALSE
bShootingComment = FALSE
cptFootChaseTrack = CPT_WAIT_TO_FINISH
ENDIF
ENDIF
ELSE
CPRINTLN(DEBUG_MISSION, "Stalker wasn't in range for dialogue in CPT_START_CONVERSATION_STALKER, go back to start and wait for another go.")
bStalkerChatting = FALSE
bTraceyChatting = FALSE
bShootingComment = FALSE
iChaseChatTimer = GET_GAME_TIMER()
cptFootChaseTrack = CPT_WAIT_TO_TRIGGER
ENDIF
ENDIF
BREAK
CASE CPT_START_CONVERSATION_TRACEY
IF IS_THIS_PRINT_BEING_DISPLAYED("MET1KSTKRED") OR IS_THIS_PRINT_BEING_DISPLAYED("MET1KSTKBLU")
IF CREATE_CONVERSATION(mConversationStruct, ciTraceyFootChaseConversations[iFootChaseTraceyChat].txtBlock, ciTraceyFootChaseConversations[iFootChaseTraceyChat].rootBlock, CONV_PRIORITY_MEDIUM, DO_NOT_DISPLAY_SUBTITLES, DO_ADD_TO_BRIEF_SCREEN)
CPRINTLN(DEBUG_MISSION, "Starting Tracey foot chase conversation ", iFootChaseTraceyChat, " with no subtitles.")
bStalkerChatting = FALSE
bTraceyChatting = TRUE
bShootingComment = FALSE
cptFootChaseTrack = CPT_WAIT_TO_FINISH
ENDIF
ELSE
IF CREATE_CONVERSATION(mConversationStruct, ciTraceyFootChaseConversations[iFootChaseTraceyChat].txtBlock, ciTraceyFootChaseConversations[iFootChaseTraceyChat].rootBlock, CONV_PRIORITY_MEDIUM)
CPRINTLN(DEBUG_MISSION, "Starting Tracey foot chase conversation ", iFootChaseTraceyChat)
bStalkerChatting = FALSE
bTraceyChatting = TRUE
bShootingComment = FALSE
cptFootChaseTrack = CPT_WAIT_TO_FINISH
ENDIF
ENDIF
BREAK
CASE CPT_START_CONVERSATION_ESCAPING
// Need to play Tracey's "Are we letting him go?" comment
IF IS_THIS_PRINT_BEING_DISPLAYED("MET1KSTKRED") OR IS_THIS_PRINT_BEING_DISPLAYED("MET1KSTKBLU")
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_TNOGOF", CONV_PRIORITY_MEDIUM, DO_NOT_DISPLAY_SUBTITLES, DO_ADD_TO_BRIEF_SCREEN)
CPRINTLN(DEBUG_MISSION, "Playing Tracey's letting-him-go conversation with no subtitles.")
bTraceyEscapeCommentDone = TRUE
bStalkerChatting = FALSE
bTraceyChatting = FALSE
bShootingComment = FALSE
cptFootChaseTrack = CPT_WAIT_TO_FINISH
ENDIF
ELSE
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_TNOGOF", CONV_PRIORITY_MEDIUM)
CPRINTLN(DEBUG_MISSION, "Playing Tracey's letting-him-go conversation.")
bTraceyEscapeCommentDone = TRUE
bStalkerChatting = FALSE
bTraceyChatting = FALSE
bShootingComment = FALSE
cptFootChaseTrack = CPT_WAIT_TO_FINISH
ENDIF
ENDIF
BREAK
// Tracey complaint about Michael going psycho
CASE CPT_START_CONVERSATION_TRACEY_WTF
IF IS_THIS_PRINT_BEING_DISPLAYED("MET1KSTKRED") OR IS_THIS_PRINT_BEING_DISPLAYED("MET1KSTKBLU")
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_MESS", CONV_PRIORITY_MEDIUM, DO_NOT_DISPLAY_SUBTITLES)
CPRINTLN(DEBUG_MISSION, "Starting Tracey player psycho complaint with no subtitles.")
iComplainTimer = GET_GAME_TIMER()
bStalkerChatting = FALSE
bTraceyChatting = FALSE
bQuickRamComment = FALSE
bShootingComment = TRUE
cptChaseTrack = CPT_WAIT_TO_FINISH
ENDIF
ELSE
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_MESS", CONV_PRIORITY_MEDIUM)
CPRINTLN(DEBUG_MISSION, "Starting Tracey player psycho complaint with subtitles.")
iComplainTimer = GET_GAME_TIMER()
bStalkerChatting = FALSE
bTraceyChatting = FALSE
bQuickRamComment = FALSE
bShootingComment = TRUE
cptChaseTrack = CPT_WAIT_TO_FINISH
ENDIF
ENDIF
BREAK
CASE CPT_WAIT_TO_FINISH
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
// Current conversation completed - increment counter for whoever was yakking
CPRINTLN(DEBUG_MISSION, "Conversation finished.")
IF bStalkerChatting
iFootChaseStalkerChat++
ELIF bTraceyChatting
iFootChaseTraceyChat++
ENDIF
IF bShootingComment
iShootingComplaintGap = GET_GAME_TIMER() + SHOOTING_COMPLAINT_GAP
ENDIF
iChaseChatTimer = GET_GAME_TIMER()
cptFootChaseTrack = CPT_WAIT_TO_TRIGGER
ELSE
// Check if we need to interrupt stalker conversations
IF bStalkerChatting
AND IS_ENTITY_ALIVE(pedStalker)
// Proximity kill
IF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedStalker, STALKER_FOOT_MUTE_DIST)
CPRINTLN(DEBUG_MISSION, "Killing a stalker conversation due to being too far away...")
KILL_FACE_TO_FACE_CONVERSATION()
ENDIF
// Check if we need to interrupt a stalker line due to injury
IF HAS_ENTITY_BEEN_DAMAGED_BY_ENTITY(pedStalker, PLAYER_PED_ID())
IF GET_SPEAKER_INT_FOR_CURRENT_STANDARD_CONVERSATION_LINE() = 4
// Stalker has been injured while speaking
CPRINTLN(DEBUG_MISSION, "Killing a stalker conversation due to injury/death")
KILL_FACE_TO_FACE_CONVERSATION_DO_NOT_FINISH_LAST_LINE()
ENDIF
CLEAR_ENTITY_LAST_DAMAGE_ENTITY(pedStalker)
ENDIF
// Check if we need to interrupt Tracey conversations
ELIF bTraceyChatting
AND IS_ENTITY_ALIVE(viTraceyCar)
// Proximity kill
IF NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar, TRUE)
AND NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedStalker, STALKER_FOOT_MUTE_DIST)
CPRINTLN(DEBUG_MISSION, "Killing a Tracey conversation due to being too far away...")
KILL_FACE_TO_FACE_CONVERSATION()
ENDIF
ENDIF
ENDIF
BREAK
CASE CPT_SPENT
// Finished
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Plays a series of chat events on the way home
PROC DRIVING_HOME_CHAT()
// Simple int controlled progression. If you change this, don't remove the breaks in the ELIF chain.
// These allow us to skip to starting next conversation if the one in the current step isn't needed without having to wait a frame.
// Keep this linear instead of a loop so it's easier to have checks for whether specific comments should play.
// First comment - dependent on stalker outcome
// Split into two parts so first part can be unsubtitled if needed
// This initial two-part conversation is allowed to progress from part 1 to part 2 while wanted - B*1054078
IF iDriveHomeChatStep = 0
// Check which comment to trigger
IF bStalkerDead
IF bTraceySawOutcome
// Stalker dead - Tracey saw
IF NOT bConversationKilled
IF IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
KILL_FACE_TO_FACE_CONVERSATION_DO_NOT_FINISH_LAST_LINE()
ENDIF
bConversationKilled = TRUE
ENDIF
IF CREATE_CONVERSATION(mConversationStruct, ciDriveHomeChat[0].txtBlock, ciDriveHomeChat[0].rootBlock, CONV_PRIORITY_MEDIUM)
CPRINTLN(DEBUG_MISSION, "DRIVING_HOME_CHAT: Triggered drive home comment 0 with subtitles")
SEQUENCE_INDEX siTrReact
OPEN_SEQUENCE_TASK(siTrReact)
TASK_PLAY_ANIM(NULL, sTraceyAnimDict, "react_intro", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, 4900, AF_NOT_INTERRUPTABLE)
TASK_PLAY_ANIM(NULL, sTraceyAnimDict, "react_loop", NORMAL_BLEND_IN, SLOW_BLEND_OUT, 12400, AF_NOT_INTERRUPTABLE)
CLOSE_SEQUENCE_TASK(siTrReact)
CLEAR_PED_TASKS(pedTracey)
TASK_PERFORM_SEQUENCE(pedTracey, siTrReact)
iDriveHomeChatStep+=4
ENDIF
ELSE
// Stalker dead - Tracey didn't see
IF CREATE_CONVERSATION(mConversationStruct, ciDriveHomeChat[1].txtBlock, ciDriveHomeChat[1].rootBlock, CONV_PRIORITY_MEDIUM)
CPRINTLN(DEBUG_MISSION, "DRIVING_HOME_CHAT: Triggered drive home comment 1 with subtitles")
iDriveHomeChatStep+=4
ENDIF
ENDIF
ELSE
IF bTraceySawOutcome
// Stalker ran off - Tracey saw
// Did she already say escape comment?
IF bTraceyEscapeCommentDone
CPRINTLN(DEBUG_MISSION, "DRIVING_HOME_CHAT: Stalker escaped on foot and Tracey saw so no immediate comment")
iDriveHomeChatStep+=4
ELSE
// Play the initial escape query now
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_TNOGOF", CONV_PRIORITY_MEDIUM)
CPRINTLN(DEBUG_MISSION, "DRIVING_HOME_CHAT: Stalker escaped on foot and Tracey saw")
iDriveHomeChatStep+=4
bTraceyEscapeCommentDone = TRUE
ENDIF
ENDIF
ELSE
IF NOT bTraceyEscapeCommentDone // Prevent fringe case where stalker escapes while Tracey is saying the let go comment and stalker not in LOS - B*1437058
// Stalker ran off - Tracey didn't see
IF CREATE_CONVERSATION(mConversationStruct, ciDriveHomeChat[2].txtBlock, ciDriveHomeChat[2].rootBlock, CONV_PRIORITY_MEDIUM) // MET1_TSESCP1
CPRINTLN(DEBUG_MISSION, "DRIVING_HOME_CHAT: Triggered drive home comment 2 with subtitles")
iDriveHomeChatStep+=4
ENDIF
ENDIF
ENDIF
ENDIF
ELIF iDriveHomeChatStep = 4
// Wait for comment to finish
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
CPRINTLN(DEBUG_MISSION, "DRIVING_HOME_CHAT: Initial comment completed")
iDriveHomeChatStep++
ENDIF
ENDIF
// End first comment
// Remainder of comments not allowed while wanted
IF GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) > 0
// Trigger wanted remark?
IF bNeedWantedRemark
AND NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
IF MISSION_OBJECTIVES_CURRENTLY_DISPLAYED()
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_TRCOPS", CONV_PRIORITY_MEDIUM, DO_NOT_DISPLAY_SUBTITLES)
iDriveHomeChatTimer = GET_GAME_TIMER()
bNeedWantedRemark = FALSE
ENDIF
ELSE
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_TRCOPS", CONV_PRIORITY_MEDIUM)
iDriveHomeChatTimer = GET_GAME_TIMER()
bNeedWantedRemark = FALSE
ENDIF
ENDIF
ENDIF
// Remainder of comments lower priority than crazy driving complaint
ELIF bNeedCrazyDrivingComment
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
IF CREATE_CONVERSATION(mConversationStruct, ciDriveHomeChat[8].txtBlock, ciDriveHomeChat[8].rootBlock, CONV_PRIORITY_MEDIUM) // Special comment if player's driving crazily
CPRINTLN(DEBUG_MISSION, "DRIVING_HOME_CHAT: Triggered drive home comment 8 (crazy driving) with subtitles")
bNeedCrazyDrivingComment = FALSE
iDriveHomeChatTimer = GET_GAME_TIMER()
ENDIF
ENDIF
// OK to do rest of stuff
ELSE
// Second comment - Tracey further comment if stalker has been killed
IF iDriveHomeChatStep = 5
// Check that starting this conversation chunk is valid
IF bStalkerDead
// Stalker dead - go to wait for chat
iDriveHomeChatStep++
iDriveHomeChatTimer = GET_GAME_TIMER()
ELSE
// Stalker wasn't killed - go straight to next comment
iDriveHomeChatStep += 3
ENDIF
ELIF iDriveHomeChatStep = 6
// Wait to trigger comment
IF GET_GAME_TIMER() - iDriveHomeChatTimer > ciDriveHomeChat[3].startDelay
AND NOT IS_ENTITY_IN_RANGE_COORDS(PLAYER_PED_ID(), vecReturnLocation, 50.0)
IF CREATE_CONVERSATION(mConversationStruct, ciDriveHomeChat[3].txtBlock, ciDriveHomeChat[3].rootBlock, CONV_PRIORITY_MEDIUM) // Delayed secondary kill comment
CPRINTLN(DEBUG_MISSION, "DRIVING_HOME_CHAT: Triggered drive home comment 3 with subtitles")
IF NOT bTraceySawOutcome
// Tracey hasnt done reaction anim yet
SEQUENCE_INDEX siTrReact
OPEN_SEQUENCE_TASK(siTrReact)
TASK_PLAY_ANIM(NULL, sTraceyAnimDict, "react_intro", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, 4900, AF_NOT_INTERRUPTABLE)
TASK_PLAY_ANIM(NULL, sTraceyAnimDict, "react_loop", NORMAL_BLEND_IN, SLOW_BLEND_OUT, 12400, AF_NOT_INTERRUPTABLE)
CLOSE_SEQUENCE_TASK(siTrReact)
CLEAR_PED_TASKS(pedTracey)
TASK_PERFORM_SEQUENCE(pedTracey, siTrReact)
ENDIF
iDriveHomeChatStep++
ENDIF
ENDIF
ELIF iDriveHomeChatStep = 7
// Wait for comment to finish
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
iDriveHomeChatStep++
ENDIF
ENDIF
// End second comment
// Third comment - Tracey comments if her car is banged up
IF iDriveHomeChatStep = 8
// Check that starting this conversation chunk is valid
IF IS_ENTITY_ALIVE(viTraceyCar)
IF GET_ENTITY_HEALTH(viTraceyCar) < 900
OR GET_VEHICLE_ENGINE_HEALTH(viTraceyCar) < 900
OR GET_VEHICLE_PETROL_TANK_HEALTH(viTraceyCar) < 900
// Car is at least moderately damaged - go to wait for chat
iDriveHomeChatStep++
iDriveHomeChatTimer = GET_GAME_TIMER()
ELSE
// Car isn't really trashed - go straight to next comment
CPRINTLN(DEBUG_MISSION, "DRIVING_HOME_CHAT: Not doing trashed car complaint, car health is ", GET_ENTITY_HEALTH(viTraceyCar))
iDriveHomeChatStep += 3
ENDIF
ENDIF
ELIF iDriveHomeChatStep = 9
// Wait to trigger comment
IF GET_GAME_TIMER() - iDriveHomeChatTimer > ciDriveHomeChat[4].startDelay
AND NOT IS_ENTITY_IN_RANGE_COORDS(PLAYER_PED_ID(), vecReturnLocation, 50.0)
IF CREATE_CONVERSATION(mConversationStruct, ciDriveHomeChat[4].txtBlock, ciDriveHomeChat[4].rootBlock, CONV_PRIORITY_MEDIUM)
CPRINTLN(DEBUG_MISSION, "DRIVING_HOME_CHAT: Triggered drive home comment 4 with subtitles")
iDriveHomeChatStep++
ENDIF
ENDIF
ELIF iDriveHomeChatStep = 10
// Wait for comment to finish
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
iDriveHomeChatStep++
ENDIF
ENDIF
// End third comment
// Fourth comment - Tracey comments on stalker running off (only if he's still alive)
IF iDriveHomeChatStep = 11
// Check that starting this conversation chunk is valid
IF NOT bStalkerDead
// Stalker fled on foot - go to wait for chat
iDriveHomeChatStep++
iDriveHomeChatTimer = GET_GAME_TIMER()
ELSE
// Stalker dead - skip comment
CPRINTLN(DEBUG_MISSION, "DRIVING_HOME_CHAT: Stalker dead skipping to general banter.")
iDriveHomeChatStep += 3
ENDIF
ELIF iDriveHomeChatStep = 12
// Wait to trigger comment
IF GET_GAME_TIMER() - iDriveHomeChatTimer > ciDriveHomeChat[5].startDelay
AND NOT IS_ENTITY_IN_RANGE_COORDS(PLAYER_PED_ID(), vecReturnLocation, 50.0)
IF CREATE_CONVERSATION(mConversationStruct, ciDriveHomeChat[5].txtBlock, ciDriveHomeChat[5].rootBlock, CONV_PRIORITY_MEDIUM)
CPRINTLN(DEBUG_MISSION, "DRIVING_HOME_CHAT: Triggered drive home comment 5 with subtitles")
iDriveHomeChatStep++
ENDIF
ENDIF
ELIF iDriveHomeChatStep = 13
// Wait for comment to finish
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
CPRINTLN(DEBUG_MISSION, "DRIVING_HOME_CHAT: Drive home chatter going to next comment.")
iDriveHomeChatStep++
ENDIF
ENDIF
// End fourth comment
// Fifth comment - Tracey asks if Michael is in danger
IF iDriveHomeChatStep = 14
// Timer
iDriveHomeChatStep++
iDriveHomeChatTimer = GET_GAME_TIMER()
ELIF iDriveHomeChatStep = 15
// Wait to trigger comment
IF GET_GAME_TIMER() - iDriveHomeChatTimer > ciDriveHomeChat[6].startDelay
AND NOT IS_ENTITY_IN_RANGE_COORDS(PLAYER_PED_ID(), vecReturnLocation, 50.0)
IF CREATE_CONVERSATION(mConversationStruct, ciDriveHomeChat[6].txtBlock, ciDriveHomeChat[6].rootBlock, CONV_PRIORITY_MEDIUM) // Tracey asks if anyone's trying to kill Michael
CPRINTLN(DEBUG_MISSION, "DRIVING_HOME_CHAT: Triggered drive home comment 6 with subtitles")
iDriveHomeChatStep++
ENDIF
ENDIF
ELIF iDriveHomeChatStep = 16
// Wait for comment to finish
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
CPRINTLN(DEBUG_MISSION, "DRIVING_HOME_CHAT: Drive home chatter going to next comment.")
iDriveHomeChatStep++
ENDIF
ENDIF
// End fifth comment
// Sixth comment - Tracey asks if Michael is in danger
IF iDriveHomeChatStep = 17
// Timer
iDriveHomeChatStep++
iDriveHomeChatTimer = GET_GAME_TIMER()
ELIF iDriveHomeChatStep = 18
// Wait to trigger comment
IF GET_GAME_TIMER() - iDriveHomeChatTimer > ciDriveHomeChat[7].startDelay
AND NOT IS_ENTITY_IN_RANGE_COORDS(PLAYER_PED_ID(), vecReturnLocation, 50.0)
IF CREATE_CONVERSATION(mConversationStruct, ciDriveHomeChat[7].txtBlock, ciDriveHomeChat[7].rootBlock, CONV_PRIORITY_MEDIUM) // Michael asks if Tracey has heard from Lazlow
CPRINTLN(DEBUG_MISSION, "DRIVING_HOME_CHAT: Triggered drive home comment 7 with subtitles")
iDriveHomeChatStep++
ENDIF
ENDIF
ELIF iDriveHomeChatStep = 19
// Wait for comment to finish
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
CPRINTLN(DEBUG_MISSION, "DRIVING_HOME_CHAT: Drive home chatter is done.")
iDriveHomeChatStep++
ENDIF
ENDIF
// End sixth comment
ENDIF
ENDPROC
/// PURPOSE:
/// Checks whether player has reached the destination that we're heading for
/// RETURNS:
/// TRUE if arrived
FUNC BOOL REACHED_DESTINATION_CHECK()
IF bGoingToHotel
IF IS_ENTITY_AT_COORD(PLAYER_PED_ID(), vecReturnLocation, << 3.5, 3.5, LOCATE_SIZE_HEIGHT >>, TRUE)
RETURN TRUE
ENDIF
ELSE
// House - needs wall proximity bodges
IF IS_ENTITY_AT_COORD(PLAYER_PED_ID(), vecReturnLocation, << 4.5, 4.5, LOCATE_SIZE_HEIGHT >>, TRUE)
//AND IS_ENTITY_IN_RANGE_COORDS(PLAYER_PED_ID(), vecReturnLocation, 3.8) // Added for B*1267303 - to prevent parking right by wall - revised to angled areas
AND (IS_ENTITY_IN_ANGLED_AREA(PLAYER_PED_ID(), <<-828.706909,176.641754,69.662125>>, <<-824.074707,184.566589,72.918724>>, 5.75) OR IS_ENTITY_IN_ANGLED_AREA(PLAYER_PED_ID(), <<-827.930786,182.045395,70.089088>>, <<-820.238708,180.178085,72.715431>>, 6.50))
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Handle the nearly there comment appropriately for the current destination
/// RETURNS:
/// TRUE if we are in range for triggering the comment
FUNC BOOL DESTINATION_COMMENT_TRIGGER()
IF bGoingToHotel
IF IS_ENTITY_IN_RANGE_COORDS(PLAYER_PED_ID(), vecHotelLocation, 30.0)
RETURN TRUE
ENDIF
ELSE
// Check proximity to house gate not destination on drive
IF IS_ENTITY_IN_RANGE_COORDS(PLAYER_PED_ID(), <<-842.5665, 159.2974, 65.9426>>, 11.0) // B*1306840
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Spawns the stalker dudes car and him in it
/// PARAMS:
/// spawnCoords - spawn location
/// fSpawnHead - spawn facing
/// RETURNS:
/// TRUE if the stalker has been successfully spawned
FUNC BOOL SPAWN_STALKER(VECTOR spawnCoords, FLOAT fSpawnHead = 0.0, BOOL bRequestModelsOnly = FALSE)
IF bRequestModelsOnly
AND NOT bStalkerModelRequested
CPRINTLN(DEBUG_MISSION, "Requesting stalker car/ped models")
REQUEST_MODEL(mnStalkerModel)
REQUEST_MODEL(mvStalkerCar.model)
bStalkerModelRequested = TRUE
ENDIF
IF IS_ENTITY_ALIVE(mvStalkerCar.vehicle)
AND IS_ENTITY_ALIVE(pedStalker)
// Stalker has already been created - nothing to do
RETURN TRUE
ELSE
IF bStalkerModelRequested
IF HAS_MODEL_LOADED(mnStalkerModel)
AND HAS_MODEL_LOADED(mvStalkerCar.model)
// Clear space
CLEAR_AREA_OF_VEHICLES(mvStalkerCar.location, 5)
// Create vehicle
mvStalkerCar.vehicle = CREATE_VEHICLE(mvStalkerCar.model, spawnCoords, fSpawnHead)
SET_MODEL_AS_NO_LONGER_NEEDED(mvStalkerCar.model)
// Configure vehicle
SET_VEHICLE_COLOURS(mvStalkerCar.vehicle, 145, 111)
SET_VEHICLE_ON_GROUND_PROPERLY(mvStalkerCar.vehicle)
LOWER_CONVERTIBLE_ROOF(mvStalkerCar.vehicle, TRUE)
SET_CONVERTIBLE_ROOF_LATCH_STATE(mvStalkerCar.vehicle, TRUE) // Lock it open
SET_VEHICLE_ENGINE_ON(mvStalkerCar.vehicle, TRUE, TRUE)
SET_ENTITY_LOAD_COLLISION_FLAG(mvStalkerCar.vehicle, TRUE)
ADD_VEHICLE_UPSIDEDOWN_CHECK(mvStalkerCar.vehicle)
SET_VEHICLE_AS_RESTRICTED(mvStalkerCar.vehicle, RESTRICTION_STALKER)
ADD_ENTITY_TO_AUDIO_MIX_GROUP(mvStalkerCar.vehicle, "M_E_TRACEY_STALKERS_CAR_GROUP")
bStalkerStuckCheckActive = TRUE
// Create stalker
pedStalker = CREATE_PED_INSIDE_VEHICLE(mvStalkerCar.vehicle, PEDTYPE_MISSION, mnStalkerModel)
SET_PED_COMPONENT_VARIATION(pedStalker, PED_COMP_SPECIAL2, 0, 0) // "task" comp - No knob please
SET_PED_COMPONENT_VARIATION(pedStalker, PED_COMP_SPECIAL, 0, 0) // Belt
SET_PED_COMPONENT_VARIATION(pedStalker, PED_COMP_HEAD, 0, 0)
SET_PED_COMPONENT_VARIATION(pedStalker, PED_COMP_HAIR, 1, 0)
SET_PED_COMPONENT_VARIATION(pedStalker, PED_COMP_TORSO, 0, 0)
SET_PED_COMPONENT_VARIATION(pedStalker, PED_COMP_LEG, 1, 0)
SET_MODEL_AS_NO_LONGER_NEEDED(mnStalkerModel)
// Configure stalker
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(pedStalker, TRUE)
SET_PED_KEEP_TASK(pedStalker, TRUE)
ADD_PED_FOR_DIALOGUE(mConversationStruct, 4, pedStalker, "Stalker")
SET_PED_CONFIG_FLAG(pedStalker, PCF_DisableGoToWritheWhenInjured, TRUE)
bStalkerModelRequested = FALSE
iDisableReplayCameraTimer = GET_GAME_TIMER() + 1000 //Fix for bug 2227778
RETURN TRUE
#IF IS_DEBUG_BUILD
ELIF bDebugStalkerTTY
CPRINTLN(DEBUG_MISSION, "Waiting for stalker models to load...") #ENDIF
ENDIF
ELSE
CPRINTLN(DEBUG_MISSION, "Requesting stalker car/ped models")
REQUEST_MODEL(mnStalkerModel)
REQUEST_MODEL(mvStalkerCar.model)
bStalkerModelRequested = TRUE
ENDIF
ENDIF
// Still loading something if we've not returned TRUE by now
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Suppresses the models for a list of stupidly big vehicles that can really snarl up traffic
/// PARAMS:
/// bSuppress - TRUE to suppress, FALSE to remove suppression
PROC TOGGLE_VEHICLE_SUPPRESSION(BOOL bSuppress)
// Services/buses
SET_VEHICLE_MODEL_IS_SUPPRESSED(BUS, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(COACH, bSuppress)
// Industrial
SET_VEHICLE_MODEL_IS_SUPPRESSED(MIXER, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(MIXER2, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(RUBBLE, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(TIPTRUCK2, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(TIPTRUCK, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(SCRAP, bSuppress)
// Trucks
SET_VEHICLE_MODEL_IS_SUPPRESSED(BIFF, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(PACKER, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(PHANTOM, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(BENSON, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(TRASH, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(POUNDER, bSuppress)
ENDPROC
/// PURPOSE:
/// Creates a coord blip for Tracey's start point - faked to look like a ped blip
/// PARAMS:
/// vCoords - Coords requiring blip.
/// bSetRoute - creates a satNav route for this blip
/// RETURNS:
/// Blip Index.
FUNC BLIP_INDEX CREATE_FAKE_TRACEY_PED_BLIP(VECTOR vCoords, BOOL bSetRoute = FALSE)
BLIP_INDEX bRetBlip = ADD_BLIP_FOR_COORD(vCoords)
SET_BLIP_PRIORITY(bRetBlip,BLIPPRIORITY_HIGH)
SET_BLIP_SCALE(bRetBlip, BLIP_SIZE_PED)
SET_BLIP_COLOUR(bRetBlip, BLIP_COLOUR_BLUE)
SET_BLIP_ROUTE(bRetBlip, bSetRoute)
SET_BLIP_NAME_FROM_TEXT_FILE(bRetBlip, "MET1LBTRAC")
RETURN bRetBlip
ENDFUNC
/// PURPOSE:
/// Local replacement for CREATE_NPC_PED functions so I can specify a specific outfit
/// RETURNS:
/// TRUE when spawned
FUNC BOOL CREATE_TRACEY(PED_INDEX &TraceyPed, VECTOR vCoords, FLOAT fHeading = 0.0, BOOL bCleanupModel = TRUE)
// Load the required model
MODEL_NAMES model = GET_NPC_PED_MODEL(CHAR_TRACEY)
REQUEST_MODEL(model)
IF HAS_MODEL_LOADED(model)
// Delete ped in case of weirdness- should be OK though
SAFE_DELETE_PED(TraceyPed)
TraceyPed = CREATE_PED(PEDTYPE_MISSION, model, vCoords, fHeading, FALSE, FALSE)
// Set up outfit
SET_PED_COMPONENT_VARIATION(TraceyPed, PED_COMP_TORSO, 1, 1)
SET_PED_COMPONENT_VARIATION(TraceyPed, PED_COMP_LEG, 5, 0)
SET_PED_COMPONENT_VARIATION(TraceyPed, PED_COMP_FEET, 2, 0) // No feet - trainers are in leg comp
SET_PED_COMPONENT_VARIATION(TraceyPed, PED_COMP_SPECIAL, 1, 0) // No accessories
// StoreAsGlobalFriend(TraceyPed, CHAR_TRACEY)
IF bCleanupModel
SET_MODEL_AS_NO_LONGER_NEEDED(model)
ENDIF
iDisableReplayCameraTimer = GET_GAME_TIMER() + 1000 //Fix for bug 2227778
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
//*************************************************************************************************************************************************
//
// Skip procs
//
//*************************************************************************************************************************************************
// Check for and delete stalker ped, vehicle and blip
PROC SKIP_REMOVE_STALKER()
// Remove stalker
SAFE_DELETE_PED(pedStalker)
IF DOES_ENTITY_EXIST(mvStalkerCar.vehicle)
IF bStalkerStuckCheckActive
REMOVE_VEHICLE_UPSIDEDOWN_CHECK(mvStalkerCar.vehicle)
bStalkerStuckCheckActive = FALSE
ENDIF
REMOVE_ENTITY_FROM_AUDIO_MIX_GROUP(mvStalkerCar.vehicle)
SAFE_DELETE_VEHICLE(mvStalkerCar.vehicle)
ENDIF
ENDPROC
/// PURPOSE:
/// Check for and delete Tracey ped, vehicle and blip
PROC SKIP_REMOVE_TRACEY()
bTraceyIsCurrentlySpawned = FALSE
IF IS_ENTITY_ALIVE(pedTracey)
IF IS_PED_IN_GROUP(pedTracey)
REMOVE_PED_FROM_GROUP(pedTracey)
ENDIF
ENDIF
SAFE_DELETE_PED(pedTracey)
IF bStuckCheckActive
REMOVE_VEHICLE_UPSIDEDOWN_CHECK(viTraceyCar)
bStuckCheckActive = FALSE
ENDIF
SAFE_DELETE_VEHICLE(viTraceyCar)
ENDPROC
/// PURPOSE:
/// (Re)spawn Tracey in her car for skips and checkpoints
/// PARAMS:
/// vecRSLoc - coordinates to respawn at
/// fRSHead - direction car faces
/// bAddAsPassenger - Tracey is spawned in the passenger seat
PROC SKIP_RESPAWN_TRACEY(VECTOR vecRSLoc, FLOAT fRSHead, BOOL bAddAsPassenger = FALSE)
// Remove Tracey/car
SKIP_REMOVE_TRACEY()
WHILE NOT CREATE_NPC_VEHICLE(viTraceyCar, CHAR_TRACEY, vecRSLoc, fRSHead, TRUE)
WAIT(0)
ENDWHILE
IF bAddAsPassenger
WHILE NOT CREATE_TRACEY(pedTracey, << vecRSLoc.x, vecRSLoc.y, vecRSLoc.z+5 >>)
WAIT(0)
ENDWHILE
IF IS_ENTITY_ALIVE(pedTracey) AND IS_ENTITY_ALIVE(viTraceyCar)
SET_PED_INTO_VEHICLE(pedTracey, viTraceyCar, VS_FRONT_RIGHT)
ENDIF
ELSE
WHILE NOT CREATE_TRACEY(pedTracey, << vecRSLoc.x, vecRSLoc.y, vecRSLoc.z+5 >>)
WAIT(0)
ENDWHILE
IF IS_ENTITY_ALIVE(pedTracey) AND IS_ENTITY_ALIVE(viTraceyCar)
SET_PED_INTO_VEHICLE(pedTracey, viTraceyCar, VS_FRONT_RIGHT)
ENDIF
ENDIF
IF IS_ENTITY_ALIVE(pedTracey)
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(pedTracey, TRUE)
ADD_PED_FOR_DIALOGUE(mConversationStruct, 3, pedTracey, "TRACEY")
// Tracey can't fly through windscreen or be jacked
SET_PED_CONFIG_FLAG(pedTracey, PCF_WillFlyThroughWindscreen, FALSE)
SET_PED_CONFIG_FLAG(pedTracey, PCF_DontAllowToBeDraggedOutOfVehicle, TRUE)
// Should load collision
SET_ENTITY_LOAD_COLLISION_FLAG(viTraceyCar, TRUE)
// Should be in friendly rel group
DO_FRIENDLY_GROUP_SETUP()
ENDIF
IF IS_ENTITY_ALIVE(viTraceyCar)
LOWER_CONVERTIBLE_ROOF(viTraceyCar, TRUE)
SET_VEHICLE_STRONG(viTraceyCar, TRUE)
SET_VEHICLE_ENGINE_ON(viTraceyCar, TRUE, TRUE)
ADD_VEHICLE_UPSIDEDOWN_CHECK(viTraceyCar)
SET_VEHICLE_HAS_STRONG_AXLES(viTraceyCar, TRUE)
SET_VEHICLE_AS_RESTRICTED(viTraceyCar, RESTRICTION_TRACEY)
bStuckCheckActive = TRUE
ENDIF
bTraceyIsCurrentlySpawned = TRUE
ENDPROC
#IF IS_DEBUG_BUILD
// Respawn stalker
PROC SKIP_RESPAWN_STALKER(VECTOR vecRSLoc, FLOAT fRSHead)
SKIP_REMOVE_STALKER()
WHILE NOT SPAWN_STALKER(vecRSLoc, fRSHead)
WAIT(0)
ENDWHILE
ENDPROC
// Player wanted level cleared
PROC SKIP_CLEAR_WANTED()
// Clear wanted level
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
// Stamp on any active conversation when skipping
PROC SKIP_KILL_CONVERSATION()
IF IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
KILL_FACE_TO_FACE_CONVERSATION_DO_NOT_FINISH_LAST_LINE()
HANG_UP_AND_PUT_AWAY_PHONE()
ENDIF
ENDPROC
/// PURPOSE:
/// DEBUG SKIP: Restart mission skip.
/// Warps player to arbitrary point as if they'd just finished phone call.
PROC SKIP_TO_MS_INIT(BOOL bGiveCar = FALSE)
CPRINTLN(DEBUG_MISSION, "DEBUG SKIP ::: Restarting mission")
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
IF bGiveCar
// Give the player a car
REQUEST_MODEL(debugPlayerCarModel)
SET_ENTITY_COORDS(PLAYER_PED_ID(), << 90.6710, -215.2205, 56.4915 >>)
WHILE NOT HAS_MODEL_LOADED(debugPlayerCarModel)
WAIT(0)
ENDWHILE
CLEAR_AREA_OF_VEHICLES(<< 90.6710, -215.2205, 53.4915 >>, 10, TRUE, FALSE, TRUE, TRUE)
VEHICLE_INDEX viTmp
viTmp = CREATE_VEHICLE(debugPlayerCarModel, << 90.6710, -215.2205, 53.4915 >>, 338.3)
SET_MODEL_AS_NO_LONGER_NEEDED(debugPlayerCarModel)
WHILE NOT DOES_ENTITY_EXIST(viTmp)
WAIT(0)
ENDWHILE
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
SET_PED_INTO_VEHICLE(PLAYER_PED_ID(), viTmp)
ENDIF
SAFE_RELEASE_VEHICLE(viTmp)
ELSE
// Warp player
IF NOT IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
OR (IS_ENTITY_ALIVE(viTraceyCar) AND IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar))
CLEAR_AREA_OF_VEHICLES(<< 90.6710, -215.2205, 53.4915 >>, 10, TRUE, FALSE, TRUE, TRUE)
SET_ENTITY_COORDS(PLAYER_PED_ID(), << 90.6710, -215.2205, 53.4915 >>)
SET_ENTITY_HEADING(PLAYER_PED_ID(), 338.3)
ELSE
// Maintain player's vehicle (as it's not Tracey's car)
CLEAR_AREA_OF_VEHICLES(<< 90.6710, -215.2205, 53.4915 >>, 10, TRUE, FALSE, TRUE, TRUE)
REPOSITION_PLAYERS_VEHICLE(<< 90.6710, -215.2205, 53.4915 >>, 338.3)
ENDIF
ENDIF
ENDIF
SKIP_REMOVE_TRACEY()
SKIP_REMOVE_STALKER()
SAFE_REMOVE_BLIP(biBlip)
bTraceyIsCurrentlySpawned = FALSE
// Vehicle density may be nobbled depending where we came from
// Commented out as SET_VEHICLE_DENSITY_MULTIPLIER has been replaced with SET_VEHICLE_DENSITY_MULTIPLIER_THIS_FRAME
//SET_VEHICLE_DENSITY_MULTIPLIER(1.0)
// Reset timeout warning call
wfcProgress = WFC_SETUP
INIT_DRIVE_TIMERS()
bWFCOnHold = FALSE
MISSION_OBJECTIVE_RESET(MO_MET1INITDRV)// Drive towards Vinewood.
MISSION_OBJECTIVE_RESET(MO_MET1TCAR) // Get into Tracey's car.
MISSION_OBJECTIVE_RESET(MO_MET1WANT) // Lose the cops.
MISSION_OBJECTIVE_RESET(MO_MET1CHASE) // Chase off the stalker.
MISSION_OBJECTIVE_RESET(MO_MET1HOME) // Take Tracey home.
MISSION_OBJECTIVE_RESET(MO_MET1HOTEL) // Take Tracey to the Hotel.
MISSION_OBJECTIVE_RESET(MO_MET1GETBKT) // Get back in Tracey's car.
MISSION_OBJECTIVE_RESET(MO_MET1DLT) // Return to Tracey's car.
msTrack = MST_DRIVE_TO_TRACEY
sProgress = SP_SETUP
ENDPROC
#ENDIF
/// PURPOSE:
/// DEBUG SKIP: Warps to point that player meets Tracey at the Vinewood Plaza.
/// THIS IS ALSO A CHECKPOINT SKIP
/// If the player is in a vehicle, they will be warped in it; If not then they will be warped on foot.
PROC SKIP_TO_MEET_TRACEY(BOOL bDoCheckpoint = FALSE)
CPRINTLN(DEBUG_MISSION, "DEBUG SKIP ::: Skipping to STAGE_DRIVE_TO_STALKER")
SAFE_REMOVE_BLIP(biBlip)
bReplayInVehicle = FALSE // Set now so it never has dodgy value for setting up audio scene later
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
IF bDoCheckPoint
// Get the player's car if available
IF IS_REPLAY_CHECKPOINT_VEHICLE_AVAILABLE()
// In vehicle
vecReplayPlayerLocation = <<99.6208, -21.0027, 67.0114>>
fReplayPlayerHeading = 251.1
bReplayInVehicle = TRUE
CLEAR_AREA_OF_VEHICLES(<<99.6208, -21.0027, 67.0114>>, 7.0) // Previous bug about ambient vehicles driving into back of player's car
// Get the car
REQUEST_REPLAY_CHECKPOINT_VEHICLE_MODEL()
WHILE NOT HAS_REPLAY_CHECKPOINT_VEHICLE_LOADED()
WAIT(0)
ENDWHILE
vehReplayCar = CREATE_REPLAY_CHECKPOINT_VEHICLE(vecReplayPlayerLocation, fReplayPlayerHeading)
ELSE
// On foot
CLEAR_AREA(<<181.2331, -33.9939, 67.2522>>, 3.0, TRUE) // Previous bug about ambient ped collisions
vecReplayPlayerLocation = <<181.2331, -33.9939, 67.2522>>
fReplayPlayerHeading = 255.3
ENDIF
START_REPLAY_SETUP(vecReplayPlayerLocation, fReplayPlayerHeading)
ELSE
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
// Respot player on foot if they are in Tracey's car or a trashed vehicle
IF (IS_ENTITY_ALIVE(viTraceyCar) AND IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar))
OR (IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID()) AND NOT IS_VEHICLE_DRIVEABLE(GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID())))
// Warp player on foot near Tracey
CLEAR_AREA(<<181.2331, -33.9939, 67.2522>>, 3.0, TRUE)
SET_ENTITY_COORDS(PLAYER_PED_ID(), <<181.2331, -33.9939, 67.2522>>)
SET_ENTITY_HEADING(PLAYER_PED_ID(), 255.3)
vecReplayPlayerLocation = <<181.2331, -33.9939, 67.2522>>
ELSE
// Move vehicle
CLEAR_AREA_OF_VEHICLES(<<99.6208, -21.0027, 67.0114>>, 7.0)
vehReplayCar = GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID())
SET_ENTITY_COORDS(vehReplayCar, <<99.6208, -21.0027, 67.0114>>)
SET_ENTITY_HEADING(vehReplayCar, 251.1)
SET_VEHICLE_ON_GROUND_PROPERLY(vehReplayCar)
vecReplayPlayerLocation = <<99.6208, -21.0027, 67.0114>>
bReplayInVehicle = TRUE
ENDIF
ELSE
// Player is on foot - warp nearer Tracey
CLEAR_AREA(<<181.2331, -33.9939, 67.2522>>, 3.0, TRUE)
SET_ENTITY_COORDS(PLAYER_PED_ID(), <<181.2331, -33.9939, 67.2522>>)
SET_ENTITY_HEADING(PLAYER_PED_ID(), 255.3)
vecReplayPlayerLocation = <<181.2331, -33.9939, 67.2522>>
ENDIF
ENDIF
ENDIF
SKIP_REMOVE_STALKER()
SKIP_REMOVE_TRACEY()
// Reset timeout warning call
wfcProgress = WFC_SETUP
INIT_DRIVE_TIMERS()
bWFCOnHold = TRUE
msTrack = MST_DRIVE_TO_TRACEY
sProgress = SP_SETUP
IF (IS_ENTITY_ALIVE(PLAYER_PED_ID()) AND IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID()))
OR bReplayInVehicle
UPDATE_AUDIO_SCENE(AUDIOSCENE_GO_TO_TRACEY)
ELSE
UPDATE_AUDIO_SCENE(AUDIOSCENE_NONE)
ENDIF
MISSION_OBJECTIVE_EXPIRE(MO_MET1INITDRV)// Drive towards Vinewood.
MISSION_OBJECTIVE_RESET(MO_MET1TCAR) // Get into Tracey's car.
MISSION_OBJECTIVE_RESET(MO_MET1WANT) // Lose the cops.
MISSION_OBJECTIVE_RESET(MO_MET1CHASE) // Chase off the stalker.
MISSION_OBJECTIVE_RESET(MO_MET1HOME) // Go home.
MISSION_OBJECTIVE_RESET(MO_MET1HOTEL) // Go home.
MISSION_OBJECTIVE_RESET(MO_MET1GETBKT) // Get back in Tracey's car.
MISSION_OBJECTIVE_RESET(MO_MET1DLT) // Return to Tracey's car.
IF bDoCheckpoint
WHILE NOT HAS_ADDITIONAL_TEXT_LOADED(MISSION_TEXT_SLOT)
WAIT(0)
ENDWHILE
IF bReplayInVehicle
END_REPLAY_SETUP(vehReplayCar)
ELSE
END_REPLAY_SETUP()
ENDIF
ELSE
WAIT_FOR_WORLD_TO_LOAD(vecReplayPlayerLocation)
ENDIF
// Rolling start if needed
IF bReplayInVehicle
IF IS_ENTITY_ALIVE(vehReplayCar)
SET_VEHICLE_ENGINE_ON(vehReplayCar, TRUE, TRUE)
SET_VEHICLE_FORWARD_SPEED(vehReplayCar, 10.0)
ENDIF
WAIT(0)
ENDIF
ENDPROC
/// PURPOSE:
/// DEBUG SKIP: Warp to stalker appearance point and create him in his "primary" location
/// THIS IS ALSO A CHECKPOINT SKIP
PROC SKIP_TO_STALKER_APPEARS(BOOL bCheckpoint = FALSE)
CPRINTLN(DEBUG_MISSION, "DEBUG SKIP ::: Skipping to STALKER_APPEARS")
SAFE_REMOVE_BLIP(biBlip)
SKIP_REMOVE_STALKER()
IF bCheckpoint
START_REPLAY_SETUP(dpLocates[NUM_DESTINATIONS-1].location, 71.4)
SKIP_RESPAWN_TRACEY(dpLocates[NUM_DESTINATIONS-1].location, 71.4, TRUE)
ELSE
// Set player in right area, get him into Tracey's car
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
SET_ENTITY_COORDS(PLAYER_PED_ID(), << dpLocates[NUM_DESTINATIONS-1].location.x, dpLocates[NUM_DESTINATIONS-1].location.y, dpLocates[NUM_DESTINATIONS-1].location.z+8 >>)
// Respawn Tracey
SKIP_RESPAWN_TRACEY(dpLocates[NUM_DESTINATIONS-1].location, 71.4, TRUE)
SAFE_SET_PED_INTO_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
ENDIF
// Clear any cars from behind player - previous bug about ambient cars driving into them
IF IS_ENTITY_ALIVE(viTraceyCar)
CLEAR_AREA_OF_VEHICLES(GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(viTraceyCar, << 0.0, -8.0, 0.0>>), 6)
ENDIF
ENDIF
// Set the points
mvStalkerCar.location = vecStalkerSpawnPoint[1]
mvStalkerCar.heading = fStalkerSpawnHead[1]
CLEAR_AREA_OF_VEHICLES(mvStalkerCar.location, 5)
MISSION_OBJECTIVE_EXPIRE(MO_MET1INITDRV)// Drive towards Vinewood.
MISSION_OBJECTIVE_EXPIRE(MO_MET1TCAR) // Get into Tracey's car.
MISSION_OBJECTIVE_RESET(MO_MET1WANT) // Lose the cops.
MISSION_OBJECTIVE_RESET(MO_MET1CHASE) // Chase off the stalker.
MISSION_OBJECTIVE_RESET(MO_MET1HOME) // Go home.
MISSION_OBJECTIVE_RESET(MO_MET1HOTEL) // Go home.
MISSION_OBJECTIVE_RESET(MO_MET1GETBKT) // Get back in Tracey's car.
MISSION_OBJECTIVE_RESET(MO_MET1DLT) // Return to Tracey's car.
UPDATE_AUDIO_SCENE(AUDIOSCENE_DRIVE_TO_STALKER)
msTrack = MST_TRIGGER_STALKER
sProgress = SP_SETUP
IF bCheckpoint
WHILE NOT HAS_ADDITIONAL_TEXT_LOADED(MISSION_TEXT_SLOT)
WAIT(0)
ENDWHILE
END_REPLAY_SETUP(viTraceyCar, VS_DRIVER, FALSE)
ELSE
WAIT_FOR_WORLD_TO_LOAD(dpLocates[NUM_DESTINATIONS-1].location)
SET_REPLAY_MID_MISSION_STAGE_WITH_NAME(CP_STALKER_APPEARS, "Stalker appears (CP2)")
ENDIF
ENDPROC
#IF IS_DEBUG_BUILD
/// PURPOSE:
/// DEBUG SKIP: Stalker wreck skip
/// If the player is in the stalker chase, all this will do is pop the stalker's tyres
/// Otherwise, it will warp to a known 'good' location and create stalker there in knackered car
PROC SKIP_TO_STALKER_WRECK()
CPRINTLN(DEBUG_MISSION, "DEBUG SKIP ::: Skipping to chase stage, wrecking stalker's car")
SAFE_REMOVE_BLIP(biBlip)
// If the stage isn't CHASE, we have more to do
IF msTrack != MST_CHASE
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
// Warp player first
SET_ENTITY_COORDS(PLAYER_PED_ID(), << -382.0171, 2.5451, 45.9866 >>)
// Respawn Tracey
SKIP_RESPAWN_TRACEY(<< -382.0171, 2.5451, 45.9866 >>, 63.13, TRUE)
SAFE_SET_PED_INTO_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
ENDIF
// Respawn stalker
SKIP_RESPAWN_STALKER(<< -409.6612, 14.4959, 45.4712 >>, 59.6)
bStalkerDead = FALSE
ENDIF
// Pop the stalker's tyres
IF IS_ENTITY_ALIVE(mvStalkerCar.vehicle)
SET_VEHICLE_ENGINE_HEALTH(mvStalkerCar.vehicle, 20)
SET_VEHICLE_TYRE_BURST(mvStalkerCar.vehicle, SC_WHEEL_CAR_FRONT_LEFT)
SET_VEHICLE_TYRE_BURST(mvStalkerCar.vehicle, SC_WHEEL_CAR_FRONT_RIGHT)
ENDIF
MISSION_OBJECTIVE_EXPIRE(MO_MET1INITDRV)// Drive towards Vinewood.
MISSION_OBJECTIVE_EXPIRE(MO_MET1TCAR) // Get into Tracey's car.
MISSION_OBJECTIVE_RESET(MO_MET1WANT) // Lose the cops.
MISSION_OBJECTIVE_EXPIRE(MO_MET1CHASE) // Chase off the stalker.
MISSION_OBJECTIVE_RESET(MO_MET1HOME) // Go home.
MISSION_OBJECTIVE_RESET(MO_MET1HOTEL) // Go home.
MISSION_OBJECTIVE_RESET(MO_MET1GETBKT) // Get back in Tracey's car.
MISSION_OBJECTIVE_RESET(MO_MET1DLT) // Return to Tracey's car.
SET_REPLAY_MID_MISSION_STAGE_WITH_NAME(CP_MET_TRACEY, "Met Tracey (CP2)")
IF msTrack != MST_CHASE
WAIT_FOR_WORLD_TO_LOAD(<< -382.0171, 2.5451, 45.9866 >>)
UPDATE_AUDIO_SCENE(AUDIOSCENE_CHASE)
ENDIF
msTrack = MST_CHASE
sProgress = SP_RUNNING
ENDPROC
/// PURPOSE:
/// Debug skip that places the stalker car wedged against a wall and the player car behind him so he can't move
PROC SKIP_TO_STALKER_STUCK()
CPRINTLN(DEBUG_MISSION, "DEBUG SKIP ::: Skipping to chase stage, placing stalker in a stuck position")
SAFE_REMOVE_BLIP(biBlip)
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
// Warp player
SET_ENTITY_COORDS(PLAYER_PED_ID(), << -185.1866, -94.4907, 51.4701 >>)
// Respawn Tracey
SKIP_RESPAWN_TRACEY(<<-190.1716, -92.7500, 51.1335>>, 70.8, TRUE)
SAFE_SET_PED_INTO_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
ENDIF
// Respawn stalker
SKIP_RESPAWN_STALKER(<< -194.8900, -91.0374, 50.8075 >>, 250.18)
bStalkerDead = FALSE
MISSION_OBJECTIVE_EXPIRE(MO_MET1INITDRV)// Drive towards Vinewood.
MISSION_OBJECTIVE_EXPIRE(MO_MET1TCAR) // Get into Tracey's car.
MISSION_OBJECTIVE_RESET(MO_MET1WANT) // Lose the cops.
MISSION_OBJECTIVE_EXPIRE(MO_MET1CHASE) // Chase off the stalker.
MISSION_OBJECTIVE_RESET(MO_MET1HOME) // Go home.
MISSION_OBJECTIVE_RESET(MO_MET1HOTEL) // Go home.
MISSION_OBJECTIVE_RESET(MO_MET1GETBKT) // Get back in Tracey's car.
MISSION_OBJECTIVE_RESET(MO_MET1DLT) // Return to Tracey's car.
SET_REPLAY_MID_MISSION_STAGE_WITH_NAME(CP_MET_TRACEY, "Met Tracey (CP2)")
IF IS_ENTITY_ALIVE(pedTracey)
// Prevents skip-specific assert - workaround for a debug-only issue
ENDIF
UPDATE_AUDIO_SCENE(AUDIOSCENE_CHASE)
msTrack = MST_CHASE
sProgress = SP_RUNNING
ENDPROC
#ENDIF
/// PURPOSE:
/// Pick a sensible spawnpoint near to where the checkpoint coords got set
/// PARAMS:
/// vCoords - Current spawn vector, will be reset to one of the presets
/// fHeading - Current spawn heading, will be reset to one of the presets
PROC GET_NEAREST_PRESET_SPAWN_TO_CP(VECTOR vTestCoords, VECTOR &vCoords, FLOAT &fHeading)
// Set up possible spawn coords/heading
VECTOR vSpawnCoords[4]
FLOAT fSpawnHead[4]
vSpawnCoords[0] = << -170.3240, -52.5700, 51.6830 >>
fSpawnHead[0] = 159.9
vSpawnCoords[1] = << 1196.1401, -1069.1301, 40.3586 >>
fSpawnHead[1] = 107.2
vSpawnCoords[2] = << -1513.5652, -676.1716, 27.4587 >>
fSpawnHead[2] = 53.2
vSpawnCoords[3] = << 55.9539, 342.8233, 111.6553 >>
fSpawnHead[3] = 242.4
// Index for best match found so far
INT iBestMatch = 0
iCount = 1
WHILE iCount < 4
// Compare distances for current 'best' and next in list (actually comparing the squares but amounts to the same thing)
IF VDIST2(vSpawnCoords[iCount], vTestCoords) < VDIST2(vSpawnCoords[iBestMatch], vTestCoords)
iBestMatch = iCount
ENDIF
iCount++
ENDWHILE
// Set the passed in variables to be the best match found
vCoords = vSpawnCoords[iBestMatch]
fHeading = fSpawnHead[iBestMatch]
ENDPROC
/// PURPOSE:
/// DEBUG SKIP: Set up as if player has just chased off stalker
/// PARAMS:
/// bCheckpoint - If true we're doing a checkpoint, otherwise just skipping
/// bStalkerAlive - CHECKPOINT ONLY - whether the stalker should be considered alive or dead
PROC SKIP_TO_DRIVE_HOME(BOOL bCheckpoint = FALSE, BOOL bStalkerAlive = TRUE)
CPRINTLN(DEBUG_MISSION, "DEBUG SKIP ::: Skipping to STAGE_DRIVE_HOME")
SAFE_REMOVE_BLIP(biBlip)
SKIP_REMOVE_STALKER()
VECTOR vecTmpCarCoord[2]
FLOAT fTmpCarHead
IF NOT bStalkerAlive
REQUEST_ANIM_DICT(sTraceyAnimDict)
ENDIF
IF bCheckpoint
// CHECKPOINT
vecTmpCarCoord[0] = GET_REPLAY_CHECKPOINT_PLAYER_POSITION()
// See if we can get a safe road node near that -
IF GET_CLOSEST_VEHICLE_NODE_WITH_HEADING(vecTmpCarCoord[0], vecTmpCarCoord[1], fTmpCarHead)
CPRINTLN(DEBUG_MISSION, "SKIP_TO_DRIVE_HOME: Got a spawn location from GET_CLOSEST_VEHICLE_NODE_WITH_HEADING")
ELSE
CPRINTLN(DEBUG_MISSION, "SKIP_TO_DRIVE_HOME: Couldn't get a spawn location from GET_CLOSEST_VEHICLE_NODE_WITH_HEADING falling back to old safe points method")
GET_NEAREST_PRESET_SPAWN_TO_CP(vecTmpCarCoord[0], vecTmpCarCoord[1], fTmpCarHead)
ENDIF
vecReplayPlayerLocation = vecTmpCarCoord[1]
fReplayPlayerHeading = fTmpCarHead
CLEAR_AREA_OF_VEHICLES(vecReplayPlayerLocation, 10)
START_REPLAY_SETUP(vecReplayPlayerLocation, fReplayPlayerHeading)
SKIP_RESPAWN_TRACEY(vecReplayPlayerLocation, fReplayPlayerHeading, TRUE)
// Set whether stalker is alive
IF bStalkerAlive
bStalkerDead = FALSE
ELSE
bStalkerDead = TRUE
ENDIF
ELSE
// NOT A CHECKPOINT
// If we're rolling with Tracey just drive home from wherever we are now
IF (msTrack = MST_DRIVE_TO_STALKER OR msTrack = MST_TRIGGER_STALKER OR msTrack = MST_CHASE)
AND IS_ENTITY_ALIVE(viTraceyCar) AND IS_ENTITY_ALIVE(pedTracey)
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
IF NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
SAFE_SET_PED_INTO_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
vecReplayPlayerLocation = GET_ENTITY_COORDS(viTraceyCar)
ENDIF
ENDIF
// If we're not with Tracey or already driving home, warp to stalker junction
ELSE
// Warp player
vecReplayPlayerLocation = << -52.8746, -110.5227, 56.9431 >>
SET_ENTITY_COORDS(PLAYER_PED_ID(), vecReplayPlayerLocation)
SKIP_RESPAWN_TRACEY(vecReplayPlayerLocation, 71.4, TRUE)
SAFE_SET_PED_INTO_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
ENDIF
ENDIF
SET_REPLAY_MID_MISSION_STAGE_WITH_NAME(CP_DRIVE_HOME, "Drive home (CP3)")
UPDATE_AUDIO_SCENE(AUDIOSCENE_DRIVE_HOME)
msTrack = MST_DRIVE_HOME
sProgress = SP_SETUP
MISSION_OBJECTIVE_EXPIRE(MO_MET1INITDRV)// Drive towards Vinewood.
MISSION_OBJECTIVE_EXPIRE(MO_MET1TCAR) // Get into Tracey's car.
MISSION_OBJECTIVE_RESET(MO_MET1WANT) // Lose the cops.
MISSION_OBJECTIVE_EXPIRE(MO_MET1CHASE) // Chase off the stalker.
MISSION_OBJECTIVE_RESET(MO_MET1HOME) // Go home.
MISSION_OBJECTIVE_RESET(MO_MET1HOTEL) // Go home.
MISSION_OBJECTIVE_RESET(MO_MET1GETBKT) // Get back in Tracey's car.
MISSION_OBJECTIVE_RESET(MO_MET1DLT) // Return to Tracey's car.
IF bCheckpoint
WHILE NOT HAS_ADDITIONAL_TEXT_LOADED(MISSION_TEXT_SLOT)
WAIT(0)
ENDWHILE
IF NOT bStalkerAlive
WHILE NOT HAS_ANIM_DICT_LOADED(sTraceyAnimDict)
WAIT(0)
ENDWHILE
ENDIF
END_REPLAY_SETUP(viTraceyCar, VS_DRIVER, FALSE)
ELSE
WAIT_FOR_WORLD_TO_LOAD(vecReplayPlayerLocation)
ENDIF
ENDPROC
#IF IS_DEBUG_BUILD
/// PURPOSE:
/// DEBUG SKIP: Set the player in Tracey's car to complete mission
PROC SKIP_TO_ARRIVE_HOME()
CPRINTLN(DEBUG_MISSION, "DEBUG SKIP ::: Skipping to STALKER_KILLED")
SAFE_REMOVE_BLIP(biBlip)
SKIP_REMOVE_STALKER()
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
// Respot player
SET_ENTITY_COORDS(PLAYER_PED_ID(), vecReturnLocation)
// Respawn Tracey
SKIP_RESPAWN_TRACEY(vecReturnLocation, 331.1, TRUE)
SAFE_SET_PED_INTO_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
ENDIF
UPDATE_AUDIO_SCENE(AUDIOSCENE_DONE)
msTrack = MST_AT_HOME
sProgress = SP_SETUP
MISSION_OBJECTIVE_EXPIRE(MO_MET1INITDRV)// Drive towards Vinewood.
MISSION_OBJECTIVE_EXPIRE(MO_MET1TCAR) // Get into Tracey's car.
MISSION_OBJECTIVE_RESET(MO_MET1WANT) // Lose the cops.
MISSION_OBJECTIVE_EXPIRE(MO_MET1CHASE) // Chase off the stalker.
MISSION_OBJECTIVE_EXPIRE(MO_MET1HOME) // Go home.
MISSION_OBJECTIVE_EXPIRE(MO_MET1HOTEL) // Go home.
MISSION_OBJECTIVE_EXPIRE(MO_MET1GETBKT) // Get back in Tracey's car.
MISSION_OBJECTIVE_EXPIRE(MO_MET1DLT) // Return to Tracey's car.
SET_REPLAY_MID_MISSION_STAGE_WITH_NAME(CP_MET_TRACEY, "Met Tracey (CP2)")
ENDPROC
/// PURPOSE:
/// Evaluates Z-skip menu output and skips to appropriate point
PROC DO_Z_SKIP(MISSION_SKIP_STAGE mssJumpTo)
// Z-skip init
RC_START_Z_SKIP()
SKIP_CLEAR_WANTED()
SKIP_KILL_CONVERSATION()
SWITCH mssJumpTo
CASE MSS_RESTART
SKIP_TO_MS_INIT()
BREAK
CASE MSS_RESTART_CAR
SKIP_TO_MS_INIT(TRUE)
BREAK
CASE MSS_MEET_TRACEY
SKIP_TO_MEET_TRACEY()
BREAK
CASE MSS_STALKER_APPEARS
SKIP_TO_STALKER_APPEARS()
BREAK
CASE MSS_STALKER_WRECK
SKIP_TO_STALKER_WRECK()
BREAK
CASE MSS_STALKER_STUCK
SKIP_TO_STALKER_STUCK()
BREAK
CASE MSS_DRIVE_HOME
SKIP_TO_DRIVE_HOME()
BREAK
CASE MSS_DRIVE_HOME_DEAD
SKIP_TO_DRIVE_HOME(FALSE, FALSE)
BREAK
CASE MSS_ARRIVE_HOME
SKIP_TO_ARRIVE_HOME()
BREAK
ENDSWITCH
// Z-skip complete
RC_END_Z_SKIP()
ENDPROC
/// PURPOSE:
/// Determines the correct stage to skip to in the mission's J-skips
PROC SKIP_FORWARD()
SWITCH msTrack
CASE MST_DRIVE_TO_TRACEY
CPRINTLN(DEBUG_MISSION, "J-skip : From MST_WAIT_TO_SPAWN_TRACEY")
DO_Z_SKIP(MSS_MEET_TRACEY)
BREAK
CASE MST_WAIT_FOR_PLAYER
CPRINTLN(DEBUG_MISSION, "J-skip : From MST_WAIT_FOR_PLAYER")
DO_Z_SKIP(MSS_STALKER_APPEARS)
BREAK
CASE MST_DRIVE_TO_STALKER
CPRINTLN(DEBUG_MISSION, "J-skip : From MST_TRACEY_START_DRIVING|MST_LINEAR_ROUTE")
DO_Z_SKIP(MSS_STALKER_APPEARS)
BREAK
CASE MST_TRIGGER_STALKER
CASE MST_CHASE
CPRINTLN(DEBUG_MISSION, "J-skip : From MST_TRIGGER_STALKER|MST_CHASE")
DO_Z_SKIP(MSS_STALKER_WRECK)
BREAK
CASE MST_STALKER_WRECKED
CPRINTLN(DEBUG_MISSION, "J-skip : From MST_STALKER_WRECKED")
DO_Z_SKIP(MSS_DRIVE_HOME)
BREAK
CASE MST_DRIVE_HOME
CPRINTLN(DEBUG_MISSION, "J-skip : From MST_DRIVE_HOME")
DO_Z_SKIP(MSS_ARRIVE_HOME)
BREAK
CASE MST_LOSE_WANTED
CPRINTLN(DEBUG_MISSION, "J-skip : From MST_LOSE_WANTED")
SKIP_CLEAR_WANTED()
BREAK
// CASE MST_FAIL_TRACEY_DRIVE_OFF
// CASE MST_PASSED
// // Do nothing, nowhere to go forward to
// CPRINTLN(DEBUG_MISSION, "J-skip was requested but script is already awaiting a pass/fail exit.")
// BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Determines the correct stage to skip to in the mission's P-skips
PROC SKIP_BACKWARD()
SWITCH msTrack
CASE MST_INIT
// Do nothing
CPRINTLN(DEBUG_MISSION, "P-skip was requested but script is already at beginning.")
BREAK
CASE MST_DRIVE_TO_TRACEY
CASE MST_WAIT_FOR_PLAYER
CPRINTLN(DEBUG_MISSION, "P-skip : From MST_WAIT_TO_SPAWN_TRACEY|MST_WAIT_FOR_PLAYER")
DO_Z_SKIP(MSS_RESTART)
BREAK
CASE MST_DRIVE_TO_STALKER
CASE MST_TRIGGER_STALKER
// CASE MST_FAIL_TRACEY_DRIVE_OFF
CPRINTLN(DEBUG_MISSION, "P-skip : From MST_TRACEY_START_DRIVING|MST_LINEAR_ROUTE|MST_TRIGGER_STALKER")
DO_Z_SKIP(MSS_MEET_TRACEY)
BREAK
CASE MST_CHASE
CASE MST_STALKER_WRECKED
CPRINTLN(DEBUG_MISSION, "P-skip : From MST_CHASE|MST_STALKER_WRECKED")
DO_Z_SKIP(MSS_STALKER_APPEARS)
BREAK
CASE MST_DRIVE_HOME
CPRINTLN(DEBUG_MISSION, "P-skip : From MST_DRIVE_HOME")
DO_Z_SKIP(MSS_STALKER_WRECK)
BREAK
CASE MST_AT_HOME
CPRINTLN(DEBUG_MISSION, "P-skip : From MST_AT_HOME")
DO_Z_SKIP(MSS_DRIVE_HOME)
BREAK
CASE MST_FAIL_CALL
CPRINTLN(DEBUG_MISSION, "P-skip : From MST_SUCCESS_CALL|MST_FAIL_CALL")
DO_Z_SKIP(MSS_STALKER_WRECK)
BREAK
CASE MST_LOSE_WANTED
CPRINTLN(DEBUG_MISSION, "P-skip : From MST_LOSE_WANTED")
SKIP_CLEAR_WANTED()
BREAK
// CASE MST_PASSED
// // Script is exiting now - do nothing
// CPRINTLN(DEBUG_MISSION, "P-skip was requested but ignoring as the script is in a fail/pass state so it's too late!")
// BREAK
ENDSWITCH
ENDPROC
#ENDIF
// ===========================================================================================================
// Termination
// ===========================================================================================================
/// -----------------------------------------------------------------------------------------------------------
/// Mission Cleanup
/// -----------------------------------------------------------------------------------------------------------
/// PARAMS:
/// bDelete - Delete everything (for debug cleanups)
/// bTraceyRelToFamily - If Tracey has already been released to the family controller, don't try releasing her again
PROC Mission_Cleanup(BOOL bDelete = FALSE, BOOL bTraceyRelToFamily = FALSE)
CPRINTLN(DEBUG_MISSION, "Mission_Cleanup")
#IF IS_DEBUG_BUILD
IF DOES_WIDGET_GROUP_EXIST(widgetGroup)
DELETE_WIDGET_GROUP(widgetGroup)
ENDIF
#ENDIF
SAFE_REMOVE_BLIP(biBlip)
// Stalker cleanup
IF DOES_ENTITY_EXIST(mvStalkerCar.vehicle)
IF bStalkerStuckCheckActive
REMOVE_VEHICLE_UPSIDEDOWN_CHECK(mvStalkerCar.vehicle)
bStalkerStuckCheckActive = FALSE
ENDIF
REMOVE_ENTITY_FROM_AUDIO_MIX_GROUP(mvStalkerCar.vehicle)
ENDIF
IF bStalkerModelRequested
SET_MODEL_AS_NO_LONGER_NEEDED(mnStalkerModel)
SET_MODEL_AS_NO_LONGER_NEEDED(mvStalkerCar.model)
ENDIF
IF bDelete
SAFE_DELETE_PED(pedStalker)
SAFE_DELETE_VEHICLE(mvStalkerCar.vehicle)
ELSE
SAFE_RELEASE_PED(pedStalker)
SAFE_RELEASE_VEHICLE(mvStalkerCar.vehicle)
ENDIF
KILL_CHASE_HINT_CAM(localChaseHintCamStruct)
// Tracey cleanup
IF bRelGroupExists
REMOVE_RELATIONSHIP_GROUP(relGroupFriendly)
ENDIF
IF IS_ENTITY_ALIVE(viTraceyCar)
AND bStuckCheckActive
REMOVE_VEHICLE_STUCK_CHECK(viTraceyCar)
bStuckCheckActive = FALSE
ENDIF
IF bDelete
SAFE_DELETE_PED(pedTracey)
SAFE_DELETE_VEHICLE(viTraceyCar)
ELSE
IF NOT bTraceyRelToFamily
IF DOES_ENTITY_EXIST(pedTracey)
IF NOT IS_ENTITY_DEAD(pedTracey)
CLEAR_PED_TASKS(pedTracey)
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(pedTracey, FALSE)
SAFE_RELEASE_PED(pedTracey)
ELSE
// This case shouldn't really happen as a dead tracey is a fail condition
SAFE_RELEASE_PED(pedTracey)
ENDIF
ENDIF
ENDIF
SAFE_RELEASE_VEHICLE(viTraceyCar)
ENDIF
SET_ROADS_BACK_TO_ORIGINAL_IN_ANGLED_AREA(naaCarPark.vEnds[0], naaCarPark.vEnds[1], naaCarPark.fWidth)
REMOVE_SCENARIO_BLOCKING_AREA(sbiCarpark)
SET_INSTANCE_PRIORITY_HINT(INSTANCE_HINT_NONE)
// Clear vehicle suppression
SET_VEHICLE_MODEL_IS_SUPPRESSED(mvStalkerCar.model, FALSE)
SET_VEHICLE_MODEL_IS_SUPPRESSED(ISSI2, FALSE)
TOGGLE_VEHICLE_SUPPRESSION(FALSE)
CLEAR_WEATHER_TYPE_PERSIST()
// Normalise wanted stuff
SET_WANTED_LEVEL_MULTIPLIER(1.0)
SET_CREATE_RANDOM_COPS(TRUE)
TERMINATE_THIS_THREAD()
ENDPROC
// -----------------------------------------------------------------------------------------------------------
// Mission Pass
// -----------------------------------------------------------------------------------------------------------
PROC Mission_Passed(BOOL bDebugPass = FALSE)
CPRINTLN(DEBUG_MISSION, "Mission_Passed")
Mission_Flow_Mission_Passed()
IF bDebugPass
Mission_Cleanup(FALSE, FALSE)
ELSE
// Tracey will have been released to the family controller
Mission_Cleanup(FALSE, TRUE)
ENDIF
ENDPROC
//*************************************************************************************************************************************************
//
// Data init proc
//
//*************************************************************************************************************************************************
// Initialise general data
PROC DATA_INIT()
CPRINTLN(DEBUG_MISSION, "DATA INIT")
// Should we be going to the hotel?
IF GetMichaelScheduleStage() = MSS_M4_WithoutFamily
CPRINTLN(DEBUG_MISSION, "MICHAEL EVENTS: Family at the hotel")
bGoingToHotel = TRUE
ELSE
CPRINTLN(DEBUG_MISSION, "MICHAEL EVENTS: Family at home, not going to hotel")
bGoingToHotel = FALSE
ENDIF
// Stalker look at timer limits
iStalkerRamLookDelay = 1000
iStalkerProxLookDelay = 1500
iStalkerLookAtTime = 3500 //600
#IF IS_DEBUG_BUILD
mSkipMenu[0].sTxtLabel = "Restart"
mSkipMenu[1].sTxtLabel = "Restart and give car"
mSkipMenu[2].sTxtLabel = "Meet Tracey (CP1)"
mSkipMenu[3].sTxtLabel = "Stalker appears (CP2)"
mSkipMenu[4].sTxtLabel = "Stalker wrecked"
mSkipMenu[5].sTxtLabel = "Stalker stuck"
mSkipMenu[6].sTxtLabel = "Drive home/hotel - Stalker escaped (CP3)"
mSkipMenu[7].sTxtLabel = "Drive home/hotel - Stalker dead (CP4)"
mSkipMenu[8].sTxtLabel = "Arrive home/hotel (end)"
IF NOT DOES_WIDGET_GROUP_EXIST(widgetGroup)
CPRINTLN(DEBUG_MISSION, "Creating widget group - look for Michael Event Tracey widgets")
widgetGroup = START_WIDGET_GROUP("Michael Event Tracey widgets")
ADD_WIDGET_BOOL("TTY Toggle - Tracey Debug Info", bDebugTraceyTTY)
ADD_WIDGET_BOOL("TTY Toggle - Stalker Debug Info", bDebugStalkerTTY)
ADD_WIDGET_BOOL("Display call monitoring info on screen", bDebugCallTimeDisplay)
ADD_WIDGET_BOOL("Flip stalker car on roof (auto-unchecks)", bDebugFlipStalkerCar)
ADD_WIDGET_BOOL("Toggle the -is player driving weird- tracking bool ", bBonkersDriving)
ADD_WIDGET_INT_SLIDER("Time to wait at the destinations (milliseconds)", iLookAroundDelay, 0, 5000, 100)
ADD_WIDGET_INT_SLIDER("Delay before stalker can do a ram triggered look", iStalkerRamLookDelay, 1000, 15000, 50)
ADD_WIDGET_INT_SLIDER("Delay before stalker can do a proximity triggered look", iStalkerProxLookDelay, 1000, 15000, 50)
ADD_WIDGET_INT_SLIDER("How long each look task should run for", iStalkerLookAtTime, 100, 15000, 50)
ADD_WIDGET_BOOL("Going to hotel instead of house if checked", bGoingToHotel)
STOP_WIDGET_GROUP()
ENDIF
#ENDIF
REQUEST_ADDITIONAL_TEXT("MET1", MISSION_TEXT_SLOT)
STORE_AUDIOSCENES()
STORE_MISSION_OBJECTIVE_DATA()
SETUP_ENROUTE_CHAT()
SETUP_NPC_CARS()
STORE_DESTINATION_POINTS()
STORE_STALKER_TRIGGERS()
SETUP_NAA_VOLUME(naaCarPark, << 200.34, -30.73, 60.91 >>, << 244.07, -45.73, 77.22 >>, 30.0)
// Suppress car model for stalker
SET_VEHICLE_MODEL_IS_SUPPRESSED(mvStalkerCar.model, TRUE)
// Suppress Issi2 (Tracey's car)
SET_VEHICLE_MODEL_IS_SUPPRESSED(ISSI2, TRUE)
// Suppress awkward vehicle models
TOGGLE_VEHICLE_SUPPRESSION(TRUE)
// Have we activated the stalker car's stuck check
bStalkerStuckCheckActive = FALSE
// Does stalker know that player is now after him
bStalkerFleeing = FALSE
// Tracey won't be spawned yet
bTraceyIsCurrentlySpawned = FALSE
// Prep player ped for phone calls etc.
ADD_PED_FOR_DIALOGUE(mConversationStruct, 0, PLAYER_PED_ID(), "MICHAEL")
// No fail reason yet
sFailReason = "DEFAULT"
// Get the current in-game time for triggering Tracey warning/fail calls
INIT_DRIVE_TIMERS()
wfcProgress = WFC_SETUP
bWFCOnHold = FALSE
// Set audioscene track so initial state will be correctly set
iCurrentAudioScene = -1
// Fix possible bad weather - we're driving with the top down
IF Is_Replay_In_Progress()
// On replay - set weather now
SET_WEATHER_TYPE_PERSIST("EXTRASUNNY")
ELSE
// Change to sunny quickly
SET_WEATHER_TYPE_OVERTIME_PERSIST("EXTRASUNNY", 20)
ENDIF
SET_INSTANCE_PRIORITY_HINT(INSTANCE_HINT_DRIVING)
// Wanted level multiplier - B*650203
SET_WANTED_LEVEL_MULTIPLIER(0.2)
ENDPROC
//*************************************************************************************************************************************************
//
// Mission stage procs
//
//*************************************************************************************************************************************************
/// PURPOSE:
/// Mission stage proc
/// Initial stage - mainly a wait for the text to load.
PROC STAGE_INIT()
CPRINTLN(DEBUG_MISSION, "STAGE_INIT: Waiting for mission text to load...")
IF HAS_ADDITIONAL_TEXT_LOADED(MISSION_TEXT_SLOT)
CPRINTLN(DEBUG_MISSION, "STAGE_INIT: Progressing to STAGE_DRIVE_TO_TRACEY.")
UPDATE_AUDIO_SCENE(AUDIOSCENE_NONE)
SET_MISSION_START_VEHICLE_AS_VEHICLE_GEN(<<0,0,0>>, 0, TRUE, CHAR_MICHAEL)
IF IS_REPEAT_PLAY_ACTIVE()
SAFE_FADE_SCREEN_IN_FROM_BLACK()
ENDIF
msTrack = MST_DRIVE_TO_TRACEY
sProgress = SP_SETUP
ENDIF
ENDPROC
/// PURPOSE:
/// Mission stage proc
/// Player drives to meet up with Tracey in Vinewood.
/// Check the player is within reasonable range before we spawn Tracey, spawning her too far away won't work.
PROC STAGE_DRIVE_TO_TRACEY()
SWITCH sProgress
CASE SP_SETUP
CPRINTLN(DEBUG_MISSION, "STAGE_DRIVE_TO_TRACEY : SP_SETUP")
// Do drive to plaza blip and objective
DISPLAY_MISSION_OBJECTIVE(MO_MET1INITDRV) // Go to Vinewood Plaza
IF NOT DOES_BLIP_EXIST(biBlip)
biBlip = CREATE_FAKE_TRACEY_PED_BLIP(vecTraceyPedStart, TRUE)
SET_TAXI_DROPOFF_LOCATION_FOR_BLIP(biBlip, <<237.2897, -60.5704, 68.4345>>, 70.9753)
ENDIF
// Initially check the audio scene state
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
bPlayerOnFoot = FALSE
UPDATE_AUDIO_SCENE(AUDIOSCENE_GO_TO_TRACEY)
ELSE
bPlayerOnFoot = TRUE
ENDIF
sProgress = SP_RUNNING
BREAK
CASE SP_RUNNING
// Check whether player is taking too long
IF NOT CHECK_WARNING_CALL()
// Update audio scene if player was on foot and gets in car
IF bPlayerOnFoot
AND IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
bPlayerOnFoot = FALSE
UPDATE_AUDIO_SCENE(AUDIOSCENE_GO_TO_TRACEY)
// NOTE: Currently we don't flip-flop this because it wasn't requested but could do easily enough
ENDIF
// Check if Michael is wanted
IF GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) > 0
// Wanted - lose cops before meeting Tracey
msTrack = MST_LOSE_WANTED
sProgress = SP_SETUP
ELSE
// Is player in range yet
IF IS_ENTITY_IN_RANGE_COORDS(PLAYER_PED_ID(), vecTraceyStartPoint, SAFE_TRIGGER_DISTANCE)
// Player near to Tracey rendezvous, progress to next stage
sProgress = SP_CLEANUP
ENDIF
ENDIF
ENDIF
BREAK
CASE SP_CLEANUP
CPRINTLN(DEBUG_MISSION, "STAGE_DRIVE_TO_TRACEY : SP_CLEANUP")
// Check whether player is taking too long
IF NOT CHECK_WARNING_CALL()
REQUEST_ANIM_DICT(sTraceyAnimDict)
// Stop ambient cars' crazy car park shenanigans
SET_ROADS_IN_ANGLED_AREA(naaCarPark.vEnds[0], naaCarPark.vEnds[1], naaCarPark.fWidth, FALSE, FALSE)
sbiCarpark = ADD_SCENARIO_BLOCKING_AREA(<< 227.4, -66.5, 67.5 >>, << 249.0, -30.8, 72.5 >>)
CLEAR_AREA(vecTraceyStartPoint, 30, FALSE)
CLEAR_AREA_OF_VEHICLES(vecTraceyStartPoint, 30, FALSE, FALSE, FALSE, FALSE) // This shouldn't be necessary but was added for B*1021065
sProgress = SP_SETUP
msTrack = MST_WAIT_FOR_PLAYER
ENDIF
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Mission stage proc
/// Create Tracey in her car, then wait for the player to reach Tracey's location and get in the car.
PROC STAGE_WAIT_FOR_PLAYER()
SWITCH sProgress
CASE SP_SETUP
CPRINTLN(DEBUG_MISSION, "STAGE_WAIT_FOR_PLAYER : SP_SETUP")
// Request anim dict again in case we're coming from a checkpoint
REQUEST_ANIM_DICT(sTraceyAnimDict)
// Check player isn't wanted
IF NOT CHECK_WANTED_FAIL()
// Check if player's taking too long to reach Tracey
IF NOT CHECK_WARNING_CALL()
// Spawn Tracey and car, go to running and wait for player to get closer once set up
IF CREATE_TRACEY(pedTracey, vecTraceyPedStart, fTraceyPedStartHead)
AND CREATE_NPC_VEHICLE(viTraceyCar, CHAR_TRACEY, vecTraceyStartPoint, fTraceyStartHeading)
AND HAS_ANIM_DICT_LOADED(sTraceyAnimDict)
AND IS_ENTITY_ALIVE(viTraceyCar) // Assert B*2233567: Possible that we'll drop through a few frames after CREATE_NPC_VEHICLE returns true
CPRINTLN(DEBUG_MISSION, "Tracey now spawned! Going on to SP_RUNNING")
bTraceyIsCurrentlySpawned = TRUE
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(pedTracey, TRUE)
// Add Tracey to conversation struct
REMOVE_PED_FOR_DIALOGUE(mConversationStruct, 3)
ADD_PED_FOR_DIALOGUE(mConversationStruct, 3, pedTracey, "TRACEY")
// Tracey can't fly through windscreen or be jacked
SET_PED_CONFIG_FLAG(pedTracey, PCF_WillFlyThroughWindscreen, FALSE)
SET_PED_CONFIG_FLAG(pedTracey, PCF_DontAllowToBeDraggedOutOfVehicle, TRUE)
// Tracey is in friendly group with player
DO_FRIENDLY_GROUP_SETUP()
// Give Tracey some stuff to do
TASK_PLAY_ANIM(pedTracey, sTraceyAnimDict, "nervous_loop", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_LOOPING)
SET_PED_KEEP_TASK(pedTracey, TRUE)
// Configure Tracey's car
SET_ENTITY_LOAD_COLLISION_FLAG(viTraceyCar, TRUE)
SET_VEHICLE_STRONG(viTraceyCar, TRUE)
LOWER_CONVERTIBLE_ROOF(viTraceyCar, TRUE)
ADD_VEHICLE_UPSIDEDOWN_CHECK(viTraceyCar)
SET_VEHICLE_HAS_STRONG_AXLES(viTraceyCar, TRUE)
SET_VEHICLE_AS_RESTRICTED(viTraceyCar, RESTRICTION_TRACEY)
bStuckCheckActive = TRUE
// Don't allow random cops to spawn from now on
SET_CREATE_RANDOM_COPS(FALSE)
// Progress checkers
iTraceyWaitingChatTimer = GET_GAME_TIMER()
iTraceyWaitingConversationCount = 0
cptTraceyWaitingComments = CPT_WAIT_TO_TRIGGER
traceyCarEntry = TCE_WAIT_FOR_PLAYER_RANGE
bMichaelCommentPlayed = FALSE
sProgress = SP_RUNNING
ENDIF
ENDIF
ENDIF
BREAK
CASE SP_RUNNING
// Check for warning call availability toggling on/off
IF bWFCOnHold = FALSE
// Warning calls are currently available - see if they should be toggled off
IF IS_ENTITY_IN_RANGE_COORDS(PLAYER_PED_ID(), vecTraceyStartPoint, SAFE_TRIGGER_DISTANCE)
CPRINTLN(DEBUG_MISSION, "STAGE_WAIT_FOR_PLAYER: SP_RUNNING: Player is within ", SAFE_TRIGGER_DISTANCE, "m of Tracey, warning/fail calls toggled OFF")
bWFCOnHold = TRUE
ENDIF
ELSE
// Warning calls are not currently available - see if they should be toggled on
IF NOT IS_ENTITY_IN_RANGE_COORDS(PLAYER_PED_ID(), vecTraceyStartPoint, TRIGGER_OFF_DISTANCE)
CPRINTLN(DEBUG_MISSION, "STAGE_WAIT_FOR_PLAYER: SP_RUNNING: Player is over ", TRIGGER_OFF_DISTANCE, "m from Tracey, warning/fail calls toggled ON")
bWFCOnHold = FALSE
ENDIF
ENDIF
// Check whether player's taking too long
IF NOT CHECK_WARNING_CALL()
// Check player isn't wanted
IF NOT CHECK_WANTED_FAIL()
SWITCH traceyCarEntry
CASE TCE_WAIT_FOR_PLAYER_RANGE
// Wait for player to be close to Tracey
// IF IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedTracey, TRACEY_MET_DIST)
IF IS_ENTITY_IN_ANGLED_AREA(PLAYER_PED_ID(), <<246.147903,-39.210854,67.896645>>, <<202.663467,-23.777254,72.904160>>, 22.0, FALSE) // B*930057 - simple distance check allows triggering from roof - B*978708 added locate chevrons
OR IS_ENTITY_IN_ANGLED_AREA(PLAYER_PED_ID(), <<233.789108,-45.515152,67.573883>>, <<229.991577,-55.962749,72.289795>>, 12.5, FALSE) // B*1028273 - adjusted trigger area so Tracey's reaction looks better
// OR IS_ENTITY_IN_ANGLED_AREA(PLAYER_PED_ID(), <<238.792450,-32.855148,68.715935>>, <<237.636749,-36.150738,69.726151>>, 1.0, TRUE) // redundant check makes a chevron show at yellow blip location - REMOVED due to B*1135720
IF IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
KILL_FACE_TO_FACE_CONVERSATION_DO_NOT_FINISH_LAST_LINE()
ENDIF
// Tracey now gets in right away
SEQUENCE_INDEX siTmpTC
OPEN_SEQUENCE_TASK(siTmpTC)
TASK_PLAY_ANIM(NULL, sTraceyAnimDict, "breakout", NORMAL_BLEND_IN, SLOW_BLEND_OUT, -1)
TASK_ENTER_VEHICLE(NULL, viTraceyCar, DEFAULT_TIME_BEFORE_WARP*3, VS_FRONT_RIGHT, PEDMOVEBLENDRATIO_RUN)
// TASK_LOOK_AT_ENTITY(NULL, PLAYER_PED_ID(), -1, SLF_WHILE_NOT_IN_FOV) // head track Michael - B*984650 - removed from task sequence due to B*1306732
CLOSE_SEQUENCE_TASK(siTmpTC)
CLEAR_PED_TASKS(pedTracey)
TASK_LOOK_AT_ENTITY(pedTracey, PLAYER_PED_ID(), 4000, SLF_DEFAULT, SLF_LOOKAT_HIGH)
TASK_PERFORM_SEQUENCE(pedTracey, siTmpTC)
CLEAR_SEQUENCE_TASK(siTmpTC)
// Make player go straight to driver door
SET_PED_CONFIG_FLAG(PLAYER_PED_ID(), PCF_ForceDirectEntry, TRUE)
// Record getting in
REPLAY_START_EVENT()
iMeetTimer = GET_GAME_TIMER() + 10000
bMeetReplayIsGoing = TRUE
traceyCarEntry = TCE_WAIT_FOR_CONVERSATION_START
ELSE
// Audio scene update triggers within this proc
TRACEY_WAITING_DIALOGUE()
ENDIF
BREAK
CASE TCE_WAIT_FOR_CONVERSATION_START
// Start the conversation
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_SEES", CONV_PRIORITY_MEDIUM, DO_NOT_DISPLAY_SUBTITLES)
OR IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar, FALSE) // Allow progression when audio doesn't play
CPRINTLN(DEBUG_MISSION, "STAGE_WAIT_FOR_PLAYER: SP_RUNNING: Michael met up with Tracey. Setting checkpoint.")
// Close enough to say they reached the checkpoint
SET_REPLAY_MID_MISSION_STAGE_WITH_NAME(CP_MET_TRACEY, "Met Tracey (CP2)")
// Disabling dawdle fail for now, may need revision // Set Tracey's wait mode to car-park
wfcProgress = WFC_ARRIVED_WAIT
iFootDawdleTimer = GET_GAME_TIMER()
// Update blips and change objective
SAFE_REMOVE_BLIP(biBlip)
biBlip = CREATE_VEHICLE_BLIP(viTraceyCar, TRUE)
DISPLAY_MISSION_OBJECTIVE(MO_MET1TCAR)
traceyCarEntry = TCE_WAIT_FOR_PLAYER_IN_CAR
ENDIF
// Stop recording
IF bMeetReplayIsGoing = TRUE
AND GET_GAME_TIMER() > iMeetTimer
REPLAY_STOP_EVENT()
bMeetReplayIsGoing = FALSE
ENDIF
// Revised brute force head track - B*1306732
TASK_LOOK_AT_ENTITY(pedTracey, PLAYER_PED_ID(), 2000, SLF_WHILE_NOT_IN_FOV, SLF_LOOKAT_VERY_HIGH)
BREAK
CASE TCE_WAIT_FOR_PLAYER_IN_CAR
IF NOT bMichaelCommentPlayed
AND NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
AND NOT IS_THIS_PRINT_BEING_DISPLAYED("MET1TCAR")
AND NOT IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
// IF IS_THIS_PRINT_BEING_DISPLAYED("MET1TCAR")
// IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_GETIN", CONV_PRIORITY_MEDIUM, DO_NOT_DISPLAY_SUBTITLES)
// bMichaelCommentPlayed = TRUE
// ENDIF
// ELSE
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_GETIN", CONV_PRIORITY_MEDIUM)
bMichaelCommentPlayed = TRUE
ENDIF
// ENDIF
ENDIF
// Wait for player and Tracey to both be in car
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar, FALSE)
SET_PED_CONFIG_FLAG(PLAYER_PED_ID(), PCF_ForceDirectEntry, FALSE)
IF IS_PED_IN_VEHICLE(pedTracey, viTraceyCar, FALSE)
CPRINTLN(DEBUG_MISSION, "STAGE_WAIT_FOR_PLAYER: SP_RUNNING: Tracey and Michael both in car.")
CLEAR_PRINTS()
CLEAR_PED_TASKS(pedTracey) // Stop looking at Michael once he's in the car
UPDATE_AUDIO_SCENE(AUDIOSCENE_DRIVE_TO_STALKER)
sProgress = SP_CLEANUP
ELIF MISSION_OBJECTIVE_WILL_DISPLAY(MO_MET1WAIT)
SAFE_REMOVE_BLIP(biBlip)
biBlip = CREATE_PED_BLIP(pedTracey, TRUE, TRUE)
SET_BLIP_NAME_FROM_TEXT_FILE(biBlip, "MET1LBTRAC")
DISPLAY_MISSION_OBJECTIVE(MO_MET1WAIT)
ENDIF
ENDIF
// Revised brute force head track - B*1306732
TASK_LOOK_AT_ENTITY(pedTracey, PLAYER_PED_ID(), 2000, SLF_WHILE_NOT_IN_FOV, SLF_LOOKAT_VERY_HIGH)
// Stop recording
IF bMeetReplayIsGoing = TRUE
AND GET_GAME_TIMER() > iMeetTimer
REPLAY_STOP_EVENT()
bMeetReplayIsGoing = FALSE
ENDIF
BREAK
ENDSWITCH
// Check that the player hasn't run off, once Tracey is properly blipped
IF (traceyCarEntry <> TCE_WAIT_FOR_PLAYER_RANGE)
// Abandon check
IF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedTracey, 100.0)
// FAIL - abandoned Tracey
sFailReason = "MET1ABANDON" // You abandoned Tracey
msTrack = MST_FAIL_FADE
sProgress = SP_SETUP
// Warn player not to run off, once Tracey is properly blipped
ELIF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedTracey, 50.0)
// Warn player
DISPLAY_MISSION_OBJECTIVE(MO_MET1DLT) // Return to Tracey.
ENDIF
ENDIF
// Check for player threatening Tracey
IF HAS_PLAYER_THREATENED_PED(pedTracey, DEFAULT, DEFAULT, DEFAULT, TRUE)
CLEAR_PED_TASKS(pedTracey)
TASK_SMART_FLEE_PED(pedTracey, PLAYER_PED_ID(), 200, -1)
IF IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
IF GET_SPEAKER_INT_FOR_CURRENT_STANDARD_CONVERSATION_LINE() = 3
KILL_FACE_TO_FACE_CONVERSATION_DO_NOT_FINISH_LAST_LINE()
ELSE
KILL_FACE_TO_FACE_CONVERSATION()
ENDIF
ENDIF
sFailReason = "MET1TRASCARE"
msTrack = MST_FAIL_FADE
sProgress = SP_SETUP
ENDIF
ENDIF
ENDIF
BREAK
CASE SP_CLEANUP
// Check player's not wanted
IF NOT CHECK_WANTED_FAIL()
CPRINTLN(DEBUG_MISSION, "STAGE_WAIT_FOR_PLAYER: SP_CLEANUP")
SAFE_REMOVE_BLIP(biBlip)
sProgress = SP_SETUP
msTrack = MST_DRIVE_TO_STALKER
INIT_BONKERS_DRIVING_CHECK()
ENDIF
// Stop recording
IF bMeetReplayIsGoing = TRUE
AND GET_GAME_TIMER() > iMeetTimer
REPLAY_STOP_EVENT()
bMeetReplayIsGoing = FALSE
ENDIF
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Mission stage proc
/// Drive around until the stalker appears
PROC STAGE_DRIVE_TO_STALKER()
// Stop recording
IF bMeetReplayIsGoing = TRUE
AND GET_GAME_TIMER() > iMeetTimer
REPLAY_STOP_EVENT()
bMeetReplayIsGoing = FALSE
ENDIF
SWITCH sProgress
CASE SP_SETUP
CPRINTLN(DEBUG_MISSION, "STAGE_DRIVE_TO_STALKER: SP_SETUP")
IF IS_ENTITY_ALIVE(viTraceyCar)
// Initialise Tracey driving monitor variables
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar, TRUE)
bPlayerOnFoot = FALSE
ELSE
bPlayerOnFoot = TRUE
ENDIF
IF IS_PED_IN_VEHICLE(pedTracey, viTraceyCar, TRUE)
bTraceyonFoot = FALSE
ELSE
bTraceyonFoot = TRUE
ENDIF
iCurrentDestination = 0
traceyDestinationState = TDS_START_CHAT
vecTraceyPositionTrack[0] = vecTraceyStartPoint
vecTraceyPositionTrack[1] = vecTraceyPositionTrack[0]
vecTraceyPositionTrack[2] = vecTraceyPositionTrack[1]
iTraceyPositionTimer = GET_GAME_TIMER()
iComplainTimer = GET_GAME_TIMER()
sProgress = SP_RUNNING
PLAYER_BONKERS_DRIVING_CHECK()
ENDIF
BREAK
CASE SP_RUNNING
// Check for wanted fail
IF NOT CHECK_WANTED_FAIL()
UPDATE_TRACEYCAR_SAVED_COORDS()
PLAYER_BONKERS_DRIVING_CHECK()
// Checks for if player has got out of the car
IF bPlayerOnFoot
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
bPlayerOnFoot = FALSE
SAFE_REMOVE_BLIP(biBlip) // Car will be blipped
CLEAR_PRINTS()
// Check whether Tracey is still in car
IF IS_PED_IN_VEHICLE(pedTracey, viTraceyCar)
bTraceyonFoot = FALSE
// If we should have a blip, recreate it
IF traceyDestinationState = TDS_DRIVING_TO_POINT
AND NOT IS_ENTITY_IN_RANGE_COORDS(PLAYER_PED_ID(), dpLocates[iCurrentDestination].location, dpLocates[iCurrentDestination].range)
biBlip = CREATE_COORD_BLIP(dpLocates[iCurrentDestination].location)
// If handler was waiting for chat to start, it will need a nudge because it's waiting on a killed conversation
ELIF traceyDestinationState = TDS_WAITING_FOR_CHAT_TO_START
traceyDestinationState = TDS_WAITING_FOR_BLIP_LINE
ENDIF
ELSE
bTraceyonFoot = TRUE
bTraceyAttemptingReentry = FALSE
ENDIF
ELIF HAS_PLAYER_THREATENED_PED(pedTracey)
CLEAR_PED_TASKS(pedTracey)
TASK_SMART_FLEE_PED(pedTracey, PLAYER_PED_ID(), 200, -1)
IF IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
IF GET_SPEAKER_INT_FOR_CURRENT_STANDARD_CONVERSATION_LINE() = 3
KILL_FACE_TO_FACE_CONVERSATION_DO_NOT_FINISH_LAST_LINE()
ELSE
KILL_FACE_TO_FACE_CONVERSATION()
ENDIF
ENDIF
sFailReason = "MET1TRASCARE"
msTrack = MST_FAIL_FADE
sProgress = SP_SETUP
ELIF MISSION_OBJECTIVE_WILL_DISPLAY(MO_MET1DLT) // Don't leave Tracey behind
IF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedTracey, WARN_DIS_TOO_FAR)
DISPLAY_MISSION_OBJECTIVE(MO_MET1DLT)
ENDIF
ELIF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedTracey, ABANDON_TRACEY_DIST)
sFailReason = "MET1ABANDON" // You abandoned Tracey
sProgress = SP_SETUP
msTrack = MST_FAIL_FADE
ELSE
// Head track
IF IS_PED_IN_VEHICLE(pedTracey, viTraceyCar)
HANDLE_BUDDY_HEAD_TRACK_WHILE_ENTERING_VEHICLE()
ENDIF
ENDIF
// Check for player getting out of car
ELIF NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
bPlayerOnFoot = TRUE
SAFE_REMOVE_BLIP(biBlip)
biBlip = CREATE_VEHICLE_BLIP(viTraceyCar, TRUE)
#IF IS_DEBUG_BUILD
IF MISSION_OBJECTIVE_WILL_DISPLAY(MO_MET1GETBKT)
CPRINTLN(DEBUG_MISSION, "Displaying get back in obective in STAGE_DRIVE_TO_STALKER: SP_RUNNING")
ENDIF
#ENDIF
DISPLAY_MISSION_OBJECTIVE(MO_MET1GETBKT)
IF IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
KILL_FACE_TO_FACE_CONVERSATION()
ENDIF
// Checks for when Tracey is not in car. (Player is in car)
ELIF bTraceyonFoot
// Should Tracey be trying to get back in
IF NOT bTraceyAttemptingReentry
AND NOT CAR_CURRENTLY_ON_ROOF()
bTraceyAttemptingReentry = TRUE
TASK_ENTER_VEHICLE(pedTracey, viTraceyCar, DEFAULT, VS_FRONT_RIGHT, PEDMOVEBLENDRATIO_RUN)
ENDIF
IF IS_PED_IN_VEHICLE(pedTracey, viTraceyCar)
bTraceyonFoot = FALSE
SAFE_REMOVE_BLIP(biBlip) // Tracey will be blipped
// Already checked that player is in car when we get here so see if conversation/objective handling needs to progress
IF iCurrentDestination < NUM_DESTINATIONS
// If handler was waiting for chat to start, it will need a nudge because it's waiting on a killed conversation
IF traceyDestinationState = TDS_WAITING_FOR_CHAT_TO_START
CPRINTLN(DEBUG_MISSION, "STAGE_DRIVE_TO_STALKER: SP_RUNNING: Player has just got back in car, TDS_WAITING_FOR_CHAT_TO_START bumped to TDS_WAITING_FOR_BLIP_LINE")
traceyDestinationState = TDS_WAITING_FOR_BLIP_LINE
ELIF traceyDestinationState = TDS_DO_DELAY
CPRINTLN(DEBUG_MISSION, "STAGE_DRIVE_TO_STALKER: SP_RUNNING: Player has just got back in car, TDS_WAITING_FOR_CHAT_TO_START bumped to TDS_WAITING_FOR_BLIP_LINE")
traceyDestinationState = TDS_LOOK_AROUND
ENDIF
ENDIF
ELIF MISSION_OBJECTIVE_WILL_DISPLAY(MO_MET1DLT) // Don't leave Tracey behind
IF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedTracey, WARN_DIS_TOO_FAR)
DISPLAY_MISSION_OBJECTIVE(MO_MET1DLT)
ENDIF
ELIF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedTracey, ABANDON_TRACEY_DIST)
sFailReason = "MET1ABANDON" // You abandoned Tracey
sProgress = SP_SETUP
msTrack = MST_FAIL_FADE
ENDIF
// Check for whether Tracey has got out of car. (Player will be in car)
ELIF NOT IS_PED_IN_VEHICLE(pedTracey, viTraceyCar)
bTraceyonFoot = TRUE
bTraceyAttemptingReentry = FALSE
SAFE_REMOVE_BLIP(biBlip) // A destination may be blipped
biBlip = CREATE_PED_BLIP(pedTracey, TRUE, TRUE)
DISPLAY_MISSION_OBJECTIVE(MO_MET1WAIT)
IF IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
KILL_FACE_TO_FACE_CONVERSATION_DO_NOT_FINISH_LAST_LINE()
ENDIF
// Check for arriving at destinations. (Player and Tracey both in car)
ELSE
// Destination check varies by step
SWITCH traceyDestinationState
CASE TDS_START_CHAT
// Don't update the chat etc. if the car is on its roof
IF NOT CAR_CURRENTLY_ON_ROOF()
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", dpLocates[iCurrentDestination].chatLabel, CONV_PRIORITY_HIGH)
CPRINTLN(DEBUG_MISSION, "Destination conversation ", iCurrentDestination, " starting...")
traceyDestinationState = TDS_WAITING_FOR_CHAT_TO_START
ENDIF
ENDIF
BREAK
CASE TDS_WAITING_FOR_CHAT_TO_START
// Wait until we're sure the conversation has started
IF IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
CPRINTLN(DEBUG_MISSION, "Destination conversation ", iCurrentDestination, " now running.")
traceyDestinationState = TDS_WAITING_FOR_BLIP_LINE
ENDIF
BREAK
CASE TDS_WAITING_FOR_BLIP_LINE
// Wait for the lines to reach the point where the blip should be displayed
IF IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
IF GET_CURRENT_SCRIPTED_CONVERSATION_LINE() >= dpLocates[iCurrentDestination].blipUpdateLine
CPRINTLN(DEBUG_MISSION, "TDS_WAITING_FOR_BLIP_LINE: Blip for destination ", iCurrentDestination, " added on line-count trigger.")
biBlip = CREATE_COORD_BLIP(dpLocates[iCurrentDestination].location)
traceyDestinationState = TDS_WAITING_TO_DELAY_QUEUING_EXTRA_CHAT
ENDIF
ELSE
// Something went awry - blip now
CPRINTLN(DEBUG_MISSION, "TDS_WAITING_FOR_BLIP_LINE: Blip for destination ", iCurrentDestination, " added on no-conversation trigger.")
biBlip = CREATE_COORD_BLIP(dpLocates[iCurrentDestination].location)
traceyDestinationState = TDS_WAITING_TO_DELAY_QUEUING_EXTRA_CHAT
ENDIF
BREAK
CASE TDS_WAITING_TO_DELAY_QUEUING_EXTRA_CHAT
// May need to recreate blip if player has been out of car - B*1135892
IF NOT DOES_BLIP_EXIST(biBlip)
biBlip = CREATE_COORD_BLIP(dpLocates[iCurrentDestination].location)
ENDIF
// Skip out on this if the player's got ahead of where we expect
IF IS_ENTITY_IN_RANGE_COORDS(PLAYER_PED_ID(), dpLocates[iCurrentDestination].location, dpLocates[iCurrentDestination].range)
CPRINTLN(DEBUG_MISSION, "TDS_WAITING_TO_DELAY_QUEUING_EXTRA_CHAT: Skipping adding extra chat, destination ", iCurrentDestination, " already in range.")
traceyDestinationState = TDS_DRIVING_TO_POINT
// Wait of current conversation to be done
ELIF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
CPRINTLN(DEBUG_MISSION, "Starting wait to queue extra conversation ", iCurrentDestination)
iLookAroundTimer = GET_GAME_TIMER() + 2000
traceyDestinationState = TDS_WAITING_TO_QUEUE_EXTRA_CHAT
ENDIF
BREAK
// ADDED ANOTHER DELAY HERE: B*996715 reopen 26/04
CASE TDS_WAITING_TO_QUEUE_EXTRA_CHAT
// May need to recreate blip if player has been out of car - B*1135892
IF NOT DOES_BLIP_EXIST(biBlip)
biBlip = CREATE_COORD_BLIP(dpLocates[iCurrentDestination].location)
ENDIF
// Skip out on this if the player's got ahead of where we expect
IF IS_ENTITY_IN_RANGE_COORDS(PLAYER_PED_ID(), dpLocates[iCurrentDestination].location, dpLocates[iCurrentDestination].range)
CPRINTLN(DEBUG_MISSION, "TDS_WAITING_TO_DELAY_QUEUING_EXTRA_CHAT: Skipping adding extra chat, destination ", iCurrentDestination, " already in range.")
traceyDestinationState = TDS_DRIVING_TO_POINT
// Wait of any active conversation to be done and delay timeout
ELIF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
AND GET_GAME_TIMER() > iLookAroundTimer
// Don't update the chat etc. if the car is on its roof
IF NOT CAR_CURRENTLY_ON_ROOF()
// See if we need to play bad driving complaint
IF bBonkersDriving
AND NOT ARE_STRINGS_EQUAL(dpLocates[iCurrentDestination].bonkersChatLabel, "NULL")
// bad driving chat
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", dpLocates[iCurrentDestination].bonkersChatLabel, CONV_PRIORITY_HIGH)
CPRINTLN(DEBUG_MISSION, "Destination extra conversation ", iCurrentDestination, " starting...")
traceyDestinationState = TDS_DRIVING_TO_POINT
ENDIF
ELSE
// Regular banter
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", dpLocates[iCurrentDestination].extraChatLabel, CONV_PRIORITY_HIGH)
CPRINTLN(DEBUG_MISSION, "Destination extra conversation ", iCurrentDestination, " starting...")
traceyDestinationState = TDS_DRIVING_TO_POINT
ENDIF
ENDIF
ENDIF
ENDIF
BREAK
CASE TDS_DRIVING_TO_POINT
// Wait for player to be in range of next point
IF IS_ENTITY_IN_RANGE_COORDS(PLAYER_PED_ID(), dpLocates[iCurrentDestination].location, dpLocates[iCurrentDestination].range)
// Update coordinate
iCurrentDestination++
SAFE_REMOVE_BLIP(biBlip)
CPRINTLN(DEBUG_MISSION, "STAGE_DRIVE_TO_STALKER: SP_RUNNING: Updating destination point to ", iCurrentDestination)
LOOK_AROUND_FOR_STALKER()
// REMOVED DELAY HERE: B*1135739/1135745
// REINSTATED DELAY HERE: B*996715 reopen 17/04
// Set wait timer unless it's the final point
IF iCurrentDestination < NUM_DESTINATIONS
AND IS_ANY_CONVERSATION_ONGOING_OR_QUEUED() // Don't delay the next destination dialogue if banter has already finished
CPRINTLN(DEBUG_MISSION, "STAGE_DRIVE_TO_STALKER: SP_RUNNING: TDS_DRIVING_TO_POINT: Setting to do a delay after killed conversation")
KILL_FACE_TO_FACE_CONVERSATION()
traceyDestinationState = TDS_DO_DELAY
ELSE
CPRINTLN(DEBUG_MISSION, "STAGE_DRIVE_TO_STALKER: SP_RUNNING: TDS_DRIVING_TO_POINT: Setting to not do a delay")
// Kill conversation if stalker time
IF iCurrentDestination = NUM_DESTINATIONS
AND IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
KILL_FACE_TO_FACE_CONVERSATION()
ENDIF
bDoingDelay = FALSE
traceyDestinationState = TDS_LOOK_AROUND
ENDIF
// May need to recreate blip if player has been out of car - B*1135892
ELIF NOT DOES_BLIP_EXIST(biBlip)
biBlip = CREATE_COORD_BLIP(dpLocates[iCurrentDestination].location)
ELSE
// Do complaint conversations
BANTER_CONTROLLER()
ENDIF
BREAK
CASE TDS_DO_DELAY
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
CPRINTLN(DEBUG_MISSION, "STAGE_DRIVE_TO_STALKER: SP_RUNNING: TDS_DO_DELAY: Conversation finished, setting the look around delay now")
bDoingDelay = TRUE
iLookAroundTimer = GET_GAME_TIMER() + iLookAroundDelay
traceyDestinationState = TDS_LOOK_AROUND
ENDIF
BREAK
CASE TDS_LOOK_AROUND
IF bDoingDelay
// Wait for delay time at location until ready to continue
IF GET_GAME_TIMER() > iLookAroundTimer
bDoingDelay = FALSE
ENDIF
ELSE
// If we have any destintions left, trigger the next destination conversation
IF iCurrentDestination < NUM_DESTINATIONS
CPRINTLN(DEBUG_MISSION, "STAGE_DRIVE_TO_STALKER: SP_RUNNING: TDS_LOOK_AROUND: Updating destination point to ", iCurrentDestination)
iLookAroundTimer = GET_GAME_TIMER()
traceyDestinationState = TDS_START_CHAT
// At final destination - trigger stalker
ELSE
CPRINTLN(DEBUG_MISSION, "STAGE_DRIVE_TO_STALKER: SP_RUNNING: TDS_LOOK_AROUND: Reached last destination, going on to SP_CLEANUP")
bMichaelArrivalLinePlayed = FALSE
REQUEST_ANIM_DICT("missmichael_event@tracy")
sProgress = SP_CLEANUP
ENDIF
ENDIF
BREAK
ENDSWITCH
ENDIF
ENDIF
// If we're heading to last destination, reduce traffic density
// SET_VEHICLE_DENSITY_MULTIPLIER has been replaced with SET_VEHICLE_DENSITY_MULTIPLIER_THIS_FRAME
IF iCurrentDestination >= NUM_DESTINATIONS-2
SET_VEHICLE_DENSITY_MULTIPLIER_THIS_FRAME(0.5)
ENDIF
BREAK
CASE SP_CLEANUP
// Get ready to spawn stalker
IF NOT CHECK_WANTED_FAIL()
CPRINTLN(DEBUG_MISSION, "STAGE_DRIVE_TO_STALKER: SP_CLEANUP: Player reached destination")
SAFE_REMOVE_BLIP(biBlip)
// Checks for if player has got out of the car
IF bPlayerOnFoot
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
bPlayerOnFoot = FALSE
SAFE_REMOVE_BLIP(biTraceyBlip) // Car will be blipped
CLEAR_PRINTS()
ENDIF
ELIF NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
bPlayerOnFoot = TRUE
biTraceyBlip = CREATE_VEHICLE_BLIP(viTraceyCar)
#IF IS_DEBUG_BUILD
IF MISSION_OBJECTIVE_WILL_DISPLAY(MO_MET1GETBKT)
CPRINTLN(DEBUG_MISSION, "Displaying get back in obective in STAGE_DRIVE_TO_STALKER: SP_CLEANUP")
ENDIF
#ENDIF
DISPLAY_MISSION_OBJECTIVE(MO_MET1GETBKT)
ENDIF
// Start requesting the models
SPAWN_STALKER(<<0,0,0>>, 0, TRUE)
// Comment on arrival
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_LOCX", CONV_PRIORITY_MEDIUM)
bMichaelArrivalLinePlayed = TRUE
ENDIF
// Prep for stalker
CHECK_STALKER_SPAWN_OCCLUSION()
msTrack = MST_TRIGGER_STALKER
sProgress = SP_SETUP
ENDIF
SET_VEHICLE_DENSITY_MULTIPLIER_THIS_FRAME(0.5)
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Mission stage proc
/// Trigger stalker and get him onto the road to follow Tracey.
/// Tracey IDs stalker so player can chase him.
PROC STAGE_TRIGGER_STALKER()
SWITCH sProgress
CASE SP_SETUP
IF NOT CHECK_WANTED_FAIL()
CHECK_STALKER_SPAWN_OCCLUSION()
// Request anim dict again in case at a checkpoint
REQUEST_ANIM_DICT("missmichael_event@tracy")
// Request Tracey's dead reaction anim - can potentially kill stalker right away
REQUEST_ANIM_DICT(sTraceyAnimDict)
// Checks for if player has got out of the car
IF bPlayerOnFoot
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
bPlayerOnFoot = FALSE
SAFE_REMOVE_BLIP(biTraceyBlip) // Car will be blipped
CLEAR_PRINTS()
ENDIF
ELIF NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
bPlayerOnFoot = TRUE
biTraceyBlip = CREATE_VEHICLE_BLIP(viTraceyCar)
#IF IS_DEBUG_BUILD
IF MISSION_OBJECTIVE_WILL_DISPLAY(MO_MET1GETBKT)
CPRINTLN(DEBUG_MISSION, "Displaying get back in obective in STAGE_TRIGGER_STALKER: SP_SETUP")
ENDIF
#ENDIF
DISPLAY_MISSION_OBJECTIVE(MO_MET1GETBKT)
ENDIF
IF NOT bMichaelArrivalLinePlayed
AND CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_LOCX", CONV_PRIORITY_MEDIUM)
bMichaelArrivalLinePlayed = TRUE
ENDIF
IF SPAWN_STALKER(mvStalkerCar.location, mvStalkerCar.heading)
CPRINTLN(DEBUG_MISSION, "STAGE_TRIGGER_STALKER: SP_SETUP: Stalker starting sequence")
// Set stalker initial tracking variables and behaviour
bPlayerWiseToStalker = FALSE
bStalkerFleeing = FALSE
bStalkerSpotChatStarted = FALSE
SET_VEHICLE_FORWARD_SPEED(mvStalkerCar.vehicle, mvStalkerCar.initialSpeed)
SEQUENCE_INDEX siTmp
OPEN_SEQUENCE_TASK(siTmp)
TASK_VEHICLE_MISSION_COORS_TARGET(NULL, mvStalkerCar.vehicle, GET_ENTITY_COORDS(viTraceyCar), MISSION_GOTO, 35.0, DRIVINGMODE_AVOIDCARS|DF_ChangeLanesAroundObstructions|DF_UseSwitchedOffNodes, 10, 12, TRUE)
TASK_VEHICLE_ESCORT(NULL, mvStalkerCar.vehicle, viTraceyCar, VEHICLE_ESCORT_REAR, 25.0, DRIVINGMODE_AVOIDCARS|DF_ChangeLanesAroundObstructions|DF_UseSwitchedOffNodes)
CLOSE_SEQUENCE_TASK(siTmp)
TASK_PERFORM_SEQUENCE(pedStalker, siTmp)
CLEAR_SEQUENCE_TASK(siTmp)
SET_REPLAY_MID_MISSION_STAGE_WITH_NAME(CP_STALKER_APPEARS, "Stalker appears (CP2)")
bStalkerDead = FALSE
// Start tracking whether Tracey can see the stalker
TICK_TRACEY_OUTCOME_TESTS()
sProgress = SP_RUNNING
#IF IS_DEBUG_BUILD
ELIF bDebugStalkerTTY
CPRINTLN(DEBUG_MISSION, "STAGE_TRIGGER_STALKER: SP_SETUP: Waiting for stalker to spawn...")
#ENDIF
ENDIF
ENDIF
// SET_VEHICLE_DENSITY_MULTIPLIER_THIS_FRAME(0.5)
BREAK
CASE SP_RUNNING
// Checks for if player has got out of the car
IF bPlayerOnFoot
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
bPlayerOnFoot = FALSE
SAFE_REMOVE_BLIP(biTraceyBlip) // Car will be blipped
CLEAR_PRINTS()
ENDIF
ELIF NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
bPlayerOnFoot = TRUE
biTraceyBlip = CREATE_VEHICLE_BLIP(viTraceyCar)
#IF IS_DEBUG_BUILD
IF MISSION_OBJECTIVE_WILL_DISPLAY(MO_MET1GETBKT)
CPRINTLN(DEBUG_MISSION, "Displaying get back in obective in STAGE_TRIGGER_STALKER: SP_RUNNING")
ENDIF
#ENDIF
DISPLAY_MISSION_OBJECTIVE(MO_MET1GETBKT)
ENDIF
// Checks for player damaging stalker
IF IS_ENTITY_ALIVE(pedStalker)
IF IS_ENTITY_ALIVE(mvStalkerCar.vehicle)
// See if we're in range for Tracey to notice the stalker
IF NOT bStalkerSpotChatStarted
// IF IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedStalker, 35.0)
// OR GET_GAME_TIMER() - iStalkerBlipTimer > STALKER_SPOT_TIMER
IF HAS_ANIM_DICT_LOADED("missmichael_event@tracy")
AND CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_TENDb", CONV_PRIORITY_MEDIUM)
TASK_PLAY_ANIM(pedTracey, "missmichael_event@tracy", "base")
REMOVE_ANIM_DICT("missmichael_event@tracy")
IF NOT DOES_BLIP_EXIST(biBlip)
biBlip = CREATE_VEHICLE_BLIP(mvStalkerCar.vehicle, FALSE)
SET_BLIP_NAME_FROM_TEXT_FILE(biBlip, "MET1LBSTALK")
UPDATE_AUDIO_SCENE(AUDIOSCENE_CHASE)
ENDIF
bStalkerSpotChatStarted = TRUE
ENDIF
// ENDIF
ELSE
// See if we can display the objective yet
IF MISSION_OBJECTIVE_WILL_DISPLAY(MO_MET1CHASE)
AND (GET_PROFILE_SETTING(PROFILE_DISPLAY_SUBTITLES) = 0 OR NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED())
CPRINTLN(DEBUG_MISSION, "Displaying MO_MET1CHASE in STAGE_TRIGGER_STALKER:SP_RUNNING after bStalkerSpotChatStarted set to TRUE")
DISPLAY_MISSION_OBJECTIVE(MO_MET1CHASE)
ENDIF
// Check whether stalker should be reacting to player proximity/damage
IF NOT bPlayerWiseToStalker
// Check if player has damaged stalker
IF HAS_ENTITY_BEEN_DAMAGED_BY_ENTITY(mvStalkerCar.vehicle, PLAYER_PED_ID())
OR HAS_ENTITY_BEEN_DAMAGED_BY_ENTITY(pedStalker, PLAYER_PED_ID())
CPRINTLN(DEBUG_MISSION, "STAGE_TRIGGER_STALKER: SP_RUNNING: player damaged stalker")
CLEAR_ENTITY_LAST_DAMAGE_ENTITY(pedStalker)
CLEAR_ENTITY_LAST_DAMAGE_ENTITY(mvStalkerCar.vehicle)
bPlayerWiseToStalker = TRUE
ELIF IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedStalker, 15.0)
CPRINTLN(DEBUG_MISSION, "STAGE_TRIGGER_STALKER: SP_RUNNING: player in spot range of stalker")
bPlayerWiseToStalker = TRUE
ENDIF
// Once stalker knows player is wise to him
ELSE
CPRINTLN(DEBUG_MISSION, "STAGE_TRIGGER_STALKER: SP_RUNNING: Set stalker to flee player after spot")
TASK_VEHICLE_MISSION_PED_TARGET(pedStalker, mvStalkerCar.vehicle, PLAYER_PED_ID(), MISSION_FLEE, 25.0, DRIVINGMODE_AVOIDCARS|DF_ChangeLanesAroundObstructions|DF_SteerAroundStationaryCars, 100.0, 1.0, TRUE)
bStalkerFleeing = TRUE
sProgress = SP_CLEANUP
ENDIF
// Check for player running away from stalker before spotting has occured
IF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedStalker, STALKER_ESCAPE_DIST)
CPRINTLN(DEBUG_MISSION, "Player drove away from stalker in STAGE_TRIGGER_STALKER!")
SAFE_REMOVE_BLIP(biBlip)
SAFE_REMOVE_BLIP(biTraceyBlip)
sFailReason = "MET1ESCAPE" // The stalker escaped.
msTrack = MST_FAIL_FADE
sProgress = SP_SETUP
ENDIF
CONTROL_PED_CHASE_HINT_CAM_IN_VEHICLE(localChaseHintCamStruct, pedStalker)
IF DOES_BLIP_EXIST(biBlip)
UPDATE_CHASE_BLIP(biBlip, pedStalker, STALKER_ESCAPE_DIST, CHASE_FLASH_DIST)
ENDIF
ENDIF
ELSE
// Player has managed to trash stalker's car already
msTrack = MST_STALKER_WRECKED
sProgress = SP_SETUP
ENDIF
TICK_TRACEY_OUTCOME_TESTS()
ELSE
// Player has managed to kill stalker already
CPRINTLN(DEBUG_MISSION, "STAGE_TRIGGER_STALKER: SP_RUNNING: Stalker killed!")
SAFE_REMOVE_BLIP(biBlip)
bStalkerDead = TRUE
bTraceySawOutcome = DID_TRACEY_SEE_OUTCOME()
START_STALKER_SNUFF_MOVIE()
sProgress = SP_SETUP
msTrack = MST_DRIVE_HOME
CLEAR_PRINTS()
ENDIF
// SET_VEHICLE_DENSITY_MULTIPLIER_THIS_FRAME(0.5)
BREAK
CASE SP_CLEANUP
CPRINTLN(DEBUG_MISSION, "STAGE_TRIGGER_STALKER: SP_CLEANUP: Going to STAGE_CHASE")
// Allow cops again
SET_CREATE_RANDOM_COPS(TRUE)
// See if we can display the objective yet
IF MISSION_OBJECTIVE_WILL_DISPLAY(MO_MET1CHASE)
AND (GET_PROFILE_SETTING(PROFILE_DISPLAY_SUBTITLES) = 0 OR NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED())
CPRINTLN(DEBUG_MISSION, "Displaying MO_MET1CHASE in STAGE_TRIGGER_STALKER:SP_CLEANUP")
DISPLAY_MISSION_OBJECTIVE(MO_MET1CHASE)
ENDIF
// Checks for if player has got out of the car
IF bPlayerOnFoot
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
bPlayerOnFoot = FALSE
SAFE_REMOVE_BLIP(biTraceyBlip) // Car will be blipped
CLEAR_PRINTS()
ENDIF
ELIF NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
bPlayerOnFoot = TRUE
biTraceyBlip = CREATE_VEHICLE_BLIP(viTraceyCar)
#IF IS_DEBUG_BUILD
IF MISSION_OBJECTIVE_WILL_DISPLAY(MO_MET1GETBKT)
CPRINTLN(DEBUG_MISSION, "Displaying get back in obective in STAGE_TRIGGER_STALKER: SP_CLEANUP")
ENDIF
#ENDIF
DISPLAY_MISSION_OBJECTIVE(MO_MET1GETBKT)
ENDIF
// Track whether Tracey can see the stalker
TICK_TRACEY_OUTCOME_TESTS()
sProgress = SP_SETUP
msTrack = MST_CHASE
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Mission stage proc
/// Player pursues the stalker's car
PROC STAGE_CHASE()
SWITCH sProgress
CASE SP_SETUP
// Stalker will now spot Michael almost straight away
IF IS_ENTITY_ALIVE(pedStalker)
CONTROL_PED_CHASE_HINT_CAM_IN_VEHICLE(localChaseHintCamStruct, pedStalker)
// Check for distance fail in case player is sat doing nothing
IF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedStalker, STALKER_ESCAPE_DIST)
SAFE_REMOVE_BLIP(biBlip)
sFailReason = "MET1ESCAPE"
msTrack = MST_FAIL_FADE
sProgress = SP_SETUP
ELSE
// See if we can display the objective yet
IF MISSION_OBJECTIVE_WILL_DISPLAY(MO_MET1CHASE)
AND (GET_PROFILE_SETTING(PROFILE_DISPLAY_SUBTITLES) = 0 OR NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED())
CPRINTLN(DEBUG_MISSION, "Displaying MO_MET1CHASE in STAGE_CHASE:SP_SETUP")
DISPLAY_MISSION_OBJECTIVE(MO_MET1CHASE)
ENDIF
CPRINTLN(DEBUG_MISSION, "STAGE_CHASE: SP_SETUP: Initialising stalker chase stuff, going to SP_RUNNING")
// Initialise the conversation tracker
iChaseChatTimer = GET_GAME_TIMER()// - CHASE_COMMENT_DELAY + 1000 // Make the first comment almost immediate
iShootingComplaintGap = GET_GAME_TIMER()
iShootingTimer = GET_GAME_TIMER() - RECENT_SHOOTING_TIMEOUT
cptChaseTrack = CPT_WAIT_TO_TRIGGER
iChaseStalkerChatCount = 0
iChaseTraceyChatCount = 0
bStalkerChatting = FALSE
bTraceyChatting = FALSE
bQuickRamComment = FALSE
bShootingComment = FALSE
iLastCollisionTime = GET_GAME_TIMER() - 1000
// Reinitialise the position tracking (stalker spawning stage can take a few seconds with no updates, or we may be restarting from a checkpoint)
vecTraceyPositionTrack[0] = vecTraceyStartPoint
vecTraceyPositionTrack[1] = vecTraceyPositionTrack[0]
vecTraceyPositionTrack[2] = vecTraceyPositionTrack[1]
iTraceyPositionTimer = GET_GAME_TIMER()
iComplainTimer = GET_GAME_TIMER()
// Start Stalker fleeing if they aren't already
IF NOT bStalkerFleeing
bStalkerFleeing = TRUE
CLEAR_PED_TASKS(pedStalker)
TASK_VEHICLE_MISSION_PED_TARGET(pedStalker, mvStalkerCar.vehicle, PLAYER_PED_ID(), MISSION_FLEE, 25.0, DRIVINGMODE_AVOIDCARS|DF_ChangeLanesAroundObstructions|DF_SteerAroundStationaryCars, 100.0, 1.0, TRUE)
ENDIF
// Initialise stalker position tracking and progress to main chase tracking
iStalkerPositionTimer = GET_GAME_TIMER()
IF IS_ENTITY_ALIVE(mvStalkerCar.vehicle)
vecStalkerPositionTrack[0] = GET_ENTITY_COORDS(mvStalkerCar.vehicle)
vecStalkerPositionTrack[1] = vecStalkerPositionTrack[0]
vecStalkerPositionTrack[2] = vecStalkerPositionTrack[1]
ENDIF
INIT_HAS_PLAYER_RAMMED_ENEMY_ENOUGH(bStalkerRammedLastFrame, iStalkerRamTimer, iStalkerRamCount, fRamClosingSpeedLastFrame)
// Init lookat behaviour delay
//SET_PED_CAN_HEAD_IK(pedStalker, FALSE)
iStalkerLookTimer = GET_GAME_TIMER()
IF DOES_BLIP_EXIST(biBlip)
UPDATE_CHASE_BLIP(biBlip, pedStalker, STALKER_ESCAPE_DIST, CHASE_FLASH_DIST)
ENDIF
sProgress = SP_RUNNING
ENDIF
// Checks for if player has got out of the car
IF bPlayerOnFoot
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
bPlayerOnFoot = FALSE
SAFE_REMOVE_BLIP(biTraceyBlip) // Car will be blipped
CLEAR_PRINTS()
ENDIF
ELIF NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
bPlayerOnFoot = TRUE
biTraceyBlip = CREATE_VEHICLE_BLIP(viTraceyCar)
#IF IS_DEBUG_BUILD
IF MISSION_OBJECTIVE_WILL_DISPLAY(MO_MET1GETBKT)
CPRINTLN(DEBUG_MISSION, "Displaying get back in obective in STAGE_CHASE: SP_SETUP")
ENDIF
#ENDIF
DISPLAY_MISSION_OBJECTIVE(MO_MET1GETBKT)
ENDIF
TICK_TRACEY_OUTCOME_TESTS()
// Stalker was killed inside vehicle
ELSE
CPRINTLN(DEBUG_MISSION, "STAGE_CHASE: SP_SETUP: Stalker killed!")
KILL_CHASE_HINT_CAM(localChaseHintCamStruct)
SAFE_REMOVE_BLIP(biBlip)
bStalkerDead = TRUE
bTraceySawOutcome = DID_TRACEY_SEE_OUTCOME()
START_STALKER_SNUFF_MOVIE()
sProgress = SP_SETUP
msTrack = MST_DRIVE_HOME
CLEAR_PRINTS()
ENDIF
BREAK
CASE SP_RUNNING
IF IS_ENTITY_ALIVE(pedStalker)
// If stalker is alive, check whether his car has become undriveable
IF STALKER_CAR_IS_TRASHED()
CPRINTLN(DEBUG_MISSION, "STAGE_CHASE: SP_RUNNING: Stalker's car trashed")
SAFE_REMOVE_BLIP(biBlip)
sProgress = SP_SETUP
msTrack = MST_STALKER_WRECKED
// Handle unexpected cases where stalker exits their vehicle - e.g. player drags them out
ELIF NOT IS_PED_IN_ANY_VEHICLE(pedStalker)
CPRINTLN(DEBUG_MISSION, "STAGE_CHASE: SP_RUNNING: Stalker not in car")
sProgress = SP_SETUP
msTrack = MST_STALKER_WRECKED
// Check for stalker escaping
ELIF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedStalker, STALKER_ESCAPE_DIST)
CPRINTLN(DEBUG_MISSION, "STAGE_CHASE: SP_RUNNING: Stalker escaped")
SAFE_REMOVE_BLIP(biBlip)
sFailReason = "MET1ESCAPE" // The stalker escaped.
msTrack = MST_FAIL_FADE
sProgress = SP_SETUP
// Stalker and his car OK, update driving check
ELSE
// See if we can display the objective yet
IF MISSION_OBJECTIVE_WILL_DISPLAY(MO_MET1CHASE)
AND (GET_PROFILE_SETTING(PROFILE_DISPLAY_SUBTITLES) = 0 OR NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED())
CPRINTLN(DEBUG_MISSION, "Displaying MO_MET1CHASE in STAGE_CHASE:SP_RUNNING")
DISPLAY_MISSION_OBJECTIVE(MO_MET1CHASE)
ENDIF
// Tick stalker's lookat responses now that we've updated the ram check
STALKER_HEAD_TRACK_CONTROLLER()
// Monitor the stalker's driving
STALKER_DRIVE_CHECK()
// Checks for if player has got out of the car
IF bPlayerOnFoot
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
bPlayerOnFoot = FALSE
SAFE_REMOVE_BLIP(biTraceyBlip) // Car will be blipped
CLEAR_PRINTS()
// Check whether Tracey is still in car
IF IS_PED_IN_VEHICLE(pedTracey, viTraceyCar, FALSE)
// Tracey is in car - get back after stalker
bTraceyonFoot = FALSE
ELSE
// Tracey is on foot - need her back in
bTraceyonFoot = TRUE
bTraceyAttemptingReentry = FALSE
biTraceyBlip = CREATE_PED_BLIP(pedTracey, TRUE, TRUE)
DISPLAY_MISSION_OBJECTIVE(MO_MET1WAIT)
ENDIF
ELIF MISSION_OBJECTIVE_WILL_DISPLAY(MO_MET1DLT) // Don't leave Tracey behind
IF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedTracey, WARN_DIS_TOO_FAR)
DISPLAY_MISSION_OBJECTIVE(MO_MET1DLT)
ENDIF
ELIF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedTracey, ABANDON_TRACEY_DIST)
sFailReason = "MET1ABANDON" // You abandoned Tracey
sProgress = SP_SETUP
msTrack = MST_FAIL_FADE
ELSE
// Head track
IF IS_PED_IN_VEHICLE(pedTracey, viTraceyCar, FALSE)
HANDLE_BUDDY_HEAD_TRACK_WHILE_ENTERING_VEHICLE()
ENDIF
ENDIF
// Check for player getting out of car
ELIF NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
bPlayerOnFoot = TRUE
biTraceyBlip = CREATE_VEHICLE_BLIP(viTraceyCar, TRUE)
// Don't show get back in objective if stalker on roof B*1307970
IF NOT STALKER_CAR_UPSIDE_DOWN()
#IF IS_DEBUG_BUILD
IF MISSION_OBJECTIVE_WILL_DISPLAY(MO_MET1GETBKT)
CPRINTLN(DEBUG_MISSION, "Displaying get back in obective in STAGE_CHASE: SP_RUNNING")
ENDIF
#ENDIF
DISPLAY_MISSION_OBJECTIVE(MO_MET1GETBKT) // Get back in Tracey's car.
ENDIF
IF IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
KILL_FACE_TO_FACE_CONVERSATION()
ENDIF
// Checks for when Tracey is not in car. (Player is in car)
ELIF bTraceyonFoot
// Should Tracey be trying to get back in
IF NOT bTraceyAttemptingReentry
AND NOT CAR_CURRENTLY_ON_ROOF()
bTraceyAttemptingReentry = TRUE
TASK_ENTER_VEHICLE(pedTracey, viTraceyCar, DEFAULT, VS_FRONT_RIGHT, PEDMOVEBLENDRATIO_RUN)
ENDIF
// Is Tracey back in?
IF IS_PED_IN_VEHICLE(pedTracey, viTraceyCar, FALSE)
bTraceyonFoot = FALSE
SAFE_REMOVE_BLIP(biTraceyBlip) // Tracey will be blipped
// Already checked that player is in car when we get here so reblip stalker
//biBlip = CREATE_VEHICLE_BLIP(mvStalkerCar.vehicle, FALSE)
ELIF MISSION_OBJECTIVE_WILL_DISPLAY(MO_MET1DLT) // Don't leave Tracey behind
IF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedTracey, WARN_DIS_TOO_FAR)
DISPLAY_MISSION_OBJECTIVE(MO_MET1DLT)
ENDIF
ELIF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedTracey, ABANDON_TRACEY_DIST)
sFailReason = "MET1ABANDON" // You abandoned Tracey
sProgress = SP_SETUP
msTrack = MST_FAIL_FADE
ENDIF
// Check for whether Tracey has got out of car. (Player will be in car)
ELIF NOT IS_PED_IN_VEHICLE(pedTracey, viTraceyCar)
bTraceyonFoot = TRUE
bTraceyAttemptingReentry = FALSE
biTraceyBlip = CREATE_PED_BLIP(pedTracey, TRUE, TRUE)
DISPLAY_MISSION_OBJECTIVE(MO_MET1WAIT)
IF IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
KILL_FACE_TO_FACE_CONVERSATION_DO_NOT_FINISH_LAST_LINE()
ENDIF
ENDIF
ENDIF
// Flash chase blip if escaping
IF DOES_BLIP_EXIST(biBlip)
AND IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
AND IS_PED_IN_VEHICLE(pedTracey, viTraceyCar)
UPDATE_CHASE_BLIP(biBlip, pedStalker, STALKER_ESCAPE_DIST, CHASE_FLASH_DIST)
ENDIF
// Chase hint cam and associated audio scene control
CONTROL_PED_CHASE_HINT_CAM_IN_VEHICLE(localChaseHintCamStruct, pedStalker)
IF IS_GAMEPLAY_HINT_ACTIVE()
TOGGLE_FOCUS_AUDIO_SCENE(TRUE)
ELSE
TOGGLE_FOCUS_AUDIO_SCENE(FALSE)
ENDIF
TICK_TRACEY_OUTCOME_TESTS()
ELSE
// Stalker was killed
CPRINTLN(DEBUG_MISSION, "STAGE_CHASE: SP_RUNNING: Stalker killed")
SAFE_REMOVE_BLIP(biBlip)
KILL_CHASE_HINT_CAM(localChaseHintCamStruct)
bStalkerDead = TRUE
START_STALKER_SNUFF_MOVIE()
bTraceySawOutcome = DID_TRACEY_SEE_OUTCOME()
sProgress = SP_SETUP
msTrack = MST_DRIVE_HOME
ENDIF
// Update vehicle coords
UPDATE_TRACEYCAR_SAVED_COORDS()
// Update chase conversations tracker
STALKER_CHASE_CHAT()
BREAK
CASE SP_CLEANUP
SCRIPT_ASSERT("MET_TRACEY1::: Chase stage is trying to exit in non-standard way. Please bug this with as much information as possible.")
Mission_Passed(TRUE)
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Mission stage proc
/// Stalker gets out of car and flees on foot when his car is wrecked
PROC STAGE_STALKER_WRECKED()
SWITCH sProgress
CASE SP_SETUP
// Request dead reaction anim dictionary again in case we're at a checkpoint
REQUEST_ANIM_DICT(sTraceyAnimDict)
// Get stalker out of vehicle, set him to flee player on foot
IF IS_ENTITY_ALIVE(pedStalker)
CLEAR_PED_TASKS(pedStalker)
SET_PED_FLEE_ATTRIBUTES(pedStalker, FA_USE_VEHICLE, FALSE)
// Stalker may already be out of vehicle or not, handle either case
IF IS_PED_IN_ANY_VEHICLE(pedStalker)
CPRINTLN(DEBUG_MISSION, "STAGE_STALKER_WRECKED : Giving stalker flee task sequence")
SEQUENCE_INDEX seqStalk
OPEN_SEQUENCE_TASK(seqStalk)
TASK_LEAVE_ANY_VEHICLE(NULL, 0, ECF_DONT_CLOSE_DOOR|ECF_RESUME_IF_INTERRUPTED)
//TASK_SMART_FLEE_PED(NULL, PLAYER_PED_ID(), 200.0, -1)
TASK_SMART_FLEE_COORD(NULL, GET_ENTITY_COORDS(PLAYER_PED_ID()), 2000.0, -1) // TASK_SMART_FLEE_PED causes him to go back and forth - B*985005
CLOSE_SEQUENCE_TASK(seqStalk)
TASK_PERFORM_SEQUENCE(pedStalker, seqStalk)
CLEAR_SEQUENCE_TASK(seqStalk)
ELSE
CPRINTLN(DEBUG_MISSION, "STAGE_STALKER_WRECKED : Giving stalker flee task")
//TASK_SMART_FLEE_PED(pedStalker, PLAYER_PED_ID(), 200.0, -1)
TASK_SMART_FLEE_COORD(pedStalker, GET_ENTITY_COORDS(PLAYER_PED_ID()), 2000.0, -1) // TASK_SMART_FLEE_PED causes him to go back and forth - B*985005
ENDIF
// Initialise stalker dialogue stuff
bTraceyOnFootAlertDone = FALSE
bTraceyEscapeCommentDone = FALSE
iFootChaseStalkerChat = 0
iFootChaseTraceyChat = 0
bTraceyChatting = FALSE
bStalkerChatting = FALSE
iChaseChatTimer = GET_GAME_TIMER()
cptFootChaseTrack = CPT_WAIT_TO_TRIGGER
// Kill any conversation that might prevent Tracey's alert firing immediately - B*1135838
IF IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
KILL_FACE_TO_FACE_CONVERSATION_DO_NOT_FINISH_LAST_LINE()
ENDIF
// Initialise stalker on-foot chase monitoring
bStalkerCowering = FALSE
bStalkerOnPedFleeTask = FALSE
FORCE_INIT_STALKER_FOOT_TRACKING()
iChaseTaskTimer[0] = GET_GAME_TIMER() + CHASE_PROX_CHECK_TIME
iChaseTaskTimer[1] = GET_GAME_TIMER() + CHASE_PROX_CHECK_TIME // Initialise to task time in case player already close to ped
// Remove stalker vehicle blip, blip on foot, init blip as a flasher
SAFE_REMOVE_BLIP(biBlip)
biBlip = CREATE_PED_BLIP(pedStalker)
INIT_FLASH_BLIP_AND_TEXT(biBlip, "MET1KSTKRED", "MET1KSTKBLU", iFlashTick, iFlashTimeout)
SET_ENTITY_IS_TARGET_PRIORITY(pedStalker, TRUE)
// Hint cam override
CONTROL_PED_CHASE_HINT_CAM_IN_VEHICLE(localChaseHintCamStruct, pedStalker)
// Getting back in Tracey's car not important at this point
SAFE_REMOVE_BLIP(biTraceyBlip)
// Update audio scene
UPDATE_AUDIO_SCENE(AUDIOSCENE_CHASE_FOOT)
TICK_TRACEY_OUTCOME_TESTS()
sProgress = SP_RUNNING
ELSE
// Stalker dead
CPRINTLN(DEBUG_MISSION, "STAGE_STALKER_WRECKED : Stalker killed during SP_SETUP!")
SAFE_REMOVE_BLIP(biBlip)
bStalkerDead = TRUE
START_STALKER_SNUFF_MOVIE()
bTraceySawOutcome = DID_TRACEY_SEE_OUTCOME()
sProgress = SP_CLEANUP
ENDIF
BREAK
CASE SP_RUNNING
// Wait for player to kill stalker or stalker to escape
// Flash blip
FLASH_BLIP_AND_TEXT(biBlip, "MET1KSTKRED", "MET1KSTKBLU", iFlashTick, iFlashTimeout)
// Check for stalker running off
IF IS_ENTITY_ALIVE(pedStalker)
IF NOT IS_ENTITY_IN_RANGE_ENTITY(pedStalker, PLAYER_PED_ID(), STALKER_FOOT_ESCAPE_DIST)
AND NOT IS_ENTITY_ON_SCREEN(pedStalker)
CPRINTLN(DEBUG_MISSION, "STAGE_STALKER_WRECKED : Stalker escaped on foot!")
SAFE_DELETE_PED(pedStalker)
bTraceySawOutcome = DID_TRACEY_SEE_OUTCOME()
sProgress = SP_CLEANUP
ELSE
// Foot chase banter
IF bTraceyOnFootAlertDone
STALKER_ON_FOOT_CHAT()
ELIF IS_PED_ON_FOOT(pedStalker)
AND NOT IS_PED_RAGDOLL(pedStalker)
AND NOT IS_PED_GETTING_UP(pedStalker)
AND NOT IS_ENTITY_ON_FIRE(pedStalker)
AND NOT IS_ENTITY_IN_AIR(pedStalker)
// Tracey does one of her "making a run for it" lines
IF IS_THIS_PRINT_BEING_DISPLAYED("MET1KSTKRED") OR IS_THIS_PRINT_BEING_DISPLAYED("MET1KSTKBLU")
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_THERUN", CONV_PRIORITY_MEDIUM, DO_NOT_DISPLAY_SUBTITLES)
bTraceyOnFootAlertDone = TRUE
ENDIF
ELSE
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_THERUN", CONV_PRIORITY_MEDIUM, DISPLAY_SUBTITLES)
bTraceyOnFootAlertDone = TRUE
ENDIF
ENDIF
ENDIF
// Head track
IF IS_PED_ON_FOOT(PLAYER_PED_ID())
HANDLE_BUDDY_HEAD_TRACK_WHILE_ENTERING_VEHICLE()
ENDIF
// Hint cam override
CONTROL_PED_CHASE_HINT_CAM_IN_VEHICLE(localChaseHintCamStruct, pedStalker)
// Check for stalker being trapped
STALKER_FLEE_CHECK()
// Nobble stalker's running speed
SET_PED_MAX_MOVE_BLEND_RATIO(pedStalker, 2.2) // Keep inside the in-range checks or may cause issues such as B*1119023
TICK_TRACEY_OUTCOME_TESTS()
ENDIF
ELSE
// Stalker is dead
CPRINTLN(DEBUG_MISSION, "STAGE_STALKER_WRECKED : Stalker killed!")
SAFE_REMOVE_BLIP(biBlip)
bStalkerDead = TRUE
bTraceySawOutcome = DID_TRACEY_SEE_OUTCOME()
START_STALKER_SNUFF_MOVIE()
sProgress = SP_CLEANUP
ENDIF
BREAK
CASE SP_CLEANUP
// Stalker dead or fled, clear blip
SAFE_REMOVE_BLIP(biBlip)
IF MISSION_OBJECTIVES_CURRENTLY_DISPLAYED()
CLEAR_PRINTS()
ENDIF
KILL_CHASE_HINT_CAM(localChaseHintCamStruct)
UPDATE_AUDIO_SCENE(AUDIOSCENE_DRIVE_HOME)
sProgress = SP_SETUP
msTrack = MST_DRIVE_HOME
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Mission stage proc
/// An eerily true-to-life "Dad's taxi" section
/// Michael drives Tracey home
PROC STAGE_DRIVE_HOME()
SWITCH sProgress
CASE SP_SETUP
IF bGoingToHotel = TRUE
vecReturnLocation = vecHotelLocation
ELSE
vecReturnLocation = vecHouseLocation
ENDIF
// Check whether player is still in Tracey's car
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
IF GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) < 0
biBlip = CREATE_COORD_BLIP(vecReturnLocation)
bPlayerWanted = FALSE
bWantedObjectiveNeeded = FALSE
ELSE
bPlayerWanted = TRUE
bWantedObjectiveNeeded = TRUE
ENDIF
IF bStalkerDead
SET_REPLAY_MID_MISSION_STAGE_WITH_NAME(CP_DRIVE_HOME_DEAD, "Drive home - stalker dead (CP4)")
CPRINTLN(DEBUG_MISSION, "Set CP_DRIVE_HOME_DEAD")
ELSE
SET_REPLAY_MID_MISSION_STAGE_WITH_NAME(CP_DRIVE_HOME, "Drive home - stalker chased off (CP3)")
CPRINTLN(DEBUG_MISSION, "Set CP_DRIVE_HOME")
ENDIF
// Initialise home chat
iDriveHomeChatStep = 0
// Tracey will be blipped if we started this section on foot
SAFE_REMOVE_BLIP(biTraceyBlip)
INIT_BONKERS_DRIVING_CHECK()
bPlayerOnFoot = FALSE
bNearlyHomeCommentDone = FALSE
bConversationKilled = FALSE
sProgress = SP_RUNNING
// Player is on foot -
ELSE
// Player has to have a chance to get back to Tracey, otherwise we may get unfair distance fails
IF NOT DOES_BLIP_EXIST(biTraceyBlip)
biTraceyBlip = CREATE_VEHICLE_BLIP(viTraceyCar)
ENDIF
#IF IS_DEBUG_BUILD
IF MISSION_OBJECTIVE_WILL_DISPLAY(MO_MET1GETBKT)
CPRINTLN(DEBUG_MISSION, "Displaying get back in obective in STAGE_DRIVE_HOME: SP_SETUP")
ENDIF
#ENDIF
DISPLAY_MISSION_OBJECTIVE(MO_MET1GETBKT)
bPlayerOnFoot = TRUE // Keep outside check or blip may get created again in SP_RUNNING
ENDIF
BREAK
CASE SP_RUNNING
IF NOT bBonkersDriving // Check we only trigger complaint conv once
PLAYER_BONKERS_DRIVING_CHECK()
IF bBonkersDriving
// Queue crazy complaint
bNeedCrazyDrivingComment = TRUE
ENDIF
ENDIF
// Checks for when player is not in car
IF bPlayerOnFoot
// Do we need the objective
IF MISSION_OBJECTIVE_WILL_DISPLAY(MO_MET1GETBKT)
IF GET_PROFILE_SETTING(PROFILE_DISPLAY_SUBTITLES) = 0
OR NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
CPRINTLN(DEBUG_MISSION, "Displaying get back in obective in STAGE_DRIVE_HOME: SP_RUNNING")
DISPLAY_MISSION_OBJECTIVE(MO_MET1GETBKT)
ENDIF
ENDIF
// Has player got back in?
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
bPlayerOnFoot = FALSE
SAFE_REMOVE_BLIP(biBlip)
SAFE_REMOVE_BLIP(biTraceyBlip)
CLEAR_PRINTS()
// Check whether Tracey is still in car
IF NOT IS_PED_IN_VEHICLE(pedTracey, viTraceyCar, FALSE)
// Tracey is on foot - need her back in as priority
bTraceyOnFoot = TRUE
bTraceyAttemptingReentry = FALSE
biTraceyBlip = CREATE_PED_BLIP(pedTracey, TRUE, TRUE)
ELSE
// Tracey is in car
bTraceyOnFoot = FALSE
bTraceyAttemptingReentry = FALSE
// Head track
HANDLE_BUDDY_HEAD_TRACK_WHILE_ENTERING_VEHICLE()
// See if player has become wanted while on foot
IF GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) < 1
bPlayerWanted = FALSE
biBlip = CREATE_COORD_BLIP(vecReturnLocation)
ELSE
bPlayerWanted = TRUE
bWantedObjectiveNeeded = TRUE
ENDIF
ENDIF
// See if they've abandoned the vehicle
ELIF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedTracey, ABANDON_TRACEY_DIST)
sFailReason = "MET1ABANDON" // You abandoned Tracey.
msTrack = MST_FAIL_FADE
sProgress = SP_SETUP
// See if we need to display an abandon warning
ELIF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedTracey, WARN_DIS_TOO_FAR)
DISPLAY_MISSION_OBJECTIVE(MO_MET1DLT) // Return to Tracey's car
ENDIF
// Check whether player just got out of car
ELIF NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
bPlayerOnFoot = TRUE
CLEAR_PRINTS()
SAFE_REMOVE_BLIP(biBlip)
biTraceyBlip = CREATE_VEHICLE_BLIP(viTraceyCar)
IF IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
KILL_FACE_TO_FACE_CONVERSATION()
ENDIF
// Checks for case where Tracey is on foot
ELIF bTraceyonFoot
// Do we need the objective
IF MISSION_OBJECTIVE_WILL_DISPLAY(MO_MET1WAIT)
AND (GET_PROFILE_SETTING(PROFILE_DISPLAY_SUBTITLES) = 0 OR NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED())
DISPLAY_MISSION_OBJECTIVE(MO_MET1WAIT)
ENDIF
// Should Tracey be trying to get back in
IF NOT bTraceyAttemptingReentry
AND NOT CAR_CURRENTLY_ON_ROOF()
bTraceyAttemptingReentry = TRUE
TASK_ENTER_VEHICLE(pedTracey, viTraceyCar, DEFAULT, VS_FRONT_RIGHT, PEDMOVEBLENDRATIO_RUN)
ENDIF
// Is Tracey back in?
IF IS_PED_IN_VEHICLE(pedTracey, viTraceyCar, FALSE)
bTraceyonFoot = FALSE
SAFE_REMOVE_BLIP(biTraceyBlip) // Tracey currently will be blipped
// Tracey is in car
bTraceyOnFoot = FALSE
bTraceyAttemptingReentry = FALSE
// See if player has become wanted while Tracey was on foot
IF GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) < 1
bPlayerWanted = FALSE
biBlip = CREATE_COORD_BLIP(vecReturnLocation)
ELSE
bPlayerWanted = TRUE
bWantedObjectiveNeeded = TRUE
ENDIF
ELIF MISSION_OBJECTIVE_WILL_DISPLAY(MO_MET1DLT) // Don't leave Tracey behind
IF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedTracey, WARN_DIS_TOO_FAR)
DISPLAY_MISSION_OBJECTIVE(MO_MET1DLT)
ENDIF
ELIF NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), pedTracey, ABANDON_TRACEY_DIST)
sFailReason = "MET1ABANDON" // You abandoned Tracey
sProgress = SP_SETUP
msTrack = MST_FAIL_FADE
ENDIF
// Check for whether Tracey has got out of car. (Player will be in car)
ELIF NOT IS_PED_IN_VEHICLE(pedTracey, viTraceyCar)
bTraceyonFoot = TRUE
bTraceyAttemptingReentry = FALSE
CLEAR_PRINTS()
SAFE_REMOVE_BLIP(biBlip) // A destination may be blipped
SAFE_REMOVE_BLIP(biTraceyBlip)
biTraceyBlip = CREATE_PED_BLIP(pedTracey, TRUE, TRUE)
IF IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
KILL_FACE_TO_FACE_CONVERSATION_DO_NOT_FINISH_LAST_LINE()
ENDIF
// Checks for when player is wanted
ELIF bPlayerWanted
// Objective - waits until we're clear of subtitles
IF bWantedObjectiveNeeded
IF GET_PROFILE_SETTING(PROFILE_DISPLAY_SUBTITLES) = 0
OR (iDriveHomeChatStep >= 4 AND NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED())
DISPLAY_MISSION_OBJECTIVE(MO_MET1WANT)
bWantedObjectiveNeeded = FALSE
ENDIF
ENDIF
// Check that player is still in car
IF NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viTraceyCar)
bPlayerOnFoot = TRUE
CLEAR_PRINTS()
SAFE_REMOVE_BLIP(biBlip)
SAFE_REMOVE_BLIP(biTraceyBlip)
biTraceyBlip = CREATE_VEHICLE_BLIP(viTraceyCar)
// Check for whether Tracey has got out of car. (Player will be in car)
ELIF NOT IS_PED_IN_VEHICLE(pedTracey, viTraceyCar)
bTraceyonFoot = TRUE
bTraceyAttemptingReentry = FALSE
biTraceyBlip = CREATE_PED_BLIP(pedTracey, TRUE, TRUE)
DISPLAY_MISSION_OBJECTIVE(MO_MET1WAIT)
// See if player is no longer wanted
ELIF GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) < 1
bPlayerWanted = FALSE
bNeedWantedRemark = FALSE
CLEAR_PRINTS()
SAFE_REMOVE_BLIP(biBlip)
biBlip = CREATE_COORD_BLIP(vecReturnLocation)
ENDIF
// Trigger chat in case we need to progress the initial conversation or need wanted dialogue
DRIVING_HOME_CHAT()
// Check whether player has become wanted
ELIF GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) > 0
// Player has become wanted
bPlayerWanted = TRUE
bWantedObjectiveNeeded = TRUE
bNeedWantedRemark = TRUE
CLEAR_PRINTS()
SAFE_REMOVE_BLIP(biBlip)
// Checks for when everything seems to be going swimmingly
ELSE
// Check if we're actually there now
IF REACHED_DESTINATION_CHECK()
sProgress = SP_CLEANUP
// Trigger nearly home comment
ELIF DESTINATION_COMMENT_TRIGGER()
AND NOT bNearlyHomeCommentDone
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_NRHOME", CONV_PRIORITY_MEDIUM)
bNearlyHomeCommentDone = TRUE
ENDIF
// Trigger banter
ELSE
DRIVING_HOME_CHAT()
ENDIF
// Drive home objective
IF MISSION_OBJECTIVE_WILL_DISPLAY(MO_MET1HOME)
AND MISSION_OBJECTIVE_WILL_DISPLAY(MO_MET1HOTEL)
IF GET_PROFILE_SETTING(PROFILE_DISPLAY_SUBTITLES) = 0
OR NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
IF bGoingToHotel
DISPLAY_MISSION_OBJECTIVE(MO_MET1HOTEL)
ELSE
DISPLAY_MISSION_OBJECTIVE(MO_MET1HOME)
ENDIF
ENDIF
ENDIF
ENDIF
BREAK
CASE SP_CLEANUP
SAFE_REMOVE_BLIP(biBlip)
UPDATE_AUDIO_SCENE(AUDIOSCENE_DONE)
IF IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
KILL_FACE_TO_FACE_CONVERSATION()
ENDIF
msTrack = MST_AT_HOME
sProgress = SP_SETUP
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Mission stage proc
/// Tracey disembarks car at house/hotel
PROC STAGE_AT_HOME()
SWITCH sProgress
CASE SP_SETUP
// Set health before vehicle has stopped for maximum leniency!
IF GET_VEHICLE_PETROL_TANK_HEALTH(viTraceyCar) < 1
SET_VEHICLE_PETROL_TANK_HEALTH(viTraceyCar, 10.0)
ENDIF
IF GET_VEHICLE_ENGINE_HEALTH(viTraceyCar) < 1
SET_VEHICLE_ENGINE_HEALTH(viTraceyCar, 10.0)
ENDIF
// Stop the vehicle ready for delivery event
IF BRING_VEHICLE_TO_HALT_AND_DISABLE_VEH_CONTROLS(viTraceyCar, 5.0, 5)
// Get Tracey out of car
SEQUENCE_INDEX siTmp
IF bGoingToHotel
vecTraceyDest = <<-1321.4403, 379.9881, 67.8596>>
OPEN_SEQUENCE_TASK(siTmp)
TASK_PAUSE(NULL, 650)
TASK_LEAVE_VEHICLE(NULL, viTraceyCar)
TASK_FOLLOW_NAV_MESH_TO_COORD(NULL, vecTraceyDest, 1)
// TASK_FOLLOW_NAV_MESH_TO_COORD(NULL, <<-813.7761, 179.4943, 71.1592>>, 1)
CLOSE_SEQUENCE_TASK(siTmp)
ELSE
vecTraceyDest = <<-813.7761, 179.4943, 71.1592>>
OPEN_SEQUENCE_TASK(siTmp)
TASK_PAUSE(NULL, 650)
TASK_LEAVE_VEHICLE(NULL, viTraceyCar)
TASK_FOLLOW_NAV_MESH_TO_COORD(NULL, <<-820.1358, 176.9053, 70.6086>>, 1)
TASK_FOLLOW_NAV_MESH_TO_COORD(NULL, vecTraceyDest, 1)
CLOSE_SEQUENCE_TASK(siTmp)
ENDIF
CLEAR_PED_TASKS(pedTracey)
TASK_PERFORM_SEQUENCE(pedTracey, siTmp)
CLEAR_SEQUENCE_TASK(siTmp)
SET_PED_CONFIG_FLAG(pedTracey, PCF_OpenDoorArmIK, TRUE)
bAtHomeCommentDone = FALSE
// Do we need the first person flash?
IF IS_PLAYER_IN_FIRST_PERSON_CAMERA()
bFPFlashNeeded = TRUE
bFPFlashTimerStarted = FALSE
SET_FIRST_PERSON_FLASH_EFFECT_VEHICLE_MODEL_NAME("ISSI2")
ELSE
bFPFlashNeeded = FALSE
bFPFlashTimerStarted = FALSE
ENDIF
// Disable phone etc.
RC_START_CUTSCENE_MODE(<<0.0,0.0,0.0>>)
// Clear area of vehicles - note we can't use the standard resolves because they will move the player vehicle
CLEAR_AREA_OF_VEHICLES(vecReturnLocation, 8.0, TRUE) // Clear locate area
IF NOT bGoingToHotel
CLEAR_AREA_OF_VEHICLES(<<-820.2315, 176.7027, 70.6122>>, 8.0, TRUE) // Don't block up the front door
ENDIF
IF bGoingToHotel
iCutTimer = GET_GAME_TIMER() + 10000
ciHomeCam = CREATE_CAM_WITH_PARAMS("DEFAULT_SCRIPTED_CAMERA", <<-1315.6117, 402.8979, 76.0077>>, <<-26.8270, -0.0000, 175.0236>>, 48.4)
ELSE
iCutTimer = GET_GAME_TIMER() + 5000
ciHomeCam = CREATE_CAM_WITH_PARAMS("DEFAULT_SCRIPTED_CAMERA", << -840.7, 185.3, 78.4 >>, << -14.0, 0.0, -110.4 >>, 48.4)
ENDIF
SET_CAM_ACTIVE(ciHomeCam, TRUE)
RENDER_SCRIPT_CAMS(TRUE, FALSE)
bOutroWasSkipped = FALSE
sProgress = SP_RUNNING
ENDIF
BREAK
CASE SP_RUNNING
IF IS_CUTSCENE_SKIP_BUTTON_JUST_PRESSED_WITH_DELAY()
bOutroWasSkipped = TRUE
bAtHomeCommentDone = TRUE
ENDIF
IF NOT bAtHomeCommentDone
IF CREATE_CONVERSATION(mConversationStruct, "MET1AUD", "MET1_ATHOME", CONV_PRIORITY_MEDIUM)
OR GET_GAME_TIMER() > iCutTimer
bAtHomeCommentDone = TRUE
ENDIF
ELIF bFPFlashTimerStarted
IF GET_GAME_TIMER() >= iFPFlashTimer
sProgress = SP_CLEANUP
ENDIF
ELIF IS_ENTITY_IN_RANGE_COORDS(pedTracey, vecTraceyDest, 2.0)
OR GET_GAME_TIMER() - iCutTimer > 5000 // 10s get-out clause
OR bOutroWasSkipped
IF bFPFlashNeeded
CPRINTLN(DEBUG_MISSION, "REQUESTED FPS FLASH")
ANIMPOSTFX_PLAY("CamPushInNeutral", 0, FALSE)
PLAY_SOUND_FRONTEND(-1, "1st_Person_Transition", "PLAYER_SWITCH_CUSTOM_SOUNDSET")
bFPFlashTimerStarted = TRUE
iFPFlashTimer = GET_GAME_TIMER() + 300
ELSE
sProgress = SP_CLEANUP
ENDIF
ENDIF
IF NOT bGoingToHotel
SET_PED_RESET_FLAG(pedTracey, PRF_UseProbeSlopeStairsDetection, TRUE)
ENDIF
BREAK
CASE SP_CLEANUP
// Sort Tracey out if cutscene was skipped
IF bGoingToHotel
SAFE_DELETE_PED(pedTracey)
ELSE
IF bOutroWasSkipped
CLEAR_PED_TASKS_IMMEDIATELY(pedTracey)
SET_ENTITY_COORDS(pedTracey, <<-813.7761, 179.4943, 71.1592>>, FALSE)
FORCE_PED_AI_AND_ANIMATION_UPDATE(pedTracey)
ENDIF
IF IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
KILL_FACE_TO_FACE_CONVERSATION_DO_NOT_FINISH_LAST_LINE()
ENDIF
SET_VEHICLE_DOOR_SHUT(viTraceyCar, SC_DOOR_FRONT_RIGHT, TRUE)
RELEASE_PED_TO_FAMILY_SCENE(pedTracey)
ENDIF
// Restore hud etc. + Enable phone etc.
RENDER_SCRIPT_CAMS(FALSE, FALSE)
RC_END_CUTSCENE_MODE()
SET_GAMEPLAY_CAM_RELATIVE_HEADING()
DESTROY_CAM(ciHomeCam)
Mission_Passed()
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Mission stage proc
/// Player has taken too long to get to Tracey. Phone call of Tracey giving Michael shit.
PROC STAGE_FAIL_CALL()
SWITCH sProgress
CASE SP_SETUP
bTraceyIsCurrentlySpawned = FALSE
SAFE_REMOVE_BLIP(biBlip)
SAFE_DELETE_PED(pedTracey)
SAFE_DELETE_VEHICLE(viTraceyCar)
REMOVE_PED_FOR_DIALOGUE(mConversationStruct, 3)
ADD_PED_FOR_DIALOGUE(mConversationStruct, 3, NULL, "TRACEY")
IF CHAR_CALL_PLAYER_CELLPHONE_FORCE_ANSWER(mConversationStruct, CHAR_TRACEY, "MET1AUD", "MET1_FAIL", CONV_PRIORITY_MEDIUM)
sProgress = SP_RUNNING
ENDIF
BREAK
CASE SP_RUNNING
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
sProgress = SP_CLEANUP
ENDIF
BREAK
CASE SP_CLEANUP
sFailReason = "MET1TOOLATE"
msTrack = MST_FAIL_FADE
sProgress = SP_SETUP
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Mission stage proc
/// Handle player becoming wanted en route to Tracey
PROC STAGE_LOSE_WANTED()
SWITCH sProgress
CASE SP_SETUP
// Check we're supposed to be in here then progress
IF GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) > 0
DISPLAY_MISSION_OBJECTIVE(MO_MET1WANT)
SAFE_REMOVE_BLIP(biBlip)
sProgress = SP_RUNNING
ELSE
msTrack = MST_DRIVE_TO_TRACEY
sProgress = SP_SETUP
ENDIF
BREAK
CASE SP_RUNNING
// Check player is still wanted
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
IF GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) < 1
CLEAR_MISSION_OBJECTIVE(MO_MET1WANT)
sProgress = SP_CLEANUP
ENDIF
ENDIF
// Check for Tracey timeout calls
CHECK_WARNING_CALL()
BREAK
CASE SP_CLEANUP
sProgress = SP_SETUP
msTrack = MST_DRIVE_TO_TRACEY
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Mission stage proc
/// Handles a fade-out/clean-up step for mission fail replay cleanliness
PROC STAGE_FAIL_FADE()
SWITCH sProgress
CASE SP_SETUP
CPRINTLN(DEBUG_MISSION, "Mission_Failed")
SAFE_REMOVE_BLIP(biBlip)
IF IS_STRING_NULL_OR_EMPTY(sFailReason)
// Guard against null string case
SCRIPT_ASSERT("Reached STAGE_FAIL_FADE with NULL/empty fail reason string - Please add detailed repro steps when bugging this.")
sFailReason = "DEFAULT"
ENDIF
IF ARE_STRINGS_EQUAL(sFailReason, "DEFAULT")
Mission_Flow_Mission_Failed()
ELSE
MISSION_FLOW_MISSION_FAILED_WITH_REASON(sFailReason)
ENDIF
sProgress = SP_RUNNING
BREAK
CASE SP_RUNNING
IF GET_MISSION_FLOW_SAFE_TO_CLEANUP()
// finished fading out
// must only take 1 frame and terminate the thread
// check if we need to respawn the player in a different position,
// if so call MISSION_FLOW_SET_FAIL_WARP_LOCATION() + SET_REPLAY_DECLINED_VEHICLE_WARP_LOCATION here
Mission_Cleanup(TRUE) // Deletes everything
ELSE
KILL_FACE_TO_FACE_CONVERSATION_DO_NOT_FINISH_LAST_LINE() // B*1118621
// not finished fading out
// you may want to handle dialogue etc here.
ENDIF
BREAK
ENDSWITCH
ENDPROC
// ===========================================================================================================
// DEBUG FUNCTIONS
// ===========================================================================================================
/// PURPOSE:
/// Handles debug keys
PROC DO_DEBUG_CHECKS()
#IF IS_DEBUG_BUILD
INT iNewStage
// Check for Pass
IF (IS_KEYBOARD_KEY_JUST_PRESSED(KEY_S))
SKIP_KILL_CONVERSATION()
CLEAR_PRINTS()
Mission_Passed(TRUE)
ELIF (IS_KEYBOARD_KEY_JUST_PRESSED(KEY_F))
SKIP_KILL_CONVERSATION()
CLEAR_PRINTS()
SET_MISSION_FAILED("DEFAULT")
ELIF (IS_KEYBOARD_KEY_JUST_PRESSED(KEY_J))
SKIP_FORWARD()
ELIF (IS_KEYBOARD_KEY_JUST_PRESSED(KEY_P))
SKIP_BACKWARD()
ELIF LAUNCH_MISSION_STAGE_MENU(mSkipMenu, iNewStage)
eTargetStage = INT_TO_ENUM(MISSION_SKIP_STAGE, iNewStage)
DO_Z_SKIP(eTargetStage)
ENDIF
// Stalker debug flip widget
IF bDebugFlipStalkerCar
bDebugFlipStalkerCar = FALSE
IF IS_ENTITY_ALIVE(mvStalkerCar.vehicle)
CPRINTLN(DEBUG_MISSION, "DEBUG FLIP STALKER CAR")
VECTOR vecStalkCoord = GET_ENTITY_COORDS(mvStalkerCar.vehicle)
VECTOR vecStalkRot = GET_ENTITY_ROTATION(mvStalkerCar.vehicle)
vecStalkCoord.z += 1.0
vecStalkRot.y = WRAP(vecStalkRot.y+180, 0, 360)
SET_ENTITY_COORDS(mvStalkerCar.vehicle, vecStalkCoord)
SET_ENTITY_ROTATION(mvStalkerCar.vehicle, vecStalkRot)
SET_ENTITY_VELOCITY(mvStalkerCar.vehicle, <<0,0,0>>)
ENDIF
ENDIF
#ENDIF
ENDPROC
// ===========================================================================================================
// Script Loop
// ===========================================================================================================
SCRIPT
CPRINTLN(DEBUG_MISSION, "...ME_Tracey1 Launched")
IF (HAS_FORCE_CLEANUP_OCCURRED())
CPRINTLN(DEBUG_MISSION, "...ME_Tracey1 Force Cleanup")
Mission_Flow_Mission_Force_Cleanup()
Mission_Cleanup()
ENDIF
SET_MISSION_FLAG(TRUE)
DATA_INIT()
// Check whether this is a replay
IF Is_Replay_In_Progress()
SWITCH GET_REPLAY_MID_MISSION_STAGE()
CASE 0
// Create a car near Michael, B* 993260
VEHICLE_INDEX viTemp
CREATE_VEHICLE_FOR_PHONECALL_TRIGGER_REPLAY(viTemp, TRUE, TRUE, TRUE, TRUE, TAILGATER)
SAFE_RELEASE_VEHICLE(viTemp)
RC_END_Z_SKIP()
BREAK
CASE CP_MET_TRACEY
RC_START_Z_SKIP()
SKIP_TO_MEET_TRACEY(TRUE)
RC_END_Z_SKIP()
BREAK
CASE CP_STALKER_APPEARS
RC_START_Z_SKIP()
SKIP_TO_STALKER_APPEARS(TRUE)
RC_END_Z_SKIP()
BREAK
CASE CP_DRIVE_HOME
RC_START_Z_SKIP()
SKIP_TO_DRIVE_HOME(TRUE, TRUE)
RC_END_Z_SKIP()
BREAK
CASE CP_DRIVE_HOME_DEAD
RC_START_Z_SKIP()
SKIP_TO_DRIVE_HOME(TRUE, FALSE)
RC_END_Z_SKIP()
BREAK
DEFAULT
RC_END_Z_SKIP()
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("M_DD")
IF iDisableReplayCameraTimer > GET_GAME_TIMER()
REPLAY_DISABLE_CAMERA_MOVEMENT_THIS_FRAME() //Fix for bug 2227778
ENDIF
IF msTrack = MST_FAIL_FADE
STAGE_FAIL_FADE()
ELSE
IF NOT MISSION_FAIL_CHECKS()
DO_DEBUG_CHECKS()
MONITOR_STALKER_SNUFF_MOVIE()
SWITCH msTrack
CASE MST_INIT
STAGE_INIT()
BREAK
CASE MST_DRIVE_TO_TRACEY
STAGE_DRIVE_TO_TRACEY()
BREAK
CASE MST_WAIT_FOR_PLAYER
STAGE_WAIT_FOR_PLAYER()
BREAK
CASE MST_DRIVE_TO_STALKER
STAGE_DRIVE_TO_STALKER()
BREAK
CASE MST_TRIGGER_STALKER
STAGE_TRIGGER_STALKER()
BREAK
CASE MST_CHASE
STAGE_CHASE()
BREAK
CASE MST_STALKER_WRECKED
STAGE_STALKER_WRECKED()
BREAK
CASE MST_DRIVE_HOME
STAGE_DRIVE_HOME()
BREAK
CASE MST_AT_HOME
STAGE_AT_HOME()
BREAK
CASE MST_FAIL_CALL
STAGE_FAIL_CALL()
BREAK
// CASE MST_FAIL_TRACEY_DRIVE_OFF
// STAGE_FAIL_TRACEY_DRIVE_OFF()
// BREAK
CASE MST_LOSE_WANTED
STAGE_LOSE_WANTED()
BREAK
ENDSWITCH
ENDIF
ENDIF
WAIT(0)
ENDWHILE
ENDSCRIPT