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

903 lines
30 KiB
Scheme
Executable File

// *****************************************************************************************
// *****************************************************************************************
// *****************************************************************************************
//
// SCRIPT NAME : TRI_Racer.sch
// AUTHOR : Nicholas Zippmann
// DESCRIPTION : Single Player Races - Racer procs/functions file
//
// *****************************************************************************************
// *****************************************************************************************
// *****************************************************************************************
USING "TRI_Head.sch"
USING "TRI_Helpers.sch"
USING "TRI_Gate.sch"
USING "chase_hint_cam.sch"
USING "stunt_plane_public.sch"
// -----------------------------------
// SETUP PROCS/FUNCTIONS
// -----------------------------------
// bits for dialogue
// tracks how many consecutive gates weve hit
ENUM GateStuntBits
Missed_Once = BIT0,
Missed_Twice = BIT1,
Missed_Third = BIT2,
Hit_Once = BIT3,
Hit_Twice = BIT4,
Hit_Third = BIT5,
Knife_Once = BIT6,
Knife_Twice = BIT7,
Knife_Third = BIT8,
Inverted_Once = BIT9,
Inverted_Twice = BIT10,
Inverted_Third = BIT11,
Perfect_Once = BIT12,
Perfect_Twice = BIT13,
Perfect_Third = BIT14,
P_Inverted_Once = BIT15,
P_Inverted_Twice = BIT16,
P_Inverted_Third = BIT17,
P_Knife_Once = BIT18,
P_Knife_Twice = BIT19,
P_Knife_Third = BIT20
ENDENUM
#IF NOT DEFINED(OFFROAD_RACE_VEHICLE_CHECK)
FUNC INT OFFROAD_RACE_VEHICLE_CHECK(TRI_RACER_STRUCT& Racer, INT& iMesaTop)
DEBUG_MESSAGE("Printing stuff inside of empty OFFROAD_RACE_VEHICLE_CHECK")
iMesaTop = -1
PRINTFLOAT(Racer.fClockTime)
PRINTNL()
RETURN -1
ENDFUNC
#ENDIF
#IF NOT DEFINED(UnfreezePlayerVehicle)
PROC UnfreezePlayerVehicle()
DEBUG_MESSAGE("Printing stuff inside of empty UnfreezePlayerVehicle")
ENDPROC
#ENDIF
#IF NOT DEFINED(Play_Gate_Audio)
PROC Play_Gate_Audio(STRING sLine, INT iBitmaskInt)
PRINTINT(iBitmaskInt)
PRINTNL()
PRINTSTRING(sLine)
PRINTNL()
ENDPROC
#ENDIF
PROC TRI_Racer_Init(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Init")
Racer.szName = "Racer"
Racer.Driver = NULL
Racer.Vehicle = NULL
Racer.Blip = NULL
Racer.iGateCur = -1
Racer.iRank = 0
Racer.fClockTime = 0.0
Racer.fPlsMnsLst = 0.0
Racer.fPlsMnsTot = 0.0
Racer.eReset = TRI_RACER_RESET_WAIT
Racer.vStartPos = TRI_Master.vDefRcrPos
Racer.fStartHead = TRI_Master.fDefRcrHead
Racer.eDriverType = TRI_Master.eDefDrvType
Racer.eDriverModel = TRI_Master.eDefDrvModel
Racer.eVehicleModel = TRI_Master.eDefVehModel
ENDPROC
PROC TRI_Racer_Setup_Misc(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Setup_Misc")
Racer.Blip = NULL
Racer.iGateCur = -1
Racer.iRank = 0
Racer.fClockTime = 0.0
Racer.eReset = TRI_RACER_RESET_WAIT
ENDPROC
PROC TRI_Racer_Setup_Name(TRI_RACER_STRUCT& Racer, STRING sName)
//DEBUG_MESSAGE("TRI_Racer_Setup_Name")
Racer.szName = sName
ENDPROC
PROC TRI_Racer_Setup_Entities(TRI_RACER_STRUCT& Racer, PED_INDEX Driver, VEHICLE_INDEX Vehicle)
//DEBUG_MESSAGE("TRI_Racer_Setup_Entities")
Racer.Driver = Driver
Racer.Vehicle = Vehicle
ENDPROC
PROC TRI_Racer_Setup_Start(TRI_RACER_STRUCT& Racer, VECTOR vStartPos, FLOAT fStartHead)
//DEBUG_MESSAGE("TRI_Racer_Setup_Start")
Racer.vStartPos = vStartPos
Racer.fStartHead = fStartHead
ENDPROC
PROC TRI_Racer_Setup_Types(TRI_RACER_STRUCT& Racer, PED_TYPE eDriverType, MODEL_NAMES eDriverModel, MODEL_NAMES eVehicleModel)
//DEBUG_MESSAGE("TRI_Racer_Setup_Types")
Racer.eDriverType = eDriverType
Racer.eDriverModel = eDriverModel
Racer.eVehicleModel = eVehicleModel
ENDPROC
// -----------------------------------
// DRIVER CREATION PROCS/FUNCTIONS
// -----------------------------------
PROC TRI_Racer_Driver_Request(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Driver_Request")
IF (Racer.eDriverModel = DUMMY_MODEL_FOR_SCRIPT)
SCRIPT_ASSERT("TRI_Racer_Driver_Request: Model invalid!")
EXIT
ENDIF
REQUEST_MODEL(Racer.eDriverModel)
ENDPROC
FUNC BOOL TRI_Racer_Driver_Stream(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Driver_Stream")
IF (Racer.eDriverModel = DUMMY_MODEL_FOR_SCRIPT)
SCRIPT_ASSERT("TRI_Racer_Driver_Stream: Model invalid!")
RETURN FALSE
ENDIF
RETURN HAS_MODEL_LOADED(Racer.eDriverModel)
ENDFUNC
PROC TRI_Racer_Driver_Evict(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Driver_Evict")
IF (Racer.eDriverModel = DUMMY_MODEL_FOR_SCRIPT)
SCRIPT_ASSERT("TRI_Racer_Driver_Evict: Model invalid!")
EXIT
ENDIF
SET_MODEL_AS_NO_LONGER_NEEDED(Racer.eDriverModel)
ENDPROC
FUNC BOOL TRI_Racer_Driver_Create(TRI_RACER_STRUCT& Racer)
DEBUG_MESSAGE("TRI_Racer_Driver_Create")
// Check if driver model is valid before continuing.
IF (Racer.eDriverModel = DUMMY_MODEL_FOR_SCRIPT)
SCRIPT_ASSERT("TRI_Racer_Driver_Create: Model invalid!")
RETURN FALSE
ENDIF
// Check if driver already exists.
IF NOT IS_ENTITY_DEAD(Racer.Driver)
// Set driver health to max.
SET_ENTITY_HEALTH(Racer.Driver, 1000)
// If vehicle also exists, put driver in it, if needed.
IF NOT IS_ENTITY_DEAD(Racer.Vehicle)
//DEBUG_MESSAGE("TRI_Racer_Driver_Create: Tele Veh")
IF NOT IS_PED_SITTING_IN_VEHICLE(Racer.Driver, Racer.Vehicle)
SET_PED_INTO_VEHICLE(Racer.Driver, Racer.Vehicle)
ENDIF
// Otherwise, vehicle doesn't exist, teleport driver to start.
ELSE
//DEBUG_MESSAGE("TRI_Racer_Driver_Create: Teleport")
SET_ENTITY_COORDS(Racer.Driver, Racer.vStartPos)
SET_ENTITY_HEADING(Racer.Driver, Racer.fStartHead)
ENDIF
// Otherwise, if vehicle exists, create driver inside it and check for validity.
ELIF NOT IS_ENTITY_DEAD(Racer.Vehicle)
//DEBUG_MESSAGE("TRI_Racer_Driver_Create: Create In Vehicle")
Racer.Driver = CREATE_PED_INSIDE_VEHICLE(Racer.Vehicle, Racer.eDriverType, Racer.eDriverModel)
IF IS_ENTITY_DEAD(Racer.Driver)
DEBUG_MESSAGE("TRI_Racer_Driver_Create: Failed to create driver in vehicle!")
RETURN FALSE
ENDIF
// Otherwise, vehicle doesn't exist, just create driver normally and check for validity.
ELSE
//DEBUG_MESSAGE("TRI_Racer_Driver_Create: Create On Foot")
Racer.Driver = CREATE_PED(Racer.eDriverType, Racer.eDriverModel, Racer.vStartPos, Racer.fStartHead)
IF IS_ENTITY_DEAD(Racer.Driver)
DEBUG_MESSAGE("TRI_Racer_Driver_Create: Failed to create driver on foot!")
RETURN FALSE
ENDIF
SET_PED_LEG_IK_MODE(Racer.Driver, LEG_IK_PARTIAL)
SET_PED_STEERS_AROUND_OBJECTS(Racer.Driver, FALSE)
CPRINTLN(DEBUG_TRIATHLON, "SET_PED_LEG_IK_MODE(Racer.Driver, LEG_IK_PARTIAL) called")
ENDIF
// Check if driver type is player.
IF (Racer.eDriverType <= PEDTYPE_PLAYER_UNUSED)
// TODO: Put player driver logic here...
// Otherwise, driver type isn't player.
ELSE
// Set driver as mission entity.
IF NOT IS_ENTITY_A_MISSION_ENTITY(Racer.Driver)
SET_ENTITY_AS_MISSION_ENTITY(Racer.Driver)
ENDIF
// Set driver debug name.
TEXT_LABEL szDebugName = Racer.szName
szDebugName += "_Drv"
SET_PED_NAME_DEBUG(Racer.Driver, szDebugName)
ENDIF
IF NOT IS_ENTITY_DEAD(Racer.Driver)
IF ORR_DOES_RACE_REQUIRE_MOTORCYCLE()
SET_PED_HELMET(Racer.Driver, TRUE)
GIVE_PED_HELMET(Racer.Driver, TRUE)
ENDIF
ENDIF
// Racer driver successfully created.
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// In Triathlon, reset the player's health.
PROC TRI_Racer_TriRacer_Create(TRI_RACER_STRUCT& Racer)
CPRINTLN(DEBUG_TRIATHLON, "[TRI_Racer.sch->TRI_Racer_TriRacer_Create] Procedure started.")
// Check if driver already exists.
IF NOT IS_ENTITY_DEAD(Racer.Driver)
CPRINTLN(DEBUG_TRIATHLON, "[TRI_Racer.sch->TRI_Racer_TriRacer_Create] Setting driver to full health.")
SET_ENTITY_HEALTH(Racer.Driver, 1000)
ENDIF
ENDPROC
/// PURPOSE:
/// In Triathlon, place the recreated player after resetting.
PROC TRI_Racer_TriRacer_Place(TRI_RACE_STRUCT& Race, TRI_RACER_STRUCT& Racer)
VECTOR vFwd
// Check if the current gate is the first one.
IF Racer.iGateCur > 0
VECTOR vProbeStartPosition //10m above the gate so we can probe
VECTOR vProbeEndPosition //10m below the gate so we can probe
VECTOR vNewPlayerPosition //the pos we'll be setting the player to
// If not, warp the bike to the start of the checkpoint previous to the current.
PRINTLN("Gate position is... ", Race.sGate[Racer.iGateCur - 1].vPos)
//Setup our test vectors, and the player coords
vProbeStartPosition = Race.sGate[Racer.iGateCur - 1].vPos
vProbeEndPosition = Race.sGate[Racer.iGateCur - 1].vPos
vNewPlayerPosition = Race.sGate[Racer.iGateCur - 1].vPos
vProbeStartPosition.z += 10
vProbeEndPosition.z -= 10
PRINTLN("Adding 10 to the z for the start probe... ", vProbeStartPosition)
PRINTLN("Subtracting 10 to the z for the end probe... ", vProbeEndPosition)
PRINTLN("Probing for water... ")
IF TEST_PROBE_AGAINST_WATER(vProbeStartPosition, vProbeEndPosition, vNewPlayerPosition)
PRINTLN("Found a water intersection! Setting new player position... ", vNewPlayerPosition)
vNewPlayerPosition.z += TRI_WATER_RESET_OFFSET
SET_ENTITY_COORDS(Racer.Driver, vNewPlayerPosition)
ELSE
PRINTLN("No water detected. Probing ground...")
IF GET_GROUND_Z_FOR_3D_COORD(vProbeStartPosition, vNewPlayerPosition.z)
PRINTLN("Found a ground z so setting the player here...", vNewPlayerPosition)
SET_ENTITY_COORDS(Racer.Driver, vNewPlayerPosition)
ELSE
PRINTLN("No ground coord so setting player here...", vNewPlayerPosition)
SET_ENTITY_COORDS(Racer.Driver, vNewPlayerPosition)
ENDIF
ENDIF
// Get the vector between the previous and current checkpoint, to use as the driver's heading.
vFwd = Race.sGate[Racer.iGateCur].vPos - Race.sGate[Racer.iGateCur - 1].vPos
ELSE
// Warp the player to the start position.
SET_ENTITY_COORDS(Racer.Driver, Racer.vStartPos)
// Get the vector between the starting position, and the current checkpoint, to use as the driver's heading.
vFwd = Race.sGate[Racer.iGateCur].vPos - Racer.vStartPos
ENDIF
// Set its heading towards the current checkpoint.
IF NOT IS_ENTITY_DEAD(Racer.Driver)
FLOAT fPlayerHeading
fPlayerHeading = GET_HEADING_FROM_VECTOR_2D(vFwd.x, vFwd.y)
SET_ENTITY_HEADING(Racer.Driver, fPlayerHeading)
ENDIF
ENDPROC
PROC TRI_Racer_Driver_Freeze(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Driver_Freeze")
IF NOT IS_ENTITY_DEAD(Racer.Driver)
IF NOT IS_PED_IN_ANY_VEHICLE(Racer.Driver)
FREEZE_ENTITY_POSITION(Racer.Driver, TRUE)
SET_ENTITY_COLLISION(Racer.Driver, FALSE)
ENDIF
SET_ENTITY_INVINCIBLE(Racer.Driver, TRUE)
ENDIF
ENDPROC
PROC TRI_Racer_Driver_UnFreeze(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Driver_UnFreeze")
IF NOT IS_ENTITY_DEAD(Racer.Driver)
IF NOT IS_PED_IN_ANY_VEHICLE(Racer.Driver)
FREEZE_ENTITY_POSITION(Racer.Driver, FALSE)
SET_ENTITY_COLLISION(Racer.Driver, TRUE)
ENDIF
SET_ENTITY_INVINCIBLE(Racer.Driver, FALSE)
ENDIF
ENDPROC
PROC TRI_Racer_Driver_Release(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Driver_Release")
IF DOES_ENTITY_EXIST(Racer.Driver)
// TODO: Decide if this player check is even needed...
IF (Racer.Driver <> PLAYER_PED_ID())
SET_PED_AS_NO_LONGER_NEEDED(Racer.Driver)
ENDIF
ENDIF
ENDPROC
PROC TRI_Racer_Driver_Destroy(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Driver_Destroy")
IF DOES_ENTITY_EXIST(Racer.Driver)
// TODO: Decide if this player check is even needed...
IF (Racer.Driver <> PLAYER_PED_ID())
DELETE_PED(Racer.Driver)
ENDIF
ENDIF
ENDPROC
// -----------------------------------
// VEHICLE CREATION PROCS/FUNCTIONS
// -----------------------------------
PROC TRI_Racer_Vehicle_Request(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Vehicle_Request")
IF (Racer.eVehicleModel = DUMMY_MODEL_FOR_SCRIPT)
DEBUG_MESSAGE("TRI_Racer_Vehicle_Request: Model invalid!")
EXIT
ENDIF
REQUEST_MODEL(Racer.eVehicleModel)
ENDPROC
FUNC BOOL TRI_Racer_Vehicle_Stream(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Vehicle_Stream")
IF (Racer.eVehicleModel = DUMMY_MODEL_FOR_SCRIPT)
DEBUG_MESSAGE("TRI_Racer_Vehicle_Stream: Model invalid!")
RETURN TRUE
ENDIF
RETURN HAS_MODEL_LOADED(Racer.eVehicleModel)
ENDFUNC
PROC TRI_Racer_Vehicle_Evict(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Vehicle_Evict")
IF (Racer.eVehicleModel = DUMMY_MODEL_FOR_SCRIPT)
DEBUG_MESSAGE("TRI_Racer_Vehicle_Evict: Model invalid!")
EXIT
ENDIF
SET_MODEL_AS_NO_LONGER_NEEDED(Racer.eVehicleModel)
ENDPROC
FUNC BOOL TRI_Racer_Vehicle_Create(TRI_RACER_STRUCT& Racer, BOOL bForceCreate)
DEBUG_MESSAGE("TRI_Racer_Vehicle_Create called.")
// If forcing a create and vehicle already exists, destroy it.
IF bForceCreate
DEBUG_MESSAGE("Inside force create vehicle")
IF TRI_Master.eRaceType = TRI_RACE_TYPE_PLANE
IF DOES_ENTITY_EXIST(Racer.Vehicle)
IF NOT IS_ENTITY_DEAD(Racer.Vehicle)
OR NOT IS_VEHICLE_DRIVEABLE(Racer.Vehicle)
IF IS_PED_IN_VEHICLE(Racer.Driver, Racer.Vehicle)
CLEAR_PED_TASKS_IMMEDIATELY(Racer.Driver)
DELETE_VEHICLE(Racer.Vehicle)
DEBUG_MESSAGE("TRI_Racer_Vehicle_Create: Force deleted Stunt plane")
ENDIF
ELSE
IF IS_PED_IN_VEHICLE(Racer.Driver, Racer.Vehicle)
SET_ENTITY_COORDS(Racer.Driver, Racer.vStartPos)
ENDIF
IF IS_PED_IN_VEHICLE(Racer.Driver, Racer.Vehicle)
DEBUG_MESSAGE("TRI_Racer_Vehicle_Create: Driver still in Vehicle, not deleting vehicle")
ELSE
DELETE_VEHICLE(Racer.Vehicle)
ENDIF
ENDIF
ENDIF
ELSE
IF DOES_ENTITY_EXIST(Racer.Vehicle)
IF NOT IS_ENTITY_DEAD(Racer.Driver)
AND NOT IS_ENTITY_DEAD(Racer.Vehicle)
IF IS_PED_IN_VEHICLE(Racer.Driver, Racer.Vehicle)
CLEAR_PED_TASKS_IMMEDIATELY(Racer.Driver)
ENDIF
ENDIF
IF IS_PED_IN_ANY_VEHICLE(Racer.Driver)
VEHICLE_INDEX iVehicle = GET_VEHICLE_PED_IS_IN(Racer.Driver)
IF IS_ENTITY_A_MISSION_ENTITY(iVehicle)
DELETE_VEHICLE(iVehicle)
DEBUG_MESSAGE("Deleted the Racer.Vehicle inside force create vehicle")
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
// If vehicle already exists, set health to max and teleport it.
IF NOT IS_ENTITY_DEAD(Racer.Vehicle)
//IF (TRI_Master.eRaceType = TRI_RACE_TYPE_PLANE) - removing for 162891
DEBUG_MESSAGE("TRI_Racer_Vehicle_Create: Teleport")
SET_ENTITY_HEALTH(Racer.Vehicle, 1000)
SET_VEHICLE_FIXED(Racer.Vehicle)
SET_VEHICLE_ENGINE_HEALTH(Racer.Vehicle, 1000.0)
SET_ENTITY_COORDS(Racer.Vehicle, Racer.vStartPos) //Racer.vStartPos)
SET_ENTITY_HEADING(Racer.Vehicle, Racer.fStartHead)
IF (TRI_Master.eRaceType = TRI_RACE_TYPE_PLANE)
SET_VEHICLE_ENGINE_ON(Racer.Vehicle, TRUE, TRUE)
SET_HELI_BLADES_FULL_SPEED(Racer.Vehicle)
ENDIF
//ENDIF
// Otherwise, vehicle doesn't exist, create it and check for validity.
ELSE
DEBUG_MESSAGE("TRI_Racer_Vehicle_Create: Create New Vehicle")
IF (Racer.eVehicleModel = DUMMY_MODEL_FOR_SCRIPT)
DEBUG_MESSAGE("TRI_Racer_Create: No vehicle to create!")
RETURN TRUE
ENDIF
IF IS_ENTITY_DEAD(Racer.Vehicle)
DEBUG_MESSAGE("TRI_Racer_Create: Failed to create vehicle!")
RETURN FALSE
ENDIF
ENDIF
IF (TRI_Master.eRaceType <> TRI_RACE_TYPE_PLANE)
IF (GET_ENTITY_HEIGHT_ABOVE_GROUND(Racer.Vehicle) <= TRI_ON_GROUND_DIST)
SET_VEHICLE_ON_GROUND_PROPERLY(Racer.Vehicle)
IF (GET_ENTITY_HEIGHT_ABOVE_GROUND(Racer.Vehicle) > 1.5)
VECTOR vEntityCoords = GET_ENTITY_COORDS(Racer.Vehicle)
vEntityCoords.z -= GET_ENTITY_HEIGHT_ABOVE_GROUND(Racer.Vehicle) + 1.5
SET_ENTITY_COORDS(Racer.Vehicle, vEntityCoords, TRUE)
ENDIF
ENDIF
ENDIF
SET_VEHICLE_ENGINE_ON(Racer.Vehicle, TRUE, TRUE)
IF TRI_Master.eRaceType = TRI_RACE_TYPE_PLANE
SET_HELI_BLADES_FULL_SPEED(Racer.Vehicle)
ENDIF
// If driver exists, put them in vehicle, if needed.
IF NOT IS_ENTITY_DEAD(Racer.Driver)
IF NOT IS_PED_SITTING_IN_VEHICLE(Racer.Driver, Racer.Vehicle)
SET_PED_INTO_VEHICLE(Racer.Driver, Racer.Vehicle)
ENDIF
ENDIF
// Set vehicle as mission entity.
IF NOT IS_ENTITY_A_MISSION_ENTITY(Racer.Vehicle)
SET_ENTITY_AS_MISSION_ENTITY(Racer.Vehicle)
ENDIF
// Set vehicle debug name.
TEXT_LABEL szDebugName = Racer.szName
szDebugName += "_Veh"
SET_VEHICLE_NAME_DEBUG(Racer.Vehicle, szDebugName)
// Check if driver type is player.
IF (Racer.eDriverType <= PEDTYPE_PLAYER_UNUSED)
// Store player vehicle in master struct.
TRI_Master.PlayerVeh = Racer.Vehicle
// Allow player to maintain speed during autopilot.
SET_VEHICLE_LIMIT_SPEED_WHEN_PLAYER_INACTIVE(Racer.Vehicle, FALSE)
// Otherwise, driver type isn't player.
ELSE
// TODO: Put ai driver logic here...
ENDIF
// Racer vehicle successfully created.
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// In Triathlon, recreate the player's bike after being reset.
PROC TRI_Racer_Tribike_Create(TRI_RACER_STRUCT& Racer)
CPRINTLN(DEBUG_TRIATHLON, "[TRI_Racer.sch->TRI_Racer_Tribike_Create] Procedure started.")
IF DOES_ENTITY_EXIST(Racer.Vehicle)
IF NOT IS_ENTITY_DEAD(Racer.Vehicle)
CPRINTLN(DEBUG_TRIATHLON, "[TRI_Racer.sch->TRI_Racer_Tribike_Create] Player bike is NOT DEAD. Fixing bike.")
// Fix up the bike.
SET_ENTITY_HEALTH(Racer.Vehicle, 1000)
SET_VEHICLE_ENGINE_HEALTH(Racer.Vehicle, 1000.0)
ELSE
CPRINTLN(DEBUG_TRIATHLON, "[TRI_Racer.sch->TRI_Racer_Tribike_Create] Player bike IS DEAD. Creating new bike.")
IF NOT IS_ENTITY_DEAD(Racer.Driver)
// If bike is dead, create a new one away from the water.
VECTOR vCreateBikeHere = GET_ENTITY_COORDS(Racer.Driver)
vCreateBikeHere.Z += 3.0
Racer.Vehicle = CREATE_VEHICLE(TRIBIKE, vCreateBikeHere, GET_ENTITY_HEADING(Racer.Driver))
ENDIF
ENDIF
IF NOT IS_ENTITY_DEAD(Racer.Driver)
// Heal the player, just in case.
SET_ENTITY_HEALTH(Racer.Driver, 1000)
ENDIF
// Teleport the player to be on the bike.
IF NOT IS_PED_IN_ANY_VEHICLE(Racer.Driver)
IF NOT IS_ENTITY_DEAD(Racer.Driver)
IF NOT IS_ENTITY_DEAD(Racer.Vehicle)
CPRINTLN(DEBUG_TRIATHLON, "[TRI_Racer.sch->TRI_Racer_Tribike_Create] Player is NOT ON BIKE. Setting player on bike.")
SET_PED_INTO_VEHICLE(Racer.Driver, Racer.Vehicle)
ENDIF
ENDIF
ENDIF
IF NOT IS_PED_IN_ANY_VEHICLE(Racer.Driver)
CPRINTLN(DEBUG_TRIATHLON, "[TRI_Racer.sch->TRI_Racer_Tribike_Create] Player STILL NOT ON BIKE!")
ELSE
CPRINTLN(DEBUG_TRIATHLON, "[TRI_Racer.sch->TRI_Racer_Tribike_Create] Player successfully put ON BIKE!")
ENDIF
ELSE
CPRINTLN(DEBUG_TRIATHLON, "[TRI_Racer.sch->TRI_Racer_Tribike_Create] Player bike DOES NOT EXIST!")
ENDIF
ENDPROC
/// PURPOSE:
/// In Triathlon, place the recreated player's bike in the correct position.
PROC TRI_Racer_Tribike_Place(TRI_RACE_STRUCT& Race, TRI_RACER_STRUCT& Racer)
VECTOR vForwardPreviousToCurrentGate
// Check if the current gate is the first one.
IF Racer.iGateCur > 0
IF NOT IS_PED_IN_ANY_VEHICLE(Racer.Driver)
IF NOT IS_ENTITY_DEAD(Racer.Driver)
IF NOT IS_ENTITY_DEAD(Racer.Vehicle)
CPRINTLN(DEBUG_TRIATHLON, "[TRI_Racer.sch->TRI_Racer_Tribike_Place] Player is NOT IN BIKE. Setting player in bike.")
SET_PED_INTO_VEHICLE(Racer.Driver, Racer.Vehicle)
ENDIF
ENDIF
ELSE
CPRINTLN(DEBUG_TRIATHLON, "[TRI_Racer.sch->TRI_Racer_Tribike_Place] Player ALREADY ON BIKE..")
ENDIF
// If not, warp the bike to the start of the checkpoint previous to the current.
//SET_ENTITY_COORDS(Racer.Vehicle, Race.sGate[Racer.iGateCur - 1].vPos)
// Teleport the player and his bike to the last gate cleared.
CPRINTLN(DEBUG_TRIATHLON, "[TRI_Racer.sch->TRI_Racer_Tribike_Place] Teleporting player with bike.")
SET_PED_COORDS_KEEP_VEHICLE(Racer.Driver, Race.sGate[Racer.iGateCur - 1].vPos)
// Get the vector between the previous and current checkpoint, to use as the bike's heading.
vForwardPreviousToCurrentGate = Race.sGate[Racer.iGateCur].vPos - Race.sGate[Racer.iGateCur - 1].vPos
// We should never get here, but just in case.
ELSE
CPRINTLN(DEBUG_TRIATHLON, "[TRI_Racer.sch->TRI_Racer_Tribike_Place] ERROR: This should not be called if gate is 0! The first gate is always a water gate!")
ENDIF
// Set its heading towards the current checkpoint.
IF NOT IS_ENTITY_DEAD(Racer.Vehicle)
FLOAT fBikeHeading
fBikeHeading = GET_HEADING_FROM_VECTOR_2D(vForwardPreviousToCurrentGate.x, vForwardPreviousToCurrentGate.y)
SET_ENTITY_HEADING(Racer.Vehicle, fBikeHeading)
ENDIF
ENDPROC
PROC TRI_Racer_Vehicle_Freeze(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Vehicle_Freeze")
IF NOT IS_ENTITY_DEAD(Racer.Vehicle)
FREEZE_ENTITY_POSITION(Racer.Vehicle, TRUE)
SET_ENTITY_COLLISION(Racer.Vehicle, FALSE)
SET_ENTITY_INVINCIBLE(Racer.Vehicle, TRUE)
ENDIF
ENDPROC
PROC TRI_Racer_Vehicle_UnFreeze(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Vehicle_UnFreeze")
IF NOT IS_ENTITY_DEAD(Racer.Vehicle)
FREEZE_ENTITY_POSITION(Racer.Vehicle, FALSE)
SET_ENTITY_COLLISION(Racer.Vehicle, TRUE)
SET_ENTITY_INVINCIBLE(Racer.Vehicle, FALSE)
ENDIF
ENDPROC
PROC TRI_Racer_Vehicle_Release(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Vehicle_Release")
IF DOES_ENTITY_EXIST(Racer.Vehicle)
// TODO: Decide if this player check is even needed...
IF (Racer.Vehicle <> TRI_Master.PlayerVeh)
IF NOT IS_ENTITY_DEAD(Racer.Driver)
IF IS_PED_IN_VEHICLE(Racer.Driver, Racer.Vehicle)
TASK_LEAVE_ANY_VEHICLE(Racer.Driver)
ENDIF
ENDIF
SET_VEHICLE_AS_NO_LONGER_NEEDED(Racer.Vehicle)
ENDIF
ENDIF
ENDPROC
PROC TRI_Racer_Vehicle_Destroy(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Vehicle_Destroy")
IF DOES_ENTITY_EXIST(Racer.Vehicle)
// TODO: Decide if this player check is even needed...
IF (Racer.Vehicle <> TRI_Master.PlayerVeh)
IF NOT ( IS_ENTITY_DEAD(Racer.Driver) OR IS_ENTITY_DEAD(Racer.Vehicle) )
IF IS_PED_IN_VEHICLE(Racer.Driver, Racer.Vehicle)
CLEAR_PED_TASKS_IMMEDIATELY(Racer.Driver)
ENDIF
ENDIF
DELETE_VEHICLE(Racer.Vehicle)
ENDIF
ENDIF
ENDPROC
// -----------------------------------
// MAIN CREATION PROCS/FUNCTIONS
// -----------------------------------
PROC TRI_Racer_Request(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Request")
TRI_Racer_Driver_Request(Racer)
TRI_Racer_Vehicle_Request(Racer)
ENDPROC
FUNC BOOL TRI_Racer_Stream(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Stream")
IF NOT TRI_Racer_Driver_Stream(Racer)
OR NOT TRI_Racer_Vehicle_Stream(Racer)
RETURN FALSE
ENDIF
RETURN TRUE
ENDFUNC
PROC TRI_Racer_Evict(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Evict")
TRI_Racer_Driver_Evict(Racer)
TRI_Racer_Vehicle_Evict(Racer)
ENDPROC
FUNC BOOL TRI_Racer_Blip_Create(TRI_RACER_STRUCT& Racer, FLOAT fScale = 1.0)
IF (Racer.Driver <> PLAYER_PED_ID())
IF NOT TRI_Blip_Entity_Create(Racer.Blip, Racer.Driver, fScale)
DEBUG_MESSAGE("TRI_Racer_Blip_Create: Failed to create racer driver blip!")
RETURN FALSE
ENDIF
ENDIF
RETURN TRUE
ENDFUNC
PROC TRI_Racer_Blip_Destroy(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Blip_Destroy")
TRI_Blip_Destroy(Racer.Blip)
ENDPROC
FUNC BOOL TRI_Racer_Create(TRI_RACER_STRUCT& Racer, BOOL bForceCreate)
DEBUG_MESSAGE("TRI_Racer_Create")
IF NOT TRI_Racer_Vehicle_Create(Racer, bForceCreate)
OR NOT TRI_Racer_Driver_Create(Racer)
RETURN FALSE
ENDIF
// TODO: Should this be done in every case?
// BUG : 86339 - they don't want racers blipped any more.
//TRI_Racer_Blip_Create(Racer)
RETURN TRUE
ENDFUNC
PROC TRI_Racer_Freeze(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Freeze")
TRI_Racer_Driver_Freeze(Racer)
TRI_Racer_Vehicle_Freeze(Racer)
ENDPROC
PROC TRI_Racer_UnFreeze(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_UnFreeze")
TRI_Racer_Driver_UnFreeze(Racer)
TRI_Racer_Vehicle_UnFreeze(Racer)
ENDPROC
PROC TRI_Racer_Release(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Release")
TRI_Racer_Blip_Destroy(Racer)
TRI_Racer_Driver_Release(Racer)
TRI_Racer_Vehicle_Release(Racer)
ENDPROC
PROC TRI_Racer_Destroy(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Destroy")
TRI_Racer_Blip_Destroy(Racer)
TRI_Racer_Driver_Destroy(Racer)
TRI_Racer_Vehicle_Destroy(Racer)
ENDPROC
// -----------------------------------
// HELPER PROCS/FUNCTIONS
// -----------------------------------
PROC TRI_Racer_Teleport(TRI_RACER_STRUCT& Racer, VECTOR vPos, FLOAT fHeading, FLOAT fSpeed)
//DEBUG_MESSAGE("TRI_Racer_Teleport")
CPRINTLN(DEBUG_TRIATHLON, "TRI_Racer_Teleport, Racer=", Racer.szName)
IF IS_ENTITY_DEAD(Racer.Vehicle) OR NOT IS_PED_IN_VEHICLE(Racer.Driver, Racer.Vehicle)
SET_ENTITY_COORDS(Racer.Driver, vPos)
SET_ENTITY_HEADING(Racer.Driver, fHeading)
ELSE
SET_ENTITY_COORDS(Racer.Vehicle, vPos)
SET_ENTITY_HEADING(Racer.Vehicle, fHeading)
SET_VEHICLE_FORWARD_SPEED(Racer.Vehicle, fSpeed)
// In Tri, this code causes the player's bike to sometimes teleport below the ground.
IF (TRI_Master.eRaceType <> TRI_RACE_TYPE_PLANE) AND (TRI_Master.eRaceType <> TRI_RACE_TYPE_TRIATHLON)
SET_VEHICLE_ON_GROUND_PROPERLY(Racer.Vehicle)
WAIT(0)
IF NOT IS_ENTITY_DEAD(Racer.Vehicle)
IF (GET_ENTITY_HEIGHT_ABOVE_GROUND(Racer.Vehicle) > 1.5)
VECTOR vEntityCoords = GET_ENTITY_COORDS(Racer.Vehicle)
vEntityCoords.z -= GET_ENTITY_HEIGHT_ABOVE_GROUND(Racer.Vehicle) + 1.5
SET_ENTITY_COORDS(Racer.Vehicle, vEntityCoords, TRUE)
ENDIF
ENDIF
ENDIF
ENDIF
ENDPROC
PROC TRI_Racer_Start(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Start")
IF NOT IS_ENTITY_DEAD(Racer.Vehicle)
IF NOT IS_ENTITY_DEAD(Racer.Driver)
IF IS_PLAYBACK_GOING_ON_FOR_VEHICLE(Racer.Vehicle)
STOP_PLAYBACK_RECORDED_VEHICLE(Racer.Vehicle)
ENDIF
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(Racer.Driver, TRUE)
ENDIF
ENDIF
// Reset racer current gate.
Racer.iGateCur = 0
// Check if racer vehicle is alive.
IF NOT IS_ENTITY_DEAD(Racer.Vehicle)
// Start racer vehicle engine, if needed.
IF NOT IS_THIS_MODEL_A_BIKE(Racer.eVehicleModel)
SET_VEHICLE_ENGINE_ON(Racer.Vehicle, TRUE, TRUE)
ENDIF
// Add stuck check for racer vehicle, if needed.
// TODO: Make sure this isn't needed at all for stuck checks.
//IF NOT DOES_VEHICLE_HAVE_STUCK_VEHICLE_CHECK(Racer.Vehicle)
// ADD_VEHICLE_STUCK_CHECK(Racer.Vehicle, TRI_VEH_STUCK_MIN_MOVE, TRI_VEH_STUCK_CHK_TIME)
//ENDIF
ENDIF
ENDPROC
PROC TRI_Racer_Finish(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Finish")
// Check if racer vehicle is alive.
IF NOT IS_ENTITY_DEAD(Racer.Vehicle)
IF TRI_Master.eRaceType <> TRI_RACE_TYPE_PLANE
UnfreezePlayerVehicle()
DEBUG_MESSAGE("Unfreezing Players pre-race vehicle")
ENDIF
// Remove stuck check for racer vehicle, if needed.
// TODO: Make sure this isn't needed at all for stuck checks.
//IF DOES_VEHICLE_HAVE_STUCK_VEHICLE_CHECK(Racer.Vehicle)
// REMOVE_VEHICLE_STUCK_CHECK(Racer.Vehicle)
//ENDIF
ENDIF
ENDPROC
FUNC BOOL TRI_Racer_Crash_Check(TRI_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("TRI_Racer_Crash_Check")
IF (Racer.eVehicleModel <> DUMMY_MODEL_FOR_SCRIPT)
IF Racer.Driver = PLAYER_PED_ID() //special cases for player in off-road, as player can get out of vehicle...spaghetti-o'd - SiM
AND TRI_Master.eRaceType = TRI_RACE_TYPE_OFFROAD
IF IS_PED_IN_VEHICLE(Racer.Driver, Racer.Vehicle)
IF IS_ENTITY_DEAD(Racer.Vehicle)
DEBUG_MESSAGE("TRI_Racer_Crash_Check: Vehicle dead!")
RETURN TRUE
ENDIF
IF NOT IS_VEHICLE_DRIVEABLE(Racer.Vehicle)
DEBUG_MESSAGE("TRI_Racer_Crash_Check: Vehicle undrivable!")
RETURN TRUE
ENDIF
ENDIF
ELSE
IF IS_ENTITY_DEAD(Racer.Vehicle)
DEBUG_MESSAGE("TRI_Racer_Crash_Check: Vehicle dead!")
RETURN TRUE
ENDIF
IF NOT IS_VEHICLE_DRIVEABLE(Racer.Vehicle)
DEBUG_MESSAGE("TRI_Racer_Crash_Check: Vehicle undrivable!")
RETURN TRUE
ENDIF
ENDIF
// TODO: Make sure this isn't needed at all for stuck checks.
//IF DOES_VEHICLE_HAVE_STUCK_VEHICLE_CHECK(Racer.Vehicle)
IF NOT IS_PLAYER_DEAD(PLAYER_ID())
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), Racer.Vehicle)
IF (GET_ENTITY_SPEED(Racer.Vehicle) <= 15.0)
IF Racer.eVehicleModel = STUNT
IF GET_ENTITY_HEIGHT_ABOVE_GROUND(Racer.Vehicle) < 3.0
OR HAS_ENTITY_COLLIDED_WITH_ANYTHING(Racer.Vehicle)
IF IS_ENTITY_UPSIDEDOWN(Racer.Vehicle) AND GET_ENTITY_HEIGHT_ABOVE_GROUND(Racer.Vehicle) < 1.0
DEBUG_MESSAGE("we on the roof!")
RETURN TRUE
ENDIF
IF IS_VEHICLE_STUCK_TIMER_UP(Racer.Vehicle, VEH_STUCK_ON_ROOF, TRI_VEH_STUCK_PLANE_ROOF_TIME)
DEBUG_MESSAGE("TRI_Racer_Crash_Check: Vehicle stuck (roof)!")
RETURN TRUE
ENDIF
IF IS_VEHICLE_STUCK_TIMER_UP(Racer.Vehicle, VEH_STUCK_ON_SIDE, TRI_VEH_STUCK_PLANE_SIDE_TIME)
DEBUG_MESSAGE("TRI_Racer_Crash_Check: Vehicle stuck (side)!")
RETURN TRUE
ENDIF
IF IS_VEHICLE_STUCK_TIMER_UP(Racer.Vehicle, VEH_STUCK_HUNG_UP, TRI_VEH_STUCK_PLANE_HUNG_TIME)
DEBUG_MESSAGE("TRI_Racer_Crash_Check: Vehicle stuck (hung)!")
RETURN TRUE
ENDIF
IF IS_VEHICLE_STUCK_TIMER_UP(Racer.Vehicle, VEH_STUCK_JAMMED, TRI_VEH_STUCK_PLANE_JAM_TIME)
DEBUG_MESSAGE("TRI_Racer_Crash_Check: Vehicle stuck (jam)!")
RETURN TRUE
ENDIF
ENDIF
ELSE
IF IS_VEHICLE_STUCK_TIMER_UP(Racer.Vehicle, VEH_STUCK_ON_ROOF, TRI_VEH_STUCK_BIKE_ROOF_TIME)
DEBUG_MESSAGE("TRI_Racer_Crash_Check: Vehicle stuck (roof)!")
RETURN TRUE
ENDIF
IF IS_VEHICLE_STUCK_TIMER_UP(Racer.Vehicle, VEH_STUCK_ON_SIDE, TRI_VEH_STUCK_BIKE_SIDE_TIME)
DEBUG_MESSAGE("TRI_Racer_Crash_Check: Vehicle stuck (side)!")
RETURN TRUE
ENDIF
IF IS_VEHICLE_STUCK_TIMER_UP(Racer.Vehicle, VEH_STUCK_HUNG_UP, TRI_VEH_STUCK_BIKE_HUNG_TIME)
DEBUG_MESSAGE("TRI_Racer_Crash_Check: Vehicle stuck (hung)!")
RETURN TRUE
ENDIF
IF IS_VEHICLE_STUCK_TIMER_UP(Racer.Vehicle, VEH_STUCK_JAMMED, TRI_VEH_STUCK_BIKE_JAM_TIME)
DEBUG_MESSAGE("TRI_Racer_Crash_Check: Vehicle stuck (jam)!")
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
//ENDIF
ENDIF
RETURN FALSE
ENDFUNC
// END OF FILE! DO NOT ADD ANYTHING BELOW THIS LINE!