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

398 lines
12 KiB
Scheme
Executable File

// *****************************************************************************************
// *****************************************************************************************
//
// FILE NAME : rc_threat_special_public.sch
// AUTHOR : Andy Mingella / Aaron Gandaa
// DESCRIPTION : Functions used in RC's to handle threats
//
// *****************************************************************************************
// *****************************************************************************************
//----------------------
// INCLUDES
//----------------------
USING "rage_builtins.sch"
USING "globals.sch"
USING "RC_Helper_Functions.sch"
USING "commands_shapetest.sch"
//----------------------
// ENUMS
//----------------------
ENUM SIGHTTEST_STATUS
SIGHTTEST_READY,
SIGHTTEST_ABORTED,
SIGHTTEST_PENDING,
SIGHTTEST_COMPLETE
ENDENUM
//----------------------
// STRUCTS
//----------------------
STRUCT PEDSIGHT_TEST
BOOL bIsFree = TRUE
PED_INDEX viewerPedID // the ped who is looking
PED_INDEX targetPedID // the ped we are looking at
FLOAT fViewCone = 170.0 // the field of view cone of the looking ped
INT iLastSightTime = 0
SHAPETEST_INDEX mShapeTest = NULL // index of the shape test we are using
BOOL bSeenOnLastCheck = FALSE
VECTOR vCheckPoint
// if we have seen him we wait a few frames before trying this again
// we randomize this so that everyone doesn't do shape test at once
INT iNextCheckTime
SIGHTTEST_STATUS mSightStatus
INT iIndex = -1
ENDSTRUCT
//----------------------
// CONSTANTS
//----------------------
CONST_INT MAXIMUM_SIGHTTESTS 10
//----------------------
// VARAIBLES
//----------------------
PEDSIGHT_TEST mSightTestArray[MAXIMUM_SIGHTTESTS]
#IF IS_DEBUG_BUILD
BOOL bDumpSightTestTTY = FALSE
#ENDIF
//----------------------
// FUNCTIONS
//----------------------
/// PURPOSE:
/// Finds a gap in the threat shape test array
/// RETURNS:
/// Returns the array number of a space for a new threat sighttest or -1 if array is full
FUNC INT GET_FREE_SIGHTTEST_INDEX()
INT i
REPEAT COUNT_OF(mSightTestArray) i
IF (mSightTestArray[i].bIsFree = TRUE)
RETURN i
ENDIF
ENDREPEAT
RETURN -1
ENDFUNC
/// PURPOSE:
/// Registers a new Ped Sight Test
/// PARAMS:
/// viewPed - The Ped who is doing the looking
/// targetPed - The ped we are looking for
/// viewCone - Field of View (For People in Aircraft set this to 0.0)
/// RETURNS:
/// Index to the sight test Array or -1 if it can't register one
FUNC INT REGISTER_PEDSIGHT_TEST(PED_INDEX viewPed, PED_INDEX tgtPed, FLOAT viewCone = 170.0)
INT i
// check to see if these peds are already registered
REPEAT COUNT_OF(mSightTestArray) i
IF ((mSightTestArray[i].viewerPedID = viewPed) AND (mSightTestArray[i].targetPedID = tgtPed))
mSightTestArray[i].fViewCone = viewCone
#IF IS_DEBUG_BUILD
IF (bDumpSightTestTTY = TRUE)
CPRINTLN(DEBUG_MISSION, "Sight Test Already Registered:", i)
ENDIF
#ENDIF
RETURN i
ENDIF
ENDREPEAT
// else try and find empty sighttest
i = GET_FREE_SIGHTTEST_INDEX()
IF (i = -1)
#IF IS_DEBUG_BUILD
IF (bDumpSightTestTTY = TRUE)
CPRINTLN(DEBUG_MISSION, "Sight Test Register Failed")
ENDIF
#ENDIF
RETURN -1
ENDIF
#IF IS_DEBUG_BUILD
IF (bDumpSightTestTTY = TRUE)
CPRINTLN(DEBUG_MISSION, "Sight Test Registered:", i)
ENDIF
#ENDIF
mSightTestArray[i].bIsFree = FALSE
mSightTestArray[i].viewerPedID = viewPed
mSightTestArray[i].targetPedID = tgtPed
mSightTestArray[i].iLastSightTime = 0
mSightTestArray[i].fViewCone = viewCone
mSightTestArray[i].mShapeTest = NULL
mSightTestArray[i].iIndex = i
RETURN i
ENDFUNC
/// PURPOSE:
/// Deregisters a new Ped Sight Test
/// Remove All Sights tests involving this ped
/// PARAMS:
/// viewPed - The Ped who is doing the looking
PROC DEREGISTER_PEDSIGHT_TEST(PEDSIGHT_TEST &sightTest)
IF (sightTest.bIsFree = FALSE)
sightTest.bIsFree = FALSE
sightTest.viewerPedID = NULL
sightTest.targetPedID = NULL
sightTest.iLastSightTime = 0
sightTest.iNextCheckTime = 0
sightTest.mSightStatus = SIGHTTEST_ABORTED
sightTest.mShapeTest = NULL
ENDIF
ENDPROC
/// PURPOSE:
/// Deregisters a new Ped Sight Test
/// Remove All Sights tests involving this ped
/// PARAMS:
/// viewPed - The Ped who is doing the looking
PROC DEREGISTER_PEDSIGHT_TESTS(PED_INDEX viewPed)
INT i
REPEAT COUNT_OF(mSightTestArray) i
IF (mSightTestArray[i].bIsFree = FALSE)
IF ((mSightTestArray[i].viewerPedID = viewPed) OR (mSightTestArray[i].targetPedID = viewPed))
DEREGISTER_PEDSIGHT_TEST(mSightTestArray[i])
ENDIF
ENDIF
ENDREPEAT
ENDPROC
/// PURPOSE:
/// Updates all Ped Sight Test
/// Call this every frame before using can ped see ped
FUNC SIGHTTEST_STATUS UPDATE_PEDSIGHT_TEST(PEDSIGHT_TEST &sightTest)
VECTOR vTemp
INT iNHits = 0
VECTOR vHitPos, vNormal
ENTITY_INDEX hitEntity
SHAPETEST_STATUS shapeStatus
// moved them inside here to so it doesn't clash with rc_threat
CONST_FLOAT SPROBE_DIST_TO_HIT 0.5 // how far away from the player the probe can be for us say he can be seen
CONST_FLOAT SPROBE_DIST_TO_HIT_CAR 2.5 // how far away from the player's car the probe can be for us say he can be seen
CONST_FLOAT SPROBE_DIST_TO_HIT_AIRCRAFT 5.0 // how far away from the player's car the probe can be for us say he can be seen
CONST_FLOAT SPROBE_DIST_TO_HIT_PED_IN_COVER 0.310
CONST_FLOAT SPROBE_DIST_TO_HIT_PED_RUNNING 0.8
// do alive checks - should do stuff to bail out here
IF (sightTest.bIsFree = TRUE)
RETURN SIGHTTEST_ABORTED
ENDIF
IF (NOT IS_ENTITY_ALIVE(sightTest.viewerPedID)) OR (NOT IS_ENTITY_ALIVE(sightTest.targetPedID))
// just grab the shape test so we can flush it
IF (sightTest.mShapeTest <> NULL)
shapeStatus = GET_SHAPE_TEST_RESULT(sightTest.mShapeTest, iNHits, vHitPos, vNormal, hitEntity)
sightTest.mShapeTest = NULL
ENDIF
IF NOT IS_ENTITY_ALIVE(sightTest.viewerPedID)
DEREGISTER_PEDSIGHT_TESTS(sightTest.viewerPedID)
ENDIF
IF NOT IS_ENTITY_ALIVE(sightTest.targetPedID)
DEREGISTER_PEDSIGHT_TESTS(sightTest.targetPedID)
ENDIF
RETURN SIGHTTEST_ABORTED
ENDIF
// check to see if target is outside of view ped's viewcont - if so bail out
FLOAT fViewLimit = sightTest.fViewCone / 2.0
VECTOR vPedPos = GET_ENTITY_COORDS(sightTest.viewerPedID)
VECTOR vPedLookForPos = GET_ENTITY_COORDS(sightTest.targetPedID)
IF (sightTest.fViewCone > 0.0)
VECTOR vNormalisedVec = NORMALISE_VECTOR(vPedLookForPos - vPedPos)
FLOAT fViewAngle = DOT_PRODUCT(GET_ENTITY_FORWARD_VECTOR(sightTest.viewerPedID), vNormalisedVec)
IF fViewAngle <= COS(fViewLimit)
// just grab the shape test so we can flush it
IF (sightTest.mShapeTest <> NULL)
shapeStatus = GET_SHAPE_TEST_RESULT(sightTest.mShapeTest, iNHits, vHitPos, vNormal, hitEntity)
sightTest.mShapeTest = NULL
ENDIF
RETURN SIGHTTEST_ABORTED // target is behind the ped and can't be seen
ENDIF
ENDIF
// grab a shape test
IF (sightTest.mShapeTest = NULL)
vPedPos = GET_PED_BONE_COORDS(sightTest.viewerPedID, BONETAG_HEAD, <<0.0, 0.6, 0.0>>)
// we need to work out how far the person will be ahead of time
sightTest.vCheckPoint = vPedLookForPos + (GET_ENTITY_VELOCITY(sightTest.targetPedID) * GET_FRAME_TIME())
vTemp = ((sightTest.vCheckPoint - vPedPos) * 1.25) + vPedPos
sightTest.mShapeTest = START_SHAPE_TEST_LOS_PROBE(vPedPos, vTemp, SCRIPT_INCLUDE_ALL)
RETURN SIGHTTEST_PENDING
ENDIF
#IF IS_DEBUG_BUILD
IF (bDumpSightTestTTY = TRUE)
DRAW_DEBUG_LINE(vPedPos, vPedLookForPos)
ENDIF
#ENDIF
// we have a shape test
FLOAT fDist
BOOL tgtSeen = FALSE
shapeStatus = GET_SHAPE_TEST_RESULT(sightTest.mShapeTest, iNHits, vHitPos, vNormal, hitEntity)
IF (shapeStatus = SHAPETEST_STATUS_NONEXISTENT)
#IF IS_DEBUG_BUILD
IF (bDumpSightTestTTY = TRUE)
CPRINTLN(DEBUG_MISSION, "Shape Test Non-Existant for Sight Test:", sightTest.iIndex)
ENDIF
#ENDIF
sightTest.mShapeTest = NULL
RETURN SIGHTTEST_ABORTED
ELIF (shapeStatus = SHAPETEST_STATUS_RESULTS_NOTREADY)
#IF IS_DEBUG_BUILD
IF (bDumpSightTestTTY = TRUE)
CPRINTLN(DEBUG_MISSION, "Shape Test Results Not Ready for Sight Test:", sightTest.iIndex)
ENDIF
#ENDIF
RETURN SIGHTTEST_PENDING
ENDIF
// process the results
IF (iNHits = 0) // we have a clear line of sight
sightTest.iLastSightTime = GET_GAME_TIMER()
#IF IS_DEBUG_BUILD
IF (bDumpSightTestTTY = TRUE)
CPRINTLN(DEBUG_MISSION, "Sight Test:", sightTest.iIndex, " Complete - Clear Line Of Sight - Time:", sightTest.iLastSightTime)
ENDIF
#ENDIF
ELSE
// check our distance between hit point and object in 2D
FLOAT spdshift = ABSF (GET_ENTITY_SPEED(sightTest.viewerPedID) * GET_FRAME_TIME())
spdshift = 0
fDist = GET_DISTANCE_BETWEEN_COORDS(sightTest.vCheckPoint, vHitPos, FALSE)// + (GET_ENTITY_SPEED(sightTest.viewerPedID) * GET_FRAME_TIME())
IF IS_PED_IN_ANY_HELI(sightTest.viewerPedID)
tgtSeen = (fDist <= (SPROBE_DIST_TO_HIT_AIRCRAFT + spdshift))
ELIF IS_PED_IN_ANY_VEHICLE(sightTest.targetPedID)
tgtSeen = (fDist <= (SPROBE_DIST_TO_HIT_CAR + spdshift))
ELIF IS_PED_RUNNING(sightTest.targetPedID)
tgtSeen = (fDist <= (SPROBE_DIST_TO_HIT_PED_RUNNING + spdshift))
ELIF IS_PED_IN_COVER(sightTest.targetPedID)
tgtSeen = (fDist <= (SPROBE_DIST_TO_HIT_PED_IN_COVER + spdshift))
ELSE
tgtSeen = (fDist <= (SPROBE_DIST_TO_HIT + spdshift))
ENDIF
// then check to see we are within height difference (+ or - 1 meter)
IF (tgtSeen) AND (ABSF(sightTest.vCheckPoint.z - vHitPos.z) > 1.0)
tgtSeen = FALSE
ENDIF
#IF IS_DEBUG_BUILD
IF (bDumpSightTestTTY = TRUE)
CPRINTLN(DEBUG_MISSION, "Sight Test:", sightTest.iIndex, " Complete - Hits:", iNHits, " Target Seen:", tgtSeen)
ENDIF
#ENDIF
sightTest.bSeenOnLastCheck = tgtSeen
// record this sight time
IF (tgtSeen)
sightTest.iLastSightTime = GET_GAME_TIMER()
#IF IS_DEBUG_BUILD
IF (bDumpSightTestTTY = TRUE)
DRAW_DEBUG_SPHERE(vHitPos, 0.0625, 255, 0, 0, 255)
ENDIF
#ENDIF
ENDIF
#IF IS_DEBUG_BUILD
IF (bDumpSightTestTTY = TRUE) AND (tgtSeen = FALSE)
DRAW_DEBUG_SPHERE(vHitPos, 0.0625)
ENDIF
#ENDIF
ENDIF
// reset safe check
sightTest.iNextCheckTime = GET_GAME_TIMER() + GET_RANDOM_INT_IN_RANGE(64, 256)
sightTest.mShapeTest = NULL
RETURN SIGHTTEST_COMPLETE
ENDFUNC
/// PURPOSE:
/// Updates all Ped Sight Test
/// Call this every frame before using can ped see ped
PROC UPDATE_PEDSIGHT_TESTS()
INT i
REPEAT COUNT_OF(mSightTestArray) i
IF NOT ((mSightTestArray[i].mSightStatus = SIGHTTEST_COMPLETE) AND (GET_GAME_TIMER() < mSightTestArray[i].iNextCheckTime))
mSightTestArray[i].mSightStatus = UPDATE_PEDSIGHT_TEST(mSightTestArray[i])
ENDIF
ENDREPEAT
ENDPROC
/// PURPOSE:
/// Use this to see if the sight test worked (person saw person)
/// PARAMS:
/// i - the sight test index you'll recieve this when you register the sight ped
/// iGraceTime - If we've seen the person within this amount of time it still counts
/// RETURNS:
/// True on Success
FUNC BOOL DID_SIGHTTEST_PASS(INT i, INT iGraceTime = 250)
IF (i = -1)
RETURN FALSE
ENDIF
IF (i >= COUNT_OF(mSightTestArray))
RETURN FALSE
ENDIF
// we don't care if the sight test in not used or he hasn't been seen yet
IF (mSightTestArray[i].bIsFree = TRUE) OR (mSightTestArray[i].iLastSightTime = 0)
RETURN FALSE
ENDIF
RETURN ((GET_GAME_TIMER() - mSightTestArray[i].iLastSightTime) < iGraceTime)
ENDFUNC
/// PURPOSE:
/// Shows sight times for all sight tests (debug only)
PROC DRAW_DEBUG_SIGHT_TIMES()
INT i
INT cnt
// draw how many tests have passed
cnt = 0
i = 0
REPEAT COUNT_OF(mSightTestArray) i
IF DID_SIGHTTEST_PASS(i)
cnt ++
ENDIF
ENDREPEAT
SET_TEXT_SCALE(0.5, 0.5)
DISPLAY_TEXT_WITH_NUMBER(0.5, 0.1, "NUMBER", cnt)
// draw sight times
i = 0
REPEAT COUNT_OF(mSightTestArray) i
SET_TEXT_SCALE(0.25, 0.25)
DISPLAY_TEXT_WITH_NUMBER(0.1, 0.2 + (TO_FLOAT(i) * 0.025), "NUMBER", mSightTestArray[i].iLastSightTime)
ENDREPEAT
ENDPROC