// ***************************************************************************************** // ***************************************************************************************** // // 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, <>) 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, <>, 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