4299 lines
128 KiB
C++
4299 lines
128 KiB
C++
// Rage headers
|
|
#include "Profile/timebars.h"
|
|
#include "crskeleton/Skeleton.h"
|
|
#include "crskeleton/SkeletonData.h"
|
|
|
|
// Framework headers
|
|
#include "fwanimation/animmanager.h"
|
|
#include "grcore/debugdraw.h"
|
|
#include "fwmaths/Angle.h"
|
|
#include "fwscene/stores/staticboundsstore.h"
|
|
|
|
// Game headers
|
|
#include "ai/debug/system/AIDebugLogManager.h"
|
|
#include "Animation/AnimBones.h"
|
|
#include "animation/FacialData.h"
|
|
#include "Camera/CamInterface.h"
|
|
#include "Camera/gameplay/GameplayDirector.h"
|
|
#include "Debug/DebugScene.h"
|
|
#include "Game/ModelIndices.h"
|
|
#include "Game/Dispatch/DispatchHelpers.h"
|
|
#include "Game/Dispatch/Orders.h"
|
|
#include "Event/Decision/EventDecisionMakerManager.h"
|
|
#include "event/Events.h"
|
|
#include "Event/ShockingEvents.h"
|
|
#include "ModelInfo/VehicleModelInfo.h"
|
|
#include "Network/NetworkInterface.h"
|
|
#include "Objects/Door.h"
|
|
#include "Objects/Object.h"
|
|
#include "PedGroup/PedGroup.h"
|
|
#include "Peds/PedDebugVisualiser.h"
|
|
#include "Peds/PedIntelligence.h"
|
|
#include "Peds/PedTaskRecord.h"
|
|
#include "Peds/PedWeapons/PedTargeting.h"
|
|
#include "Peds/Ped.h"
|
|
#include "Peds/QueriableInterface.h"
|
|
#include "Physics/Physics.h"
|
|
#include "physics/WorldProbe/worldprobe.h"
|
|
#include "script/script_channel.h"
|
|
#include "script/thread.h"
|
|
#include "scene/world/GameWorld.h"
|
|
#include "Stats/StatsInterface.h"
|
|
#include "Task/Combat/Cover/Cover.h"
|
|
#include "Task/Combat/Cover/TaskCover.h"
|
|
#include "Task/Combat/TaskCombat.h"
|
|
#include "Task/Combat/TaskCombatMelee.h"
|
|
#include "Task/Combat/TaskCombatMounted.h"
|
|
#include "Task/Combat/TaskNewCombat.h"
|
|
#include "Task/Combat/TaskWrithe.h"
|
|
#include "Task/Default/TaskPlayer.h"
|
|
#include "Task/General/TaskBasic.h"
|
|
#include "Task/General/TaskSecondary.h"
|
|
#include "Task/Movement/TaskNavMesh.h"
|
|
#include "Task/Movement/TaskMoveWander.h"
|
|
#include "Task/Motion/Locomotion/TaskMotionPed.h"
|
|
#include "Task/Physics/TaskNM.h"
|
|
#include "Task/Response/TaskFlee.h"
|
|
#include "Task/Response/TaskShockingEvents.h"
|
|
#include "Task/Vehicle/TaskCar.h"
|
|
#include "Task/System/TaskTreeClone.h"
|
|
#include "Task/System/TaskTreePed.h"
|
|
#include "Task/Weapons/Gun/TaskGun.h"
|
|
#include "Vehicles/Bike.h"
|
|
#include "Vehicles/Boat.h"
|
|
#include "Vehicles/Heli.h"
|
|
#include "Weapons/AirDefence.h"
|
|
|
|
#include "Task/Default/TaskCuffed.h"
|
|
|
|
// grrr. This macro conflicts under visual studio.
|
|
#if defined (MSVC_COMPILER)
|
|
#ifdef max
|
|
#undef max
|
|
#endif //max
|
|
#endif //MSVC_COMPILER
|
|
|
|
AI_OPTIMISATIONS()
|
|
AI_MOTION_OPTIMISATIONS()
|
|
|
|
/////////////////////
|
|
//PED INTELLIGENCE
|
|
/////////////////////
|
|
|
|
|
|
bool CPedIntelligence::ms_bPedsRespondToObjectCollisionEvents = true;
|
|
|
|
const u32 CPedIntelligence::ms_iMinFreqToAcceptBuildingCollisionEvents = 2000;
|
|
bool CPedIntelligence::ms_bAllowTeleportingWhenRoutesTimeOut = true;
|
|
const u32 CPedIntelligence::ms_iLastHeardGunFireTolerance = 800;
|
|
const u32 CPedIntelligence::ms_iLastPinnedDownTolerance = 175;
|
|
const u16 CPedIntelligence::ms_MinFramesPerRagdollBoundUpdate = 10;
|
|
|
|
#if !__FINAL
|
|
bool CPedIntelligence::ms_bWarnAboutRouteTaskBuildingCollisions = false;
|
|
#endif
|
|
|
|
PF_PAGE(GTA_PedAI, "GTA Ped AI");
|
|
PF_GROUP(GTA_AI);
|
|
PF_LINK(GTA_PedAI, GTA_AI);
|
|
PF_TIMER(AI_Process, GTA_AI);
|
|
PF_TIMER(AI_Process_NearbyEntityLists, GTA_AI);
|
|
PF_TIMER(AI_ProcessUpdateTargetting, GTA_AI);
|
|
PF_TIMER(AI_ProcessPhysics, GTA_AI);
|
|
PF_TIMER(AI_ProcessPostPhysics, GTA_AI);
|
|
PF_TIMER(AI_Process_Tasks, GTA_AI);
|
|
PF_TIMER(AI_ProcessPostMovement, GTA_AI);
|
|
PF_TIMER(AI_ProcessPostCamera, GTA_AI);
|
|
PF_TIMER(AI_ProcessPreRender2, GTA_AI);
|
|
PF_TIMER(AI_ProcessPostPreRender, GTA_AI);
|
|
PF_TIMER(AI_ProcessPostPreRenderAfterAttachments, GTA_AI);
|
|
PF_TIMER(AI_BuildQueriableState, GTA_AI);
|
|
PF_TIMER(AI_ScanForEvents, GTA_AI);
|
|
|
|
static const float s_fDefaultDriverRacingModifier = 0.25f;
|
|
|
|
CPedIntelligence::CPedIntelligence(CPed* pPed)
|
|
: m_pPed(pPed),
|
|
m_pedStealth(),
|
|
m_fTimeSinceLastAiUpdate(0.0f),
|
|
m_taskManager(pPed, PED_TASK_TREE_MAX),
|
|
m_eventGroup(pPed),
|
|
m_eventHandler(pPed),
|
|
m_pedPerception(pPed),
|
|
m_uMaxNumFriendsToInform(MAX_NUM_FRIENDS_TO_INFORM),
|
|
m_fMaxInformFriendDistance(MAX_INFORM_FRIEND_DISTANCE),
|
|
m_iHighestPriorityEventType(EVENT_NONE),
|
|
m_iHighestPriorityEventPriority(0),
|
|
m_vehicleScanner(CExpensiveProcess::PPT_VehicleScanner),
|
|
m_doorScanner(CExpensiveProcess::PPT_DoorScanner),
|
|
m_pedScanner(CExpensiveProcess::PPT_PedScanner),
|
|
m_objectScanner(CExpensiveProcess::PPT_ObjectCollisionScanner),
|
|
m_audioDistributer(CExpensiveProcess::PPT_AudioDistributer),
|
|
m_climbDetector(*pPed),
|
|
m_dropDownDetector(*pPed),
|
|
m_pPedTargetting( NULL ),
|
|
m_pCoverFinder( NULL ),
|
|
m_pDefaultScoringFn( CPedTargetting::DefaultTargetScoring ),
|
|
m_uLastAimedAtByPlayerTime(0),
|
|
m_iLastClimbTime(fwTimer::GetTimeInMilliseconds()),
|
|
m_fClimbStrength(1.0f),
|
|
m_fClimbStrengthRate(0.0f),
|
|
m_vLastAttemptedClimbPosition(Vector3(9999.0f,9999.0f,9999.0f)),
|
|
m_iLastAttemptedClimbTime(0),
|
|
m_iEnclosedSearchVolumeCheckIndex(0),
|
|
m_vFailedClimb(Vector3(9999.0f,9999.0f,9999.0f)),
|
|
m_vLastMainNavMeshPoly(VEC3_ZERO),
|
|
m_iNumTimesClimbFailed(0),
|
|
m_vCrossRoadStartPos(VEC3_ZERO),
|
|
m_vCrossRoadEndPos(VEC3_ZERO),
|
|
m_vChargeGoalPosOverride(V_ZERO),
|
|
m_uLastFinishedCrossRoadTimeMS(0),
|
|
m_fTimeTilNextBulletReaction(0.0f),
|
|
m_fPinnedDown(0.0f),
|
|
m_bForcePinned(false),
|
|
m_bWillForceScanOnSwitchToHighPhysLod(true),
|
|
m_fForcedPinnedTimer(0.0f),
|
|
m_iIgnoreLowPriShockingEventsCount(0),
|
|
m_uNumPedsFiringAt(0),
|
|
m_uRelationshipGroupIndex(0),
|
|
m_iPedAlertness(0),
|
|
m_iLastReactedToDeadPedTime(0),
|
|
m_iLastHeardGunFireTime(0),
|
|
m_iLastSeenGunFireTime(0),
|
|
m_iLastPinnedDownTime(0),
|
|
m_iLastFiringVariationTime(0),
|
|
m_iLastCombatRollTime(0),
|
|
m_iLastGetUpTime(0),
|
|
m_iLastAimGrenadeThrowTime(0),
|
|
m_uLastTimeHonkedAt(0),
|
|
m_uLastTimeHonkAgitatedUs(0),
|
|
m_uLastTimeRevvedAt(0),
|
|
m_uLastTimeRevAgitatedUs(0),
|
|
m_uLastTimeWeWereKnockedToTheGround(0),
|
|
m_uLastTimeWeWereRammedInVehicle(0),
|
|
m_uLastTimeShot(0),
|
|
m_uLastTimeOurPositionWasShouted(0),
|
|
m_uLastTimeBumpedWhenStill(0),
|
|
m_uLastTimeEvaded(0),
|
|
m_uLastTimeCollidedWithPlayer(0),
|
|
m_uLastTimeTriedToSayAudioForDamage(0),
|
|
m_uLastTimeCalledPolice(0),
|
|
m_uLastTimeLeftVehicle(0),
|
|
m_uLastTimeStartedToEnterVehicle(0),
|
|
m_uTintIndexForParachute(-1),
|
|
m_uTintIndexForReserveParachute(-1),
|
|
m_uTintIndexForParachutePack(0),
|
|
m_alertState(AS_NotAlert),
|
|
m_pCombatDirector(NULL),
|
|
m_pRelationshipGroupDefault(CRelationshipManager::s_pCivmaleGroup),
|
|
m_fTimeUntilNextCopVariableUpdate(0.0f),
|
|
m_fDriverAbilityOverride(-1.0f),
|
|
m_fDriverAggressivenessOverride(-1.0f),
|
|
m_fDriverRacingModifier(s_fDefaultDriverRacingModifier),
|
|
m_fBattleAwareness(0.0f),
|
|
m_nLastBattleEventTime(0),
|
|
m_nLastBattleEventTimeLocalPlayer(0),
|
|
m_nLastTimeDeadPedSeen(0),
|
|
m_bBattleAwarenessForcingRun(false),
|
|
m_bHadAmbientFriend(false),
|
|
m_pLastEntityThatKnockedUsToTheGround(NULL),
|
|
m_pLastPedThatRammedUsInVehicle(NULL),
|
|
m_pLastUsedScenarioPoint(NULL),
|
|
m_iLastUsedScenarioPointType(-1),
|
|
m_uLastUsedScenarioFlags(0),
|
|
m_pLastEntityEvaded(NULL),
|
|
m_pFriendlyException(NULL),
|
|
m_pAchievedCombatVictoryOver(NULL),
|
|
m_pAmbientFriend(NULL),
|
|
m_pLastVehiclePedInside(NULL),
|
|
m_ControlPassingFailTime(0)
|
|
{
|
|
m_audioDistributer.RegisterSlot();
|
|
|
|
#if __DEV
|
|
for( s32 i = 0; i < MAX_DEBUG_SCRIPT_TASK_HISTORY; i++ )
|
|
{
|
|
m_aScriptHistoryName[i] = NULL;
|
|
m_aScriptHistoryTaskName[i] = NULL;
|
|
m_aScriptHistoryTime[i] = 0;
|
|
m_szScriptName[i][0] = '\0';
|
|
m_aProgramCounter[i] = 0;
|
|
}
|
|
|
|
m_iCurrentHistroyTop = 0;
|
|
m_bNewTaskThisFrame = false;
|
|
m_bAssertIfRouteNotFound = false;
|
|
#endif
|
|
}
|
|
|
|
CPedIntelligence::~CPedIntelligence()
|
|
{
|
|
// If the targeting system is still active, delete it now
|
|
ShutdownTargetting();
|
|
|
|
if(m_pCoverFinder)
|
|
{
|
|
delete m_pCoverFinder;
|
|
m_pCoverFinder = NULL;
|
|
}
|
|
|
|
if (m_pOrder)
|
|
{
|
|
m_pOrder->ClearAssignedToEntity();
|
|
}
|
|
}
|
|
|
|
void CPedIntelligence::SetDecisionMakerId(u32 uDecisionMakerId)
|
|
{
|
|
if(aiVerifyf(CEventDecisionMakerManager::GetIsDecisionMakerValid(uDecisionMakerId), "Decision maker with Id [%d] does not exist", uDecisionMakerId))
|
|
{
|
|
m_PedDecisionMaker.SetDecisionMakerId(uDecisionMakerId);
|
|
}
|
|
}
|
|
|
|
bool CPedIntelligence::FindRespectedFriendInInformRange()
|
|
{
|
|
bool bFoundFriendInRange=false;
|
|
|
|
u32 i=0;
|
|
const CEntityScannerIterator pedList = GetNearbyPeds();
|
|
const CEntity* pEntity = pedList.GetFirst();
|
|
const ScalarV svMaxInformDistanceSquared = ScalarVFromF32(m_fMaxInformFriendDistance*m_fMaxInformFriendDistance);
|
|
while(pEntity && i < m_uMaxNumFriendsToInform)
|
|
{
|
|
const CPed* pNextPed=static_cast<const CPed*>(pEntity);
|
|
i++;
|
|
|
|
const CRelationshipGroup* pRelGroup = m_pPed->GetPedIntelligence()->GetRelationshipGroup();
|
|
//if(nRespected & CPedType::GetPedFlag(pNextPed->GetPedType()))
|
|
if( pRelGroup && pRelGroup->CheckRelationship( ACQUAINTANCE_TYPE_PED_RESPECT, pNextPed->GetPedIntelligence()->GetRelationshipGroupIndex() ) )
|
|
{
|
|
if(IsLessThanAll(DistSquared(m_pPed->GetTransform().GetPosition(), pNextPed->GetTransform().GetPosition()), svMaxInformDistanceSquared))
|
|
{
|
|
bFoundFriendInRange=true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bFoundFriendInRange;
|
|
}
|
|
|
|
CEntityScannerIterator CPedIntelligence::GetNearbyObjects()
|
|
{
|
|
if(!m_pPed->GetPedAiLod().IsLodFlagSet(CPedAILod::AL_LodEntityScanning))
|
|
{
|
|
if(m_objectScanner.GetTimer().Tick())
|
|
{
|
|
m_objectScanner.GetTimer().SetCount(0);
|
|
m_objectScanner.ScanForEntitiesInRange(*m_pPed);
|
|
}
|
|
}
|
|
return m_objectScanner.GetIterator();
|
|
}
|
|
|
|
CObject* CPedIntelligence::GetClosestObjectInRange()
|
|
{
|
|
if(!m_pPed->GetPedAiLod().IsLodFlagSet(CPedAILod::AL_LodEntityScanning))
|
|
{
|
|
if(m_objectScanner.GetTimer().Tick())
|
|
{
|
|
m_objectScanner.GetTimer().SetCount(0);
|
|
m_objectScanner.ScanForEntitiesInRange(*m_pPed);
|
|
}
|
|
return m_objectScanner.GetClosestObjectInRange();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CEntityScannerIterator CPedIntelligence::GetNearbyVehicles()
|
|
{
|
|
if(!m_pPed->GetPedAiLod().IsLodFlagSet(CPedAILod::AL_LodEntityScanning))
|
|
{
|
|
if(m_vehicleScanner.GetTimer().Tick())
|
|
{
|
|
m_vehicleScanner.GetTimer().SetCount(0);
|
|
m_vehicleScanner.ScanForEntitiesInRange(*m_pPed);
|
|
}
|
|
}
|
|
return m_vehicleScanner.GetIterator();
|
|
}
|
|
|
|
CVehicle* CPedIntelligence::GetClosestVehicleInRange(bool bForce /*= false*/)
|
|
{
|
|
if(!m_pPed->GetPedAiLod().IsLodFlagSet(CPedAILod::AL_LodEntityScanning) || bForce)
|
|
{
|
|
if (m_vehicleScanner.GetTimer().Tick() || bForce)
|
|
{
|
|
m_vehicleScanner.GetTimer().SetCount(0);
|
|
m_vehicleScanner.ScanForEntitiesInRange(*m_pPed, bForce);
|
|
}
|
|
return m_vehicleScanner.GetClosestVehicleInRange();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void CPedIntelligence::ClearScanners()
|
|
{
|
|
m_vehicleScanner.Clear();
|
|
m_pedScanner.Clear();
|
|
m_objectScanner.Clear();
|
|
m_doorScanner.Clear();
|
|
}
|
|
|
|
bool CPedIntelligence::IsRespondingToEvent(const int iEventType) const
|
|
{
|
|
return GetEventHandler()->IsRespondingToEvent(iEventType);
|
|
}
|
|
|
|
// returns true if the ped's decision maker has a response to this event type (defined in EventData.h)
|
|
bool CPedIntelligence::HasResponseToEvent(int iEventType) const
|
|
{
|
|
return GetPedDecisionMaker().HandlesEventOfType(static_cast<eEventType>(iEventType));
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Determine if we can attack a specific ped
|
|
//-------------------------------------------------------------------------
|
|
bool CPedIntelligence::CanAttackPed(const CPed* pPed)
|
|
{
|
|
if( !pPed )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Don't attack any targets that have been marked as invalid targets
|
|
if( pPed->GetPedResetFlag(CPED_RESET_FLAG_CannotBeTargeted) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(!pPed->IsAPlayerPed())
|
|
{
|
|
CVehicle* pTargetVehicle = pPed->GetVehiclePedInside();
|
|
if( pTargetVehicle && pTargetVehicle->InheritsFromHeli() &&
|
|
GetCombatBehaviour().IsFlagSet(CCombatData::BF_DisableAimAtAITargetsInHelis) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool bOnlyAttackLawIfPlayerWanted = (pPed->IsLawEnforcementPed() && m_pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_OnlyAttackLawIfPlayerIsWanted));
|
|
if( bOnlyAttackLawIfPlayerWanted ||
|
|
(m_pPed->IsLawEnforcementPed() && (pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_LawWillOnlyAttackIfPlayerIsWanted) || m_pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_DontAttackPlayerWithoutWantedLevel))) )
|
|
{
|
|
CWanted* pTargetWantedLevel = pPed->GetPlayerWanted();
|
|
if(pTargetWantedLevel)
|
|
{
|
|
if( pTargetWantedLevel->GetWantedLevel() == WANTED_CLEAN ||
|
|
(bOnlyAttackLawIfPlayerWanted && pTargetWantedLevel->CopsAreSearching()) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Shuts down the peds targeting if it is active
|
|
//-------------------------------------------------------------------------
|
|
void CPedIntelligence::ShutdownTargetting( void )
|
|
{
|
|
if( m_pPedTargetting )
|
|
{
|
|
delete m_pPedTargetting;
|
|
m_pPedTargetting = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Updates the peds targetting system if active
|
|
//-------------------------------------------------------------------------
|
|
bool CPedIntelligence::UpdateTargetting(bool bDoFullUpdate)
|
|
{
|
|
PF_FUNC(AI_ProcessUpdateTargetting);
|
|
|
|
if( m_pPedTargetting )
|
|
{
|
|
// Updates the peds targetting system
|
|
// if it returns false, it is inactive and ready for
|
|
// destruction
|
|
Assert(CPedTargetting::GetPool()->IsValidPtr(m_pPedTargetting));
|
|
if( !m_pPedTargetting->UpdateTargetting(bDoFullUpdate) )
|
|
{
|
|
delete m_pPedTargetting;
|
|
m_pPedTargetting = NULL;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// returns the best target, activating the targetting system if it is disabled
|
|
//-------------------------------------------------------------------------
|
|
CPed* CPedIntelligence::GetBestTarget(void)
|
|
{
|
|
// Is this peds targetting system active,
|
|
// If not create now
|
|
if( !m_pPedTargetting )
|
|
{
|
|
m_pPedTargetting = rage_checked_pool_new(CPedTargetting) (m_pPed);
|
|
}
|
|
|
|
// Pool must be full return null, this forces the ped to stick with
|
|
// their default target
|
|
if( !m_pPedTargetting )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return m_pPedTargetting->GetBestTarget();
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Returns a pointer to the current target if targetting is active
|
|
//-------------------------------------------------------------------------
|
|
CPed* CPedIntelligence::GetCurrentTarget(void)
|
|
{
|
|
// Is the targetting system active,
|
|
if( !m_pPedTargetting )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return (CPed*)m_pPedTargetting->GetCurrentTarget();
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Adds a specific threat into the targetting system
|
|
//-------------------------------------------------------------------------
|
|
void CPedIntelligence::RegisterThreat(const CPed* pThreat, bool bTargetSeen )
|
|
{
|
|
if( m_pPed == pThreat )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Is this peds targetting system active,
|
|
// If not create now
|
|
if( !m_pPedTargetting )
|
|
{
|
|
m_pPedTargetting = rage_checked_pool_new(CPedTargetting) (m_pPed);
|
|
}
|
|
|
|
// Pool must be full, return null, this forces the ped to stick with
|
|
// their default target
|
|
if( !m_pPedTargetting )
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_pPedTargetting->RegisterThreat( (const CEntity*)pThreat, bTargetSeen );
|
|
|
|
//Assert( !IsFriendlyWith(*pThreat ) );
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Sets this peds default target scoring function
|
|
//-------------------------------------------------------------------------
|
|
void CPedIntelligence::SetDefaultTargetScoringFunction( CPedTargetting::TargetScoringFunction* pScoringFn )
|
|
{
|
|
// If the scoring function is currently set to the default,
|
|
// replace it with the new default
|
|
if( m_pPedTargetting )
|
|
{
|
|
if( m_pPedTargetting->GetScoringFunction() == m_pDefaultScoringFn )
|
|
{
|
|
m_pDefaultScoringFn = pScoringFn;
|
|
m_pPedTargetting->ResetScoringFunction();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_pDefaultScoringFn = pScoringFn;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Calculates how long it's been since we have been aimed at by a player
|
|
//-------------------------------------------------------------------------
|
|
float CPedIntelligence::GetTimeSinceLastAimedAtByPlayer() const
|
|
{
|
|
if(m_uLastAimedAtByPlayerTime > 0)
|
|
{
|
|
u32 timeSinceLastAimedAt = 0;
|
|
u32 currTime = fwTimer::GetTimeInMilliseconds();
|
|
|
|
// handle the time wrapping
|
|
if (currTime < m_uLastAimedAtByPlayerTime)
|
|
{
|
|
timeSinceLastAimedAt = (MAX_UINT32 - m_uLastAimedAtByPlayerTime) + currTime;
|
|
}
|
|
else
|
|
{
|
|
timeSinceLastAimedAt = currTime - m_uLastAimedAtByPlayerTime;
|
|
}
|
|
|
|
return ((float) timeSinceLastAimedAt / 1000.0f);
|
|
}
|
|
|
|
return LARGE_FLOAT;
|
|
}
|
|
|
|
void CPedIntelligence::UpdateEnclosedSearchRegions(bool)
|
|
{
|
|
bool inside = false;
|
|
if ( m_pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_UpdateEnclosedSearchRegion) )
|
|
{
|
|
static int s_NumPerFrame = 5;
|
|
int iNumPerFrame = Min(s_NumPerFrame, CDispatchHelperVolumes::GetTunables().m_EnclosedSearchRegions.m_Areas.GetCount());
|
|
int index = m_iEnclosedSearchVolumeCheckIndex;
|
|
int end = ( m_iEnclosedSearchVolumeCheckIndex + iNumPerFrame ) % CDispatchHelperVolumes::GetTunables().m_EnclosedSearchRegions.m_Areas.GetCount();
|
|
|
|
m_iEnclosedSearchVolumeCheckIndex = end;
|
|
while( index != end )
|
|
{
|
|
if ( CDispatchHelperVolumes::GetTunables().m_EnclosedSearchRegions.m_Areas[index].m_Area.IsPointInArea(VEC3V_TO_VECTOR3(m_pPed->GetTransform().GetPosition())) )
|
|
{
|
|
// start here next time
|
|
m_iEnclosedSearchVolumeCheckIndex = index;
|
|
inside = true;
|
|
break;
|
|
}
|
|
index = ( index + 1 ) % CDispatchHelperVolumes::GetTunables().m_EnclosedSearchRegions.m_Areas.GetCount();
|
|
}
|
|
}
|
|
m_pPed->SetPedResetFlag(CPED_RESET_FLAG_InsideEnclosedSearchRegion, inside);
|
|
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Updates the cover finder (if there is one)
|
|
//-------------------------------------------------------------------------
|
|
void CPedIntelligence::UpdateCoverFinder(bool bDoFullUpdate)
|
|
{
|
|
if(m_pCoverFinder)
|
|
{
|
|
// If the cover finder is no longer being used then we need to remove it
|
|
if(!m_pCoverFinder->IsReferenced() || m_pPed->IsInjured())
|
|
{
|
|
delete m_pCoverFinder;
|
|
m_pCoverFinder = NULL;
|
|
}
|
|
else if(m_pCoverFinder->IsActive())
|
|
{
|
|
// If we don't do a full update we still need to validate our cover points
|
|
if(!bDoFullUpdate)
|
|
{
|
|
m_pCoverFinder->ValidateCoverPoints();
|
|
return;
|
|
}
|
|
|
|
// If the player has aimed at us recently we should force an update
|
|
static float fMaxTimeForceCoverFinderUpdate = 1.0f;
|
|
if(GetTimeSinceLastAimedAtByPlayer() < fMaxTimeForceCoverFinderUpdate)
|
|
{
|
|
m_pCoverFinder->SetForceUpdateThisFrame();
|
|
}
|
|
|
|
// Is our ped visible in some view port?
|
|
if(m_pPed->GetIsVisibleInSomeViewportThisFrame())
|
|
{
|
|
// Grab our local player
|
|
CPed* pLocalPlayer = CGameWorld::FindLocalPlayer();
|
|
if(pLocalPlayer)
|
|
{
|
|
// Go through the player's nearby peds
|
|
CEntityScannerIterator entityList = pLocalPlayer->GetPedIntelligence()->GetNearbyPeds();
|
|
for(CEntity* pEntity = entityList.GetFirst(); pEntity; pEntity = entityList.GetNext())
|
|
{
|
|
CPed* pNearbyPed = static_cast<CPed*>(pEntity);
|
|
if(pNearbyPed)
|
|
{
|
|
// If we hit our ped then it means we are the closest visible ped so force an update
|
|
if(pNearbyPed == m_pPed)
|
|
{
|
|
m_pCoverFinder->SetForceUpdateThisFrame();
|
|
break;
|
|
}
|
|
else if(pNearbyPed->GetIsVisibleInSomeViewportThisFrame())
|
|
{
|
|
// If we hit a visible ped before we hit ourselves it means we aren't closest so don't force an update
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update our cover finder (internally figures out if an update is needed)
|
|
m_pCoverFinder->Update();
|
|
}
|
|
else if(m_pCoverFinder->GetLastSearchResult(false) != CCoverFinderFSM::SEARCH_INACTIVE)
|
|
{
|
|
UpdateDefensiveAreaFromCoverFinderResult(m_pCoverFinder->GetLastSearchResult(true));
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Gets the cover finder, wakes it up if needed
|
|
//-------------------------------------------------------------------------
|
|
CCoverFinder* CPedIntelligence::GetCoverFinder(void)
|
|
{
|
|
if(!m_pCoverFinder)
|
|
{
|
|
// Create our cover finder (this is pooled which is why it's a pointer)
|
|
if(CCoverFinder::GetPool()->GetNoOfFreeSpaces() > 0)
|
|
{
|
|
m_pCoverFinder = rage_new CCoverFinder();
|
|
}
|
|
|
|
// Assert on our cover finder, this should tell us we need to bump up the pool size
|
|
Assertf(m_pCoverFinder, "Cover finder pool ran out of spaces. Increase the value of CCoverFinder in gameconfig.xml");
|
|
}
|
|
|
|
return m_pCoverFinder;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Given a cover finding result it will update the defensive areas we are using currently and want to use for cover
|
|
//-------------------------------------------------------------------------
|
|
void CPedIntelligence::UpdateDefensiveAreaFromCoverFinderResult( s32 iCoverSearchResult )
|
|
{
|
|
if(iCoverSearchResult == CCoverFinderFSM::SEARCH_SUCCEEDED)
|
|
{
|
|
if(m_defensiveAreaManager.GetCurrentDefensiveArea() != m_defensiveAreaManager.GetDefensiveAreaForCoverSearch())
|
|
{
|
|
m_defensiveAreaManager.SetCurrentDefensiveArea(m_defensiveAreaManager.GetDefensiveAreaForCoverSearch());
|
|
}
|
|
}
|
|
else if(iCoverSearchResult == CCoverFinderFSM::SEARCH_FAILED)
|
|
{
|
|
// If we have a valid cover point then this would be an alternate cover search so don't swap our defensive areas
|
|
if(m_pPed->GetCoverPoint())
|
|
{
|
|
// If we're standing at our cover point but not using it then it means it's not safe and we should try looking in the other defensive area
|
|
if( GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_COMBAT) )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_defensiveAreaManager.SwapDefensiveAreaForCoverSearch();
|
|
}
|
|
}
|
|
|
|
CTask* CPedIntelligence::FindTaskByType(const int iType) const
|
|
{
|
|
CTask* p=0;
|
|
p=FindTaskDefaultByType(iType);
|
|
if(p)
|
|
{
|
|
return p;
|
|
}
|
|
|
|
p=FindTaskPrimaryByType(iType);
|
|
if(p)
|
|
{
|
|
return p;
|
|
}
|
|
|
|
p=FindTaskEventResponseByType(iType);
|
|
if(p)
|
|
{
|
|
return p;
|
|
}
|
|
|
|
p=FindTaskPhysicalResponseByType(iType);
|
|
if(p)
|
|
{
|
|
return p;
|
|
}
|
|
|
|
p=FindMovementTaskByType(iType);
|
|
if(p)
|
|
{
|
|
return p;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Returns a pointer to any movement task present of the given type
|
|
//-------------------------------------------------------------------------
|
|
CTask* CPedIntelligence::FindMovementTaskByType(const int iType) const
|
|
{
|
|
aiTask* p=0;
|
|
p=m_taskManager.FindTaskByTypeWithPriority(PED_TASK_TREE_MOVEMENT, iType, PED_TASK_MOVEMENT_EVENT_RESPONSE);
|
|
if(p)
|
|
{
|
|
return static_cast<CTask*>(p);
|
|
}
|
|
|
|
p=m_taskManager.FindTaskByTypeWithPriority(PED_TASK_TREE_MOVEMENT, iType, PED_TASK_MOVEMENT_GENERAL);
|
|
if(p)
|
|
{
|
|
return static_cast<CTask*>(p);
|
|
}
|
|
|
|
p=m_taskManager.FindTaskByTypeWithPriority(PED_TASK_TREE_MOVEMENT, iType, PED_TASK_MOVEMENT_DEFAULT);
|
|
if(p)
|
|
{
|
|
return static_cast<CTask*>(p);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if __ASSERT
|
|
bool CPedIntelligence::IsRunningRagdollTask()
|
|
{
|
|
bool bHandlesRagdoll = false;
|
|
bHandlesRagdoll = m_taskManager.HandlesRagdoll(PED_TASK_TREE_PRIMARY, m_pPed);
|
|
|
|
if (!bHandlesRagdoll)
|
|
bHandlesRagdoll = m_taskManager.HandlesRagdoll(PED_TASK_TREE_MOTION, m_pPed);
|
|
|
|
return bHandlesRagdoll;
|
|
}
|
|
#endif //__ASSERT
|
|
|
|
CTaskCombat* CPedIntelligence::GetTaskCombat() const
|
|
{
|
|
return static_cast<CTaskCombat*>(m_taskManager.FindTaskByTypeActive(PED_TASK_TREE_PRIMARY, CTaskTypes::TASK_COMBAT));
|
|
}
|
|
|
|
CTaskGun* CPedIntelligence::GetTaskGun() const
|
|
{
|
|
return static_cast<CTaskGun*>(m_taskManager.FindTaskByTypeActive(PED_TASK_TREE_PRIMARY, CTaskTypes::TASK_GUN));
|
|
}
|
|
|
|
CTaskCombat* CPedIntelligence::FindTaskCombat() const
|
|
{
|
|
return static_cast<CTaskCombat*>(FindTaskByType(CTaskTypes::TASK_COMBAT));
|
|
}
|
|
|
|
CTaskGun* CPedIntelligence::FindTaskGun() const
|
|
{
|
|
return static_cast<CTaskGun*>(FindTaskByType(CTaskTypes::TASK_GUN));
|
|
}
|
|
|
|
CTaskWrithe* CPedIntelligence::GetTaskWrithe() const
|
|
{
|
|
return static_cast<CTaskWrithe*>(m_taskManager.FindTaskByTypeActive(PED_TASK_TREE_PRIMARY, CTaskTypes::TASK_WRITHE));
|
|
}
|
|
|
|
CTaskMelee* CPedIntelligence::GetTaskMelee() const
|
|
{
|
|
return static_cast<CTaskMelee*>(m_taskManager.FindTaskByTypeActive(PED_TASK_TREE_PRIMARY, CTaskTypes::TASK_MELEE));
|
|
}
|
|
|
|
CTaskMeleeActionResult* CPedIntelligence::GetTaskMeleeActionResult() const
|
|
{
|
|
return static_cast<CTaskMeleeActionResult*>(m_taskManager.FindTaskByTypeActive(PED_TASK_TREE_PRIMARY, CTaskTypes::TASK_MELEE_ACTION_RESULT));
|
|
}
|
|
|
|
CTaskSmartFlee* CPedIntelligence::GetTaskSmartFlee() const
|
|
{
|
|
return static_cast<CTaskSmartFlee*>(m_taskManager.FindTaskByTypeActive(PED_TASK_TREE_PRIMARY, CTaskTypes::TASK_SMART_FLEE));
|
|
}
|
|
|
|
CTaskSmartFlee* CPedIntelligence::FindTaskSmartFlee() const
|
|
{
|
|
return static_cast<CTaskSmartFlee*>(FindTaskByType(CTaskTypes::TASK_SMART_FLEE));
|
|
}
|
|
|
|
CTaskSimpleMoveSwim *CPedIntelligence::GetTaskSwim() const
|
|
{
|
|
//CDE - Swimming is now handled by a custom move blender, not a task.
|
|
return NULL;
|
|
}
|
|
|
|
bool CPedIntelligence::IsPedSwimming() const
|
|
{
|
|
//CDE - we are no longer using a specific task for swimming, just a different move blender.
|
|
// - TODO: Ensure that this still works remotely by virtue of the isDrowning flag being synced.
|
|
return (m_pPed->GetIsSwimming());
|
|
}
|
|
|
|
bool CPedIntelligence::IsPedInAir() const
|
|
{
|
|
return m_pPed->GetPedResetFlag( CPED_RESET_FLAG_IsJumping );
|
|
}
|
|
|
|
bool CPedIntelligence::IsPedClimbing() const
|
|
{
|
|
return m_pPed->GetPedResetFlag( CPED_RESET_FLAG_IsClimbing );
|
|
}
|
|
|
|
bool CPedIntelligence::IsPedVaulting() const
|
|
{
|
|
return m_pPed->GetPedResetFlag( CPED_RESET_FLAG_IsVaulting );
|
|
}
|
|
|
|
bool CPedIntelligence::IsPedFalling() const
|
|
{
|
|
return m_pPed->GetPedResetFlag( CPED_RESET_FLAG_IsFalling );
|
|
}
|
|
|
|
bool CPedIntelligence::IsPedJumping() const
|
|
{
|
|
return m_pPed->GetPedResetFlag( CPED_RESET_FLAG_IsJumping );
|
|
}
|
|
|
|
bool CPedIntelligence::IsPedLanding() const
|
|
{
|
|
return m_pPed->GetPedResetFlag( CPED_RESET_FLAG_IsLanding);
|
|
}
|
|
|
|
bool CPedIntelligence::IsPedDiving() const
|
|
{
|
|
return m_pPed->GetPedResetFlag( CPED_RESET_FLAG_IsDiving );
|
|
}
|
|
|
|
bool CPedIntelligence::IsPedGettingUp() const
|
|
{
|
|
if( GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_BLEND_FROM_NM) ||
|
|
GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_FALL_OVER) ||
|
|
GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_GET_UP) ||
|
|
GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_FALL_AND_GET_UP) ||
|
|
GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_GET_UP_AND_STAND_STILL))
|
|
{
|
|
return true;
|
|
}
|
|
else if (FindTaskActiveMotionByType(CTaskTypes::TASK_GET_UP))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//dev_bool TREAT_SLIDING_INTO_COVER_AS_IN_COVER = true;
|
|
float CPedIntelligence::DISTANCE_TO_START_COVER_CAM_WHEN_SLIDING = 1.0f;
|
|
bool CPedIntelligence::GetPedCoverStatus( s32& iCoverState, bool& bCanFire ) const
|
|
{
|
|
iCoverState = -1;
|
|
bCanFire = false;
|
|
if( GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_IN_COVER) )
|
|
{
|
|
CTaskInCover* pCoverTask = (CTaskInCover*)FindTaskActiveByType(CTaskTypes::TASK_IN_COVER);
|
|
if( pCoverTask )
|
|
{
|
|
iCoverState = pCoverTask->GetState();
|
|
bCanFire = m_pPed->GetCoverPoint() != NULL;
|
|
}
|
|
return true;
|
|
}
|
|
// else if( TREAT_SLIDING_INTO_COVER_AS_IN_COVER )
|
|
// {
|
|
// // Only count as in cover near to the point for a smooth camera transition.
|
|
// if( m_pPed->GetCoverPoint() && GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_SLIDE_INTO_COVER) )
|
|
// {
|
|
// if( m_pPed->GetCoverPoint()->GetType() != CCoverPoint::COVTYPE_OBJECT )
|
|
// {
|
|
// Vector3 vCoverPos;
|
|
// if( m_pPed->GetCoverPoint()->GetCoverPointPosition(vCoverPos) )
|
|
// {
|
|
// if( (VEC3V_TO_VECTOR3(m_pPed->GetTransform().GetPosition())-vCoverPos).XYMag2() < rage::square(DISTANCE_TO_START_COVER_CAM_WHEN_SLIDING) )
|
|
// {
|
|
// return true;
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
return false;
|
|
}
|
|
|
|
bool CPedIntelligence::IsPedClimbingLadder() const
|
|
{
|
|
if(GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_CLIMB_LADDER))
|
|
return true;
|
|
|
|
if(GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_GO_TO_AND_CLIMB_LADDER))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CPedIntelligence::IsPedInMelee() const
|
|
{
|
|
if(GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_MELEE))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CPedIntelligence::IsPedBlindFiring() const
|
|
{
|
|
CTaskInCover* pCoverTask = static_cast<CTaskInCover*>(m_pPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_IN_COVER));
|
|
if (pCoverTask && ( pCoverTask->GetState() == CTaskInCover::State_BlindFiring ))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
CTaskClimbLadder* CPedIntelligence::GetTaskClimbLadder() const
|
|
{
|
|
aiTask* pTask = m_taskManager.GetActiveLeafTask(PED_TASK_TREE_PRIMARY);
|
|
if(pTask
|
|
&& pTask->GetTaskType()==CTaskTypes::TASK_CLIMB_LADDER)
|
|
return (CTaskClimbLadder *)pTask;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void CPedIntelligence::ClearPrimaryTask()
|
|
{
|
|
m_taskManager.SetTask(PED_TASK_TREE_PRIMARY, NULL, PED_TASK_PRIORITY_PRIMARY);
|
|
}
|
|
|
|
void CPedIntelligence::ClearSecondaryTask(s32 iType)
|
|
{
|
|
m_taskManager.SetTask(PED_TASK_TREE_SECONDARY, NULL, iType);
|
|
}
|
|
|
|
void CPedIntelligence::ClearPrimaryTaskAtPriority(s32 iPriority)
|
|
{
|
|
m_taskManager.ClearTask(PED_TASK_TREE_PRIMARY, iPriority);
|
|
}
|
|
|
|
void CPedIntelligence::ClearTasks(const bool bClearMainTask, const bool bClearSecondaryTask)
|
|
{
|
|
Assertf(!m_pPed->IsInjured(), "Ped was injured when ClearTasks was called!");
|
|
if(bClearMainTask)
|
|
{
|
|
bool bFailedToAbort = false;
|
|
|
|
// Shut down the targeting if active
|
|
ShutdownTargetting();
|
|
|
|
// First try and clear the main task tree by aborting all tasks, if this succeeds NULL all trees immediately.
|
|
const bool bInVehicle = m_pPed->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ) && (m_pPed->GetMyVehicle());
|
|
CEventGivePedTask makeAbortEvent(PED_TASK_PRIORITY_PRIMARY, rage_new CTaskDoNothing(0, bInVehicle?1:0));
|
|
|
|
int i;
|
|
for(i=0;i<PED_TASK_PRIORITY_MAX;i++)
|
|
{
|
|
aiTask* pTask=m_taskManager.GetTask(PED_TASK_TREE_PRIMARY, i);
|
|
if(pTask && pTask->MakeAbortable( aiTask::ABORT_PRIORITY_URGENT,&makeAbortEvent))
|
|
{
|
|
if( i != PED_TASK_PRIORITY_DEFAULT )
|
|
{
|
|
m_taskManager.ClearTask(PED_TASK_TREE_PRIMARY,i);
|
|
}
|
|
}
|
|
else if( pTask )
|
|
{
|
|
bFailedToAbort = true;
|
|
}
|
|
}
|
|
|
|
// The make abortable above failed as some tasks failed to quit, add an event so this can be processed next frame.
|
|
if( bFailedToAbort )
|
|
{
|
|
// Ends any tasks being performed on the ped and gives him the most basic task to do (stand still or sit in vehicle)
|
|
if ( (m_pPed->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle )) && (m_pPed->GetMyVehicle()) )
|
|
{
|
|
if(!m_eventGroup.HasScriptCommandOfTaskType(CTaskTypes::TASK_IN_VEHICLE_BASIC))
|
|
{
|
|
//Already got a default drive task so no need for a primary drive task. Just add a task that will
|
|
//finish immediately to ensure all tasks are removed in event handler.
|
|
CEventGivePedTask event(PED_TASK_PRIORITY_PRIMARY, rage_new CTaskDoNothing(0, 1));
|
|
AddEvent(event);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(!m_eventGroup.HasScriptCommandOfTaskType(CTaskTypes::TASK_DO_NOTHING))
|
|
{
|
|
//Clear everything by getting the ped to stand still.
|
|
CEventGivePedTask event(PED_TASK_PRIORITY_PRIMARY, rage_new CTaskDoNothing(0));
|
|
AddEvent(event);
|
|
}
|
|
}
|
|
}
|
|
|
|
//Update the record straight away in case the flush was called during a script process.
|
|
CPedScriptedTaskRecord::Process();
|
|
}
|
|
|
|
if(bClearSecondaryTask)
|
|
{
|
|
int i;
|
|
for(i=0;i<PED_TASK_SECONDARY_MAX;i++)
|
|
{
|
|
aiTask* pTask=m_taskManager.GetTask(PED_TASK_TREE_SECONDARY, i);
|
|
if(pTask && pTask->MakeAbortable( aiTask::ABORT_PRIORITY_URGENT,0))
|
|
{
|
|
m_taskManager.SetTask(PED_TASK_TREE_SECONDARY,0,i);
|
|
}
|
|
}
|
|
}
|
|
Assertf(!m_pPed->IsInjured(), "Ped is injured after ClearTasks was called!");
|
|
}
|
|
|
|
void CPedIntelligence::FlushImmediately(const bool bRestartDefaultTasks, const bool bAbortMotionTasks, const bool bAbortRagdoll, const bool bProcessTaskRecord)
|
|
{
|
|
// Ensure intelligence update next frame
|
|
m_pPed->GetPedAiLod().SetForceNoTimesliceIntelligenceUpdate(true);
|
|
|
|
// Shut down the targetting if active
|
|
ShutdownTargetting();
|
|
|
|
// cache the current anim sets here
|
|
CTaskMotionPed::CPersistentData data;
|
|
|
|
if (m_pPed->GetPrimaryMotionTask() && m_pPed->GetPrimaryMotionTask()->GetTaskType()==CTaskTypes::TASK_MOTION_PED)
|
|
{
|
|
CTaskMotionPed* pTask = static_cast<CTaskMotionPed*>(m_pPed->GetPrimaryMotionTask());
|
|
data.Init(*pTask);
|
|
}
|
|
|
|
//Store the facial task if there is one.
|
|
m_eventGroup.FlushAll();
|
|
GetEventHandler()->FlushImmediately();
|
|
|
|
if(bAbortMotionTasks)
|
|
{
|
|
m_taskManager.AbortTasks();
|
|
}
|
|
else
|
|
{
|
|
s32 numTrees = m_taskManager.GetTreeCount();
|
|
|
|
for(s32 treeIndex = 0; treeIndex < numTrees; treeIndex++)
|
|
{
|
|
aiTaskTree *taskTree = m_taskManager.GetTree(treeIndex);
|
|
if(taskTree && treeIndex != PED_TASK_TREE_MOTION)
|
|
{
|
|
taskTree->AbortTasks();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ragdoll will normally be cleared up by this point, but sometimes script can clear the ped
|
|
// tasks between physics activating the ragdoll and the task to handle it being run.
|
|
if (bAbortRagdoll && m_pPed->GetUsingRagdoll())
|
|
{
|
|
nmEntityDebugf(m_pPed, "CPedIntelligence::FlushImmediately switching to animated");
|
|
m_pPed->SwitchToAnimated();
|
|
}
|
|
|
|
// Make sure the ped is out of the car after a flush immediately!
|
|
u32 iFlags = CPed::PVF_IgnoreSettingJustLeftVehicle;
|
|
if(bRestartDefaultTasks == false)
|
|
iFlags |= CPed::PVF_DontResetDefaultTasks;
|
|
|
|
if (m_pPed->IsNetworkClone())
|
|
{
|
|
// if the ped is being removed from the vehicle via a task flush, he needs to be warped out
|
|
iFlags |= CPed::PVF_Warp;
|
|
|
|
// a bit of a hacky fix to get clone players to exit planes properly when being warped out (B* 1816633)
|
|
CVehicle* pPedVehicle = m_pPed->GetIsInVehicle() ? m_pPed->GetMyVehicle() : NULL;
|
|
|
|
if (pPedVehicle && pPedVehicle->GetIsAircraft() && pPedVehicle->IsInAir())
|
|
{
|
|
iFlags |= CPed::PVF_InheritVehicleVelocity;
|
|
iFlags |= CPed::PVF_NoCollisionUntilClear;
|
|
}
|
|
}
|
|
|
|
m_pPed->SetPedOutOfVehicle(iFlags);
|
|
m_pPed->SetPedOffMount();
|
|
//if I'm a ridable ped, clear my rider
|
|
if (m_pPed->GetSeatManager()) {
|
|
for (s32 seatId=0; seatId< m_pPed->GetSeatManager()->GetMaxSeats(); seatId++)
|
|
{
|
|
if (m_pPed->GetSeatManager()->GetPedInSeat(seatId))
|
|
{
|
|
m_pPed->GetSeatManager()->GetPedInSeat(seatId)->SetPedOffMount();
|
|
}
|
|
}
|
|
}
|
|
|
|
//Update the record straight away in case the flush was called during a script process.
|
|
if(bProcessTaskRecord)
|
|
{
|
|
CPedScriptedTaskRecord::Process();
|
|
}
|
|
|
|
if(bAbortMotionTasks)
|
|
{
|
|
//we always want to restart the primary motion task. If there isn't one we can't process physics on the ped.
|
|
m_pPed->StartPrimaryMotionTask();
|
|
|
|
// Set the default anim sets back on the ped
|
|
if ( m_pPed->GetPrimaryMotionTask() && m_pPed->GetPrimaryMotionTask()->GetTaskType()==CTaskTypes::TASK_MOTION_PED)
|
|
{
|
|
CTaskMotionPed* pTask = static_cast<CTaskMotionPed*>(m_pPed->GetPrimaryMotionTask());
|
|
data.Apply(*pTask);
|
|
}
|
|
|
|
m_pPed->SetBoundPitch(0);
|
|
|
|
#if __ASSERT
|
|
fwAttachmentEntityExtension *pPedAttachmentExtension = m_pPed->GetAttachmentExtension();
|
|
if(pPedAttachmentExtension)
|
|
{
|
|
pPedAttachmentExtension->SetAttachFlag(ATTACH_FLAG_DONT_ASSERT_ON_MATRIX_CHANGE, true); // Necessary to stop spurious assert in fwEntity::SetMatrix().
|
|
}
|
|
#endif // __ASSERT
|
|
m_pPed->SetPitch(0);
|
|
#if __ASSERT
|
|
if(pPedAttachmentExtension)
|
|
{
|
|
pPedAttachmentExtension->SetAttachFlag(ATTACH_FLAG_DONT_ASSERT_ON_MATRIX_CHANGE, false);
|
|
}
|
|
#endif // __ASSERT
|
|
m_pPed->GetMotionData()->Reset();
|
|
}
|
|
|
|
if(bRestartDefaultTasks)
|
|
{
|
|
|
|
AddTaskDefault(m_pPed->ComputeDefaultTask(*m_pPed));
|
|
|
|
// this can happen in a network game if CNetObjPed::CanSetTask returns false
|
|
Assertf(GetTaskDefault() != 0, "Unable to restart default task for a ped!");
|
|
// if(m_pPed->IsPlayer())
|
|
// {
|
|
// // might be that they should return to ski, so now they return to default player control task for their moveblend class
|
|
// AddTaskDefault(m_pPed->GetMoveBlender()->CreatePlayerControlTask() );
|
|
// }
|
|
// else if(m_pPed->PopTypeIsMission())
|
|
// {
|
|
// AddTaskDefault(new CTaskSimpleStandStill(0,true));
|
|
// }
|
|
// else
|
|
// {
|
|
// AddTaskDefault(m_pPed->ComputeWanderTask(*m_pPed));
|
|
// }
|
|
}
|
|
|
|
if (!m_pPed->IsNetworkClone()) // the queriable state is dictated by the machine which owns the ped
|
|
{
|
|
GetTaskManager()->SetTask(PED_TASK_TREE_MOVEMENT, rage_new CTaskMoveStandStill(), PED_TASK_MOVEMENT_DEFAULT);
|
|
|
|
BuildQueriableState();
|
|
}
|
|
|
|
m_fDriverAbilityOverride = -1.0f;
|
|
m_fDriverAggressivenessOverride = -1.0f;
|
|
m_fDriverRacingModifier = s_fDefaultDriverRacingModifier;
|
|
}
|
|
|
|
void CPedIntelligence::ClearTasksAbovePriority(const s32 iPriority)
|
|
{
|
|
aiTaskTree *taskTree = m_taskManager.GetTree(PED_TASK_TREE_PRIMARY);
|
|
taskTree->AbortTasksAbovePriority(iPriority);
|
|
}
|
|
|
|
void
|
|
CPedIntelligence::FlushEvents(void)
|
|
{
|
|
m_eventGroup.FlushAll();
|
|
GetEventHandler()->FlushImmediately();
|
|
}
|
|
|
|
|
|
void CPedIntelligence::BuildQueriableState()
|
|
{
|
|
PF_FUNC(AI_BuildQueriableState);
|
|
|
|
aiTaskTree *taskTree = m_taskManager.GetTree(PED_TASK_TREE_PRIMARY);
|
|
if(taskTree->IsCloneTree())
|
|
{
|
|
gnetDebug1("%s skipping BuildQueriableState because of clone tree", m_pPed->GetLogName());
|
|
return;
|
|
}
|
|
|
|
Assert(dynamic_cast<CTaskTreePed*>(taskTree));
|
|
CTaskTreePed *taskTreePed = (static_cast<CTaskTreePed*>(taskTree));
|
|
|
|
// Write out the current main tasks to the queriable interface
|
|
CQueriableInterface* pQueriableInterface = GetQueriableInterface();
|
|
taskTreePed->WriteTasksToQueriableInterface( pQueriableInterface );
|
|
|
|
pQueriableInterface->UpdateCachedInfo();
|
|
|
|
NetworkInterface::OnQueriableStateBuilt(*m_pPed);
|
|
}
|
|
|
|
// The default climb strength regeneration rate
|
|
dev_float CPedIntelligence::DEFAULT_CLIMB_STRENGTH_RATE = 0.2f;
|
|
|
|
// How fast the local player must be traveling in a vehicle for peds with a response to encroachment
|
|
// to scan every frame.
|
|
dev_float CPedIntelligence::ms_fFrequentScanVehicleVelocitySquaredThreshold = 64.0;
|
|
|
|
// Alertness threshold
|
|
const u8 CPedIntelligence::ALERTNESS_RESPONSE_THRESHOLD = 10;
|
|
|
|
// Distance to inform params
|
|
const u8 CPedIntelligence::MAX_NUM_FRIENDS_TO_INFORM = 3u;
|
|
const float CPedIntelligence::MAX_INFORM_FRIEND_DISTANCE = 50.0f;
|
|
|
|
const u8 CPedIntelligence::MAX_NUM_FRIENDS_TO_INFORM_MAX_VALUE = 255;
|
|
const int CPedIntelligence::MAX_NUM_FRIENDS_TO_INFORM_NUM_BITS = 8;
|
|
|
|
const float CPedIntelligence::MAX_INFORM_FRIEND_DISTANCE_MAX_VALUE = 1000.0f;
|
|
const s32 CPedIntelligence::MAX_INFORM_FRIEND_DISTANCE_NUM_BITS = 16;
|
|
|
|
void CPedIntelligence::Process(bool fullUpdate, float fOverrideTimeStep)
|
|
{
|
|
#if GTA_REPLAY
|
|
if(CReplayMgr::IsEditModeActive())
|
|
{
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
PF_FUNC(AI_Process);
|
|
|
|
// Debug update
|
|
DEV_ONLY(Process_Debug();)
|
|
|
|
const float dt = fOverrideTimeStep >= 0.0f ? fOverrideTimeStep : fwTimer::GetTimeStep();
|
|
Assert(dt >= 0.0f);
|
|
const float fTimeslicedTimeStep = m_fTimeSinceLastAiUpdate + dt;
|
|
m_fTimeSinceLastAiUpdate = fTimeslicedTimeStep;
|
|
|
|
if(fullUpdate)
|
|
{
|
|
m_pPed->SetPedConfigFlag(CPED_CONFIG_FLAG_LowPhysicsLodMayPlaceOnNavMesh, false);
|
|
|
|
#if __BANK
|
|
if(CPedAILodManager::ms_bDisplayFullUpdatesLines || CPedAILodManager::ms_bDisplayFullUpdatesSpheres)
|
|
{
|
|
CPedAILodManager::DrawFullUpdate(*m_pPed);
|
|
}
|
|
#endif
|
|
|
|
// Update climb scanner
|
|
GetClimbDetector().Process();
|
|
}
|
|
|
|
// Maintain lists of nearby entities
|
|
Process_NearbyEntityLists();
|
|
|
|
if(fullUpdate)
|
|
{
|
|
// Update any CPedIntelligence variables pre task update
|
|
Process_UpdateVariables(fTimeslicedTimeStep);
|
|
}
|
|
|
|
//Compute all events.
|
|
GetEventScanner()->ScanForEvents(*m_pPed, fullUpdate);
|
|
|
|
//Update decision maker
|
|
UpdatePedDecisionMaker(fullUpdate);
|
|
|
|
// Update the targeting system
|
|
UpdateTargetting(fullUpdate);
|
|
|
|
// Update the cover finder
|
|
UpdateCoverFinder(fullUpdate);
|
|
|
|
// update enclosed search regions
|
|
UpdateEnclosedSearchRegions(fullUpdate);
|
|
|
|
if(fullUpdate)
|
|
{
|
|
|
|
//Handle the events by computing event response tasks.
|
|
aiEvent::BeginEventUpdates(fTimeslicedTimeStep);
|
|
GetEventHandler()->HandleEvents();
|
|
aiEvent::EndEventUpdates();
|
|
|
|
// Update our defensive area information before task update
|
|
GetDefensiveAreaManager()->UpdateDefensiveAreas();
|
|
|
|
// reset our single frame shoot rate modifier
|
|
GetCombatBehaviour().ResetSingleFrameShootRateModifier();
|
|
}
|
|
|
|
// Update firing pattern
|
|
bool bUpdateFiringPattern = GetFiringPattern().ShouldProcess(*m_pPed);
|
|
|
|
if(bUpdateFiringPattern)
|
|
{
|
|
// Update the firing pattern before the task update
|
|
GetFiringPattern().PreProcess(*m_pPed);
|
|
}
|
|
|
|
// Update the AI tasks
|
|
Process_Tasks(fullUpdate, fTimeslicedTimeStep);
|
|
|
|
if(fullUpdate)
|
|
{
|
|
// Update any CPedIntelligence variables after task update
|
|
Process_UpdateVariablesAfterTaskProcess(fTimeslicedTimeStep);
|
|
|
|
// Build this peds queriable state after the task update
|
|
BuildQueriableState();
|
|
}
|
|
|
|
if(bUpdateFiringPattern)
|
|
{
|
|
// Update the firing pattern after the task update
|
|
GetFiringPattern().PostProcess(fwTimer::GetTimeStep());
|
|
}
|
|
|
|
if(fullUpdate)
|
|
{
|
|
// Update the peds perception
|
|
m_pedPerception.Update();
|
|
|
|
// Update the peds stealth status
|
|
m_pedStealth.Update(fTimeslicedTimeStep);
|
|
}
|
|
|
|
// Update the peds motivation
|
|
m_pedMotivation.Update();
|
|
|
|
#if LAZY_RAGDOLL_BOUNDS_UPDATE
|
|
// Check for some more conditions that should trigger a bounds update
|
|
Process_BoundUpdateRequest();
|
|
#endif
|
|
|
|
if(fullUpdate)
|
|
{
|
|
m_fTimeSinceLastAiUpdate = 0.0f;
|
|
}
|
|
}
|
|
|
|
bool CPedIntelligence::ProcessPhysics(float fTimeStep, int nTimeSlice, bool nmTasksOnly)
|
|
{
|
|
PF_FUNC(AI_ProcessPhysics);
|
|
|
|
Assert(m_taskManager.GetTreeCount() == PED_TASK_TREE_MAX);
|
|
CompileTimeAssert(PED_TASK_TREE_MAX == 4);
|
|
|
|
bool ret = false;
|
|
|
|
// Only call Ped Intelligence Process Physics when necessary.
|
|
CPed* pPed = m_pPed;
|
|
if(pPed->GetPedResetFlag(CPED_RESET_FLAG_ProcessPhysicsTasks) || pPed->GetPedResetFlag(CPED_RESET_FLAG_ProcessPhysicsTasksTimeSliced))
|
|
{
|
|
ret |= static_cast<CTaskTree*>(m_taskManager.GetTree(PED_TASK_TREE_PRIMARY))->ProcessPhysics(fTimeStep, nTimeSlice, nmTasksOnly);
|
|
|
|
// Probably not needed, since we don't run a whole lot of tasks on the secondary tree, and none of them
|
|
// appear to currently be using ProcessPhysics(). If needed, we could either use the same flag, or add a new one.
|
|
// ret |= static_cast<CTaskTree*>(m_taskManager.GetTree(PED_TASK_TREE_SECONDARY))->ProcessPhysics(fTimeStep, nTimeSlice, nmTasksOnly);
|
|
}
|
|
if(pPed->GetPedResetFlag(CPED_RESET_FLAG_ProcessPhysicsTasksMovement))
|
|
{
|
|
ret |= static_cast<CTaskTree*>(m_taskManager.GetTree(PED_TASK_TREE_MOVEMENT))->ProcessPhysics(fTimeStep, nTimeSlice, nmTasksOnly);
|
|
}
|
|
if(pPed->GetPedResetFlag(CPED_RESET_FLAG_ProcessPhysicsTasksMotion))
|
|
{
|
|
ret |= static_cast<CTaskTree*>(m_taskManager.GetTree(PED_TASK_TREE_MOTION))->ProcessPhysics(fTimeStep, nTimeSlice, nmTasksOnly);
|
|
}
|
|
|
|
// The code below may help to track down possible problems with the ProcessPhysics reset flags.
|
|
// For example, if a motion task has a ProcessPhysics() function that potentially did something
|
|
// because some other task set CPED_RESET_FLAG_ProcessPhysicsTasks.
|
|
#if 0
|
|
if(pPed->GetPedResetFlag(CPED_RESET_FLAG_ProcessPhysicsTasks || pPed->GetPedResetFlag(CPED_RESET_FLAG_ProcessPhysicsTasksTimeSliced))
|
|
|| pPed->GetPedResetFlag(CPED_RESET_FLAG_ProcessPhysicsTasksMovement)
|
|
|| pPed->GetPedResetFlag(CPED_RESET_FLAG_ProcessPhysicsTasksMotion))
|
|
{
|
|
bool ret1 = static_cast<CTaskTree*>(m_taskManager.GetTree(PED_TASK_TREE_PRIMARY))->ProcessPhysics(fTimeStep, nTimeSlice, nmTasksOnly);
|
|
Assert(!ret1 || pPed->GetPedResetFlag(CPED_RESET_FLAG_ProcessPhysicsTasks) || pPed->GetPedResetFlag(CPED_RESET_FLAG_ProcessPhysicsTasksTimeSliced));
|
|
bool ret1b = static_cast<CTaskTree*>(m_taskManager.GetTree(PED_TASK_TREE_SECONDARY))->ProcessPhysics(fTimeStep, nTimeSlice, nmTasksOnly);
|
|
Assert(!ret1b);
|
|
bool ret2 = static_cast<CTaskTree*>(m_taskManager.GetTree(PED_TASK_TREE_MOVEMENT))->ProcessPhysics(fTimeStep, nTimeSlice, nmTasksOnly);
|
|
Assert(!ret2 || pPed->GetPedResetFlag(CPED_RESET_FLAG_ProcessPhysicsTasksMovement));
|
|
bool ret3 = static_cast<CTaskTree*>(m_taskManager.GetTree(PED_TASK_TREE_MOTION))->ProcessPhysics(fTimeStep, nTimeSlice, nmTasksOnly);
|
|
Assert(!ret3 || pPed->GetPedResetFlag(CPED_RESET_FLAG_ProcessPhysicsTasksMotion));
|
|
|
|
ret = ret1 | ret1b | ret2 | ret3;
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
#if __BANK
|
|
EXTERN_PARSER_ENUM(ePedResetFlags);
|
|
#endif // __BANK
|
|
|
|
void CPedIntelligence::ProcessPostPhysics()
|
|
{
|
|
PF_FUNC(AI_ProcessPostPhysics);
|
|
|
|
m_pPed->m_PedResetFlags.ResetPostPhysics(m_pPed);
|
|
|
|
#if __BANK
|
|
if (CPedDebugVisualiserMenu::ms_bDebugPostPhysicsResetFlagActive)
|
|
{
|
|
CEntity* pEntity = CDebugScene::FocusEntities_Get(0);
|
|
|
|
if (!pEntity || !pEntity->GetIsTypePed())
|
|
{
|
|
pEntity = FindPlayerPed();
|
|
}
|
|
|
|
if (pEntity == m_pPed)
|
|
{
|
|
for(int i = 0; i < PARSER_ENUM(ePedResetFlags).m_NumEnums; i++)
|
|
{
|
|
if (CPedDebugVisualiserMenu::ms_DebugPedPostPhysicsResetFlagsBitSet.BitSet().IsSet(i))
|
|
{
|
|
m_pPed->SetPedResetFlag((ePedResetFlags)i, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // __BANK
|
|
|
|
if (m_pPed->GetPlayerInfo())
|
|
{
|
|
m_pPed->GetPlayerInfo()->GetPlayerResetFlags().ResetPostPhysics();
|
|
m_pPed->GetPlayerInfo()->SetVehiclePlayerPreferRearSeat(NULL);
|
|
m_pPed->GetPlayerInfo()->SetVehiclePlayerPreferFrontPassengerSeat(NULL);
|
|
}
|
|
}
|
|
|
|
// needs processed AFTER ProcessCollision loop
|
|
void CPedIntelligence::ProcessPostMovement()
|
|
{
|
|
PF_FUNC(AI_ProcessPostMovement);
|
|
|
|
if (m_pPed->IsLocalPlayer())
|
|
{
|
|
m_pPed->GetPlayerInfo()->ProcessPostMovement();
|
|
}
|
|
|
|
// do positioning of peds relative to vehicles here
|
|
if (!m_pPed->GetUsingRagdoll())
|
|
{
|
|
m_taskManager.ProcessPostMovement(PED_TASK_TREE_PRIMARY);
|
|
m_taskManager.ProcessPostMovement(PED_TASK_TREE_MOTION);
|
|
}
|
|
}
|
|
|
|
// needs processed AFTER camManager::Update
|
|
void CPedIntelligence::ProcessPostCamera()
|
|
{
|
|
PF_FUNC(AI_ProcessPostCamera);
|
|
|
|
m_taskManager.ProcessPostCamera(PED_TASK_TREE_PRIMARY);
|
|
m_taskManager.ProcessPostCamera(PED_TASK_TREE_SECONDARY);
|
|
m_taskManager.ProcessPostCamera(PED_TASK_TREE_MOTION);
|
|
}
|
|
|
|
void CPedIntelligence::ProcessPreRender2()
|
|
{
|
|
PF_FUNC(AI_ProcessPreRender2);
|
|
|
|
m_taskManager.ProcessPreRender2(PED_TASK_TREE_PRIMARY);
|
|
m_taskManager.ProcessPreRender2(PED_TASK_TREE_SECONDARY);
|
|
m_taskManager.ProcessPreRender2(PED_TASK_TREE_MOTION);
|
|
}
|
|
|
|
// needs processed AFTER PreRender (so ped's matrices have been calculated)
|
|
void CPedIntelligence::ProcessPostPreRender()
|
|
{
|
|
PF_FUNC(AI_ProcessPostPreRender);
|
|
|
|
m_taskManager.ProcessPostPreRender(PED_TASK_TREE_PRIMARY);
|
|
m_taskManager.ProcessPostPreRender(PED_TASK_TREE_MOTION);
|
|
}
|
|
|
|
// needs to be processed AFTER attachments
|
|
void CPedIntelligence::ProcessPostPreRenderAfterAttachments()
|
|
{
|
|
PF_FUNC(AI_ProcessPostPreRenderAfterAttachments);
|
|
|
|
m_taskManager.ProcessPostPreRenderAfterAttachments(PED_TASK_TREE_PRIMARY);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Sets the ped's default relationship group
|
|
//-------------------------------------------------------------------------
|
|
void CPedIntelligence::SetRelationshipGroupDefault(CRelationshipGroup* pRelationshipGroup)
|
|
{
|
|
aiAssertf(pRelationshipGroup, "The relationship group is invalid.");
|
|
m_pRelationshipGroupDefault = pRelationshipGroup;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Sets the ped's relationship group
|
|
//-------------------------------------------------------------------------
|
|
void CPedIntelligence::SetRelationshipGroup(CRelationshipGroup* pRelationshipGroup)
|
|
{
|
|
if(aiVerifyf(pRelationshipGroup, "The relationship group is invalid."))
|
|
{
|
|
const int index = pRelationshipGroup->GetIndex();
|
|
Assertf(index >= 0 && index < MAX_RELATIONSHIP_GROUPS, "Rel group %s has an index of %d", pRelationshipGroup->GetName().TryGetCStr(), index);
|
|
m_uRelationshipGroupIndex = (u16)index;
|
|
}
|
|
m_pRelationshipGroup = pRelationshipGroup;
|
|
}
|
|
|
|
CRelationshipGroup* CPedIntelligence::GetRelationshipGroup() const
|
|
{
|
|
//Check if the relationship group is valid.
|
|
if(aiVerifyf(m_pRelationshipGroup, "The relationship group is invalid."))
|
|
{
|
|
return m_pRelationshipGroup;
|
|
}
|
|
|
|
//Check if the default relationship group is valid.
|
|
if(aiVerifyf(m_pRelationshipGroupDefault, "The default relationship group is invalid."))
|
|
{
|
|
return m_pRelationshipGroupDefault;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
int CPedIntelligence::GetRelationshipGroupIndex() const
|
|
{
|
|
Assertf(m_uRelationshipGroupIndex == m_pRelationshipGroup->GetIndex(), "\nCached relationship group index (%d) doesn't match current index (%d) of relationship group %s\n",
|
|
m_uRelationshipGroupIndex, m_pRelationshipGroup->GetIndex(), m_pRelationshipGroup->GetName().TryGetCStr());
|
|
|
|
return m_uRelationshipGroupIndex;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Sets the peds default relationship group depending on the ped type
|
|
//-------------------------------------------------------------------------
|
|
void CPedIntelligence::SetDefaultRelationshipGroup()
|
|
{
|
|
// Force player controlled peds groups
|
|
if(m_pPed->IsControlledByLocalOrNetworkPlayer() || CPedType::IsSinglePlayerType(m_pPed->GetPedType()))
|
|
{
|
|
SetRelationshipGroupDefault(CRelationshipManager::s_pPlayerGroup);
|
|
SetRelationshipGroup(CRelationshipManager::s_pPlayerGroup);
|
|
}
|
|
else if(m_pPed->GetPedType() == PEDTYPE_COP || m_pPed->GetPedType() == PEDTYPE_SWAT)
|
|
{
|
|
SetRelationshipGroupDefault(CRelationshipManager::s_pCopGroup);
|
|
SetRelationshipGroup(CRelationshipManager::s_pCopGroup);
|
|
}
|
|
else if(m_pPed->GetPedType() == PEDTYPE_ARMY)
|
|
{
|
|
SetRelationshipGroupDefault(CRelationshipManager::s_pArmyGroup);
|
|
SetRelationshipGroup(CRelationshipManager::s_pArmyGroup);
|
|
}
|
|
else if(m_pPed->PopTypeIsMission())
|
|
{
|
|
SetRelationshipGroupDefault(CRelationshipManager::s_pNoRelationshipGroup);
|
|
SetRelationshipGroup(CRelationshipManager::s_pNoRelationshipGroup);
|
|
}
|
|
else
|
|
{
|
|
bool isRelGroupValid = false;
|
|
if(Verifyf(m_pPed->IsArchetypeSet(), "Peds model index is not valid, falling back to default %s", m_pRelationshipGroupDefault->GetName().GetCStr()))
|
|
{
|
|
CPedModelInfo* pModelInfo = m_pPed->GetPedModelInfo();
|
|
if(Verifyf(pModelInfo, "Peds model info not found, falling back to default relationship %s", m_pRelationshipGroupDefault->GetName().GetCStr()))
|
|
{
|
|
CRelationshipGroup* pRelationshipGroup = CRelationshipManager::FindRelationshipGroup(pModelInfo->GetRelationshipGroupHash());
|
|
if(Verifyf(pRelationshipGroup, "%s Relationship group not found, falling back to default %s", pModelInfo->GetRelationshipGroupHash().GetCStr(), m_pRelationshipGroupDefault->GetName().GetCStr()))
|
|
{
|
|
isRelGroupValid = true;
|
|
SetRelationshipGroupDefault(pRelationshipGroup);
|
|
SetRelationshipGroup(pRelationshipGroup);
|
|
}
|
|
}
|
|
}
|
|
|
|
// We'll fall back to the old way of setting the relationship group if the one in the model info is invalid
|
|
if(!isRelGroupValid)
|
|
{
|
|
SetRelationshipGroup(m_pRelationshipGroupDefault);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPedIntelligence::FixRelationshipGroup()
|
|
{
|
|
//Ensure the relationship group is invalid.
|
|
if(m_pRelationshipGroup)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Set the default relationship group.
|
|
SetDefaultRelationshipGroup();
|
|
}
|
|
|
|
bool CPedIntelligence::IsFriendlyWithEntity(const CEntity* pEntity, bool bIncludeFriendsDueToScenarios, bool bDebug /*= false*/) const
|
|
{
|
|
if (!pEntity)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (pEntity->GetIsTypePed())
|
|
{
|
|
const CPed* pPed = SafeCast(const CPed, pEntity);
|
|
return IsFriendlyWith(*pPed, bDebug) || (bIncludeFriendsDueToScenarios && IsFriendlyWithDueToScenarios(*pPed));
|
|
}
|
|
else if (pEntity->GetIsTypeVehicle())
|
|
{
|
|
const CVehicle* pVehicle = SafeCast(const CVehicle, pEntity);
|
|
return IsFriendlyWithVehicleOccupants(*pVehicle, bIncludeFriendsDueToScenarios, bDebug);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CPedIntelligence::IsFriendlyWithVehicleOccupants(const CVehicle& rVehicle, bool bIncludeFriendsDueToScenarios, bool bDebug /*= false*/) const
|
|
{
|
|
const CSeatManager* pSeatManager = rVehicle.GetSeatManager();
|
|
const int iNumSeats = pSeatManager ? pSeatManager->GetMaxSeats() : 0;
|
|
|
|
bool bHasOccupants = false;
|
|
bool bAllOcuppantsFriendly = true;
|
|
for(int iSeat = 0; bAllOcuppantsFriendly && iSeat < iNumSeats; iSeat++)
|
|
{
|
|
CPed *pOccupantPed = CTaskVehicleFSM::GetPedInOrUsingSeat(rVehicle, iSeat);
|
|
if (pOccupantPed)
|
|
{
|
|
bHasOccupants = true;
|
|
bAllOcuppantsFriendly = IsFriendlyWith(*pOccupantPed, bDebug) || (bIncludeFriendsDueToScenarios && IsFriendlyWithDueToScenarios(*pOccupantPed));
|
|
}
|
|
}
|
|
|
|
return bHasOccupants && bAllOcuppantsFriendly;
|
|
}
|
|
|
|
bool CPedIntelligence::IsFriendlyWith(const CPed& otherPed, bool OUTPUT_ONLY(bDebug)) const
|
|
{
|
|
Assert(m_pPed->GetPedIntelligence() != NULL);
|
|
Assert(otherPed.GetPedIntelligence() != NULL);
|
|
|
|
//Ensure the ped is not the friendly exception.
|
|
if(m_pFriendlyException == &otherPed)
|
|
{
|
|
#if !__NO_OUTPUT
|
|
if ((Channel_weapon.FileLevel >= DIAG_SEVERITY_DEBUG3) || (Channel_weapon.TtyLevel >= DIAG_SEVERITY_DEBUG3))
|
|
{
|
|
if (bDebug)
|
|
{
|
|
weaponDebugf3("CPedIntelligence::IsFriendlyWith--(m_pFriendlyException == &otherPed)-->return false");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
const CRelationshipGroup* pRelGroup = m_pPed->GetPedIntelligence()->GetRelationshipGroup();
|
|
const int otherRelationshipGroupIndex = otherPed.GetPedIntelligence()->GetRelationshipGroupIndex();
|
|
Assert(pRelGroup);
|
|
Assert(otherPed.GetPedIntelligence()->GetRelationshipGroup());
|
|
bool bFriendly = pRelGroup && (
|
|
pRelGroup->CheckRelationship( ACQUAINTANCE_TYPE_PED_RESPECT, otherRelationshipGroupIndex ) ||
|
|
pRelGroup->CheckRelationship( ACQUAINTANCE_TYPE_PED_LIKE, otherRelationshipGroupIndex )||
|
|
(m_pPed->GetPedsGroup() && (m_pPed->GetPedsGroup() == otherPed.GetPedsGroup())));
|
|
|
|
// Now check against ped types assuming neither ped has been set to ignore ped types for is friendly with checks
|
|
if(!bFriendly && !m_pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_IgnorePedTypeForIsFriendlyWith) && !otherPed.GetPedConfigFlag(CPED_CONFIG_FLAG_IgnorePedTypeForIsFriendlyWith))
|
|
{
|
|
bFriendly = (m_pPed->GetPedType() == PEDTYPE_MEDIC && otherPed.GetPedType() == PEDTYPE_MEDIC) ||
|
|
(m_pPed->GetPedType() == PEDTYPE_FIRE && otherPed.GetPedType() == PEDTYPE_FIRE) ||
|
|
(m_pPed->GetPedType() == PEDTYPE_COP && otherPed.GetPedType() == PEDTYPE_COP);
|
|
}
|
|
|
|
#if !__NO_OUTPUT
|
|
if ((Channel_weapon.FileLevel >= DIAG_SEVERITY_DEBUG3) || (Channel_weapon.TtyLevel >= DIAG_SEVERITY_DEBUG3))
|
|
{
|
|
if (bDebug)
|
|
{
|
|
weaponDebugf3("CPedIntelligence::IsFriendlyWith--m_pPed.GroupIndex[%d] otherPed.GetGroundIndex[%d] RESPECT[%d] LIKE[%d] SAMEPEDGROUP[%d] MEDIC[%d] FIRE[%d] COP[%d]-->return bFriendly[%d]",
|
|
m_pPed->GetPedsGroup() ? m_pPed->GetPedsGroup()->GetGroupIndex() : -9,
|
|
otherPed.GetPedsGroup() ? otherPed.GetPedsGroup()->GetGroupIndex() : -9,
|
|
pRelGroup ? pRelGroup->CheckRelationship( ACQUAINTANCE_TYPE_PED_RESPECT, otherRelationshipGroupIndex ) : 0,
|
|
pRelGroup ? pRelGroup->CheckRelationship( ACQUAINTANCE_TYPE_PED_LIKE, otherRelationshipGroupIndex ) : 0,
|
|
(m_pPed->GetPedsGroup() && (m_pPed->GetPedsGroup() == otherPed.GetPedsGroup())),
|
|
(m_pPed->GetPedType() == PEDTYPE_MEDIC && otherPed.GetPedType() == PEDTYPE_MEDIC),
|
|
(m_pPed->GetPedType() == PEDTYPE_FIRE && otherPed.GetPedType() == PEDTYPE_FIRE),
|
|
(m_pPed->GetPedType() == PEDTYPE_COP && otherPed.GetPedType() == PEDTYPE_COP),
|
|
bFriendly);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return bFriendly;
|
|
|
|
}
|
|
|
|
bool CPedIntelligence::IsFriendlyWithByAnyMeans(const CPed& rOtherPed) const
|
|
{
|
|
return (IsFriendlyWith(rOtherPed) || IsFriendlyWithDueToScenarios(rOtherPed) || IsFriendlyWithDueToSameVehicle(rOtherPed));
|
|
}
|
|
|
|
bool CPedIntelligence::IsFriendlyWithDueToSameVehicle(const CPed& rOtherPed) const
|
|
{
|
|
//Ensure the peds are random.
|
|
if(!m_pPed->PopTypeIsRandom() || !rOtherPed.PopTypeIsRandom())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the vehicle is valid.
|
|
if(!m_pPed->GetMyVehicle() || (m_pPed->GetMyVehicle() != rOtherPed.GetMyVehicle()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CPedIntelligence::IsFriendlyWithDueToScenarios(const CPed& rOtherPed) const
|
|
{
|
|
//Ensure the peds are random.
|
|
if(!m_pPed->PopTypeIsRandom() || !rOtherPed.PopTypeIsRandom())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the last used scenario point is valid.
|
|
const CScenarioPoint* pLastUsedScenarioPoint = m_pLastUsedScenarioPoint;
|
|
if(!pLastUsedScenarioPoint)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure the other last used scenario point is valid.
|
|
const CScenarioPoint* pOtherLastUsedScenarioPoint = rOtherPed.GetPedIntelligence()->GetLastUsedScenarioPoint();
|
|
if(!pOtherLastUsedScenarioPoint)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Grab the max distance.
|
|
TUNE_GROUP_FLOAT(FRIENDLY_WITH, fMaxDistanceBetweenScenarios, 3.5f, 0.0f, 50.0f, 0.1f);
|
|
|
|
//Ensure the distance is within the threshold.
|
|
ScalarV scDistSq = DistSquared(pLastUsedScenarioPoint->GetPosition(), pOtherLastUsedScenarioPoint->GetPosition());
|
|
ScalarV scMaxDistSq = ScalarVFromF32(square(fMaxDistanceBetweenScenarios));
|
|
if(IsGreaterThanAll(scDistSq, scMaxDistSq))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CPedIntelligence::IsThreatenedBy(const CPed& otherPed, bool bIncludeDislike, bool bIncludeNonFriendly, bool bForceInCombatCheck) const
|
|
{
|
|
bool bThreatened = false;
|
|
|
|
bool bInCombat = bForceInCombatCheck || m_pPed->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_COMBAT);
|
|
|
|
// If we aren't told to include non-friendly peds but we have TreatNonFriendlyAsHateWhenInCombat flag set then we should include non-friendly ones
|
|
if( !bIncludeNonFriendly && bInCombat && m_pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_TreatNonFriendlyAsHateWhenInCombat))
|
|
{
|
|
bIncludeNonFriendly = true;
|
|
}
|
|
|
|
if (!bThreatened && bIncludeNonFriendly)
|
|
{
|
|
bThreatened = !CPedGroups::AreInSameGroup(m_pPed, &otherPed) && !IsFriendlyWith(otherPed);
|
|
}
|
|
|
|
if (!bThreatened)
|
|
{
|
|
// If we aren't told to include dislike but we are in combat with the treat dislike as hate when in combat flag set then we should include dislike
|
|
if( !bIncludeDislike && bInCombat && m_pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_TreatDislikeAsHateWhenInCombat) )
|
|
{
|
|
bIncludeDislike = true;
|
|
}
|
|
|
|
const CRelationshipGroup* pRelGroup = m_pPed->GetPedIntelligence()->GetRelationshipGroup();
|
|
const int otherPedRelationshipGroupIndex = otherPed.GetPedIntelligence()->GetRelationshipGroupIndex();
|
|
bThreatened = pRelGroup &&
|
|
((bIncludeDislike && pRelGroup->CheckRelationship( ACQUAINTANCE_TYPE_PED_DISLIKE, otherPedRelationshipGroupIndex ) ) ||
|
|
pRelGroup->CheckRelationship( ACQUAINTANCE_TYPE_PED_HATE, otherPedRelationshipGroupIndex ) ||
|
|
pRelGroup->CheckRelationship( ACQUAINTANCE_TYPE_PED_WANTED, otherPedRelationshipGroupIndex ));
|
|
}
|
|
|
|
// The relationship groups aren't correctly set up in MP games
|
|
// Gam it here instead.
|
|
if( NetworkInterface::IsGameInProgress() )
|
|
{
|
|
bool isLawEnforcementPed = m_pPed->IsLawEnforcementPed();
|
|
|
|
if( !bThreatened && isLawEnforcementPed)
|
|
{
|
|
if(otherPed.IsAPlayerPed() && otherPed.GetPlayerWanted() && otherPed.GetPlayerWanted()->GetWantedLevel() > WANTED_CLEAN )
|
|
{
|
|
bThreatened=true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( bThreatened && ((m_pPed->GetPedsGroup() && m_pPed->GetPedsGroup() == otherPed.GetPedsGroup()) || IsFriendlyWith(otherPed)) )
|
|
{
|
|
bThreatened = false;
|
|
}
|
|
|
|
return bThreatened;
|
|
}
|
|
|
|
bool CPedIntelligence::IsPedThreatening(const CPed& rOtherPed, bool bIncludeNonViolentWeapons) const
|
|
{
|
|
//Check if the ped is threatened by the other ped.
|
|
if(IsThreatenedBy(rOtherPed))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//Check if the other ped is firing a weapon.
|
|
if(rOtherPed.GetPedResetFlag(CPED_RESET_FLAG_FiringWeapon))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//Check if the other ped is armed.
|
|
const CPedWeaponManager* pWeaponManager = rOtherPed.GetWeaponManager();
|
|
if(pWeaponManager && pWeaponManager->GetIsArmed() && (bIncludeNonViolentWeapons || !pWeaponManager->GetEquippedWeaponInfo() || !pWeaponManager->GetEquippedWeaponInfo()->GetIsNonViolent()))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//Check if the other ped is a player.
|
|
if(rOtherPed.IsPlayer())
|
|
{
|
|
//Check if the player is wanted.
|
|
const CWanted* pWanted = rOtherPed.GetPlayerWanted();
|
|
if(pWanted && (pWanted->GetWantedLevel() > WANTED_CLEAN))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool CPedIntelligence::Respects(const CPed& otherPed) const
|
|
{
|
|
const CRelationshipGroup* pRelGroup = m_pPed->GetPedIntelligence()->GetRelationshipGroup();
|
|
return pRelGroup && pRelGroup->CheckRelationship( ACQUAINTANCE_TYPE_PED_RESPECT, otherPed.GetPedIntelligence()->GetRelationshipGroupIndex() );
|
|
}
|
|
|
|
bool CPedIntelligence::Ignores(const CPed& otherPed) const
|
|
{
|
|
const CRelationshipGroup* pRelGroup = m_pPed->GetPedIntelligence()->GetRelationshipGroup();
|
|
return pRelGroup && pRelGroup->CheckRelationship( ACQUAINTANCE_TYPE_PED_IGNORE, otherPed.GetPedIntelligence()->GetRelationshipGroupIndex() );
|
|
}
|
|
|
|
u8 CPedIntelligence::CountFriends(float fMaxDistance, bool bDueToScenarios, bool bDueToSameVehicle) const
|
|
{
|
|
//Keep track of the count.
|
|
u8 uCount = 0;
|
|
|
|
//Grab the position.
|
|
Vec3V vPosition = m_pPed->GetTransform().GetPosition();
|
|
|
|
//Calculate the max distance.
|
|
ScalarV scMaxDistSq = ScalarVFromF32(square(fMaxDistance));
|
|
|
|
//Iterate over the nearby peds.
|
|
const CEntityScannerIterator it = GetNearbyPeds();
|
|
for(const CEntity* pEntity = it.GetFirst(); pEntity != NULL; pEntity = it.GetNext())
|
|
{
|
|
//Ensure the distance is within the threshold.
|
|
ScalarV scDistSq = DistSquared(vPosition, pEntity->GetTransform().GetPosition());
|
|
if(IsGreaterThanAll(scDistSq, scMaxDistSq))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//Ensure the other ped is alive.
|
|
const CPed* pOtherPed = static_cast<const CPed *>(pEntity);
|
|
if(pOtherPed->IsInjured())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//Check if the other ped is friendly.
|
|
if(IsFriendlyWith(*pOtherPed))
|
|
{
|
|
++uCount;
|
|
}
|
|
else if(bDueToScenarios && IsFriendlyWithDueToScenarios(*pOtherPed))
|
|
{
|
|
++uCount;
|
|
}
|
|
else if(bDueToSameVehicle && IsFriendlyWithDueToSameVehicle(*pOtherPed))
|
|
{
|
|
uCount++;
|
|
}
|
|
}
|
|
|
|
return uCount;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// FUNCTION: IsInACarOrEnteringOne
|
|
// PURPOSE: Returns true if this ped happens to be in a car or is
|
|
// is in the process of entering one.
|
|
///////////////////////////////////////////////////////////
|
|
|
|
CVehicle *CPedIntelligence::IsInACarOrEnteringOne()
|
|
{
|
|
if( GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_ENTER_VEHICLE) )
|
|
{
|
|
return (CVehicle*) GetQueriableInterface()->GetTargetForTaskType(CTaskTypes::TASK_ENTER_VEHICLE);
|
|
}
|
|
if( GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_IN_VEHICLE_BASIC) )
|
|
{
|
|
return (CVehicle*) m_pPed->GetMyVehicle();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CPropManagementHelper* CPedIntelligence::GetActivePropManagementHelper()
|
|
{
|
|
// get our active task
|
|
CTask* pBaseTask = GetTaskActive();
|
|
//taskDisplayf("TaskActive: %s", pBaseTask->GetTaskName());
|
|
|
|
// get the first running prop manangement helper in the task hierarchy
|
|
return GetPropManagementHelper(pBaseTask);
|
|
}
|
|
|
|
CPropManagementHelper* CPedIntelligence::GetPropManagementHelper(CTask* pTask)
|
|
{
|
|
// if we have a task
|
|
if (pTask)
|
|
{
|
|
// check if the task has a prop helper
|
|
if (pTask->GetPropHelper())
|
|
{
|
|
//taskDisplayf("CurrentTask: %s has a prop helper", pTask->GetTaskName());
|
|
return pTask->GetPropHelper();
|
|
}
|
|
// otherwise, try again with our task's subtask
|
|
else
|
|
{
|
|
//taskDisplayf("CurrentTask: %s doesn't have a prop helper", pTask->GetTaskName());
|
|
return GetPropManagementHelper(pTask->GetSubTask());
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
bool CPedIntelligence::AreFriends(const CPed& ped1, const CPed& ped2)
|
|
{
|
|
return (ped1.GetPedIntelligence()->IsFriendlyWith(ped2) ||
|
|
ped2.GetPedIntelligence()->IsFriendlyWith(ped1));
|
|
}
|
|
|
|
void
|
|
CPedIntelligence::RecordAttemptedClimbOnRoute(const Vector3 & vClimbPosition)
|
|
{
|
|
m_vLastAttemptedClimbPosition = vClimbPosition;
|
|
m_iLastAttemptedClimbTime = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
|
|
void CPedIntelligence::RecordFailedClimbOnRoute(const Vector3 & vClimbPosition)
|
|
{
|
|
static const float fSameClimbEps = 0.1f;
|
|
static const float fSameClimbEpsZ = 2.0f;
|
|
static const s32 iMaxNumAttempts = 3;
|
|
|
|
if(rage::IsNearZero(vClimbPosition.x - m_vFailedClimb.x, fSameClimbEps) &&
|
|
rage::IsNearZero(vClimbPosition.y - m_vFailedClimb.y, fSameClimbEps) &&
|
|
rage::IsNearZero(vClimbPosition.z - m_vFailedClimb.z, fSameClimbEpsZ))
|
|
{
|
|
m_iNumTimesClimbFailed++;
|
|
if(m_iNumTimesClimbFailed >= iMaxNumAttempts)
|
|
{
|
|
// Disable any regular adjacency climb here
|
|
CPathServer::DisableClimbAtPosition(vClimbPosition, false);
|
|
// Disable any climbable object link here
|
|
CPathServer::DisableClimbAtPosition(vClimbPosition, true);
|
|
|
|
m_vFailedClimb = Vector3(0.0f,0.0f,0.0f);
|
|
m_iNumTimesClimbFailed = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_vFailedClimb = vClimbPosition;
|
|
m_iNumTimesClimbFailed = 1;
|
|
}
|
|
}
|
|
|
|
s32 CPedIntelligence::RecordStaticCountReachedMax(const Vector3& vPosition)
|
|
{
|
|
static dev_u32 iMaxDelta = 15000;
|
|
static dev_float fMaxDistSqr = 5.0f*5.0f;
|
|
|
|
u32 iDeltaMs = fwTimer::GetTimeInMilliseconds() - m_iFirstStuckTime;
|
|
if(iDeltaMs < iMaxDelta)
|
|
{
|
|
const float fDistSqr = (m_vStuckPosition - vPosition).Mag2();
|
|
if(fDistSqr < fMaxDistSqr)
|
|
{
|
|
m_iNumTimesStuck++;
|
|
return m_iNumTimesStuck;
|
|
}
|
|
}
|
|
|
|
m_vStuckPosition = vPosition;
|
|
m_iNumTimesStuck = 1;
|
|
m_iFirstStuckTime = fwTimer::GetTimeInMilliseconds();
|
|
|
|
return m_iFirstStuckTime;
|
|
}
|
|
|
|
void CPedIntelligence::RecordBeganCrossingRoad(const Vector3& vStartPos, const Vector3& vEndPos)
|
|
{
|
|
m_pPed->SetPedConfigFlag(CPED_CONFIG_FLAG_BeganCrossingRoad, true);
|
|
m_vCrossRoadStartPos = vStartPos;
|
|
m_vCrossRoadEndPos = vEndPos;
|
|
}
|
|
|
|
void CPedIntelligence::RecordFinishedCrossingRoad()
|
|
{
|
|
m_pPed->SetPedConfigFlag(CPED_CONFIG_FLAG_BeganCrossingRoad, false);
|
|
m_uLastFinishedCrossRoadTimeMS = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
|
|
bool CPedIntelligence::ShouldResumeCrossingRoad() const
|
|
{
|
|
return m_pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_BeganCrossingRoad);
|
|
}
|
|
|
|
void CPedIntelligence::RestartNavigationThroughRegion(const Vector3 & vMin, const Vector3 & vMax)
|
|
{
|
|
CTaskMoveWander * pTaskWander = (CTaskMoveWander*) FindTaskActiveMovementByType(CTaskTypes::TASK_MOVE_WANDER);
|
|
if(pTaskWander)
|
|
{
|
|
if(pTaskWander->GetPathIntersectsRegion(vMin, vMax))
|
|
{
|
|
pTaskWander->Restart();
|
|
}
|
|
}
|
|
CTaskMoveFollowNavMesh * pTaskNavMesh = (CTaskMoveFollowNavMesh*) FindTaskActiveMovementByType(CTaskTypes::TASK_MOVE_FOLLOW_NAVMESH);
|
|
if(pTaskNavMesh)
|
|
{
|
|
if(pTaskNavMesh->GetPathIntersectsRegion(vMin, vMax))
|
|
{
|
|
pTaskNavMesh->Restart();
|
|
}
|
|
}
|
|
}
|
|
|
|
float CPedIntelligence::GetMoveBlendRatioFromGoToTask(void) const
|
|
{
|
|
// If this is a player ped, and they're doing TASK_MOVE_PLAYER - then get the move state
|
|
// from the ped's moveblend vector within the CPedMoveBlendData class.
|
|
CTask * pSimplestMoveTask = GetActiveSimplestMovementTask();
|
|
if( pSimplestMoveTask && pSimplestMoveTask->GetTaskType() == CTaskTypes::TASK_MOVE_PLAYER )
|
|
{
|
|
Vector2 vMoveBlend;
|
|
m_pPed->GetMotionData()->GetCurrentMoveBlendRatio(vMoveBlend);
|
|
float fMoveBlendRatio = vMoveBlend.Mag();
|
|
return fMoveBlendRatio;
|
|
}
|
|
|
|
if(!pSimplestMoveTask)
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
Assert( pSimplestMoveTask->IsMoveTask() );
|
|
|
|
if (!pSimplestMoveTask->GetMoveInterface())
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
return pSimplestMoveTask->GetMoveInterface()->GetMoveBlendRatio();
|
|
}
|
|
|
|
void CPedIntelligence::RefreshCombatDirector()
|
|
{
|
|
//Release the combat director.
|
|
CCombatDirector::RefreshCombatDirector(*m_pPed);
|
|
}
|
|
|
|
void CPedIntelligence::ReleaseCombatDirector()
|
|
{
|
|
//Release the combat director.
|
|
CCombatDirector::ReleaseCombatDirector(*m_pPed);
|
|
}
|
|
|
|
void CPedIntelligence::RequestCombatDirector()
|
|
{
|
|
//Request a combat director.
|
|
CCombatDirector::RequestCombatDirector(*m_pPed);
|
|
}
|
|
|
|
CCombatOrders* CPedIntelligence::GetCombatOrders() const
|
|
{
|
|
//Find the combat task.
|
|
CTaskCombat* pCombatTask = static_cast<CTaskCombat *>(FindTaskActiveByType(CTaskTypes::TASK_COMBAT));
|
|
if(!pCombatTask)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return &(pCombatTask->GetCombatOrders());
|
|
}
|
|
|
|
void CPedIntelligence::RecordEventForScript(const int iEventType, const int iEventPriority)
|
|
{
|
|
if(EVENT_SCRIPT_COMMAND==iEventType)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if((EVENT_NONE==iEventType) || (iEventPriority>m_iHighestPriorityEventPriority))
|
|
{
|
|
m_iHighestPriorityEventType=(u8)iEventType;
|
|
m_iHighestPriorityEventPriority=(u8)iEventPriority;
|
|
}
|
|
}
|
|
|
|
bool CPedIntelligence::NetworkMigrationAllowed(const netPlayer& player, eMigrationType migrationType) const
|
|
{
|
|
// only check top level (active) tasks, as soon as we find a task at one of the priorities
|
|
// we don't want to allow lower priority tasks to prevent migration
|
|
bool bPrimaryTaskFound = false;
|
|
|
|
for(int i=0; i<PED_TASK_PRIORITY_MAX && !bPrimaryTaskFound; i++)
|
|
{
|
|
CTask * pTask = static_cast<CTask*>(m_taskManager.GetTask(PED_TASK_TREE_PRIMARY, i));
|
|
|
|
bPrimaryTaskFound = pTask != 0;
|
|
|
|
while(pTask)
|
|
{
|
|
#if ENABLE_NETWORK_LOGGING
|
|
if(pTask->IsClonedFSMTask())
|
|
{
|
|
static_cast<CTaskFSMClone*>(pTask)->ResetLastTaskMigrateFailReason();
|
|
}
|
|
#endif //ENABLE_NETWORK_LOGGING
|
|
|
|
if (pTask->IsClonedFSMTask() && !static_cast<CTaskFSMClone*>(pTask)->ControlPassingAllowed(m_pPed, player, migrationType) && !CanForcefullyAllowControlPass())
|
|
{
|
|
#if ENABLE_NETWORK_LOGGING
|
|
formatf(m_lastMigrationFailTaskReason, "%s / %s", pTask->GetName().c_str(), static_cast<CTaskFSMClone*>(pTask)->GetLastTaskMigrateFailReason());
|
|
#endif // ENABLE_NETWORK_LOGGING
|
|
return false;
|
|
}
|
|
|
|
pTask = pTask->GetSubTask();
|
|
}
|
|
}
|
|
|
|
// only check top level (active) tasks, as soon as we find a task at one of the priorities
|
|
// we don't want to allow lower priority tasks to prevent migration
|
|
bool bSecondaryTaskFound = false;
|
|
|
|
for(int i=0;i<PED_TASK_SECONDARY_MAX && !bSecondaryTaskFound;i++)
|
|
{
|
|
CTask *pTask = static_cast<CTask*>(m_taskManager.GetTask(PED_TASK_TREE_SECONDARY, i));
|
|
|
|
bSecondaryTaskFound = pTask != 0;
|
|
|
|
while(pTask)
|
|
{
|
|
#if ENABLE_NETWORK_LOGGING
|
|
if(pTask->IsClonedFSMTask())
|
|
{
|
|
static_cast<CTaskFSMClone*>(pTask)->ResetLastTaskMigrateFailReason();
|
|
}
|
|
#endif //ENABLE_NETWORK_LOGGING
|
|
|
|
if (pTask->IsClonedFSMTask() && !static_cast<CTaskFSMClone*>(pTask)->ControlPassingAllowed(m_pPed, player, migrationType) && !CanForcefullyAllowControlPass())
|
|
{
|
|
#if ENABLE_NETWORK_LOGGING
|
|
formatf(m_lastMigrationFailTaskReason , "%s / %s", pTask->GetName().c_str(), static_cast<CTaskFSMClone*>(pTask)->GetLastTaskMigrateFailReason());
|
|
#endif // ENABLE_NETWORK_LOGGING
|
|
return false;
|
|
}
|
|
|
|
pTask = pTask->GetSubTask();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
CTaskFSMClone *CPedIntelligence::CreateCloneTaskForTaskType(u32 taskType) const
|
|
{
|
|
if(m_pPed && taskVerifyf(m_pPed->IsNetworkClone(), "Trying to create a clone task on a local ped!"))
|
|
{
|
|
CTaskTreeClone *cloneTaskTree = SafeCast(CTaskTreeClone, m_taskManager.GetTree(PED_TASK_TREE_PRIMARY));
|
|
if(cloneTaskTree)
|
|
{
|
|
return cloneTaskTree->CreateCloneTaskForTaskType(taskType);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
CTask* CPedIntelligence::CreateCloneTaskFromInfo(CTaskInfo *taskInfo) const
|
|
{
|
|
if(m_pPed && taskVerifyf(m_pPed->IsNetworkClone(), "Trying to create a clone task on a local ped!"))
|
|
{
|
|
CTaskTreeClone *cloneTaskTree = SafeCast(CTaskTreeClone, m_taskManager.GetTree(PED_TASK_TREE_PRIMARY));
|
|
if(cloneTaskTree)
|
|
{
|
|
return cloneTaskTree->CreateCloneTaskFromInfo(taskInfo);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void CPedIntelligence::RecalculateCloneTasks()
|
|
{
|
|
if(m_pPed && taskVerifyf(m_pPed->IsNetworkClone(), "Trying to recalculate clone tasks on a local ped!"))
|
|
{
|
|
CTaskTreeClone *cloneTaskTree = SafeCast(CTaskTreeClone, m_taskManager.GetTree(PED_TASK_TREE_PRIMARY));
|
|
if(cloneTaskTree)
|
|
{
|
|
cloneTaskTree->RecalculateTasks();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPedIntelligence::UpdateTaskSpecificData()
|
|
{
|
|
if(m_pPed && taskVerifyf(m_pPed->IsNetworkClone(), "Trying to recalculate clone tasks on a local ped!"))
|
|
{
|
|
CTaskTreeClone *cloneTaskTree = SafeCast(CTaskTreeClone, m_taskManager.GetTree(PED_TASK_TREE_PRIMARY));
|
|
if(cloneTaskTree)
|
|
{
|
|
cloneTaskTree->UpdateTaskSpecificData();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CPedIntelligence::CanForcefullyAllowControlPass() const
|
|
{
|
|
bool useTaskFailTimer = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("CONTROL_PASSING_TASK_FAIL_TIMER_ENABLED", 0x768BFC4D), true);
|
|
if(useTaskFailTimer)
|
|
{
|
|
u32 lastFailTime = m_ControlPassingFailTime;
|
|
gnetDebug3("[ControlPassing] %s passing not allowed. lastFailTime: %u", m_pPed->GetLogName(), lastFailTime);
|
|
|
|
// If the last fail time happened a long time ago:
|
|
if(lastFailTime < fwTimer::GetSystemTimeInMilliseconds() - RESET_CONTROL_PASS_FAIL_TIME)
|
|
{
|
|
gnetDebug3("[ControlPassing] %s setting fail timer: %u", m_pPed->GetLogName(), fwTimer::GetSystemTimeInMilliseconds());
|
|
m_ControlPassingFailTime = fwTimer::GetSystemTimeInMilliseconds();
|
|
}
|
|
else
|
|
{
|
|
gnetDebug3("[ControlPassing] %s checking fail timer. Current: %u", m_pPed->GetLogName(), fwTimer::GetSystemTimeInMilliseconds());
|
|
// If the last fail time happened more than MAX_CONTROL_PASS_FAIL_TIME seconds ago, then we can allow control passing
|
|
// Otherwise we continue blocking control passing to allow the task some time fix itself up
|
|
if(fwTimer::GetSystemTimeInMilliseconds() > (lastFailTime + MAX_CONTROL_PASS_FAIL_TIME))
|
|
{
|
|
gnetDebug3("[ControlPassing] %s passing forcefully allowed.", m_pPed->GetLogName());
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CPedIntelligence::ControlPassingAllowed(const netPlayer& player, eMigrationType migrationType)
|
|
{
|
|
if(m_pPed && taskVerifyf(m_pPed->IsNetworkClone(), "Calling ControlPassingAllowed on a local ped!"))
|
|
{
|
|
CTaskTreeClone *cloneTaskTree = SafeCast(CTaskTreeClone, m_taskManager.GetTree(PED_TASK_TREE_PRIMARY));
|
|
if(cloneTaskTree)
|
|
{
|
|
bool passingAllowed = cloneTaskTree->ControlPassingAllowed(player, migrationType);
|
|
|
|
if(!passingAllowed && netObject::IsCriticalMigration(migrationType))
|
|
{
|
|
passingAllowed = CanForcefullyAllowControlPass();
|
|
}
|
|
|
|
return passingAllowed;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CPedIntelligence::InstantTaskUpdate(bool bForceFullUpdate)
|
|
{
|
|
bool fullUpdate = bForceFullUpdate || CPedAILodManager::ShouldDoFullUpdate(*m_pPed);
|
|
|
|
const float fTimeslicedTimeStep = m_fTimeSinceLastAiUpdate;
|
|
m_fTimeSinceLastAiUpdate = fTimeslicedTimeStep;
|
|
|
|
Process_Tasks(fullUpdate, fTimeslicedTimeStep);
|
|
|
|
if(fullUpdate)
|
|
{
|
|
m_fTimeSinceLastAiUpdate = 0.0f;
|
|
}
|
|
}
|
|
|
|
void CAi::Init()
|
|
{
|
|
CTaskSmartFlee::InitClass();
|
|
CTaskMoveCombatMounted::InitTransitionTables();
|
|
|
|
CTaskCombat::InitClass();
|
|
//@@: location CAI_INIT_CCOMBBATMANAGER_INITCLASS
|
|
CCombatManager::InitClass();
|
|
|
|
// Disabled, didn't have any effect these days.
|
|
// CScriptedPriorities::Init();
|
|
}
|
|
|
|
void CAi::Shutdown()
|
|
{
|
|
CCombatManager::ShutdownClass();
|
|
CTaskSmartFlee::ShutdownClass();
|
|
|
|
GetEventGlobalGroup()->Shutdown();
|
|
|
|
// Disabled, didn't have any effect these days.
|
|
// CScriptedPriorities::Shutdown();
|
|
}
|
|
|
|
#if __DEV
|
|
void CPedIntelligence::PrintTasks()
|
|
{
|
|
// Print the task hierarchy for this ped
|
|
Printf("Task hierarchy\n");
|
|
CTask* pActiveTask = GetTaskActive();
|
|
if(pActiveTask)
|
|
{
|
|
CTask* pTaskToPrint = pActiveTask;
|
|
while(pTaskToPrint)
|
|
{
|
|
Printf("name: %s\n", (const char*) pTaskToPrint->GetName());
|
|
pTaskToPrint=pTaskToPrint->GetSubTask();
|
|
}
|
|
}
|
|
|
|
// Print the movement task hierarchy for this ped
|
|
Printf("Movement task hierarchy\n");
|
|
pActiveTask = GetActiveMovementTask();
|
|
if(pActiveTask)
|
|
{
|
|
CTask* pTaskToPrint = pActiveTask;
|
|
while(pTaskToPrint)
|
|
{
|
|
Printf("name: %s\n", (const char*) pTaskToPrint->GetName());
|
|
pTaskToPrint=pTaskToPrint->GetSubTask();
|
|
}
|
|
}
|
|
}
|
|
//*******************************************************************************
|
|
// PrintTasksAndEvents
|
|
// This function should be as above, but output all task trees & events.
|
|
//*******************************************************************************
|
|
|
|
void CPedIntelligence::PrintTasksAndEvents()
|
|
{
|
|
for(int iTaskPriority=0; iTaskPriority<PED_TASK_PRIORITY_MAX; iTaskPriority++)
|
|
{
|
|
aiTask * pTask = m_taskManager.GetTask(PED_TASK_TREE_PRIMARY, iTaskPriority);
|
|
while(pTask)
|
|
{
|
|
|
|
pTask = pTask->GetSubTask();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPedIntelligence::PrintEvents() const
|
|
{
|
|
m_eventHandler.Print();
|
|
m_eventGroup.Print();
|
|
}
|
|
|
|
void CPedIntelligence::PrintScriptTaskHistory() const
|
|
{
|
|
const s32 iStart = m_iCurrentHistroyTop - 1;
|
|
for (s32 i=0; i > -MAX_DEBUG_SCRIPT_TASK_HISTORY; i--)
|
|
{
|
|
s32 iIndex = iStart + i;
|
|
if (iIndex < 0)
|
|
{
|
|
iIndex += MAX_DEBUG_SCRIPT_TASK_HISTORY;
|
|
}
|
|
|
|
if(m_aScriptHistoryName[iIndex] != NULL )
|
|
{
|
|
float fHowLongAgo = ((float)(fwTimer::GetTimeInMilliseconds() - m_aScriptHistoryTime[iIndex])) / 1000.0f;
|
|
printf("%s %s script(%s, %d) - %.2f\n",
|
|
m_aScriptHistoryName[iIndex],
|
|
m_aScriptHistoryTaskName[iIndex],
|
|
m_szScriptName[iIndex],
|
|
m_aProgramCounter[iIndex],
|
|
fHowLongAgo);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // __DEV
|
|
|
|
bank_float CPedIntelligence::BATTLE_AWARENESS_BULLET_WHIZBY = 20.0f;
|
|
bank_float CPedIntelligence::BATTLE_AWARENESS_BULLET_IMPACT = 15.0f;
|
|
bank_float CPedIntelligence::BATTLE_AWARENESS_GUNSHOT = 10.0f;
|
|
bank_float CPedIntelligence::BATTLE_AWARENESS_GUNSHOT_LOCAL_PLAYER = 50.0f;
|
|
bank_float CPedIntelligence::BATTLE_AWARENESS_DAMAGE = 25.0f;
|
|
bank_float CPedIntelligence::BATTLE_AWARENESS_MELEE_FIGHT = 75.0f;
|
|
bank_float CPedIntelligence::BATTLE_AWARENESS_SHOVED = 50.0f;
|
|
bank_float CPedIntelligence::BATTLE_AWARENESS_MIN = 0.0f;
|
|
bank_float CPedIntelligence::BATTLE_AWARENESS_MAX = 100.0f;
|
|
bank_float CPedIntelligence::BATTLE_AWARENESS_DEPLETION_TIME_FROM_MAX = 10.0f;
|
|
bank_float CPedIntelligence::BATTLE_AWARENESS_MIN_TIME = 5.0f;
|
|
bank_u32 CPedIntelligence::BATTLE_AWARENESS_GUNSHOT_LOCAL_PLAYER_COOLDOWN = 1000;
|
|
|
|
void CPedIntelligence::IncreaseBattleAwareness(float fIncrement, bool bLocalPedTriggered, bool bForceRun)
|
|
{
|
|
bool bIncrease = true;
|
|
|
|
if(bLocalPedTriggered)
|
|
{
|
|
//! Only add awareness if fired outwith small tolerance.
|
|
if( (m_nLastBattleEventTimeLocalPlayer > 0) &&
|
|
fwTimer::GetTimeInMilliseconds() < (m_nLastBattleEventTimeLocalPlayer + BATTLE_AWARENESS_GUNSHOT_LOCAL_PLAYER_COOLDOWN) )
|
|
{
|
|
bIncrease = false;
|
|
}
|
|
else
|
|
{
|
|
m_nLastBattleEventTimeLocalPlayer = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_nLastBattleEventTime = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
|
|
if(bIncrease)
|
|
{
|
|
m_fBattleAwareness = Min(m_fBattleAwareness + fIncrement, BATTLE_AWARENESS_MAX);
|
|
}
|
|
|
|
if(bForceRun)
|
|
{
|
|
m_bBattleAwarenessForcingRun = true;
|
|
}
|
|
}
|
|
|
|
void CPedIntelligence::DecreaseBattleAwareness(float fDecrement)
|
|
{
|
|
m_fBattleAwareness = Max(m_fBattleAwareness - fDecrement, 0.0f);
|
|
if(m_fBattleAwareness <= 0.0f)
|
|
{
|
|
m_bBattleAwarenessForcingRun = false;
|
|
}
|
|
}
|
|
|
|
void CPedIntelligence::ResetBattleAwareness()
|
|
{
|
|
m_fBattleAwareness = 0.0f;
|
|
m_nLastBattleEventTime = 0;
|
|
m_nLastBattleEventTimeLocalPlayer = 0;
|
|
m_bBattleAwarenessForcingRun = false;
|
|
}
|
|
|
|
bool CPedIntelligence::IsBattleAwareForcingRun() const
|
|
{
|
|
return m_bBattleAwarenessForcingRun;
|
|
}
|
|
|
|
bool CPedIntelligence::IsBattleAware() const
|
|
{
|
|
return m_fBattleAwareness > BATTLE_AWARENESS_MIN;
|
|
}
|
|
|
|
float CPedIntelligence::GetBattleAwareness() const
|
|
{
|
|
return (m_fBattleAwareness-BATTLE_AWARENESS_MIN)/(BATTLE_AWARENESS_MAX-BATTLE_AWARENESS_MIN);
|
|
}
|
|
|
|
float CPedIntelligence::GetBattleAwarenessSecs() const
|
|
{
|
|
return (m_fBattleAwareness-BATTLE_AWARENESS_MIN)/(BATTLE_AWARENESS_MAX-BATTLE_AWARENESS_MIN)*BATTLE_AWARENESS_DEPLETION_TIME_FROM_MAX;
|
|
}
|
|
|
|
u32 CPedIntelligence::GetLastBattleEventTime(bool bIncludeLocalEvents) const
|
|
{
|
|
if(bIncludeLocalEvents)
|
|
{
|
|
return m_nLastBattleEventTimeLocalPlayer > m_nLastBattleEventTime ? m_nLastBattleEventTimeLocalPlayer : m_nLastBattleEventTime;
|
|
}
|
|
return m_nLastBattleEventTime;
|
|
}
|
|
|
|
void CPedIntelligence::RecordPedSeenDeadPed()
|
|
{
|
|
m_nLastTimeDeadPedSeen = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
|
|
bool CPedIntelligence::HasPedSeenDeadPedRecently(u32 uDiffMS) const
|
|
{
|
|
const u32 uDelta = fwTimer::GetTimeInMilliseconds() - m_nLastTimeDeadPedSeen;
|
|
if (uDelta < uDiffMS)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CPedIntelligence::CanEntityIncreaseBattleAwareness(CEntity *pEntity) const
|
|
{
|
|
bool bIncrease = false;
|
|
if(pEntity && pEntity->GetType() == ENTITY_TYPE_PED)
|
|
{
|
|
CPed *pOtherPed = static_cast<CPed*>(pEntity);
|
|
|
|
CPedTargetting *pOtherTargeting = pOtherPed->GetPedIntelligence()->GetTargetting();
|
|
|
|
//! If either ped is a threat to each other, then increase.
|
|
if(pOtherPed->GetPedIntelligence()->IsThreatenedBy(*m_pPed) || m_pPed->GetPedIntelligence()->IsThreatenedBy(*pOtherPed))
|
|
{
|
|
bIncrease = true;
|
|
}
|
|
else if(pOtherTargeting)
|
|
{
|
|
//! If this ped is in other peds targeting list, or if other ped is targeting a friendly, increase.
|
|
const int numActiveTargets = pOtherTargeting->GetNumActiveTargets();
|
|
for(int i = 0; i < numActiveTargets; ++i)
|
|
{
|
|
const CEntity *pTargetEntity = pOtherTargeting->GetTargetAtIndex(i);
|
|
if(pTargetEntity && pTargetEntity->GetIsTypePed())
|
|
{
|
|
const CPed *pTargetPed = static_cast<const CPed*>(pTargetEntity);
|
|
if( (pTargetPed == m_pPed) || IsFriendlyWith(*pTargetPed))
|
|
{
|
|
bIncrease = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bIncrease = true;
|
|
}
|
|
|
|
return bIncrease;
|
|
}
|
|
|
|
bool CPedIntelligence::CanBePinnedDown() const
|
|
{
|
|
//Check if we can be pinned down.
|
|
if(!m_combatBehaviour.IsFlagSet(CCombatData::BF_DisablePinnedDown))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool CPedIntelligence::CanPinDownOthers() const
|
|
{
|
|
//Check if we can pin down others.
|
|
if(!m_combatBehaviour.IsFlagSet(CCombatData::BF_DisablePinDownOthers))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Increase the amount by which the ped is pinned down,
|
|
// The increase is altered by the peds pinned down affector, to make less
|
|
// cautious peds less affected by shots etc...
|
|
//-------------------------------------------------------------------------
|
|
void CPedIntelligence::IncreaseAmountPinnedDown ( float fIncrease )
|
|
{
|
|
// Record the last time we increased our pinned down time
|
|
SetLastPinnedDownTime(fwTimer::GetTimeInMilliseconds());
|
|
|
|
// The amount a ped caution is increased is dependent upon how cautious they are,
|
|
// the more cautions the more they are bothered, am i bothered tho?
|
|
float fPedCaution;
|
|
if(GetCombatBehaviour().GetCombatAbility() == CCombatData::CA_Poor )
|
|
{
|
|
fPedCaution = 0.75f;
|
|
}
|
|
else // CA_Average and CA_Professional
|
|
{
|
|
fPedCaution = 0.55f;
|
|
}
|
|
|
|
SetAmountPinnedDown(m_fPinnedDown + fIncrease * fPedCaution);
|
|
}
|
|
|
|
void CPedIntelligence::IncreaseAlertnessState()
|
|
{
|
|
AlertnessState current_state = GetAlertnessState();
|
|
if (current_state < AS_MustGoToCombat)
|
|
{
|
|
SetAlertnessState(static_cast<AlertnessState>(current_state + 1));
|
|
}
|
|
}
|
|
|
|
|
|
void CPedIntelligence::DecreaseAlertnessState()
|
|
{
|
|
AlertnessState current_state = GetAlertnessState();
|
|
if (current_state > AS_NotAlert)
|
|
{
|
|
SetAlertnessState(static_cast<AlertnessState>(current_state - 1));
|
|
}
|
|
}
|
|
|
|
bool CPedIntelligence::GetCanCombatRoll() const
|
|
{
|
|
#if __DEV
|
|
TUNE_GROUP_BOOL(PED_MOVEMENT, INSTANT_COMBAT_ROLL, false);
|
|
if(INSTANT_COMBAT_ROLL)
|
|
{
|
|
return true;
|
|
}
|
|
#endif // __DEV
|
|
|
|
// Don't combat roll if disabled by script
|
|
if (m_pPed->GetPedResetFlag(CPED_RESET_FLAG_DisablePlayerCombatRoll))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(m_pPed->GetIsInCover() || m_pPed->GetIsSwimming())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(m_pPed->GetHasJetpackEquipped())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Don't combat roll if super jump is enabled.
|
|
if (m_pPed->GetPlayerInfo() && m_pPed->GetPlayerInfo()->GetPlayerResetFlags().IsFlagSet(CPlayerResetFlags::PRF_SUPER_JUMP_ON))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(m_iLastCombatRollTime == 0)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
TUNE_GROUP_FLOAT(PED_MOVEMENT, COMBAT_ROLL_WORST_TIME, 3.0f, 0.f, 100.f, 0.01f);
|
|
TUNE_GROUP_FLOAT(PED_MOVEMENT, COMBAT_ROLL_BEST_TIME, 1.0f, 0.f, 100.f, 0.01f);
|
|
|
|
static const float ONE_OVER_100 = 1.f / 100.f;
|
|
float fShootingAbility = StatsInterface::GetFloatStat(StatsInterface::GetStatsModelHashId("SHOOTING_ABILITY")) * ONE_OVER_100;
|
|
float fCombatRollTime = COMBAT_ROLL_WORST_TIME + ((COMBAT_ROLL_BEST_TIME - COMBAT_ROLL_WORST_TIME) * fShootingAbility);
|
|
u32 iCombatRollMinTime = (u32)(fCombatRollTime * 1000.f);
|
|
|
|
if((m_iLastCombatRollTime + iCombatRollMinTime) < fwTimer::GetTimeInMilliseconds())
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CPedIntelligence::GetCanDoAimGrenadeThrow() const
|
|
{
|
|
#if __DEV
|
|
TUNE_GROUP_BOOL(AIMING_TUNE, INSTANT_AIM_GRENADE_THROW, false);
|
|
if(INSTANT_AIM_GRENADE_THROW)
|
|
{
|
|
return true;
|
|
}
|
|
#endif // __DEV
|
|
|
|
if(m_pPed->GetIsSwimming())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(m_iLastAimGrenadeThrowTime == 0)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
TUNE_GROUP_FLOAT(AIMING_TUNE, AIM_GRENADE_THROW_TIME, 1.0f, 0.f, 100.f, 0.01f);
|
|
float fAimGrenadeThrowTime = AIM_GRENADE_THROW_TIME;
|
|
u32 iAimGrenadeThrowMinTime = (u32)(fAimGrenadeThrowTime * 1000.f);
|
|
|
|
if((m_iLastAimGrenadeThrowTime + iAimGrenadeThrowMinTime) < fwTimer::GetTimeInMilliseconds())
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Decrease the amount by which the ped is pinned down
|
|
//-------------------------------------------------------------------------
|
|
void CPedIntelligence::DecreaseAmountPinnedDown ( float fDecrease )
|
|
{
|
|
SetAmountPinnedDown(m_fPinnedDown - fDecrease);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Directly set the amount by which this is ped is pinned down
|
|
//-------------------------------------------------------------------------
|
|
void CPedIntelligence::SetAmountPinnedDown ( float fPinnedDown, bool bForce )
|
|
{
|
|
float fMaxPinnedDown = (bForce || CanBePinnedDown()) ? 100.0f : 0.0f;
|
|
m_fPinnedDown = Clamp(fPinnedDown, 0.0f, fMaxPinnedDown);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Directly set the amount by which this is ped is pinned down
|
|
//-------------------------------------------------------------------------
|
|
void CPedIntelligence::ForcePedPinnedDown (const bool bPinned, float fTime )
|
|
{
|
|
m_fForcedPinnedTimer = fTime;
|
|
m_bForcePinned = bPinned;
|
|
SetAmountPinnedDown( 100.0f );
|
|
}
|
|
|
|
void CPedIntelligence::SetCheckShockFlag(bool bCheck)
|
|
{
|
|
if (bCheck)
|
|
{
|
|
m_Flags.SetFlag(PIFlag_CheckShockingEvents);
|
|
}
|
|
else
|
|
{
|
|
m_Flags.ClearFlag(PIFlag_CheckShockingEvents);
|
|
}
|
|
}
|
|
|
|
void CPedIntelligence::SetStartNewHangoutScenario(bool bOnOff)
|
|
{
|
|
if (bOnOff)
|
|
{
|
|
m_Flags.SetFlag(PIFlag_StartNewHangoutScenario);
|
|
}
|
|
else
|
|
{
|
|
m_Flags.ClearFlag(PIFlag_StartNewHangoutScenario);
|
|
}
|
|
}
|
|
|
|
void CPedIntelligence::SetTakenNiceCarPicture(bool bOnOff)
|
|
{
|
|
if (bOnOff)
|
|
{
|
|
m_Flags.SetFlag(PIFlag_TakenNiceCarPicture);
|
|
}
|
|
else
|
|
{
|
|
m_Flags.ClearFlag(PIFlag_TakenNiceCarPicture);
|
|
}
|
|
}
|
|
|
|
void CPedIntelligence::GetOverriddenGameplayCameraSettings(tGameplayCameraSettings& settings) const
|
|
{
|
|
//Find the lowest/simplest task in the hierarchy that overrides the camera and use its settings.
|
|
|
|
CTask* activeTask = GetTaskActiveSimplest();
|
|
while(activeTask)
|
|
{
|
|
tGameplayCameraSettings tempSettings(settings);
|
|
|
|
activeTask->GetOverriddenGameplayCameraSettings(tempSettings);
|
|
|
|
//NOTE: A task must override the camera in order to override the other settings.
|
|
if(tempSettings.m_CameraHash)
|
|
{
|
|
settings = tempSettings;
|
|
return;
|
|
}
|
|
|
|
activeTask = activeTask->GetParent();
|
|
}
|
|
|
|
// taskmobile phone is a secondary
|
|
activeTask = GetTaskSecondaryActive();
|
|
while(activeTask)
|
|
{
|
|
tGameplayCameraSettings tempSettings(settings);
|
|
|
|
activeTask->GetOverriddenGameplayCameraSettings(tempSettings);
|
|
|
|
//NOTE: A task must override the camera in order to override the other settings.
|
|
if(tempSettings.m_CameraHash)
|
|
{
|
|
settings = tempSettings;
|
|
return;
|
|
}
|
|
|
|
activeTask = activeTask->GetParent();
|
|
}
|
|
|
|
activeTask = GetMotionTaskActiveSimplest();
|
|
while(activeTask)
|
|
{
|
|
tGameplayCameraSettings tempSettings(settings);
|
|
|
|
activeTask->GetOverriddenGameplayCameraSettings(tempSettings);
|
|
|
|
//NOTE: A task must override the camera in order to override the other settings.
|
|
if(tempSettings.m_CameraHash)
|
|
{
|
|
settings = tempSettings;
|
|
return;
|
|
}
|
|
|
|
activeTask = activeTask->GetParent();
|
|
}
|
|
}
|
|
|
|
//set the ped
|
|
void CPedIntelligence::SetPedMotivation(const CPed* pPed)
|
|
{
|
|
if (pPed)
|
|
{
|
|
if (pPed->GetBaseModelInfo() != NULL)
|
|
{
|
|
u32 MotivationGroup = pPed->GetMotivation();
|
|
m_pedMotivation.SetMotivationProfile(MotivationGroup);
|
|
}
|
|
}
|
|
}
|
|
|
|
//set the ped's nav capabilities
|
|
void CPedIntelligence::SetNavCapabilities(const CPed* pPed)
|
|
{
|
|
if (pPed)
|
|
{
|
|
if(pPed->GetBaseModelInfo() != NULL)
|
|
{
|
|
const CPedNavCapabilityInfo* pNavCapabilities = CPedNavCapabilityInfoManager::GetInfo(pPed->GetPedModelInfo()->GetNavCapabilitiesHash().GetHash());
|
|
Assert(pNavCapabilities);
|
|
if (pNavCapabilities)
|
|
{
|
|
m_navCapabilities = *pNavCapabilities;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//set the ped's perception based on its hash value
|
|
void CPedIntelligence::SetPedPerception(const CPed* pPed)
|
|
{
|
|
if (pPed)
|
|
{
|
|
if (pPed->GetBaseModelInfo() != NULL)
|
|
{
|
|
const SPedPerceptionInfo* pPerception = CPedPerceptionInfoManager::GetPerception(pPed->GetPedModelInfo()->GetPerceptionHash().GetHash());
|
|
Assertf(pPerception, "Could not find a valid perception for ped!");
|
|
if (pPerception)
|
|
{
|
|
m_pedPerception.SetEncroachmentRange(pPerception->m_EncroachmentRange);
|
|
m_pedPerception.SetEncroachmentCloseRange(pPerception->m_EncroachmentCloseRange);
|
|
m_pedPerception.SetSeeingRange(pPerception->m_SeeingRange);
|
|
m_pedPerception.SetHearingRange(pPerception->m_HearingRange);
|
|
m_pedPerception.SetSeeingRangePeripheral(pPerception->m_SeeingRangePeripheral);
|
|
m_pedPerception.SetVisualFieldMinAzimuthAngle(pPerception->m_VisualFieldMinAzimuthAngle);
|
|
m_pedPerception.SetVisualFieldMaxAzimuthAngle(pPerception->m_VisualFieldMaxAzimuthAngle);
|
|
m_pedPerception.SetVisualFieldMinElevationAngle(pPerception->m_VisualFieldMinElevationAngle);
|
|
m_pedPerception.SetVisualFieldMaxElevationAngle(pPerception->m_VisualFieldMaxElevationAngle);
|
|
m_pedPerception.SetCentreOfGazeMaxAngle(pPerception->m_CentreFieldOfGazeMaxAngle);
|
|
m_pedPerception.SetCanAlwaysSenseEncroachingPlayers(pPerception->m_CanAlwaysSenseEncroachingPlayers);
|
|
m_pedPerception.SetPerformsEncroachmentChecksIn3D(pPerception->m_PerformEncroachmentChecksIn3D);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// B*2343564: Set cop ped perception values based on script-specified overrides (from SET_COP_PERCEPTION_OVERRIDES).
|
|
void CPedIntelligence::SetCopPedOverridenPerception(const CPed *pPed)
|
|
{
|
|
if (pPed && CPedPerception::ms_bCopOverridesSet)
|
|
{
|
|
m_pedPerception.SetSeeingRange(CPedPerception::ms_fCopSeeingRangeOverride);
|
|
m_pedPerception.SetSeeingRangePeripheral(CPedPerception::ms_fCopSeeingRangePeripheralOverride);
|
|
m_pedPerception.SetHearingRange(CPedPerception::ms_fCopHearingRangeOverride);
|
|
m_pedPerception.SetVisualFieldMinAzimuthAngle(CPedPerception::ms_fCopVisualFieldMinAzimuthAngleOverride);
|
|
m_pedPerception.SetVisualFieldMaxAzimuthAngle(CPedPerception::ms_fCopVisualFieldMaxAzimuthAngleOverride);
|
|
m_pedPerception.SetCentreOfGazeMaxAngle(CPedPerception::ms_fCopCentreOfGazeMaxAngleOverride);
|
|
m_pedPerception.SetRearViewRangeOverride(CPedPerception::ms_fCopRearViewRangeOverride);
|
|
}
|
|
}
|
|
|
|
void CPedIntelligence::Process_NearbyEntityLists()
|
|
{
|
|
PF_FUNC(AI_Process_NearbyEntityLists);
|
|
|
|
//reset near-door flag
|
|
m_pPed->SetPedResetFlag(CPED_RESET_FLAG_IsNearDoor, false);
|
|
|
|
if(!m_pPed->GetPedAiLod().IsLodFlagSet(CPedAILod::AL_LodEntityScanning))
|
|
{
|
|
// Update all peds and vehicles and objects in range.
|
|
m_vehicleScanner.ScanForEntitiesInRange(*m_pPed);
|
|
m_pedScanner.ScanForEntitiesInRange(*m_pPed, ShouldForcePedScanThisFrame());
|
|
|
|
if( m_pPed->GetPedResetFlag( CPED_RESET_FLAG_SearchingForDoors ) )
|
|
{
|
|
m_pPed->SetPedResetFlag(CPED_RESET_FLAG_SearchingForDoors, false);
|
|
m_doorScanner.ScanForEntitiesInRange(*m_pPed);
|
|
|
|
//update near-Door flag
|
|
CDoor* pClosestDoor = GetClosestDoorInRange();
|
|
if (pClosestDoor && !pClosestDoor->IsBaseFlagSet( fwEntity::IS_FIXED ))
|
|
{
|
|
static dev_float sfMinDoorDistance = rage::square(1.5f);
|
|
const float fDoorDistance = VEC3V_TO_VECTOR3(m_pPed->GetTransform().GetPosition()).Dist2(VEC3V_TO_VECTOR3(pClosestDoor->GetTransform().GetPosition()));
|
|
if (fDoorDistance <= sfMinDoorDistance)
|
|
m_pPed->SetPedResetFlag(CPED_RESET_FLAG_IsNearDoor, true);
|
|
}
|
|
|
|
// Note: in other code paths, m_doorScanner should always be empty, so there is no need to
|
|
// run the code above as it could never have any effect.
|
|
}
|
|
else
|
|
{
|
|
m_doorScanner.Clear();
|
|
}
|
|
|
|
// Don't update the object scanners, just tick them on so any recent forallentities
|
|
// are invalidated after time, set the count to 0 if it finishes so that any subsequent call will immediately update
|
|
// the list again
|
|
if( m_objectScanner.GetTimer().Tick() )
|
|
{
|
|
m_objectScanner.GetTimer().SetCount(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//m_vehicleScanner.Clear();
|
|
//m_doorScanner.Clear();
|
|
m_pedScanner.Clear();
|
|
//m_objectScanner.Clear();
|
|
//Assert(m_vehicleScanner.IsEmpty());
|
|
Assert(m_doorScanner.IsEmpty());
|
|
//Assert(m_pedScanner.IsEmpty());
|
|
//Assert(m_objectScanner.IsEmpty());
|
|
}
|
|
}
|
|
|
|
bool CPedIntelligence::ShouldForcePedScanThisFrame() const
|
|
{
|
|
const CPed* pPlayer = CGameWorld::FindLocalPlayer();
|
|
if (pPlayer)
|
|
{
|
|
CVehicle* pVehicle = pPlayer->GetVehiclePedInside();
|
|
if (pVehicle)
|
|
{
|
|
if (HasResponseToEvent(EVENT_ENCROACHING_PED))
|
|
{
|
|
if (IsGreaterThanAll(MagSquared(pVehicle->GetAiVelocityConstRef()), ScalarV(ms_fFrequentScanVehicleVelocitySquaredThreshold)))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CPedIntelligence::Process_FacialDataPreTasks()
|
|
{
|
|
//Ensure the facial data is valid.
|
|
CFacialDataComponent* pFacialData = m_pPed->GetFacialData();
|
|
if(!pFacialData)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Process the facial data.
|
|
pFacialData->ProcessPreTasks(*m_pPed);
|
|
|
|
if(m_pPed->GetPedResetFlag(CPED_RESET_FLAG_DoDamageCoughFacial))
|
|
{
|
|
pFacialData->RequestFacialIdleClip(CFacialDataComponent::FICT_Coughing);
|
|
}
|
|
}
|
|
|
|
void CPedIntelligence::Process_FacialDataPostTasks(float fTimeStep)
|
|
{
|
|
//Ensure the facial data is valid.
|
|
CFacialDataComponent* pFacialData = m_pPed->GetFacialData();
|
|
if(!pFacialData)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Process the facial data.
|
|
pFacialData->ProcessPostTasks(*m_pPed);
|
|
|
|
//Update the facial data.
|
|
pFacialData->Update(*m_pPed, fTimeStep);
|
|
}
|
|
|
|
#if LAZY_RAGDOLL_BOUNDS_UPDATE
|
|
//-------------------------------------------------------------------------
|
|
// Check for some more conditions that should trigger a bounds update
|
|
//-------------------------------------------------------------------------
|
|
void CPedIntelligence::Process_BoundUpdateRequest()
|
|
{
|
|
if (!m_pPed->IsRagdollBoundsUpdateRequested())
|
|
{
|
|
// players and peds with ActivateRagdollOnCollision enabled should update
|
|
if (m_pPed->IsPlayer() || m_pPed->GetActivateRagdollOnCollision())
|
|
{
|
|
m_pPed->RequestRagdollBoundsUpdate();
|
|
return;
|
|
}
|
|
|
|
// Update if a player is using a special ability
|
|
CPed *pLocalPlayer = CGameWorld::FindLocalPlayer();
|
|
if (pLocalPlayer && pLocalPlayer->GetSpecialAbility() && pLocalPlayer->GetSpecialAbility()->IsActive())
|
|
{
|
|
m_pPed->RequestRagdollBoundsUpdate();
|
|
return;
|
|
}
|
|
|
|
// Should update if on screen and...
|
|
if (m_pPed->m_nDEflags.bIsVisibleInSomeViewportThisFrame)
|
|
{
|
|
// ...a player is aiming a weapon (conservative method to handle stray bullets hitting peds)
|
|
if (pLocalPlayer && pLocalPlayer->GetPedConfigFlag(CPED_CONFIG_FLAG_IsAimingGun))
|
|
{
|
|
m_pPed->RequestRagdollBoundsUpdate();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Should update at least once every X frames at a minimum
|
|
size_t staggerUpdateFrame = ((size_t)m_pPed) + fwTimer::GetFrameCount();
|
|
if (staggerUpdateFrame % ms_MinFramesPerRagdollBoundUpdate == 0)
|
|
{
|
|
m_pPed->RequestRagdollBoundsUpdate(1);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void CPedIntelligence::Process_Tasks(bool fullUpdate, float fTimeStep)
|
|
{
|
|
PF_FUNC(AI_Process_Tasks);
|
|
|
|
// If this reset flag is set, call ProcessMoveSignals() regardless of whether this is a full update or
|
|
// not, so we don't miss out on signals from Move.
|
|
if(m_pPed->GetPedResetFlag(CPED_RESET_FLAG_TasksNeedProcessMoveSignalCalls))
|
|
{
|
|
// Call ProcessMoveSignals() on each tree.
|
|
// Note: currently we don't do this for PED_TASK_TREE_MOVEMENT,
|
|
// which is similar to how it's done for ProcessPostMovement(), etc. We could do it if needed, though.
|
|
bool stillDoingSomething = m_taskManager.ProcessMoveSignals(PED_TASK_TREE_PRIMARY);
|
|
stillDoingSomething |= m_taskManager.ProcessMoveSignals(PED_TASK_TREE_MOTION);
|
|
stillDoingSomething |= m_taskManager.ProcessMoveSignals(PED_TASK_TREE_SECONDARY);
|
|
|
|
// If no task returned true, we let this reset flag reset, stopping the updates.
|
|
if(!stillDoingSomething)
|
|
{
|
|
m_pPed->SetPedResetFlag(CPED_RESET_FLAG_TasksNeedProcessMoveSignalCalls, false);
|
|
}
|
|
}
|
|
|
|
if(fullUpdate)
|
|
{
|
|
// Reset any appropriate reset flags
|
|
m_pPed->m_PedResetFlags.ResetPreTask();
|
|
|
|
if(!m_pPed->IsNetworkClone())
|
|
{
|
|
// Reset the ped's strafing status also
|
|
// If script are forcing the ped to strafe set the ped to strafe
|
|
m_pPed->GetMotionData()->SetIsStrafing(m_pPed->GetPedResetFlag(CPED_RESET_FLAG_ForcePedToStrafe));
|
|
}
|
|
|
|
// Tell the IK manager that we are about to update tasks, for resetting things similar
|
|
// to the reset flags above.
|
|
m_pPed->GetIkManager().PreTaskUpdate();
|
|
|
|
//Process the facial data (pre-tasks).
|
|
Process_FacialDataPreTasks();
|
|
|
|
// Perform the designated task.
|
|
m_taskManager.Process(fTimeStep);
|
|
|
|
// Clear the tasks set needing update since we have just updated this frame
|
|
m_pPed->GetPedAiLod().SetTaskSetNeedIntelligenceUpdate(false);
|
|
|
|
//Process the facial data (post-tasks).
|
|
Process_FacialDataPostTasks(fTimeStep);
|
|
|
|
#if ENABLE_TASKS_ARREST_CUFFED
|
|
// Update cuffed secondary task.
|
|
CTaskPlayCuffedSecondaryAnims::ProcessHandCuffs(*m_pPed);
|
|
#endif // ENABLE_TASKS_ARREST_CUFFED
|
|
|
|
// Block Anim timeslicing for this ped if tasks have been creating lots of network players without an anim update.
|
|
// B* 1909275.
|
|
m_pPed->DoPostTaskUpdateAnimChecks();
|
|
|
|
m_pPed->ClearRenderDelayFlag(CPed::PRDF_DontRenderUntilNextTaskUpdate);
|
|
}
|
|
}
|
|
|
|
|
|
void CPedIntelligence::Process_UpdateVariables(float fTimeStep)
|
|
{
|
|
// If we are a law enforcement ped we will need to make sure certain variables are set
|
|
if(m_pPed->IsLawEnforcementPed())
|
|
{
|
|
// only do this a couple times a second, no exact science so just use magic numbers
|
|
m_fTimeUntilNextCopVariableUpdate -= fTimeStep;
|
|
if(m_fTimeUntilNextCopVariableUpdate <= 0.0f)
|
|
{
|
|
UpdateCopPedVariables();
|
|
m_fTimeUntilNextCopVariableUpdate = fwRandom::GetRandomNumberInRange(0.2f, 0.35f);
|
|
}
|
|
}
|
|
|
|
// Reset the climb strength rate to the default
|
|
SetClimbStrengthRate(DEFAULT_CLIMB_STRENGTH_RATE);
|
|
|
|
// If the ped currently has a coverpoint claimed but is not actually using it,
|
|
// Discard it if it is outside a reasonable range
|
|
if( m_pPed->GetCoverPoint() )
|
|
{
|
|
// Coverpoints of type 'COVTYPE_NONE' should never be available for use.
|
|
// However according to url:bugstar:426681 this has happened, so ensure that
|
|
// we release any COVTYPE_NONE coverpoint immediately if it is found
|
|
if( m_pPed->GetCoverPoint()->GetType() == CCoverPoint::COVTYPE_NONE )
|
|
{
|
|
Assertf(false, "Ped '%s' at (%.1f, %.1f, %.1f) has cover point of type COVTYPE_NONE.", m_pPed->GetModelName(), m_pPed->GetTransform().GetPosition().GetXf(), m_pPed->GetTransform().GetPosition().GetYf(), m_pPed->GetTransform().GetPosition().GetZf() );
|
|
|
|
m_pPed->ReleaseCoverPoint();
|
|
}
|
|
else if( !m_pPed->GetPedConfigFlag( CPED_CONFIG_FLAG_UsingCoverPoint ) && !m_pPed->GetPedResetFlag( CPED_RESET_FLAG_KeepCoverPoint ) )
|
|
{
|
|
Vector3 vCoverPos;
|
|
m_pPed->GetCoverPoint()->GetCoverPointPosition(vCoverPos);
|
|
if( vCoverPos.Dist2( VEC3V_TO_VECTOR3(m_pPed->GetTransform().GetPosition())) > rage::square(3.0f) )
|
|
{
|
|
m_pPed->ReleaseCoverPoint();
|
|
}
|
|
}
|
|
}
|
|
|
|
DecreaseBattleAwareness(fTimeStep * (BATTLE_AWARENESS_MAX/BATTLE_AWARENESS_DEPLETION_TIME_FROM_MAX));
|
|
|
|
// The amount by which the ped is pinned down degrades all the time
|
|
DecreaseAmountPinnedDown( fTimeStep * CTaskInCover::ms_Tunables.m_PinnedDownDecreaseAmountPerSecond );
|
|
|
|
// If this ped is being forced pinned, update the timer and set as pinned
|
|
if( m_fForcedPinnedTimer > 0.0f )
|
|
{
|
|
m_fForcedPinnedTimer = MAX( m_fForcedPinnedTimer - fTimeStep, 0.0f );
|
|
if( m_bForcePinned )
|
|
{
|
|
SetAmountPinnedDown( 100.0f );
|
|
}
|
|
else
|
|
{
|
|
SetAmountPinnedDown( 0.0f );
|
|
}
|
|
}
|
|
|
|
// Reset the movement vector which is tested against the navmesh, to detect peds steering into walls
|
|
m_pPed->GetNavMeshTracker().ResetAvoidanceLineTest();
|
|
m_pPed->GetNavMeshTracker().ResetLookAheadLineTest();
|
|
|
|
// Rest path-following data
|
|
m_bHasClosestPosOnRoute = false;
|
|
|
|
// Update the time for bullet reactions
|
|
m_fTimeTilNextBulletReaction -= fTimeStep;
|
|
|
|
// Flag if any local peds are within an air defence zone.
|
|
if (!m_pPed->IsNetworkClone() && CAirDefenceManager::AreAirDefencesActive())
|
|
{
|
|
u8 uIntersectingZoneIdx = 0;
|
|
if (CAirDefenceManager::IsPositionInDefenceZone(m_pPed->GetTransform().GetPosition(), uIntersectingZoneIdx))
|
|
{
|
|
m_pPed->SetPedResetFlag(CPED_RESET_FLAG_InAirDefenceSphere, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPedIntelligence::Process_UpdateVariablesAfterTaskProcess(float fTimeStep)
|
|
{
|
|
// Update the climb strength
|
|
m_fClimbStrength += m_fClimbStrengthRate * fTimeStep;
|
|
m_fClimbStrength = Clamp(m_fClimbStrength, 0.0f, 1.0f);
|
|
|
|
//if we have a valid order, we shouldn't be culled as readily
|
|
if(GetOrder() && GetOrder()->GetIsValid())
|
|
{
|
|
m_pPed->SetPedResetFlag( CPED_RESET_FLAG_CullExtraFarAway, true );
|
|
m_pPed->SetPedResetFlag( CPED_RESET_FLAG_TaskCullExtraFarAway, true );
|
|
}
|
|
|
|
m_pPed->SetPedResetFlag( CPED_RESET_FLAG_TriggerRoadRageAnim, false );
|
|
}
|
|
|
|
#if __DEV
|
|
void CPedIntelligence::Process_Debug()
|
|
{
|
|
DEV_BREAK_IF_FOCUS( CDebugScene::ShouldDebugBreakOnProcessIntelligenceOfFocusEntity(), m_pPed );
|
|
|
|
m_bNewTaskThisFrame = false;
|
|
|
|
TUNE_GROUP_BOOL(PED_NAVMESH_TRACKING, s_bDisplay, false);
|
|
if(s_bDisplay)
|
|
{
|
|
if(m_pPed->GetNavMeshTracker().GetIsValid())
|
|
{
|
|
#if DEBUG_DRAW
|
|
const Vector3 vPos = m_pPed->GetNavMeshTracker().GetLastNavMeshIntersection();
|
|
grcDebugDraw::Cross(VECTOR3_TO_VEC3V(vPos), 0.25f, Color_white);
|
|
grcDebugDraw::Arrow(VECTOR3_TO_VEC3V(vPos), VECTOR3_TO_VEC3V(vPos+m_pPed->GetNavMeshTracker().GetPolyNormal()), 0.25f, Color_yellow);
|
|
#endif // DEBUG_DRAW
|
|
}
|
|
}
|
|
}
|
|
#endif //__DEV
|
|
|
|
dev_s32 MAX_COPS_ON_FOOT_PLAYER_IN_INTERIOR = 10;
|
|
|
|
void CPedIntelligence::UpdateCopPedVariables()
|
|
{
|
|
if(m_pPed->PopTypeIsMission())
|
|
{
|
|
return;
|
|
}
|
|
|
|
CVehicle* pVehicleInside = m_pPed->GetVehiclePedInside();
|
|
if( m_pPed->GetPedType() == PEDTYPE_COP && !pVehicleInside && !m_pPed->IsInjured() )
|
|
{
|
|
CWanted::sm_iNumCopsOnFoot++;
|
|
}
|
|
|
|
//Grab the combat behavior.
|
|
CCombatBehaviour &combatBehaviour = GetCombatBehaviour();
|
|
|
|
//Check if the cop can be aggressive.
|
|
bool bDontBeAggressive = m_pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_NoCopWantedAggro);
|
|
bool bCanBeAggressive = !bDontBeAggressive;
|
|
if(bCanBeAggressive)
|
|
{
|
|
//Give the cop a radio.
|
|
combatBehaviour.SetFlag(CCombatData::BF_HasRadio);
|
|
|
|
//The cop can chase the target on foot.
|
|
combatBehaviour.SetFlag(CCombatData::BF_CanChaseTargetOnFoot);
|
|
|
|
//The cop should be aggressive.
|
|
combatBehaviour.SetFlag(CCombatData::BF_Aggressive);
|
|
}
|
|
|
|
//Cops should drag their friends to safety.
|
|
combatBehaviour.SetFlag(CCombatData::BF_WillDragInjuredPedsToSafety);
|
|
|
|
if( m_pPed->GetPedType() != PEDTYPE_SWAT )
|
|
{
|
|
combatBehaviour.SetFlag(CCombatData::BF_CanBust);
|
|
}
|
|
|
|
eWantedLevel playerWantedLevel = WANTED_CLEAN;
|
|
CWanted* pLocalPlayerWanted = CGameWorld::FindLocalPlayerWanted();
|
|
if(pLocalPlayerWanted)
|
|
{
|
|
playerWantedLevel = pLocalPlayerWanted->GetWantedLevel();
|
|
}
|
|
|
|
CPedPerception &pPedPerception = GetPedPerception();
|
|
|
|
//Law enforcement peds should not fly through the windscreen.
|
|
#if __DEV
|
|
if (m_pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_WillFlyThroughWindscreen))
|
|
{
|
|
scriptDisplayf("Ped %s set to not fly through windscreen at frame %i, via code CPedIntelligence::UpdateCopPedVariables", m_pPed->GetDebugName() ? m_pPed->GetDebugName() : "NULL", fwTimer::GetFrameCount());
|
|
scrThread::PrePrintStackTrace();
|
|
}
|
|
#endif
|
|
m_pPed->SetPedConfigFlag(CPED_CONFIG_FLAG_WillFlyThroughWindscreen, false);
|
|
weaponAssert(m_pPed->GetWeaponManager());
|
|
|
|
//Keep track of the ped attributes.
|
|
CWanted::PedAttributes attributes;
|
|
|
|
//Check if the wanted is valid.
|
|
if(pLocalPlayerWanted)
|
|
{
|
|
//Calculate the ped attributes.
|
|
pLocalPlayerWanted->CalculatePedAttributes(*m_pPed, attributes);
|
|
}
|
|
|
|
//Set the shoot rate.
|
|
combatBehaviour.SetShootRateModifier(attributes.m_fShootRateModifier);
|
|
|
|
//Set the weapon accuracy.
|
|
combatBehaviour.SetCombatFloat(kAttribFloatWeaponAccuracy, attributes.m_fWeaponAccuracy);
|
|
|
|
//Set the automobile speed modifier.
|
|
combatBehaviour.SetCombatFloat(kAttribFloatAutomobileSpeedModifier, attributes.m_fAutomobileSpeedModifier);
|
|
|
|
//Set the heli speed modifier.
|
|
combatBehaviour.SetCombatFloat(kAttribFloatHeliSpeedModifier, attributes.m_fHeliSpeedModifier);
|
|
|
|
//Set the senses range.
|
|
if (!m_pPed->IsNetworkClone() && !CPedPerception::ms_bCopOverridesSet)
|
|
{
|
|
pPedPerception.SetSeeingRange(attributes.m_fSensesRange);
|
|
pPedPerception.SetHearingRange(attributes.m_fSensesRange);
|
|
}
|
|
|
|
//Set the identification range.
|
|
pPedPerception.SetIdentificationRange(attributes.m_fIdentificationRange);
|
|
|
|
//Set the drivebys flag.
|
|
combatBehaviour.ChangeFlag(CCombatData::BF_CanDoDrivebys, attributes.m_bCanDoDrivebys || (pVehicleInside && pVehicleInside->InheritsFromBike()));
|
|
|
|
//Check if the ped is in a vehicle.
|
|
if(pVehicleInside)
|
|
{
|
|
//Keep track of whether we can leave our vehicle.
|
|
bool bCanLeaveVehicle = false;
|
|
|
|
//Check if the ped is in a heli.
|
|
if(pVehicleInside->InheritsFromHeli())
|
|
{
|
|
//Check if the heli is landed, or in water.
|
|
CHeli* pHeli = static_cast<CHeli *>(pVehicleInside);
|
|
if(pHeli->GetHeliIntelligence()->IsLanded() || (pHeli->m_Buoyancy.GetStatus() != NOT_IN_WATER && pHeli->m_Buoyancy.GetSubmergedLevel() >= 0.5f))
|
|
{
|
|
bCanLeaveVehicle = true;
|
|
}
|
|
}
|
|
//Check if the vehicle is a boat.
|
|
else if(pVehicleInside->InheritsFromBoat())
|
|
{
|
|
bCanLeaveVehicle = true;
|
|
}
|
|
//Combat handles if we can exit the plane in combat so set it to always be true. Cars and bikes should allow exits
|
|
if( pVehicleInside->InheritsFromPlane() ||
|
|
pVehicleInside->GetVehicleType() == VEHICLE_TYPE_CAR ||
|
|
pVehicleInside->InheritsFromBike() )
|
|
{
|
|
bCanLeaveVehicle = true;
|
|
}
|
|
|
|
//Check if we can leave the vehicle.
|
|
if(bCanLeaveVehicle)
|
|
{
|
|
bool bOptimiseVehicleExit = true;
|
|
if( NetworkInterface::IsGameInProgress() )
|
|
bOptimiseVehicleExit = false;
|
|
|
|
if( bOptimiseVehicleExit && playerWantedLevel >= WANTED_LEVEL1)
|
|
{
|
|
bool bIsInWater = (pVehicleInside->m_Buoyancy.GetStatus() != NOT_IN_WATER);
|
|
if(!bIsInWater && !g_StaticBoundsStore.GetBoxStreamer().HasLoadedAboutPos(pVehicleInside->GetTransform().GetPosition(), fwBoxStreamerAsset::FLAG_STATICBOUNDS_MOVER))
|
|
{
|
|
bCanLeaveVehicle = false;
|
|
}
|
|
else if( CGameWorld::FindLocalPlayer() && CGameWorld::FindLocalPlayer()->GetIsInInterior() && CWanted::sm_iNumCopsOnFoot > MAX_COPS_ON_FOOT_PLAYER_IN_INTERIOR )
|
|
{
|
|
bCanLeaveVehicle = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Change the flag.
|
|
combatBehaviour.ChangeFlag(CCombatData::BF_CanLeaveVehicle, bCanLeaveVehicle);
|
|
}
|
|
}
|
|
|
|
CTaskNMBehaviour* CPedIntelligence::GetLowestLevelNMTask(CPed* UNUSED_PARAM(pThisPed)) const
|
|
{
|
|
// Scan through the task tree from the bottom up until a Natural Motion task is
|
|
// found and return a pointer to this task. Null pointer returned if there is no
|
|
// valid NM task.
|
|
|
|
CTaskNMBehaviour* pTaskNM = NULL;
|
|
|
|
CTask* pTask = GetTaskActiveSimplest();
|
|
// Iterate up from the bottom task in the task chain while we have a valid task
|
|
// pointer. Break out early if we find a Natural Motion behaviour sub-task.
|
|
while(pTask && !pTaskNM)
|
|
{
|
|
pTaskNM = pTask->IsNMBehaviourTask() ? (CTaskNMBehaviour *) pTask : NULL;
|
|
pTask = pTask->GetParent();
|
|
}
|
|
|
|
if (!pTaskNM)
|
|
{
|
|
//check the motion task tree too
|
|
CTask* pTask = GetMotionTaskActiveSimplest();
|
|
// Iterate up from the bottom task in the task chain while we have a valid task
|
|
// pointer. Break out early if we find a Natural Motion behaviour sub-task.
|
|
while(pTask && !pTaskNM)
|
|
{
|
|
pTaskNM = pTask->IsNMBehaviourTask() ? (CTaskNMBehaviour *) pTask : NULL;
|
|
pTask = pTask->GetParent();
|
|
}
|
|
}
|
|
|
|
return pTaskNM;
|
|
}
|
|
|
|
CTask* CPedIntelligence::GetLowestLevelRagdollTask(CPed* pThisPed) const
|
|
{
|
|
// Scan through the task tree from the bottom up until a Natural Motion task is
|
|
// found and return a pointer to this task. Null pointer returned if there is no
|
|
// valid NM task.
|
|
|
|
CTask* pTaskRagdoll = NULL;
|
|
|
|
CTask* pTask = GetTaskActiveSimplest();
|
|
// Iterate up from the bottom task in the task chain while we have a valid task
|
|
// pointer. Break out early if we find a Natural Motion behaviour sub-task.
|
|
while(pTask && !pTaskRagdoll)
|
|
{
|
|
pTaskRagdoll = pTask->HandlesRagdoll(pThisPed) ? pTask : NULL;
|
|
pTask = pTask->GetParent();
|
|
}
|
|
|
|
if (!pTaskRagdoll)
|
|
{
|
|
//check the motion task tree too
|
|
CTask* pTask = GetMotionTaskActiveSimplest();
|
|
// Iterate up from the bottom task in the task chain while we have a valid task
|
|
// pointer. Break out early if we find a Natural Motion behaviour sub-task.
|
|
while(pTask && !pTaskRagdoll)
|
|
{
|
|
pTaskRagdoll = pTask->HandlesRagdoll(pThisPed) ? pTask : NULL;
|
|
pTask = pTask->GetParent();
|
|
}
|
|
}
|
|
|
|
return pTaskRagdoll;
|
|
}
|
|
|
|
void CPedIntelligence::HonkedAt(const CVehicle& rVehicle)
|
|
{
|
|
//Get the current time.
|
|
u32 uTime = fwTimer::GetTimeInMilliseconds();
|
|
|
|
//Update the last honked at time.
|
|
u32 uLastTimeHonkedAt = m_uLastTimeHonkedAt;
|
|
m_uLastTimeHonkedAt = uTime;
|
|
|
|
//Ensure we have been honked at quickly and successively.
|
|
TUNE_GROUP_FLOAT(HONK, fMaxTimeBetweenHonksToBecomeAgitated, 5.0f, 0.0f, 60.0f, 1.0f);
|
|
if(uLastTimeHonkedAt + (u32)(fMaxTimeBetweenHonksToBecomeAgitated * 1000.0f) < uTime)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Ensure we haven't been agitated in the last few seconds.
|
|
TUNE_GROUP_FLOAT(HONK, fMinTimeBetweenAgitationEvents, 5.0f, 0.0f, 60.0f, 1.0f);
|
|
if(m_uLastTimeHonkAgitatedUs + (u32)(fMinTimeBetweenAgitationEvents * 1000.0f) > uTime)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Become agitated at the driver.
|
|
CEventAgitated event(rVehicle.GetDriver(), AT_HonkedAt);
|
|
AddEvent(event);
|
|
|
|
//Update the last time a honk agitated us.
|
|
m_uLastTimeHonkAgitatedUs = uTime;
|
|
}
|
|
|
|
void CPedIntelligence::RevvedAt(const CVehicle& rVehicle)
|
|
{
|
|
//Get the current time.
|
|
u32 uTime = fwTimer::GetTimeInMilliseconds();
|
|
|
|
//Update the last revved at time.
|
|
u32 uLastTimeRevvedAt = m_uLastTimeRevvedAt;
|
|
m_uLastTimeRevvedAt = uTime;
|
|
|
|
//Ensure we have been revved at quickly and successively.
|
|
TUNE_GROUP_FLOAT(REV, fMaxTimeBetweenRevsToBecomeAgitated, 5.0f, 0.0f, 60.0f, 1.0f);
|
|
if(uLastTimeRevvedAt + (u32)(fMaxTimeBetweenRevsToBecomeAgitated * 1000.0f) < uTime)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Ensure we haven't been agitated in the last few seconds.
|
|
TUNE_GROUP_FLOAT(REV, fMinTimeBetweenAgitationEvents, 5.0f, 0.0f, 60.0f, 1.0f);
|
|
if(m_uLastTimeRevAgitatedUs + (u32)(fMinTimeBetweenAgitationEvents * 1000.0f) > uTime)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Become agitated at the driver.
|
|
CEventAgitated event(rVehicle.GetDriver(), AT_RevvedAt);
|
|
AddEvent(event);
|
|
|
|
//Update the last time a rev agitated us.
|
|
m_uLastTimeRevAgitatedUs = uTime;
|
|
}
|
|
|
|
void CPedIntelligence::KnockedToGround(const CEntity* pEntity)
|
|
{
|
|
//Set the last entity that knocked us to the ground.
|
|
m_pLastEntityThatKnockedUsToTheGround = pEntity;
|
|
|
|
//Set the time that we were last knocked to the ground.
|
|
m_uLastTimeWeWereKnockedToTheGround = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
|
|
void CPedIntelligence::RammedInVehicle(const CPed* pPed)
|
|
{
|
|
//Set the last ped that rammed us in vehicle.
|
|
m_pLastPedThatRammedUsInVehicle = pPed;
|
|
|
|
//Set the time that we were last rammed in vehicle.
|
|
m_uLastTimeWeWereRammedInVehicle = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
|
|
const CAmbientAudio* CPedIntelligence::GetLastAudioSaidToPed(const CPed& rPed) const
|
|
{
|
|
//Ensure we last talked to the ped.
|
|
if(m_TalkedToPedInfo.m_pPed != &rPed)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return &m_TalkedToPedInfo.m_Audio;
|
|
}
|
|
|
|
bool CPedIntelligence::HasRecentlyTalkedToPed(const CPed& rPed, float fMaxTime) const
|
|
{
|
|
//Ensure we last talked to the ped.
|
|
if(m_TalkedToPedInfo.m_pPed != &rPed)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure we talked to the ped within the max time.
|
|
if(m_TalkedToPedInfo.m_uTime + (u32)(fMaxTime * 1000.0f) < fwTimer::GetTimeInMilliseconds())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CPedIntelligence::HasRecentlyTalkedToPed(const CPed& rPed, float fMaxTime, const CAmbientAudio& rAudio) const
|
|
{
|
|
//Ensure we have recently talked to the ped.
|
|
if(!HasRecentlyTalkedToPed(rPed, fMaxTime))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Ensure we last said the audio.
|
|
if(rAudio.GetPartialHash() != m_TalkedToPedInfo.m_Audio.GetPartialHash())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CPedIntelligence::UpdatePedDecisionMaker(bool bFullUpdate)
|
|
{
|
|
aiAssert(m_pPed);
|
|
m_PedDecisionMaker.Update(*m_pPed, bFullUpdate);
|
|
}
|
|
|
|
void CPedIntelligence::Bumped(const CEntity& rEntity, float fEntitySpeedSq)
|
|
{
|
|
//Check if we are still.
|
|
static dev_float s_fMaxSpeed = 0.5f;
|
|
bool bIsStill = (m_pPed->GetVelocity().Mag2() < square(s_fMaxSpeed));
|
|
if(bIsStill)
|
|
{
|
|
m_uLastTimeBumpedWhenStill = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
|
|
//Check if the other entity is a ped.
|
|
if(rEntity.GetIsTypePed())
|
|
{
|
|
//Check if the ped is a player.
|
|
const CPed& rPed = static_cast<const CPed &>(rEntity);
|
|
if(rPed.IsPlayer())
|
|
{
|
|
//Get the speed.
|
|
if(fEntitySpeedSq < 0.0f)
|
|
{
|
|
fEntitySpeedSq = rPed.GetVelocity().Mag2();
|
|
}
|
|
|
|
//Check if the ped is not still.
|
|
static dev_float s_fMinSpeed = 0.1f;
|
|
float fMinSpeedSq = square(s_fMinSpeed);
|
|
if(fEntitySpeedSq >= fMinSpeedSq)
|
|
{
|
|
//Note that this ped was bumped by a player.
|
|
m_pPed->SetPedConfigFlag(CPED_CONFIG_FLAG_BumpedByPlayer, true);
|
|
|
|
//Let the ambient friend know that their buddy was bumped by a player.
|
|
CPed* pAmbientFriend = m_pPed->GetPedIntelligence()->GetAmbientFriend();
|
|
if (pAmbientFriend)
|
|
{
|
|
pAmbientFriend->SetPedConfigFlag(CPED_CONFIG_FLAG_AmbientFriendBumpedByPlayer, true);
|
|
}
|
|
|
|
if(!m_pPed->IsDead())
|
|
{
|
|
CPlayerSpecialAbilityManager::ChargeEvent(ACET_BUMPED_INTO_PED, const_cast<CPed*>(&rPed), 0.f, 0, this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//Check if the other entity is a vehicle.
|
|
else if(rEntity.GetIsTypeVehicle())
|
|
{
|
|
//Check if the driver is a player.
|
|
const CVehicle& rVehicle = static_cast<const CVehicle &>(rEntity);
|
|
const CPed* pDriver = rVehicle.GetDriver();
|
|
if(pDriver && pDriver->IsPlayer())
|
|
{
|
|
//Get the speed.
|
|
if(fEntitySpeedSq < 0.0f)
|
|
{
|
|
fEntitySpeedSq = rVehicle.GetVelocity().Mag2();
|
|
}
|
|
|
|
//Check if the vehicle is not still.
|
|
static dev_float s_fMinSpeed = 0.2f;
|
|
float fMinSpeedSq = square(s_fMinSpeed);
|
|
if(fEntitySpeedSq >= fMinSpeedSq)
|
|
{
|
|
//Note that the player bumped the ped.
|
|
pDriver->GetPlayerInfo()->BumpedInVehicle(*m_pPed);
|
|
|
|
//Note that this ped was bumped by a player vehicle.
|
|
m_pPed->SetPedConfigFlag(CPED_CONFIG_FLAG_BumpedByPlayerVehicle, true);
|
|
|
|
//Let the ambient friend know that their buddy was bumped by a player.
|
|
CPed* pAmbientFriend = m_pPed->GetPedIntelligence()->GetAmbientFriend();
|
|
if (pAmbientFriend)
|
|
{
|
|
pAmbientFriend->SetPedConfigFlag(CPED_CONFIG_FLAG_AmbientFriendBumpedByPlayerVehicle, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPedIntelligence::Dodged(const CEntity& rEntity, float fEntitySpeedSq)
|
|
{
|
|
//Check if the other entity is a ped.
|
|
if(rEntity.GetIsTypePed())
|
|
{
|
|
//Check if the ped is a player.
|
|
const CPed& rPed = static_cast<const CPed &>(rEntity);
|
|
if(rPed.IsPlayer())
|
|
{
|
|
//Get the speed.
|
|
if(fEntitySpeedSq < 0.0f)
|
|
{
|
|
fEntitySpeedSq = rPed.GetVelocity().Mag2();
|
|
}
|
|
|
|
//Check if the ped is not still.
|
|
static dev_float s_fMinSpeed = 0.1f;
|
|
float fMinSpeedSq = square(s_fMinSpeed);
|
|
if(fEntitySpeedSq >= fMinSpeedSq)
|
|
{
|
|
//Note that this ped dodged a player.
|
|
m_pPed->SetPedConfigFlag(CPED_CONFIG_FLAG_DodgedPlayer, true);
|
|
}
|
|
}
|
|
}
|
|
//Check if the other entity is a vehicle.
|
|
else if(rEntity.GetIsTypeVehicle())
|
|
{
|
|
//Check if the driver is a player.
|
|
const CVehicle& rVehicle = static_cast<const CVehicle &>(rEntity);
|
|
const CPed* pDriver = rVehicle.GetDriver();
|
|
if(pDriver && pDriver->IsPlayer())
|
|
{
|
|
//Get the speed.
|
|
if(fEntitySpeedSq < 0.0f)
|
|
{
|
|
fEntitySpeedSq = rVehicle.GetVelocity().Mag2();
|
|
}
|
|
|
|
//Check if the vehicle is not still.
|
|
static dev_float s_fMinSpeed = 0.2f;
|
|
float fMinSpeedSq = square(s_fMinSpeed);
|
|
if(fEntitySpeedSq >= fMinSpeedSq)
|
|
{
|
|
//Note that this ped dodged a player vehicle.
|
|
m_pPed->SetPedConfigFlag(CPED_CONFIG_FLAG_DodgedPlayerVehicle, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPedIntelligence::Evaded(CEntity* pEntity)
|
|
{
|
|
//Set the last entity evaded.
|
|
m_pLastEntityEvaded = pEntity;
|
|
|
|
//Set the time that we last evaded.
|
|
m_uLastTimeEvaded = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
|
|
void CPedIntelligence::CollidedWithPlayer()
|
|
{
|
|
//Set the time that we last collided with the player.
|
|
m_uLastTimeCollidedWithPlayer = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
|
|
void CPedIntelligence::TriedToSayAudioForDamage()
|
|
{
|
|
//Set the time.
|
|
m_uLastTimeTriedToSayAudioForDamage = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
|
|
void CPedIntelligence::CalledPolice()
|
|
{
|
|
//Set the time.
|
|
//@@: location CPEDINTELLIGENCE_CALLEDPOLICE
|
|
m_uLastTimeCalledPolice = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
|
|
u32 CPedIntelligence::GetCurrentTintIndexForParachute() const
|
|
{
|
|
s32 sIndex = 0;
|
|
|
|
#if __BANK || __ASSERT
|
|
static const u32 nMaxTints = 16;
|
|
#endif
|
|
|
|
//Use network value if we don't own the ped
|
|
if(m_pPed->IsNetworkClone())
|
|
{
|
|
sIndex = GetTintIndexForParachute();
|
|
}
|
|
else
|
|
{
|
|
s32 sOverrideTintIndex = 0;
|
|
|
|
//! Clones will sync the correct tint index depending on this flag being set on a local ped.
|
|
if(m_pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_UseReserveParachute))
|
|
{
|
|
sOverrideTintIndex = GetTintIndexForReserveParachute();
|
|
}
|
|
else
|
|
{
|
|
sOverrideTintIndex = GetTintIndexForParachute();
|
|
}
|
|
|
|
#if __BANK
|
|
TUNE_GROUP_BOOL(TASK_PARACHUTE, bOverrideTintIndexForParachute, false);
|
|
if (bOverrideTintIndexForParachute)
|
|
{
|
|
TUNE_GROUP_INT(TASK_PARACHUTE, uOverrideTintIndexForParachute, 0, -1, nMaxTints, 1);
|
|
sOverrideTintIndex = (u32)uOverrideTintIndexForParachute;
|
|
}
|
|
#endif
|
|
|
|
if(sOverrideTintIndex >= 0)
|
|
{
|
|
sIndex = sOverrideTintIndex;
|
|
}
|
|
else
|
|
{
|
|
//Randomise in single player only
|
|
bool bRandomize = (!NetworkInterface::IsGameInProgress() && !CTheScripts::GetPlayerIsOnAMission());
|
|
if(bRandomize)
|
|
{
|
|
sIndex = (u32)fwRandom::GetRandomNumberInRange(0, 8);
|
|
}
|
|
}
|
|
}
|
|
|
|
//Ensure index is >= 0. I.e. -1 defaults to 0.
|
|
u32 uIndex = (u32)MAX(0, sIndex);
|
|
|
|
pedAssertf(uIndex < nMaxTints, "Tint index out of range. %d", uIndex);
|
|
|
|
return uIndex;
|
|
}
|
|
|
|
///////////////////////////////////////////////////
|
|
// CLASS: CPedEventDecisionMaker
|
|
// PURPOSE: Ped decision making instance data
|
|
///////////////////////////////////////////////////
|
|
|
|
//
|
|
bool CPedEventDecisionMaker::IsEventResponseDisabled(const eEventType eventType) const
|
|
{
|
|
const SResponse* pPedResponse = FindResponse(eventType);
|
|
if (pPedResponse)
|
|
{
|
|
return !pPedResponse->HasCooldownExpired();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//
|
|
void CPedEventDecisionMaker::OnDecisionMade(const CEvent& rEvent, const CEventResponseTheDecision& rDecision)
|
|
{
|
|
const CEventDecisionMakerResponse* const pResponse = rDecision.GetResponse();
|
|
if (!aiVerify(pResponse))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Calculate cooldown
|
|
const CEventDecisionMakerResponse::sCooldown& rResponseCooldown = pResponse->GetCooldown();
|
|
if (rResponseCooldown.Time > 0.0f)
|
|
{
|
|
SResponse* pPedResponse = FindResponse(pResponse->GetEventType());
|
|
if (pPedResponse)
|
|
{
|
|
pPedResponse->vEventPosition = rEvent.GetEntityOrSourcePosition();
|
|
pPedResponse->uEventTS = fwTimer::GetTimeInMilliseconds();
|
|
pPedResponse->Cooldown = rResponseCooldown;
|
|
}
|
|
else
|
|
{
|
|
AddResponse(pResponse->GetEventType(), rResponseCooldown, rEvent.GetEntityOrSourcePosition(), fwTimer::GetTimeInMilliseconds());
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
void CPedEventDecisionMaker::Update(const CPed& rPed, bool bFullUpdate)
|
|
{
|
|
if (bFullUpdate)
|
|
{
|
|
UpdateResponses(rPed);
|
|
}
|
|
}
|
|
|
|
//
|
|
void CPedEventDecisionMaker::UpdateResponses(const CPed& rPed)
|
|
{
|
|
const Vec3V vPedPosition = rPed.GetTransform().GetPosition();
|
|
const eEventType ePedCurrentEventType = static_cast<eEventType>(rPed.GetPedIntelligence()->GetCurrentEventType());
|
|
|
|
u32 uNumResponses = m_aResponses.GetCount();
|
|
for (u32 uResponseIdx = 0; uResponseIdx < uNumResponses; )
|
|
{
|
|
SResponse& rPedResponse = m_aResponses[uResponseIdx];
|
|
|
|
const bool bRemoveResponse = rPedResponse.HasCooldownExpired() || IsGreaterThanAll(DistSquared(vPedPosition, rPedResponse.vEventPosition), ScalarV(rage::square(rPedResponse.Cooldown.MaxDistance)));
|
|
if (!bRemoveResponse)
|
|
{
|
|
if (ePedCurrentEventType == rPedResponse.eventType)
|
|
{
|
|
rPedResponse.uEventTS = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
|
|
++uResponseIdx;
|
|
}
|
|
else
|
|
{
|
|
m_aResponses.DeleteFast(uResponseIdx);
|
|
aiAssert(uNumResponses > 0);
|
|
--uNumResponses;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
CPedEventDecisionMaker::SResponse* CPedEventDecisionMaker::AddResponse(const eEventType eventType, const CEventDecisionMakerResponse::sCooldown& rCooldown, Vec3V_In vPosition, u32 uTS)
|
|
{
|
|
aiAssert(!FindResponse(eventType));
|
|
|
|
SResponse& rResponse = m_aResponses.Grow();
|
|
|
|
rResponse.eventType = eventType;
|
|
rResponse.vEventPosition = vPosition;
|
|
rResponse.uEventTS = uTS;
|
|
rResponse.Cooldown = rCooldown;
|
|
|
|
return &rResponse;
|
|
}
|
|
|
|
//
|
|
CPedEventDecisionMaker::SResponse* CPedEventDecisionMaker::FindResponse(const eEventType eventType)
|
|
{
|
|
const u32 uNumResponses = m_aResponses.GetCount();
|
|
for (u32 uResponseIdx = 0; uResponseIdx < uNumResponses; ++uResponseIdx)
|
|
{
|
|
SResponse& rResponse = m_aResponses[uResponseIdx];
|
|
if (rResponse.eventType == eventType)
|
|
{
|
|
return &rResponse;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
const CPedEventDecisionMaker::SResponse* CPedEventDecisionMaker::FindResponse(const eEventType eventType) const
|
|
{
|
|
const u32 uNumResponses = m_aResponses.GetCount();
|
|
for (u32 uResponseIdx = 0; uResponseIdx < uNumResponses; ++uResponseIdx)
|
|
{
|
|
const SResponse& rResponse = m_aResponses[uResponseIdx];
|
|
if (rResponse.eventType == eventType)
|
|
{
|
|
return &rResponse;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
const CEventDecisionMaker* CPedEventDecisionMaker::GetDecisionMaker() const
|
|
{
|
|
return CEventDecisionMakerManager::GetDecisionMaker(m_uDecisionMakerId);
|
|
}
|
|
|
|
//
|
|
CEventDecisionMaker* CPedEventDecisionMaker::GetDecisionMaker()
|
|
{
|
|
return CEventDecisionMakerManager::GetDecisionMaker(m_uDecisionMakerId);
|
|
}
|
|
|
|
#if !__FINAL
|
|
//
|
|
const char* CPedEventDecisionMaker::GetDecisionMakerName() const
|
|
{
|
|
const CEventDecisionMaker* pDecisionMaker = GetDecisionMaker();
|
|
return pDecisionMaker ? pDecisionMaker->GetName() : "null";
|
|
}
|
|
#endif // __FINAL
|
|
|
|
//
|
|
bool CPedEventDecisionMaker::HandlesEventOfType(const eEventType eventType) const
|
|
{
|
|
const CEventDecisionMaker* pDecisionMaker = GetDecisionMaker();
|
|
return pDecisionMaker && pDecisionMaker->HandlesEventOfType(eventType);
|
|
}
|
|
|
|
//
|
|
void CPedEventDecisionMaker::MakeDecision(const CPed* pPed, const CEventEditableResponse* pEvent, CEventResponseTheDecision &theDecision)
|
|
{
|
|
if (!aiVerify(pPed && pEvent))
|
|
{
|
|
return;
|
|
}
|
|
|
|
const eEventType eventType = static_cast<eEventType>(pEvent->GetEventType());
|
|
|
|
if (!IsEventResponseDisabled(eventType))
|
|
{
|
|
const CEventDecisionMaker* pDecisionMaker = GetDecisionMaker();
|
|
if(aiVerifyf(pDecisionMaker, "Ped does not have a valid decision maker"))
|
|
{
|
|
pDecisionMaker->MakeDecision(pPed, pEvent, theDecision);
|
|
const CTaskTypes::eTaskType taskType = (CTaskTypes::eTaskType)theDecision.GetTaskType();
|
|
if(taskType != CTaskTypes::TASK_INVALID_ID)
|
|
{
|
|
OnDecisionMade(*pEvent, theDecision);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#if __DEV
|
|
void CPedIntelligence::AddScriptHistoryString(const char* pStaticString, const char* pStaticTaskString, const char* scriptName, const s32 iProgramCounter)
|
|
{
|
|
// Record the task in the debug list
|
|
m_aScriptHistoryName[m_iCurrentHistroyTop] = pStaticString;
|
|
m_aScriptHistoryTaskName[m_iCurrentHistroyTop] = pStaticTaskString;
|
|
m_aScriptHistoryTime[m_iCurrentHistroyTop] = fwTimer::GetTimeInMilliseconds();
|
|
strncpy(m_szScriptName[m_iCurrentHistroyTop], scriptName, 32);
|
|
m_aProgramCounter[m_iCurrentHistroyTop] = iProgramCounter;
|
|
++m_iCurrentHistroyTop;
|
|
if(m_iCurrentHistroyTop >= MAX_DEBUG_SCRIPT_TASK_HISTORY)
|
|
{
|
|
m_iCurrentHistroyTop = 0;
|
|
}
|
|
|
|
m_bNewTaskThisFrame = true;
|
|
}
|
|
#endif
|
|
|
|
#if __BANK
|
|
void CPedIntelligence::AddWidgets(bkBank& bank)
|
|
{
|
|
bank.AddSlider("Battle Awareness Whizby", &BATTLE_AWARENESS_BULLET_WHIZBY, 0.0f, 1000.0f, 0.1f);
|
|
bank.AddSlider("Battle Awareness Bullet Impact", &BATTLE_AWARENESS_BULLET_IMPACT, 0.0f, 1000.0f, 0.1f);
|
|
bank.AddSlider("Battle Awareness Gun Shot", &BATTLE_AWARENESS_GUNSHOT, 0.0f, 1000.0f, 0.1f);
|
|
bank.AddSlider("Battle Awareness Gun Shot Local Player", &BATTLE_AWARENESS_GUNSHOT_LOCAL_PLAYER, 0.0f, 1000.0f, 0.1f);
|
|
bank.AddSlider("Battle Awareness Damage", &BATTLE_AWARENESS_DAMAGE, 0.0f, 1000.0f, 0.1f);
|
|
bank.AddSlider("Battle Awareness Min", &BATTLE_AWARENESS_MIN, 0.0f, 1000.0f, 0.1f);
|
|
bank.AddSlider("Battle Awareness Max", &BATTLE_AWARENESS_MAX, 100.0f, 1000.0f, 0.1f);
|
|
bank.AddSlider("Battle Awareness Depletion Time from max", &BATTLE_AWARENESS_DEPLETION_TIME_FROM_MAX, 1.0f, 1000.0f, 0.1f);
|
|
bank.AddSlider("Battle Awareness Min Time", &BATTLE_AWARENESS_MIN_TIME, 0.0f, 1000.0f, 0.1f);
|
|
bank.AddSlider("Battle Awareness Gun Shot Local Player Cooldown", &BATTLE_AWARENESS_GUNSHOT_LOCAL_PLAYER_COOLDOWN, 0, 5000, 100);
|
|
}
|
|
#endif
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// EVENT HISTORY
|
|
|
|
#if DEBUG_EVENT_HISTORY
|
|
|
|
//
|
|
const char* CPedIntelligence::SEventHistoryEntry::SState::GetName(Enum state)
|
|
{
|
|
static const char* apszStateNames[] =
|
|
{
|
|
"CREATED",
|
|
"DISCARDED_PRIORITY",
|
|
"DISCARDED_CANNOT_INTERRUPT_SAME_EVENT",
|
|
"TRYING_TO_ABORT_TASK",
|
|
"ACCEPTED",
|
|
"REMOVED",
|
|
"DELETED",
|
|
"EXPIRED"
|
|
};
|
|
|
|
CompileTimeAssert(sizeof(apszStateNames) / sizeof(const char*) == COUNT);
|
|
|
|
if (aiVerify(state < COUNT))
|
|
{
|
|
return apszStateNames[state];
|
|
}
|
|
|
|
return "INVALID";
|
|
}
|
|
|
|
|
|
CPedIntelligence::SEventHistoryEntry::SEventHistoryEntry()
|
|
: eType(EVENT_INVALID)
|
|
, sSourceDescription("")
|
|
, sEventDescription("")
|
|
, vPosition(V_ZERO)
|
|
, uCreatedTS(0u)
|
|
, uModifiedTS(0u)
|
|
, uProcessedTS(0u)
|
|
//, bProcessedReady(false)
|
|
, bProcesedValidForPed(false)
|
|
//, bProcessedAffectsPed(false)
|
|
, bProcessedCalculatedResponse(false)
|
|
, bProcessedWillRespond(false)
|
|
, uSelectedAsHighestTS(0u)
|
|
, eState(SState::INVALID)
|
|
, fDelayTimerMax(0.0f)
|
|
, fDelayTimerLast(0.0f)
|
|
, pEvent(NULL)
|
|
{
|
|
}
|
|
|
|
CPedIntelligence::SEventHistoryEntry::SEventHistoryEntry(const CEvent& rEvent)
|
|
: eType(static_cast<eEventType>(rEvent.GetEventType()))
|
|
, sSourceDescription(AILogging::GetEntityDescription(rEvent.GetSourceEntity()))
|
|
, sEventDescription(rEvent.GetDescription())
|
|
, vPosition(rEvent.GetEntityOrSourcePosition())
|
|
, uCreatedTS(fwTimer::GetFrameCount())
|
|
, uModifiedTS(fwTimer::GetFrameCount())
|
|
, uProcessedTS(0u)
|
|
//, bProcessedReady(false)
|
|
, bProcesedValidForPed(false)
|
|
//, bProcessedAffectsPed(false)
|
|
, bProcessedCalculatedResponse(false)
|
|
, bProcessedWillRespond(false)
|
|
, uSelectedAsHighestTS(0u)
|
|
, eState(SState::CREATED)
|
|
, fDelayTimerMax(rEvent.GetDelayTimer())
|
|
, fDelayTimerLast(rEvent.GetDelayTimer())
|
|
, pEvent(&rEvent)
|
|
{
|
|
}
|
|
|
|
void CPedIntelligence::AddToEventHistory(const CEvent& rEvent)
|
|
{
|
|
SEventHistoryEntry CurrentEventEntry;
|
|
if (static_cast<size_t>(m_EventHistory.GetCount()) == m_EventHistory.Size())
|
|
{
|
|
SEventHistoryEntry& rFirstEntry = m_EventHistory.Get(0);
|
|
if (rFirstEntry.pEvent && rFirstEntry.pEvent == GetCurrentEvent())
|
|
{
|
|
CurrentEventEntry = rFirstEntry;
|
|
}
|
|
}
|
|
|
|
m_EventHistory.Append(SEventHistoryEntry(rEvent));
|
|
|
|
if (CurrentEventEntry.pEvent)
|
|
{
|
|
m_EventHistory[0] = CurrentEventEntry;
|
|
}
|
|
}
|
|
|
|
void CPedIntelligence::SetEventHistoryEntryState(const CEvent& rEvent, SEventHistoryEntry::SState::Enum eState)
|
|
{
|
|
SEventHistoryEntry* pEntry = FindEventHistoryEntry(rEvent);
|
|
if (pEntry)
|
|
{
|
|
pEntry->eState = eState;
|
|
pEntry->uModifiedTS = fwTimer::GetFrameCount();
|
|
pEntry->fDelayTimerLast = rEvent.GetDelayTimer();
|
|
pEntry->fDelayTimerMax = Max(pEntry->fDelayTimerMax, pEntry->fDelayTimerLast);
|
|
}
|
|
}
|
|
|
|
void CPedIntelligence::SetEventHistoryEntryProcessed(const CEvent& rEvent)
|
|
{
|
|
SEventHistoryEntry* pEntry = FindEventHistoryEntry(rEvent);
|
|
if (pEntry)
|
|
{
|
|
pEntry->uProcessedTS = fwTimer::GetFrameCount();
|
|
pEntry->uProcessedTS = fwTimer::GetFrameCount();
|
|
//pEntry->bProcessedReady = rEvent.IsEventReady(m_pPed);
|
|
pEntry->bProcesedValidForPed = rEvent.IsValidForPed(m_pPed);
|
|
//pEntry->bProcessedAffectsPed = pEntry->bProcesedValidForPed ? rEvent.AffectsPed(m_pPed) : false;
|
|
if (rEvent.HasEditableResponse())
|
|
{
|
|
const CEventEditableResponse& rEventEditable = static_cast<const CEventEditableResponse&>(rEvent);
|
|
pEntry->bProcessedCalculatedResponse = rEventEditable.HasCalculatedResponse();
|
|
pEntry->bProcessedWillRespond = rEventEditable.WillRespond();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPedIntelligence::SetEventHistoryEntrySelectedAsHighest(const CEvent& rEvent)
|
|
{
|
|
SEventHistoryEntry* pEntry = FindEventHistoryEntry(rEvent);
|
|
if (pEntry)
|
|
{
|
|
pEntry->uSelectedAsHighestTS = fwTimer::GetFrameCount();
|
|
}
|
|
}
|
|
|
|
|
|
void CPedIntelligence::EventHistoryOnEventSetTaskBegin(const CEvent& rEvent)
|
|
{
|
|
SEventHistoryEntry* pEntry = FindEventHistoryEntry(rEvent);
|
|
if (pEntry)
|
|
{
|
|
for (u32 uTaskTreeIdx = 0; uTaskTreeIdx < PED_TASK_TREE_MAX; ++uTaskTreeIdx)
|
|
{
|
|
aiTask* pTask = m_taskManager.GetActiveTask(uTaskTreeIdx);
|
|
pEntry->eBeforeAcceptedActiveTaskType[uTaskTreeIdx] = pTask ? static_cast<CTaskTypes::eTaskType>(pTask->GetTaskType()) : CTaskTypes::TASK_NONE;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPedIntelligence::EventHistoryOnEventSetTaskEnd(const CEvent& rEvent)
|
|
{
|
|
SEventHistoryEntry* pEntry = FindEventHistoryEntry(rEvent);
|
|
if (pEntry)
|
|
{
|
|
for (u32 uTaskTreeIdx = 0; uTaskTreeIdx < PED_TASK_TREE_MAX; ++uTaskTreeIdx)
|
|
{
|
|
aiTask* pTask = m_taskManager.GetActiveTask(uTaskTreeIdx);
|
|
pEntry->eAfterAcceptedActiveTaskType[uTaskTreeIdx] = pTask ? static_cast<CTaskTypes::eTaskType>(pTask->GetTaskType()) : CTaskTypes::TASK_NONE;
|
|
}
|
|
|
|
CEvent* pCurrentEvent = GetCurrentEvent();
|
|
if (pCurrentEvent && *pCurrentEvent == rEvent)
|
|
{
|
|
pEntry->pEvent = pCurrentEvent;
|
|
}
|
|
}
|
|
|
|
SetEventHistoryEntryState(rEvent, SEventHistoryEntry::SState::ACCEPTED);
|
|
}
|
|
|
|
void CPedIntelligence::EventHistoryOnEventRemoved(const CEvent& rEvent)
|
|
{
|
|
SetEventHistoryEntryState(rEvent, SEventHistoryEntry::SState::REMOVED);
|
|
}
|
|
|
|
CPedIntelligence::SEventHistoryEntry* CPedIntelligence::FindEventHistoryEntry(const CEvent& rEvent)
|
|
{
|
|
const u32 uEventCount = m_EventHistory.GetCount();
|
|
for (u32 uEventIdx = 0; uEventIdx < uEventCount; ++uEventIdx)
|
|
{
|
|
SEventHistoryEntry& rEntry = m_EventHistory[uEventIdx];
|
|
if (rEntry.pEvent == &rEvent)
|
|
{
|
|
return &rEntry;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void CPedIntelligence::EventHistoryMarkExpiredEvents()
|
|
{
|
|
const u32 uNumEvents = GetNumEventsInQueue();
|
|
for (u32 uEventIdx = 0; uEventIdx < uNumEvents; ++uEventIdx)
|
|
{
|
|
const CEvent* pEvent = GetEventByIndex(uEventIdx);
|
|
if (AssertVerify(pEvent) && !pEvent->GetBeingProcessed() && pEvent->HasExpired())
|
|
{
|
|
SetEventHistoryEntryState(*pEvent, SEventHistoryEntry::SState::EXPIRED);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPedIntelligence::EventHistoryMarkProcessedEvents()
|
|
{
|
|
const u32 uNumEvents = GetNumEventsInQueue();
|
|
for (u32 uEventIdx = 0; uEventIdx < uNumEvents; ++uEventIdx)
|
|
{
|
|
CEvent* pEvent = GetEventByIndex(uEventIdx);
|
|
if (AssertVerify(pEvent))
|
|
{
|
|
SetEventHistoryEntryProcessed(*pEvent);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif //DEBUG_EVENT_HISTORY
|
|
|