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

1302 lines
45 KiB
Scheme
Executable File

// *****************************************************************************************
// *****************************************************************************************
// *****************************************************************************************
//
// SCRIPT NAME : SPTT_Racer.sch
// AUTHOR : Nicholas Zippmann
// DESCRIPTION : Single Player Races - Racer procs/functions file
//
// *****************************************************************************************
// *****************************************************************************************
// *****************************************************************************************
USING "SPTT_Head.sch"
USING "SPTT_Helpers.sch"
USING "SPTT_Gate.sch"
USING "chase_hint_cam.sch"
USING "stunt_plane_public.sch"
// -----------------------------------
// SETUP PROCS/FUNCTIONS
// -----------------------------------
// bits for dialogue
INT iMissedGateBits // missed gates
INT iClearedGateBits // cleared gates
INT iPerfectGateBits // perfect gates
INT iInvertedGateBits // inverted gates
INT iKnifeGateBits // knife gates
INT iPerfInvGateBits // perfect inverted gates
INT iPerfKniGateBits // perfect knife gates
// tracks how many consecutive gates weve hit
INT iStuntGateHit
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(SPTT_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 SPTT_Racer_Init(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_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 = SPTT_RACER_RESET_WAIT
Racer.vStartPos = SPTT_Master.vDefRcrPos
Racer.fStartHead = SPTT_Master.fDefRcrHead
Racer.eDriverType = SPTT_Master.eDefDrvType
Racer.eDriverModel = SPTT_Master.eDefDrvModel
Racer.eVehicleModel = SPTT_Master.eDefVehModel
ENDPROC
PROC SPTT_Racer_Setup_Misc(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_Racer_Setup_Misc")
Racer.Blip = NULL
Racer.iGateCur = -1
Racer.iRank = 0
Racer.fClockTime = 0.0
Racer.eReset = SPTT_RACER_RESET_WAIT
ENDPROC
PROC SPTT_Racer_Setup_Name(SPTT_RACER_STRUCT& Racer, STRING sName)
//DEBUG_MESSAGE("SPTT_Racer_Setup_Name")
Racer.szName = sName
ENDPROC
PROC SPTT_Racer_Setup_Entities(SPTT_RACER_STRUCT& Racer, PED_INDEX Driver, VEHICLE_INDEX Vehicle)
//DEBUG_MESSAGE("SPTT_Racer_Setup_Entities")
Racer.Driver = Driver
Racer.Vehicle = Vehicle
GET_VEHICLE_SETUP( Vehicle, SPTT_Master.vssPlaneSetup )
ENDPROC
PROC SPTT_Racer_Setup_Start(SPTT_RACER_STRUCT& Racer, VECTOR vStartPos, FLOAT fStartHead)
//DEBUG_MESSAGE("SPTT_Racer_Setup_Start")
Racer.vStartPos = vStartPos
Racer.fStartHead = fStartHead
ENDPROC
PROC SPTT_Racer_Setup_Types(SPTT_RACER_STRUCT& Racer, PED_TYPE eDriverType, MODEL_NAMES eDriverModel, MODEL_NAMES eVehicleModel)
//DEBUG_MESSAGE("SPTT_Racer_Setup_Types")
Racer.eDriverType = eDriverType
Racer.eDriverModel = eDriverModel
Racer.eVehicleModel = eVehicleModel
ENDPROC
PROC SPTT_RESET_GATE_DIALOGUE_BITS()
iMissedGateBits = 0 // missed gates
iClearedGateBits = 0 // cleared gates
iPerfectGateBits = 0 // perfect gates
iInvertedGateBits = 0 // inverted gates
iKnifeGateBits = 0 // knife gates
iPerfInvGateBits = 0 // perfect inverted gates
iPerfKniGateBits = 0 // perfect knife gates
iStuntGateHit = 0
ENDPROC
// -----------------------------------
// DRIVER CREATION PROCS/FUNCTIONS
// -----------------------------------
PROC SPTT_Racer_Driver_Request(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_Racer_Driver_Request")
IF (Racer.eDriverModel = DUMMY_MODEL_FOR_SCRIPT)
SCRIPT_ASSERT("SPTT_Racer_Driver_Request: Model invalid!")
EXIT
ENDIF
REQUEST_MODEL(Racer.eDriverModel)
ENDPROC
FUNC BOOL SPTT_Racer_Driver_Stream(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_Racer_Driver_Stream")
IF (Racer.eDriverModel = DUMMY_MODEL_FOR_SCRIPT)
SCRIPT_ASSERT("SPTT_Racer_Driver_Stream: Model invalid!")
RETURN FALSE
ENDIF
RETURN HAS_MODEL_LOADED(Racer.eDriverModel)
ENDFUNC
PROC SPTT_Racer_Driver_Evict(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_Racer_Driver_Evict")
IF (Racer.eDriverModel = DUMMY_MODEL_FOR_SCRIPT)
SCRIPT_ASSERT("SPTT_Racer_Driver_Evict: Model invalid!")
EXIT
ENDIF
SET_MODEL_AS_NO_LONGER_NEEDED(Racer.eDriverModel)
ENDPROC
FUNC BOOL SPTT_Racer_Driver_Create(SPTT_RACER_STRUCT& Racer)
PRINTLN("[SPTT_Racer_Driver_Create] Function called.")
// Check if driver model is valid before continuing.
IF (Racer.eDriverModel = DUMMY_MODEL_FOR_SCRIPT)
SCRIPT_ASSERT("[SPTT_Racer_Driver_Create] Model invalid!")
RETURN FALSE
ENDIF
// Check if driver already exists.
IF NOT IS_ENTITY_DEAD(Racer.Driver)
PRINTLN("[SPTT_Racer_Driver_Create] Player is ALIVE.")
SET_ENTITY_HEALTH(Racer.Driver, 1000)
// If vehicle also exists, put driver in it, if needed.
IF NOT IS_ENTITY_DEAD(Racer.Vehicle)
PRINTLN("[SPTT_Racer_Driver_Create] Placing player in stunt plane.")
IF NOT IS_PED_SITTING_IN_VEHICLE(Racer.Driver, Racer.Vehicle)
SET_PED_INTO_VEHICLE(Racer.Driver, Racer.Vehicle)
ENDIF
ELSE
PRINTLN("[SPTT_Racer_Driver_Create] Warp the player to the start position.")
SET_ENTITY_COORDS(Racer.Driver, Racer.vStartPos)
SET_ENTITY_HEADING(Racer.Driver, Racer.fStartHead)
ENDIF
// The driver does not exist, but the vehicle is dead, so create driver inside it and check for validity.
ELIF NOT IS_ENTITY_DEAD(Racer.Vehicle)
PRINTLN("[SPTT_Racer_Driver_Create] Player is DEAD, but stunt plane is ALIVE. Create the player inside the vehicle.")
Racer.Driver = CREATE_PED_INSIDE_VEHICLE(Racer.Vehicle, Racer.eDriverType, Racer.eDriverModel)
IF IS_ENTITY_DEAD(Racer.Driver)
PRINTLN("[SPTT_Racer_Driver_Create] Failed to create the player in the stunt plane.")
RETURN FALSE
ENDIF
// The driver nor the vehicle exist.
ELSE
PRINTLN("[SPTT_Racer_Driver_Create] Player and stunt plane are BOTH DEAD. Create the player on foot.")
Racer.Driver = CREATE_PED(Racer.eDriverType, Racer.eDriverModel, Racer.vStartPos, Racer.fStartHead)
IF IS_ENTITY_DEAD(Racer.Driver)
PRINTLN("[SPTT_Racer_Driver_Create] Failed to create the player on foot.")
RETURN FALSE
ENDIF
ENDIF
// Check if driver type is player. NOTE: This may not be needed.
IF (Racer.eDriverType > PEDTYPE_PLAYER_UNUSED)
// 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
// Racer driver successfully created.
PRINTLN("[SPTT_Racer_Driver_Create] Player successfully created and/or set.")
RETURN TRUE
ENDFUNC
PROC SPTT_Racer_Driver_Freeze(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_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 SPTT_Racer_Driver_UnFreeze(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_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 SPTT_Racer_Driver_Release(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_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 SPTT_Racer_Driver_Destroy(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_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 SPTT_Racer_Vehicle_Request(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_Racer_Vehicle_Request")
IF (Racer.eVehicleModel = DUMMY_MODEL_FOR_SCRIPT)
DEBUG_MESSAGE("SPTT_Racer_Vehicle_Request: Model invalid!")
EXIT
ENDIF
REQUEST_MODEL(Racer.eVehicleModel)
ENDPROC
FUNC BOOL SPTT_Racer_Vehicle_Stream(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_Racer_Vehicle_Stream")
IF (Racer.eVehicleModel = DUMMY_MODEL_FOR_SCRIPT)
DEBUG_MESSAGE("SPTT_Racer_Vehicle_Stream: Model invalid!")
RETURN TRUE
ENDIF
RETURN HAS_MODEL_LOADED(Racer.eVehicleModel)
ENDFUNC
PROC SPTT_Racer_Vehicle_Evict(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_Racer_Vehicle_Evict")
IF (Racer.eVehicleModel = DUMMY_MODEL_FOR_SCRIPT)
DEBUG_MESSAGE("SPTT_Racer_Vehicle_Evict: Model invalid!")
EXIT
ENDIF
SET_MODEL_AS_NO_LONGER_NEEDED(Racer.eVehicleModel)
ENDPROC
FUNC BOOL SPTT_Racer_Vehicle_Create(SPTT_RACER_STRUCT& Racer, BOOL bForceCreate)
PRINTLN("[SPTT_Racer_Vehicle_Create] Function called.")
// If forced to create a new stunt plane and one already exists, destroy it!
IF bForceCreate
PRINTLN("[SPTT_Racer_Vehicle_Create] Force-creating the stunt plane was passed as TRUE.")
IF DOES_ENTITY_EXIST(Racer.Vehicle)
PRINTLN("[SPTT_Racer_Vehicle_Create] The stunt plane still exists.")
IF NOT IS_ENTITY_DEAD(Racer.Vehicle)
OR NOT IS_VEHICLE_DRIVEABLE(Racer.Vehicle)
PRINTLN("[SPTT_Racer_Vehicle_Create] The stunt plane is not dead, but it's not driveable.")
IF IS_PED_IN_VEHICLE(Racer.Driver, Racer.Vehicle)
PRINTLN("[SPTT_Racer_Vehicle_Create] The player is inside the stunt plane. Clear his tasks and delete the stunt plane.")
CLEAR_PED_TASKS_IMMEDIATELY(Racer.Driver)
SET_ENTITY_AS_MISSION_ENTITY(Racer.Vehicle, TRUE, TRUE)
DELETE_VEHICLE(Racer.Vehicle)
ENDIF
ELSE
IF IS_PED_IN_VEHICLE(Racer.Driver, Racer.Vehicle)
PRINTLN("[SPTT_Racer_Vehicle_Create] Warping the player out of the stunt plane.")
SET_ENTITY_COORDS(Racer.Driver, Racer.vStartPos)
ENDIF
IF IS_PED_IN_VEHICLE(Racer.Driver, Racer.Vehicle)
PRINTLN("[SPTT_Racer_Vehicle_Create] The player is still in the stunt plane.")
ELSE
PRINTLN("[SPTT_Racer_Vehicle_Create] The player is no longer in the stunt plane. Delete the stunt plane.")
SET_ENTITY_AS_MISSION_ENTITY(Racer.Vehicle, TRUE, TRUE)
DELETE_VEHICLE(Racer.Vehicle)
ENDIF
ENDIF
ENDIF
ENDIF
// If vehicle already exists, set health to max and teleport it.
IF NOT IS_ENTITY_DEAD(Racer.Vehicle)
PRINTLN("[SPTT_Racer_Vehicle_Create] A vehicle exists, so fix it. We should never reach here if we're forced to create a new stunt plane.")
// Fix vehicle.
SET_ENTITY_HEALTH(Racer.Vehicle, 1000)
SET_VEHICLE_FIXED(Racer.Vehicle)
SET_VEHICLE_ENGINE_HEALTH(Racer.Vehicle, 1000.0)
// Position and orient the vehicle.
SET_ENTITY_COORDS(Racer.Vehicle, Racer.vStartPos)
SET_ENTITY_HEADING(Racer.Vehicle, Racer.fStartHead)
SET_VEHICLE_ON_GROUND_PROPERLY( Racer.Vehicle )
ELSE
PRINTLN("[SPTT_Racer_Vehicle_Create] A vehicle exists, so fix it.")
IF (Racer.eVehicleModel = DUMMY_MODEL_FOR_SCRIPT)
PRINTLN("[SPTT_Racer_Vehicle_Create] ERROR: Cannot create vehicle. No model name stored for vehicle. Returning TRUE.")
RETURN TRUE
ENDIF
// Create the stunt plane with strong axles.
Racer.Vehicle = CREATE_VEHICLE(STUNT, Racer.vStartPos, Racer.fStartHead)
IF IS_ENTITY_DEAD(Racer.Vehicle)
PRINTLN("[SPTT_Racer_Vehicle_Create] ERROR: Vehicle not created.")
RETURN FALSE
ENDIF
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
SET_VEHICLE_DOORS_LOCKED(GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID()), VEHICLELOCK_LOCKED_PLAYER_INSIDE)
ENDIF
SET_VEHICLE_HAS_STRONG_AXLES(Racer.Vehicle, TRUE)
SET_VEHICLE_SETUP( Racer.Vehicle, SPTT_Master.vssPlaneSetup )
ENDIF
ENABLE_STALL_WARNING_SOUNDS(Racer.Vehicle, FALSE)
SET_VEHICLE_ENGINE_ON(Racer.Vehicle, TRUE, TRUE)
SET_HELI_BLADES_FULL_SPEED(Racer.Vehicle)
// 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)
PRINTLN("[SPTT_Racer_Vehicle_Create] Setting the player inside the stunt plane.")
SET_PED_INTO_VEHICLE(Racer.Driver, Racer.Vehicle)
ENDIF
ENDIF
// Set vehicle as mission entity.
IF NOT IS_ENTITY_A_MISSION_ENTITY(Racer.Vehicle)
PRINTLN("[SPTT_Racer_Vehicle_Create] Set plane as mission entity.")
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)
PRINTLN("[SPTT_Racer_Vehicle_Create] Store the vehicle in the system, and set vehicle to maintain speed when in autopilot.")
SET_VEHICLE_LIMIT_SPEED_WHEN_PLAYER_INACTIVE(Racer.Vehicle, FALSE)
SPTT_Master.PlayerVeh = Racer.Vehicle
ENDIF
// Racer vehicle successfully created.
PRINTLN("[SPTT_Racer_Vehicle_Create] Stunt plane has been successfully created!")
RETURN TRUE
ENDFUNC
PROC SPTT_Racer_Vehicle_Freeze(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_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 SPTT_Racer_Vehicle_UnFreeze(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_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 SPTT_Racer_Vehicle_Release(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_Racer_Vehicle_Release")
IF DOES_ENTITY_EXIST(Racer.Vehicle)
// TODO: Decide if this player check is even needed...
IF (Racer.Vehicle <> SPTT_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 SPTT_Racer_Vehicle_Destroy(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_Racer_Vehicle_Destroy")
IF DOES_ENTITY_EXIST(Racer.Vehicle)
// TODO: Decide if this player check is even needed...
IF (Racer.Vehicle <> SPTT_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 SPTT_Racer_Request(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_Racer_Request")
SPTT_Racer_Driver_Request(Racer)
SPTT_Racer_Vehicle_Request(Racer)
ENDPROC
FUNC BOOL SPTT_Racer_Stream(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_Racer_Stream")
IF NOT SPTT_Racer_Driver_Stream(Racer)
OR NOT SPTT_Racer_Vehicle_Stream(Racer)
RETURN FALSE
ENDIF
RETURN TRUE
ENDFUNC
PROC SPTT_Racer_Evict(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_Racer_Evict")
SPTT_Racer_Driver_Evict(Racer)
SPTT_Racer_Vehicle_Evict(Racer)
ENDPROC
FUNC BOOL SPTT_Racer_Blip_Create(SPTT_RACER_STRUCT& Racer, BOOL bFriendly = TRUE, FLOAT fScale = 1.0)
//DEBUG_MESSAGE("SPTT_Racer_Blip_Create")
/* SAVE: Just bliping the driver now, since they will be in the vehicle that we are bliping.
IF IS_ENTITY_DEAD(Racer.Vehicle)
IF (Racer.Driver <> PLAYER_PED_ID())
IF NOT SPTT_Blip_Entity_Create(Racer.Blip, Racer.Driver, bFriendly, fScale, Racer.szName)
DEBUG_MESSAGE("SPTT_Racer_Blip_Create: Failed to create racer driver blip!")
RETURN FALSE
ENDIF
ENDIF
ELIF (Racer.Driver <> PLAYER_PED_ID())
OR NOT IS_PED_SITTING_IN_VEHICLE(Racer.Driver, Racer.Vehicle)
IF NOT SPTT_Blip_Entity_Create(Racer.Blip, Racer.Vehicle, bFriendly, fScale, Racer.szName)
DEBUG_MESSAGE("SPTT_Racer_Blip_Create: Failed to create racer vehicle blip!")
RETURN FALSE
ENDIF
ENDIF*/
IF (Racer.Driver <> PLAYER_PED_ID())
IF NOT SPTT_Blip_Entity_Create(Racer.Blip, Racer.Driver, bFriendly, fScale, Racer.szName)
DEBUG_MESSAGE("SPTT_Racer_Blip_Create: Failed to create racer driver blip!")
RETURN FALSE
ENDIF
ENDIF
RETURN TRUE
ENDFUNC
PROC SPTT_Racer_Blip_Destroy(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_Racer_Blip_Destroy")
SPTT_Blip_Destroy(Racer.Blip)
ENDPROC
FUNC BOOL SPTT_Racer_Create(SPTT_RACER_STRUCT& Racer, BOOL bForceCreate)
PRINTLN("[SPTT_Racer_Create] Function called.")
IF NOT SPTT_Racer_Vehicle_Create(Racer, bForceCreate)
OR NOT SPTT_Racer_Driver_Create(Racer)
PRINTLN("[SPTT_Racer_Create] Vehicle and driver both NOT yet created.")
RETURN FALSE
ENDIF
PRINTLN("[SPTT_Racer_Create] Vehicle and driver BOTH created.")
RETURN TRUE
ENDFUNC
PROC SPTT_Racer_Freeze(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_Racer_Freeze")
SPTT_Racer_Driver_Freeze(Racer)
SPTT_Racer_Vehicle_Freeze(Racer)
ENDPROC
PROC SPTT_Racer_UnFreeze(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_Racer_UnFreeze")
SPTT_Racer_Driver_UnFreeze(Racer)
SPTT_Racer_Vehicle_UnFreeze(Racer)
ENDPROC
PROC SPTT_Racer_Release(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_Racer_Release")
SPTT_Racer_Blip_Destroy(Racer)
SPTT_Racer_Driver_Release(Racer)
SPTT_Racer_Vehicle_Release(Racer)
ENDPROC
PROC SPTT_Racer_Destroy(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_Racer_Destroy")
SPTT_Racer_Blip_Destroy(Racer)
SPTT_Racer_Driver_Destroy(Racer)
SPTT_Racer_Vehicle_Destroy(Racer)
ENDPROC
// -----------------------------------
// HELPER PROCS/FUNCTIONS
// -----------------------------------
FUNC BOOL SPTT_IS_PLANE_KNIFING(ENTITY_INDEX entKnifing)
VECTOR vFront, vSide, vUp, vPos
IF IS_ENTITY_DEAD(entKnifing)
RETURN FALSE
ENDIF
GET_ENTITY_MATRIX(entKnifing, vFront, vSide, vUp, vPos)
IF ABSF(vUp.z) > STUNT_PLANE_KNIFE_THRESHOLD
RETURN FALSE
ENDIF
RETURN vSide.z <> 0.0
ENDFUNC
FUNC BOOL SPTT_IS_PLANE_KNIFING_LEFT(ENTITY_INDEX entKnifing)
VECTOR vFront, vSide, vUp, vPos
IF IS_ENTITY_DEAD(entKnifing)
RETURN FALSE
ENDIF
GET_ENTITY_MATRIX(entKnifing, vFront, vSide, vUp, vPos)
IF ABSF(vUp.z) > STUNT_PLANE_KNIFE_THRESHOLD
RETURN FALSE
ENDIF
RETURN vSide.z > 0.0
ENDFUNC
FUNC BOOL SPTT_IS_PLANE_KNIFING_RIGHT(ENTITY_INDEX entKnifing)
VECTOR vFront, vSide, vUp, vPos
IF IS_ENTITY_DEAD(entKnifing)
RETURN FALSE
ENDIF
GET_ENTITY_MATRIX(entKnifing, vFront, vSide, vUp, vPos)
IF ABSF(vUp.z) > STUNT_PLANE_KNIFE_THRESHOLD
RETURN FALSE
ENDIF
RETURN vSide.z < 0.0
ENDFUNC
PROC SPTT_Racer_Teleport(SPTT_RACER_STRUCT& Racer, VECTOR vPos, FLOAT fHeading, FLOAT fSpeed)
PRINTLN("[SPTT_Racer_Teleport] Procedure started.")
SET_ENTITY_COORDS(Racer.Vehicle, vPos)
SET_ENTITY_HEADING(Racer.Vehicle, fHeading)
SET_VEHICLE_FORWARD_SPEED(Racer.Vehicle, fSpeed)
ENDPROC
PROC SPTT_Racer_Start(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_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, SPTT_VEH_STUCK_MIN_MOVE, SPTT_VEH_STUCK_CHK_TIME)
//ENDIF
ENDIF
ENDPROC
PROC SPTT_Racer_Finish(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_Racer_Finish")
// Check if racer vehicle is alive.
IF NOT IS_ENTITY_DEAD(Racer.Vehicle)
IF SPTT_Master.eRaceType <> SPTT_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 SPTT_Racer_Stuck_Check(SPTT_RACER_STRUCT& Racer)
//DEBUG_MESSAGE("SPTT_Racer_Crash_Check")
IF (Racer.eVehicleModel <> DUMMY_MODEL_FOR_SCRIPT)
// 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, SPTT_VEH_STUCK_PLANE_ROOF_TIME)
DEBUG_MESSAGE("SPTT_Racer_Crash_Check: Vehicle stuck (roof)!")
RETURN TRUE
ENDIF
IF IS_VEHICLE_STUCK_TIMER_UP(Racer.Vehicle, VEH_STUCK_ON_SIDE, SPTT_VEH_STUCK_PLANE_SIDE_TIME)
DEBUG_MESSAGE("SPTT_Racer_Crash_Check: Vehicle stuck (side)!")
RETURN TRUE
ENDIF
IF IS_VEHICLE_STUCK_TIMER_UP(Racer.Vehicle, VEH_STUCK_HUNG_UP, SPTT_VEH_STUCK_PLANE_HUNG_TIME)
DEBUG_MESSAGE("SPTT_Racer_Crash_Check: Vehicle stuck (hung)!")
RETURN TRUE
ENDIF
IF IS_VEHICLE_STUCK_TIMER_UP(Racer.Vehicle, VEH_STUCK_JAMMED, SPTT_VEH_STUCK_PLANE_JAM_TIME)
DEBUG_MESSAGE("SPTT_Racer_Crash_Check: Vehicle stuck (jam)!")
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
//ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC SPTT_RACE_GATE_STATUS SPTT_Racer_Gate_Check(SPTT_RACE_STRUCT& Race, SPTT_GATE_STRUCT& GateCur, SPTT_GATE_STRUCT& GateNxt, PED_INDEX pedDriver)
VECTOR vFront, vSide, vUp, vPos
#IF NOT DEFINED(bAudioPlayedLastGate)
BOOL bAudioPlayedLastGate
bAudioPlayedLastGate = bAudioPlayedLastGate
#ENDIF
IF IS_ENTITY_DEAD(Race.Racer[0].Vehicle)
RETURN SPTT_RACE_GATE_STATUS_INVALID
ENDIF
SPTT_RACE_GATE_STATUS status = SPTT_Gate_Check_Pass(GateCur, pedDriver)
IF (SPTT_Master.eRaceType = SPTT_RACE_TYPE_PLANE)
IF (status <> SPTT_RACE_GATE_STATUS_INCOMPLETE)
// INNER (PERFECT) RING CHECKS
IF (status = SPTT_RACE_GATE_STATUS_PASS_INNER)
// left roll
IF SPTT_IS_PLANE_KNIFING_LEFT(Race.Racer[0].Vehicle)
// Check if its a stunt gate
IF GateCur.eStuntType <> SPTT_RACE_STUNT_GATE_NORMAL
// check if its a KNIFE LEFT stunt gate
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_SIDE_LEFT
// if left stunt gate, return successfull stunt
status = SPTT_RACE_GATE_STATUS_STUNT_KL
RETURN status
ENDIF
// check if its a KNIFE RIGHT stunt gate
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_SIDE_RIGHT
// if right stunt gate, return hit
status = SPTT_RACE_GATE_STATUS_STUNT_KR_HIT
RETURN status
ENDIF
// check if its a INVERTED stunt gate
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_INVERTED
// if inverted gate, return hit
status = SPTT_RACE_GATE_STATUS_STUNT_INV_HIT
RETURN status
ENDIF
ELSE
// check bits
status = SPTT_RACE_GATE_STATUS_KNIFE_INNER
// new perfect Knife
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, P_Knife_Once)
// play audio
Play_Gate_Audio("Gate_PK", iPerfKniGateBits)
// clear then set audio tracking bit (done on all first hits to reset consecutives for all audio bits)
iStuntGateHit = 0
SET_BITMASK_AS_ENUM(iStuntGateHit, P_Knife_Once)
RETURN status
ELSE
// hit 2 perfect Knife in a row
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, P_Knife_Twice)
// consecutive audio
Play_Gate_Audio("Con_PK", iPerfKniGateBits)
SET_BITMASK_AS_ENUM(iStuntGateHit, P_Knife_Twice)
RETURN status
ELSE
// hit 3 perfect Knife in a row
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, P_Knife_Third)
// consecutive audio
Play_Gate_Audio("Con_PK", iPerfKniGateBits)
SET_BITMASK_AS_ENUM(iStuntGateHit, P_Knife_Third)
RETURN status
ELSE // 4 or more perfect Knife in a row
// consecutive audio
Play_Gate_Audio("Con_PK", iPerfKniGateBits)
RETURN status
ENDIF
ENDIF
ENDIF
ENDIF
// right roll
ELIF SPTT_IS_PLANE_KNIFING_RIGHT(Race.Racer[0].Vehicle)
PRINTLN("SPTT_Racer_Gate_Check: plane is rolled right")
// Check if its a stunt gate
IF GateCur.eStuntType <> SPTT_RACE_STUNT_GATE_NORMAL
// check if its a KNIFE RIGHT stunt gate
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_SIDE_RIGHT
// if right stunt gate, return successfull stunt
status = SPTT_RACE_GATE_STATUS_STUNT_KR
RETURN status
ENDIF
// check if its a KNIFE LEFT stunt gate
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_SIDE_LEFT
// if left stunt gate, return hit
status = SPTT_RACE_GATE_STATUS_STUNT_KL_HIT
RETURN status
ENDIF
// check if its a INVERTED stunt gate
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_INVERTED
// if inverted gate, return hit
status = SPTT_RACE_GATE_STATUS_STUNT_INV_HIT
RETURN status
ENDIF
ELSE
status = SPTT_RACE_GATE_STATUS_KNIFE_INNER
// new perfect Knife
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, P_Knife_Once)
// play audio
Play_Gate_Audio("Gate_PK", iPerfKniGateBits)
// clear then set audio tracking bit (done on all first hits to reset consecutives for all audio bits)
iStuntGateHit = 0
SET_BITMASK_AS_ENUM(iStuntGateHit, P_Knife_Once)
RETURN status
ELSE
// hit 2 perfect Knife in a row
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, P_Knife_Twice)
// consecutive audio
Play_Gate_Audio("Con_PK", iPerfKniGateBits)
SET_BITMASK_AS_ENUM(iStuntGateHit, P_Knife_Twice)
RETURN status
ELSE
// hit 3 perfect Knife in a row
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, P_Knife_Third)
// consecutive audio
Play_Gate_Audio("Con_PK", iPerfKniGateBits)
SET_BITMASK_AS_ENUM(iStuntGateHit, P_Knife_Third)
RETURN status
ELSE
// 4 or more perfect Knife in a row
// consecutive audio
Play_Gate_Audio("Con_PK", iPerfKniGateBits)
RETURN status
ENDIF
ENDIF
ENDIF
ENDIF
// Inverted
ELSE
GET_ENTITY_MATRIX(Race.Racer[0].Vehicle, vFront, vSide, vUp, vPos)
IF vUp.z <= SPTT_STUNT_INVERT_THRESHOLD
// Check if its a stunt gate
IF GateCur.eStuntType <> SPTT_RACE_STUNT_GATE_NORMAL
// check if its a INVERTED stunt gate
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_INVERTED
// if inverted stunt gate, return successfull stunt
status = SPTT_RACE_GATE_STATUS_STUNT_INV
RETURN status
ENDIF
// check if its a KNIFE LEFT stunt gate
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_SIDE_LEFT
// if left stunt gate, return hit
status = SPTT_RACE_GATE_STATUS_STUNT_KL_HIT
RETURN status
ENDIF
// check if its a KNIFE RIGHT stunt gate
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_SIDE_RIGHT
// if right stunt gate, return hit
status = SPTT_RACE_GATE_STATUS_STUNT_KR_HIT
RETURN status
ENDIF
ELSE
// check bits
status = SPTT_RACE_GATE_STATUS_INVERT_INNER
// new perfect Inverted
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, P_Inverted_Once)
// play audio
Play_Gate_Audio("Gate_PI", iPerfInvGateBits)
// clear then set audio tracking bit (done on all first hits to reset consecutives for all audio bits)
iStuntGateHit = 0
SET_BITMASK_AS_ENUM(iStuntGateHit, P_Inverted_Once)
RETURN status
ELSE
// hit 2 perfect Inverted in a row
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, P_Inverted_Twice)
// consecutive audio
Play_Gate_Audio("Con_PI", iPerfInvGateBits)
SET_BITMASK_AS_ENUM(iStuntGateHit, P_Inverted_Twice)
RETURN status
ELSE
// hit 3 perfect Inverted in a row
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, P_Inverted_Third)
// consecutive audio
Play_Gate_Audio("Con_PI", iPerfInvGateBits)
SET_BITMASK_AS_ENUM(iStuntGateHit, P_Inverted_Third)
RETURN status
ELSE
// 4 or more perfect Inverted in a row
// consecutive audio
Play_Gate_Audio("Con_PI", iPerfInvGateBits)
RETURN status
ENDIF
ENDIF
ENDIF
ENDIF
ELSE // Flew through gate normally
// if its a stunt gate
IF GateCur.eStuntType <> SPTT_RACE_STUNT_GATE_NORMAL
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_SIDE_LEFT
status = SPTT_RACE_GATE_STATUS_STUNT_KL_HIT
RETURN status
ELIF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_SIDE_RIGHT
status = SPTT_RACE_GATE_STATUS_STUNT_KR_HIT
RETURN status
ELIF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_INVERTED
status = SPTT_RACE_GATE_STATUS_STUNT_INV_HIT
RETURN status
ENDIF
ELSE
status = SPTT_RACE_GATE_STATUS_PASS_INNER
// new perfect Knife
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, Perfect_Once)
// play audio
Play_Gate_Audio("Get_Perf", iPerfectGateBits)
// clear then set audio tracking bit (done on all first hits to reset consecutives for all audio bits)
iStuntGateHit = 0
SET_BITMASK_AS_ENUM(iStuntGateHit, Perfect_Once)
RETURN status
ELSE
// hit 2 perfect in a row
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, Perfect_Twice)
// consecutive audio
Play_Gate_Audio("Con_Perf", iPerfectGateBits)
SET_BITMASK_AS_ENUM(iStuntGateHit, Perfect_Twice)
RETURN status
ELSE
// hit 3 perfect in a row
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, Perfect_Third)
// consecutive audio
Play_Gate_Audio("Con_Perf", iPerfectGateBits)
SET_BITMASK_AS_ENUM(iStuntGateHit, Perfect_Third)
RETURN status
ELSE
// 4 or more perfect in a row
// consecutive audio
Play_Gate_Audio("Con_Perf", iPerfectGateBits)
RETURN status
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
// OUTTER RING CHECKS
IF (status = SPTT_RACE_GATE_STATUS_PASS_OUTTER)
// knife left check
IF SPTT_IS_PLANE_KNIFING_LEFT(Race.Racer[0].Vehicle)
// Check if its a stunt gate
IF GateCur.eStuntType <> SPTT_RACE_STUNT_GATE_NORMAL
// check if its a KNIFE LEFT stunt gate
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_SIDE_LEFT
// if left stunt gate, return successfull stunt
status = SPTT_RACE_GATE_STATUS_STUNT_KL
RETURN status
ENDIF
// check if its a KNIFE RIGHT stunt gate
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_SIDE_RIGHT
// if right stunt gate, return hit
status = SPTT_RACE_GATE_STATUS_STUNT_KR_HIT
RETURN status
ENDIF
// check if its a INVERTED stunt gate
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_INVERTED
// if inverted gate, return hit
status = SPTT_RACE_GATE_STATUS_STUNT_INV_HIT
RETURN status
ENDIF
ELSE
// check bits
status = SPTT_RACE_GATE_STATUS_KNIFE_OUTTER
// new knife
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, Knife_Once)
// play audio
Play_Gate_Audio("Gate_Kni", iKnifeGateBits)
// clear then set audio tracking bit (done on all first hits to reset consecutives for all audio bits)
iStuntGateHit = 0
SET_BITMASK_AS_ENUM(iStuntGateHit, Knife_Once)
RETURN status
ELSE
// hit 2 Knife in a row
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, Knife_Twice)
// consecutive audio
Play_Gate_Audio("Con_Kni", iKnifeGateBits)
SET_BITMASK_AS_ENUM(iStuntGateHit, Knife_Twice)
RETURN status
ELSE
// hit 3 Knife in a row
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, Knife_Third)
// consecutive audio
Play_Gate_Audio("Con_Kni", iKnifeGateBits)
SET_BITMASK_AS_ENUM(iStuntGateHit, Knife_Third)
RETURN status
ELSE
// 4 or more Knife in a row
// consecutive audio
Play_Gate_Audio("Con_Kni", iKnifeGateBits)
RETURN status
ENDIF
ENDIF
ENDIF
ENDIF
ELIF SPTT_IS_PLANE_KNIFING_RIGHT(Race.Racer[0].Vehicle)
// Check if its a stunt gate
IF GateCur.eStuntType <> SPTT_RACE_STUNT_GATE_NORMAL
// check if its a KNIFE RIGHT stunt gate
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_SIDE_RIGHT
// if right stunt gate, return successfull stunt
status = SPTT_RACE_GATE_STATUS_STUNT_KR
RETURN status
ENDIF
// check if its a KNIFE LEFT stunt gate
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_SIDE_LEFT
// if left stunt gate, return hit
status = SPTT_RACE_GATE_STATUS_STUNT_KL_HIT
RETURN status
ENDIF
// check if its a INVERTED stunt gate
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_INVERTED
// if inverted gate, return hit
status = SPTT_RACE_GATE_STATUS_STUNT_INV_HIT
RETURN status
ENDIF
ELSE
// check bits
status = SPTT_RACE_GATE_STATUS_KNIFE_OUTTER
// new knife
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, Knife_Once)
// play audio
Play_Gate_Audio("Gate_Kni", iKnifeGateBits)
// clear then set audio tracking bit (done on all first hits to reset consecutives for all audio bits)
iStuntGateHit = 0
SET_BITMASK_AS_ENUM(iStuntGateHit, Knife_Once)
RETURN status
ELSE
// hit 2 Knife in a row
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, Knife_Twice)
// consecutive audio
Play_Gate_Audio("Con_Kni", iKnifeGateBits)
SET_BITMASK_AS_ENUM(iStuntGateHit, Knife_Twice)
RETURN status
ELSE
// hit 3 Knife in a row
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, Knife_Third)
// consecutive audio
Play_Gate_Audio("Con_Kni", iKnifeGateBits)
SET_BITMASK_AS_ENUM(iStuntGateHit, Knife_Third)
RETURN status
ELSE
// 4 or more Knife in a row
// consecutive audio
Play_Gate_Audio("Con_Kni", iKnifeGateBits)
RETURN status
ENDIF
ENDIF
ENDIF
ENDIF
ELSE
GET_ENTITY_MATRIX(Race.Racer[0].Vehicle, vFront, vSide, vUp, vPos)
IF vUp.z <= SPTT_STUNT_INVERT_THRESHOLD
// Check if its a stunt gate
IF GateCur.eStuntType <> SPTT_RACE_STUNT_GATE_NORMAL
// check if its a INVERTED stunt gate
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_INVERTED
// if inverted gate, return successful stunt
status = SPTT_RACE_GATE_STATUS_STUNT_INV
RETURN status
ENDIF
// check if its a KNIFE LEFT stunt gate
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_SIDE_LEFT
// if left stunt gate, return successfull stunt
status = SPTT_RACE_GATE_STATUS_STUNT_KL
RETURN status
ENDIF
// check if its a KNIFE RIGHT stunt gate
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_SIDE_RIGHT
// if right stunt gate, return hit
status = SPTT_RACE_GATE_STATUS_STUNT_KR_HIT
RETURN status
ENDIF
ELSE
// check bits
status = SPTT_RACE_GATE_STATUS_INVERT_OUTTER
// new Inverted
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, Inverted_Once)
// play audio
Play_Gate_Audio("Gate_Inv", iInvertedGateBits)
// clear then set audio tracking bit (done on all first hits to reset consecutives for all audio bits)
iStuntGateHit = 0
SET_BITMASK_AS_ENUM(iStuntGateHit, Inverted_Once)
RETURN status
ELSE
// hit 2 Inverted in a row
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, Inverted_Twice)
// consecutive audio
Play_Gate_Audio("Con_Inv", iInvertedGateBits)
// set audio tracking bit
SET_BITMASK_AS_ENUM(iStuntGateHit, Inverted_Twice)
RETURN status
ELSE
// Hit 3 Inverted in a row
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, Inverted_Third)
// consecutive audio
Play_Gate_Audio("Con_Inv", iInvertedGateBits)
SET_BITMASK_AS_ENUM(iStuntGateHit, Inverted_Third)
RETURN status
ELSE
// hit 4 or more Inverted in a row
// consecutive audio
Play_Gate_Audio("Con_Inv", iInvertedGateBits)
RETURN status
ENDIF
ENDIF
ENDIF
ENDIF
ELSE
// if its a stunt gate
IF GateCur.eStuntType <> SPTT_RACE_STUNT_GATE_NORMAL
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_SIDE_LEFT
status = SPTT_RACE_GATE_STATUS_STUNT_KL_HIT
RETURN status
ELIF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_SIDE_RIGHT
status = SPTT_RACE_GATE_STATUS_STUNT_KR_HIT
RETURN status
ELIF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_INVERTED
status = SPTT_RACE_GATE_STATUS_STUNT_INV_HIT
RETURN status
ENDIF
ELSE
// check bits
status = SPTT_RACE_GATE_STATUS_PASS_OUTTER
// New Gate Hit
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, Hit_Once)
// play audio
Play_Gate_Audio("Gate_Hit", iClearedGateBits)
// clear then set audio tracking bit (done on all first hits to reset consecutives for all audio bits)
iStuntGateHit = 0
SET_BITMASK_AS_ENUM(iStuntGateHit, Hit_Once)
RETURN status
ELSE
// hit 2 Gates in a row
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, Hit_Twice)
// consecutive audio
Play_Gate_Audio("Con_Hit", iClearedGateBits)
SET_BITMASK_AS_ENUM(iStuntGateHit, Hit_Twice)
RETURN status
ELSE
// hit 3 Gates in a row
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, Hit_Third)
// consecutive audio
Play_Gate_Audio("Con_Hit", iClearedGateBits)
SET_BITMASK_AS_ENUM(iStuntGateHit, Hit_Third)
RETURN status
ELSE
// Hit 4 or more Gates in a row
// consecutive audio
Play_Gate_Audio("Con_Hit", iClearedGateBits)
RETURN status
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
IF SPTT_Gate_Check_Miss(GateCur, GateNxt, pedDriver)
// Check if its a stunt gate
IF GateCur.eStuntType <> SPTT_RACE_STUNT_GATE_NORMAL
// check if its a knife left stunt gate
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_SIDE_LEFT
// missed the knife left stunt gate
status = SPTT_RACE_GATE_STATUS_STUNT_KL_MISS
RETURN status
ENDIF
// check if its a knife right stunt gate
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_SIDE_RIGHT
// missed the knife right stunt gate
status = SPTT_RACE_GATE_STATUS_STUNT_KR_MISS
RETURN status
ENDIF
// check if its a INVERTED stunt gate
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_INVERTED
// if inverted stunt gate, return successfull stunt
status = SPTT_RACE_GATE_STATUS_STUNT_INV_MISS
RETURN status
ENDIF
ELSE
status = SPTT_RACE_GATE_STATUS_MISSED_GATE
// new Missed Gate
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, Missed_Once)
// play audio
Play_Gate_Audio("Gate_Miss", iMissedGateBits)
// clear then set audio tracking bit (done on all first hits to reset consecutives for all audio bits)
iStuntGateHit = 0
SET_BITMASK_AS_ENUM(iStuntGateHit, Missed_Once)
RETURN status
ELSE
// Missed 2 Gates in a row
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, Missed_Twice)
// consecutive audio
Play_Gate_Audio("Con_Miss", iMissedGateBits)
SET_BITMASK_AS_ENUM(iStuntGateHit, Missed_Twice)
RETURN status
ELSE
// Missed 3 Gates in a row
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, Missed_Third)
// consecutive audio
Play_Gate_Audio("Con_Miss", iMissedGateBits)
SET_BITMASK_AS_ENUM(iStuntGateHit, Missed_Third)
RETURN status
ELSE
// Missed 4 or more Gates in a row
// consecutive audio
Play_Gate_Audio("Con_Miss", iMissedGateBits)
RETURN status
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
RETURN status
ENDIF
IF SPTT_Gate_Check_Miss(GateCur, GateNxt, pedDriver)
// Check if its a stunt gate
IF GateCur.eStuntType <> SPTT_RACE_STUNT_GATE_NORMAL
// check if its a knife left stunt gate
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_SIDE_LEFT
// missed the knife left stunt gate
status = SPTT_RACE_GATE_STATUS_STUNT_KL_MISS
RETURN status
ENDIF
// check if its a knife right stunt gate
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_SIDE_RIGHT
// missed the knife right stunt gate
status = SPTT_RACE_GATE_STATUS_STUNT_KR_MISS
RETURN status
ENDIF
// check if its a INVERTED stunt gate
IF GateCur.eStuntType = SPTT_RACE_STUNT_GATE_INVERTED
// if inverted stunt gate, return successfull stunt
status = SPTT_RACE_GATE_STATUS_STUNT_INV_MISS
RETURN status
ENDIF
ELSE
// check bits
status = SPTT_RACE_GATE_STATUS_MISSED_GATE
// new Missed Gate
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, Missed_Once)
// play audio
Play_Gate_Audio("Gate_Miss", iMissedGateBits)
// clear then set audio tracking bit (done on all first hits to reset consecutives for all audio bits)
iStuntGateHit = 0
SET_BITMASK_AS_ENUM(iStuntGateHit, Missed_Once)
RETURN status
ELSE
// Missed 2 Gates in a row
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, Missed_Twice)
// consecutive audio
Play_Gate_Audio("Con_Miss", iMissedGateBits)
SET_BITMASK_AS_ENUM(iStuntGateHit, Missed_Twice)
RETURN status
ELSE
// Missed 3 Gates in a row
IF NOT IS_BITMASK_AS_ENUM_SET(iStuntGateHit, Missed_Third)
// consecutive audio
Play_Gate_Audio("Con_Miss", iMissedGateBits)
SET_BITMASK_AS_ENUM(iStuntGateHit, Missed_Third)
RETURN status
ELSE
// Missed 4 or more Gates in a row
// consecutive audio
Play_Gate_Audio("Con_Miss", iMissedGateBits)
RETURN status
ENDIF
ENDIF
ENDIF
ENDIF
RETURN status
ENDIF
ELIF status != SPTT_RACE_GATE_STATUS_INCOMPLETE
RETURN status
ENDIF
RETURN SPTT_RACE_GATE_STATUS_INVALID
ENDFUNC
// END OF FILE! DO NOT ADD ANYTHING BELOW THIS LINE!