bool CAISensorPassTarget::ContinueHoldingPosition( bool* pbCalledFindPath ) { // Continue holding if AI does not have a new cover or ambush dest. CAIWMFact factQuery; factQuery.SetFactType( kFact_Task ); factQuery.SetTaskType( kTask_Cover ); CAIWMFact* pFact = m_pAI->GetAIWorkingMemory()->FindWMFact( factQuery ); if( !pFact ) { factQuery.SetTaskType( kTask_Ambush ); pFact = m_pAI->GetAIWorkingMemory()->FindWMFact( factQuery ); } if( !( pFact && ( pFact->GetConfidence( CAIWMFact::kFactMask_TaskType ) == 1.f ) ) ) { return true; } // Stop holding position if AI is scripted to go for cover. if( pFact->GetFactFlags() & kFactFlag_Scripted ) { return false; } // Continue holding if cover dest is not valid. HOBJECT hNode = pFact->GetTargetObject(); AINode* pNode = (AINode*)g_pLTServer->HandleToObject( hNode ); if( !( pNode && pNode->IsNodeValid( m_pAI, m_pAI->GetPosition(), NULL, kThreatPos_TargetPos, kNodeStatus_Avoid ) ) ) { return true; } // Continue holding if no path exists to the node. *pbCalledFindPath = true; static CAIPathNavMesh NMPath; if( !FindPathToNode( hNode, &NMPath ) ) { return true; } // Continue holding if path crosses target's position. ENUM_NMPolyID ePolyTarget = GetTargetNavMeshPoly(); if( PathIncludesPoly( NMPath, ePolyTarget ) ) { AvoidNode( hNode ); return true; } // Stop holding position! // We are ready to move somewhere new for cover or ambush. return false; }
HOBJECT CAIGoalFlee::FindBestNode( EnumAINodeType eNodeType ) { // Return the cached node if still valid. // This ensures an AI keeps heading to the same node if interrupted. if( m_hCachedBestNode ) { HOBJECT hThreat = m_pAI->GetAIBlackBoard()->GetBBTargetObject(); AINode* pNode = (AINode*)g_pLTServer->HandleToObject( m_hCachedBestNode ); if( pNode->IsNodeValid( m_pAI, m_pAI->GetPosition(), hThreat, kThreatPos_TargetPos, m_dwNodeStatus ) ) { return m_hCachedBestNode; } } // Default behavior. HOBJECT hNode = super::FindBestNode( eNodeType ); if( hNode ) { return hNode; } // Fail-safe. uint32 dwNodeStatus = kNodeStatus_Disabled | kNodeStatus_LockedByOther | kNodeStatus_ThreatInsideRadius; HOBJECT hThreat = m_pAI->GetAIBlackBoard()->GetBBTargetObject(); CAIWMFact* pFact = m_pAI->GetAIWorkingMemory()->FindFactNodeMax( m_pAI, kNode_Cover, dwNodeStatus, m_hNode, hThreat ); if( pFact ) { return pFact->GetTargetObject(); } pFact = m_pAI->GetAIWorkingMemory()->FindFactNodeMax( m_pAI, kNode_Ambush, dwNodeStatus, m_hNode, hThreat ); if( pFact ) { return pFact->GetTargetObject(); } // No nodes. return NULL; }
void CAIGoalUseArmoredAutonomous::CalculateGoalRelevance() { // Using armored nodes requires a ranged weapon. if( !AIWeaponUtils::HasWeaponType( m_pAI, kAIWeaponType_Ranged, AIWEAP_CHECK_HOLSTER ) ) { m_fGoalRelevance = 0.f; return; } // Using armored nodes require ammo to fire the ranged weapon. if( !AIWeaponUtils::HasWeaponType( m_pAI, kAIWeaponType_Ranged, AIWEAP_CHECK_HOLSTER ) ) { m_fGoalRelevance = 0.f; return; } // If we are not executing the goal, and if we are already at a valid // node of the correct type, then the goal is not relevant. if (!IsGoalInProgress()) { SAIWORLDSTATE_PROP* pAtNodeProp = m_pAI->GetAIWorldState()->GetWSProp( kWSK_AtNodeType, m_pAI->m_hObject ); if (pAtNodeProp && ( pAtNodeProp->eAINodeTypeWSValue == m_pGoalRecord->eNodeType) ) { SAIWORLDSTATE_PROP* pAtNode = m_pAI->GetAIWorldState()->GetWSProp( kWSK_AtNode, m_pAI->m_hObject ); if (pAtNode) { AINode* pNode = AINode::HandleToObject(pAtNode->hWSValue); if( pNode && pNode->IsNodeValid( m_pAI, m_pAI->GetPosition(), m_pAI->GetAIBlackBoard()->GetBBTargetObject(), kThreatPos_TargetPos, kNodeStatus_All ) ) { m_fGoalRelevance = 0.f; return; } } } } // Default Behavior. super::CalculateGoalRelevance(); }
bool CAIGoalStalk::IsPlanValid() { if (!super::IsPlanValid()) { return false; } // Insure the stalking node is still valid. AINode* pNode = AINode::HandleToObject(m_hStalkingNode); if (!pNode) { return false; } if(!pNode->IsNodeValid( m_pAI, m_pAI->GetPosition(), m_pAI->GetAIBlackBoard()->GetBBTargetObject(), kThreatPos_TargetPos, kNodeStatus_All ) ) { return false; } return true; }
bool CAIActionAttackGrenade::ValidateContextPreconditions( CAI* pAI, CAIWorldState& wsWorldStateGoal, bool bIsPlanning ) { // Target is not visible. if( !pAI->HasTarget( kTarget_Character | kTarget_Object ) ) { return false; } // AI does not have a weapon of the correct type if (!AIWeaponUtils::HasWeaponType(pAI, GetWeaponType(), bIsPlanning)) { return false; } // AI does not have any ammo for this weapon. if ( !AIWeaponUtils::HasAmmo( pAI, GetWeaponType(), bIsPlanning ) ) { return false; } // At a node that does not allow grenade throwing. bool bAtNode = false; bool bStraightPathCheckRequired = true; SAIWORLDSTATE_PROP* pProp = pAI->GetAIWorldState()->GetWSProp( kWSK_AtNode, pAI->m_hObject ); if( pProp && pProp->hWSValue ) { AINode* pNode = (AINode*)g_pLTServer->HandleToObject( pProp->hWSValue ); if( !pNode ) { return false; } if( !pNode->AllowThrowGrenades() ) { return false; } if( !pNode->IsNodeValid( pAI, pAI->GetPosition(), pAI->GetAIBlackBoard()->GetBBTargetObject(), kThreatPos_TargetPos, kNodeStatus_ThreatOutsideFOV ) ) { return false; } bStraightPathCheckRequired = pNode->RequiresStraightPathToThrowGrenades(); bAtNode = true; } // Target is not in range. if (!AIWeaponUtils::IsInRange(pAI, GetWeaponType(), bIsPlanning)) { return false; } // Someone else has thrown a grenade recently. CAIWMFact factQuery; factQuery.SetFactType(kFact_Knowledge); factQuery.SetKnowledgeType(kKnowledge_NextGrenadeTime); CAIWMFact* pFact = g_pAIWorkingMemoryCentral->FindWMFact(factQuery); if(pFact && g_pLTServer->GetTime() < pFact->GetTime() ) { return false; } // Throw at the last known position. CAIWMFact factTargetQuery; HOBJECT hTarget = pAI->GetAIBlackBoard()->GetBBTargetObject(); factTargetQuery.SetFactType( kFact_Character ); factTargetQuery.SetTargetObject( hTarget ); pFact = pAI->GetAIWorkingMemory()->FindWMFact( factTargetQuery ); if( !pFact ) { return false; } LTVector vTargetPos = pFact->GetPos(); /*** // Only throw a grenade when not at a node when there is a // straight path to the target. This is to ensure the // grenade won't bounce back and kill the thrower. if( bStraightPathCheckRequired ) { if( !g_pAIPathMgrNavMesh->StraightPathExists( pAI, pAI->GetCharTypeMask(), pAI->GetPosition(), vTargetPos, pAI->GetLastNavMeshPoly(), 0.f ) ) { return false; } } ***/ /*** // Don't throw if someone is closer to the target than me, // because I might hit them! if( pAI != g_pCharacterMgr->FindNearestAIAlly( pAI, vTargetPos ) ) { return false; } ***/ // Get the grenade explosion radius. HAMMO hAmmo = AIWeaponUtils::GetWeaponAmmo( pAI, GetWeaponType(), bIsPlanning ); HAMMODATA hAmmoData = g_pWeaponDB->GetAmmoData(hAmmo,true); float fRadius = g_pWeaponDB->GetFloat( hAmmoData, WDB_AMMO_fAreaDamageRadius ); fRadius *= 1.2f; // Don't throw if we're in the radius! if( pAI->GetPosition().DistSqr( vTargetPos ) < fRadius * fRadius ) { return false; } // Don't throw if an ally is in the blast radius. if( g_pCharacterMgr->FindAIAllyInRadius( pAI, vTargetPos, fRadius ) ) { return false; } /*** // Don't throw grenade at allies. CTList<CCharacter*> lstChars; if( g_pCharacterMgr->FindCharactersWithinRadius( &lstChars, vTargetPos, fRadius, CCharacterMgr::kList_AIs ) ) { // Iterate over characters in grenade's radius. CCharacter** pCur = lstChars.GetItem(TLIT_FIRST); while( pCur ) { CCharacter* pChar = (CCharacter*)*pCur; pCur = lstChars.GetItem(TLIT_NEXT); // Skip the AI himself. if( pChar == pAI ) { continue; } // Action is invalid if grenade could hit someone // that the AI does not hate. if( g_pCharacterDB->GetStance( pAI->GetAlignment(), pChar->GetAlignment() ) != kCharStance_Hate ) { return false; } } } ***/ // Throw a grenade. return true; }
void CAIGoalGoto::CalculateGoalRelevance() { // Wait until after teleporting to Goto. // This is necessary due to update ordering between CAI and CCharacter. if( m_pAI->GetTeleportTriggerState() != eTeleporTriggerStateNone ) { m_fGoalRelevance = 0.f; return; } // Bail if we have no Goto task. CAIWMFact factQuery; factQuery.SetFactType( kFact_Task ); factQuery.SetTaskType( m_eTaskType ); CAIWMFact* pFact = m_pAI->GetAIWorkingMemory()->FindWMFact( factQuery ); if( pFact && pFact->IsSet( CAIWMFact::kFactMask_TargetObject ) && pFact->GetTargetObject() && ( pFact->GetConfidence( CAIWMFact::kFactMask_TaskType ) == 1.f ) ) { // Bail if node is disabled or invalid. HOBJECT hNode = pFact->GetTargetObject(); if( IsAINode( hNode ) ) { // Node is disabled. AINode* pNode = (AINode*)g_pLTServer->HandleToObject( hNode ); if( pNode->IsNodeDisabled() ) { hNode = NULL; m_fGoalRelevance = 0.f; return; } // Node is invalid. if( m_bCheckNodeValidity && !pNode->IsNodeValid( m_pAI, m_pAI->GetPosition(), NULL, kThreatPos_TargetPos, kNodeStatus_All ) ) { hNode = NULL; m_fGoalRelevance = 0.f; return; } } // New node requested. if( m_NodeCurrent.eFactID != pFact->GetFactID() ) { m_NodePending.hNode = hNode; m_NodePending.eFactID = pFact->GetFactID(); m_NodePending.bTaskIsScripted = ( pFact->GetFactFlags() & kFactFlag_Scripted ) ? true : false; } m_fGoalRelevance = m_pGoalRecord->fIntrinsicRelevance; return; } // There is no Goto task. m_fGoalRelevance = 0.f; }
int operator()(CAIWMFact* pFact) { // Ignore facts that are not node facts. // Ignore node facts with the wrong node type. // Ignore node facts whose object matches the hExclude. if( ( pFact->GetFactType() != kFact_Node ) || ( pFact->GetNodeType() != kNode_Stalk )) { return 0; } AINode* pNode = AINode::HandleToObject( pFact->GetTargetObject() ); if( !pNode ) { return 0; } // Bail if node is locked by someone else, disabled, or timed out. // TODO: Handle the AI locking the node. This can work when code to // handle an invalid node is written. if (pNode->GetHOBJECT() == m_hIgnoreStalkingNode) { return false; } if (pNode->IsNodeLocked()/* && pNode->GetLockingAI() != m_pAI->GetHOBJECT()*/) { return false; } if( pNode->IsNodeDisabled() || pNode->IsNodeTimedOut() ) { return false; } // Require the AI be in the radius or region if (!pNode->IsAIInRadiusOrRegion( m_pAI, m_pAI->GetPosition(), 1.f )) { return 0; } // If AI is aware of a threat, ignore nodes that are invalid. if( m_hThreat && ( !pNode->IsNodeValid( m_pAI, m_pAI->GetPosition(), m_hThreat, kThreatPos_TargetPos, kNodeStatus_All ^ kNodeStatus_ThreatOutsideFOV) ) ) { return 0; } // Failed to find a valid destination position. LTVector vDestination; if (!pNode->GetDestinationPosition(m_pAI, m_pAI->GetAIBlackBoard()->GetBBTargetPosition(), vDestination)) { return 0; } // The stalking position is further from the AIs goal than the AI // currently is (don't run backwards to stalk). float flNodeDestToAIDestDistSqr = vDestination.DistSqr(m_pAI->GetAIBlackBoard()->GetBBTargetReachableNavMeshPosition()); if (flNodeDestToAIDestDistSqr > m_flDistanceToDestinationSqr) { return 0; } // Node is behind the AI. if (0 > m_pAI->GetForwardVector().Dot( vDestination - m_pAI->GetPosition())) { return 0; } // Already found a closer node. float flDistanceFromAISqr = vDestination.DistSqr(m_pAI->GetPosition()); if (flDistanceFromAISqr > m_flBestDistanceFromAISqr) { return 0; } m_vBestPosition = vDestination; m_flBestDistanceFromAISqr = flDistanceFromAISqr; m_pBestFact = pFact; return 0; }
void CAIGoalStalk::CalculateGoalRelevance() { // The target MUST be a character, regardless of current goal status. if ( !m_pAI->HasTarget( kTarget_Character ) ) { m_fGoalRelevance = 0.f; return; } // Goal in progress, and the node is still valid. if ( m_pAI->GetGoalMgr()->IsCurGoal(this) ) { if (!IsWSSatisfied(m_pAI->GetAIWorldState())) { AINode* pNode = AINode::HandleToObject(m_hStalkingNode); if (pNode) { if (pNode->IsNodeValid(m_pAI, m_pAI->GetPosition(), m_pAI->GetAIBlackBoard()->GetBBTargetObject(), kThreatPos_TargetPos, kNodeStatus_All)) { m_fGoalRelevance = m_pGoalRecord->fIntrinsicRelevance; return; } } } } // Damaged recently CAIWMFact factQuery; factQuery.SetFactType(kFact_Damage); CAIWMFact* pDamagedFact = m_pAI->GetAIWorkingMemory()->FindWMFact( factQuery ); if( pDamagedFact && ( DidDamage(m_pAI, pDamagedFact) ) && ( pDamagedFact->GetUpdateTime() < g_pLTServer->GetTime() - 4.f ) ) { m_fGoalRelevance = 0.f; return; } // No target object if (NULL == m_pAI->GetAIBlackBoard()->GetBBTargetObject()) { m_fGoalRelevance = 0.f; return; } // Enemy is too close (note that this NOT the destination, this is the // threat -- AIs next to their enemies should not attempt to stalk) float flAIToAIDestDistSqr = m_pAI->GetPosition().DistSqr(m_pAI->GetAIBlackBoard()->GetBBTargetReachableNavMeshPosition()); if ( flAIToAIDestDistSqr < kThreatTooCloseThresholdSqr) { m_fGoalRelevance = 0.f; return; } // Stalking is relevent when the AI is in the process of going somewhere if( m_pAI->GetAIBlackBoard()->GetBBDestStatus() != kNav_Set ) { m_fGoalRelevance = 0.f; return; } // Failed to find a potential stalking node. StalkingNodeFinder finder(m_pAI, m_pAI->GetAIBlackBoard()->GetBBTargetObject(), m_hPreviousStalkingNode, flAIToAIDestDistSqr); m_pAI->GetAIWorkingMemory()->CollectFact(finder); CAIWMFact* pFact = finder.GetFact(); if (!pFact) { m_fGoalRelevance = 0.f; return; } m_vPendingStalkingPosition = finder.GetPosition(); m_hPendingStalkingNode = pFact->GetTargetObject(); m_fGoalRelevance = m_pGoalRecord->fIntrinsicRelevance; }
void CAIGoalFlee::CalculateGoalRelevance() { // Don't flee if we have not yet seen our target. if( m_pAI->GetAIBlackBoard()->GetBBTargetLastVisibleTime() == 0.f ) { m_fGoalRelevance = 0.f; return; } // Maintain relevance if goal was already active, and animation // is locked. (e.g. while AI is mounting a node). if( m_hNode && m_pAI->GetAnimationContext()->IsLocked() ) { m_fGoalRelevance = m_pGoalRecord->fIntrinsicRelevance; return; } // Always try to flee when target is unreachable. LTVector vTargetPos = m_pAI->GetAIBlackBoard()->GetBBTargetPosition(); if( !g_pAIPathMgrNavMesh->HasPath( m_pAI, m_pAI->GetCharTypeMask(), vTargetPos ) ) { super::CalculateGoalRelevance(); return; } // Always try to flee when target exist AI's guard radius. if( !m_pAI->GetAnimationContext()->IsLocked() ) { CAIWMFact factQueryGuard; factQueryGuard.SetFactType(kFact_Node); factQueryGuard.SetNodeType(kNode_Guard); CAIWMFact* pFactGuard = m_pAI->GetAIWorkingMemory()->FindWMFact( factQueryGuard ); if( pFactGuard ) { AINodeGuard* pGuardNode = (AINodeGuard*)g_pLTServer->HandleToObject( pFactGuard->GetTargetObject() ); if( pGuardNode && pGuardNode->IsAIInRadiusOrRegion( m_pAI, m_pAI->GetPosition(), 1.f ) ) { ENUM_NMPolyID ePoly = m_pAI->GetAIBlackBoard()->GetBBTargetTrueNavMeshPoly(); if( pGuardNode && !pGuardNode->IsPosInRadiusOrRegion( vTargetPos, ePoly, 1.f ) ) { super::CalculateGoalRelevance(); return; } } } } // We may be at some node. HOBJECT hNode = NULL; SAIWORLDSTATE_PROP* pProp = m_pAI->GetAIWorldState()->GetWSProp( kWSK_AtNode, m_pAI->m_hObject ); if( pProp ) { hNode = pProp->hWSValue; } // Goal has no relevance if we are already at a node, and this goal is not currently active. if( hNode && !m_hNode ) { m_fGoalRelevance = 0.f; return; } // Goal has no relevance if we are already at an invalid node. if( hNode ) { AINode* pNode = (AINode*)g_pLTServer->HandleToObject( hNode ); if( pNode && ( !pNode->IsNodeValid( m_pAI, m_pAI->GetPosition(), m_pAI->GetAIBlackBoard()->GetBBTargetObject(), kThreatPos_TargetPos, m_dwNodeStatus ) ) ) { m_fGoalRelevance = 0.f; return; } } // Goal is only relevant if we have the desire to retreat or flee. bool bFlee = false; CAIWMFact factQuery; factQuery.SetFactType(kFact_Desire); factQuery.SetDesireType(kDesire_Retreat); CAIWMFact* pFact = m_pAI->GetAIWorkingMemory()->FindWMFact( factQuery ); if( pFact ) { bFlee = true; } else { factQuery.SetDesireType(kDesire_Flee); pFact = m_pAI->GetAIWorkingMemory()->FindWMFact( factQuery ); if( pFact ) { bFlee = true; } } // Default calculation. if( bFlee ) { super::CalculateGoalRelevance(); } // Do not flee. else { m_fGoalRelevance = 0.f; } }