Files
gtav-src/script/dev_ng/singleplayer/include/public/jb700_gadgets.sch
T
2025-09-29 00:52:08 +02:00

657 lines
24 KiB
XML
Executable File

//Header for controlling various gadgets available when driving the JB700 car.
USING "rage_builtins.sch"
USING "globals.sch"
USING "commands_audio.sch"
USING "commands_camera.sch"
USING "commands_debug.sch"
USING "commands_fire.sch"
USING "commands_graphics.sch"
USING "commands_hud.sch"
USING "commands_misc.sch"
USING "commands_object.sch"
USING "commands_pad.sch"
USING "commands_ped.sch"
USING "commands_physics.sch"
USING "commands_player.sch"
USING "commands_script.sch"
USING "commands_streaming.sch"
USING "commands_task.sch"
USING "commands_vehicle.sch"
USING "model_enums.sch"
USING "script_player.sch"
USING "script_misc.sch"
USING "vehicle_public.sch"
USING "cellphone_public.sch"
STRUCT SPIKE_DATA
OBJECT_INDEX ObjectIndex
VEHICLE_INDEX VehicleIndex
INT iForceTimer
INT iCutsceneTimer
VECTOR vPosition
BOOL bPtfxPlayed
ENDSTRUCT
STRUCT JB700_GADGET_DATA
//guns
INT iGunTimer
INT iGunOffsetModifier
INT iGunSoundID
INT iGunOffSoundID
BOOL bGunsFired
CONTROL_ACTION caGunInput // Input for firing guns - we use INPUT_VEH_ATTACK on PC instead of INPUT_VEH_AIM as that maps to left mouse.
//spikes
SPIKE_DATA SpikesData[3]
INT iSpikesTimer
INT iTimesSpikesDropped
BOOL bCutsceneTriggered
BOOL bSpikesDropped
BOOL bSpikesHit
INT iSpikeHit
INT iSpikesHits
ENDSTRUCT
PROC RESET_JB700_GADGET_DATA(JB700_GADGET_DATA &sGadgetData)
sGadgetData.iGunTimer = 0
sGadgetData.iGunSoundID = -1
sGadgetData.iGunOffSoundID = -1
sGadgetData.iGunOffsetModifier = 1
sGadgetData.bGunsFired = FALSE
sGadgetData.caGunInput = INPUT_VEH_AIM
sGadgetData.iSpikeHit = 0
sGadgetData.iSpikesHits = 0
sGadgetData.iSpikesTimer = 0
sGadgetData.iTimesSpikesDropped = 0
sGadgetData.bCutsceneTriggered = FALSE
sGadgetData.bSpikesDropped = FALSE
sGadgetData.bSpikesHit = FALSE
ENDPROC
PROC RESET_JB700_SPIKES_CUTSCENE_TIMERS(JB700_GADGET_DATA &sGadgetData)
sGadgetData.SpikesData[0].iCutsceneTimer = 0
sGadgetData.SpikesData[1].iCutsceneTimer = 0
sGadgetData.SpikesData[2].iCutsceneTimer = 0
ENDPROC
PROC CLEANUP_JB700_SPIKES(JB700_GADGET_DATA &sGadgetData, BOOL bDelete)
INT i
REPEAT COUNT_OF(sGadgetData.SpikesData) i
IF DOES_ENTITY_EXIST(sGadgetData.SpikesData[i].ObjectIndex)
IF ( bDelete = TRUE )
DELETE_OBJECT(sGadgetData.SpikesData[i].ObjectIndex)
ELSE
SET_OBJECT_AS_NO_LONGER_NEEDED(sGadgetData.SpikesData[i].ObjectIndex)
ENDIF
ENDIF
ENDREPEAT
ENDPROC
PROC CLEANUP_JB700_GUNS(JB700_GADGET_DATA &sGadgetData)
REMOVE_PTFX_ASSET()
IF sGadgetData.iGunSoundID != -1
IF NOT HAS_SOUND_FINISHED(sGadgetData.iGunSoundID)
STOP_SOUND(sGadgetData.iGunSoundID)
ENDIF
RELEASE_SOUND_ID(sGadgetData.iGunSoundID)
sGadgetData.iGunSoundID = -1
ENDIF
IF sGadgetData.iGunOffSoundID != -1
IF NOT HAS_SOUND_FINISHED(sGadgetData.iGunOffSoundID)
STOP_SOUND(sGadgetData.iGunOffSoundID)
ENDIF
RELEASE_SOUND_ID(sGadgetData.iGunOffSoundID)
sGadgetData.iGunOffSoundID = -1
ENDIF
ENDPROC
PROC UPDATE_JB700_GUNS(VEHICLE_INDEX veh, JB700_GADGET_DATA &sGadgetData)
REQUEST_PTFX_ASSET()
IF IS_USING_KEYBOARD_AND_MOUSE(PLAYER_CONTROL)
sGadgetData.caGunInput = INPUT_VEH_ATTACK
ELSE
sGadgetData.caGunInput = INPUT_VEH_AIM
ENDIF
IF sGadgetData.iGunSoundID = -1
sGadgetData.iGunSoundID = GET_SOUND_ID()
ENDIF
IF sGadgetData.iGunOffSoundID = -1
sGadgetData.iGunOffSoundID = GET_SOUND_ID()
ENDIF
IF IS_VEHICLE_DRIVEABLE(veh)
AND IS_PED_SITTING_IN_VEHICLE(PLAYER_PED_ID(), veh)
AND HAS_PTFX_ASSET_LOADED()
//DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_VEH_ATTACK)
//DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_VEH_ATTACK2)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_VEH_AIM)
IF IS_USING_KEYBOARD_AND_MOUSE(PLAYER_CONTROL)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_VEH_ATTACK)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_AIM) // This is a kludge because PC is using this for vehicle aim.
ENDIF
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_VEH_SELECT_NEXT_WEAPON)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_VEH_SELECT_PREV_WEAPON)
//IF IS_DISABLED_CONTROL_PRESSED(PLAYER_CONTROL, INPUT_VEH_ATTACK)
IF IS_DISABLED_CONTROL_PRESSED(PLAYER_CONTROL, sGadgetData.caGunInput)
IF GET_GAME_TIMER() - sGadgetData.iGunTimer > 225
IF sGadgetData.iGunOffsetModifier = 0
sGadgetData.iGunOffsetModifier = 1
ENDIF
VECTOR vStartOffset = <<0.6826 * sGadgetData.iGunOffsetModifier, 1.6707, 0.3711>>
VECTOR vEndOffset = <<vStartOffset.x, vStartOffset.y + 10.0, vStartOffset.z - 0.1>>
VECTOR vStartPos = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(veh, vStartOffset)
VECTOR vEndPos = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(veh, vEndOffset)
SHOOT_SINGLE_BULLET_BETWEEN_COORDS(vStartPos, vEndPos, 15, TRUE, WEAPONTYPE_ASSAULTSHOTGUN, PLAYER_PED_ID())
SHOOT_SINGLE_BULLET_BETWEEN_COORDS(vStartPos + <<0.0, 0.0, 0.2>>, vEndPos + <<0.0, 0.0, 0.2>>, 15, TRUE, WEAPONTYPE_ASSAULTSHOTGUN, PLAYER_PED_ID())
START_PARTICLE_FX_NON_LOOPED_ON_ENTITY("scr_carsteal5_car_muzzle_flash", veh, vStartOffset + <<0.0, -0.15, 0.0>>, <<0.0, 0.0, 90.0>>)
IF IS_PLAYER_PLAYING(PLAYER_ID())
IF IS_SPECIAL_ABILITY_ACTIVE(PLAYER_ID())
PLAY_SOUND_FROM_COORD(-1, "FRANKLIN_GUN_MASTER", vStartPos) //play this during special ability mode, see B*1490847
ELSE
PLAY_SOUND_FROM_COORD(-1, "JB700_GUN_PLAYER_MASTER", vStartPos) //play this when special ability mode is off
ENDIF
ENDIF
sGadgetData.bGunsFired = TRUE
sGadgetData.iGunOffsetModifier *= -1
sGadgetData.iGunTimer = GET_GAME_TIMER()
ENDIF
/*IF HAS_SOUND_FINISHED(sGadgetData.iGunSoundID)
PLAY_SOUND_FROM_ENTITY(sGadgetData.iGunSoundID, "JB700_GUN_PLAYER_MASTER", veh)
ENDIF
IF NOT HAS_SOUND_FINISHED(sGadgetData.iGunOffSoundID)
STOP_SOUND(sGadgetData.iGunOffSoundID)
ENDIF*/
ELSE
/*IF NOT HAS_SOUND_FINISHED(sGadgetData.iGunSoundID)
STOP_SOUND(sGadgetData.iGunSoundID)
ENDIF
IF HAS_SOUND_FINISHED(sGadgetData.iGunOffSoundID)
PLAY_SOUND_FROM_ENTITY(sGadgetData.iGunOffSoundID, "JB700_GUN_PLAYER_RPRT_MASTER", veh)
ENDIF*/
ENDIF
ELSE
IF NOT HAS_SOUND_FINISHED(sGadgetData.iGunSoundID)
STOP_SOUND(sGadgetData.iGunSoundID)
ENDIF
IF NOT HAS_SOUND_FINISHED(sGadgetData.iGunOffSoundID)
STOP_SOUND(sGadgetData.iGunOffSoundID)
ENDIF
ENDIF
ENDPROC
FUNC BOOL HAS_ENTITY_BEEN_DAMAGED_BY_JB700_GUNS(ENTITY_INDEX entity, VEHICLE_INDEX vehJB700)
IF NOT IS_ENTITY_DEAD(entity)
AND NOT IS_ENTITY_DEAD(vehJB700)
IF HAS_ENTITY_BEEN_DAMAGED_BY_WEAPON(entity, WEAPONTYPE_PUMPSHOTGUN)
AND IS_PED_SITTING_IN_VEHICLE(PLAYER_PED_ID(), vehJB700)
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC INT FIND_FREE_SPIKE_SLOT_NEW(JB700_GADGET_DATA sGadgetData)
INT i = 0
REPEAT COUNT_OF(sGadgetData.SpikesData) i
IF sGadgetData.SpikesData[i].vPosition.x = 0.0
RETURN i
ENDIF
ENDREPEAT
RETURN -1
ENDFUNC
/// PURPOSE:
/// Converts a rotation vector into a normalised direction vector (basically the direction an entity would move if it moved forward at the current rotation).
/// For example: a rotation of <<0.0, 0.0, 45.0>> gives a direction vector of <<-0.707107, 0.707107, 0.0>>
/// PARAMS:
/// vRot - The rotation vector
/// RETURNS:
/// The direction vector
FUNC VECTOR CONVERT_ROTATION_TO_DIRECTION_VECTOR_NEW(VECTOR vRot)
RETURN <<-SIN(vRot.z) * COS(vRot.x), COS(vRot.z) * COS(vRot.x), SIN(vRot.x)>>
ENDFUNC
PROC UPDATE_JB700_SPIKES_DROPPING(VEHICLE_INDEX VehicleIndex, JB700_GADGET_DATA &sGadgetData)
ALLOW_ALTERNATIVE_SCRIPT_CONTROLS_LAYOUT(PLAYER_CONTROL)
IF DOES_ENTITY_EXIST(VehicleIndex)
IF NOT IS_ENTITY_DEAD(VehicleIndex)
INT i
IF IS_PED_SITTING_IN_VEHICLE(PLAYER_PED_ID(), VehicleIndex)
IF NOT IS_CINEMATIC_SHOT_ACTIVE(SHOTTYPE_CAMERA_MAN)
AND NOT IS_STUNT_JUMP_IN_PROGRESS()
REQUEST_PTFX_ASSET()
REQUEST_AMBIENT_AUDIO_BANK("CAR_THEFT_MOVIE_LOT")
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_VEH_HORN)
IF IS_CONTROL_JUST_PRESSED(PLAYER_CONTROL, INPUT_SCRIPT_LS)
AND IS_ENTITY_UPRIGHT(VehicleIndex)
IF GET_GAME_TIMER() - sGadgetData.iSpikesTimer > 1600
BOOL bNewSpikesAllowed = TRUE
IF DOES_ENTITY_EXIST(sGadgetData.SpikesData[0].ObjectIndex)
IF VDIST2(GET_ENTITY_COORDS(VehicleIndex), GET_ENTITY_COORDS(sGadgetData.SpikesData[0].ObjectIndex)) < 9.0
bNewSpikesAllowed = FALSE
ENDIF
ENDIF
IF ( bNewSpikesAllowed = TRUE )
INT iSlot = FIND_FREE_SPIKE_SLOT_NEW(sGadgetData)
//For the moment just go back to the start of the array once all the slots are full.
//This could be improved to find the furthest away spike and overwrite that one.
IF iSlot = -1
iSlot = 0
ENDIF
sGadgetData.SpikesData[iSlot].vPosition = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(VehicleIndex, <<0.0, -2.0, 0.0>>)
sGadgetData.SpikesData[iSlot].iForceTimer = 0
sGadgetData.SpikesData[iSlot].iCutsceneTimer = 0
sGadgetData.SpikesData[iSlot].VehicleIndex = NULL
sGadgetData.iSpikeHit = 0
sGadgetData.bSpikesHit = FALSE
REPEAT COUNT_OF(sGadgetData.SpikesData) i
IF DOES_ENTITY_EXIST(sGadgetData.SpikesData[i].ObjectIndex)
IF NOT IS_ENTITY_ON_SCREEN(sGadgetData.SpikesData[i].ObjectIndex)
DELETE_OBJECT(sGadgetData.SpikesData[i].ObjectIndex)
ELSE
SET_OBJECT_AS_NO_LONGER_NEEDED(sGadgetData.SpikesData[i].ObjectIndex)
ENDIF
ENDIF
ENDREPEAT
sGadgetData.iSpikesTimer = GET_GAME_TIMER()
ENDIF
ENDIF
ENDIF
IF ( sGadgetData.iSpikesTimer != 0 ) //create new spikes
IF GET_GAME_TIMER() - sGadgetData.iSpikesTimer > 100
IF NOT DOES_ENTITY_EXIST(sGadgetData.SpikesData[0].ObjectIndex)
sGadgetData.SpikesData[0].ObjectIndex = CREATE_OBJECT(PROP_TYRE_SPIKE_01, GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(VehicleIndex, << 0.0, -1.2, -0.4 >>))
SET_ENTITY_NO_COLLISION_ENTITY(sGadgetData.SpikesData[0].ObjectIndex, VehicleIndex, TRUE)
PLAY_SOUND_FROM_ENTITY(-1, "CAR_THEFT_MOVIE_LOT_DROP_SPIKES" , VehicleIndex)
ENDIF
ENDIF
IF GET_GAME_TIMER() - sGadgetData.iSpikesTimer > 200
IF NOT DOES_ENTITY_EXIST(sGadgetData.SpikesData[1].ObjectIndex)
sGadgetData.SpikesData[1].ObjectIndex = CREATE_OBJECT(PROP_TYRE_SPIKE_01, GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(VehicleIndex, << 0.1, -1.1, -0.4 >>))
SET_ENTITY_NO_COLLISION_ENTITY(sGadgetData.SpikesData[1].ObjectIndex, VehicleIndex, TRUE)
ENDIF
ENDIF
IF GET_GAME_TIMER() - sGadgetData.iSpikesTimer > 300
IF NOT DOES_ENTITY_EXIST(sGadgetData.SpikesData[2].ObjectIndex)
sGadgetData.SpikesData[2].ObjectIndex = CREATE_OBJECT(PROP_TYRE_SPIKE_01, GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(VehicleIndex, << -0.1, -1.0, -0.34 >>))
SET_ENTITY_NO_COLLISION_ENTITY(sGadgetData.SpikesData[2].ObjectIndex, VehicleIndex, TRUE)
sGadgetData.iSpikesTimer = 0
ENDIF
sGadgetData.iTimesSpikesDropped++
#IF IS_DEBUG_BUILD
PRINTLN(GET_THIS_SCRIPT_NAME(), ": Number of JB700 spike drops: ", sGadgetData.iTimesSpikesDropped, ".")
#ENDIF
sGadgetData.bSpikesDropped = TRUE
sGadgetData.iSpikeHit = 0
sGadgetData.bSpikesHit = FALSE
sGadgetData.SpikesData[0].bPtfxPlayed = FALSE
sGadgetData.SpikesData[1].bPtfxPlayed = FALSE
sGadgetData.SpikesData[2].bPtfxPlayed = FALSE
ENDIF
ENDIF
//play spikes sparks effect
IF HAS_PTFX_ASSET_LOADED()
IF ( sGadgetData.SpikesData[0].bPtfxPlayed = FALSE )
IF DOES_ENTITY_EXIST(sGadgetData.SpikesData[0].ObjectIndex)
IF NOT IS_ENTITY_IN_AIR(sGadgetData.SpikesData[0].ObjectIndex)
START_PARTICLE_FX_NON_LOOPED_ON_ENTITY("scr_carsteal3_tyre_spikes", sGadgetData.SpikesData[0].ObjectIndex, << 0.0, 0.0, -0.025 >>, << 0.0, 0.0, 0.0 >>)
sGadgetData.SpikesData[0].bPtfxPlayed = TRUE
ENDIF
ENDIF
ENDIF
IF ( sGadgetData.SpikesData[1].bPtfxPlayed = FALSE )
IF DOES_ENTITY_EXIST(sGadgetData.SpikesData[1].ObjectIndex)
IF NOT IS_ENTITY_IN_AIR(sGadgetData.SpikesData[1].ObjectIndex)
START_PARTICLE_FX_NON_LOOPED_ON_ENTITY("scr_carsteal3_tyre_spikes", sGadgetData.SpikesData[1].ObjectIndex, << 0.0, 0.0, -0.025 >>, << 0.0, 0.0, 0.0 >>)
sGadgetData.SpikesData[1].bPtfxPlayed = TRUE
ENDIF
ENDIF
ENDIF
IF ( sGadgetData.SpikesData[2].bPtfxPlayed = FALSE )
IF DOES_ENTITY_EXIST(sGadgetData.SpikesData[2].ObjectIndex)
IF NOT IS_ENTITY_IN_AIR(sGadgetData.SpikesData[2].ObjectIndex)
START_PARTICLE_FX_NON_LOOPED_ON_ENTITY("scr_carsteal3_tyre_spikes", sGadgetData.SpikesData[2].ObjectIndex, << 0.0, 0.0, -0.025 >>, << 0.0, 0.0, 0.0 >>)
sGadgetData.SpikesData[2].bPtfxPlayed = TRUE
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
REPEAT COUNT_OF(sGadgetData.SpikesData) i //activate physiscs on the spikes
IF DOES_ENTITY_EXIST(sGadgetData.SpikesData[i].ObjectIndex)
IF NOT IS_ENTITY_DEAD(sGadgetData.SpikesData[i].ObjectIndex)
IF DOES_ENTITY_HAVE_PHYSICS(sGadgetData.SpikesData[i].ObjectIndex)
ACTIVATE_PHYSICS(sGadgetData.SpikesData[i].ObjectIndex)
SET_ENTITY_DYNAMIC(sGadgetData.SpikesData[i].ObjectIndex, TRUE)
ENDIF
ENDIF
ENDIF
ENDREPEAT
ENDIF
ENDIF
ENDPROC
PROC UPDATE_JB700_SPIKES_CUTSCENE(VEHICLE_INDEX VehicleIndex, JB700_GADGET_DATA &sGadgetData, STRING AudioSceneName = NULL)
IF DOES_ENTITY_EXIST(VehicleIndex)
IF NOT IS_ENTITY_DEAD(VehicleIndex)
IF ( sGadgetData.bSpikesHit = TRUE )
IF ( sGadgetData.SpikesData[sGadgetData.iSpikeHit].iCutsceneTimer != 0 )
IF ( sGadgetData.bCutsceneTriggered = FALSE )
AND NOT IS_CINEMATIC_SHOT_ACTIVE(SHOTTYPE_CAMERA_MAN)
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), VehicleIndex)
AND NOT IS_STUNT_JUMP_IN_PROGRESS()
IF IS_VEHICLE_DRIVEABLE(sGadgetData.SpikesData[sGadgetData.iSpikeHit].VehicleIndex)
CLEAR_HELP()
DISPLAY_HUD(FALSE)
DISPLAY_RADAR(FALSE)
DISABLE_CELLPHONE(TRUE)
HANG_UP_AND_PUT_AWAY_PHONE(TRUE)
SPECIAL_ABILITY_DEACTIVATE(PLAYER_ID())
ENABLE_SPECIAL_ABILITY(PLAYER_ID(), FALSE)
CREATE_CINEMATIC_SHOT(SHOTTYPE_CAMERA_MAN, 2000, NULL, sGadgetData.SpikesData[sGadgetData.iSpikeHit].VehicleIndex)
IF NOT IS_STRING_NULL_OR_EMPTY(AudioSceneName)
START_AUDIO_SCENE(AudioSceneName)
ENDIF
sGadgetData.bCutsceneTriggered = TRUE
#IF IS_DEBUG_BUILD
PRINTLN(GET_THIS_SCRIPT_NAME(), ": Starting cinematic shot spikes cutscene.")
#ENDIF
ENDIF
ELSE
RESET_JB700_SPIKES_CUTSCENE_TIMERS(sGadgetData)
ENDIF
ELSE
IF ( GET_GAME_TIMER() - sGadgetData.SpikesData[sGadgetData.iSpikeHit].iCutsceneTimer > 1500 )
OR NOT IS_VEHICLE_DRIVEABLE(sGadgetData.SpikesData[sGadgetData.iSpikeHit].VehicleIndex)
STOP_CINEMATIC_SHOT(SHOTTYPE_CAMERA_MAN)
REMOVE_PTFX_ASSET()
DISPLAY_HUD(TRUE)
DISPLAY_RADAR(TRUE)
DISABLE_CELLPHONE(FALSE)
IF NOT IS_STRING_NULL_OR_EMPTY(AudioSceneName)
STOP_AUDIO_SCENE(AudioSceneName)
ENDIF
ENABLE_SPECIAL_ABILITY(PLAYER_ID(), TRUE)
sGadgetData.SpikesData[sGadgetData.iSpikeHit].vPosition = << 0.0, 0.0, 0.0 >>
RESET_JB700_SPIKES_CUTSCENE_TIMERS(sGadgetData)
#IF IS_DEBUG_BUILD
PRINTLN(GET_THIS_SCRIPT_NAME(), ": Ending cinematic shot spikes cutscene.")
#ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDPROC
PROC UPDATE_JB700_SPIKES_COLLISIONS(VEHICLE_INDEX VehicleIndex, JB700_GADGET_DATA &sGadgetData, BOOL bGiveWantedLevel = TRUE)
IF DOES_ENTITY_EXIST(VehicleIndex)
IF NOT IS_ENTITY_DEAD(VehicleIndex)
INT i
VECTOR vSpikeOffsets[3]
FLOAT fSpikeGroundZ[3]
REPEAT COUNT_OF(sGadgetData.SpikesData) i
IF ( sGadgetData.SpikesData[i].vPosition.x != 0.0 )
IF ( sGadgetData.bSpikesHit = FALSE )
IF ( VDIST2(sGadgetData.SpikesData[i].vPosition, GET_ENTITY_COORDS(VehicleIndex)) > 2500.0 )
sGadgetData.SpikesData[i].vPosition = << 0.0, 0.0, 0.0 >>
ELSE
sGadgetData.SpikesData[i].VehicleIndex = GET_CLOSEST_VEHICLE(sGadgetData.SpikesData[i].vPosition, 20.0,
DUMMY_MODEL_FOR_SCRIPT,
VEHICLE_SEARCH_FLAG_ALLOW_VEHICLE_OCCUPANTS_TO_BE_PERFORMING_A_NON_DEFAULT_TASK |
VEHICLE_SEARCH_FLAG_ALLOW_VEHICLE_OCCUPANTS_TO_BE_PERFORMING_A_SCRIPTED_TASK |
VEHICLE_SEARCH_FLAG_ALLOW_LAW_ENFORCER_VEHICLES_WITH_WANTED_LEVEL |
VEHICLE_SEARCH_FLAG_RETURN_LAW_ENFORCER_VEHICLES |
VEHICLE_SEARCH_FLAG_RETURN_MISSION_VEHICLES |
VEHICLE_SEARCH_FLAG_RETURN_RANDOM_VEHICLES)
IF DOES_ENTITY_EXIST(sGadgetData.SpikesData[i].VehicleIndex) //if clostest vehicle is found
AND sGadgetData.SpikesData[i].VehicleIndex != VehicleIndex //and it's not vehicle that dropped spikes
REQUEST_PTFX_ASSET()
IF IS_VEHICLE_DRIVEABLE(sGadgetData.SpikesData[i].VehicleIndex)
AND NOT IS_THIS_MODEL_A_BIKE(GET_ENTITY_MODEL(sGadgetData.SpikesData[i].VehicleIndex))
VECTOR vVehiclePosition = GET_ENTITY_COORDS(sGadgetData.SpikesData[i].VehicleIndex)
#IF IS_DEBUG_BUILD
DRAW_DEBUG_LINE(sGadgetData.SpikesData[i].vPosition, vVehiclePosition, 255, 255, 255)
DRAW_DEBUG_TEXT(GET_STRING_FROM_INT(i),vVehiclePosition, 255, 255, 255)
#ENDIF
IF ( VDIST2(vVehiclePosition, sGadgetData.SpikesData[i].vPosition) < 100.0 ) //check distance from vehicle to spike
AND ( ABSF(vVehiclePosition.z - sGadgetData.SpikesData[i].vPosition.z) < 1.25 ) //check Z difference from vehicle to spike
AND ( GET_ENTITY_SPEED(sGadgetData.SpikesData[i].VehicleIndex) > 10.0 ) //check vehicle speed
AND IS_ENTITY_UPRIGHT(sGadgetData.SpikesData[i].VehicleIndex) //check if vehicle is not flipped
AND IS_VEHICLE_ON_ALL_WHEELS(sGadgetData.SpikesData[i].VehicleIndex) //check if vehicle is not off the ground
AND DOES_ENTITY_EXIST(sGadgetData.SpikesData[0].ObjectIndex)
AND DOES_ENTITY_EXIST(sGadgetData.SpikesData[1].ObjectIndex)
AND DOES_ENTITY_EXIST(sGadgetData.SpikesData[2].ObjectIndex)
VECTOR vVehicleDirection = CONVERT_ROTATION_TO_DIRECTION_VECTOR_NEW(<<0.0, 0.0, GET_ENTITY_HEADING(sGadgetData.SpikesData[i].VehicleIndex)>>)
VECTOR vDirectionToSpike = sGadgetData.SpikesData[i].vPosition - vVehiclePosition
vDirectionToSpike = vDirectionToSpike / VMAG(vDirectionToSpike)
IF DOT_PRODUCT(vVehicleDirection, vDirectionToSpike) > 0.0
sGadgetData.iSpikesHits++
sGadgetData.bSpikesHit = TRUE
sGadgetData.iSpikeHit = i
sGadgetData.bCutsceneTriggered = FALSE
sGadgetData.SpikesData[i].iCutsceneTimer = GET_GAME_TIMER()
#IF IS_DEBUG_BUILD
PRINTLN(GET_THIS_SCRIPT_NAME(), ": Number of JB700 spike hits: ", sGadgetData.iSpikesHits, ".")
#ENDIF
//Reposition the spikes so they always hit if the cutscene triggers
vSpikeOffsets[0] = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(sGadgetData.SpikesData[i].VehicleIndex, <<0.8, 6.0, 0.0>>)
vSpikeOffsets[1] = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(sGadgetData.SpikesData[i].VehicleIndex, <<0.5, 7.0, 0.0>>)
vSpikeOffsets[2] = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(sGadgetData.SpikesData[i].VehicleIndex, <<0.9, 8.0, 0.0>>)
GET_GROUND_Z_FOR_3D_COORD(vSpikeOffsets[0], fSpikeGroundZ[0])
GET_GROUND_Z_FOR_3D_COORD(vSpikeOffsets[1], fSpikeGroundZ[1])
GET_GROUND_Z_FOR_3D_COORD(vSpikeOffsets[2], fSpikeGroundZ[2])
SET_ENTITY_COORDS(sGadgetData.SpikesData[0].ObjectIndex, <<vSpikeOffsets[0].x, vSpikeOffsets[0].y, fSpikeGroundZ[0] + 0.05>>)
SET_ENTITY_COORDS(sGadgetData.SpikesData[1].ObjectIndex, <<vSpikeOffsets[1].x, vSpikeOffsets[1].y, fSpikeGroundZ[1] + 0.05>>)
SET_ENTITY_COORDS(sGadgetData.SpikesData[2].ObjectIndex, <<vSpikeOffsets[2].x, vSpikeOffsets[2].y, fSpikeGroundZ[2] + 0.05>>)
sGadgetData.SpikesData[i].vPosition = vSpikeOffsets[1]
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ELSE //once the vehicle hits the spike, make it spin and burst tyres
IF IS_VEHICLE_DRIVEABLE(sGadgetData.SpikesData[i].VehicleIndex)
VECTOR vVehiclePosition = GET_ENTITY_COORDS(sGadgetData.SpikesData[i].VehicleIndex, FALSE)
IF ( sGadgetData.SpikesData[i].iForceTimer = 0 )
REQUEST_PTFX_ASSET()
IF ( VDIST2(vVehiclePosition, sGadgetData.SpikesData[i].vPosition) < 9.0 )
FLOAT fMaxSpeed = GET_VEHICLE_ESTIMATED_MAX_SPEED(sGadgetData.SpikesData[i].VehicleIndex)
FLOAT fCurrentSpeed = GET_ENTITY_SPEED(sGadgetData.SpikesData[i].VehicleIndex)
FLOAT fSpeedRatio = fCurrentSpeed / (fMaxSpeed - 20.0)
IF fSpeedRatio > 1.0
fSpeedRatio = 1.0
ELIF fSpeedRatio < 0.3
fSpeedRatio = 0.3
ENDIF
sGadgetData.SpikesData[i].iForceTimer = GET_GAME_TIMER() - ROUND((1.0 - fSpeedRatio) * 1000.0)
SET_VEHICLE_TYRE_BURST(sGadgetData.SpikesData[i].VehicleIndex, SC_WHEEL_CAR_FRONT_LEFT, TRUE)
SET_VEHICLE_TYRE_BURST(sGadgetData.SpikesData[i].VehicleIndex, SC_WHEEL_CAR_FRONT_RIGHT, TRUE)
PLAY_SOUND_FROM_ENTITY(-1, "CAR_STEAL_3_AGENT_TYRE_BURST", sGadgetData.SpikesData[i].VehicleIndex, "CAR_STEAL_3_AGENT")
IF HAS_PTFX_ASSET_LOADED()
START_PARTICLE_FX_NON_LOOPED_ON_ENTITY("scr_carsteal4_tyre_spiked", sGadgetData.SpikesData[i].VehicleIndex, << 1.2, 2.0, -0.3 >>, << 0.0, 0.0, 0.0 >>)
START_PARTICLE_FX_NON_LOOPED_ON_ENTITY("scr_carsteal4_tyre_spiked", sGadgetData.SpikesData[i].VehicleIndex, << -1.2, 2.0, -0.3 >>, << 0.0, 0.0, 0.0 >>)
ENDIF
IF ( bGiveWantedLevel = TRUE )
IF IS_PLAYER_PLAYING(PLAYER_ID())
IF NOT IS_PLAYER_WANTED_LEVEL_GREATER(PLAYER_ID(), 0)
IF IS_MODEL_POLICE_VEHICLE(GET_ENTITY_MODEL(sGadgetData.SpikesData[i].VehicleIndex))
SET_PLAYER_WANTED_LEVEL(PLAYER_ID(), 1)
SET_PLAYER_WANTED_LEVEL_NOW(PLAYER_ID())
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ELSE
IF GET_GAME_TIMER() - sGadgetData.SpikesData[i].iForceTimer < 1000
APPLY_FORCE_TO_ENTITY(sGadgetData.SpikesData[i].VehicleIndex, APPLY_TYPE_FORCE, << -25.0, 0.0, 0.0 >>, << 0.0, -2.0, 0.0 >>, 0, TRUE, TRUE, TRUE)
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDREPEAT
ENDIF
ENDIF
ENDPROC