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

994 lines
34 KiB
Scheme
Executable File

// *****************************************************************************************
// *****************************************************************************************
//
// MISSION NAME : RC_Threat_Public.sch
// AUTHOR : Andy M / Aaron G / (Ste did some too...)
// DESCRIPTION : Threat Functions
//
// *****************************************************************************************
// *****************************************************************************************
//----------------------
// INCLUDES
//----------------------
USING "RC_Helper_Functions.sch"
//----------------------
// CONSTANTS
//----------------------
CONST_INT MAX_THREAT_SLOTS 10
CONST_FLOAT MAX_SIGHT_DIST 200.0
//----------------------
// STRUCTS
//----------------------
STRUCT THREAT_SLOT
SHAPETEST_INDEX mShapeTest = NULL
ENTITY_INDEX mSeeker = NULL
ENTITY_INDEX mTarget = NULL
INT iLastSightTime = 0
ENDSTRUCT
//----------------------
// ENUMS
//----------------------
// used to test specific body parts in the threat test
ENUM THREAT_TEST_BODY_PART_ENUM
TT_BODY_PART_PED_ROOT = 0,
TT_BODY_PART_HEAD,
TT_BODY_PART_NECK,
TT_BODY_PART_L_HAND,
TT_BODY_PART_R_HAND,
TT_BODY_PART_L_CALF,
TT_BODY_PART_R_CALF,
TT_BODY_PART_RANDOM
ENDENUM
//----------------------
// VARIABLES
//----------------------
THREAT_SLOT mThreatSlot[MAX_THREAT_SLOTS]
BOOL bDrawDebugThreat = FALSE
//----------------------
// MISC FUNCTIONS
//----------------------
/// PURPOSE:
/// Given an index return us a bone to look at
/// PARAMS:
/// mPed - Ped
/// iCheckIndex - Number
/// RETURNS:
/// Position of Bone
FUNC VECTOR GET_SIGHT_BONE_POS(PED_INDEX mPed, THREAT_TEST_BODY_PART_ENUM eBodyPart = TT_BODY_PART_RANDOM)
// generate a random selection for TT_BODY_PART_RANDOM
IF eBodyPart = TT_BODY_PART_RANDOM
INT iRand = GET_RANDOM_INT_IN_RANGE(0, ENUM_TO_INT(TT_BODY_PART_RANDOM))
eBodyPart = INT_TO_ENUM(THREAT_TEST_BODY_PART_ENUM, iRand)
ENDIF
IF eBodyPart = TT_BODY_PART_PED_ROOT
RETURN GET_ENTITY_COORDS(mPed)
ELIF eBodyPart = TT_BODY_PART_HEAD
RETURN GET_PED_BONE_COORDS(mPed, BONETAG_HEAD, <<0.0, 0.0, 0.0>>)
ELIF eBodyPart = TT_BODY_PART_NECK
RETURN GET_PED_BONE_COORDS(mPed, BONETAG_NECK, <<0.0, 0.0, 0.0>>)
ELIF eBodyPart = TT_BODY_PART_L_HAND
RETURN GET_PED_BONE_COORDS(mPed, BONETAG_L_HAND, <<0.0, 0.0, 0.0>>)
ELIF eBodyPart = TT_BODY_PART_R_HAND
RETURN GET_PED_BONE_COORDS(mPed, BONETAG_R_HAND, <<0.0, 0.0, 0.0>>)
ELIF eBodyPart = TT_BODY_PART_L_CALF
RETURN GET_PED_BONE_COORDS(mPed, BONETAG_L_CALF, <<0.0, 0.0, 0.0>>)
ELIF eBodyPart = TT_BODY_PART_R_CALF
RETURN GET_PED_BONE_COORDS(mPed, BONETAG_R_CALF, <<0.0, 0.0, 0.0>>)
ENDIF
RETURN GET_ENTITY_COORDS(mPed)
ENDFUNC
/// PURPOSE: Gets a heading between 2 vectors
FUNC FLOAT GET_HEADING_FROM_COORDS_TO_COORDS(vector oldCoords,vector newCoords, bool invert=true)
float heading
float dX = newCoords.x - oldCoords.x
float dY = newCoords.y - oldCoords.y
if dY != 0
heading = ATAN2(dX,dY)
ELSE
if dX < 0
heading = -90
ELSE
heading = 90
ENDIF
ENDIF
//flip because for some odd reason the coders think west is a heading of 90 degrees, so this'll match the output of commands such as GET_ENTITY_HEADING()
IF invert = TRUE
heading *= -1.0
//below not necessary but helps for debugging
IF heading < 0
heading += 360.0
ENDIF
ENDIF
RETURN heading
ENDFUNC
/// PURPOSE:
/// Draw the sight cone from a ped
/// PARAMS:
/// ped - ped to draw from
/// angle - the angle of the cone so 180.0 is can see in a half circle
PROC DRAW_PED_HEAD_CONE(PED_INDEX ped, FLOAT angle = 120.0)
angle /= 2.0
VECTOR head = GET_PED_BONE_COORDS(ped, BONETAG_HEAD, <<0, 0, 0>>)
FLOAT heading = GET_HEADING_FROM_COORDS_TO_COORDS(head, GET_PED_BONE_COORDS(ped, BONETAG_HEAD, <<0, 5, 0>>))
DRAW_DEBUG_LINE(head, GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS(head, heading + angle, <<0, 5, 0>>))
DRAW_DEBUG_LINE(head, GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS(head, heading - angle, <<0, 5, 0>>))
DRAW_DEBUG_LINE(head, GET_PED_BONE_COORDS(ped, BONETAG_HEAD, <<0, 5, 0>>), 255, 255, 255)
ENDPROC
/// PURPOSE:
/// Tells us if a ped is within the viewcone of another ped
/// PARAMS:
/// vPedLookFor - the ped we are looking for
/// vPed - the ped who is doing the seeing
/// viewcone - viewcone of the seeing ped
/// useHead - if this is true we take into account which way the seeing ped is looking or we use his forward vector
/// RETURNS:
/// True or False
FUNC BOOL IS_PED_IN_PED_VIEW_CONE(PED_INDEX vPed, PED_INDEX vPedLookFor, FLOAT viewcone = 170.0, BOOL useHead = FALSE)
VECTOR vNormalisedVec = NORMALISE_VECTOR(GET_ENTITY_COORDS(vPedLookFor) - GET_ENTITY_COORDS(vPed))
VECTOR fwd
FLOAT fViewAngle
IF viewcone < 0.1 OR viewcone > 360
RETURN TRUE
ENDIF
IF (useHead = FALSE)
fwd = GET_ENTITY_FORWARD_VECTOR(vPed)
ELSE
fwd = NORMALISE_VECTOR(GET_PED_BONE_COORDS(vPed, BONETAG_HEAD, <<0, 5, 0>>) - GET_PED_BONE_COORDS(vPed, BONETAG_HEAD, <<0, 0, 0>>))
ENDIF
fViewAngle = DOT_PRODUCT(fwd, vNormalisedVec)
IF fViewAngle <= COS(viewcone / 2.0)
RETURN FALSE
ENDIF
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Tells us if a ped is within the viewcone of another ped
/// PARAMS:
/// vPedLookFor - the ped we are looking for
/// vPed - the ped who is doing the seeing
/// viewcone - viewcone of the seeing ped
/// useHead - if this is true we take into account which way the seeing ped is looking or we use his forward vector
/// RETURNS:
/// True or False
FUNC BOOL IS_POSITION_IN_PED_VIEW_CONE(PED_INDEX vPed, VECTOR vPos, FLOAT viewcone = 170.0, BOOL useHead = FALSE)
VECTOR vNormalisedVec = NORMALISE_VECTOR(vPos - GET_ENTITY_COORDS(vPed))
VECTOR fwd
FLOAT fViewAngle
IF viewcone < 0.1 OR viewcone > 360
RETURN TRUE
ENDIF
IF (useHead = FALSE)
fwd = GET_ENTITY_FORWARD_VECTOR(vPed)
ELSE
fwd = NORMALISE_VECTOR(GET_PED_BONE_COORDS(vPed, BONETAG_HEAD, <<0, 5, 0>>) - GET_PED_BONE_COORDS(vPed, BONETAG_HEAD, <<0, 0, 0>>))
ENDIF
fViewAngle = DOT_PRODUCT(fwd, vNormalisedVec)
IF fViewAngle <= COS(viewcone / 2.0)
RETURN FALSE
ENDIF
RETURN TRUE
ENDFUNC
//----------------------
// THREAT FUNCTIONS
//----------------------
/// PURPOSE:
/// Sets all shape test variables to be null
PROC RESET_THREAT_SLOT(THREAT_SLOT &slot)
slot.mShapeTest = NULL
slot.mSeeker = NULL
slot.mTarget = NULL
slot.iLastSightTime = 0
ENDPROC
/// PURPOSE:
/// Sets all shape test variables to be null
PROC RESET_THREAT_SHAPETESTS()
INT i
REPEAT COUNT_OF(mThreatSlot) i
RESET_THREAT_SLOT(mThreatSlot[i])
ENDREPEAT
ENDPROC
/// PURPOSE:
/// For script to use for clearing a specific threat test
/// PARAMS:
/// src - source threat entity
/// tgt - target ped entity
/// RETURNS:
/// TRUE threat slot is reset if a threat test was detected with the two entities
FUNC BOOL RESET_THREAT_SLOT_FOR_ENTITIES(ENTITY_INDEX src, ENTITY_INDEX tgt)
INT i
BOOL bResetTest = FALSE
REPEAT COUNT_OF(mThreatSlot) i
IF (mThreatSlot[i].mSeeker = src) AND (mThreatSlot[i].mTarget = tgt)
RESET_THREAT_SLOT(mThreatSlot[i])
bResetTest = TRUE
ENDIF
ENDREPEAT
RETURN bResetTest
ENDFUNC
/// PURPOSE:
/// Checks if the peds already has a threat shape test associated with it
/// PARAMS:
/// src - source threat entity
/// tgt - target ped entity
/// RETURNS:
/// the number of the ped's threat shapetest, or -1 if they don't have one
FUNC INT GET_THREAT_SLOT(ENTITY_INDEX src, ENTITY_INDEX tgt)
INT i
REPEAT COUNT_OF(mThreatSlot) i
IF (mThreatSlot[i].mSeeker = src) AND (mThreatSlot[i].mTarget = tgt)
RETURN i
ENDIF
ENDREPEAT
RETURN -1
ENDFUNC
/// PURPOSE:
/// Finds a gap in the threat shape test array
/// RETURNS:
/// Returns the array number of a space for a new threat shapetest or -1 if array is full
FUNC INT GET_EMPTY_THREAT_SLOTS()
INT i
REPEAT COUNT_OF(mThreatSlot) i
IF (mThreatSlot[i].mShapeTest = NULL
AND mThreatSlot[i].mSeeker = NULL // Bug fix B*1158712 - function was stomping on existing test so needs to check there isn't already entities assigned to the slots
AND mThreatSlot[i].mTarget = NULL)
RETURN i
ENDIF
ENDREPEAT
RETURN -1
ENDFUNC
/// PURPOSE:
/// Check a direct line of sight from point to ped
/// PARAMS:
/// stest - shape test index
/// pt - point to check
/// mPedLookFor - ped to look for
/// seeTime - if we can see the point this is set to current game time
/// exclude - entity to eliminate from the search
/// eBodyPart - set specific body part to check can be seen, default picks part at random
/// RETURNS:
/// true or false
FUNC BOOL CAN_POINT_SEE_PED_SHAPETEST_RETURN_TIME(SHAPETEST_INDEX &stest, VECTOR pt, PED_INDEX mPedLookFor, INT &seeTime, ENTITY_INDEX exclude = NULL, THREAT_TEST_BODY_PART_ENUM eBodyPart = TT_BODY_PART_RANDOM)
INT iHits
VECTOR vec
VECTOR vNormal
ENTITY_INDEX hitEntity = NULL
// dead people can't see things - bail
IF NOT IS_ENTITY_ALIVE(mPedLookFor)
stest = NULL
RETURN FALSE
ENDIF
// start the shape test - if we dont have one
IF (stest = NULL)
// exclude the person we are looking for a clear line of sight is a hit
vec = GET_SIGHT_BONE_POS(mPedLookFor, eBodyPart)
stest = START_SHAPE_TEST_LOS_PROBE(pt, vec + ((vec - pt) * 0.1), SCRIPT_INCLUDE_ALL, exclude)
//CPRINTLN(DEBUG_MISSION, "[SHAPE TEST]: Created")
RETURN FALSE
ENDIF
// we had a shape test from before check the results
SHAPETEST_STATUS shapeStatus = GET_SHAPE_TEST_RESULT(stest, iHits, vec, vNormal, hitEntity)
// if the shape test doesn't exist - nullify and exit or if it isn't ready exit
IF (shapeStatus = SHAPETEST_STATUS_NONEXISTENT)
stest = NULL
RETURN FALSE
ELIF (shapeStatus = SHAPETEST_STATUS_RESULTS_NOTREADY)
RETURN FALSE
ENDIF
// clear line of sight reference
stest = NULL
// check if we've hit someone and they are the person we are looking for
IF IS_ENTITY_A_PED(hitEntity)
IS_ENTITY_ALIVE(hitEntity)
IF (GET_PED_INDEX_FROM_ENTITY_INDEX(hitEntity) = mPedLookFor)
IF (bDrawDebugThreat)
DRAW_DEBUG_LINE_WITH_TWO_COLOURS(pt, GET_ENTITY_COORDS(mPedLookFor))
ENDIF
seeTime = GET_GAME_TIMER()
RETURN TRUE
ENDIF
RETURN FALSE
ENDIF
IF IS_ENTITY_A_VEHICLE(hitEntity)
IS_ENTITY_ALIVE(hitEntity)
IF IS_PED_IN_ANY_VEHICLE(mPedLookFor)
IF (GET_VEHICLE_INDEX_FROM_ENTITY_INDEX(hitEntity) = GET_VEHICLE_PED_IS_IN(mPedLookFor))
IF (bDrawDebugThreat)
// CPRINTLN(DEBUG_MISSION, "Ped in car we can see him")
DRAW_DEBUG_LINE_WITH_TWO_COLOURS(pt, GET_ENTITY_COORDS(mPedLookFor))
ENDIF
seeTime = GET_GAME_TIMER()
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Does a direct line of sight check from ped to ped
/// PARAMS:
/// stest - shape test index
/// mPed - ped doing the looking
/// mPedLookFor - ped to look for
/// seeTime - if we can see the point this is set to current game time
/// viewCone - field of view set this to 0.0 if you don't care
/// useHead - take the line from the lookers head or body
/// eBodyPart - set specific body part to check can be seen, default picks part at random
/// RETURNS:
/// true or false
FUNC BOOL CAN_PED_SEE_PED_SHAPETEST_RETURN_TIME(SHAPETEST_INDEX &stest, PED_INDEX mPed, PED_INDEX mPedLookFor, INT &seeTime, FLOAT viewCone = 170.0, BOOL useHead = TRUE, THREAT_TEST_BODY_PART_ENUM eBodyPart = TT_BODY_PART_RANDOM)
// dead people can't see things - bail
IF NOT IS_ENTITY_ALIVE(mPed)
OR NOT IS_ENTITY_ALIVE(mPedLookFor)
stest = NULL
RETURN FALSE
ENDIF
// do cone check
IF NOT IS_PED_IN_PED_VIEW_CONE(mPed, mPedLookFor, viewCone, useHead)
stest = NULL
RETURN FALSE
ENDIF
RETURN CAN_POINT_SEE_PED_SHAPETEST_RETURN_TIME(stest, GET_PED_BONE_COORDS(mPed, BONETAG_HEAD, <<0, 0, 0>>), mPedLookFor, seeTime, mPed, eBodyPart)
ENDFUNC
/// PURPOSE:
/// Does a direct line of sight check from heli to ped
/// PARAMS:
/// stest - shape test index
/// mHeli - heli doing the looking
/// mPedLookFor - ped to look for
/// seeTime - if we can see the point this is set to current game time
/// eBodyPart - set specific body part to check can be seen, default picks part at random
/// RETURNS:
/// true or false
FUNC BOOL CAN_HELI_SEE_PED_SHAPETEST_RETURN_TIME(SHAPETEST_INDEX &stest, VEHICLE_INDEX mHeli, PED_INDEX mPedLookFor, INT &seeTime, THREAT_TEST_BODY_PART_ENUM eBodyPart = TT_BODY_PART_RANDOM)
IF NOT IS_ENTITY_ALIVE(mHeli)
stest = NULL
RETURN FALSE
ENDIF
RETURN CAN_POINT_SEE_PED_SHAPETEST_RETURN_TIME(stest, GET_ENTITY_COORDS(mHeli), mPedLookFor, seeTime, eBodyPart)
ENDFUNC
/// PURPOSE:
/// Does lien of sight but it gets it's own shapetest
/// PARAMS:
/// mPed - ped doing the looking
/// mPedLookFor - ped to look for
/// seeTime - if we can see the point this is set to current game time
/// viewCone - field of view set this to 0.0 if you don't care
/// useHead - take the line from the lookers head or body
/// eBodyPart - set specific body part to check can be seen, default picks part at random
/// RETURNS:
/// true or false
FUNC BOOL CAN_PED_SEE_PED_RETURN_TIME(PED_INDEX mPed, PED_INDEX mPedLookFor, INT &seeTime, FLOAT viewCone = 170.0, BOOL useHead = TRUE, THREAT_TEST_BODY_PART_ENUM eBodyPart = TT_BODY_PART_RANDOM)
INT iSlot = GET_THREAT_SLOT(mPed, mPedLookFor)
// dead people can't see things - bail
IF NOT IS_ENTITY_ALIVE(mPed)
OR NOT IS_ENTITY_ALIVE(mPedLookFor)
IF (iSlot <> -1)
RESET_THREAT_SLOT(mThreatSlot[iSlot])
ENDIF
RETURN FALSE
ENDIF
// do cone check
IF NOT IS_PED_IN_PED_VIEW_CONE(mPed, mPedLookFor, viewCone, useHead)
RETURN FALSE
ENDIF
IF (iSlot = -1)
iSlot = GET_EMPTY_THREAT_SLOTS()
IF (iSlot = -1)
RETURN FALSE
ENDIF
mThreatSlot[iSlot].mSeeker = mPed
mThreatSlot[iSlot].mTarget = mPedLookFor
ENDIF
RETURN CAN_POINT_SEE_PED_SHAPETEST_RETURN_TIME(mThreatSlot[iSlot].mShapeTest, GET_PED_BONE_COORDS(mPed, BONETAG_HEAD, <<0, 0, 0>>), mPedLookFor, seeTime, mPed, eBodyPart)
ENDFUNC
/// PURPOSE:
/// Does lien of sight but it gets it's own shapetest
/// PARAMS:
/// mPed - ped doing the looking
/// mPedLookFor - ped to look for
/// viewCone - field of view set this to 0.0 if you don't care
/// useHead - take the line from the lookers head or body
/// iGrace -
/// eBodyPart - set specific body part to check can be seen, default picks part at random
/// RETURNS:
/// Returns True if test passes or test pass within 0.25 second of this check
FUNC BOOL CAN_PED_SEE_PED(PED_INDEX mPed, PED_INDEX mPedLookFor, FLOAT viewCone = 170.0, BOOL useHead = TRUE, INT iGrace = 250, THREAT_TEST_BODY_PART_ENUM eBodyPart = TT_BODY_PART_RANDOM)
BOOL bOK
VECTOR v
INT iSlot = GET_THREAT_SLOT(mPed, mPedLookFor)
// dead people can't see things - bail
IF NOT IS_ENTITY_ALIVE(mPed)
OR NOT IS_ENTITY_ALIVE(mPedLookFor)
IF (iSlot <> -1)
RESET_THREAT_SLOT(mThreatSlot[iSlot])
ENDIF
RETURN FALSE
ENDIF
// do cone check
IF NOT IS_PED_IN_PED_VIEW_CONE(mPed, mPedLookFor, viewCone, useHead)
RETURN FALSE
ENDIF
IF (iSlot = -1)
iSlot = GET_EMPTY_THREAT_SLOTS()
IF (iSlot = -1)
RETURN FALSE
ENDIF
mThreatSlot[iSlot].mSeeker = mPed
mThreatSlot[iSlot].mTarget = mPedLookFor
ENDIF
v = GET_PED_BONE_COORDS(mPed, BONETAG_HEAD, <<0, 0, 0>>)
bOK = CAN_POINT_SEE_PED_SHAPETEST_RETURN_TIME(mThreatSlot[iSlot].mShapeTest, v, mPedLookFor, mThreatSlot[iSlot].iLastSightTime, mPed, eBodyPart)
RETURN (bOk) OR ((GET_GAME_TIMER() - mThreatSlot[iSlot].iLastSightTime) < iGrace)
ENDFUNC
/// PURPOSE:
/// Does lien of sight but it gets it's own shapetest
/// PARAMS:
/// mheli - heli doing the looking
/// mPedLookFor - ped to look for
/// eBodyPart - set specific body part to check can be seen, default picks part at random
/// RETURNS:
/// true or false
FUNC BOOL CAN_HELI_SEE_PED(VEHICLE_INDEX mHeli, PED_INDEX mPedLookFor, INT iGrace = 250, THREAT_TEST_BODY_PART_ENUM eBodyPart = TT_BODY_PART_RANDOM)
INT iSlot = GET_THREAT_SLOT(mHeli, mPedLookFor)
// dead people can't see things - bail
IF NOT IS_ENTITY_ALIVE(mHeli)
OR NOT IS_ENTITY_ALIVE(mPedLookFor)
IF (iSlot <> -1)
RESET_THREAT_SLOT(mThreatSlot[iSlot])
ENDIF
RETURN FALSE
ENDIF
IF (iSlot = -1)
iSlot = GET_EMPTY_THREAT_SLOTS()
IF (iSlot = -1)
RETURN FALSE
ENDIF
mThreatSlot[iSlot].mSeeker = mHeli
mThreatSlot[iSlot].mTarget = mPedLookFor
ENDIF
BOOL bOK = CAN_POINT_SEE_PED_SHAPETEST_RETURN_TIME(mThreatSlot[iSlot].mShapeTest, GET_ENTITY_COORDS(mHeli), mPedLookFor, mThreatSlot[iSlot].iLastSightTime, mHeli, eBodyPart)
RETURN (bOk) OR ((GET_GAME_TIMER() - mThreatSlot[iSlot].iLastSightTime) < iGrace)
ENDFUNC
//----------------------
// OVERLOAD FUNCTIONS
//----------------------
/// PURPOSE:
/// Checks if a ped can see the player (using LOS shape test)
/// PARAMS:
/// mPed - the ped we are checking
/// seeTime - The Game Timer when we last saw the player
/// viewCone - The Persons View Cone - Use 0.0 to not worry about viewcone
/// RETURNS:
/// true if ped can see player, false otherwise (or if test is still ongoing)
FUNC BOOL CAN_PED_SEE_PLAYER_RETURN_TIME(PED_INDEX mPed, INT &seeTime, FLOAT viewCone = 170.0)
RETURN CAN_PED_SEE_PED_RETURN_TIME(mPed, PLAYER_PED_ID(), seeTime, viewCone)
ENDFUNC
/// PURPOSE:
/// Checks if a ped can see the player (using LOS shape test)
/// PARAMS:
/// mPed - the ped we are checking
/// RETURNS:
/// true if ped can see player, false otherwise (or if test is still ongoing)
FUNC BOOL CAN_PED_SEE_PLAYER(PED_INDEX mPed, FLOAT viewCone = 170.0)
RETURN CAN_PED_SEE_PED(mPed, PLAYER_PED_ID(), viewCone)
ENDFUNC
/// PURPOSE:
/// Checks if a ped can see the player (using LOS shape test)
/// PARAMS:
/// mPed - the ped we are checking
/// RETURNS:
/// true if ped can see player, false otherwise (or if test is still ongoing)
FUNC BOOL CAN_HELI_SEE_PLAYER(VEHICLE_INDEX mHeli)
RETURN CAN_HELI_SEE_PED(mHeli, PLAYER_PED_ID())
ENDFUNC
/// PURPOSE:
/// Checks if a ped can see the player (using LOS shape test)
/// PARAMS:
/// mPed - the ped we are checking
/// RETURNS:
/// true if ped can see player, false otherwise (or if test is still ongoing)
FUNC BOOL CAN_HELI_PILOT_SEE_PLAYER(PED_INDEX ped)
RETURN CAN_HELI_SEE_PED(GET_VEHICLE_PED_IS_IN(ped), PLAYER_PED_ID())
ENDFUNC
//----------------------
// CHECK FUNCTIONS
//----------------------
FUNC BOOL CAN_PED_SEE_PROJECTILE(PED_INDEX mPed, FLOAT fRange)
ENTITY_INDEX ent
VECTOR vPos
IF GET_PROJECTILE_OF_PROJECTILE_TYPE_WITHIN_DISTANCE(mPed, WEAPONTYPE_GRENADE, fRange, vPos, ent)
OR GET_PROJECTILE_OF_PROJECTILE_TYPE_WITHIN_DISTANCE(mPed, WEAPONTYPE_SMOKEGRENADE, fRange, vPos, ent)
OR GET_PROJECTILE_OF_PROJECTILE_TYPE_WITHIN_DISTANCE(mPed, WEAPONTYPE_BZGAS, fRange, vPos, ent)
OR GET_PROJECTILE_OF_PROJECTILE_TYPE_WITHIN_DISTANCE(mPed, WEAPONTYPE_STICKYBOMB, fRange, vPos, ent)
OR GET_PROJECTILE_OF_PROJECTILE_TYPE_WITHIN_DISTANCE(mPed, WEAPONTYPE_MOLOTOV, fRange, vPos, ent)
IF IS_POSITION_IN_PED_VIEW_CONE(mPed, vPos, 90)
CPRINTLN(DEBUG_MISSION, "ped Can see projectile - ", GET_THIS_SCRIPT_NAME())
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Checks if a projectile is in an area around a ped
/// Created to maintain readablity in IS_PLAYER_SHOOTING_NEAR_PED
/// with the addition of the extra flag to that function bCheckSilencedWep
/// PARAMS:
/// mPed -
/// fRange -
/// bIgnoreStickyBomb - toggles ignoring a sticky bomb unless it is in a 5 meter radius of the ped
/// RETURNS:
/// True if a projectile is in the area around the ped
FUNC BOOL CHECK_PROJECTILE_IN_RANGE(PED_INDEX mPed, FLOAT fRange, BOOL bIgnoreStickyBomb = FALSE, BOOL bCheckCanSeeProjectile = FALSE)
VECTOR vMin, vMax // is one of the player's projectiles nearby?
vMin = GET_ENTITY_COORDS(mPed)
vMax = vMin
vMin.x= vMin.x - fRange
vMin.y = vMin.y -fRange
vMin.z = vMin.z - fRange
vMax.x = vMax.x + fRange
vMax.y = vMax.y + fRange
vMax.z = vMax.z + fRange
IF bIgnoreStickyBomb
IF IS_PROJECTILE_TYPE_WITHIN_DISTANCE(GET_ENTITY_COORDS(mPed), WEAPONTYPE_GRENADE, fRange, TRUE)
OR IS_PROJECTILE_TYPE_WITHIN_DISTANCE(GET_ENTITY_COORDS(mPed), WEAPONTYPE_MOLOTOV, fRange, TRUE)
OR IS_PROJECTILE_TYPE_WITHIN_DISTANCE(GET_ENTITY_COORDS(mPed), WEAPONTYPE_SMOKEGRENADE, fRange, TRUE)
OR IS_PROJECTILE_TYPE_WITHIN_DISTANCE(GET_ENTITY_COORDS(mPed), WEAPONTYPE_STICKYBOMB, 5, TRUE)
IF bCheckCanSeeProjectile
IF CAN_PED_SEE_PROJECTILE(mPed, fRange)
RETURN TRUE
ELSE
RETURN FALSE
ENDIF
ENDIF
CPRINTLN(DEBUG_MISSION, "projectile is in distance of ped (ignore stickys) - ", GET_THIS_SCRIPT_NAME())
RETURN TRUE
ENDIF
ELSE
IF bCheckCanSeeProjectile
IF CAN_PED_SEE_PROJECTILE(mPed, fRange)
RETURN TRUE
ELSE
RETURN FALSE
ENDIF
ENDIF
IF IS_PROJECTILE_IN_AREA(vMin, vMax, TRUE)
CPRINTLN(DEBUG_MISSION, "projectile is in distance of ped - ", GET_THIS_SCRIPT_NAME())
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Checks to see if the player is shooting in range if the ped scares
/// easily
///
/// Created to maintain readablity in IS_PLAYER_SHOOTING_NEAR_PED
/// with the addition of the extra flag to that function bCheckSilencedWep
/// PARAMS:
/// bIsEasilyScared -
/// fRangeRad -
/// fRangeArea -
/// RETURNS:
///
FUNC BOOL CHECK_BULLET_SHOOTING_IN_AREA(PED_INDEX mPed, BOOL bIsEasilyScared, FLOAT fRangeRad, FLOAT fRangeArea)
IF bIsEasilyScared
IF IS_PED_SHOOTING(PLAYER_PED_ID()) // is player shooting nearby?
IF IS_ENTITY_AT_ENTITY(PLAYER_PED_ID(), mPed, <<fRangeArea,fRangeArea,fRangeArea>>)
CPRINTLN(DEBUG_MISSION, "Player shooting near ped - ", GET_THIS_SCRIPT_NAME())
RETURN TRUE
ENDIF
ENDIF
ENDIF
IF IS_BULLET_IN_AREA(GET_ENTITY_COORDS(mPed), fRangeRad, TRUE) // is one of the player's bullets nearby?
CPRINTLN(DEBUG_MISSION, "Player Bullet near ped - ", GET_THIS_SCRIPT_NAME())
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Checks if the player is shooting near the RC ped
/// This is used to stop the RC missions triggering if the player spooks the RC ped.
/// Also used to fail non-violent RC missions.
/// PARAMS:
/// mPed - the ped we are checking
/// bCheckSilencedWep - If true the player weapon is checked for a silencer
/// and the player has to fire past the ped at a very close range to trigger
/// a reaction
/// RETURNS:
/// true if the player is shooting near the ped
FUNC BOOL IS_PLAYER_SHOOTING_NEAR_PED(PED_INDEX mPed, BOOL bIsEasilyScared = TRUE, BOOL bCheckSilencedWep = FALSE, BOOL bIgnoreStickyBomb = FALSE, BOOL bCheckCanSeeProjectile = FALSE)
FLOAT BULLET_AREA_RADIUS = 8.0
FLOAT PROJECTILE_AREA_RADIUS = 15.0
IF bIsEasilyScared = FALSE
BULLET_AREA_RADIUS = 1.86
PROJECTILE_AREA_RADIUS = 1.86
ENDIF
IF bCheckSilencedWep
BULLET_AREA_RADIUS = 2
ENDIF
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
AND IS_ENTITY_ALIVE(mPed)
WEAPON_TYPE current_player_weapon
GET_CURRENT_PED_WEAPON(PLAYER_PED_ID(), current_player_weapon)
IF current_player_weapon = WEAPONTYPE_PETROLCAN // See B*887171
BULLET_AREA_RADIUS = 3
PROJECTILE_AREA_RADIUS = 3
ENDIF
IF NOT bCheckSilencedWep
IF CHECK_BULLET_SHOOTING_IN_AREA(mPed, bIsEasilyScared, BULLET_AREA_RADIUS, PROJECTILE_AREA_RADIUS)
RETURN TRUE
ENDIF
IF CHECK_PROJECTILE_IN_RANGE(mPed, PROJECTILE_AREA_RADIUS, bIgnoreStickyBomb, bCheckCanSeeProjectile)
RETURN TRUE
ENDIF
ELSE
//New addition
IF IS_PED_CURRENT_WEAPON_SILENCED(PLAYER_PED_ID())
IF IS_PED_SHOOTING(PLAYER_PED_ID()) // is player shooting nearby?
IF IS_BULLET_IN_AREA( GET_ENTITY_COORDS(mPed), BULLET_AREA_RADIUS)
CPRINTLN(DEBUG_MISSION, "Bullet in area of ped (silenced) - ", GET_THIS_SCRIPT_NAME())
RETURN TRUE
ENDIF
ENDIF
ELSE
IF bIsEasilyScared
BULLET_AREA_RADIUS = 1.86
PROJECTILE_AREA_RADIUS = 1.86
ENDIF
IF CHECK_BULLET_SHOOTING_IN_AREA(mPed, bIsEasilyScared, BULLET_AREA_RADIUS, PROJECTILE_AREA_RADIUS) // is one of the player's bullets nearby?
RETURN TRUE
ENDIF
ENDIF
IF CHECK_PROJECTILE_IN_RANGE(mPed, PROJECTILE_AREA_RADIUS, bIgnoreStickyBomb, bCheckCanSeeProjectile)
RETURN TRUE
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Checks if anyone is shooting near the RC ped
/// PARAMS:
/// mPed - the ped we are checking
/// RETURNS:
/// true if anyone is shooting near the ped
FUNC BOOL IS_ANYONE_SHOOTING_NEAR_PED(PED_INDEX mPed, FLOAT BULLET_AREA_RADIUS = 8.0, FLOAT PROJECTILE_AREA_RADIUS = 15.0)
IF IS_ENTITY_ALIVE(mPed)
IF IS_BULLET_IN_AREA(GET_ENTITY_COORDS(mPed), BULLET_AREA_RADIUS, FALSE) // are anyone's bullets nearby?
RETURN TRUE
ENDIF
IF CHECK_PROJECTILE_IN_RANGE(mPed,PROJECTILE_AREA_RADIUS)
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Checks if the player is armed and aiming a weapon at a ped.
/// Needed because IS_PLAYER_FREE_AIMING_AT_ENTITY will return true even if the player is unarmed and pressing L2 near a ped in their group - Causing bugs like 1293303.
/// PARAMS:
/// mPed - the ped we are checking
FUNC BOOL IS_PLAYER_FREE_AIMING_WEAPON_AT_PED(PED_INDEX mPed)
IF IS_PLAYER_FREE_AIMING_AT_ENTITY(PLAYER_ID(), mPed)
AND IS_PED_ARMED(PLAYER_PED_ID(), WF_INCLUDE_GUN|WF_INCLUDE_PROJECTILE)
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Checks if a ped can see that the player is aiming a weapon at them
/// PARAMS:
/// mPed - the ped we are checking
/// fViewCone - angle that player has to be targetting in before this fails
/// RETURNS:
/// True if the player is targetting the ped, and the ped can see them
FUNC BOOL IS_PLAYER_VISIBLY_TARGETTING_PED(PED_INDEX mPed, FLOAT fViewCone = 170.0)
FLOAT fTargettingDist
if IS_ENTITY_ALIVE(PLAYER_PED_ID()) AND IS_ENTITY_ALIVE(mPed)
IF IS_PLAYER_FREE_AIMING_WEAPON_AT_PED(mPed)
OR IS_PLAYER_TARGETTING_ENTITY(PLAYER_ID(), mPed)
// player is targetting ped, set targetting dist based on whether he is using melee or ranged attack
IF IS_PED_ARMED(PLAYER_PED_ID(), WF_INCLUDE_GUN|WF_INCLUDE_PROJECTILE)
fTargettingDist = 40.0 // ranged distance
ELSE
fTargettingDist = 3.0 // melee distance
ENDIF
IF IS_ENTITY_AT_ENTITY(PLAYER_PED_ID(), mPed, <<fTargettingDist, fTargettingDist, fTargettingDist>>, FALSE) // player is nearby
IF CAN_PED_SEE_PLAYER(mPed, fViewCone) // player can be seen by other ped
CPRINTLN(DEBUG_MISSION, "Player targeting ped - ", GET_THIS_SCRIPT_NAME())
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Checks if the player has threatened a ped by shooting near them, damaging them or targetting them.
/// PARAMS:
/// mPed - the ped that the player might have threatened
/// bIsEasilyScared - if this is true we worry about scaredness
/// fMeleeAngle - angle that players has to be meleeing in before this fails
/// fViewCone - angle that player has to be targetting in before this fails
/// bIgnoreAiming - This ped doesn't mind you aiming at them if set to TRUE
/// bCheckSilencedWep - If true the player weapon is checked for a silencer
/// and the player has to fire past the ped at a very close range to trigger
/// a reaction
/// RETURNS:
/// true if the player has scared the ped, false otherwise
FUNC BOOL HAS_PLAYER_THREATENED_PED(PED_INDEX mPed, BOOL bIsEasilyScared = TRUE, FLOAT fMeleeAngle = 70.0, FLOAT fViewCone = 170.0, BOOL bIgnoreAiming = FALSE, BOOL bCheckSilencedWep = FALSE, BOOL bIgnoreStickyBomb = FALSE, BOOL bCheckCanSeeProjectile = FALSE)
VECTOR v
IF IS_ENTITY_ALIVE(PLAYER_PED_ID()) AND IS_ENTITY_ALIVE(mPed)
IF HAS_ENTITY_BEEN_DAMAGED_BY_ENTITY(mPed, PLAYER_PED_ID())
RETURN TRUE
ENDIF
IF IS_PLAYER_SHOOTING_NEAR_PED(mPed, bIsEasilyScared, bCheckSilencedWep, bIgnoreStickyBomb, bCheckCanSeeProjectile)
RETURN TRUE
ENDIF
IF NOT IS_PED_ARMED(PLAYER_PED_ID(), WF_INCLUDE_GUN|WF_INCLUDE_PROJECTILE)
IF IS_PED_IN_MELEE_COMBAT(PLAYER_PED_ID())
// get the squared distance between the player and the ped (2.5^2 is roughly 5)
v = GET_ENTITY_COORDS(PLAYER_PED_ID()) - GET_ENTITY_COORDS(mPed)
IF ((v.x * v.x + v.y * v.y + v.z * v.z) <= 5.0)
RETURN IS_ENTITY_IN_ARC_2D(PLAYER_PED_ID(), mPed, fMeleeAngle)
ENDIF
ENDIF
ENDIF
IF bIgnoreAiming
RETURN FALSE
ELIF IS_PLAYER_VISIBLY_TARGETTING_PED(mPed, fViewCone)
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Checks if a ped is in a vehicle and has bumped into a specific ped
/// PARAMS:
/// hPedInVehicle - ped index of the ped who may have driven into other peds
/// hPossiblyBumpedPed - ped index of the ped who may have been driven into
/// bTestRagdolling - do we need to test if hPossiblyBumpedPed is ragdolling or not
/// RETURNS:
/// TRUE if hPedInVehicle has bumped into hPossiblyBumpedPed with a vehicle
/// if bTestRagdolling is true, it returns TRUE if hPedInVehicle has bumped into hPossiblyBumpedPed with a vehicle and hPossiblyBumpedPed is ragdolling
FUNC BOOL HAS_PED_BUMPED_PED_WITH_VEHICLE(PED_INDEX hPedInVehicle, PED_INDEX hPossiblyBumpedPed, BOOL bTestRagdolling = TRUE)
IF IS_PED_UNINJURED(hPedInVehicle) AND IS_PED_IN_ANY_VEHICLE(hPedInVehicle)
IF IS_PED_UNINJURED(hPossiblyBumpedPed)
IF IS_ENTITY_TOUCHING_ENTITY(hPossiblyBumpedPed, hPedInVehicle)
IF bTestRagdolling
IF IS_PED_RAGDOLL(hPossiblyBumpedPed)
RETURN TRUE
ENDIF
ELSE
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Checks if a ped is in a vehicle and has knocked any other peds of balance by bumping it into them
/// PARAMS:
/// hPedInVehicle - ped index of the ped who may have driven into other peds
/// RETURNS:
/// TRUE if hPedInVehicle has bumped into another ped with a vehicle
/// if bTestRagdolling is true, it returns TRUE if hPedInVehicle has bumped into another ped with a vehicle and that ped is ragdolling
FUNC BOOL HAS_PED_BUMPED_ANY_PED_WITH_VEHICLE(PED_INDEX hPedInVehicle, BOOL bTestRagdolling = TRUE)
IF IS_PED_IN_ANY_VEHICLE(hPedInVehicle)
PED_INDEX pedsNearPlayer[16]
GET_PED_NEARBY_PEDS(hPedInVehicle, pedsNearPlayer)
INT i
FOR i = 0 TO ( COUNT_OF(pedsNearPlayer) - 1 )
IF IS_PED_UNINJURED(pedsNearPlayer[i])
IF IS_ENTITY_TOUCHING_ENTITY(pedsNearPlayer[i], hPedInVehicle)
IF bTestRagdolling
IF IS_PED_RAGDOLL(pedsNearPlayer[i])
RETURN TRUE
ENDIF
ELSE
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDFOR
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Initialises the external variables used by HAS_PLAYER_RAMMED_ENEMY_ENOUGH
/// PARAMS:
/// bRammedLastFrame - Keeps track of whether or not the player was touching the enemy vehicle in the previous frame, initialised to FALSE
/// iRamTimer - Triggers the ramming check every 500ms, initialised to zero
/// iRammedCount - Keeps track of how many times the player has rammed the enemy vehicle, initialised to zero
/// fClosingSpeedLastFrame - Keeps track of the relative velocity of the player and enemy in the previous frame, initialised to zero
PROC INIT_HAS_PLAYER_RAMMED_ENEMY_ENOUGH(BOOL & bRammedLastFrame, INT & iRamTimer, INT & iRammedCount, FLOAT & fClosingSpeedLastFrame)
bRammedLastFrame = FALSE
iRamTimer = 0
iRammedCount = 0
fClosingSpeedLastFrame = 0.0
ENDPROC
/// PURPOSE:
/// Keeps track of how many times the player has rammed another vehicle
/// PARAMS:
/// viEnemy - The vehicle we're testing if the player has rammed
/// bRammedLastFrame - Keeps track of whether or not the player was touching the enemy vehicle in the previous frame
/// iRamTimer - Triggers the ramming check every 500ms
/// iRammedCount - Keeps track of how many times the player has rammed the enemy vehicle
/// fClosingSpeedLastFrame - Keeps track of the relative velocity of the player and enemy in the previous frame
/// MAX_NUM_RAMS - The number of times the player must ram the enemy vehicle
/// MIN_CLOSING_SPEED - The player must be travelling faster than this, relative to the enemy vehicle, for the collision to count. Reduce this value to make it easier
/// RETURNS:
/// TRUE when the player has rammed the enemy vehicle enough times
FUNC BOOL HAS_PLAYER_RAMMED_ENEMY_ENOUGH(VEHICLE_INDEX viEnemy, BOOL & bRammedLastFrame, INT & iRamTimer, INT & iRammedCount, FLOAT & fClosingSpeedLastFrame, INT MAX_NUM_RAMS = 6, FLOAT MIN_CLOSING_SPEED = 4.0)
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
// Count number of times the van has been rammed with enough force
VEHICLE_INDEX vehPlayer = GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID())
IF IS_ENTITY_TOUCHING_ENTITY(vehPlayer, viEnemy)
IF NOT bRammedLastFrame
AND GET_GAME_TIMER() - iRamTimer > 500
AND fClosingSpeedLastFrame >= MIN_CLOSING_SPEED // check the closing speed was high enough the prev frame to register a collision (using the last frame due to the velocities already having been resolved by the physics engine)
bRammedLastFrame = TRUE
iRammedCount++
IF iRammedCount >= MAX_NUM_RAMS
RETURN TRUE
ENDIF
ENDIF
ELSE
IF bRammedLastFrame
iRamTimer = GET_GAME_TIMER()
bRammedLastFrame = FALSE
ENDIF
ENDIF
// Calculate closing velocity for this frame
VECTOR vContactNormal = NORMALISE_VECTOR(GET_ENTITY_COORDS(vehPlayer) - GET_ENTITY_COORDS(viEnemy))
VECTOR vVelBMinusVelA = GET_ENTITY_VELOCITY(viEnemy) - GET_ENTITY_VELOCITY(vehPlayer)
fClosingSpeedLastFrame = DOT_PRODUCT(vVelBMinusVelA, vContactNormal)
ENDIF
RETURN FALSE
ENDFUNC