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

4377 lines
166 KiB
XML
Executable File

//Includes
USING "rage_builtins.sch"
USING "globals.sch"
USING "cutscene_public.sch"
USING "commands_brains.sch"
USING "commands_cutscene.sch"
USING "commands_entity.sch"
USING "commands_script.sch"
USING "common_Races.sch"
USING "flow_help_public.sch"
USING "minigames_helpers.sch"
USING "net_mission.sch"
USING "script_heist.sch"
USING "shared_hud_displays.sch"
USING "track_Data.sch"
USING "rgeneral_include.sch"
USING "RC_Helper_Functions.sch"
USING "RC_Threat_Public.sch"
USING "Race_Results.sch"
USING "net_common_functions.sch"
USING "commands_misc.sch"
// *****************************************************************************************
// *****************************************************************************************
// *****************************************************************************************
//
// SCRIPT NAME : Race_Control.sch
// AUTHOR : Joe Binks
// DESCRIPTION : All the functions for running city and sea races
//
// *****************************************************************************************
// *****************************************************************************************
// *****************************************************************************************
// ===========================================================================================================
// Enums and Structs
// ===========================================================================================================
ENUM FAIL_REASON
FAIL_DEFAULT,
FAIL_RIVAL_ATTACKED,
FAIL_RIVAL_DEAD,
FAIL_WRECKED,
FAIL_ABANDONED_RACE,
FAIL_MISSED_CHECKPOINT,
FAIL_ALERTED_COPS
ENDENUM
ENUM RACE_STATE
RACE_STATE_LINEUP,
RACE_STATE_COUNTDOWN,
RACE_STATE_ACTIVE,
RACE_STATE_END,
RACE_STATE_REWARD,
RACE_STATE_FAIL
ENDENUM
ENUM RACE_SUBSTATE
RACE_SUBSTATE_SETUP,
RACE_SUBSTATE_RUNNING,
RACE_SUBSTATE_CLEANUP
ENDENUM
ENUM AI_RACE_STATE
AI_RACER_INIT,
AI_RACER_WAIT_FOR_BURNOUT_FINISHED,
AI_RACER_ACTIVE,
AI_RACER_FINISHED_RACE
ENDENUM
ENUM AI_DRIVING_SPEED
AI_DRIVING_SPEED_SLOW,
AI_DRIVING_SPEED_MEDIUM,
AI_DRIVING_SPEED_FAST
ENDENUM
ENUM RACES_RESPAWN_STATE
RACES_RESPAWN_SET,
RACES_RESPAWN_INIT,
RACES_RESPAWN_CHECK,
RACES_RESPAWN_ACTIVE,
RACES_RESPAWN_WAIT
ENDENUM
ENUM LAP_TEXT_STATE
LTS_READY,
LTS_LOAD,
LTS_INIT,
LTS_DISPLAY,
LTS_CLEANUP
ENDENUM
ENUM BOOST_STATE
BS_READY,
BS_ACTIVE,
BS_FINISHED
ENDENUM
ENUM AI_WARP_STATE
AWS_READY,
AWS_WARPING,
AWS_FINISHED
ENDENUM
// AI Racer
STRUCT AI_RACER
AI_RACE_STATE stateAI = AI_RACER_INIT
AI_DRIVING_SPEED drivingSpeed = AI_DRIVING_SPEED_MEDIUM
FLOAT fLastSpeed
PED_INDEX piDriver
MODEL_NAMES modelPed
MODEL_NAMES modelCar
VEHICLE_INDEX viCar
BLIP_INDEX biBlip
INT iCurrentPoint = 0
INT iCurrentLap = 1
BOOL gGivenGetOnVehTask = FALSE
//BOOL bWarping = FALSE
BOOST_STATE eBoostState = BS_READY
INT iBoostTimer
BOOL bReadyToLook = TRUE
INT iLookAtTimer
AI_WARP_STATE eWarpState = AWS_READY
INT iWarpWaypoint = 0
INT iWarpTimer = 0
ENDSTRUCT
STRUCT RACE_HUD
COUNTDOWN_UI uiCountdown
INT iStartTime = 0
INT iCurrentCheckPoint = 0
INT iMaxCheckpoints = 0
INT iCurrentLap = 0
INT iCurrentTime = 0
INT iBestTime = -1
INT iPlayerPosition = 0
ENDSTRUCT
// ===========================================================================================================
// Race Cleanup
// ===========================================================================================================
FAIL_REASON failReason = FAIL_DEFAULT
RACE_STATE eRaceState
RACE_SUBSTATE eRaceSubState
RACES_RESPAWN_STATE racesRespawnState
BOOST_STATE eBoostState = BS_READY
INT iBoostTimer = -1
CONST_INT BOOST_START_WINDOW 500
CONST_INT BOOST_TIME 2000
CONST_FLOAT WARP_DISTANCE 40.0
CONST_FLOAT STOP_WARP_DISTANCE 30.0
INT iReplayRecordWarpTimer //Fix for bug 2226208
RACE_DATA sRaceData // Race information
RACE_HUD sRaceHUD
TRACK_DATA sTrackData
AI_RACER sRacer[MAX_AI_RACERS] // AI Racers
BOOL bRacersReleased // Handles if the racers have been allowed to start driving off
CONST_INT BURNOUT_SPREAD_TIME 100 // The AI racers due a burnout at the start, to help spread the grid at the start each racer does a successively longer burnout by this time
BOOL bLoopWaypointRecording
//structPedsForConversation conversationStruct
INT iPreviousPosition
INT iPositionConvTimer
CONST_INT POSITION_CONV_TIME 7500
// Deals with playing the stopped sound effect
INT iStoppedTimer = -1
INT iStoppedSound = -1
BLIP_INDEX biCurrentCheckPoint // Race markers
BLIP_INDEX biNextCheckPoint
CHECKPOINT_INDEX ciCurrentCheckPoint
CHECKPOINT_INDEX ciPreviousCheckPoint
INT iPrevAlpha
FLOAT fNextCheckpointDistance
INT iPrevClosestRacer = -1
INT iRaceMixTimer = 0
VEHICLE_INDEX viPlayerMain
CAMERA_INDEX gridCamera[8] // Camera panning shot
VECTOR vStartPos
VECTOR vStartRot
VECTOR vEndPos
VECTOR vEndRot
VECTOR vStartPos2
VECTOR vStartRot2
VECTOR vEndPos2
VECTOR vEndRot2
VECTOR vStartPos3
VECTOR vStartRot3
VECTOR vEndPos3
VECTOR vEndRot3
VECTOR vStartPos4
VECTOR vStartRot4
VECTOR vEndPos4
VECTOR vEndRot4
VECTOR vMidPos
VECTOR vMidRot
BOOL bPlayedIntroSound
INT iIntroSound = GET_SOUND_ID()
LAP_TEXT_STATE eLapTextState = LTS_READY
SCALEFORM_INDEX siLapText
CONST_INT LAP_TEXT_TIME (BIG_MESSAGE_DISPLAY_TIME)// + 3000)
INT iLapTextTimer
//SCENARIO_BLOCKING_INDEX scenarioBlockArea // World setup
RACE_RESULTS_STRUCT sRaceResults // struct used only by the race results screen
//MEGA_PLACEMENT_TOOLS mptRaceResultPlacement // Placement info for race results
SCALEFORM_INDEX uiLeaderboard[2]
INT iFinalRaceTime
CONST_INT END_SCREEN_TIME 30000 // how long to show the end camera and screen for
//INT iEndScreenTimer
CONST_INT QUIT_BUTTON_DELAY 2000
INT iQuitDelayTimer
//INT iSecondPlaceID = -1 // the ID of the second place AI racer, only used if the player wins
CONST_INT DPAD_DOWN_TIME 10000
//INT iDpadDownTimer = 0
INT iStuckHelpTimer
CONST_INT STUCK_HELP_TIME 1000
CONST_INT WARP_DIST_FROM_PLAYER 1//2 // How many checkpoints back from the player's current checkpoint should we warp them to
// Commenting out until the correct animations are available
PED_INDEX piGridGirlOne
PED_INDEX piGridGirlTwo
PED_INDEX piGridGirlThree
STRING sAnimDict = "random@street_race"
VECTOR vGridGirlOnePos
VECTOR vGridGirlTwoPos
VECTOR vGridGirlThreePos
FLOAT fGridGirlHeading
VECTOR vGridGirlTarget
//VECTOR vGridGirlCountPos
BOOL bGridGirlOneConvReady = TRUE
BOOL bGridGirlThreeConvReady = TRUE
// Variables for the end scene of the race
PED_INDEX piRewardPed
VECTOR vWinningCarPos
FLOAT fWinningCarHeading
// Commenting out for now in case this is needed again
/*VECTOR vAltCarPos
FLOAT fAltCarHeading*/
VECTOR vRewardPedPos
FLOAT fRewardPedHeading
INT iSpeedophileOutOfWaterTimer = -1
INT iCamTimer
/*CONST_INT END_SCENE_TIME 10000
INT iEndSceneTimer = 0*/
STRING sRaceWaypointRecording
SEQUENCE_INDEX seqGetInVehicle
// Disable road nodes near the race start
VECTOR vRaceRoadPos1 = <<0,0,0>>
VECTOR vRaceRoadPos2 = <<0,0,0>>
FLOAT fRaceRoadWidth = 0
SCENARIO_BLOCKING_INDEX sbiRaceStart
// Commenting out for now in case this is needed again
//BOOL bDeleteSecondPlace // Used in the cleanup to decide if the second place vehicle should be deleted or kept
BOOL bPlayerVehicleNeedsRecovering
INT iPlayerWantsToRecoverVehicleTimer
structPedsForConversation pedConvStruct
//BOOL bConversationStarted = FALSE
//INT iConvFailTimer = -1
INT iNumWaypoints
PROC AllowCompiling()
uiLeaderboard = uiLeaderboard
iQuitDelayTimer = iQuitDelayTimer
//vRewardDest = vRewardDest
//iEndScreenTimer = iEndScreenTimer
//bConversationStarted = bConversationStarted
eRaceSubState = eRaceSubState
iFinalRaceTime = iFinalRaceTime
//mptRaceResultPlacement = mptRaceResultPlacement
eRaceState = eRaceState
failReason = failReason
//iConvFailTimer = iConvFailTimer
vStartPos2 = vStartPos2
vStartRot2 = vStartRot2
vEndPos2 = vEndPos2
vEndRot2 = vEndRot2
vStartPos3 = vStartPos3
vStartRot3 = vStartRot3
vEndPos3 = vEndPos3
vEndRot3 = vEndRot3
vStartPos4 = vStartPos4
vStartRot4 = vStartRot4
vEndPos4 = vEndPos4
vEndRot4 = vEndRot4
vMidPos = vMidPos
vMidRot = vMidRot
ENDPROC
// ===========================================================================================================
// Race Cleanup
// ===========================================================================================================
/// PURPOSE: Remove street racers and their vehicles
PROC CLEANUP_RACERS(BOOL b_delete_racers)//, BOOL b_delete_second_place = TRUE)
INT i_index
SEQUENCE_INDEX siLeave
FOR i_index = 0 TO MAX_AI_RACERS-1
IF i_index < sTrackData.iNumAIRacers
SET_VEHICLE_MODEL_IS_SUPPRESSED(sRacer[i_index].modelCar, FALSE)
ENDIF
IF b_delete_racers = TRUE
//IF b_delete_second_place OR i_index <> iSecondPlaceID
IF DOES_ENTITY_EXIST(sRacer[i_index].piDriver)
DELETE_PED(sRacer[i_index].piDriver)
ENDIF
IF DOES_ENTITY_EXIST(sRacer[i_index].viCar)
DELETE_VEHICLE(sRacer[i_index].viCar)
ENDIF
//ENDIF
ELSE
IF DOES_ENTITY_EXIST(sRacer[i_index].piDriver)
IF NOT IS_ENTITY_DEAD(sRacer[i_index].piDriver)
SET_PED_KEEP_TASK(sRacer[i_index].piDriver, TRUE)
IF DOES_ENTITY_EXIST(sRacer[i_index].viCar)
AND NOT IS_ENTITY_DEAD(sRacer[i_index].viCar)
SET_ENTITY_ONLY_DAMAGED_BY_PLAYER(sRacer[i_index].piDriver, FALSE)
SET_ENTITY_LOAD_COLLISION_FLAG(sRacer[i_index].viCar, FALSE)
IF sRaceData.eRaceTrack = SEA_RACE_04
OPEN_SEQUENCE_TASK(siLeave)
//TASK_VEHICLE_DRIVE_TO_COORD(NULL, sRacer[i_index].viCar, <<418.8445, -2819.7339, 2.3325>>, 20, DRIVINGSTYLE_NORMAL, GET_ENTITY_MODEL(sRacer[i_index].viCar), DRIVINGMODE_STOPFORCARS, 10, 5)
TASK_VEHICLE_DRIVE_TO_COORD(NULL, sRacer[i_index].viCar, <<380.1007, -3498.0776, -29.3627>>, 20, DRIVINGSTYLE_NORMAL, GET_ENTITY_MODEL(sRacer[i_index].viCar), DRIVINGMODE_STOPFORCARS, 10, 5)
TASK_VEHICLE_DRIVE_WANDER(NULL, sRacer[i_index].viCar, 20, DRIVINGMODE_STOPFORCARS)
CLOSE_SEQUENCE_TASK(siLeave)
TASK_PERFORM_SEQUENCE(sRacer[i_index].piDriver, siLeave)
ELSE
TASK_VEHICLE_DRIVE_WANDER(sRacer[i_index].piDriver, sRacer[i_index].viCar, 20, DRIVINGMODE_STOPFORCARS)
ENDIF
ENDIF
ENDIF
SAFE_RELEASE_PED(sRacer[i_index].piDriver)
ENDIF
IF DOES_ENTITY_EXIST(sRacer[i_index].viCar)
IF NOT IS_ENTITY_DEAD(sRacer[i_index].viCar)
SET_ENTITY_ONLY_DAMAGED_BY_PLAYER(sRacer[i_index].viCar, FALSE)
SET_VEHICLE_STRONG(sRacer[i_index].viCar, FALSE)
SET_VEHICLE_HAS_STRONG_AXLES(sRacer[i_index].viCar, FALSE)
SET_VEHICLE_TYRES_CAN_BURST(sRacer[i_index].viCar, TRUE)
//SET_VEHICLE_CAN_DEFORM_WHEELS(sRacer[i_index].viCar, TRUE)
ENDIF
SET_VEHICLE_AS_NO_LONGER_NEEDED(sRacer[i_index].viCar)
ENDIF
ENDIF
ENDFOR
ENDPROC
/// PURPOSE:
/// Removes all the AI racers' blips
PROC CLEANUP_RACER_BLIPS()
INT i_index
FOR i_index = 0 TO MAX_AI_RACERS-1
SAFE_REMOVE_BLIP(sRacer[i_index].biBlip)
ENDFOR
ENDPROC
/// PURPOSE:
/// Removes the race checkpoints
PROC CLEANUP_RACE_CHECKPOINTS()
IF DOES_BLIP_EXIST(biCurrentCheckPoint)
REMOVE_BLIP(biCurrentCheckPoint)
ENDIF
IF DOES_BLIP_EXIST(biNextCheckPoint)
REMOVE_BLIP(biNextCheckPoint)
ENDIF
// Remove checkpoint
IF ciCurrentCheckPoint != NULL
DELETE_CHECKPOINT(ciCurrentCheckPoint)
ENDIF
ENDPROC
/// PURPOSE: Removes blips and checkpoints
PROC CLEANUP_CHECKPOINTS(BOOL b_delete_player_vehicle_blip = TRUE)
// Remove all blips
CLEANUP_RACE_CHECKPOINTS()
IF b_delete_player_vehicle_blip = TRUE
AND DOES_BLIP_EXIST(sPlayerVehicle.biPlayerVehicle)
REMOVE_BLIP(sPlayerVehicle.biPlayerVehicle)
ENDIF
INT i_index = 0
FOR i_index = 0 TO sTrackData.iNumAIRacers-1
SAFE_REMOVE_BLIP(sRacer[i_index].biBlip)
ENDFOR
ENDPROC
/// PURPOSE:
/// Updates the next race track
PROC CHECK_FOR_TRACK_UNLOCK()
BOOL bUnlockedNewTrack = FALSE
CC_CommID eTextMessageID = COMM_NONE
SWITCH sRaceData.eRaceTrack
CASE STREET_RACE_01
CPRINTLN(DEBUG_MISSION, "Mission Race: Completed STREET_RACE_LOS_SANTOS")
// Currently this should never get called as both Street Race 1 and 2
// are unlocked upon completion of RC Hao 1
IF NOT IS_BIT_SET(g_savedGlobals.sStreetRaceData.iRaceUnlocked, ENUM_TO_INT(STREET_RACE_CITY_CIRCUIT))
CPRINTLN(DEBUG_MISSION, "Mission Race: Unlocked STREET_RACE_CITY_CIRCUIT")
SET_BIT(g_savedGlobals.sStreetRaceData.iRaceUnlocked, ENUM_TO_INT(STREET_RACE_CITY_CIRCUIT))
bUnlockedNewTrack = TRUE
ENDIF
BREAK
CASE STREET_RACE_02
CPRINTLN(DEBUG_MISSION, "Mission Race: Completed STREET_RACE_CITY_CIRCUIT")
IF NOT IS_BIT_SET(g_savedGlobals.sStreetRaceData.iRaceUnlocked, ENUM_TO_INT(STREET_RACE_AIRPORT))
AND g_savedGlobals.sStreetRaceData.eRaceToUnlock <> STREET_RACE_AIRPORT
CPRINTLN(DEBUG_MISSION, "Mission Race: Preparing unlock text for STREET_RACE_AIRPORT")
g_savedGlobals.sStreetRaceData.eRaceToUnlock = STREET_RACE_AIRPORT
eTextMessageID = TEXT_STREET_RACE_AIRPORT
bUnlockedNewTrack = TRUE
ENDIF
BREAK
CASE STREET_RACE_04
CPRINTLN(DEBUG_MISSION, "Mission Race: Completed STREET_RACE_AIRPORT")
IF NOT IS_BIT_SET(g_savedGlobals.sStreetRaceData.iRaceUnlocked, ENUM_TO_INT(STREET_RACE_FREEWAY))
AND g_savedGlobals.sStreetRaceData.eRaceToUnlock <> STREET_RACE_FREEWAY
CPRINTLN(DEBUG_MISSION, "Mission Race: Preparing unlock text for STREET_RACE_FREEWAY")
g_savedGlobals.sStreetRaceData.eRaceToUnlock = STREET_RACE_FREEWAY
eTextMessageID = TEXT_STREET_RACE_FREEWAY
bUnlockedNewTrack = TRUE
ENDIF
BREAK
CASE STREET_RACE_05
CPRINTLN(DEBUG_MISSION, "Mission Race: Completed STREET_RACE_FREEWAY")
IF NOT IS_BIT_SET(g_savedGlobals.sStreetRaceData.iRaceUnlocked, ENUM_TO_INT(STREET_RACE_VESPUCCI_CANALS))
AND g_savedGlobals.sStreetRaceData.eRaceToUnlock <> STREET_RACE_VESPUCCI_CANALS
CPRINTLN(DEBUG_MISSION, "Mission Race: Preparing unlock text for STREET_RACE_VESPUCCI_CANALS")
g_savedGlobals.sStreetRaceData.eRaceToUnlock = STREET_RACE_VESPUCCI_CANALS
eTextMessageID = TEXT_STREET_RACE_CANALS
bUnlockedNewTrack = TRUE
ENDIF
BREAK
CASE STREET_RACE_06
CPRINTLN(DEBUG_MISSION, "Mission Race: Completed STREET_RACE_VESPUCCI_CANALS")
BREAK
ENDSWITCH
// Send text message and autosave when unlocking new track
IF bUnlockedNewTrack
IF eTextMessageID != COMM_NONE
CPRINTLN(DEBUG_MISSION, "Adding text to queue: ", GET_COMM_ID_DEBUG_STRING(eTextMessageID))
REGISTER_TEXT_MESSAGE_FROM_CHARACTER_TO_PLAYER(eTextMessageID, CT_FLOW, BIT_FRANKLIN, CHAR_HAO, 2880000, 10000, VID_BLANK, CID_UNLOCK_NEXT_STREET_RACE)
ENDIF
MAKE_AUTOSAVE_REQUEST()
ENDIF
ENDPROC
/// PURPOSE:
/// Check whether to make the race blip "gold"
PROC CHECK_FOR_MAP_BLIP_UPDATE()
SWITCH sRaceData.eRaceTrack
// Sea Races
CASE SEA_RACE_01
IF NOT IS_BIT_SET(g_savedGlobals.sSeaRaceData.iRaceWon, ENUM_TO_INT(SEA_RACE_EAST))
//SET_STATIC_BLIP_COLOUR(STATIC_BLIP_MINIGAME_SEA_RACE1, ENUM_TO_INT(BLIP_COLOUR_YELLOW))
SET_STATIC_BLIP_HAS_CHECKMARK(STATIC_BLIP_MINIGAME_SEA_RACE1, TRUE)
SET_BIT(g_savedGlobals.sSeaRaceData.iRaceWon, ENUM_TO_INT(SEA_RACE_EAST))
MAKE_AUTOSAVE_REQUEST()
ENDIF
BREAK
CASE SEA_RACE_02
IF NOT IS_BIT_SET(g_savedGlobals.sSeaRaceData.iRaceWon, ENUM_TO_INT(SEA_RACE_NORTH))
//SET_STATIC_BLIP_COLOUR(STATIC_BLIP_MINIGAME_SEA_RACE2, ENUM_TO_INT(BLIP_COLOUR_YELLOW))
SET_STATIC_BLIP_HAS_CHECKMARK(STATIC_BLIP_MINIGAME_SEA_RACE2, TRUE)
SET_BIT(g_savedGlobals.sSeaRaceData.iRaceWon, ENUM_TO_INT(SEA_RACE_NORTH))
MAKE_AUTOSAVE_REQUEST()
ENDIF
BREAK
CASE SEA_RACE_03
IF NOT IS_BIT_SET(g_savedGlobals.sSeaRaceData.iRaceWon, ENUM_TO_INT(SEA_RACE_CANYON))
//SET_STATIC_BLIP_COLOUR(STATIC_BLIP_MINIGAME_SEA_RACE3, ENUM_TO_INT(BLIP_COLOUR_YELLOW))
SET_STATIC_BLIP_HAS_CHECKMARK(STATIC_BLIP_MINIGAME_SEA_RACE3, TRUE)
SET_BIT(g_savedGlobals.sSeaRaceData.iRaceWon, ENUM_TO_INT(SEA_RACE_CANYON))
MAKE_AUTOSAVE_REQUEST()
ENDIF
BREAK
CASE SEA_RACE_04
IF NOT IS_BIT_SET(g_savedGlobals.sSeaRaceData.iRaceWon, ENUM_TO_INT(SEA_RACE_CITY))
//SET_STATIC_BLIP_COLOUR(STATIC_BLIP_MINIGAME_SEA_RACE4, ENUM_TO_INT(BLIP_COLOUR_YELLOW))
SET_STATIC_BLIP_HAS_CHECKMARK(STATIC_BLIP_MINIGAME_SEA_RACE4, TRUE)
SET_BIT(g_savedGlobals.sSeaRaceData.iRaceWon, ENUM_TO_INT(SEA_RACE_CITY))
MAKE_AUTOSAVE_REQUEST()
ENDIF
BREAK
// Street Races
CASE STREET_RACE_01
IF NOT IS_BIT_SET(g_savedGlobals.sStreetRaceData.iRaceWon, ENUM_TO_INT(STREET_RACE_LOS_SANTOS))
//SET_STATIC_BLIP_COLOUR(STATIC_BLIP_MINIGAME_STREET_RACE1, ENUM_TO_INT(BLIP_COLOUR_YELLOW))
SET_STATIC_BLIP_HAS_CHECKMARK(STATIC_BLIP_MINIGAME_STREET_RACE1, TRUE)
SET_BIT(g_savedGlobals.sStreetRaceData.iRaceWon, ENUM_TO_INT(STREET_RACE_LOS_SANTOS))
MAKE_AUTOSAVE_REQUEST()
ENDIF
BREAK
CASE STREET_RACE_02
IF NOT IS_BIT_SET(g_savedGlobals.sStreetRaceData.iRaceWon, ENUM_TO_INT(STREET_RACE_CITY_CIRCUIT))
//SET_STATIC_BLIP_COLOUR(STATIC_BLIP_MINIGAME_STREET_RACE2, ENUM_TO_INT(BLIP_COLOUR_YELLOW))
SET_STATIC_BLIP_HAS_CHECKMARK(STATIC_BLIP_MINIGAME_STREET_RACE2, TRUE)
SET_BIT(g_savedGlobals.sStreetRaceData.iRaceWon, ENUM_TO_INT(STREET_RACE_CITY_CIRCUIT))
MAKE_AUTOSAVE_REQUEST()
ENDIF
BREAK
CASE STREET_RACE_04
IF NOT IS_BIT_SET(g_savedGlobals.sStreetRaceData.iRaceWon, ENUM_TO_INT(STREET_RACE_AIRPORT))
//SET_STATIC_BLIP_COLOUR(STATIC_BLIP_MINIGAME_STREET_RACE4, ENUM_TO_INT(BLIP_COLOUR_YELLOW))
SET_STATIC_BLIP_HAS_CHECKMARK(STATIC_BLIP_MINIGAME_STREET_RACE4, TRUE)
SET_BIT(g_savedGlobals.sStreetRaceData.iRaceWon, ENUM_TO_INT(STREET_RACE_AIRPORT))
MAKE_AUTOSAVE_REQUEST()
ENDIF
BREAK
CASE STREET_RACE_05
IF NOT IS_BIT_SET(g_savedGlobals.sStreetRaceData.iRaceWon, ENUM_TO_INT(STREET_RACE_FREEWAY))
//SET_STATIC_BLIP_COLOUR(STATIC_BLIP_MINIGAME_STREET_RACE5, ENUM_TO_INT(BLIP_COLOUR_YELLOW))
SET_STATIC_BLIP_HAS_CHECKMARK(STATIC_BLIP_MINIGAME_STREET_RACE5, TRUE)
SET_BIT(g_savedGlobals.sStreetRaceData.iRaceWon, ENUM_TO_INT(STREET_RACE_FREEWAY))
MAKE_AUTOSAVE_REQUEST()
ENDIF
BREAK
CASE STREET_RACE_06
IF NOT IS_BIT_SET(g_savedGlobals.sStreetRaceData.iRaceWon, ENUM_TO_INT(STREET_RACE_VESPUCCI_CANALS))
//SET_STATIC_BLIP_COLOUR(STATIC_BLIP_MINIGAME_STREET_RACE6, ENUM_TO_INT(BLIP_COLOUR_YELLOW))
SET_STATIC_BLIP_HAS_CHECKMARK(STATIC_BLIP_MINIGAME_STREET_RACE6, TRUE)
SET_BIT(g_savedGlobals.sStreetRaceData.iRaceWon, ENUM_TO_INT(STREET_RACE_VESPUCCI_CANALS))
MAKE_AUTOSAVE_REQUEST()
ENDIF
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE: Handles loading/unloading rival ped and vehicle models
PROC LOAD_RIVAL_MODELS(BOOL b_load_models)
/*IF b_load_models = TRUE
AND IS_ENTITY_ALIVE(sPlayerVehicle.vehPlayerVehicle)
CPRINTLN(DEBUG_MISSION, "Mission Race: Player car max speed ", GET_VEHICLE_ESTIMATED_MAX_SPEED(sPlayerVehicle.vehPlayerVehicle))
ENDIF*/
INT i_index
IF b_load_models = TRUE
FOR i_index = 0 TO sTrackData.iNumAIRacers - 1
IF sRacer[i_index].modelPed = DUMMY_MODEL_FOR_SCRIPT
OR sRacer[i_index].modelPed = DUMMY_MODEL_FOR_SCRIPT
SCRIPT_ASSERT("Replay in progress: Models haven't been specified for all rivals")
ENDIF
REQUEST_MODEL(sRacer[i_index].modelPed)
REQUEST_MODEL(sRacer[i_index].modelCar)
//CPRINTLN(DEBUG_MISSION, "Mission Race: Model ", i_index, " Max speed ", GET_VEHICLE_MODEL_ESTIMATED_MAX_SPEED(sRacer[i_index].modelCar))
ENDFOR
ELSE
FOR i_index = 0 TO sTrackData.iNumAIRacers - 1
SET_MODEL_AS_NO_LONGER_NEEDED(sRacer[i_index].modelPed)
SET_MODEL_AS_NO_LONGER_NEEDED(sRacer[i_index].modelCar)
ENDFOR
ENDIF
ENDPROC
/// PURPOSE:
/// Starts the race mixer and adds the AI racers to it
PROC START_RACE_MIXER()
IF sRaceData.eRaceType = RACETYPE_SEA
START_AUDIO_SCENE("SEA_RACE_DURING_RACE")
ELSE
START_AUDIO_SCENE("STREET_RACE_DURING_RACE")
ENDIF
INT i_index
FOR i_index = 0 TO sTrackData.iNumAIRacers - 1
IF IS_VEHICLE_OK(sRacer[i_index].viCar)
IF sRaceData.eRaceType = RACETYPE_SEA
ADD_ENTITY_TO_AUDIO_MIX_GROUP(sRacer[i_index].viCar,"SEA_RACE_DURING_RACE_NPC_GENERAL")
ELSE
ADD_ENTITY_TO_AUDIO_MIX_GROUP(sRacer[i_index].viCar,"STREET_RACE_NPC_GENERAL")
ENDIF
ENDIF
ENDFOR
ENDPROC
/// PURPOSE:
/// Adds the closest racer to the audio mixer and removes the previous closest
PROC ADD_CLOSEST_RACER_TO_MIX()
IF GET_GAME_TIMER() > iRaceMixTimer
FLOAT fDist
FLOAT fClosestDist = 9999999.9
INT iClosestRacer
INT i_index
FOR i_index = 0 TO sTrackData.iNumAIRacers - 1
IF IS_VEHICLE_OK(sRacer[i_index].viCar)
fDist = GET_DISTANCE_BETWEEN_ENTITIES(PLAYER_PED_ID(), sRacer[i_index].viCar)
IF fDist < fClosestDist
fDist = fClosestDist
iClosestRacer = i_index
ENDIF
ENDIF
ENDFOR
IF sRaceData.eRaceType = RACETYPE_SEA
IF iPrevClosestRacer > -1 AND IS_VEHICLE_OK(sRacer[iPrevClosestRacer].viCar)
REMOVE_ENTITY_FROM_AUDIO_MIX_GROUP(sRacer[iPrevClosestRacer].viCar)
ADD_ENTITY_TO_AUDIO_MIX_GROUP(sRacer[iPrevClosestRacer].viCar,"SEA_RACE_DURING_RACE_NPC_GENERAL")
ENDIF
IF IS_VEHICLE_OK(sRacer[iClosestRacer].viCar)
ADD_ENTITY_TO_AUDIO_MIX_GROUP(sRacer[iClosestRacer].viCar,"SEA_RACE_DURING_RACE_COLSEST_NPC")
ENDIF
ELSE
IF iPrevClosestRacer > -1 AND IS_VEHICLE_OK(sRacer[iPrevClosestRacer].viCar)
REMOVE_ENTITY_FROM_AUDIO_MIX_GROUP(sRacer[iPrevClosestRacer].viCar)
ADD_ENTITY_TO_AUDIO_MIX_GROUP(sRacer[iPrevClosestRacer].viCar,"STREET_RACE_NPC_GENERAL")
ENDIF
IF IS_VEHICLE_OK(sRacer[iClosestRacer].viCar)
ADD_ENTITY_TO_AUDIO_MIX_GROUP(sRacer[iClosestRacer].viCar,"STREET_RACE_NPC_CLOSEST")
ENDIF
ENDIF
iPrevClosestRacer = iClosestRacer
iRaceMixTimer = GET_GAME_TIMER() + 2000
ENDIF
ENDPROC
/// PURPOSE: Cleanup and terminate script
PROC RACE_CLEANUP(BOOL b_delete_racers)
CPRINTLN(DEBUG_MISSION, "Mission Race: SCRIPT_CLEANUP")
SET_PLAYER_CONTROL(PLAYER_ID(), TRUE)
Stop_Finish_Cam(sRaceResults)
SET_INSTANCE_PRIORITY_HINT(INSTANCE_HINT_NONE)
// Unlock new track?
IF sRaceHUD.iPlayerPosition = 1 OR sRaceHUD.iPlayerPosition = 2 OR sRaceHUD.iPlayerPosition = 3
CHECK_FOR_TRACK_UNLOCK()
ENDIF
// Update map blip if required
IF sRaceHUD.iPlayerPosition = 1
CHECK_FOR_MAP_BLIP_UPDATE()
ENDIF
// Allow player to fly through windscreen
IF IS_PLAYER_PLAYING(PLAYER_ID())
SET_PED_CONFIG_FLAG(PLAYER_PED_ID(), PCF_WillFlyThroughWindscreen, TRUE)
ENDIF
IF IS_PED_UNINJURED(PLAYER_PED_ID())
SET_PED_HELMET(PLAYER_PED_ID(), TRUE)
ENDIF
// Restore peds
SET_REDUCE_PED_MODEL_BUDGET(FALSE)
SET_PED_POPULATION_BUDGET(3)
// Traffic and cops
SET_REDUCE_VEHICLE_MODEL_BUDGET(FALSE)
SET_VEHICLE_POPULATION_BUDGET(3)
SET_DISPATCH_COPS_FOR_PLAYER(PLAYER_ID(), TRUE)
SET_CREATE_RANDOM_COPS(TRUE)
SET_WANTED_LEVEL_MULTIPLIER(1)
SET_RANDOM_TRAINS(TRUE)
// Restore scenarios and vehicle gens
//REMOVE_SCENARIO_BLOCKING_AREA(scenarioBlockArea)
SET_SCENARIO_TYPE_ENABLED("DRIVE", TRUE)
SET_ALL_VEHICLE_GENERATORS_ACTIVE_IN_AREA(<<-7000.0, -7000.0, -100.0>>, <<7000.0, 7000.0, 100.0>>, TRUE)
// Remove checkpoints and street racers
CLEANUP_CHECKPOINTS()
CLEANUP_RACERS(b_delete_racers)
// Commenting out for now in case this is needed again
// CLEANUP_RACERS(FALSE)
// Unload all required rival models
// LOAD_RIVAL_MODELS(FALSE)
// Only try to remove the waypoint recording is it's been set
IF NOT IS_STRING_NULL_OR_EMPTY(sRaceWaypointRecording)
REMOVE_WAYPOINT_RECORDING(sRaceWaypointRecording)
ENDIF
// Remove the reward ped
SAFE_RELEASE_PED(piRewardPed)
SAFE_RELEASE_PED(piGridGirlOne)
SAFE_RELEASE_PED(piGridGirlTwo)
SAFE_RELEASE_PED(piGridGirlThree)
// Release stored player vehicle handle
IF DOES_ENTITY_EXIST(sPlayerVehicle.vehPlayerVehicle)
IF NOT IS_ENTITY_DEAD(sPlayerVehicle.vehPlayerVehicle)
SET_VEHICLE_STRONG(sPlayerVehicle.vehPlayerVehicle, FALSE)
SET_VEHICLE_HAS_STRONG_AXLES(sPlayerVehicle.vehPlayerVehicle, FALSE)
// In bike races set rivals as hard to knock off their bikes
//IF (sRaceData.eRaceType = RACETYPE_BIKE OR sRaceData.eRaceType = RACETYPE_SEA) AND IS_PED_UNINJURED(PLAYER_PED_ID()) AND IS_PLAYER_PLAYING(PLAYER_ID())
IF sRaceData.eRaceType = RACETYPE_SEA AND IS_PED_UNINJURED(PLAYER_PED_ID()) AND IS_PLAYER_PLAYING(PLAYER_ID())
SET_PED_CAN_BE_KNOCKED_OFF_VEHICLE(PLAYER_PED_ID(), KNOCKOFFVEHICLE_DEFAULT)
ENDIF
ENDIF
SAFE_RELEASE_VEHICLE(sPlayerVehicle.vehPlayerVehicle)
CPRINTLN(DEBUG_MISSION, "Mission Race: Released vehPlayerVehicle")
ENDIF
// Turn off the slipstream effect
SET_ENABLE_VEHICLE_SLIPSTREAMING(FALSE)
// Reset the show laps when pressing Dpad variable
g_Show_Lap_Dpad = FALSE
// Release audio banks loaded for the intro sound
IF sRaceData.eRaceType = RACETYPE_SEA
STOP_SOUND(iIntroSound)
RELEASE_SCRIPT_AUDIO_BANK()
ELSE
STOP_STREAM()
ENDIF
// Stop music
IF sRaceData.eRaceType = RACETYPE_SEA
TRIGGER_MUSIC_EVENT("MGSR_STOP")
ENDIF
ENDPROC
// ===========================================================================================================
// Helper Functions
// ===========================================================================================================
/// PURPOSE:
/// Checks if the player is in a suitable vehicle for the race
/// RETURNS:
/// TRUE if the player is in a suitable car
FUNC BOOL IS_PLAYER_IN_SUITABLE_RACE_CAR()
IF IS_PED_SITTING_IN_ANY_VEHICLE(PLAYER_PED_ID())
//VEHICLE_INDEX viPlayer = GET_PLAYERS_LAST_VEHICLE()
viPlayerMain = GET_PLAYERS_LAST_VEHICLE()
//IF IS_VEHICLE_OK(viPlayer)
IF IS_VEHICLE_OK(viPlayerMain)
//MODEL_NAMES mnPlayer = GET_ENTITY_MODEL(viPlayer)
MODEL_NAMES mnPlayer = GET_ENTITY_MODEL(viPlayerMain)
// General checks
IF IS_MODEL_POLICE_VEHICLE(mnPlayer)
//CPRINTLN(DEBUG_MISSION, "Player in police vehicle")
RETURN FALSE
ENDIF
// these vehicles aren't covered by the IS_MODEL_POLICE_VEHICLE function
IF mnPlayer = POLICE4
OR mnPlayer = POLICEOLD1
OR mnPlayer = POLICEOLD2
OR mnPlayer = FBI
OR mnPlayer = FBI2
OR mnPlayer = LGUARD
OR mnPlayer = SHERIFF
OR mnPlayer = SHERIFF2
//CPRINTLN(DEBUG_MISSION, "Player in police vehicle")
RETURN FALSE
ENDIF
IF IS_THIS_MODEL_A_BIKE(mnPlayer)
OR IS_THIS_MODEL_A_BOAT(mnPlayer)
OR IS_THIS_MODEL_A_HELI(mnPlayer)
OR IS_THIS_MODEL_A_PLANE(mnPlayer)
//CPRINTLN(DEBUG_MISSION, "Player not in car")
RETURN FALSE
ENDIF
// Race blacklist
INT iIndex
MODEL_NAMES mBlacklist[89]
mBlacklist[0] = AMBULANCE
mBlacklist[1] = BENSON
mBlacklist[2] = BIFF
mBlacklist[3] = BUS
mBlacklist[4] = FIRETRUK
mBlacklist[5] = FORKLIFT
mBlacklist[6] = MULE
mBlacklist[7] = MULE2
mBlacklist[8] = PACKER
mBlacklist[9] = PHANTOM
mBlacklist[10] = MOWER
mBlacklist[11] = STOCKADE
mBlacklist[12] = SQUALO
mBlacklist[13] = MAVERICK
mBlacklist[14] = POLMAV
mBlacklist[15] = AIRTUG
mBlacklist[16] = PRANGER
mBlacklist[17] = ANNIHILATOR
mBlacklist[18] = DINGHY
mBlacklist[19] = POLICE
mBlacklist[20] = RIPLEY
mBlacklist[21] = TRASH
mBlacklist[22] = BURRITO
mBlacklist[23] = PONY
mBlacklist[24] = SPEEDO
mBlacklist[25] = MARQUIS
mBlacklist[26] = SANCHEZ
mBlacklist[27] = AIRTUG
mBlacklist[28] = TACO
mBlacklist[29] = BARRACKS
mBlacklist[30] = ROMERO
mBlacklist[31] = BLAZER
mBlacklist[32] = BLAZER2
mBlacklist[33] = BODHI2
mBlacklist[34] = BOXVILLE2
mBlacklist[35] = BULLDOZER
mBlacklist[36] = CADDY
mBlacklist[37] = CADDY2
mBlacklist[38] = CAMPER
mBlacklist[39] = TIPTRUCK
mBlacklist[40] = TOURBUS
mBlacklist[41] = TOWTRUCK
mBlacklist[42] = TOWTRUCK2
mBlacklist[43] = TRACTOR
mBlacklist[44] = TRACTOR2
mBlacklist[45] = UTILLITRUCK
mBlacklist[46] = UTILLITRUCK2
mBlacklist[47] = UTILLITRUCK3
mBlacklist[48] = RATLOADER
mBlacklist[49] = DLOADER
mBlacklist[50] = DOCKTUG
mBlacklist[51] = DUMP
mBlacklist[52] = GBURRITO
mBlacklist[53] = HANDLER
mBlacklist[54] = HAULER
mBlacklist[55] = JOURNEY
mBlacklist[56] = RENTALBUS
mBlacklist[57] = MIXER
mBlacklist[58] = RHINO
mBlacklist[59] = CUTTER
mBlacklist[60] = POUNDER
mBlacklist[61] = TIPTRUCK2
mBlacklist[62] = MIXER2
mBlacklist[63] = RUBBLE
mBlacklist[64] = SCRAP
mBlacklist[65] = ARMYTANKER
mBlacklist[66] = BARRACKS2
mBlacklist[67] = AIRBUS
mBlacklist[68] = COACH
mBlacklist[69] = PBUS
mBlacklist[70] = RIOT
mBlacklist[71] = BOXVILLE3
mBlacklist[72] = STOCKADE3
mBlacklist[73] = FLATBED
mBlacklist[74] = BOXVILLE
mBlacklist[75] = BURRITO2
mBlacklist[76] = BURRITO3
mBlacklist[77] = BURRITO4
mBlacklist[78] = RUMPO
mBlacklist[79] = SPEEDO2
mBlacklist[80] = BLIMP
mBlacklist[81] = BLIMP2
mBlacklist[82] = SUBMERSIBLE
mBlacklist[83] = SUBMERSIBLE2
mBlacklist[84] = BLAZER3
mBlacklist[85] = PONY2
mBlacklist[86] = RUMPO2
mBlacklist[87] = TAXI
mBlacklist[88] = DUMMY_MODEL_FOR_SCRIPT
REPEAT COUNT_OF(mBlacklist) iIndex
IF mnPlayer = mBlacklist[iIndex]
//CPRINTLN(DEBUG_MISSION, "Player in unsuitable car")
RETURN FALSE
ENDIF
ENDREPEAT
//CPRINTLN(DEBUG_MISSION, "Player in OK car")
RETURN TRUE
ENDIF
ENDIF
//CPRINTLN(DEBUG_MISSION, "Player not in vehicle")
RETURN FALSE
ENDFUNC
/// PURPOSE: Is the player on a motorbike (not bicycle or quad bike)
FUNC BOOL IS_PLAYER_ON_SUITABLE_BIKE()
IF IS_PED_SITTING_IN_ANY_VEHICLE(PLAYER_PED_ID())
viPlayerMain = GET_PLAYERS_LAST_VEHICLE()
IF IS_VEHICLE_OK(viPlayerMain)
MODEL_NAMES model = GET_ENTITY_MODEL(viPlayerMain)
IF IS_THIS_MODEL_A_BIKE(model)
AND NOT IS_THIS_MODEL_A_BICYCLE(model)
AND NOT IS_MODEL_POLICE_VEHICLE(model)
RETURN TRUE
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE: Is the player in a race suitable sea vehicle
FUNC BOOL IS_PLAYER_IN_SUITABLE_SEA_VEHICLE()
IF IS_PED_SITTING_IN_ANY_VEHICLE(PLAYER_PED_ID())
viPlayerMain = GET_PLAYERS_LAST_VEHICLE()
IF IS_VEHICLE_OK(viPlayerMain)
MODEL_NAMES model = GET_ENTITY_MODEL(viPlayerMain)
IF model = SEASHARK
OR model = SEASHARK2
RETURN TRUE
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE: Fade out for the Races
PROC SAFE_RACE_FADE_OUT(INT iTime = 500, BOOL bWaitForFade = TRUE)
CPRINTLN(DEBUG_MISSION, "Mission Race: SAFE_RACE_FADE_OUT")
IF IS_SCREEN_FADED_IN()
IF NOT IS_SCREEN_FADING_OUT()
DO_SCREEN_FADE_OUT(iTime)
IF bWaitForFade
WHILE NOT IS_SCREEN_FADED_OUT()
WAIT(0)
ENDWHILE
ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE: Fade in for the Races
PROC SAFE_RACE_FADE_IN(INT iTime = 500, BOOL bWaitForFade = TRUE)
CPRINTLN(DEBUG_MISSION, "Mission Race: SAFE_RACE_FADE_IN")
IF IS_SCREEN_FADED_OUT()
IF NOT IS_SCREEN_FADING_IN()
DO_SCREEN_FADE_IN(iTime)
IF bWaitForFade
WHILE NOT IS_SCREEN_FADED_IN()
WAIT(0)
ENDWHILE
ENDIF
ENDIF
ENDIF
ENDPROC
PROC SetupGridGirlPositions()
SWITCH sRaceData.eRaceTrack
// South Los Santos
CASE STREET_RACE_01
vGridGirlOnePos = <<-155.646805,-1566.948730,33.993595>>
vGridGirlTwoPos = <<-153.63, -1563.70, 34.95>>
vGridGirlThreePos = <<-148.09, -1557.66, 34.75>>
fGridGirlHeading = -30.86
vGridGirlTarget = <<-141.65, -1550.75, 33.46>>
//vGridGirlCountPos = <<-163.53, -1564.02, 35.19>>
BREAK
// City Circuit
CASE STREET_RACE_02
vGridGirlOnePos = <<367.616058,312.457153,102.586121>>
vGridGirlTwoPos = <<368.454742,312.210846,102.567795>>//<<369.88, 312.04, 103.54>>
vGridGirlThreePos = <<375.17, 310.64, 103.44>>
fGridGirlHeading = -103.75
vGridGirlTarget = <<386.63, 307.77, 102.20>>
//vGridGirlCountPos = <<367.43, 320.07, 103.59>>
BREAK
// Airport
CASE STREET_RACE_04
vGridGirlOnePos = <<-813.803345,-2546.800781,12.800132>>
vGridGirlTwoPos = <<-814.429321,-2547.747559,12.800570>>//<<-814.614685,-2547.981445,12.800852>>//<<-814.88, -2549.98, 13.79>>
vGridGirlThreePos = <<-816.56, -2554.45, 13.77>>
fGridGirlHeading = 157.75
vGridGirlTarget = <<-819.68, -2562.68, 12.75>>
//vGridGirlCountPos = <<-805.14, -2545.79, 13.76>>
BREAK
// Freeway
CASE STREET_RACE_05
vGridGirlOnePos = <<777.396179,-1148.09,28.059130>>
vGridGirlTwoPos = <<778.631165,-1148.070068,28.008192>>//<<781.51, -1148.08, 28.93>>
vGridGirlThreePos = <<787.00, -1148.06, 28.92>>
fGridGirlHeading = -89.30
vGridGirlTarget = <<795.30, -1147.23, 27.98>>
//vGridGirlCountPos = <<779.19, -1139.34, 28.93>>
BREAK
ENDSWITCH
ENDPROC
PROC SETUP_GRID_GIRLS()
IF sRaceData.eRaceType = RACETYPE_CAR
REQUEST_MODEL(A_F_Y_Genhot_01)
REQUEST_ANIM_DICT(sAnimDict)
SetupGridGirlPositions()
WHILE NOT HAS_MODEL_LOADED(A_F_Y_Genhot_01)
WAIT(0)
ENDWHILE
WHILE NOT HAS_ANIM_DICT_LOADED(sAnimDict)
WAIT(0)
ENDWHILE
piGridGirlOne = CREATE_PED(PEDTYPE_SPECIAL, A_F_Y_Genhot_01, vGridGirlOnePos, fGridGirlHeading)
SET_ENTITY_HEADING_FACE_COORDS(piGridGirlOne, vGridGirlTarget)
piGridGirlTwo = CREATE_PED(PEDTYPE_SPECIAL, A_F_Y_Genhot_01, vGridGirlTwoPos, fGridGirlHeading)
SET_ENTITY_HEADING_FACE_COORDS(piGridGirlTwo, vGridGirlTarget)
piGridGirlThree = CREATE_PED(PEDTYPE_SPECIAL, A_F_Y_Genhot_01, vGridGirlThreePos, fGridGirlHeading)
SET_ENTITY_HEADING_FACE_COORDS(piGridGirlThree, vGridGirlTarget)
SEQUENCE_INDEX siGridGirl
TASK_PAUSE(piGridGirlOne, -1)
ADD_PED_FOR_DIALOGUE(pedConvStruct, 3, piGridGirlOne, "GIRL1")
bGridGirlOneConvReady = TRUE
VECTOR vDestination
vDestination = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(piGridGirlTwo, <<0,20,0>>)
OPEN_SEQUENCE_TASK(siGridGirl)
TASK_PLAY_ANIM(NULL, sAnimDict, "grid_girl_a", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_NOT_INTERRUPTABLE | AF_LOOPING)
TASK_GO_STRAIGHT_TO_COORD(NULL, vDestination, PEDMOVE_WALK)
TASK_WANDER_STANDARD(NULL)
CLOSE_SEQUENCE_TASK(siGridGirl)
TASK_PERFORM_SEQUENCE(piGridGirlTwo, siGridGirl)
CLEAR_SEQUENCE_TASK(siGridGirl)
vDestination = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(piGridGirlThree, <<0,15,0>>)
OPEN_SEQUENCE_TASK(siGridGirl)
TASK_PLAY_ANIM(NULL, sAnimDict, "grid_girl_b", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_NOT_INTERRUPTABLE | AF_LOOPING)
TASK_GO_STRAIGHT_TO_COORD(NULL, vDestination, PEDMOVE_WALK)
TASK_WANDER_STANDARD(NULL)
CLOSE_SEQUENCE_TASK(siGridGirl)
TASK_PERFORM_SEQUENCE(piGridGirlThree, siGridGirl)
CLEAR_SEQUENCE_TASK(siGridGirl)
ADD_PED_FOR_DIALOGUE(pedConvStruct, 4, piGridGirlThree, "GIRL2")
bGridGirlThreeConvReady = TRUE
SET_MODEL_AS_NO_LONGER_NEEDED(A_F_Y_Genhot_01)
ENDIF
ENDPROC
PROC SET_GRID_GIRLS_FOR_COUNTDOWN(FLOAT fStartPhase)
IF sRaceData.eRaceType = RACETYPE_CAR
IF IS_PED_UNINJURED(piGridGirlOne)
/*SET_ENTITY_COORDS_GROUNDED(piGridGirlOne, vGridGirlOnePos) //vGridGirlCountPos)
SET_ENTITY_HEADING(piGridGirlOne, fGridGirlHeading)*/
SEQUENCE_INDEX siGridGirl
OPEN_SEQUENCE_TASK(siGridGirl)
TASK_PLAY_ANIM(NULL, sAnimDict, "grid_girl_race_start", SLOW_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_NOT_INTERRUPTABLE, fStartPhase)
TASK_GO_STRAIGHT_TO_COORD(NULL, vGridGirlTarget, PEDMOVE_WALK)
TASK_WANDER_STANDARD(NULL)
CLOSE_SEQUENCE_TASK(siGridGirl)
TASK_PERFORM_SEQUENCE(piGridGirlOne, siGridGirl)
CLEAR_SEQUENCE_TASK(siGridGirl)
ENDIF
ENDIF
ENDPROC
PROC CLEANUP_GRID_GIRLS()
IF sRaceData.eRaceType = RACETYPE_CAR
SEQUENCE_INDEX siGridGirl
VECTOR vDestination
IF IS_PED_UNINJURED(piGridGirlTwo)
vDestination = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(piGridGirlTwo, <<0,20,0>>)
OPEN_SEQUENCE_TASK(siGridGirl)
TASK_GO_STRAIGHT_TO_COORD(NULL, vDestination, PEDMOVE_WALK)
TASK_WANDER_STANDARD(NULL)
CLOSE_SEQUENCE_TASK(siGridGirl)
TASK_PERFORM_SEQUENCE(piGridGirlTwo, siGridGirl)
CLEAR_SEQUENCE_TASK(siGridGirl)
ENDIF
IF IS_PED_UNINJURED(piGridGirlThree)
vDestination = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(piGridGirlThree, <<0,15,0>>)
OPEN_SEQUENCE_TASK(siGridGirl)
TASK_GO_STRAIGHT_TO_COORD(NULL, vDestination, PEDMOVE_WALK)
TASK_WANDER_STANDARD(NULL)
CLOSE_SEQUENCE_TASK(siGridGirl)
TASK_PERFORM_SEQUENCE(piGridGirlThree, siGridGirl)
CLEAR_SEQUENCE_TASK(siGridGirl)
ENDIF
SAFE_RELEASE_PED(piGridGirlOne)
SAFE_RELEASE_PED(piGridGirlTwo)
SAFE_RELEASE_PED(piGridGirlThree)
ENDIF
ENDPROC
PROC SETUP_SEA_RACERS_AT_END()
INT i_index
VECTOR vNewPos = GET_ENTITY_COORDS(PLAYER_PED_ID())
FLOAT fNewHead
SWITCH sRaceData.eRaceTrack
CASE SEA_RACE_01
fNewHead = 180.8171
BREAK
CASE SEA_RACE_02
fNewHead = 49.2605
BREAK
CASE SEA_RACE_03
fNewHead = 69.4601
BREAK
CASE SEA_RACE_04
fNewHead = 191.0019
BREAK
ENDSWITCH
FOR i_index = 0 TO sTrackData.iNumAIRacers - 1
IF IS_PED_UNINJURED(sRacer[i_index].piDriver) AND IS_VEHICLE_OK(sRacer[i_index].viCar)
CLEAR_PED_TASKS(sRacer[i_index].piDriver)
SET_ENTITY_HEADING(sRacer[i_index].piDriver, fNewHead)
SWITCH i_index
CASE 0
IF sRaceData.eRaceTrack = SEA_RACE_04
SET_ENTITY_COORDS(sRacer[i_index].viCar, vNewPos+<<10,-10,0>>)
ELSE
SET_ENTITY_COORDS(sRacer[i_index].viCar, vNewPos+<<10,0,0>>)
ENDIF
BREAK
CASE 1
IF sRaceData.eRaceTrack = SEA_RACE_04
SET_ENTITY_COORDS(sRacer[i_index].viCar, vNewPos+<<0,-10,0>>)
ELSE
SET_ENTITY_COORDS(sRacer[i_index].viCar, vNewPos+<<0,10,0>>)
ENDIF
BREAK
CASE 2
IF sRaceData.eRaceTrack = SEA_RACE_04
SET_ENTITY_COORDS(sRacer[i_index].viCar, vNewPos+<<-10,-12,0>>)
ELSE
SET_ENTITY_COORDS(sRacer[i_index].viCar, vNewPos+<<10,10,0>>)
ENDIF
BREAK
DEFAULT
SAFE_DELETE_PED(sRacer[i_index].piDriver)
SAFE_DELETE_VEHICLE(sRacer[i_index].viCar)
BREAK
ENDSWITCH
ENDIF
ENDFOR
ENDPROC
/// PURPOSE: Common setup when triggering the race cutscene
PROC START_RACE_CUTSCENE()
CPRINTLN(DEBUG_MISSION, "Mission Race: START_RACE_CUTSCENE")
// Disable player control
PLAYER_INDEX pId = GET_PLAYER_INDEX()
IF IS_PLAYER_PLAYING(pId)
// Stop player control
SET_PLAYER_CONTROL(pId, FALSE, SPC_REMOVE_EXPLOSIONS)
SET_PLAYER_CONTROL(pId, FALSE, SPC_REMOVE_PROJECTILES)
// Reset wanted level
SET_PLAYER_WANTED_LEVEL(pId,0)
SET_PLAYER_WANTED_LEVEL_NOW(pId)
// Stop anyone from attacking the player
SET_EVERYONE_IGNORE_PLAYER(pId, TRUE)
// Make player invisible as will be repositioned from cloud data
/*SET_ENTITY_VISIBLE(PLAYER_PED_ID(), FALSE)
// Get vehicle reference
IF DOES_ENTITY_EXIST(sPlayerVehicle.vehPlayerVehicle)
SET_ENTITY_VISIBLE(sPlayerVehicle.vehPlayerVehicle, FALSE)
ENDIF*/
ENDIF
// Setup systems for scripted cutscene
SET_SCRIPTS_SAFE_FOR_CUTSCENE(TRUE,DEFAULT,FALSE)
// Clear area around race location
CLEAR_AREA(GET_PLAYER_COORDS(pId), 50.0, TRUE)
CLEAR_AREA_OF_VEHICLES(GET_PLAYER_COORDS(pId), 500.0) // use a big area since ambient vehicles get turned off anyway
CLEAR_AREA_OF_PEDS(GET_PLAYER_COORDS(pId), 500.0)
SET_RANDOM_TRAINS(FALSE)
DELETE_ALL_TRAINS()
// Remove radar and HUD
DISPLAY_RADAR(FALSE)
DISPLAY_HUD(FALSE)
// Clear objective and help text
CLEAR_PRINTS()
CLEAR_HELP()
ENDPROC
/// PURPOSE: Restore player control and script systems
PROC END_RACE_CUTSCENE()
CPRINTLN(DEBUG_MISSION, "Mission Race: END_RACE_CUTSCENE")
PLAYER_INDEX pId = GET_PLAYER_INDEX()
SAFE_RELEASE_PED(piGridGirlOne)
SAFE_RELEASE_PED(piGridGirlTwo)
SAFE_RELEASE_PED(piGridGirlThree)
IF IS_PLAYER_PLAYING(pId)
SET_EVERYONE_IGNORE_PLAYER(pId, FALSE)
SET_PLAYER_CONTROL(pId, TRUE)
ENDIF
IF sRaceData.eRaceType = RACETYPE_BIKE OR sRaceData.eRaceType = RACETYPE_CAR
REMOVE_SCENARIO_BLOCKING_AREA(sbiRaceStart)
SET_ROADS_BACK_TO_ORIGINAL_IN_ANGLED_AREA(vRaceRoadPos1, vRaceRoadPos2, fRaceRoadWidth)
ENDIF
SET_SCRIPTS_SAFE_FOR_CUTSCENE(FALSE,DEFAULT,FALSE)
DISPLAY_RADAR(TRUE)
DISPLAY_HUD(TRUE)
CLEAR_HELP()
ENDPROC
PROC CHECK_PLAYER_SHOOTING_IN_COUNTDOWN()
IF IS_PED_UNINJURED(PLAYER_PED_ID()) AND IS_PED_SHOOTING(PLAYER_PED_ID())
AND IS_PED_ARMED(PLAYER_PED_ID(), WF_INCLUDE_GUN|WF_INCLUDE_PROJECTILE)
failReason = FAIL_RIVAL_ATTACKED
eRaceState = RACE_STATE_FAIL
eRaceSubState = RACE_SUBSTATE_SETUP
IF IS_PED_UNINJURED(piGridGirlOne)
TASK_SMART_FLEE_PED(piGridGirlOne, PLAYER_PED_ID(), 500, -1)
SET_PED_KEEP_TASK(piGridGirlOne, TRUE)
ENDIF
IF IS_PED_UNINJURED(piGridGirlTwo)
TASK_SMART_FLEE_PED(piGridGirlTwo, PLAYER_PED_ID(), 500, -1)
SET_PED_KEEP_TASK(piGridGirlTwo, TRUE)
ENDIF
IF IS_PED_UNINJURED(piGridGirlThree)
TASK_SMART_FLEE_PED(piGridGirlThree, PLAYER_PED_ID(), 500, -1)
SET_PED_KEEP_TASK(piGridGirlThree, TRUE)
ENDIF
SEQUENCE_INDEX siLeave
INT i_index = 0
REPEAT sTrackData.iNumAIRacers i_index
IF IS_PED_UNINJURED(sRacer[i_index].piDriver)
OPEN_SEQUENCE_TASK(siLeave)
TASK_PAUSE(NULL, i_index*60)
IF IS_VEHICLE_OK(sRacer[i_index].viCar)
AND IS_PED_IN_VEHICLE(sRacer[i_index].piDriver, sRacer[i_index].viCar)
TASK_VEHICLE_DRIVE_WANDER(NULL, sRacer[i_index].viCar, 50, DRIVINGMODE_AVOIDCARS_RECKLESS)
ELSE
TASK_SMART_FLEE_PED(NULL, PLAYER_PED_ID(), 500, -1)
ENDIF
CLOSE_SEQUENCE_TASK(siLeave)
TASK_PERFORM_SEQUENCE(sRacer[i_index].piDriver, siLeave)
CLEAR_SEQUENCE_TASK(siLeave)
SET_PED_KEEP_TASK(sRacer[i_index].piDriver, TRUE)
ENDIF
ENDREPEAT
IF IS_VEHICLE_OK(sPlayerVehicle.vehPlayerVehicle)
IF sRaceData.eRaceType = RACETYPE_SEA
SET_BOAT_ANCHOR(sPlayerVehicle.vehPlayerVehicle, FALSE)
ELSE
SET_VEHICLE_HANDBRAKE(sPlayerVehicle.vehPlayerVehicle, FALSE)
ENDIF
ENDIF
END_RACE_CUTSCENE()
ENDIF
ENDPROC
FUNC BOOL IS_RACER_OK(INT i_index)
IF DOES_ENTITY_EXIST(sRacer[i_index].piDriver)
IF IS_ENTITY_DEAD(sRacer[i_index].piDriver)
failReason = FAIL_RIVAL_DEAD
eRaceState = RACE_STATE_FAIL
eRaceSubState = RACE_SUBSTATE_SETUP
ELIF IS_PED_INJURED(sRacer[i_index].piDriver)
failReason = FAIL_RIVAL_DEAD
eRaceState = RACE_STATE_FAIL
eRaceSubState = RACE_SUBSTATE_SETUP
ELIF IS_PLAYER_SHOOTING_NEAR_PED(sRacer[i_index].piDriver)
failReason = FAIL_RIVAL_ATTACKED
eRaceState = RACE_STATE_FAIL
eRaceSubState = RACE_SUBSTATE_SETUP
ENDIF
ENDIF
IF DOES_ENTITY_EXIST(sRacer[i_index].viCar)
IF IS_ENTITY_DEAD(sRacer[i_index].viCar)
failReason = FAIL_RIVAL_DEAD
eRaceState = RACE_STATE_FAIL
eRaceSubState = RACE_SUBSTATE_SETUP
ELIF HAS_ENTITY_BEEN_DAMAGED_BY_ENTITY(sRacer[i_index].viCar, PLAYER_PED_ID(), FALSE)
failReason = FAIL_RIVAL_ATTACKED
eRaceState = RACE_STATE_FAIL
eRaceSubState = RACE_SUBSTATE_SETUP
ENDIF
ENDIF
IF IS_ENTITY_ALIVE(sRacer[i_index].piDriver)
AND IS_ENTITY_ALIVE(sRacer[i_index].viCar)
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
PROC SUPPRESS_LARGE_VEHICLES(BOOL bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(AIRBUS, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(BENSON, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(BIFF, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(COACH, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(FIRETRUK, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(FLATBED, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(MULE, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(MULE2, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(PACKER, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(POUNDER, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(RUBBLE, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(TIPTRUCK, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(TIPTRUCK2, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(TOURBUS, bSuppress)
SET_VEHICLE_MODEL_IS_SUPPRESSED(TRASH, bSuppress)
ENDPROC
/// PURPOSE:
/// Checks if the player is driving a large vehicle
/// RETURNS:
/// true if the player is driving a large vehicle
FUNC BOOL PLAYERS_CAR_IS_BIG()
IF IS_VEHICLE_OK(sPlayerVehicle.vehPlayerVehicle)
MODEL_NAMES mnCar = GET_ENTITY_MODEL(sPlayerVehicle.vehPlayerVehicle)
IF mnCar = BALLER
OR mnCar = BALLER2
OR mnCar = BISON
OR mnCar = BISON2
OR mnCar = BISON3
OR mnCar = BOBCATXL
OR mnCar = CAVALCADE
OR mnCar = CAVALCADE2
OR mnCar = CRUSADER
OR mnCar = DUBSTA
OR mnCar = DUBSTA2
OR mnCar = FQ2
OR mnCar = GRANGER
OR mnCar = GRESLEY
OR mnCar = LANDSTALKER
OR mnCar = MESA
OR mnCar = MESA2
OR mnCar = MESA3
OR mnCar = PATRIOT
OR mnCar = RADI
OR mnCar = RANCHERXL
OR mnCar = RANCHERXL2
OR mnCar = REBEL
OR mnCar = ROCOTO
OR mnCar = SADLER
OR mnCar = SADLER2
OR mnCar = SANDKING
OR mnCar = SANDKING2
OR mnCar = SEMINOLE
OR mnCar = SUPERD
OR mnCar = INT_TO_ENUM(MODEL_NAMES, GET_HASH_KEY("huntley"))
OR mnCar = INT_TO_ENUM(MODEL_NAMES, GET_HASH_KEY("DUBSTA3"))
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Checks if the player is driving a very large vehicle
/// RETURNS:
/// TRUE if the player is driving a very large vehicle
FUNC BOOL PLAYERS_CAR_IS_VERY_BIG()
IF IS_VEHICLE_OK(sPlayerVehicle.vehPlayerVehicle)
MODEL_NAMES mnCar = GET_ENTITY_MODEL(sPlayerVehicle.vehPlayerVehicle)
IF mnCar = INT_TO_ENUM(MODEL_NAMES, GET_HASH_KEY("MONSTER"))
OR mnCar = INT_TO_ENUM(MODEL_NAMES, GET_HASH_KEY("MARSHALL"))
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
// ===========================================================================================================
// Mission Functions
// ===========================================================================================================
/// PURPOSE: Setup camera shots for race lineup
PROC SETUP_RACE_CAMERAS()
IF sRaceData.eRaceType = RACETYPE_CAR
// Need to use different cameras based on what car the player is driving
IF PLAYERS_CAR_IS_BIG()
vStartPos = <<-0.7054, 6.3148, 2.0821>>
vStartRot = <<0.9495, 3.9011, 2.7419>>
vEndPos = <<-0.5670, 2.6724, 1.0077>>
vEndRot = <<0.0940, -0.2423, 0.7483>>
ELIF PLAYERS_CAR_IS_VERY_BIG()
vStartPos = <<-1.6831, 6.0271, 4.2228>>
vStartRot = <<-0.0245, 3.5705, 4.6855>>
vEndPos = <<-1.0281, 4.6872, 0.9868>>
vEndRot = <<-0.1850, 1.8112, 1.1221>>
ELSE
vStartPos = <<-1.5802, 5.8398, 2.2274>>
vStartRot = <<0.1970, 3.5006, 2.8356>>
vEndPos = <<-0.6678, 1.9740, 0.6876>>
vEndRot = <<0.1440, -0.9013, 0.4170>>
ENDIF
ENDIF
//Setup our cameras for the intro.
SWITCH sRaceData.eRaceTrack
// South Los Santos
CASE STREET_RACE_01
vStartPos2 = <<-159.215897,-1564.124756,34.616402>>
vStartRot2 = <<-0.073293,0.055000,-119.273537>>
vEndPos2 = <<-158.811478,-1563.368530,34.617287>>
vEndRot2 = <<-0.073293,0.055000,-123.730537>>
vStartPos3 = <<-156.860825,-1565.342407,35.709652>>
vStartRot3 = <<-8.040299,-0.000500,-49.006474>>
vEndPos3 = <<-155.176834,-1567.141479,35.709652>>
vEndRot3 = <<-8.040299,-0.000500,-41.346287>>
vStartPos4 = <<-142.312408,-1550.573608,34.580471>>
vStartRot4 = <<-2.928405,-0.000499,142.620285>>
vEndPos4 = <<-141.3225, -1546.8904, 34.7891>>
vEndRot4 = <<-2.8368, -0.0000, 139.8128>>
BREAK
// City Circuit
CASE STREET_RACE_02
vStartPos2 = <<368.3612, 317.4821, 102.8912>>
vStartRot2 = <<4.9194, 0.0225, -176.9747>>
vEndPos2 = <<369.0639, 317.4726, 102.8941>>
vEndRot2 = <<5.0420, 0.0225, 178.1396>>
vStartPos3 = <<367.0291, 314.8014, 104.3700>>
vStartRot3 = <<-7.4995, 0.0233, -111.5294>>
vEndPos3 = <<366.4452, 312.9081, 104.3692>>
vEndRot3 = <<-7.4995, 0.0233, -100.7122>>
vStartPos4 = <<386.8919, 308.6107, 103.2039>>
vStartRot4 = <<0.3447, 0.0472, 82.1591>>
vEndPos4 = <<390.2213, 309.4721, 103.7617>>//<<390.0258, 309.9036, 103.8237>>
vEndRot4 = <<-4.3057, 0.0299, 76.2809>>//<<-9.7627, 0.0472, 75.7797>>
BREAK
// Airport
CASE STREET_RACE_04
vStartPos2 = <<-809.5582, -2548.8481, 13.2619>>
vStartRot2 = <<6.3036, 0.0031, 78.7380>>
vEndPos2 = <<-809.6061, -2549.2407, 13.2619>>
vEndRot2 = <<3.5967, 0.0031, 75.5951>>
vStartPos3 = <<-811.1327, -2544.3936, 14.5064>>
vStartRot3 = <<-5.1571, 0.0031, 151.0646>>
vEndPos3 = <<-812.3164, -2543.7571, 14.5063>>
vEndRot3 = <<-5.1571, 0.0031, 154.2332>>
vStartPos4 = <<-819.8916, -2564.1191, 13.8366>>
vStartRot4 = <<-3.7349, 0.0039, -21.4233>>
vEndPos4 = <<-819.4815, -2566.3877, 14.1387>>
vEndRot4 = <<-5.6248, 0.0039, -24.2558>>
BREAK
// Freeway
CASE STREET_RACE_05
vStartPos2 = <<776.7724, -1143.7301, 28.3016>>
vStartRot2 = <<11.6472, -0.0085, -162.2849>>
vEndPos2 = <<778.1360, -1143.3521, 28.3014>>
vEndRot2 = <<10.4548, -0.0085, -170.7270>>
vStartPos3 = <<775.2812, -1146.7396, 29.6806>>
vStartRot3 = <<-6.3919, -0.0085, -92.6672>>
vEndPos3 = <<775.3398, -1148.5911, 29.6808>>
vEndRot3 = <<-6.3919, -0.0085, -84.5603>>
vStartPos4 = <<795.9290, -1146.4519, 29.5086>>
vStartRot4 = <<-2.9460, -0.0103, 96.9974>>
vEndPos4 = <<799.1780, -1144.6638, 29.6776>>
vEndRot4 = <<-3.7454, -0.0103, 96.6498>>
BREAK
// Vespucci Canals
CASE STREET_RACE_06
vStartPos = <<-1081.7926, -1162.1034, 6.1895>>//<<-1089.3351, -1169.4821, 5.6114>>//<<-0.7720, 4.3825, 3.2025>>//<<-2.8850, 7.6435, 0.1487>>
vStartRot = <<9.5571, 0.0247, 143.3313>>//<<7.2179, 0.0136, 150.2166>>//<<0.7560, 1.8015, 3.2625>>//<<-1.2368, 5.2159, 0.7739>>
vEndPos = <<-0.2995, 1.6679, 0.9952>>//<<-0.740853,1.714846,0.995416>>//<<-0.7403, 1.4276, 0.3739>>
vEndRot = <<0.7073, -1.1228, 0.5493>>//<<-0.0425, -1.4900, 0.3528>>
vStartPos2 = <<-1059.430298,-1154.966675,1.585626>>//<<-1060.2167, -1154.1942, 1.6801>>//<<-1061.4971, -1154.3755, 1.5735>>
vStartRot2 = <<5.101318,0.220638,46.046375>>//<<4.2472, 0.1153, 54.0298>>//<<7.5848, 0.0002, 49.8066>>
vEndPos2 = <<-1060.098877,-1155.653564,1.589301>>//<<-1061.1595, -1155.4127, 1.6868>>//<<-1062.5969, -1156.5006, 1.5319>>
vEndRot2 = <<5.101318,0.220639,55.210567>>//<<4.2472, 0.1153, 50.3485>>//<<7.6255, 0.0002, 46.0695>>
vStartPos3 = <<-1057.832642,-1153.639648,2.157057>>//<<-1058.7335, -1151.7834, 2.0384>>
vStartRot3 = <<-1.541381,0.155416,92.089836>>//<<4.6126, 0.1930, 111.5182>>
vEndPos3 = <<-1058.799072,-1151.694580,3.019048>>//<<-1059.2340, -1150.5060, 2.9722>>
vEndRot3 = <<-8.483372,0.155417,120.072861>>//<<-2.6296, 0.1930, 121.5018>>
vStartPos4 = <<-1073.324341,-1160.225464,2.467393>>//<<-1073.0204, -1158.7783, 2.1917>>
vStartRot4 = <<-9.078616,0.014872,-56.525623>>//<<5.2140, 0.0002, -55.0704>>
vEndPos4 = <<-1074.406738,-1161.540771,2.727543>>//<<-1074.1484, -1160.3064, 2.9714>>
vEndRot4 = <<-9.078616,0.014872,-56.525623>>//<<-8.1078, 0.0002, -56.6515>>
BREAK
// East Coast
CASE SEA_RACE_01
vStartPos = <<3071.705322,680.428650,-0.578093>>//<<3065.011719,655.342773,-1.230181>>//<< 3088.8801, 651.6200, 8.7519 >>
vStartRot = <<-14.997413,0.000000,169.470261>>//<<-14.041778,0.000000,173.785065>>//<< 0.6311, -1.0914, 54.0716 >>
vMidPos = <<3071.258057,678.948547,3.839843>>//<<3065.253662,654.307251,2.788802>>//<< 3088.8801, 651.6200, 8.7519 >>
vMidRot = <<-0.639399,0.000000,168.924576>>//<<0.727129,0.000000,173.297226>>//<< -16.2995, -1.0914, 129.8059 >>
vEndPos = <<3070.761230,677.424866,3.822088>>//<<3065.399658,653.291687,2.795851>>
vEndRot = <<-0.639399,-0.000000,169.174271>>//<<-0.507238,0.000000,176.951553>>
vStartPos2 = <<3074.792236,632.816650,2.242464>>
vStartRot2 = <<0.235991,0.000000,58.909027>>
vEndPos2 = <<3072.678711,629.829773,2.241469>>
vEndRot2 = <<0.235991,0.000000,32.618481>>
BREAK
// North East Coast
CASE SEA_RACE_02
vStartPos = <<3482.450684,5171.119629,-2.622287>>
vStartRot = <<-12.389264,0.000000,26.136166>>
vMidPos = <<3481.746582,5172.553711,6.654290>>//<<3481.746582,5172.553711,3.654290>>
vMidRot = <<-9.870142,-0.000000,26.136162>>//<<-5.870142,-0.000000,26.136162>>
vEndPos = <<3479.126709,5173.103027,6.487341>>//<<3479.126709,5173.103027,3.487341>>
vEndRot = <<-9.603036,-0.000000,21.927107>>//<<-3.603036,-0.000000,21.927107>>
vStartPos2 = <<3457.3281, 5187.8604, 4.6358>>//<<3457.3552, 5190.9644, 3.3545>>//<<3455.060059,5195.183105,4.763378>>//<<3455.060059,5195.183105,1.763378>>
vStartRot2 = <<-24.8126, 0.0397, -41.5662>>//<<-16.9467, -0.0000, -40.3688>>//<<-7.357910,-0.000000,-102.864853>>//<<-2.357910,-0.000000,-102.864853>>
vEndPos2 = <<3456.5105, 5197.8965, 3.9627>>//<<3458.0938, 5197.1948, 3.6239>>//<<3455.694580,5198.888672,4.791954>>//<<3455.694580,5198.888672,1.791954>>
vEndRot2 = <<-23.4737, 0.0397, -128.4928>>//<<-19.8012, 0.0000, -121.2162>>//<<-7.923511,-0.000000,-119.349350>>//<<-3.923511,-0.000000,-119.349350>>
BREAK
// Raton Canyon
CASE SEA_RACE_03
vStartPos = <<190.542801,3596.879395,28.210274>>
vStartRot = <<-16.612904,-0.000001,-17.123419>>
vMidPos = <<190.928116,3598.445557,33.460209>>
vMidRot = <<-6.513790,-0.000000,-20.806368>>
vEndPos = <<189.055481,3599.193604,33.459087>>
vEndRot = <<-6.431107,-0.000000,-24.993280>>
vStartPos2 = <<187.353561,3623.253174,31.377996>>
vStartRot2 = <<0.968793,-0.000000,-171.133331>>
vEndPos2 = <<189.636276,3623.848389,31.370586>>
vEndRot2 = <<0.968793,-0.000000,175.618408>>
BREAK
// Los Santos
CASE SEA_RACE_04
vStartPos = <<617.705261,-2150.886475,-6.053663>>
vStartRot = <<-12.894878,-0.000002,-21.272289>>
vMidPos = <<616.813843,-2149.222412,1.441951>>
vMidRot = <<5.644659,-0.000002,-24.942902>>
vEndPos = <<615.142029,-2147.595703,1.518046>>
vEndRot = <<3.451776,-0.000002,-29.075584>>
vStartPos2 = <<615.476135,-2124.191895,1.467611>>
vStartRot2 = <<0.143961,0.000000,-156.622635>>
vEndPos2 = <<617.970764,-2122.319092,1.464393>>
vEndRot2 = <<0.143961,0.000000,179.064117>>
BREAK
ENDSWITCH
// Need to change the last intro cameras if the player is using a very big vehicle
IF PLAYERS_CAR_IS_VERY_BIG()
vStartPos4 = <<-2.0797, -1.4090, 1.3288>>
vStartRot4 = <<-2.1875, 1.5807, 1.1049>>
vEndPos4 = <<-0.6528, -5.8087, 2.3170>>
vEndRot4 = <<-0.7557, -2.8423, 1.8806>>
ENDIF
ENDPROC
PROC CREATE_RACE_CAMERAS()
INT i_index = 0
REPEAT COUNT_OF(gridCamera) i_index
IF DOES_CAM_EXIST(gridCamera[i_index])
DESTROY_CAM(gridCamera[i_index])
ENDIF
ENDREPEAT
IF sRaceData.eRaceType = RACETYPE_SEA
gridCamera[0] = CREATE_CAMERA_WITH_PARAMS(CAMTYPE_SCRIPTED, vStartPos, vStartRot, 35.0)
SET_CAM_ACTIVE(gridCamera[0], TRUE)
RENDER_SCRIPT_CAMS(TRUE, FALSE, 0)
gridCamera[1] = CREATE_CAMERA_WITH_PARAMS(CAMTYPE_SCRIPTED, vMidPos, vMidRot, 35.0)
gridCamera[2] = CREATE_CAMERA_WITH_PARAMS(CAMTYPE_SCRIPTED, vEndPos, vEndRot, 35.0)
gridCamera[3] = CREATE_CAMERA_WITH_PARAMS(CAMTYPE_SCRIPTED, vStartPos2, vStartRot2, 50.0)
gridCamera[4] = CREATE_CAMERA_WITH_PARAMS(CAMTYPE_SCRIPTED, vEndPos2, vEndRot2, 50.0)
iCamTimer = GET_GAME_TIMER() + 250
ELSE
IF sRaceData.eRaceTrack = STREET_RACE_06
gridCamera[0] = CREATE_CAMERA_WITH_PARAMS(CAMTYPE_SCRIPTED, vStartPos, vStartRot, 40.0)
ELSE
gridCamera[0] = CREATE_CAM("DEFAULT_SCRIPTED_CAMERA")
SET_CAM_FOV(gridCamera[0], 40.0)
ATTACH_CAM_TO_ENTITY(gridCamera[0], sPlayerVehicle.vehPlayerVehicle, vStartPos)
POINT_CAM_AT_ENTITY(gridCamera[0], sPlayerVehicle.vehPlayerVehicle, vStartRot)
ENDIF
gridCamera[1] = CREATE_CAM("DEFAULT_SCRIPTED_CAMERA")
SET_CAM_FOV(gridCamera[1], 40.0)
ATTACH_CAM_TO_ENTITY(gridCamera[1], sPlayerVehicle.vehPlayerVehicle, vEndPos)
POINT_CAM_AT_ENTITY(gridCamera[1], sPlayerVehicle.vehPlayerVehicle, vEndRot)
SET_CAM_ACTIVE(gridCamera[0], TRUE)
SET_CAM_ACTIVE_WITH_INTERP(gridCamera[1],gridCamera[0],3000,GRAPH_TYPE_SIN_ACCEL_DECEL)
RENDER_SCRIPT_CAMS(TRUE, FALSE, 0)
SHAKE_CAM(gridCamera[0],"ROAD_VIBRATION_SHAKE",0.5)
SHAKE_CAM(gridCamera[1],"ROAD_VIBRATION_SHAKE",0.5)
gridCamera[2] = CREATE_CAMERA_WITH_PARAMS(CAMTYPE_SCRIPTED, vStartPos2, vStartRot2, 40.0)
gridCamera[3] = CREATE_CAMERA_WITH_PARAMS(CAMTYPE_SCRIPTED, vEndPos2, vEndRot2, 40.0)
gridCamera[4] = CREATE_CAMERA_WITH_PARAMS(CAMTYPE_SCRIPTED, vStartPos3, vStartRot3, 40.0)
gridCamera[5] = CREATE_CAMERA_WITH_PARAMS(CAMTYPE_SCRIPTED, vEndPos3, vEndRot3, 40.0)
// Very big vehicles need custom finish cameras that are vehicle relative
IF PLAYERS_CAR_IS_VERY_BIG()
gridCamera[6] = CREATE_CAM("DEFAULT_SCRIPTED_CAMERA")
SET_CAM_FOV(gridCamera[6], 50.0)
ATTACH_CAM_TO_ENTITY(gridCamera[6], sPlayerVehicle.vehPlayerVehicle, vStartPos4)
POINT_CAM_AT_ENTITY(gridCamera[6], sPlayerVehicle.vehPlayerVehicle, vStartRot4)
gridCamera[7] = CREATE_CAM("DEFAULT_SCRIPTED_CAMERA")
SET_CAM_FOV(gridCamera[7], 50.0)
ATTACH_CAM_TO_ENTITY(gridCamera[7], sPlayerVehicle.vehPlayerVehicle, vEndPos4)
POINT_CAM_AT_ENTITY(gridCamera[7], sPlayerVehicle.vehPlayerVehicle, vEndRot4)
ELSE
gridCamera[6] = CREATE_CAMERA_WITH_PARAMS(CAMTYPE_SCRIPTED, vStartPos4, vStartRot4, 50.0)
gridCamera[7] = CREATE_CAMERA_WITH_PARAMS(CAMTYPE_SCRIPTED, vEndPos4, vEndRot4, 50.0)
ENDIF
iCamTimer = -1
ENDIF
ENDPROC
/// PURPOSE: Plays the sound effect while the camera is interpolating, see 1218670 and 1224076
PROC PLAY_INTRO_SOUND(BOOL bPlaySound = TRUE)
IF bPlayedIntroSound = FALSE
IF sRaceData.eRaceType = RACETYPE_SEA
IF LOAD_STREAM("INTRO_STREAM","SEA_RACES_SOUNDSET")
//CPRINTLN(DEBUG_MISSION, "Street Race: Audio banks for intro sfx have loaded")
IF bPlaySound
CPRINTLN(DEBUG_MISSION, "Street Race: Audio banks for intro sfx have loaded, so playing intro sfx")
PLAY_STREAM_FRONTEND()
bPlayedIntroSound = TRUE
ENDIF
ENDIF
ELIF sRaceData.eRaceType = RACETYPE_BIKE
IF LOAD_STREAM("VESPUCCI_CANAL_INTRO","ROAD_RACE_SOUNDSET")
//CPRINTLN(DEBUG_MISSION, "Street Race: Audio banks for intro sfx have loaded")
IF bPlaySound
CPRINTLN(DEBUG_MISSION, "Street Race: Audio banks for intro sfx have loaded, so playing intro sfx")
PLAY_STREAM_FRONTEND()
bPlayedIntroSound = TRUE
ENDIF
ENDIF
ELSE
IF LOAD_STREAM("INTRO_STREAM","ROAD_RACE_SOUNDSET")
//CPRINTLN(DEBUG_MISSION, "Street Race: Audio banks for intro sfx have loaded")
IF bPlaySound
CPRINTLN(DEBUG_MISSION, "Street Race: Audio banks for intro sfx have loaded, so playing intro sfx")
PLAY_STREAM_FRONTEND()
bPlayedIntroSound = TRUE
ENDIF
ENDIF
ENDIF
ENDIF
ENDPROC
FUNC BOOL CONTROL_RACE_CAMERAS()
IF sRaceData.eRaceType = RACETYPE_SEA
IF DOES_CAM_EXIST(gridCamera[0])
IF GET_GAME_TIMER() > iCamTimer
SET_CAM_ACTIVE_WITH_INTERP(gridCamera[1],gridCamera[0],500,GRAPH_TYPE_LINEAR)
SET_CAM_MOTION_BLUR_STRENGTH(gridCamera[1],1.0)
PLAY_SOUND_FRONTEND(-1,"UNDER_WATER_COME_UP")
DESTROY_CAM(gridCamera[0])
ELSE
PLAY_INTRO_SOUND(FALSE)
ENDIF
ELIF DOES_CAM_EXIST(gridCamera[1])
IF NOT IS_CAM_INTERPOLATING(gridCamera[1])
SET_CAM_ACTIVE_WITH_INTERP(gridCamera[2],gridCamera[1],3000,GRAPH_TYPE_LINEAR)
SHAKE_CAM(gridCamera[2],"HAND_SHAKE",0.3)
DESTROY_CAM(gridCamera[1])
ELSE
PLAY_INTRO_SOUND()
ENDIF
ELIF DOES_CAM_EXIST(gridCamera[2])
IF NOT IS_CAM_INTERPOLATING(gridCamera[2])
SET_CAM_ACTIVE(gridCamera[3], TRUE)
SET_CAM_ACTIVE_WITH_INTERP(gridCamera[4],gridCamera[3],5000,GRAPH_TYPE_DECEL)
SHAKE_CAM(gridCamera[4],"HAND_SHAKE",0.3)
DESTROY_CAM(gridCamera[2])
iCamTimer = GET_GAME_TIMER() + 2000
ELSE
PLAY_INTRO_SOUND()
ENDIF
ELIF DOES_CAM_EXIST(gridCamera[4])
IF NOT IS_CAM_INTERPOLATING(gridCamera[4])
STOP_RENDERING_SCRIPT_CAMS_USING_CATCH_UP()
INT i_index = 0
REPEAT COUNT_OF(gridCamera) i_index
IF DOES_CAM_EXIST(gridCamera[i_index])
DESTROY_CAM(gridCamera[i_index])
ENDIF
ENDREPEAT
ELSE
PLAY_INTRO_SOUND()
ENDIF
IF GET_GAME_TIMER() > iCamTimer
RETURN TRUE
ENDIF
ELSE
RETURN TRUE
ENDIF
ELSE
IF DOES_CAM_EXIST(gridCamera[1])
//IF GET_GAME_TIMER() > iCamTimer
IF NOT IS_CAM_INTERPOLATING(gridCamera[1])
IF iCamTimer < 0
iCamTimer = GET_GAME_TIMER() + 1000
ELIF GET_GAME_TIMER() > iCamTimer
SET_CAM_ACTIVE_WITH_INTERP(gridCamera[3],gridCamera[2],4000,GRAPH_TYPE_LINEAR)
SHAKE_CAM(gridCamera[2],"HAND_SHAKE",1.0)
SHAKE_CAM(gridCamera[3],"HAND_SHAKE",1.0)
DESTROY_CAM(gridCamera[1])
ENDIF
ELSE
PLAY_INTRO_SOUND(FALSE)
ENDIF
ELIF DOES_CAM_EXIST(gridCamera[3])
IF NOT IS_CAM_INTERPOLATING(gridCamera[3])
SET_CAM_ACTIVE_WITH_INTERP(gridCamera[5],gridCamera[4],4000,GRAPH_TYPE_LINEAR)
SHAKE_CAM(gridCamera[4],"HAND_SHAKE",1.0)
SHAKE_CAM(gridCamera[5],"HAND_SHAKE",1.0)
DESTROY_CAM(gridCamera[3])
IF sRaceData.eRaceTrack = STREET_RACE_04
OR sRaceData.eRaceTrack = STREET_RACE_06
IF DOES_ENTITY_EXIST(sPlayerVehicle.vehPlayerVehicle)
IF IS_VEHICLE_DRIVEABLE(sPlayerVehicle.vehPlayerVehicle)
SET_ENTITY_COORDS(sPlayerVehicle.vehPlayerVehicle, sTrackData.vStartGrid[sTrackData.iNumAIRacers])
SET_ENTITY_HEADING(sPlayerVehicle.vehPlayerVehicle, sTrackData.fStartGrid[sTrackData.iNumAIRacers])
SET_VEHICLE_ON_GROUND_PROPERLY(sPlayerVehicle.vehPlayerVehicle)
ENDIF
ENDIF
ENDIF
ELSE
IF IS_PED_UNINJURED(piGridGirlOne)
IF bGridGirlOneConvReady
IF CREATE_CONVERSATION(pedConvStruct, "STR1AUD", "STR1_GIRL1", CONV_PRIORITY_HIGH)
bGridGirlOneConvReady = FALSE
ENDIF
ENDIF
ENDIF
PLAY_INTRO_SOUND()
ENDIF
ELIF DOES_CAM_EXIST(gridCamera[5])
IF NOT IS_CAM_INTERPOLATING(gridCamera[5])
IF PLAYERS_CAR_IS_VERY_BIG()
SET_CAM_ACTIVE(gridCamera[6], TRUE)
ENDIF
SET_CAM_ACTIVE_WITH_INTERP(gridCamera[7],gridCamera[6],3000,GRAPH_TYPE_DECEL)
SHAKE_CAM(gridCamera[6],"HAND_SHAKE",1.0)
SHAKE_CAM(gridCamera[7],"HAND_SHAKE",1.0)
DESTROY_CAM(gridCamera[5])
RETURN TRUE
ELSE
IF IS_PED_UNINJURED(piGridGirlThree)
IF bGridGirlThreeConvReady
IF CREATE_CONVERSATION(pedConvStruct, "STR1AUD", "STR1_GIRL2", CONV_PRIORITY_HIGH)
bGridGirlThreeConvReady = FALSE
ENDIF
ENDIF
ENDIF
PLAY_INTRO_SOUND()
ENDIF
ELIF DOES_CAM_EXIST(gridCamera[7])
IF NOT IS_CAM_INTERPOLATING(gridCamera[7])
STOP_RENDERING_SCRIPT_CAMS_USING_CATCH_UP()
INT i_index = 0
REPEAT COUNT_OF(gridCamera) i_index
IF DOES_CAM_EXIST(gridCamera[i_index])
DESTROY_CAM(gridCamera[i_index])
ENDIF
ENDREPEAT
//RETURN TRUE
ELSE
IF IS_PED_UNINJURED(piGridGirlThree)
IF bGridGirlThreeConvReady
IF CREATE_CONVERSATION(pedConvStruct, "STR1AUD", "STR1_GIRL2", CONV_PRIORITY_HIGH)
bGridGirlThreeConvReady = FALSE
ENDIF
ENDIF
ENDIF
PLAY_INTRO_SOUND()
ENDIF
ELSE
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Sets up ped and car models for the Hao race
PROC SETUP_ALT_RIVAL_MODELS()
sRacer[0].modelCar = PRAIRIE
sRacer[1].modelCar = DOMINATOR
sRacer[2].modelCar = SENTINEL
sRacer[3].modelCar = PRAIRIE
sRacer[4].modelCar = DOMINATOR
sRacer[5].modelCar = SENTINEL
sRacer[6].modelCar = PRAIRIE
sRacer[0].modelPed = A_M_M_SOCENLAT_01
sRacer[1].modelPed = A_M_Y_HIPSTER_02
sRacer[2].modelPed = A_M_Y_EASTSA_01
sRacer[3].modelPed = A_M_M_SOCENLAT_01
sRacer[4].modelPed = A_M_Y_HIPSTER_02
sRacer[5].modelPed = A_M_Y_EASTSA_01
sRacer[6].modelPed = A_M_M_SOCENLAT_01
sRacer[7].modelPed = A_M_Y_HIPSTER_02
sRacer[8].modelPed = A_M_Y_EASTSA_01
sRacer[9].modelPed = A_M_M_SOCENLAT_01
sRacer[10].modelPed = A_M_Y_HIPSTER_02
sRacer[11].modelPed = A_M_Y_EASTSA_01
sRacer[12].modelPed = A_M_M_SOCENLAT_01
sRacer[13].modelPed = A_M_Y_HIPSTER_02
sRacer[14].modelPed = A_M_Y_EASTSA_01
ENDPROC
/// PURPOSE: Setup required ped and car models depending on the race
PROC SETUP_RIVAL_MODELS()
// Set vehicle models
SWITCH sRaceData.eRaceTrack
CASE STREET_RACE_01 // South Los Santos
CASE STREET_RACE_02 // City Circuit
CASE STREET_RACE_04 // Airport
CASE STREET_RACE_05 // Freeway
sRacer[0].modelCar = BANSHEE
sRacer[1].modelCar = FELTZER2
sRacer[2].modelCar = SENTINEL
sRacer[3].modelCar = BANSHEE
sRacer[4].modelCar = FELTZER2
sRacer[5].modelCar = SENTINEL
sRacer[6].modelCar = BANSHEE
BREAK
CASE STREET_RACE_06 // Vespucci Canals
sRacer[0].modelCar = BATI
sRacer[1].modelCar = BATI
sRacer[2].modelCar = RUFFIAN
sRacer[3].modelCar = RUFFIAN
sRacer[4].modelCar = BATI
sRacer[5].modelCar = RUFFIAN
sRacer[6].modelCar = BATI
BREAK
CASE SEA_RACE_01 // North Coast
CASE SEA_RACE_02 // South Coast
CASE SEA_RACE_03 // Raton Canyon
CASE SEA_RACE_04 // Los Santos
sRacer[0].modelCar = GET_MODEL_NAME_BASED_OFF_RACE_TYPE(sRaceData.eRaceType)
sRacer[1].modelCar = GET_MODEL_NAME_BASED_OFF_RACE_TYPE(sRaceData.eRaceType)
sRacer[2].modelCar = GET_MODEL_NAME_BASED_OFF_RACE_TYPE(sRaceData.eRaceType)
sRacer[3].modelCar = GET_MODEL_NAME_BASED_OFF_RACE_TYPE(sRaceData.eRaceType)
sRacer[4].modelCar = GET_MODEL_NAME_BASED_OFF_RACE_TYPE(sRaceData.eRaceType)
sRacer[5].modelCar = GET_MODEL_NAME_BASED_OFF_RACE_TYPE(sRaceData.eRaceType)
sRacer[6].modelCar = GET_MODEL_NAME_BASED_OFF_RACE_TYPE(sRaceData.eRaceType)
sRacer[7].modelCar = GET_MODEL_NAME_BASED_OFF_RACE_TYPE(sRaceData.eRaceType)
sRacer[8].modelCar = GET_MODEL_NAME_BASED_OFF_RACE_TYPE(sRaceData.eRaceType)
sRacer[9].modelCar = GET_MODEL_NAME_BASED_OFF_RACE_TYPE(sRaceData.eRaceType)
sRacer[10].modelCar = GET_MODEL_NAME_BASED_OFF_RACE_TYPE(sRaceData.eRaceType)
sRacer[11].modelCar = GET_MODEL_NAME_BASED_OFF_RACE_TYPE(sRaceData.eRaceType)
sRacer[12].modelCar = GET_MODEL_NAME_BASED_OFF_RACE_TYPE(sRaceData.eRaceType)
sRacer[13].modelCar = GET_MODEL_NAME_BASED_OFF_RACE_TYPE(sRaceData.eRaceType)
sRacer[14].modelCar = GET_MODEL_NAME_BASED_OFF_RACE_TYPE(sRaceData.eRaceType)
BREAK
ENDSWITCH
// Set ped models
SWITCH sRaceData.eRaceTrack
CASE STREET_RACE_01 // South Los Santos
CASE STREET_RACE_02 // City Circuit
CASE STREET_RACE_04 // Airport
CASE STREET_RACE_05 // Freeway
sRacer[0].modelPed = A_M_M_SOCENLAT_01
sRacer[1].modelPed = A_M_Y_HIPSTER_02
sRacer[2].modelPed = A_M_Y_EASTSA_01
sRacer[3].modelPed = A_M_M_SOCENLAT_01
sRacer[4].modelPed = A_M_Y_HIPSTER_02
sRacer[5].modelPed = A_M_Y_EASTSA_01
sRacer[6].modelPed = A_M_M_SOCENLAT_01
sRacer[7].modelPed = A_M_Y_HIPSTER_02
sRacer[8].modelPed = A_M_Y_EASTSA_01
sRacer[9].modelPed = A_M_M_SOCENLAT_01
sRacer[10].modelPed = A_M_Y_HIPSTER_02
sRacer[11].modelPed = A_M_Y_EASTSA_01
sRacer[12].modelPed = A_M_M_SOCENLAT_01
sRacer[13].modelPed = A_M_Y_HIPSTER_02
sRacer[14].modelPed = A_M_Y_EASTSA_01
BREAK
CASE STREET_RACE_06 // Vespucci Canals
sRacer[0].modelPed = A_M_Y_MotoX_02
sRacer[1].modelPed = A_M_Y_MotoX_01
sRacer[2].modelPed = A_M_Y_MotoX_02
sRacer[3].modelPed = A_M_Y_MotoX_01
sRacer[4].modelPed = A_M_Y_MotoX_02
sRacer[5].modelPed = A_M_Y_MotoX_01
sRacer[6].modelPed = A_M_Y_MotoX_02
sRacer[7].modelPed = A_M_Y_MotoX_01
sRacer[8].modelPed = A_M_Y_MotoX_02
sRacer[9].modelPed = A_M_Y_MotoX_01
sRacer[10].modelPed = A_M_Y_MotoX_02
sRacer[11].modelPed = A_M_Y_MotoX_01
sRacer[12].modelPed = A_M_Y_MotoX_02
sRacer[13].modelPed = A_M_Y_MotoX_01
sRacer[14].modelPed = A_M_Y_MotoX_02
BREAK
CASE SEA_RACE_01 // North Coast
CASE SEA_RACE_02 // South Coast
CASE SEA_RACE_03 // Raton Canyon
CASE SEA_RACE_04 // Los Santos
sRacer[0].modelPed = A_M_Y_Jetski_01
sRacer[1].modelPed = A_M_Y_JETSKI_01
sRacer[2].modelPed = A_M_Y_Jetski_01
sRacer[3].modelPed = A_M_Y_Jetski_01
sRacer[4].modelPed = A_M_Y_Jetski_01
sRacer[5].modelPed = A_M_Y_Jetski_01
sRacer[6].modelPed = A_M_Y_Jetski_01
sRacer[7].modelPed = A_M_Y_Jetski_01
sRacer[8].modelPed = A_M_Y_Jetski_01
sRacer[9].modelPed = A_M_Y_Jetski_01
sRacer[10].modelPed = A_M_Y_Jetski_01
sRacer[11].modelPed = A_M_Y_Jetski_01
sRacer[12].modelPed = A_M_Y_Jetski_01
sRacer[13].modelPed = A_M_Y_Jetski_01
sRacer[14].modelPed = A_M_Y_Jetski_01
BREAK
ENDSWITCH
ENDPROC
PROC SETUP_END_SCENE_COORDS()
SWITCH sRaceData.eRaceTrack
// South Los Santos
CASE STREET_RACE_01
vWinningCarPos = <<-168.60, -1575.16, 34.70>>//<<-72.35, -1798.04, 27.36>>
fWinningCarHeading = 141.57//229.02
vRewardPedPos = <<-170.0685, -1573.8658, 34.3070>>//<<-169.9326, -1574.0978, 34.3036>>//<<-171.44, -1572.61, 35.34>>//<<-76.34, -1800.73, 27.93>>
fRewardPedHeading = 191.3066//224.8836//-128.52//-28.65
// Commenting out for now in case this is needed again
/*vAltCarPos = <<-69.02, -1787.95, 27.51>>
fAltCarHeading = 229.77*/
BREAK
// City Circuit
CASE STREET_RACE_02
vWinningCarPos = <<353.12, 321.39, 103.65>>
fWinningCarHeading = 77.40
vRewardPedPos = <<353.4853, 323.3662, 103.0862>>//<<354.22, 326.03, 104.09>>
fRewardPedHeading = 127.4//166.34
// Commenting out for now in case this is needed again
/*vAltCarPos = <<-511.93, 270.68, 82.72>>
fAltCarHeading = 137.65*/
BREAK
// Airport
CASE STREET_RACE_04
vWinningCarPos = <<-794.5402, -2527.3931, 12.7891>>//<<-796.32, -2526.38, 13.40>>//<<-994.50, -2437.56, 19.61>>
fWinningCarHeading = 332.72//151.98
vRewardPedPos = <<-792.37, -2527.87, 13.83>>//<<-998.18, -2434.28, 20.17>>
fRewardPedHeading = 22.72//80.21//-112.87
// Commenting out for now in case this is needed again
/*vAltCarPos = <<-990.51, -2430.89, 19.62>>
fAltCarHeading = 151.97*/
BREAK
// Freeway
CASE STREET_RACE_05
// on the freeway, no end scene for this race
vWinningCarPos = <<818.13, -1161.54, 28.00>>
fWinningCarHeading = 76.46
vRewardPedPos = <<818.5804, -1159.6426, 27.2778>>//<<818.74, -1157.61, 28.26>>
fRewardPedHeading = 126.46//178.19
BREAK
// Vespucci Canals
CASE STREET_RACE_06
vWinningCarPos = <<-1025.28, -1130.25, 1.70>>//<<-989.86, -1111.95, 1.64>>//<<-1246.85, -1086.30, 7.71>>
fWinningCarHeading = 305.27//202.39
vRewardPedPos = <<-1024.5483, -1131.2539, 1.1861>>//<<-1022.20, -1134.06, 2.16>>//<<-987.10, -1114.75, 2.15>>//<<-1251.45, -1087.25, 8.32>>
fRewardPedHeading = 355.27//48.70//-64.17
// Commenting out for now in case this is needed again
/*vAltCarPos = <<-1249.22, -1078.61, 7.81>>
fAltCarHeading = 197.10*/
BREAK
ENDSWITCH
ENDPROC
// ===========================================================================================================
// Mission States
// ===========================================================================================================
/// PURPOSE: Basic race setup
PROC RACE_INIT(BOOL bAltLineup = FALSE, BOOL bClearAreaPeds = TRUE)
CPRINTLN(DEBUG_MISSION, "Mission Race: RACE_INIT")
IF DOES_ENTITY_EXIST(sPlayerVehicle.vehPlayerVehicle)
CPRINTLN(DEBUG_MISSION, "Mission Race: vehPlayerVehicle already exists")
ELSE
IF DOES_ENTITY_EXIST(PLAYER_PED_ID())
AND NOT IS_ENTITY_DEAD(PLAYER_PED_ID())
ADD_PED_FOR_DIALOGUE(pedConvStruct, 1, PLAYER_PED_ID(), "FRANKLIN")
IF IS_PED_SITTING_IN_ANY_VEHICLE(PLAYER_PED_ID())
sPlayerVehicle.vehPlayerVehicle = GET_VEHICLE_PED_IS_USING(PLAYER_PED_ID())
CPRINTLN(DEBUG_MISSION, "Mission Race: Acquired player's vehicle")
ELSE
sPlayerVehicle.vehPlayerVehicle = GET_PLAYERS_LAST_VEHICLE()
IF IS_VEHICLE_OK(sPlayerVehicle.vehPlayerVehicle)
SET_PED_INTO_VEHICLE(PLAYER_PED_ID(), sPlayerVehicle.vehPlayerVehicle)
ELSE
SCRIPT_ASSERT("Mission Race: somehow managed to allow the race to start when the player isn't in a vehicle")
ENDIF
ENDIF
ENDIF
ENDIF
// Init states
eRaceState = RACE_STATE_LINEUP
eRaceSubState = RACE_SUBSTATE_SETUP
failReason = FAIL_DEFAULT
// Load track info
INIT_TRACK_DATA(sRaceData, sTrackData)
// Set the instance priority
SET_INSTANCE_PRIORITY_HINT(INSTANCE_HINT_DRIVING)
// Setup race lineup cameras
SETUP_RACE_CAMERAS()
// Setup all required ped and car models we'll need to load in
IF bAltLineup
SETUP_ALT_RIVAL_MODELS()
ELSE
SETUP_RIVAL_MODELS()
// Deduct race fee
//DEBIT_BANK_ACCOUNT(GET_CURRENT_PLAYER_PED_ENUM(), BAAC_UNLOGGED_SMALL_ACTION, sRaceData.iRaceFee)
ENDIF
// Setup end scene coordinates
SETUP_END_SCENE_COORDS()
// Disable all peds...
SET_PED_POPULATION_BUDGET(0)
SET_REDUCE_PED_MODEL_BUDGET(TRUE)
// Traffic and cops
SET_VEHICLE_POPULATION_BUDGET(2)
SET_REDUCE_VEHICLE_MODEL_BUDGET(TRUE)
SET_DISPATCH_COPS_FOR_PLAYER(PLAYER_ID(), FALSE)
SET_CREATE_RANDOM_COPS(FALSE)
// Turn on the slipstream effect
SET_ENABLE_VEHICLE_SLIPSTREAMING(TRUE)
// Block all scenarios
//scenarioBlockArea = ADD_SCENARIO_BLOCKING_AREA(<<-7000.0, -7000.0, -100.0>>, <<7000.0, 7000.0, 100.0>>)
// Needed to disable scenarios at docks
SET_SCENARIO_TYPE_ENABLED("DRIVE", FALSE)
// Remove vehicle gens
SET_ALL_VEHICLE_GENERATORS_ACTIVE_IN_AREA(<<-7000.0, -7000.0, -100.0>>, <<7000.0, 7000.0, 100.0>>, FALSE)
REMOVE_VEHICLES_FROM_GENERATORS_IN_AREA(<<-7000.0, -7000.0, -100.0>>, <<7000.0, 7000.0, 100.0>>)
IF bClearAreaPeds AND DOES_ENTITY_EXIST(PLAYER_PED_ID())
AND NOT IS_ENTITY_DEAD(PLAYER_PED_ID())
IF sRaceData.eRaceType = RACETYPE_BIKE OR sRaceData.eRaceType = RACETYPE_CAR
sbiRaceStart = ADD_SCENARIO_BLOCKING_AREA_FROM_POSITION_AND_RADIUS(GET_ENTITY_COORDS(PLAYER_PED_ID()), 60.0)
SETUP_ROAD_BLOCKING(sRaceData.eRaceTrack, vRaceRoadPos1, vRaceRoadPos2, fRaceRoadWidth)
SET_ROADS_IN_ANGLED_AREA(vRaceRoadPos1, vRaceRoadPos2, fRaceRoadWidth, FALSE, FALSE)
ENDIF
CLEAR_AREA_OF_PEDS(GET_ENTITY_COORDS(PLAYER_PED_ID()), 500) // B*930668 - make sure there are no ambient peds still mooching around
ENDIF
// Player cannot fly through windscrren
IF IS_PLAYER_PLAYING(PLAYER_ID())
SET_PED_CONFIG_FLAG(PLAYER_PED_ID(),PCF_WillFlyThroughWindscreen, FALSE)
ENDIF
SET_WANTED_LEVEL_MULTIPLIER(0.1) // See B*930410 - Imran asked for this to be set at 0.1
sPlayerVehicle.bDisplayedGetBackInVehicleText = FALSE
ENDPROC
/// PURPOSE: Setup player for start of race
PROC SETUP_PLAYER_PRECUTSCENE()
IF IS_PLAYER_PLAYING(PLAYER_ID())
// Teleport player and their vehicle to starting grid
IF DOES_ENTITY_EXIST(sPlayerVehicle.vehPlayerVehicle)
IF IS_VEHICLE_DRIVEABLE(sPlayerVehicle.vehPlayerVehicle)
IF NOT IS_PED_SITTING_IN_VEHICLE(PLAYER_PED_ID(), sPlayerVehicle.vehPlayerVehicle) AND IS_VEHICLE_SEAT_FREE(sPlayerVehicle.vehPlayerVehicle)
SET_PED_INTO_VEHICLE(PLAYER_PED_ID(), sPlayerVehicle.vehPlayerVehicle)
ENDIF
// Clear area around race location
PLAYER_INDEX pId = GET_PLAYER_INDEX()
CLEAR_AREA(GET_PLAYER_COORDS(pId), 50.0, TRUE)
CLEAR_AREA_OF_VEHICLES(GET_PLAYER_COORDS(pId), 500.0) // use a big area since ambient vehicles get turned off anyway
CLEAR_AREA_OF_PEDS(GET_PLAYER_COORDS(pId), 500.0)
VECTOR behind_pos = GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS(sTrackData.vStartGrid[sTrackData.iNumAIRacers], sTrackData.fStartGrid[sTrackData.iNumAIRacers], <<0.0, -15.0, 1.0>>)
SNAP_3D_COORD_TO_GROUND(behind_pos)
FLOAT start_heading = sTrackData.fStartGrid[sTrackData.iNumAIRacers]
IF sRaceData.eRaceTrack = STREET_RACE_04
behind_pos = <<-825.03, -2575.13, 13.25>>
start_heading = 334.98
ELIF sRaceData.eRaceTrack = STREET_RACE_06
behind_pos = <<-1093.4932, -1173.1614, 2.4978>>
ELIF sRaceData.eRaceType = RACETYPE_SEA
behind_pos = sTrackData.vStartGrid[sTrackData.iNumAIRacers]
ENDIF
VECTOR targetPos
FLOAT fSpeed
/*IF sRaceData.eRaceType = RACETYPE_SEA
targetPos = GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS(sTrackData.vStartGrid[sTrackData.iNumAIRacers], sTrackData.fStartGrid[sTrackData.iNumAIRacers], <<1.0, 2.0, 0.0>>)
fSpeed = 7.0
EL*/IF sRaceData.eRaceType = RACETYPE_BIKE
targetPos = GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS(sTrackData.vStartGrid[sTrackData.iNumAIRacers], sTrackData.fStartGrid[sTrackData.iNumAIRacers], <<0.0, -1.0, 0.0>>)
fSpeed = 7.0
ELSE
targetPos = GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS(sTrackData.vStartGrid[sTrackData.iNumAIRacers], sTrackData.fStartGrid[sTrackData.iNumAIRacers], <<0.0, 2.3, 0.0>>)
fSpeed = 3.0
ENDIF
//CLEAR_PED_TASKS(PLAYER_PED_ID())
SET_VEHICLE_ENGINE_ON(sPlayerVehicle.vehPlayerVehicle, TRUE, TRUE)
SET_ENTITY_HEADING(sPlayerVehicle.vehPlayerVehicle, start_heading)
SET_ENTITY_COORDS_GROUNDED(sPlayerVehicle.vehPlayerVehicle, behind_pos)
//SET_VEHICLE_ON_GROUND_PROPERLY(sPlayerVehicle.vehPlayerVehicle)
SET_VEHICLE_DOORS_SHUT(sPlayerVehicle.vehPlayerVehicle)
SET_VEHICLE_IS_RACING(sPlayerVehicle.vehPlayerVehicle, TRUE)
IF sRaceData.eRaceType = RACETYPE_SEA
SET_VEHICLE_ON_GROUND_PROPERLY(sPlayerVehicle.vehPlayerVehicle)
SET_BOAT_LOW_LOD_ANCHOR_DISTANCE(sPlayerVehicle.vehPlayerVehicle,0)
SET_BOAT_REMAINS_ANCHORED_WHILE_PLAYER_IS_DRIVER(sPlayerVehicle.vehPlayerVehicle,TRUE)
SET_FORCE_LOW_LOD_ANCHOR_MODE(sPlayerVehicle.vehPlayerVehicle,TRUE)
IF CAN_ANCHOR_BOAT_HERE(sPlayerVehicle.vehPlayerVehicle)
SET_BOAT_ANCHOR(sPlayerVehicle.vehPlayerVehicle,TRUE)
ENDIF
ELSE
TASK_VEHICLE_DRIVE_TO_COORD(PLAYER_PED_ID(), sPlayerVehicle.vehPlayerVehicle, targetPos, fSpeed, DRIVINGSTYLE_NORMAL, GET_ENTITY_MODEL(sPlayerVehicle.vehPlayerVehicle), DF_ForceStraightLine|DRIVINGMODE_PLOUGHTHROUGH, 0.5, 15.0)
SET_VEHICLE_FORWARD_SPEED(sPlayerVehicle.vehPlayerVehicle, fSpeed)
FORCE_ENTITY_AI_AND_ANIMATION_UPDATE(sPlayerVehicle.vehPlayerVehicle)
ENDIF
//SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(PLAYER_PED_ID(), TRUE)
SET_ENTITY_VISIBLE(sPlayerVehicle.vehPlayerVehicle, TRUE)
// reduce the amount of damage the player's vehicle takes
SET_VEHICLE_STRONG(sPlayerVehicle.vehPlayerVehicle, TRUE)
SET_VEHICLE_HAS_STRONG_AXLES(sPlayerVehicle.vehPlayerVehicle, TRUE)
//IF (sRaceData.eRaceType = RACETYPE_BIKE OR sRaceData.eRaceType = RACETYPE_SEA) AND IS_PED_UNINJURED(PLAYER_PED_ID()) AND IS_PLAYER_PLAYING(PLAYER_ID())
IF sRaceData.eRaceType = RACETYPE_SEA AND IS_PED_UNINJURED(PLAYER_PED_ID()) AND IS_PLAYER_PLAYING(PLAYER_ID())
SET_PED_CAN_BE_KNOCKED_OFF_VEHICLE(PLAYER_PED_ID(), KNOCKOFFVEHICLE_HARD)
ENDIF
//SET_FRONTEND_RADIO_ACTIVE(FALSE)
ENDIF
ENDIF
// Turn player visibility back on
SET_ENTITY_VISIBLE(PLAYER_PED_ID(), TRUE)
ENDIF
ENDPROC
PROC SETUP_PLAYER_COUNTDOWN()
IF IS_PLAYER_PLAYING(PLAYER_ID())
// clear player tasks asthe drive to line up task may still be running
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
CLEAR_PED_TASKS(PLAYER_PED_ID())
ENDIF
IF DOES_ENTITY_EXIST(sPlayerVehicle.vehPlayerVehicle)
IF IS_VEHICLE_DRIVEABLE(sPlayerVehicle.vehPlayerVehicle)
SET_ENTITY_COORDS(sPlayerVehicle.vehPlayerVehicle, sTrackData.vStartGrid[sTrackData.iNumAIRacers])
SET_ENTITY_HEADING(sPlayerVehicle.vehPlayerVehicle, sTrackData.fStartGrid[sTrackData.iNumAIRacers])
SET_VEHICLE_ON_GROUND_PROPERLY(sPlayerVehicle.vehPlayerVehicle)
ENDIF
SET_ENTITY_VISIBLE(PLAYER_PED_ID(), TRUE)
SET_ENTITY_VISIBLE(sPlayerVehicle.vehPlayerVehicle, TRUE)
ENDIF
ENDIF
ENDPROC
/// PURPOSE: Setups up some random mods on the rival's race vehicle
PROC SETUP_RIVAL_VEHICLE_MODS(INT i_car)
IF DOES_ENTITY_EXIST(sRacer[i_car].viCar)
AND NOT IS_ENTITY_DEAD(sRacer[i_car].viCar)
SET_VEHICLE_COLOUR_COMBINATION(sRacer[i_car].viCar, GET_RANDOM_INT_IN_RANGE(0, GET_NUMBER_OF_VEHICLE_COLOURS(sRacer[i_car].viCar)))
IF GET_NUM_MOD_KITS(sRacer[i_car].viCar) > 0
SET_VEHICLE_MOD_KIT(sRacer[i_car].viCar, 0)
IF GET_NUM_VEHICLE_MODS(sRacer[i_car].viCar, MOD_SPOILER) > 0
SET_VEHICLE_MOD(sRacer[i_car].viCar, MOD_SPOILER, GET_RANDOM_VEHICLE_MOD_INDEX(sRacer[i_car].viCar, MOD_SPOILER))
ENDIF
IF GET_NUM_VEHICLE_MODS(sRacer[i_car].viCar, MOD_BUMPER_F) > 0
SET_VEHICLE_MOD(sRacer[i_car].viCar, MOD_BUMPER_F, GET_RANDOM_VEHICLE_MOD_INDEX(sRacer[i_car].viCar, MOD_BUMPER_F))
ENDIF
IF GET_NUM_VEHICLE_MODS(sRacer[i_car].viCar, MOD_BUMPER_R) > 0
SET_VEHICLE_MOD(sRacer[i_car].viCar, MOD_BUMPER_R, GET_RANDOM_VEHICLE_MOD_INDEX(sRacer[i_car].viCar, MOD_BUMPER_R))
ENDIF
IF GET_NUM_VEHICLE_MODS(sRacer[i_car].viCar, MOD_SKIRT) > 0
SET_VEHICLE_MOD(sRacer[i_car].viCar, MOD_SKIRT, GET_RANDOM_VEHICLE_MOD_INDEX(sRacer[i_car].viCar, MOD_SKIRT))
ENDIF
IF GET_NUM_VEHICLE_MODS(sRacer[i_car].viCar, MOD_EXHAUST) > 0
SET_VEHICLE_MOD(sRacer[i_car].viCar, MOD_EXHAUST, GET_RANDOM_VEHICLE_MOD_INDEX(sRacer[i_car].viCar, MOD_EXHAUST))
ENDIF
IF GET_NUM_VEHICLE_MODS(sRacer[i_car].viCar, MOD_CHASSIS) > 0
SET_VEHICLE_MOD(sRacer[i_car].viCar, MOD_CHASSIS, GET_RANDOM_VEHICLE_MOD_INDEX(sRacer[i_car].viCar, MOD_CHASSIS))
ENDIF
IF GET_NUM_VEHICLE_MODS(sRacer[i_car].viCar, MOD_GRILL) > 0
SET_VEHICLE_MOD(sRacer[i_car].viCar, MOD_GRILL, GET_RANDOM_VEHICLE_MOD_INDEX(sRacer[i_car].viCar, MOD_GRILL))
ENDIF
IF GET_NUM_VEHICLE_MODS(sRacer[i_car].viCar, MOD_WING_L) > 0
SET_VEHICLE_MOD(sRacer[i_car].viCar, MOD_WING_L, GET_RANDOM_VEHICLE_MOD_INDEX(sRacer[i_car].viCar, MOD_WING_L))
ENDIF
IF GET_NUM_VEHICLE_MODS(sRacer[i_car].viCar, MOD_WING_R) > 0
SET_VEHICLE_MOD(sRacer[i_car].viCar, MOD_WING_R, GET_RANDOM_VEHICLE_MOD_INDEX(sRacer[i_car].viCar, MOD_WING_R))
ENDIF
IF GET_NUM_VEHICLE_MODS(sRacer[i_car].viCar, MOD_ENGINE) > 0
SET_VEHICLE_MOD(sRacer[i_car].viCar, MOD_ENGINE, GET_RANDOM_VEHICLE_MOD_INDEX(sRacer[i_car].viCar, MOD_ENGINE))
ENDIF
IF GET_NUM_VEHICLE_MODS(sRacer[i_car].viCar, MOD_BRAKES) > 0
SET_VEHICLE_MOD(sRacer[i_car].viCar, MOD_BRAKES, GET_RANDOM_VEHICLE_MOD_INDEX(sRacer[i_car].viCar, MOD_BRAKES))
ENDIF
IF GET_NUM_VEHICLE_MODS(sRacer[i_car].viCar, MOD_GEARBOX) > 0
SET_VEHICLE_MOD(sRacer[i_car].viCar, MOD_GEARBOX, GET_RANDOM_VEHICLE_MOD_INDEX(sRacer[i_car].viCar, MOD_GEARBOX))
ENDIF
TOGGLE_VEHICLE_MOD(sRacer[i_car].viCar, MOD_TOGGLE_TURBO, TRUE)
TOGGLE_VEHICLE_MOD(sRacer[i_car].viCar, MOD_TOGGLE_NITROUS, TRUE)
TOGGLE_VEHICLE_MOD(sRacer[i_car].viCar, MOD_TOGGLE_XENON_LIGHTS, TRUE)
ENDIF
ENDIF
ENDPROC
/// PURPOSE: Set skill levels for the rivals
PROC SETUP_RIVAL_VEHICLE_SKILL_LEVELS()
// All tracks currently use this same data. Add a switch statement for tracks if required.
IF sRaceData.eRaceTrack = STREET_RACE_05
sRacer[0].drivingSpeed = AI_DRIVING_SPEED_FAST
sRacer[1].drivingSpeed = AI_DRIVING_SPEED_FAST
sRacer[2].drivingSpeed = AI_DRIVING_SPEED_FAST
sRacer[3].drivingSpeed = AI_DRIVING_SPEED_MEDIUM
sRacer[4].drivingSpeed = AI_DRIVING_SPEED_MEDIUM
sRacer[5].drivingSpeed = AI_DRIVING_SPEED_SLOW
sRacer[6].drivingSpeed = AI_DRIVING_SPEED_SLOW
ELIF sRaceData.eRaceTrack = SEA_RACE_03
sRacer[0].drivingSpeed = AI_DRIVING_SPEED_FAST
sRacer[1].drivingSpeed = AI_DRIVING_SPEED_MEDIUM
sRacer[2].drivingSpeed = AI_DRIVING_SPEED_SLOW
ELSE
sRacer[0].drivingSpeed = AI_DRIVING_SPEED_FAST
sRacer[1].drivingSpeed = AI_DRIVING_SPEED_FAST
sRacer[2].drivingSpeed = AI_DRIVING_SPEED_MEDIUM
sRacer[3].drivingSpeed = AI_DRIVING_SPEED_MEDIUM
sRacer[4].drivingSpeed = AI_DRIVING_SPEED_SLOW
sRacer[5].drivingSpeed = AI_DRIVING_SPEED_SLOW
sRacer[6].drivingSpeed = AI_DRIVING_SPEED_SLOW
ENDIF
ENDPROC
PROC SET_RIVALS_INFO(INT i_index)
IF IS_PED_UNINJURED(sRacer[i_index].piDriver)
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(sRacer[i_index].piDriver, TRUE)
SET_VEHICLE_IS_RACING(sRacer[i_index].viCar, TRUE)
SET_VEHICLE_IS_CONSIDERED_BY_PLAYER(sRacer[i_index].viCar, FALSE)
SET_VEHICLE_ENGINE_ON(sRacer[i_index].viCar, TRUE, TRUE)
SET_ENTITY_ONLY_DAMAGED_BY_PLAYER(sRacer[i_index].piDriver, TRUE)
SET_ENTITY_ONLY_DAMAGED_BY_PLAYER(sRacer[i_index].viCar, TRUE)
SET_VEHICLE_STRONG(sRacer[i_index].viCar, TRUE)
SET_VEHICLE_HAS_STRONG_AXLES(sRacer[i_index].viCar, TRUE)
SET_VEHICLE_TYRES_CAN_BURST(sRacer[i_index].viCar, FALSE)
//SET_VEHICLE_CAN_DEFORM_WHEELS(sRacer[i_index].viCar, FALSE)
SET_PED_CONFIG_FLAG(sRacer[i_index].piDriver, PCF_WillFlyThroughWindscreen, FALSE)
SET_PED_CONFIG_FLAG(sRacer[i_index].piDriver, PCF_GetOutUndriveableVehicle, FALSE)
SET_PED_CONFIG_FLAG(sRacer[i_index].piDriver, PCF_GetOutBurningVehicle, FALSE)
SET_PED_CONFIG_FLAG(sRacer[i_index].piDriver, PCF_RunFromFiresAndExplosions, FALSE)
SET_PED_CONFIG_FLAG(sRacer[i_index].piDriver, PCF_DontAllowToBeDraggedOutOfVehicle, TRUE)
SET_ENTITY_LOAD_COLLISION_FLAG(sRacer[i_index].viCar, TRUE)
SET_PED_DIES_IN_WATER(sRacer[i_index].piDriver, FALSE)
/*IF sRaceData.eRaceType = RACETYPE_BIKE OR sRaceData.eRaceType = RACETYPE_SEA // In bike races set rivals as hard to knock off their bikes
SET_PED_CAN_BE_KNOCKED_OFF_VEHICLE(sRacer[i_index].piDriver, KNOCKOFFVEHICLE_HARD)
ENDIF*/
IF sRaceData.eRaceType = RACETYPE_SEA // See B*963685 - This ensures sea race rivals will update if more than 300m away
SET_ENTITY_SHOULD_FREEZE_WAITING_ON_COLLISION(sRacer[i_index].viCar, FALSE)
SET_PED_CAN_BE_KNOCKED_OFF_VEHICLE(sRacer[i_index].piDriver, KNOCKOFFVEHICLE_HARD)
ENDIF
sRacer[i_index].stateAI = AI_RACER_INIT
ENDIF
ENDPROC
/// PURPOSE: Setup AI racer positions on the starting grid
PROC SETUP_AI_RACERS(PED_INDEX piRacerOne = NULL, VEHICLE_INDEX viRacerOne = NULL)
INT i_index
// The vehicle that Hao replaces
i_index = sTrackData.iNumAIRacers-1//1
IF IS_PED_UNINJURED(piRacerOne) AND IS_VEHICLE_OK(viRacerOne) AND NOT DOES_ENTITY_EXIST(sRacer[i_index].viCar)
/*IF NOT IS_PED_IN_VEHICLE(piRacerOne, viRacerOne) AND IS_VEHICLE_SEAT_FREE(viRacerOne)
SET_PED_INTO_VEHICLE(piRacerOne, viRacerOne)
ENDIF*/
sRacer[i_index].viCar = viRacerOne
sRacer[i_index].modelCar = GET_ENTITY_MODEL(viRacerOne)
/*SET_ENTITY_COORDS(sRacer[i_index].viCar, sTrackData.vStartGrid[i_index])
SET_ENTITY_HEADING(sRacer[i_index].viCar, sTrackData.fStartGrid[i_index])*/
sRacer[i_index].piDriver = piRacerOne
sRacer[i_index].modelPed = GET_ENTITY_MODEL(piRacerOne)
SET_RIVALS_INFO(i_index)
ENDIF
LOAD_RIVAL_MODELS(TRUE) // Load all required rival models
// Create racers
VECTOR vModifiedStartPos
VECTOR targetPos
SEQUENCE_INDEX siDrive
FOR i_index = 0 TO sTrackData.iNumAIRacers - 1
// Create vehicle
WHILE NOT DOES_ENTITY_EXIST(sRacer[i_index].viCar)
IF HAS_MODEL_LOADED(sRacer[i_index].modelCar)
vModifiedStartPos = sTrackData.vStartGrid[i_index]
IF sRaceData.eRaceType = RACETYPE_SEA
vModifiedStartPos.x += GET_RANDOM_FLOAT_IN_RANGE(-1, 1)
vModifiedStartPos.y += GET_RANDOM_FLOAT_IN_RANGE(-1, 1)
ELSE
vModifiedStartPos = GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS(sTrackData.vStartGrid[i_index], sTrackData.fStartGrid[i_index], <<0.0, -3.0, 0.0>>)
ENDIF
sRacer[i_index].viCar = CREATE_VEHICLE(sRacer[i_index].modelCar, vModifiedStartPos, sTrackData.fStartGrid[i_index])
IF sRaceData.eRaceType = RACETYPE_SEA
SET_VEHICLE_ON_GROUND_PROPERLY(sRacer[i_index].viCar)
SET_BOAT_LOW_LOD_ANCHOR_DISTANCE(sRacer[i_index].viCar,0)
SET_BOAT_REMAINS_ANCHORED_WHILE_PLAYER_IS_DRIVER(sRacer[i_index].viCar,TRUE)
SET_FORCE_LOW_LOD_ANCHOR_MODE(sRacer[i_index].viCar,TRUE)
IF CAN_ANCHOR_BOAT_HERE(sRacer[i_index].viCar)
SET_BOAT_ANCHOR(sRacer[i_index].viCar,TRUE)
ENDIF
SET_ENTITY_DYNAMIC(sRacer[i_index].viCar,FALSE)
ELSE
SET_VEHICLE_ON_GROUND_PROPERLY(sRacer[i_index].viCar)
ENDIF
SET_VEHICLE_MODEL_IS_SUPPRESSED(sRacer[i_index].modelCar, TRUE)
ENDIF
WAIT(0)
ENDWHILE
SETUP_RIVAL_VEHICLE_SKILL_LEVELS()
IF IS_PED_UNINJURED(piRacerOne)
sRacer[sTrackData.iNumAIRacers-1].drivingSpeed = AI_DRIVING_SPEED_FAST
ENDIF
// Place driver inside vehicle
WHILE NOT DOES_ENTITY_EXIST(sRacer[i_index].piDriver)
IF DOES_ENTITY_EXIST(sRacer[i_index].viCar)
AND IS_VEHICLE_DRIVEABLE(sRacer[i_index].viCar)
AND HAS_MODEL_LOADED(sRacer[i_index].modelPed)
sRacer[i_index].piDriver = CREATE_PED_INSIDE_VEHICLE(sRacer[i_index].viCar, PEDTYPE_MISSION, sRacer[i_index].modelPed)
IF sRaceData.eRaceType <> RACETYPE_SEA
SET_VEHICLE_USE_ALTERNATE_HANDLING(sRacer[i_index].viCar, TRUE)
targetPos = GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS(sTrackData.vStartGrid[i_index], sTrackData.fStartGrid[i_index], <<0.0, 2.3, 0.0>>)
IF i_index = 0
OPEN_SEQUENCE_TASK(siDrive)
TASK_PAUSE(NULL, 2800)
TASK_VEHICLE_DRIVE_TO_COORD(NULL, sRacer[i_index].viCar, targetPos, 1.5, DRIVINGSTYLE_NORMAL, sRacer[i_index].modelCar, DF_ForceStraightLine|DRIVINGMODE_PLOUGHTHROUGH, 0.5, 15.0)
CLOSE_SEQUENCE_TASK(siDrive)
TASK_PERFORM_SEQUENCE(sRacer[i_index].piDriver, siDrive)
CLEAR_SEQUENCE_TASK(siDrive)
ELIF i_index = 1
OPEN_SEQUENCE_TASK(siDrive)
TASK_PAUSE(NULL, 3300)
TASK_VEHICLE_DRIVE_TO_COORD(NULL, sRacer[i_index].viCar, targetPos, 1.2, DRIVINGSTYLE_NORMAL, sRacer[i_index].modelCar, DF_ForceStraightLine|DRIVINGMODE_PLOUGHTHROUGH, 0.5, 15.0)
CLOSE_SEQUENCE_TASK(siDrive)
TASK_PERFORM_SEQUENCE(sRacer[i_index].piDriver, siDrive)
CLEAR_SEQUENCE_TASK(siDrive)
ELSE
OPEN_SEQUENCE_TASK(siDrive)
TASK_PAUSE(NULL, (5500+(i_index*100)))
TASK_VEHICLE_DRIVE_TO_COORD(NULL, sRacer[i_index].viCar, targetPos, 0.8, DRIVINGSTYLE_NORMAL, sRacer[i_index].modelCar, DF_ForceStraightLine|DRIVINGMODE_PLOUGHTHROUGH, 0.5, 15.0)
CLOSE_SEQUENCE_TASK(siDrive)
TASK_PERFORM_SEQUENCE(sRacer[i_index].piDriver, siDrive)
CLEAR_SEQUENCE_TASK(siDrive)
ENDIF
ENDIF
IF sRaceData.eRaceType = RACETYPE_BIKE
GIVE_PED_HELMET(sRacer[i_index].piDriver)
ENDIF
SETUP_RIVAL_VEHICLE_MODS(i_index)
IF IS_PED_UNINJURED(sRacer[i_index].piDriver)
SET_RIVALS_INFO(i_index)
ENDIF
ENDIF
WAIT(0)
ENDWHILE
ENDFOR
// Hao needs a fast driving speed
IF IS_PED_UNINJURED(piRacerOne)
sRacer[sTrackData.iNumAIRacers-1].drivingSpeed = AI_DRIVING_SPEED_FAST
ENDIF
LOAD_RIVAL_MODELS(FALSE) // Unload all required rival models
SWITCH sRaceData.eRaceTrack
CASE STREET_RACE_01
sRaceWaypointRecording = "CityRace0_route1"
BREAK
CASE STREET_RACE_02
sRaceWaypointRecording = "CityRace1_route1"
BREAK
CASE STREET_RACE_04
sRaceWaypointRecording = "CityRace3_route1"
BREAK
CASE STREET_RACE_05
sRaceWaypointRecording = "CityRace4_route1"
BREAK
CASE STREET_RACE_06
sRaceWaypointRecording = "CityRace5_route1"
BREAK
CASE SEA_RACE_01
sRaceWaypointRecording = "SeaRace0_route1"
BREAK
CASE SEA_RACE_02
sRaceWaypointRecording = "SeaRace1_route1"
BREAK
CASE SEA_RACE_03
sRaceWaypointRecording = "SeaRace2_route1"
BREAK
CASE SEA_RACE_04
sRaceWaypointRecording = "SeaRace3_route1"
BREAK
ENDSWITCH
REQUEST_WAYPOINT_RECORDING(sRaceWaypointRecording)
WHILE NOT GET_IS_WAYPOINT_RECORDING_LOADED(sRaceWaypointRecording)
WAIT(0)
ENDWHILE
WAYPOINT_RECORDING_GET_NUM_POINTS(sRaceWaypointRecording, iNumWaypoints)
iNumWaypoints-=1 // adjust this to starting at zero
IF sTrackData.iNumLaps = 0
bLoopWaypointRecording = FALSE
ELSE
bLoopWaypointRecording = TRUE
ENDIF
ENDPROC
PROC PLACE_RACERS_FOR_RESTART()
INT i_index
FOR i_index = 0 TO sTrackData.iNumAIRacers - 1
IF IS_PED_UNINJURED(sRacer[i_index].piDriver) AND IS_VEHICLE_OK(sRacer[i_index].viCar)
CLEAR_PED_TASKS(sRacer[i_index].piDriver)
BRING_VEHICLE_TO_HALT(sRacer[i_index].viCar, 0.1, 1)
SET_ENTITY_HEADING(sRacer[i_index].viCar, sTrackData.fStartGrid[i_index])
SET_ENTITY_COORDS_GROUNDED(sRacer[i_index].viCar, sTrackData.vStartGrid[i_index])
CPRINTLN(DEBUG_MISSION, "Placed racer ", i_index)
ENDIF
ENDFOR
ENDPROC
/// PURPOSE: Set distance to the next checkpoint - for use with mission failing if the player drives in the opposite direction, see B*929375
PROC SET_DISTANCE_TO_NEXT_CHECKPOINT()
IF IS_PLAYER_PLAYING(PLAYER_ID())
fNextCheckpointDistance = GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(PLAYER_PED_ID()), sTrackData.vCheckPoint[sRaceHUD.iCurrentCheckPoint])
// CPRINTLN(DEBUG_MISSION, "Mission Race: fNextCheckpointDistance = ", fNextCheckpointDistance)
ENDIF
ENDPROC
/// PURPOSE: Resets race HUD to initial values
PROC INIT_RACE_HUD()
// Initialise HUD values
sRaceHUD.iStartTime = GET_GAME_TIMER()
sRaceHUD.iCurrentCheckPoint = 0
sRaceHUD.iCurrentLap = 1
sRaceHUD.iCurrentTime = GET_GAME_TIMER()
sRaceHUD.iMaxCheckpoints = sTrackData.iNumCheckpoints
SET_DISTANCE_TO_NEXT_CHECKPOINT()
ENDPROC
/// PURPOSE:
/// Displays the lap number when the player starts a new lap
PROC DISPLAY_LAPS_BIG_TEXT()
SWITCH eLapTextState
CASE LTS_LOAD
siLapText = REQUEST_SCALEFORM_MOVIE("MIDSIZED_MESSAGE")
IF HAS_SCALEFORM_MOVIE_LOADED(siLapText)
eLapTextState = LTS_INIT
ENDIF
BREAK
CASE LTS_INIT
BEGIN_SCALEFORM_MOVIE_METHOD(siLapText, "SHOW_SHARD_MIDSIZED_MESSAGE")
BEGIN_TEXT_COMMAND_SCALEFORM_STRING("BM_LAP_STR")
SET_COLOUR_OF_NEXT_TEXT_COMPONENT(HUD_COLOUR_WHITE)
ADD_TEXT_COMPONENT_SUBSTRING_TEXT_LABEL("BM_LAP")
SET_COLOUR_OF_NEXT_TEXT_COMPONENT(HUD_COLOUR_WHITE)
ADD_TEXT_COMPONENT_INTEGER(sRaceHUD.iCurrentLap)
SET_COLOUR_OF_NEXT_TEXT_COMPONENT(HUD_COLOUR_WHITE)
ADD_TEXT_COMPONENT_INTEGER(sTrackData.iNumLaps)
END_TEXT_COMMAND_SCALEFORM_STRING()
END_SCALEFORM_MOVIE_METHOD_RETURN_VALUE()
iLapTextTimer = GET_GAME_TIMER() + LAP_TEXT_TIME
eLapTextState = LTS_DISPLAY
BREAK
CASE LTS_DISPLAY
IF GET_GAME_TIMER() < iLapTextTimer - 500
IF HAS_SCALEFORM_MOVIE_LOADED(siLapText)
DRAW_SCALEFORM_MOVIE_FULLSCREEN(siLapText, 255, 255, 255, 255)
SET_SCRIPT_GFX_DRAW_ORDER(GFX_ORDER_AFTER_HUD)
ENDIF
ELSE
BEGIN_SCALEFORM_MOVIE_METHOD(siLapText, "SHARD_ANIM_OUT")
SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(ENUM_TO_INT(HUD_COLOUR_WHITE))
SCALEFORM_MOVIE_METHOD_ADD_PARAM_FLOAT(0.33)
END_SCALEFORM_MOVIE_METHOD()
eLapTextState = LTS_CLEANUP
ENDIF
BREAK
CASE LTS_CLEANUP
IF GET_GAME_TIMER() < iLapTextTimer
IF HAS_SCALEFORM_MOVIE_LOADED(siLapText)
DRAW_SCALEFORM_MOVIE_FULLSCREEN(siLapText, 255, 255, 255, 255)
SET_SCRIPT_GFX_DRAW_ORDER(GFX_ORDER_AFTER_HUD)
ENDIF
ELSE
IF HAS_SCALEFORM_MOVIE_LOADED(siLapText)
SET_SCALEFORM_MOVIE_AS_NO_LONGER_NEEDED(siLapText)
ENDIF
eLapTextState = LTS_READY
ENDIF
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE: Updates race HUD (current lap, lap time, etc)
PROC UPDATE_RACE_HUD()
IF sRaceData.eRaceTrack = SEA_RACE_01 OR sRaceData.eRaceTrack = SEA_RACE_02 OR sRaceData.eRaceTrack = SEA_RACE_03 OR sRaceData.eRaceTrack = SEA_RACE_04
OR sRaceData.eRaceTrack = STREET_RACE_02
SET_FAR_RIGHT_TITLE_POSITION_HUD_THIS_FRAME()
DRAW_P2P_RACE_HUD(GET_GAME_TIMER() - sRaceHUD.iStartTime, // Race time elapsed
0, // friend/world/crew/personal record
"", // "RACE TIME"
sRaceHUD.iPlayerPosition, // PositionNum - The position Number
sTrackData.iNumAIRacers+1, // PositionMaxNumber - The position maximum number
"", // "POSITION"
0, // ExtraTimeGiven - If any extra time is given to the timer, pass this in to display +xs -xs
HUD_COLOUR_WHITE, // PlacementColour - The position numbers can change colour
sRaceHUD.iCurrentCheckPoint, // Current checkpoint number
sRaceHUD.iMaxCheckpoints, // Total number of checkpoints
"",
HUD_COLOUR_WHITE,TRUE, -1, "", -1.0, HUD_COLOUR_WHITE,-1.0,
iGlobalBest)
ELSE
//MAINTAIN_BIG_MESSAGE()
DISPLAY_LAPS_BIG_TEXT()
DISABLE_SCRIPT_HUD_THIS_FRAME(HUDPART_RANKBAR)
IF IS_CONTROL_PRESSED(PLAYER_CONTROL, INPUT_MULTIPLAYER_INFO)
/*IF IS_CONTROL_JUST_PRESSED(PLAYER_CONTROL, INPUT_FRONTEND_DOWN)
g_Show_Lap_Dpad = NOT g_Show_Lap_Dpad
iDpadDownTimer = GET_GAME_TIMER() + DPAD_DOWN_TIME*/
IF NOT g_Show_Lap_Dpad
g_Show_Lap_Dpad = TRUE
ENDIF
ELSE//IF GET_GAME_TIMER() > iDpadDownTimer
IF g_Show_Lap_Dpad
g_Show_Lap_Dpad = FALSE
ENDIF
ENDIF
SET_FAR_RIGHT_TITLE_POSITION_HUD_THIS_FRAME()
DRAW_LOOP_RACE_HUD(GET_GAME_TIMER() - sRaceHUD.iStartTime, // Race time elapsed
0, // friend/world/crew/personal record
"", // "RACE TIME"
sRaceHUD.iCurrentLap, // Current lap number
sTrackData.iNumLaps, // Maximum number of laps
"", // "LAP"
sRaceHUD.iPlayerPosition, // PositionNum - The position Number
sTrackData.iNumAIRacers+1, // PositionMaxNumber - The position maximum number
"", // "POSITION"
0, // ExtraTimeGiven - If any extra time is given to the timer, pass this in to display +xs -xs
HUD_COLOUR_WHITE, // PlacementColour - The position numbers can change colour
sRaceHUD.iCurrentCheckPoint, // Current checkpoint number
sRaceHUD.iMaxCheckpoints, // Total number of checkpoints
"",
HUD_COLOUR_WHITE,
-1,
-1,
"",
HUD_COLOUR_RED,
sRaceHUD.iBestTime, // BestTime - Will need this hooked up with save data
"", // BestTimeTitle - NULL by default
GET_GAME_TIMER() - sRaceHUD.iCurrentTime, // Time elapsed for current lap
"", // "CURRENT LAP"
PODIUMPOS_NONE,TRUE,-1,"",-1,HUD_COLOUR_WHITE,HUD_COLOUR_WHITE,-1,
iGlobalBest)
ENDIF
ENDPROC
/// PURPOSE:
/// Checks if the player's vehicle is in water and if it should be
/// RETURNS:
/// TRUE if the player is in water and driving a speedophile OR is not in water and is driving a car or bike
FUNC BOOL IS_PLAYER_VEHICLE_APPROPRIATELY_IN_WATER()
IF IS_ENTITY_IN_WATER(GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID()))
IF sRaceData.eRaceType = RACETYPE_CAR
OR sRaceData.eRaceType = RACETYPE_BIKE
CPRINTLN(DEBUG_MISSION, "Mission Race: Player car / bike is in water")
RETURN FALSE
ELSE
IF iSpeedophileOutOfWaterTimer >=0
iSpeedophileOutOfWaterTimer = -1
ENDIF
ENDIF
ELSE
IF sRaceData.eRaceType = RACETYPE_SEA
IF IS_ENTITY_IN_AIR(GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID()))
iSpeedophileOutOfWaterTimer = -1
RETURN TRUE
ENDIF
IF iSpeedophileOutOfWaterTimer < 0
iSpeedophileOutOfWaterTimer = GET_GAME_TIMER() + 2000
ELIF GET_GAME_TIMER() > iSpeedophileOutOfWaterTimer
CPRINTLN(DEBUG_MISSION, "Mission Race: Player speedophile is not in water")
RETURN FALSE
ENDIF
ENDIF
ENDIF
RETURN TRUE
ENDFUNC
/// PURPOSE: Allows the player to respawn his car back on the track at the last checkpoint
PROC UPDATE_PLAYER_VEHICLE_RESPAWNING()
//SET_INPUT_EXCLUSIVE(FRONTEND_CONTROL, INPUT_SCRIPT_RUP)
IF IS_PED_UNINJURED(PLAYER_PED_ID())
SET_PED_RESET_FLAG(PLAYER_PED_ID(), PRF_OnlyExitVehicleOnButtonRelease, TRUE)
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
IF NOT IS_VEHICLE_DRIVEABLE(GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID()))
bPlayerVehicleNeedsRecovering = TRUE
racesRespawnState = RACES_RESPAWN_ACTIVE
CPRINTLN(DEBUG_MISSION, "Mission Race: Player vehicle is undriveable")
ENDIF
IF NOT IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("RACES_RHELP")
IF NOT IS_PLAYER_VEHICLE_APPROPRIATELY_IN_WATER()
PRINT_HELP_FOREVER("RACES_RHELP") // Press and hold ~INPUT_SCRIPT_RUP~ to return to the race.
iStuckHelpTimer = GET_GAME_TIMER() + STUCK_HELP_TIME
ENDIF
IF IS_VEHICLE_STUCK_TIMER_UP(sPlayerVehicle.vehPlayerVehicle, VEH_STUCK_HUNG_UP, 30000)
OR IS_VEHICLE_STUCK_TIMER_UP(sPlayerVehicle.vehPlayerVehicle, VEH_STUCK_JAMMED, 60000)
OR IS_VEHICLE_STUCK_TIMER_UP(sPlayerVehicle.vehPlayerVehicle, VEH_STUCK_ON_ROOF, 20000)
OR IS_VEHICLE_STUCK_TIMER_UP(sPlayerVehicle.vehPlayerVehicle, VEH_STUCK_ON_SIDE, 3000)
PRINT_HELP_FOREVER("RACES_RHELP") // Press and hold ~INPUT_SCRIPT_RUP~ to return to the race.
iStuckHelpTimer = GET_GAME_TIMER() + STUCK_HELP_TIME
CPRINTLN(DEBUG_MISSION, "Mission Race: Player vehicle is stuck")
ENDIF
ELIF GET_GAME_TIMER() > iStuckHelpTimer
IF IS_PLAYER_VEHICLE_APPROPRIATELY_IN_WATER()
OR IS_VEHICLE_STUCK_TIMER_UP(sPlayerVehicle.vehPlayerVehicle, VEH_STUCK_HUNG_UP, 30000)
OR IS_VEHICLE_STUCK_TIMER_UP(sPlayerVehicle.vehPlayerVehicle, VEH_STUCK_JAMMED, 60000)
OR IS_VEHICLE_STUCK_TIMER_UP(sPlayerVehicle.vehPlayerVehicle, VEH_STUCK_ON_ROOF, 20000)
OR IS_VEHICLE_STUCK_TIMER_UP(sPlayerVehicle.vehPlayerVehicle, VEH_STUCK_ON_SIDE, 3000)
CLEAR_HELP()
ENDIF
ENDIF
ENDIF
ENDIF
IF IS_CONTROL_PRESSED(PLAYER_CONTROL, INPUT_VEH_EXIT)
OR IS_DISABLED_CONTROL_PRESSED(PLAYER_CONTROL, INPUT_VEH_EXIT)
OR bPlayerVehicleNeedsRecovering = TRUE
INT i_waypoint_to_warp_to, i_waypoint_to_point_to
VECTOR v_waypoint_to_warp_to, v_next_waypoint
SWITCH racesRespawnState
CASE RACES_RESPAWN_SET
iPlayerWantsToRecoverVehicleTimer = GET_GAME_TIMER()
racesRespawnState = RACES_RESPAWN_INIT
BREAK
CASE RACES_RESPAWN_INIT
IF (GET_GAME_TIMER() - iPlayerWantsToRecoverVehicleTimer) > 500
iPlayerWantsToRecoverVehicleTimer = GET_GAME_TIMER() // Set again
racesRespawnState = RACES_RESPAWN_CHECK
ENDIF
BREAK
CASE RACES_RESPAWN_CHECK
DRAW_GENERIC_METER(GET_GAME_TIMER() - iPlayerWantsToRecoverVehicleTimer, 1500, "RACES_RMETER", HUD_COLOUR_RED, 0, HUDORDER_EIGHTHBOTTOM, -1, -1, FALSE, TRUE)
IF (GET_GAME_TIMER() - iPlayerWantsToRecoverVehicleTimer) >= 400
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_VEH_EXIT)
ENDIF
IF (GET_GAME_TIMER() - iPlayerWantsToRecoverVehicleTimer) >= 1500
CPRINTLN(DEBUG_MISSION, "Mission Race: Player has held triangle for long enough, so begin respawning at last checkpoint")
racesRespawnState = RACES_RESPAWN_ACTIVE
ENDIF
BREAK
CASE RACES_RESPAWN_ACTIVE
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_VEH_EXIT)
IF DOES_ENTITY_EXIST(sPlayerVehicle.vehPlayerVehicle)
/*IF sRaceHUD.iCurrentCheckPoint = 0
WAYPOINT_RECORDING_GET_COORD(sRaceWaypointRecording, 0, v_waypoint_to_warp_to)
WAYPOINT_RECORDING_GET_COORD(sRaceWaypointRecording, 1, v_next_waypoint)
ELSE*/
VECTOR vCheckpointToWarpTo
IF sRaceHUD.iCurrentCheckPoint > 0
vCheckpointToWarpTo = sTrackData.vCheckPoint[sRaceHUD.iCurrentCheckPoint - 1]
ELSE
vCheckpointToWarpTo = sTrackData.vStartGrid[sTrackData.iNumAIRacers]
ENDIF
WAYPOINT_RECORDING_GET_CLOSEST_WAYPOINT(sRaceWaypointRecording, vCheckpointToWarpTo, i_waypoint_to_warp_to)
i_waypoint_to_point_to = i_waypoint_to_warp_to + 1
IF i_waypoint_to_point_to >= iNumWaypoints
i_waypoint_to_point_to = 0
ENDIF
WAYPOINT_RECORDING_GET_COORD(sRaceWaypointRecording, i_waypoint_to_warp_to, v_waypoint_to_warp_to)
WAYPOINT_RECORDING_GET_COORD(sRaceWaypointRecording, i_waypoint_to_point_to, v_next_waypoint)
//ENDIF
IF NOT IS_ANY_VEHICLE_NEAR_POINT(v_waypoint_to_warp_to, 5) // Make sure the new checkpoint we're warping to is clear
OR GET_DISTANCE_BETWEEN_ENTITY_AND_COORD(sPlayerVehicle.vehPlayerVehicle, v_waypoint_to_warp_to) < 5 // In case it's this vehicle within the 5 metres
SET_ENTITY_COORDS(sPlayerVehicle.vehPlayerVehicle, v_waypoint_to_warp_to)
SET_ENTITY_HEADING_FACE_COORDS(sPlayerVehicle.vehPlayerVehicle, v_next_waypoint) // Face next waypoint
SET_VEHICLE_FIXED(sPlayerVehicle.vehPlayerVehicle)
IF NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), sPlayerVehicle.vehPlayerVehicle)
SET_PED_INTO_VEHICLE(PLAYER_PED_ID(), sPlayerVehicle.vehPlayerVehicle)
ENDIF
SET_GAMEPLAY_CAM_RELATIVE_PITCH()
SET_GAMEPLAY_CAM_RELATIVE_HEADING()
IF IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("RACES_RHELP")
CLEAR_HELP()
ENDIF
CPRINTLN(DEBUG_MISSION, "Mission Race: Successfully repositioned player's vehicle")
racesRespawnState = RACES_RESPAWN_WAIT
ENDIF
ENDIF
BREAK
CASE RACES_RESPAWN_WAIT
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_VEH_EXIT)
IF NOT IS_CONTROL_PRESSED(PLAYER_CONTROL, INPUT_VEH_EXIT)
AND NOT IS_DISABLED_CONTROL_PRESSED(PLAYER_CONTROL, INPUT_VEH_EXIT)
CPRINTLN(DEBUG_MISSION, "Mission Race: Player released triangle")
ENABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_VEH_EXIT)
iPlayerWantsToRecoverVehicleTimer = -1
bPlayerVehicleNeedsRecovering = FALSE
racesRespawnState = RACES_RESPAWN_SET
ENDIF
BREAK
ENDSWITCH
ELSE
/*IF iPlayerWantsToRecoverVehicleTimer > -1
AND (GET_GAME_TIMER() - iPlayerWantsToRecoverVehicleTimer) < 500 // Player only tapped triangle, so wants to exit his car
CPRINTLN(DEBUG_MISSION, "Mission Race: Player wants to exit vehicle, triangle was pressed for ", GET_GAME_TIMER() - iPlayerWantsToRecoverVehicleTimer)
IF GET_ENTITY_SPEED(PLAYER_PED_ID()) < 1.0
TASK_LEAVE_ANY_VEHICLE(PLAYER_PED_ID())
ELSE
TASK_LEAVE_ANY_VEHICLE(PLAYER_PED_ID(), 0, ECF_JUMP_OUT)
ENDIF
ENDIF*/
iPlayerWantsToRecoverVehicleTimer = -1
bPlayerVehicleNeedsRecovering = FALSE
racesRespawnState = RACES_RESPAWN_SET
ENDIF
ENDPROC
/// PURPOSE: Works out which checkpoint type is needed based upon angle between checkpoints
FUNC CHECKPOINT_TYPE GET_RACE_CHECKPOINT_TYPE(INT iCheckNum)
VECTOR pos, pos2, pos3
VECTOR vec1, vec2
FLOAT fReturnAngle
pos = sTrackData.vCheckpoint[iCheckNum]
IF iCheckNum+1 = sTrackData.iNumCheckpoints //- 1
pos2 = sTrackData.vCheckpoint[0]
ELSE
pos2 = sTrackData.vCheckpoint[iCheckNum + 1]
ENDIF
IF iCheckNum - 1 >= 0
pos3 = sTrackData.vCheckpoint[iCheckNum-1]
ENDIF
vec1 = pos3 - pos
vec2 = pos2 - pos
fReturnAngle = GET_ANGLE_BETWEEN_2D_VECTORS(vec1.x, vec1.y, vec2.x, vec2.y)
IF fReturnAngle > 180
fReturnAngle = (360.0 - fReturnAngle)
ENDIF
IF fReturnAngle < fChev3
RETURN CHECKPOINT_RACE_GROUND_CHEVRON_3
ELIF fReturnAngle < fChev2
RETURN CHECKPOINT_RACE_GROUND_CHEVRON_2
ELIF fReturnAngle < fChev1
RETURN CHECKPOINT_RACE_GROUND_CHEVRON_1
ELSE
RETURN CHECKPOINT_RACE_GROUND_CHEVRON_1
ENDIF
RETURN CHECKPOINT_RACE_GROUND_CHEVRON_1
ENDFUNC
/// PURPOSE: Gets appropriate checkpoint colour based on chevron type
/*FUNC HUD_COLOURS GET_RACE_CHECKPOINT_COLOUR(CHECKPOINT_TYPE cpType)
IF cpType = CHECKPOINT_RACE_GROUND_CHEVRON_3
OR cpType = CHECKPOINT_RACE_WATER_CHEVRON_3
RETURN HUD_COLOUR_YELLOWDARK
ELIF cpType = CHECKPOINT_RACE_GROUND_CHEVRON_2
OR cpType = CHECKPOINT_RACE_WATER_CHEVRON_2
RETURN HUD_COLOUR_YELLOW
ELSE
RETURN HUD_COLOUR_YELLOWLIGHT
ENDIF
ENDFUNC*/
/// PURPOSE:
/// Gets appropriate checkpoint alpha based on the player's distance from the checkpoint
FUNC INT GET_CHECKPOINT_ALPHA(INT iCheckpoint)
FLOAT fDis
INT iA = 240
IF NOT IS_ENTITY_DEAD(PLAYER_PED_ID())
fDis = GET_DISTANCE_BETWEEN_COORDS(sTrackData.vCheckPoint[iCheckpoint], GET_ENTITY_COORDS(PLAYER_PED_ID()))
IF fDis > 100.0
iA = 240
ELSE
iA = ROUND(fDis*2.4)
ENDIF
ENDIF
RETURN iA
ENDFUNC
FUNC VECTOR CALCULATE_QUAD_NORMAL(VECTOR &verts[])
INT i
VECTOR u, v
VECTOR norm
REPEAT COUNT_OF(verts) i
u = verts[i]
v = verts[(i + 1) % COUNT_OF(verts)]
norm.x += (u.y - v.y) * (u.z + v.z)
norm.y += (u.z - v.z) * (u.x + v.x)
norm.z += (u.x - v.x) * (u.y + v.y)
ENDREPEAT
RETURN NORMALISE_VECTOR(norm)
ENDFUNC
FUNC VECTOR GET_CHECKOINT_OFFSET(INT i)
SWITCH i
CASE 0 RETURN <<0,0,1>>
CASE 1 RETURN <<FMMC_CHECKPOINT_SIZE/2,0,1>>
CASE 2 RETURN <<-FMMC_CHECKPOINT_SIZE/2,0,1>>
CASE 3 RETURN <<0,FMMC_CHECKPOINT_SIZE/2,1>>
CASE 4 RETURN <<0,-FMMC_CHECKPOINT_SIZE/2,1>>
CASE 5 RETURN <<FMMC_CHECKPOINT_SIZE/2,FMMC_CHECKPOINT_SIZE/2,1>>
CASE 6 RETURN <<-FMMC_CHECKPOINT_SIZE/2,-FMMC_CHECKPOINT_SIZE/2,1>>
CASE 7 RETURN <<FMMC_CHECKPOINT_SIZE/2,-FMMC_CHECKPOINT_SIZE/2,1>>
CASE 8 RETURN <<-FMMC_CHECKPOINT_SIZE/2,FMMC_CHECKPOINT_SIZE/2,1>>
ENDSWITCH
RETURN <<0,0,0>>
ENDFUNC
/// PURPOSE:
/// Clips the bottom off the checkpoint so it doesn't poke through roads
/// PARAMS:
/// checkP - The checpoint to clip
/// vCheckPointGroundPos - The coordinates of the checkpoint
PROC CLIP_THE_CHECKPOINT(CHECKPOINT_INDEX checkP, VECTOR vCheckPointGroundPos)
int i
VECTOR vecArray[8]
FLOAT fGround
FOR i = 0 TO 7
vecArray[i] = vCheckPointGroundPos + GET_CHECKOINT_OFFSET(i)
GET_GROUND_Z_FOR_3D_COORD(vecArray[i],fGround)
IF fGround < vCheckPointGroundPos.z - 2
OR fGround > vCheckPointGroundPos.z + 2
vecArray[i].z = vCheckPointGroundPos.z
PRINTINT(i)PRINTSTRING("vPos z. vecArray[i] = ")PRINTVECTOR(vecArray[i])PRINTNL()
ELSE
vecArray[i].z = fGround
PRINTINT(i)PRINTSTRING("offset z. vecArray[i] = ")PRINTVECTOR(vecArray[i])PRINTNL()
ENDIF
ENDFOR
VECTOR vFinalNormal = CALCULATE_QUAD_NORMAL(vecArray)
PRINTNL()PRINTSTRING("vFinalNormal = ")PRINTVECTOR(vFinalNormal)PRINTNL()PRINTNL()
SET_CHECKPOINT_CLIPPLANE_WITH_POS_NORM(checkP, vCheckPointGroundPos - <<0,0,0.3>>, vFinalNormal)
ENDPROC
/// PURPOSE: Blip the destination marker
PROC BLIP_CURRENT_CHECKPOINT(INT iCheckpoint, BOOL bRaceFlag = FALSE, BOOL bDoPulse = TRUE)
INT iR, iG, iB, iA, iR2, iG2, iB2, iA2, iPreviousCheck
CHECKPOINT_TYPE cpType = GET_RACE_CHECKPOINT_TYPE(iCheckpoint)
GET_HUD_COLOUR(HUD_COLOUR_YELLOWLIGHT, iR, iG, iB, iA)
GET_HUD_COLOUR(HUD_COLOUR_NORTH_BLUE, iR2, iG2, iB2, iA2)
iA = GET_CHECKPOINT_ALPHA(iCheckpoint)
// Is this marker already shown?
IF DOES_BLIP_EXIST(biCurrentCheckPoint)
IF ciCurrentCheckPoint != NULL
SET_CHECKPOINT_RGBA(ciCurrentCheckPoint,iR, iG, iB, iA)
SET_CHECKPOINT_RGBA2(ciCurrentCheckPoint,iR2, iG2, iB2, iA)
ENDIF
ELSE
VECTOR currentCheckpoint, nextCheckpoint, prevCheckpoint, vHeightCheck
FLOAT fCheckPointSize, fCheckPointHeight, fHeightCheck
// Get current checkpoint position
currentCheckpoint = sTrackData.vCheckPoint[iCheckpoint]
// check the type of checkpoint needed
vHeightCheck = currentCheckpoint
vHeightCheck.z += 20.0
BOOL bUnderBridge = FALSE
IF GET_GROUND_Z_FOR_3D_COORD(vHeightCheck, fHeightCheck)
IF fHeightCheck > (currentCheckpoint.z + 1.0)
fCheckPointSize = FMMC_CHECKPOINT_SIZE*1*0.66
fCheckPointHeight = 2.0
bUnderBridge = TRUE
ELSE
fCheckPointSize = (CHECKPOINT_VISUAL_SIZE * CHECKPOINT_VISUAL_SIZE_MODIFIER)
fCheckPointHeight = fCheckpointDrawSize
ENDIF
ELSE
fCheckPointSize = (CHECKPOINT_VISUAL_SIZE * CHECKPOINT_VISUAL_SIZE_MODIFIER)
fCheckPointHeight = fCheckpointDrawSize
ENDIF
// Next checkpoint
IF iCheckpoint = sTrackData.iNumCheckpoints - 1
nextCheckpoint = sTrackData.vCheckPoint[0]
ELSE
nextCheckpoint = sTrackData.vCheckPoint[iCheckpoint+1]
ENDIF
// Previous checkpoint
iPreviousCheck = iCheckpoint - 1
IF iPreviousCheck < 0
iPreviousCheck = sTrackData.iNumCheckpoints - 1
ENDIF
prevCheckpoint = sTrackData.vCheckPoint[iPreviousCheck]
// Blip checkpoint
biCurrentCheckPoint = ADD_BLIP_FOR_COORD(currentCheckpoint)
SHOW_HEIGHT_ON_BLIP(biCurrentCheckPoint, FALSE)
// Setup race arrow for current checkpoint
IF NOT bRaceFlag
SET_BLIP_COLOUR(biCurrentCheckPoint, BLIP_COLOUR_YELLOW)
SET_BLIP_SCALE(biCurrentCheckPoint, 1.2)
SET_BLIP_PRIORITY(biCurrentCheckPoint, BLIPPRIORITY_HIGHEST)
IF bUnderBridge
SWITCH cpType
CASE CHECKPOINT_RACE_WATER_CHEVRON_3
CASE CHECKPOINT_RACE_GROUND_CHEVRON_3
cpType = CHECKPOINT_RACE_AIR_CHEVRON_3
BREAK
CASE CHECKPOINT_RACE_WATER_CHEVRON_2
CASE CHECKPOINT_RACE_GROUND_CHEVRON_2
cpType = CHECKPOINT_RACE_AIR_CHEVRON_2
BREAK
CASE CHECKPOINT_RACE_WATER_CHEVRON_1
CASE CHECKPOINT_RACE_GROUND_CHEVRON_1
cpType = CHECKPOINT_RACE_AIR_CHEVRON_1
BREAK
ENDSWITCH
ENDIF
ciCurrentCheckPoint = CREATE_CHECKPOINT(cpType,(currentCheckpoint+<<0,0,fCheckPointHeight>>), nextCheckpoint, fCheckPointSize, iR, iG, iB, iA)//150)
SET_CHECKPOINT_RGBA2(ciCurrentCheckPoint,iR2, iG2, iB2, iA)
CLIP_THE_CHECKPOINT(ciCurrentCheckPoint, currentCheckpoint)
// Chequered flag!
ELSE
SET_BLIP_SPRITE(biCurrentCheckPoint, RADAR_TRACE_RACEFLAG)
SET_BLIP_PRIORITY(biCurrentCheckPoint, BLIPPRIORITY_HIGH)
SET_BLIP_SCALE(biCurrentCheckPoint, 1.2)
ciCurrentCheckPoint = CREATE_CHECKPOINT(CHECKPOINT_RACE_GROUND_FLAG, (currentCheckpoint+<<0,0,fCheckPointHeight>>), nextCheckpoint, fCheckPointSize, iR, iG, iB, iA)//150)
SET_CHECKPOINT_RGBA2(ciCurrentCheckPoint,iR2, iG2, iB2, iA)
CLIP_THE_CHECKPOINT(ciCurrentCheckPoint, currentCheckpoint)
ENDIF
IF bDoPulse
cpType = GET_RACE_CHECKPOINT_TYPE(iPreviousCheck)
GET_HUD_COLOUR(HUD_COLOUR_WHITE, iR, iG, iB, iPrevAlpha)
iPrevAlpha = 180
IF ciPreviousCheckPoint != NULL
DELETE_CHECKPOINT(ciPreviousCheckPoint)
ENDIF
ciPreviousCheckPoint = CREATE_CHECKPOINT(cpType,(prevCheckpoint+<<0,0,fCheckPointHeight>>), currentCheckpoint, fCheckPointSize, iR, iG, iB, iPrevAlpha)//150)
ENDIF
IF sRaceData.eRaceType = RACETYPE_SEA
SET_CHECKPOINT_CYLINDER_HEIGHT(ciCurrentCheckPoint,16.0,16.0,100)
SET_CHECKPOINT_CYLINDER_HEIGHT(ciPreviousCheckPoint,16.0,16.0,100)
ELSE
SET_CHECKPOINT_CYLINDER_HEIGHT(ciCurrentCheckPoint,9.5,9.5,100)
SET_CHECKPOINT_CYLINDER_HEIGHT(ciPreviousCheckPoint,9.5,9.5,100)
ENDIF
SET_BLIP_NAME_FROM_TEXT_FILE(biCurrentCheckPoint, "BLIP_CPOINT")
ENDIF
// Fade out the green checkpoint
IF ciPreviousCheckPoint != NULL
iPrevAlpha -= 25
IF iPrevAlpha > 0
GET_HUD_COLOUR(HUD_COLOUR_WHITE, iR, iG, iB, iA)
SET_CHECKPOINT_RGBA(ciPreviousCheckPoint,iR, iG, iB, iPrevAlpha)
ELSE
DELETE_CHECKPOINT(ciPreviousCheckPoint)
ENDIF
ENDIF
ENDPROC
/// PURPOSE: Draw the current race marker
PROC BLIP_NEXT_CHECKPOINT(VECTOR nextCheckpoint, BOOL bRaceFlag = FALSE)
IF NOT DOES_BLIP_EXIST(biNextCheckPoint)
biNextCheckPoint = ADD_BLIP_FOR_COORD(nextCheckpoint)
SHOW_HEIGHT_ON_BLIP(biNextCheckPoint, FALSE)
IF NOT bRaceFlag
// Normal checkpoint
SET_BLIP_COLOUR(biNextCheckPoint, BLIP_COLOUR_YELLOW)
SET_BLIP_SCALE(biNextCheckPoint, 0.7)
ELSE
// Finish line
IF sRaceHUD.iCurrentLap = sTrackData.iNumLaps
SET_BLIP_SPRITE(biNextCheckPoint, RADAR_TRACE_RACEFLAG)
SET_BLIP_SCALE(biNextCheckPoint, 1.2)
/*ELSE
SET_BLIP_COLOUR(biNextCheckPoint, BLIP_COLOUR_WHITE)
SET_BLIP_SCALE(biNextCheckPoint, 0.7)*/
ENDIF
ENDIF
SET_BLIP_NAME_FROM_TEXT_FILE(biNextCheckPoint, "BLIP_CPOINT")
ENDIF
ENDPROC
/// PURPOSE: Draw the current race markers
PROC DRAW_RACE_CHECKPOINTS(BOOL bDoPulse = TRUE)
// Get next checkpoint
INT iNextCheckpoint = sRaceHUD.iCurrentCheckPoint + 1
// POINT TO POINT RACE
IF sRaceData.bP2P
// Final checkpoint of current lap
IF sRaceHUD.iCurrentCheckPoint = sTrackData.iNumCheckpoints - 1
BLIP_CURRENT_CHECKPOINT(sTrackData.iNumCheckpoints-1, TRUE, bDoPulse)
ELSE
// Normal checkpoint
BLIP_CURRENT_CHECKPOINT(sRaceHUD.iCurrentCheckPoint, FALSE, bDoPulse)
// Next checkpoint is the finish/new lap
IF iNextCheckpoint = sTrackData.iNumCheckpoints - 1
BLIP_NEXT_CHECKPOINT(sTrackData.vCheckPoint[iNextCheckpoint], TRUE)
ELSE
BLIP_NEXT_CHECKPOINT(sTrackData.vCheckPoint[iNextCheckpoint])
ENDIF
ENDIF
// CIRCUIT RACE
ELSE
// Final checkpoint of current lap
IF sRaceHUD.iCurrentCheckPoint = sTrackData.iNumCheckpoints - 1
// Finish line
IF sRaceHUD.iCurrentLap = sTrackData.iNumLaps
BLIP_CURRENT_CHECKPOINT(sRaceHUD.iCurrentCheckPoint, TRUE, bDoPulse)
ELSE
// New lap
BLIP_CURRENT_CHECKPOINT(sRaceHUD.iCurrentCheckPoint, FALSE, bDoPulse)
BLIP_NEXT_CHECKPOINT(sTrackData.vCheckPoint[0])
ENDIF
ELSE
// Normal checkpoint
BLIP_CURRENT_CHECKPOINT(sRaceHUD.iCurrentCheckPoint, FALSE, bDoPulse)
// Next checkpoint is the finish/new lap
IF iNextCheckpoint = sTrackData.iNumCheckpoints - 1
BLIP_NEXT_CHECKPOINT(sTrackData.vCheckPoint[iNextCheckpoint], TRUE)
ELSE
BLIP_NEXT_CHECKPOINT(sTrackData.vCheckPoint[iNextCheckpoint])
ENDIF
ENDIF
ENDIF
ENDPROC
PROC REMOVE_PLAYER_VEHICLE_BLIP_AND_TEXT()
IF DOES_BLIP_EXIST(sPlayerVehicle.biPlayerVehicle)
REMOVE_BLIP(sPlayerVehicle.biPlayerVehicle)
IF sRaceData.eRaceType = RACETYPE_SEA
TRIGGER_MUSIC_EVENT("MGSR_BACK_ON")
ENDIF
ENDIF
IF IS_THIS_PRINT_BEING_DISPLAYED("CAR_BACK") // ~s~Get back in the ~b~car.
OR IS_THIS_PRINT_BEING_DISPLAYED("BIKE_BACK") // ~s~Get back on the ~b~bike.
OR IS_THIS_PRINT_BEING_DISPLAYED("SEA_BACK") // ~s~Get back on the ~b~seashark.
CLEAR_PRINTS()
ENDIF
ENDPROC
FUNC BOOL IS_PLAYER_VEHICLE_STUCK()
IF NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), sPlayerVehicle.vehPlayerVehicle) AND sRaceData.eRaceType = RACETYPE_CAR
IF IS_VEHICLE_STUCK_TIMER_UP(sPlayerVehicle.vehPlayerVehicle, VEH_STUCK_ON_ROOF, 1000)
OR IS_VEHICLE_STUCK_TIMER_UP(sPlayerVehicle.vehPlayerVehicle, VEH_STUCK_ON_SIDE, 1000)
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE: Checks for player being in his race vehicle, see B*929894
FUNC BOOL IS_PLAYER_IN_VEHICLE()
IF IS_PLAYER_PLAYING(PLAYER_ID())
IF GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) > 0
failReason = FAIL_ALERTED_COPS
eRaceState = RACE_STATE_FAIL
eRaceSubState = RACE_SUBSTATE_SETUP
RETURN FALSE
ENDIF
ENDIF
IF DOES_ENTITY_EXIST(sPlayerVehicle.vehPlayerVehicle)
IF IS_VEHICLE_DRIVEABLE(sPlayerVehicle.vehPlayerVehicle) AND NOT IS_PLAYER_VEHICLE_STUCK()
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), sPlayerVehicle.vehPlayerVehicle)
REMOVE_PLAYER_VEHICLE_BLIP_AND_TEXT()
UPDATE_PLAYER_VEHICLE_RESPAWNING()
RETURN TRUE
ELSE
IF IS_PED_SITTING_IN_ANY_VEHICLE(PLAYER_PED_ID())
CPRINTLN(DEBUG_MISSION, "Mission Race: Player has got into a different vehicle so abandoning race")
failReason = FAIL_ABANDONED_RACE
eRaceState = RACE_STATE_FAIL
eRaceSubState = RACE_SUBSTATE_SETUP
ELSE
CLEANUP_CHECKPOINTS(FALSE)
// Remove the help message if the player leaves their vehicle
IF IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("RACES_RHELP") AND GET_GAME_TIMER() > iStuckHelpTimer
CLEAR_HELP()
ENDIF
IF NOT DOES_BLIP_EXIST(sPlayerVehicle.biPlayerVehicle) AND NOT
(sRaceData.eRaceType = RACETYPE_CAR AND (IS_VEHICLE_STUCK_TIMER_UP(sPlayerVehicle.vehPlayerVehicle, VEH_STUCK_ON_ROOF, 1000)
OR IS_VEHICLE_STUCK_TIMER_UP(sPlayerVehicle.vehPlayerVehicle, VEH_STUCK_ON_SIDE, 1000)))
sPlayerVehicle.biPlayerVehicle = ADD_BLIP_FOR_ENTITY(sPlayerVehicle.vehPlayerVehicle)
SET_BLIP_AS_FRIENDLY(sPlayerVehicle.biPlayerVehicle, TRUE)
SET_BLIP_SCALE(sPlayerVehicle.biPlayerVehicle, BLIP_SIZE_VEHICLE)
IF sRaceData.eRaceType = RACETYPE_SEA
TRIGGER_MUSIC_EVENT("MGSR_FELL_OFF")
ENDIF
IF sPlayerVehicle.bDisplayedGetBackInVehicleText = FALSE
sPlayerVehicle.bDisplayedGetBackInVehicleText = TRUE
SWITCH sRaceData.eRaceType
CASE RACETYPE_BIKE
PRINT_NOW("BIKE_BACK", DEFAULT_GOD_TEXT_TIME, 1) // ~s~Get back on the ~b~bike.
BREAK
CASE RACETYPE_CAR
PRINT_NOW("CAR_BACK", DEFAULT_GOD_TEXT_TIME, 1) // ~s~Get back in the ~b~car.
BREAK
CASE RACETYPE_SEA
PRINT_NOW("SEA_BACK", DEFAULT_GOD_TEXT_TIME, 1) // ~s~Get back in the ~b~vehicle.
BREAK
ENDSWITCH
ENDIF
ENDIF
ENDIF
/*IF sPlayerVehicle.vehPlayerVehicle = GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID())
CLEANUP_CHECKPOINTS(FALSE)
IF NOT DOES_BLIP_EXIST(sPlayerVehicle.biPlayerVehicle)
sPlayerVehicle.biPlayerVehicle = ADD_BLIP_FOR_ENTITY(sPlayerVehicle.vehPlayerVehicle)
SET_BLIP_AS_FRIENDLY(sPlayerVehicle.biPlayerVehicle, TRUE)
SET_BLIP_SCALE(sPlayerVehicle.biPlayerVehicle, BLIP_SIZE_VEHICLE)
IF sRaceData.eRaceType = RACETYPE_SEA
TRIGGER_MUSIC_EVENT("MGSR_FELL_OFF")
ENDIF
IF sPlayerVehicle.bDisplayedGetBackInVehicleText = FALSE
sPlayerVehicle.bDisplayedGetBackInVehicleText = TRUE
SWITCH sRaceData.eRaceType
CASE RACETYPE_BIKE
PRINT_NOW("CMN_GENGETBCKBK", DEFAULT_GOD_TEXT_TIME, 1) // ~s~Get back on the ~b~bike.
BREAK
CASE RACETYPE_CAR
PRINT_NOW("CMN_GENGETBCK", DEFAULT_GOD_TEXT_TIME, 1) // ~s~Get back in the ~b~car.
BREAK
CASE RACETYPE_SEA
PRINT_NOW("SEA_GETINVEH", DEFAULT_GOD_TEXT_TIME, 1) // ~s~Get back in the ~b~vehicle.
BREAK
ENDSWITCH
ENDIF
ENDIF
ELSE
CPRINTLN(DEBUG_MISSION, "Mission Race: Player has got into a different vehicle so abandoning race")
failReason = FAIL_ABANDONED_RACE
eRaceState = RACE_STATE_FAIL
eRaceSubState = RACE_SUBSTATE_SETUP
ENDIF*/
ENDIF
ELSE
REMOVE_PLAYER_VEHICLE_BLIP_AND_TEXT()
failReason = FAIL_WRECKED
eRaceState = RACE_STATE_FAIL
eRaceSubState = RACE_SUBSTATE_SETUP
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE: Updates checkpoint markers
FUNC BOOL UPDATE_RACE_CHECKPOINTS(BOOL bDoPulse = TRUE)
IF IS_PLAYER_PLAYING(PLAYER_ID())
// Handle drawing checkpoints and race arrows
DRAW_RACE_CHECKPOINTS(bDoPulse)
// Handle J-skipping
#IF IS_DEBUG_BUILD
IF IS_KEYBOARD_KEY_JUST_PRESSED(KEY_J)
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
SET_PED_COORDS_KEEP_VEHICLE(PLAYER_PED_ID(), sTrackData.vCheckPoint[sRaceHUD.iCurrentCheckPoint])
ENDIF
ENDIF
#ENDIF
// Swap for less costly IS_ENTITY_AT_COORDS check?
IF GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(PLAYER_PED_ID()), sTrackData.vCheckPoint[sRaceHUD.iCurrentCheckPoint]) <= CHECKPOINT_LOCATE_SIZE
PLAY_SOUND_FRONTEND(-1, "CHECKPOINT_NORMAL", "HUD_MINI_GAME_SOUNDSET", FALSE)
// End of current lap
IF sRaceHUD.iCurrentCheckPoint = sRaceHUD.iMaxCheckpoints - 1
// Reset lap time
INT iLapTime = GET_GAME_TIMER() - sRaceHUD.iCurrentTime
IF sRaceHUD.iBestTime < 0 OR sRaceHUD.iBestTime > iLapTime
sRaceHUD.iBestTime = iLapTime
ENDIF
sRaceHUD.iCurrentTime = GET_GAME_TIMER()
// Chequered flag!
IF sRaceHUD.iCurrentLap = sTrackData.iNumLaps
CLEANUP_RACE_CHECKPOINTS()
RETURN TRUE
ELSE
// New lap
sRaceHUD.iCurrentLap++
sRaceHUD.iCurrentCheckPoint = 0
//SETUP_NEW_BIG_MESSAGE_WITH_2_INTS(BIG_MESSAGE_LAP, sRaceHUD.iCurrentLap, sTrackData.iNumLaps)
eLapTextState = LTS_LOAD
ENDIF
ELSE
// Update checkpoint
sRaceHUD.iCurrentCheckPoint++
ENDIF
//KILL_CHASE_HINT_CAM(localChaseHintCamStruct)
//CONTROL_COORD_CHASE_HINT_CAM_ANY_MEANS(localChaseHintCamStruct, sTrackData.vCheckPoint[sRaceHUD.iCurrentCheckPoint])
SET_DISTANCE_TO_NEXT_CHECKPOINT()
// Remove checkpoint marker
CLEANUP_RACE_CHECKPOINTS()
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Returns the base speed for this particular rival driver on this particular track
FUNC FLOAT GET_BASE_SPEED(INT i_index)
FLOAT f_base_speed
SWITCH sRacer[i_index].drivingSpeed
CASE AI_DRIVING_SPEED_SLOW
f_base_speed = sTrackData.drivingSpeedSlow
BREAK
CASE AI_DRIVING_SPEED_MEDIUM
f_base_speed = sTrackData.drivingSpeedMedium
BREAK
CASE AI_DRIVING_SPEED_FAST
f_base_speed = sTrackData.drivingSpeedFast
BREAK
ENDSWITCH
RETURN f_base_speed
ENDFUNC
/// PURPOSE:
/// Increases/decreases the driver's speed if it's behind/ahead of the player
PROC UPDATE_WAYPOINT_RECORDING_SPEED(INT i_index)
FLOAT f_new_speed = GET_BASE_SPEED(i_index)
FLOAT f_distance = GET_DISTANCE_BETWEEN_ENTITIES(sRacer[i_index].viCar, PLAYER_PED_ID())
IF sRacer[i_index].iCurrentPoint-1 < sRaceHUD.iCurrentCheckPoint // Rival is behind the player
IF f_distance > 50
f_new_speed += (f_new_speed*1.0) // Rival is far behind so speed up a lot
ELIF f_distance > 25
f_new_speed += (f_new_speed*0.7) // Rival is far behind so speed up a lot
ELSE
f_new_speed += (f_new_speed*0.3)
ENDIF
ELSE // Rival is ahead of the player
IF f_distance > 100
f_new_speed -= (f_new_speed*0.3) // Rival is far ahead so decrease a lot
ELIF f_distance > 25
f_new_speed -= (f_new_speed*0.1)
ENDIF
ENDIF
IF sRacer[i_index].fLastSpeed <> f_new_speed
sRacer[i_index].fLastSpeed = f_new_speed
SET_DRIVE_TASK_CRUISE_SPEED(sRacer[i_index].piDriver, f_new_speed)
//CPRINTLN(DEBUG_MISSION, "Mission Race: Rival ", i_index, " speed set to ", f_new_speed)
ENDIF
ENDPROC
PROC UPDATE_AI_CHECKPOINT(INT i_index)
INT iFinalCheckpoint
IF sTrackData.iNumLaps = 0
iFinalCheckpoint = sTrackData.iNumCheckpoints - 2
ELSE
iFinalCheckpoint = sTrackData.iNumCheckpoints - 1
ENDIF
IF sRacer[i_index].iCurrentPoint = iFinalCheckpoint // End of current lap
sRacer[i_index].iCurrentPoint = 0
sRacer[i_index].iCurrentLap++
// Don't want AI racers to stop at the end of the final lap, keep them driving
CPRINTLN(DEBUG_MISSION, "Mission Race: Rival ", i_index, " starting lap ", sRacer[i_index].iCurrentLap)
// Switch the point to point racers to a wander task
IF sRacer[i_index].iCurrentLap > sTrackData.iNumLaps
TASK_VEHICLE_DRIVE_WANDER(sRacer[i_index].piDriver, sRacer[i_index].viCar, 30, DRIVINGMODE_AVOIDCARS_RECKLESS)
ENDIF
ELSE
sRacer[i_index].iCurrentPoint++
ENDIF
// Update AI destination
IF sRacer[i_index].stateAI = AI_RACER_ACTIVE
UPDATE_WAYPOINT_RECORDING_SPEED(i_index)
ENDIF
ENDPROC
/// PURPOSE:
/// Checks if a racer is in a suitable point to start warping
/// PARAMS:
/// i_index - which racer we're checking
/// bUsingHao - is Hao in this race
/// RETURNS:
/// TRUE if we might need to warp this racer
FUNC BOOL SHOULD_WE_CHECK_FOR_WARPING(INT i_index, BOOL bUsingHao)
IF (sRacer[i_index].iCurrentLap < sRaceHUD.iCurrentLap)
// a lap behind the player so good to check
RETURN TRUE
ENDIF
IF (sRacer[i_index].iCurrentLap > sRaceHUD.iCurrentLap)
// a lap ahead of the player so no need to check
RETURN FALSE
ENDIF
IF (sRacer[i_index].iCurrentLap = sRaceHUD.iCurrentLap AND sRacer[i_index].iCurrentPoint >= sRaceHUD.iCurrentCheckPoint)
// Racer is on the same lap as the player but at a later checkpoint so no need to check
RETURN FALSE
ENDIF
// If we've got here, the racer is on the same lap but behind player
IF bUsingHao = FALSE
// Hao is not in this race so we're fine
RETURN TRUE
ENDIF
IF bUsingHao AND i_index <> (sTrackData.iNumAIRacers-1)
// Hao is in this race but this isn't him so we're fine
RETURN TRUE
ENDIF
IF (bUsingHao AND i_index = (sTrackData.iNumAIRacers-1) AND sRacer[i_index].iCurrentPoint > 1)
// This is Hao so also make sure he's not heading for the first checkpoint
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Tries to reposition the rival closer behind the player
PROC CHECK_REPOSITION_RIVAL_BEHIND_PLAYER(INT i_index, BOOL bUsingHao)
FLOAT fStopWarpDistance
IF bUsingHao AND i_index = (sTrackData.iNumAIRacers-1)
fStopWarpDistance = 20
ELSE
fStopWarpDistance = STOP_WARP_DISTANCE
ENDIF
SWITCH sRacer[i_index].eWarpState
CASE AWS_READY
IF SHOULD_WE_CHECK_FOR_WARPING(i_index, bUsingHao)
AND IS_PED_IN_VEHICLE(sRacer[i_index].piDriver, sRacer[i_index].viCar) AND IS_WAYPOINT_PLAYBACK_GOING_ON_FOR_VEHICLE(sRacer[i_index].viCar)
AND GET_IS_WAYPOINT_RECORDING_LOADED(sRaceWaypointRecording)
INT iCurrentWaypoint, iPlayerWaypoint, iWarpWaypoint
FLOAT fDistance
VECTOR vWarpCoords, vPlayerWaypoint//, vNextWaypoint
iCurrentWaypoint = GET_VEHICLE_WAYPOINT_TARGET_POINT(sRacer[i_index].viCar)
IF iCurrentWaypoint < 1
iCurrentWaypoint = 1
ENDIF
//iCurrentWaypoint++ // increase by 1 to get the next waypoint along the route
IF iCurrentWaypoint < iNumWaypoints
iWarpWaypoint = iCurrentWaypoint + 1
ELSE
iWarpWaypoint = 1//0
ENDIF
IF WAYPOINT_RECORDING_GET_CLOSEST_WAYPOINT(sRaceWaypointRecording, GET_ENTITY_COORDS(PLAYER_PED_ID()), iPlayerWaypoint)
AND WAYPOINT_RECORDING_GET_COORD(sRaceWaypointRecording, iWarpWaypoint, vWarpCoords)
AND WAYPOINT_RECORDING_GET_COORD(sRaceWaypointRecording, iPlayerWaypoint, vPlayerWaypoint)
fDistance = GET_DISTANCE_BETWEEN_COORDS(vPlayerWaypoint, vWarpCoords)
IF (fDistance > WARP_DISTANCE OR sRacer[i_index].iCurrentLap < sRaceHUD.iCurrentLap) // Racer is too far behind the player
AND NOT IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), sRacer[i_index].viCar, fStopWarpDistance)// AND sRacer[i_index].iCurrentLap >= sRaceHUD.iCurrentLap)
AND NOT IS_ENTITY_IN_RANGE_COORDS(PLAYER_PED_ID(), vWarpCoords, fStopWarpDistance)// AND sRacer[i_index].iCurrentLap >= sRaceHUD.iCurrentLap)
AND NOT IS_SPHERE_VISIBLE(GET_ENTITY_COORDS(sRacer[i_index].viCar), 5.0) // Racer has caught up so start racing again
AND NOT IS_SPHERE_VISIBLE(vWarpCoords, 5.0)
CPRINTLN(DEBUG_MISSION, "Mission Race: Rival ", i_index, " has entered its warping state")
IF bUsingHao AND i_index = (sTrackData.iNumAIRacers-1)
sRacer[i_index].iWarpTimer = GET_GAME_TIMER() + 90
ELSE
sRacer[i_index].iWarpTimer = GET_GAME_TIMER() + 100 + (i_index*10)
ENDIF
sRacer[i_index].iWarpWaypoint = iWarpWaypoint
sRacer[i_index].eWarpState = AWS_WARPING
ENDIF
ENDIF
ENDIF
BREAK
CASE AWS_WARPING
INT iTargetWaypoint
VECTOR vWarpCoords, vNextWaypoint
IF sRacer[i_index].iWarpWaypoint < iNumWaypoints
iTargetWaypoint = sRacer[i_index].iWarpWaypoint + 1
ELSE
iTargetWaypoint = 1//0
ENDIF
IF WAYPOINT_RECORDING_GET_COORD(sRaceWaypointRecording, sRacer[i_index].iWarpWaypoint, vWarpCoords)
IF IS_ENTITY_IN_RANGE_ENTITY(PLAYER_PED_ID(), sRacer[i_index].viCar, fStopWarpDistance)// AND sRacer[i_index].iCurrentLap >= sRaceHUD.iCurrentLap)
OR IS_ENTITY_IN_RANGE_COORDS(PLAYER_PED_ID(), vWarpCoords, fStopWarpDistance)// AND sRacer[i_index].iCurrentLap >= sRaceHUD.iCurrentLap)
OR IS_SPHERE_VISIBLE(GET_ENTITY_COORDS(sRacer[i_index].viCar), 5.0) // Racer has caught up so start racing again
OR IS_SPHERE_VISIBLE(vWarpCoords, 5.0)
OR (sRacer[i_index].iCurrentLap = sRaceHUD.iCurrentLap AND sRacer[i_index].iCurrentPoint >= sRaceHUD.iCurrentCheckPoint) // Same lap but ahead of player
OR sRacer[i_index].iCurrentLap > sRaceHUD.iCurrentLap
TASK_VEHICLE_FOLLOW_WAYPOINT_RECORDING(sRacer[i_index].piDriver, sRacer[i_index].viCar, sRaceWaypointRecording, DRIVINGMODE_AVOIDCARS_RECKLESS, iTargetWaypoint, EWAYPOINT_VEHICLES_USE_AI_SLOWDOWN, -1, GET_BASE_SPEED(i_index), bLoopWaypointRecording)
IF sRaceData.eRaceType = RACETYPE_SEA
SET_VEHICLE_FORWARD_SPEED(sRacer[i_index].viCar, GET_BASE_SPEED(i_index)*0.25)
ELSE
SET_VEHICLE_FORWARD_SPEED(sRacer[i_index].viCar, GET_BASE_SPEED(i_index)*0.5)
ENDIF
CPRINTLN(DEBUG_MISSION, "Mission Race: Rival ", i_index, " has completed its warping at waypoint ", sRacer[i_index].iWarpWaypoint)
sRacer[i_index].eWarpState = AWS_READY
ELSE
//IF NOT IS_SPHERE_VISIBLE(vWarpCoords, 3) AND NOT IS_SPHERE_VISIBLE(GET_ENTITY_COORDS(sRacer[i_index].viCar), 3.0) // Racer and target point are off screen
IF GET_GAME_TIMER() > sRacer[i_index].iWarpTimer
IF NOT IS_ANY_VEHICLE_NEAR_POINT(vWarpCoords, 5) // Make sure the new checkpoint we're warping to is clear
OR IS_ENTITY_IN_RANGE_COORDS(sRacer[i_index].viCar, vWarpCoords, 5) // In case it's this vehicle within the 5 metres
IF WAYPOINT_RECORDING_GET_COORD(sRaceWaypointRecording, iTargetWaypoint, vNextWaypoint)
SET_ENTITY_COORDS(sRacer[i_index].viCar, vWarpCoords)
SET_ENTITY_HEADING_FACE_COORDS(sRacer[i_index].viCar, vNextWaypoint) // Face next checkpoint
// Check if racer has reached next checkpoint
IF GET_DISTANCE_BETWEEN_COORDS(vWarpCoords, sTrackData.vCheckPoint[sRacer[i_index].iCurrentPoint], FALSE) <= (CHECKPOINT_LOCATE_SIZE*2)
//CPRINTLN(DEBUG_MISSION, "Mission Race: Updating checkpoint while warping for Rival ", i_index)
UPDATE_AI_CHECKPOINT(i_index)
ENDIF
sRacer[i_index].iWarpWaypoint = iTargetWaypoint
sRacer[i_index].iWarpTimer = GET_GAME_TIMER() + 100 + (i_index*10)
IF NOT IS_PED_IN_VEHICLE(sRacer[i_index].piDriver, sRacer[i_index].viCar)
SET_PED_INTO_VEHICLE(sRacer[i_index].piDriver, sRacer[i_index].viCar)
ENDIF
iReplayRecordWarpTimer = GET_GAME_TIMER() + 1500
iReplayRecordWarpTimer = iReplayRecordWarpTimer
//CPRINTLN(DEBUG_MISSION, "Mission Race: Rival ", i_index, " has been repositioned at waypoint ", iTargetWaypoint)
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
BREAK
CASE AWS_FINISHED
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Checks if the rival vehicle needs recovering (ie upsidedown, in water, etc)
FUNC BOOL DOES_RIVAL_VEHICLE_NEED_RECOVERING(INT i_index)
IF sRaceData.eRaceType = RACETYPE_CAR // Bikes and speedophiles can be corrected so only check if upsidedown for car races
IF IS_ENTITY_UPSIDEDOWN(sRacer[i_index].viCar)
OR IS_VEHICLE_STUCK_TIMER_UP(sRacer[i_index].viCar, VEH_STUCK_ON_ROOF, ROOF_TIME)
OR IS_VEHICLE_STUCK_TIMER_UP(sRacer[i_index].viCar, VEH_STUCK_ON_SIDE, SIDE_TIME)
CPRINTLN(DEBUG_MISSION, "Mission Race: Rival ", i_index, " is upsidedown so needs recovering")
RETURN TRUE
ENDIF
ENDIF
IF IS_ENTITY_IN_WATER(sRacer[i_index].viCar)
IF sRaceData.eRaceType = RACETYPE_CAR
OR sRaceData.eRaceType = RACETYPE_BIKE
CPRINTLN(DEBUG_MISSION, "Mission Race: Rival ", i_index, " is in water so needs recovering")
RETURN TRUE
ENDIF
ENDIF
IF IS_VEHICLE_STUCK_TIMER_UP(sRacer[i_index].viCar, VEH_STUCK_HUNG_UP, HUNG_UP_TIME)
OR IS_VEHICLE_STUCK_TIMER_UP(sRacer[i_index].viCar, VEH_STUCK_JAMMED, JAMMED_TIME)
RETURN TRUE
ENDIF
/*IF sRacer[i_index].iCurrentLap <= sRaceHUD.iCurrentLap AND sRacer[i_index].iCurrentPoint < sRaceHUD.iCurrentCheckPoint
AND GET_DISTANCE_BETWEEN_ENTITIES(sRacer[i_index].viCar, PLAYER_PED_ID(), FALSE) > 100
RETURN TRUE
ENDIF*/
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Rival needs recovering and is not onscreen, so warp him
PROC RECOVER_RIVAL_USING_WARP(INT i_index)
INT i_waypoint_to_warp_to
VECTOR v_waypoint_to_warp_to, v_next_waypoint
WAYPOINT_RECORDING_GET_CLOSEST_WAYPOINT(sRaceWaypointRecording, GET_ENTITY_COORDS(sRacer[i_index].viCar), i_waypoint_to_warp_to)
WAYPOINT_RECORDING_GET_COORD(sRaceWaypointRecording, i_waypoint_to_warp_to, v_waypoint_to_warp_to)
WAYPOINT_RECORDING_GET_COORD(sRaceWaypointRecording, i_waypoint_to_warp_to+1, v_next_waypoint)
//FLOAT f_PlayerWarpPointDist = GET_DISTANCE_BETWEEN_ENTITY_AND_COORD(PLAYER_PED_ID(), v_waypoint_to_warp_to, FALSE)
// Make sure the point to warp to is far enough from the player that the warp won't be seen
//IF f_PlayerWarpPointDist > 200
// Make sure the new checkpoint we're warping to is clear and not on screen
IF NOT IS_SPHERE_VISIBLE(v_waypoint_to_warp_to, 3)
IF NOT IS_ANY_VEHICLE_NEAR_POINT(v_waypoint_to_warp_to, 5)
OR GET_DISTANCE_BETWEEN_ENTITY_AND_COORD(sRacer[i_index].viCar, v_waypoint_to_warp_to) < 5 // In case it's this vehicle within the 5 metres
SET_ENTITY_COORDS(sRacer[i_index].viCar, v_waypoint_to_warp_to)
SET_ENTITY_HEADING_FACE_COORDS(sRacer[i_index].viCar, v_next_waypoint) // Face next checkpoint
IF NOT IS_PED_IN_VEHICLE(sRacer[i_index].piDriver, sRacer[i_index].viCar)
SET_PED_INTO_VEHICLE(sRacer[i_index].piDriver, sRacer[i_index].viCar)
ENDIF
TASK_VEHICLE_FOLLOW_WAYPOINT_RECORDING(sRacer[i_index].piDriver, sRacer[i_index].viCar, sRaceWaypointRecording, DRIVINGMODE_AVOIDCARS_RECKLESS, i_waypoint_to_warp_to, EWAYPOINT_VEHICLES_USE_AI_SLOWDOWN, -1, GET_BASE_SPEED(i_index)*2, bLoopWaypointRecording)
CPRINTLN(DEBUG_MISSION, "Mission Race: Warped rival ", i_index, " into his vehicle")
iReplayRecordWarpTimer = GET_GAME_TIMER() + 1500
iReplayRecordWarpTimer = iReplayRecordWarpTimer
ENDIF
ENDIF
//ENDIF
ENDPROC
/// PURPOSE:
/// Rival no longer in his vehicle, and is onscreen, so task him to get back inside
PROC RECOVER_RIVAL_NOT_USING_WARP(INT i_index)
IF sRacer[i_index].gGivenGetOnVehTask = FALSE
INT i_waypoint_to_warp_to
WAYPOINT_RECORDING_GET_CLOSEST_WAYPOINT(sRaceWaypointRecording, GET_ENTITY_COORDS(sRacer[i_index].viCar), i_waypoint_to_warp_to)
OPEN_SEQUENCE_TASK(seqGetInVehicle)
TASK_ENTER_VEHICLE(NULL, sRacer[i_index].viCar, 10000)
TASK_VEHICLE_FOLLOW_WAYPOINT_RECORDING(NULL, sRacer[i_index].viCar, sRaceWaypointRecording, DRIVINGMODE_AVOIDCARS_RECKLESS, i_waypoint_to_warp_to, EWAYPOINT_VEHICLES_USE_AI_SLOWDOWN, -1, GET_BASE_SPEED(i_index)*2, bLoopWaypointRecording)
CLOSE_SEQUENCE_TASK(seqGetInVehicle)
TASK_PERFORM_SEQUENCE(sRacer[i_index].piDriver, seqGetInVehicle)
CLEAR_SEQUENCE_TASK(seqGetInVehicle)
sRacer[i_index].gGivenGetOnVehTask = TRUE
CPRINTLN(DEBUG_MISSION, "Mission Race: Tasked rival ", i_index, " to get into his vehicle")
ENDIF
ENDPROC
/// PURPOSE:
/// Checks if the AI racer has reached their next checkpoint, different tests for sea races and street races
/// PARAMS:
/// i_index - the index of the racer we're testing
/// RETURNS:
/// TRUE if the racer has reached their checkpoint
FUNC BOOL IS_AI_RACER_AT_CHECKPOINT(INT i_index)
VECTOR vCurrentRacerCoords
//IF sRaceData.eRaceType = RACETYPE_SEA
vCurrentRacerCoords = GET_ENTITY_COORDS(sRacer[i_index].viCar)
IF GET_DISTANCE_BETWEEN_COORDS(vCurrentRacerCoords, sTrackData.vCheckPoint[sRacer[i_index].iCurrentPoint], FALSE) <= CHECKPOINT_LOCATE_SIZE // Racer has reached next checkpoint
RETURN TRUE
ENDIF
INT iClosestWaypoint
INT j_index = 0
WAYPOINT_RECORDING_GET_CLOSEST_WAYPOINT(sRaceWaypointRecording, GET_ENTITY_COORDS(sRacer[i_index].viCar), iClosestWaypoint)
REPEAT 5 j_index
IF (iClosestWaypoint-j_index) > 0
WAYPOINT_RECORDING_GET_COORD(sRaceWaypointRecording, iClosestWaypoint-j_index, vCurrentRacerCoords)
IF GET_DISTANCE_BETWEEN_COORDS(vCurrentRacerCoords, sTrackData.vCheckPoint[sRacer[i_index].iCurrentPoint], FALSE) <= CHECKPOINT_LOCATE_SIZE // Racer has reached next checkpoint
RETURN TRUE
ENDIF
ENDIF
ENDREPEAT
/*ELSE
vCurrentRacerCoords = GET_ENTITY_COORDS(sRacer[i_index].viCar)
IF GET_DISTANCE_BETWEEN_COORDS(vCurrentRacerCoords, sTrackData.vCheckPoint[sRacer[i_index].iCurrentPoint], FALSE) <= CHECKPOINT_LOCATE_SIZE // Racer has reached next checkpoint
RETURN TRUE
ENDIF
ENDIF*/
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Handles the AI racer boosting at the race start
/// PARAMS:
/// i_index - the AI racer to boost
PROC HANDLE_AI_BOOST(INT i_index)
SWITCH sRacer[i_index].eBoostState
CASE BS_READY
// Only trigger on racers ahead of player
VECTOR vRacerRelativePos
vRacerRelativePos = GET_OFFSET_FROM_ENTITY_GIVEN_WORLD_COORDS(PLAYER_PED_ID(), GET_ENTITY_COORDS(sRacer[i_index].viCar))
IF (vRacerRelativePos.x > -2 AND vRacerRelativePos.x < 2) OR i_index = 0
CPRINTLN(DEBUG_MISSION,"Boost triggered for racer ", i_index)
sRacer[i_index].iBoostTimer = GET_GAME_TIMER() + BOOST_TIME
sRacer[i_index].eBoostState = BS_ACTIVE
ELSE
CPRINTLN(DEBUG_MISSION,"Racer ", i_index, " wasn't directly in front of the player")
sRacer[i_index].eBoostState = BS_FINISHED
ENDIF
BREAK
CASE BS_ACTIVE
IF GET_GAME_TIMER() > sRacer[i_index].iBoostTimer
CPRINTLN(DEBUG_MISSION,"Boost finished for racer ", i_index)
sRacer[i_index].eBoostState = BS_FINISHED
ELSE
APPLY_FORCE_TO_ENTITY(sRacer[i_index].viCar, APPLY_TYPE_FORCE, <<0.0, 25.0, 0.0>>, <<0.0, 0.0, 0.0>>, 0, TRUE, TRUE, TRUE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Makes the AI racer look at the player if the player bumps them
/// PARAMS:
/// i_index - the AI racer to handle looking
PROC HANDLE_AI_LOOKAT(INT i_index)
IF IS_VEHICLE_OK(sPlayerVehicle.vehPlayerVehicle) AND IS_PED_UNINJURED(PLAYER_PED_ID())
IF sRacer[i_index].bReadyToLook
IF IS_ENTITY_TOUCHING_ENTITY(sPlayerVehicle.vehPlayerVehicle, sRacer[i_index].viCar)
sRacer[i_index].bReadyToLook = FALSE
sRacer[i_index].iLookAtTimer = GET_GAME_TIMER() + 4000
CPRINTLN(DEBUG_MISSION,"Player bumped racer ", i_index, ", starting lookat")
ENDIF
ELSE
IF sRacer[i_index].iLookAtTimer > GET_GAME_TIMER()
SET_IK_TARGET(sRacer[i_index].piDriver, IK_PART_HEAD, PLAYER_PED_ID(), 0, << 0.0, 0.0, 0.0 >>, ITF_DEFAULT, 150, 400)
ELSE
sRacer[i_index].bReadyToLook = TRUE
CPRINTLN(DEBUG_MISSION,"Lookat finished for racer ", i_index)
ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Individual racer update
PROC UPDATE_RACER(INT i_index, BOOL bAllowRacerSix = TRUE, BOOL bUsingHao = FALSE)
// Array safety check
IF (i_index >= 0) OR (i_index < sTrackData.iNumAIRacers)
// Valid references
IF IS_RACER_OK(i_index)
CHECK_REPOSITION_RIVAL_BEHIND_PLAYER(i_index, bUsingHao)
// Check if the driver isn't in his vehicle, or the vehicle needs recovering
IF NOT IS_PED_IN_VEHICLE(sRacer[i_index].piDriver, sRacer[i_index].viCar)
/*IF GET_DISTANCE_BETWEEN_ENTITIES(sRacer[i_index].viCar, PLAYER_PED_ID(), FALSE) > 100
CHECK_REPOSITION_RIVAL_BEHIND_PLAYER(i_index)
EL*/IF NOT IS_SPHERE_VISIBLE(GET_ENTITY_COORDS(sRacer[i_index].viCar), 3)
RECOVER_RIVAL_USING_WARP(i_index)
ELSE
RECOVER_RIVAL_NOT_USING_WARP(i_index)
ENDIF
ELIF DOES_RIVAL_VEHICLE_NEED_RECOVERING(i_index)
/*IF GET_DISTANCE_BETWEEN_ENTITIES(sRacer[i_index].viCar, PLAYER_PED_ID(), FALSE) > 100
CHECK_REPOSITION_RIVAL_BEHIND_PLAYER(i_index)
EL*/IF NOT IS_SPHERE_VISIBLE(GET_ENTITY_COORDS(sRacer[i_index].viCar), 3)
RECOVER_RIVAL_USING_WARP(i_index)
ENDIF
ELSE
HANDLE_AI_BOOST(i_index)
HANDLE_AI_LOOKAT(i_index)
// Handle racer state
SWITCH sRacer[i_index].stateAI
CASE AI_RACER_INIT
sRacer[i_index].iCurrentPoint = 0
sRacer[i_index].iCurrentLap = 1
IF sRaceData.eRaceType = RACETYPE_CAR // Only cars can do burnouts
IF bAllowRacerSix OR i_index <> 6
TASK_VEHICLE_TEMP_ACTION(sRacer[i_index].piDriver, sRacer[i_index].viCar, TEMPACT_BURNOUT, (i_index+1)*BURNOUT_SPREAD_TIME)
ENDIF
ENDIF
sRacer[i_index].stateAI = AI_RACER_WAIT_FOR_BURNOUT_FINISHED
BREAK
CASE AI_RACER_WAIT_FOR_BURNOUT_FINISHED
IF (((GET_GAME_TIMER() - sRaceHUD.iStartTime) > (i_index+1)*BURNOUT_SPREAD_TIME) OR sRaceData.eRaceType <> RACETYPE_CAR)
AND GET_IS_WAYPOINT_RECORDING_LOADED(sRaceWaypointRecording)
sRacer[i_index].fLastSpeed = GET_BASE_SPEED(i_index)
IF sRaceData.eRaceTrack = STREET_RACE_01 OR sRaceData.eRaceTrack = STREET_RACE_02 OR sRaceData.eRaceTrack = STREET_RACE_04 OR sRaceData.eRaceTrack = STREET_RACE_05 OR sRaceData.eRaceTrack = STREET_RACE_06
INT iTarget
VECTOR vDest
FLOAT fStraightLine
IF i_index = 0 OR i_index = 2 OR i_index = 4
iTarget = 0
ELSE
iTarget = 1
ENDIF
vDest = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(sRacer[iTarget].viCar, <<0,20,0>>)
fStraightLine = GET_DISTANCE_BETWEEN_ENTITY_AND_COORD(sRacer[i_index].viCar, vDest)
SEQUENCE_INDEX siRace
OPEN_SEQUENCE_TASK(siRace)
TASK_VEHICLE_DRIVE_TO_COORD(NULL, sRacer[i_index].viCar, vDest, sRacer[i_index].fLastSpeed, DRIVINGSTYLE_RACING, GET_ENTITY_MODEL(sRacer[i_index].viCar), DRIVINGMODE_AVOIDCARS_RECKLESS | DF_ForceStraightLine, 3.0, fStraightLine)
TASK_VEHICLE_FOLLOW_WAYPOINT_RECORDING(NULL, sRacer[i_index].viCar, sRaceWaypointRecording, DRIVINGMODE_AVOIDCARS_RECKLESS, 0, EWAYPOINT_VEHICLES_USE_AI_SLOWDOWN | EWAYPOINT_START_FROM_CLOSEST_POINT, -1, sRacer[i_index].fLastSpeed, bLoopWaypointRecording)
CLOSE_SEQUENCE_TASK(siRace)
TASK_PERFORM_SEQUENCE(sRacer[i_index].piDriver, siRace)
CLEAR_SEQUENCE_TASK(siRace)
ELSE
TASK_VEHICLE_FOLLOW_WAYPOINT_RECORDING(sRacer[i_index].piDriver, sRacer[i_index].viCar, sRaceWaypointRecording, DRIVINGMODE_AVOIDCARS_RECKLESS, 0, EWAYPOINT_VEHICLES_USE_AI_SLOWDOWN, -1, sRacer[i_index].fLastSpeed, bLoopWaypointRecording)
ENDIF
sRacer[i_index].stateAI = AI_RACER_ACTIVE
ENDIF
BREAK
CASE AI_RACER_ACTIVE
/*VECTOR vCurrentRacerCoords
IF sRaceData.eRaceType = RACETYPE_SEA
INT iClosestWaypoint
WAYPOINT_RECORDING_GET_CLOSEST_WAYPOINT(sRaceWaypointRecording, GET_ENTITY_COORDS(sRacer[i_index].viCar), iClosestWaypoint)
WAYPOINT_RECORDING_GET_COORD(sRaceWaypointRecording, iClosestWaypoint, vCurrentRacerCoords)
ELSE
vCurrentRacerCoords = GET_ENTITY_COORDS(sRacer[i_index].viCar)
ENDIF
IF GET_DISTANCE_BETWEEN_COORDS(vCurrentRacerCoords, sTrackData.vCheckPoint[sRacer[i_index].iCurrentPoint], FALSE) <= (FMMC_CHECKPOINT_SIZE + FMMC_CHECKPOINT_SIZE) // Racer has reached next checkpoint
*/
IF IS_AI_RACER_AT_CHECKPOINT(i_index) AND sRacer[i_index].eWarpState <> AWS_WARPING
UPDATE_AI_CHECKPOINT(i_index)
ENDIF
//CHECK_REPOSITION_RIVAL_BEHIND_PLAYER(i_index)
sRacer[i_index].gGivenGetOnVehTask = FALSE
BREAK
CASE AI_RACER_FINISHED_RACE
BREAK
ENDSWITCH
ENDIF
// Update the vehicle's blip position, this ensures the blip moves smoothly if the vehicle is warped
MANAGE_SLIDY_BLIP_FOR_ENTITY(sRacer[i_index].biBlip, sRacer[i_index].viCar, TRUE)
/*IF NOT DOES_BLIP_EXIST(sPlayerVehicle.biPlayerVehicle)
IF NOT DOES_BLIP_EXIST(sRacer[i_index].biBlip)
sRacer[i_index].biBlip = CREATE_BLIP_FOR_VEHICLE(sRacer[i_index].viCar)
ENDIF
ENDIF*/
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Updates the player's current position
PROC UPDATE_PLAYER_POS()
IF IS_PLAYER_PLAYING(PLAYER_ID())
sRaceHUD.iPlayerPosition = 1 // If no racers are ahead of the player he is at position 1 so start with this value
INT i_closest_checkpoint_to_player = sRaceHUD.iCurrentCheckPoint // Set initially to the checkpoint that the player should be heading to
FLOAT fPlayerDistFromCheckpoint = GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(PLAYER_PED_ID()), sTrackData.vCheckPoint[sRaceHUD.iCurrentCheckPoint])
IF fPlayerDistFromCheckpoint > fNextCheckpointDistance+100
CPRINTLN(DEBUG_MISSION, "Mission Race: Player going in opposite direction to the next checkpoint so abandoning race")
INT iNextCheckpoint = sRaceHUD.iCurrentCheckPoint + 1
IF iNextCheckpoint >= sTrackData.iNumCheckpoints
iNextCheckpoint = 0
ENDIF
FLOAT fPlayerDistFromNextCheckpoint = GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(PLAYER_PED_ID()), sTrackData.vCheckPoint[iNextCheckpoint])
FLOAT fDistBetweenCheckpoints = GET_DISTANCE_BETWEEN_COORDS(sTrackData.vCheckPoint[iNextCheckpoint], sTrackData.vCheckPoint[sRaceHUD.iCurrentCheckPoint]) + 20
IF fPlayerDistFromNextCheckpoint < fDistBetweenCheckpoints
failReason = FAIL_MISSED_CHECKPOINT
ELSE
failReason = FAIL_ABANDONED_RACE
ENDIF
eRaceState = RACE_STATE_FAIL
eRaceSubState = RACE_SUBSTATE_SETUP
EXIT
ELIF fPlayerDistFromCheckpoint > fNextCheckpointDistance+FMMC_CHECKPOINT_SIZE // Player is driving away from the checkpoint he should be heading towards, FMMC_CHECKPOINT_SIZE is for overlap
// Player can reach a checkpoint then backtrack, so we need to check for which checkpoint the player is actually nearest to
// If he's nearer to a checkpoint less than sRaceHUD.iCurrentCheckPoint, we'll use the updated checkpoint to determine his position in the race, not the checkpoint he should be heading towards
INT i_checkpoint = 0
FLOAT f_closest_distance = 9999
FLOAT f_current_distance
REPEAT sTrackData.iNumCheckpoints i_checkpoint
IF i_checkpoint < sRaceHUD.iCurrentCheckPoint // Only look at checkpoints less than sRaceHUD.iCurrentCheckPoint
f_current_distance = GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(PLAYER_PED_ID()), sTrackData.vCheckPoint[i_checkpoint])
IF f_current_distance < f_closest_distance
f_closest_distance = f_current_distance
i_closest_checkpoint_to_player = i_checkpoint
ENDIF
ENDIF
ENDREPEAT
ENDIF
//CPRINTLN(DEBUG_MISSION, "Mission Race: i_closest_checkpoint_to_player = ", i_closest_checkpoint_to_player, " fPlayerDistFromCheckpoint = ", fPlayerDistFromCheckpoint, " fNextCheckpointDistance = ", fNextCheckpointDistance)
FLOAT fRacerDistFromCheckpoint
INT i_index = 0
REPEAT sTrackData.iNumAIRacers i_index
IF IS_ENTITY_ALIVE(sRacer[i_index].piDriver)
IF sRacer[i_index].stateAI = AI_RACER_FINISHED_RACE // The racer has already finished
sRaceHUD.iPlayerPosition++
ELIF sRacer[i_index].iCurrentLap > sRaceHUD.iCurrentLap // The racer is a lap ahead of the player
sRaceHUD.iPlayerPosition++
ELIF sRacer[i_index].iCurrentLap = sRaceHUD.iCurrentLap // The racer is on the same lap as the player so compare their checkpoint positions
IF sRacer[i_index].iCurrentPoint > i_closest_checkpoint_to_player // The racer's checkpoint is further along than the player's checkpoint
sRaceHUD.iPlayerPosition++
ELIF sRacer[i_index].iCurrentPoint = i_closest_checkpoint_to_player // The racer's checkpoint is the same as the player's so compare their distances from it
fPlayerDistFromCheckpoint = GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(PLAYER_PED_ID()), sTrackData.vCheckPoint[i_closest_checkpoint_to_player])
fRacerDistFromCheckpoint = GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(sRacer[i_index].viCar), sTrackData.vCheckPoint[sRacer[i_index].iCurrentPoint])
IF fRacerDistFromCheckpoint < fPlayerDistFromCheckpoint // The racer is closer to the checkpoint than the player is
sRaceHUD.iPlayerPosition++
ENDIF
ENDIF
ENDIF
ENDIF
ENDREPEAT
ENDIF
ENDPROC
// Commenting out for now in case this is needed again
/// PURPOSE:
/// Gets the index of the second placed AI driver
/// RETURNS:
/// -1 if the function was unable to get a second place driver, the index otherwise
/*FUNC INT GET_SECOND_PLACE_CAR()
INT iRacerPosition[MAX_AI_RACERS]
FLOAT fRacerIDistFromCheckpoint
FLOAT fRacerJDistFromCheckpoint
INT i_index = 0
REPEAT sTrackData.iNumAIRacers i_index
IF IS_VEHICLE_OK(sRacer[i_index].viCar)
iRacerPosition[i_index] = 2 // for this to have been called, the player must be in first place, therefore the best AI racer is in second
INT j_index = 0
REPEAT sTrackData.iNumAIRacers j_index
IF IS_VEHICLE_OK(sRacer[j_index].viCar)
IF sRacer[j_index].iCurrentLap > sRacer[i_index].iCurrentLap // racer j is a lap ahead of racer i
iRacerPosition[i_index]++
ELIF sRacer[j_index].iCurrentLap = sRacer[i_index].iCurrentLap // The racers are on the same lap so compare their checkpoint positions
IF sRacer[j_index].iCurrentPoint > sRacer[i_index].iCurrentPoint // racer j's checkpoint is further along than racer i's checkpoint
iRacerPosition[i_index]++
ELIF sRacer[j_index].iCurrentPoint = sRacer[i_index].iCurrentPoint // The racers's checkpoints are the same so compare their distances from it
fRacerIDistFromCheckpoint = GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(sRacer[i_index].viCar), sTrackData.vCheckPoint[sRacer[i_index].iCurrentPoint])
fRacerJDistFromCheckpoint = GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(sRacer[j_index].viCar), sTrackData.vCheckPoint[sRacer[j_index].iCurrentPoint])
IF fRacerJDistFromCheckpoint < fRacerIDistFromCheckpoint // The racer is closer to the checkpoint than the player is
iRacerPosition[i_index]++
ENDIF
ENDIF
ENDIF
ENDIF
ENDREPEAT
ENDIF
ENDREPEAT
i_index = 0
REPEAT sTrackData.iNumAIRacers i_index
IF iRacerPosition[i_index] = 2
RETURN i_index
ENDIF
ENDREPEAT
RETURN -1
ENDFUNC*/
/// PURPOSE:
/// Sets the hint camera to target the AI car ahead of the player
/*PROC CAM_HINT_FOR_NEXT_CAR()
// If the player is in the lead, don't bother with the hint camera
IF sRaceHUD.iPlayerPosition > 1
INT iRacerPosition[MAX_AI_RACERS]
FLOAT fRacerIDistFromCheckpoint
FLOAT fOtherDistFromCheckpoint
INT i_index = 0
REPEAT sTrackData.iNumAIRacers i_index
IF IS_VEHICLE_OK(sRacer[i_index].viCar)
iRacerPosition[i_index] = 1
VEHICLE_INDEX viPlayer
viPlayer = GET_PLAYERS_LAST_VEHICLE()
IF IS_VEHICLE_OK(viPlayer)
IF sRaceHUD.iCurrentLap > sRacer[i_index].iCurrentLap // player is a lap ahead of the racer
iRacerPosition[i_index]++
ELIF sRacer[i_index].iCurrentLap = sRaceHUD.iCurrentLap // racer is on the same lap as the player so compare checkpoint positions
IF sRacer[i_index].iCurrentPoint > sRaceHUD.iCurrentCheckPoint // player's checkpoint is ahead of the racer's
iRacerPosition[i_index]++
ELIF sRacer[i_index].iCurrentPoint = sRaceHUD.iCurrentCheckPoint // player's checkpoint is the same as the racer's, compare distances from it
fRacerIDistFromCheckpoint = GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(sRacer[i_index].viCar), sTrackData.vCheckPoint[sRacer[i_index].iCurrentPoint])
fOtherDistFromCheckpoint = GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(viPlayer), sTrackData.vCheckPoint[sRaceHUD.iCurrentCheckPoint])
IF fOtherDistFromCheckpoint < fRacerIDistFromCheckpoint // The player is closer to the checkpoint than the racer is
iRacerPosition[i_index]++
ENDIF
ENDIF
ENDIF
ENDIF
INT j_index = 0
REPEAT sTrackData.iNumAIRacers j_index
IF IS_VEHICLE_OK(sRacer[j_index].viCar)
IF sRacer[j_index].iCurrentLap > sRacer[i_index].iCurrentLap // racer j is a lap ahead of racer i
iRacerPosition[i_index]++
ELIF sRacer[j_index].iCurrentLap = sRacer[i_index].iCurrentLap // The racers are on the same lap so compare their checkpoint positions
IF sRacer[j_index].iCurrentPoint > sRacer[i_index].iCurrentPoint // racer j's checkpoint is further along than racer i's checkpoint
iRacerPosition[i_index]++
ELIF sRacer[j_index].iCurrentPoint = sRacer[i_index].iCurrentPoint // The racers's checkpoints are the same so compare their distances from it
fRacerIDistFromCheckpoint = GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(sRacer[i_index].viCar), sTrackData.vCheckPoint[sRacer[i_index].iCurrentPoint])
fOtherDistFromCheckpoint = GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(sRacer[j_index].viCar), sTrackData.vCheckPoint[sRacer[j_index].iCurrentPoint])
IF fOtherDistFromCheckpoint < fRacerIDistFromCheckpoint // racer j is closer to the checkpoint than racer i is
iRacerPosition[i_index]++
ENDIF
ENDIF
ENDIF
ENDIF
ENDREPEAT
ENDIF
ENDREPEAT
i_index = 0
REPEAT sTrackData.iNumAIRacers i_index
IF iRacerPosition[i_index] = (sRaceHUD.iPlayerPosition - 1)
IF IS_VEHICLE_OK(sRacer[i_index].viCar)
CONTROL_VEHICLE_CHASE_HINT_CAM_ANY_MEANS(localChaseHintCamStruct, sRacer[i_index].viCar)
ENDIF
ENDIF
ENDREPEAT
ELSE
KILL_CHASE_HINT_CAM(localChaseHintCamStruct)
ENDIF
ENDPROC*/
FUNC BOOL HAS_PLAYER_COLLIDED_WITH_RACER()
INT i_index
FOR i_index = 0 TO sTrackData.iNumAIRacers-1
IF IS_VEHICLE_OK(sRacer[i_index].viCar)
IF IS_ENTITY_TOUCHING_ENTITY(sPlayerVehicle.vehPlayerVehicle, sRacer[i_index].viCar)
RETURN TRUE
ENDIF
ENDIF
ENDFOR
RETURN FALSE
ENDFUNC
PROC INIT_STOPPED_SOUND()
iStoppedSound = GET_SOUND_ID()
iStoppedTimer = -1
ENDPROC
FUNC INT GET_CLOSEST_RACER()
INT i_index
FLOAT fDistance = 0
FLOAT fClosestDistance = 9999999999.9
INT iClosest
FOR i_index = 0 TO sTrackData.iNumAIRacers-1
IF IS_VEHICLE_OK(sRacer[i_index].viCar)
fDistance = GET_DISTANCE_BETWEEN_ENTITIES(PLAYER_PED_ID(), sRacer[i_index].viCar)
IF fDistance < fClosestDistance
fClosestDistance = fDistance
iClosest = i_index
ENDIF
ENDIF
ENDFOR
RETURN iClosest
ENDFUNC
/// PURPOSE:
/// Plays dialogue if the player gains a position, loses a position or crashes
PROC CHECK_RACE_CONVS()
//IF GET_GAME_TIMER() > iPositionConvTimer AND NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED() AND IS_PED_IN_VEHICLE(PLAYER_PED_ID(), sPlayerVehicle.vehPlayerVehicle)
IF GET_CURRENT_PLAYER_PED_ENUM() = CHAR_FRANKLIN
IF sRaceHUD.iPlayerPosition < iPreviousPosition
// Player has gained one or more positions
IF GET_GAME_TIMER() > iPositionConvTimer /*AND NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()*/ AND IS_PED_IN_VEHICLE(PLAYER_PED_ID(), sPlayerVehicle.vehPlayerVehicle)
PLAY_PED_AMBIENT_SPEECH_WITH_VOICE(PLAYER_PED_ID(), "RACE_RANKUP", "FRANKLIN_NORMAL", SPEECH_PARAMS_FORCE_SHOUTED_CLEAR)
CPRINTLN(DEBUG_MISSION, "Triggered Rankup line")
//IF CREATE_CONVERSATION(pedConvStruct, "SPR_CIT", "SPR_RANKUP", CONV_PRIORITY_MEDIUM)
iPreviousPosition = sRaceHUD.iPlayerPosition
iPositionConvTimer = GET_GAME_TIMER() + POSITION_CONV_TIME
//ENDIF
ELSE
iPreviousPosition = sRaceHUD.iPlayerPosition
ENDIF
ELIF sRaceHUD.iPlayerPosition > iPreviousPosition
// Player has lost one or more positions
IF GET_GAME_TIMER() > iPositionConvTimer /*AND NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()*/ AND IS_PED_IN_VEHICLE(PLAYER_PED_ID(), sPlayerVehicle.vehPlayerVehicle)
PLAY_PED_AMBIENT_SPEECH_WITH_VOICE(PLAYER_PED_ID(), "RACE_RANKDOWN", "FRANKLIN_NORMAL", SPEECH_PARAMS_FORCE_SHOUTED_CLEAR)
CPRINTLN(DEBUG_MISSION, "Triggered Rankdown line")
//IF CREATE_CONVERSATION(pedConvStruct, "SPR_CIT", "SPR_RANKDOWN", CONV_PRIORITY_MEDIUM)
iPreviousPosition = sRaceHUD.iPlayerPosition
iPositionConvTimer = GET_GAME_TIMER() + POSITION_CONV_TIME
//ENDIF
ELSE
iPreviousPosition = sRaceHUD.iPlayerPosition
ENDIF
ENDIF
IF /*sRaceData.eRaceType <> RACETYPE_SEA AND*/ IS_VEHICLE_OK(sPlayerVehicle.vehPlayerVehicle)
IF GET_GAME_TIMER() > iPositionConvTimer /*AND NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()*/ AND IS_PED_IN_VEHICLE(PLAYER_PED_ID(), sPlayerVehicle.vehPlayerVehicle)
//IF HAS_ENTITY_COLLIDED_WITH_ANYTHING(sPlayerVehicle.vehPlayerVehicle)
IF HAS_PLAYER_COLLIDED_WITH_RACER()
PLAY_PED_AMBIENT_SPEECH_WITH_VOICE(PLAYER_PED_ID(), "RACE_CRASH", "FRANKLIN_NORMAL", SPEECH_PARAMS_FORCE_SHOUTED_CLEAR)
CPRINTLN(DEBUG_MISSION, "Triggered Crash line")
//IF CREATE_CONVERSATION(pedConvStruct, "SPR_CIT", "SPR_CRASH", CONV_PRIORITY_MEDIUM)
iPositionConvTimer = GET_GAME_TIMER() + POSITION_CONV_TIME
//ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
IF sRaceData.eRaceType <> RACETYPE_SEA
IF IS_CHAR_ALMOST_STOPPED(PLAYER_PED_ID())
IF iStoppedTimer < 0
iStoppedTimer = GET_GAME_TIMER() + GET_RANDOM_INT_IN_RANGE(2500, 5000)
ELIF GET_GAME_TIMER() > iStoppedTimer
PLAY_SOUND_FROM_ENTITY(iStoppedSound, "DISTANT_RACERS", sRacer[GET_CLOSEST_RACER()].viCar, "ROAD_RACE_SOUNDSET")
iStoppedTimer = -1
ENDIF
ELSE
IF iStoppedTimer > 0
iStoppedTimer = -1
ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Checks if it's a valid condition to create the end scene
/// RETURNS:
/// TRUE if it's OK to create the end scene
FUNC BOOL END_SCENE_IS_VALID()
IF sRaceHUD.iPlayerPosition = 1
IF sRaceData.eRaceTrack = STREET_RACE_01
OR sRaceData.eRaceTrack = STREET_RACE_02
OR sRaceData.eRaceTrack = STREET_RACE_04
OR sRaceData.eRaceTrack = STREET_RACE_05
OR sRaceData.eRaceTrack = STREET_RACE_06
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Puts the player's car in the correct position and gives them their reward for winning the race
PROC SETUP_END_SCENE(VEHICLE_INDEX viPlayer)
IF END_SCENE_IS_VALID()
IF NOT IS_PED_UNINJURED(piRewardPed)
IF HAS_MODEL_LOADED(A_F_Y_Genhot_01)
// Commenting out for now in case this is needed again
//bDeleteSecondPlace = TRUE
//IF sRaceHUD.iPlayerPosition = 1
// Player has won the race, give them a reward
/*IF sRaceData.eRaceType = RACETYPE_SEA
// sea race, give them double their money back
CREDIT_BANK_ACCOUNT(CHAR_FRANKLIN, BAAC_UNLOGGED_SMALL_ACTION, (sRaceData.iRaceFee * 2))
ELSE
// Car race, refund their money and give them the number two driver's car
CREDIT_BANK_ACCOUNT(CHAR_FRANKLIN, BAAC_UNLOGGED_SMALL_ACTION, sRaceData.iRaceFee)
iSecondPlaceID = GET_SECOND_PLACE_CAR()
IF iSecondPlaceID > -1 AND IS_VEHICLE_OK(sRacer[iSecondPlaceID].viCar)
SET_VEHICLE_FIXED(sRacer[iSecondPlaceID].viCar)
SET_VEHICLE_FORWARD_SPEED(sRacer[iSecondPlaceID].viCar, 0.0)
SET_ENTITY_COORDS(sRacer[iSecondPlaceID].viCar, sTrackData.vRewardVehicleSpawnPoint)
SET_ENTITY_HEADING(sRacer[iSecondPlaceID].viCar, sTrackData.fRewardVehicleHeading)
SET_VEHICLE_IS_CONSIDERED_BY_PLAYER(sRacer[iSecondPlaceID].viCar, TRUE)
SAFE_DELETE_PED(sRacer[iSecondPlaceID].piDriver)
ADD_HELP_TO_FLOW_QUEUE("STREET_WIN", FHP_HIGH)
bDeleteSecondPlace = FALSE
ENDIF
ENDIF*/
// Place the player's car in the winning position for the end of race scene
/*IF IS_VEHICLE_OK(sPlayerVehicle.vehPlayerVehicle)
SET_VEHICLE_FORWARD_SPEED(sPlayerVehicle.vehPlayerVehicle, 0.0)
SET_ENTITY_COORDS(sPlayerVehicle.vehPlayerVehicle, vWinningCarPos)
SET_ENTITY_HEADING(sPlayerVehicle.vehPlayerVehicle, fWinningCarHeading)
ENDIF*/
IF IS_VEHICLE_OK(viPlayer)
CLEAR_PED_TASKS(PLAYER_PED_ID())
SET_VEHICLE_FORWARD_SPEED(viPlayer, 0.0)
SET_ENTITY_COORDS(viPlayer, vWinningCarPos)
SET_ENTITY_HEADING(viPlayer, fWinningCarHeading)
ENDIF
// Commenting out for now in case this is needed again
/*ELIF sRaceData.eRaceType <> RACETYPE_SEA
// Place the winning car in the winning position and the player's car in the alternative position
IF IS_VEHICLE_OK(sPlayerVehicle.vehPlayerVehicle)
SET_VEHICLE_FORWARD_SPEED(sPlayerVehicle.vehPlayerVehicle, 0.0)
SET_ENTITY_COORDS(sPlayerVehicle.vehPlayerVehicle, vAltCarPos)
SET_ENTITY_HEADING(sPlayerVehicle.vehPlayerVehicle, fAltCarHeading)
ENDIF
iSecondPlaceID = GET_SECOND_PLACE_CAR() // This function returns the leading AI car, if the player didn't win, this car did
IF iSecondPlaceID > -1 AND IS_VEHICLE_OK(sRacer[iSecondPlaceID].viCar) AND IS_PED_UNINJURED(sRacer[iSecondPlaceID].piDriver)
SET_VEHICLE_FIXED(sRacer[iSecondPlaceID].viCar)
SET_VEHICLE_FORWARD_SPEED(sRacer[iSecondPlaceID].viCar, 0.0)
SET_ENTITY_COORDS(sRacer[iSecondPlaceID].viCar, vWinningCarPos)
SET_ENTITY_HEADING(sRacer[iSecondPlaceID].viCar, fWinningCarHeading)
TASK_VEHICLE_TEMP_ACTION(sRacer[iSecondPlaceID].piDriver, sRacer[iSecondPlaceID].viCar, TEMPACT_WAIT, 99999999)
bDeleteSecondPlace = FALSE
ENDIF
ENDIF*/
CLEANUP_RACERS(TRUE)//, bDeleteSecondPlace)
// Create the reward giving ped
piRewardPed = CREATE_PED(PEDTYPE_CIVFEMALE, A_F_Y_Genhot_01, vRewardPedPos, fRewardPedHeading)
ADD_PED_FOR_DIALOGUE(pedConvStruct, 3, piRewardPed, "StrRaceGirl", TRUE)
STOP_PED_SPEAKING(piRewardPed, TRUE)
SET_MODEL_AS_NO_LONGER_NEEDED(A_F_Y_Genhot_01)
ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE: Updates AI racers
PROC UPDATE_RACE_AI(BOOL bAllowRacerSix = TRUE, BOOL bUsingHao = FALSE)
// Iterate through all racers
INT i_index
FOR i_index = 0 TO sTrackData.iNumAIRacers-1
UPDATE_RACER(i_index, bAllowRacerSix, bUsingHao)
ENDFOR
ADD_CLOSEST_RACER_TO_MIX()
ENDPROC
PROC HANDLE_START_BOOST()
SWITCH eBoostState
CASE BS_READY
IF iBoostTimer > 0
IF GET_GAME_TIMER() < iBoostTimer
IF IS_CONTROL_JUST_PRESSED(PLAYER_CONTROL, INPUT_VEH_ACCELERATE)
CPRINTLN(DEBUG_MISSION,"Player triggered boost")
IF IS_VEHICLE_OK(sPlayerVehicle.vehPlayerVehicle) AND IS_PED_SITTING_IN_VEHICLE(PLAYER_PED_ID(), sPlayerVehicle.vehPlayerVehicle)
IF IS_THIS_MODEL_A_CAR(GET_ENTITY_MODEL(sPlayerVehicle.vehPlayerVehicle))
SET_VEHICLE_BOOST_ACTIVE(sPlayerVehicle.vehPlayerVehicle, TRUE)
ENDIF
ANIMPOSTFX_PLAY("RaceTurbo", 0, FALSE)
iBoostTimer = GET_GAME_TIMER() + BOOST_TIME
eBoostState = BS_ACTIVE
ENDIF
ENDIF
ELSE
CPRINTLN(DEBUG_MISSION,"Boost window timed out")
eBoostState = BS_FINISHED
ENDIF
ENDIF
BREAK
CASE BS_ACTIVE
IF GET_GAME_TIMER() > iBoostTimer
CPRINTLN(DEBUG_MISSION,"Boost finished")
IF IS_VEHICLE_OK(sPlayerVehicle.vehPlayerVehicle) AND IS_PED_SITTING_IN_VEHICLE(PLAYER_PED_ID(), sPlayerVehicle.vehPlayerVehicle)
IF IS_THIS_MODEL_A_CAR(GET_ENTITY_MODEL(sPlayerVehicle.vehPlayerVehicle))
SET_VEHICLE_BOOST_ACTIVE(sPlayerVehicle.vehPlayerVehicle, FALSE)
ENDIF
eBoostState = BS_FINISHED
ENDIF
ELSE
IF IS_VEHICLE_OK(sPlayerVehicle.vehPlayerVehicle) AND IS_PED_SITTING_IN_VEHICLE(PLAYER_PED_ID(), sPlayerVehicle.vehPlayerVehicle)
APPLY_FORCE_TO_ENTITY(sPlayerVehicle.vehPlayerVehicle, APPLY_TYPE_FORCE, <<0.0, 25.0, 0.0>>, <<0.0, 0.0, 0.0>>, 0, TRUE, TRUE, TRUE)
ENDIF
ENDIF
BREAK
ENDSWITCH
ENDPROC