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