AINode* CAINodeMgr::FindRandomOwnedNode(CAI* pAI, EnumAINodeType eNodeType, HOBJECT hOwner) { // It is NOT OK for hOwner to be NULL. Only return nodes that are owned by someone. if( !hOwner ) { return LTNULL; } // Get AIs Path Knowledge. CAIPathKnowledgeMgr* pPathKnowledgeMgr = LTNULL; if( pAI && pAI->GetPathKnowledgeMgr() ) { pPathKnowledgeMgr = pAI->GetPathKnowledgeMgr(); } s_lstTempNodes.clear(); AINode* pNode; AINODE_MAP::iterator it; for(it = m_mapAINodes.lower_bound(eNodeType); it != m_mapAINodes.upper_bound(eNodeType); ++it) { pNode = it->second; // Skip nodes in unreachable volumes. if( pPathKnowledgeMgr && ( pPathKnowledgeMgr->GetPathKnowledge( pNode->GetNodeContainingVolume() ) == CAIPathMgr::kPath_NoPathFound ) ) { continue; } // Skip nodes that are not in volumes. if( !pNode->GetNodeContainingVolume() ) { continue; } // Skip node if required alignment does not match. if( ( pNode->GetRequiredRelationTemplateID() != -1 ) && ( pNode->GetRequiredRelationTemplateID() != pAI->GetRelationMgr()->GetTemplateID() ) ) { continue; } // Do NOT check if node type is active. // This will prevent AI from walking up to a disturbed // file cabinet to do some work, so he will never noticed the cabinet. /** if( !pNode->NodeTypeIsActive( eNodeType ) ) { continue; } **/ if( pNode->GetNodeOwner() != hOwner ) { continue; } if (!pNode->IsLockedDisabledOrTimedOut()) { s_lstTempNodes.push_back( pNode ); } } // Randomly select one of the valid nodes. if( !s_lstTempNodes.empty() ) { pNode = s_lstTempNodes[ GetRandom( 0, s_lstTempNodes.size() - 1 ) ]; s_lstTempNodes.clear(); } else { pNode = LTNULL; } // Ensure that AI can pathfind to the destination node. // Ideally, we would like to do this check for each node as we iterate, // but that could result in multiple runs of BuildVolumePath() which // is expensive. So instead we just check the final returned node. // The calling code can call this function again later, and will not get // this node again. if( pAI && pNode ) { AIVolume* pVolumeDest = pNode->GetNodeContainingVolume(); if( !g_pAIPathMgr->HasPath( pAI, pVolumeDest ) ) { return LTNULL; } } return pNode; }
AINode* CAINodeMgr::FindNearestOwnedNode(CAI* pAI, EnumAINodeType eNodeType, const LTVector& vPos, HOBJECT hOwner) { // It is NOT OK for hOwner to be NULL. Only return nodes that are owned by someone. if( !hOwner ) { return LTNULL; } // Get AIs Path Knowledge. CAIPathKnowledgeMgr* pPathKnowledgeMgr = LTNULL; if( pAI && pAI->GetPathKnowledgeMgr() ) { pPathKnowledgeMgr = pAI->GetPathKnowledgeMgr(); } LTFLOAT fMinDistanceSqr = (float)INT_MAX; AINode* pClosestNode = LTNULL; AINode* pNode; AINODE_MAP::iterator it; for(it = m_mapAINodes.lower_bound(eNodeType); it != m_mapAINodes.upper_bound(eNodeType); ++it) { pNode = it->second; // Skip nodes in unreachable volumes. if( pPathKnowledgeMgr && ( pPathKnowledgeMgr->GetPathKnowledge( pNode->GetNodeContainingVolume() ) == CAIPathMgr::kPath_NoPathFound ) ) { continue; } // Skip nodes that are not in volumes. if( !pNode->GetNodeContainingVolume() ) { continue; } // Skip node if required alignment does not match. if( ( pNode->GetRequiredRelationTemplateID() != -1 ) && ( pNode->GetRequiredRelationTemplateID() != pAI->GetRelationMgr()->GetTemplateID() ) ) { continue; } if( !pNode->NodeTypeIsActive( eNodeType ) ) { continue; } if( pNode->GetNodeOwner() != hOwner ) { continue; } // Owned nodes are locked by the owner, so just check for // disabled and timed out. if ( !( pNode->IsDisabled() || pNode->IsTimedOut() ) ) { LTFLOAT fDistanceSqr = VEC_DISTSQR(vPos, pNode->GetPos()); if ( fDistanceSqr < fMinDistanceSqr ) { fMinDistanceSqr = fDistanceSqr; pClosestNode = pNode; } } } // Ensure that AI can pathfind to the destination node. // Ideally, we would like to do this check for each node as we iterate, // but that could result in multiple runs of BuildVolumePath() which // is expensive. So instead we just check the final returned node. // The calling code can call this function again later, and will not get // this node again. if( pAI && pClosestNode ) { AIVolume* pVolumeDest = pClosestNode->GetNodeContainingVolume(); if( !g_pAIPathMgr->HasPath( pAI, pVolumeDest ) ) { return LTNULL; } } return pClosestNode; }
bool CAITargetSelectDisturbanceBeyondGuard::ValidatePreconditions( CAI* pAI ) { // Intentionally do NOT call super::ValidateContextPreconditions. // This Selector is only valid if AI is reacting to a disturbance outside his guard area. // Sanity check. if( !pAI ) { return false; } // Fail if AI was previously aware of a character target. if( pAI->GetAIBlackBoard()->GetBBTargetedTypeMask() & kTarget_Character ) { return false; } // No disturbance. CAIWMFact* pFact = pAI->GetAIWorkingMemory()->FindFactDisturbanceMax(); if( !pFact ) { return false; } // Disturbance is not from a character. HOBJECT hTarget = pFact->GetTargetObject(); if( !IsCharacter( hTarget ) ) { return false; } // Find AI's Guard node. CAIWMFact factGuardQuery; AINodeGuard* pNodeGuard = NULL; factGuardQuery.SetFactType(kFact_Node); factGuardQuery.SetNodeType(kNode_Guard); pFact = pAI->GetAIWorkingMemory()->FindWMFact( factGuardQuery ); if( pFact && IsAINode( pFact->GetTargetObject() ) ) { HOBJECT hNode = pFact->GetTargetObject(); AINode* pNode = (AINode*)g_pLTServer->HandleToObject( hNode ); if( pNode->GetType() == kNode_Guard ) { pNodeGuard = (AINodeGuard*)pNode; } } // No Guard node. if( !pNodeGuard ) { return false; } // Selector is only valid if disturbance is outside of the Guarded area. if( pNodeGuard->IsCharacterInRadiusOrRegion( hTarget ) ) { return false; } // Preconditions are met. return true; }
AINode* CAIGoalAbstractUseObject::HandleGoalAttractors() { // Do not search for attractors if goal is already active. // Do not search on first update, to allow commands a chance to disable nodes. // Do not search if AI has any damage flags set (e.g. sleeping damage). if( m_pGoalMgr->IsCurGoal(this) || m_pAI->IsFirstUpdate() || m_pAI->GetDamageFlags() ) { return LTNULL; } // If this goal reacts to stimulus, check if it has been too // long since stimulation. AIGBM_GoalTemplate* pTemplate = g_pAIGoalButeMgr->GetTemplate( GetGoalType() ); if( ( pTemplate->flagSenseTriggers != kSense_None ) && ( !m_hStimulusSource ) ) { return LTNULL; } // Lock the last UseObject node, so that we don't try to use it again. BlockAttractorNodeFromSearch( m_hLastNodeUseObject ); // Find the nearest attractor. AINode* pNode = FindNearestAttractorNode(); if(pNode != LTNULL) { AIASSERT(pNode->GetType() == kNode_UseObject, m_pAI->m_hObject, "CAIGoalAbstractUseObject::HandleGoalAttractors: AINode is not of type UseObject."); AINodeUseObject* pNodeUseObject = (AINodeUseObject*)pNode; if( pNodeUseObject->HasObject() && !pNodeUseObject->GetHObject() ) { pNode = LTNULL; m_fCurImportance = 0.f; AIASSERT( 0, pNodeUseObject->m_hObject, "CAIGoalAbstractUseObject::HandleGoalAttractors: AINodeUseObject points to invalid object" ); } else if( pNodeUseObject->IsOneWay() && ( pNodeUseObject->GetForward().Dot( m_pAI->GetForwardVector() ) < 0.0f ) ) { pNode = LTNULL; m_fCurImportance = 0.f; } else { AITRACE(AIShowGoals, ( m_pAI->m_hObject, "Setting node: %s", ::ToString( pNode->GetName() ) ) ); m_hNodeUseObject = pNode->m_hObject; SetCurToBaseImportance(); } } if( !pNode ) { ClearUseObjectNode(); m_hStimulusSource = LTNULL; } // If we locked a node prior to the search, unlock it. UnblockAttractorNodeFromSearch( m_hLastNodeUseObject ); return pNode; }
void CAIActionSurpriseAttackLaunch::ActivateAction( CAI* pAI, CAIWorldState& wsWorldStateGoal ) { super::ActivateAction( pAI, wsWorldStateGoal ); // Verify we selected a valid action. EnumAnimProp eAction = GetAtNodeAttackProp( *pAI->GetAIWorldState(), pAI->GetHOBJECT(), pAI->GetAIBlackBoard()->GetBBTargetObject() ); if ( kAP_Invalid == eAction ) { AIASSERT( 0, pAI->GetHOBJECT(), "CAIActionSurpriseAttackLaunch::ActivateAction: Failed to find an action despite passing precondition test." ); return; } // Verify the node specifies a smartobject. const AIDB_SmartObjectRecord* pSmartObject = GetAtNodeSmartObjectRecord( *pAI->GetAIWorldState(), pAI->GetHOBJECT() ); if ( !pSmartObject ) { AIASSERT( 0, pAI->GetHOBJECT(), "CAIActionSurpriseAttackLaunch::ActivateAction: Failed to smartobject for node despite passing precondition test." ); return; } // Notify the surprise node that it has been used. AINodeSurprise* pSurprise = GetSurpriseNode( *pAI->GetAIWorldState(), pAI->GetHOBJECT() ); if ( pSurprise ) { pSurprise->HandleSurpriseAttack(); } // Depart from a node (this must be done AFTER we get the node) 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 ) { pNode->HandleAIDeparture( pAI ); // Insure we call the PostActivate to fire off any commands/to // reset the activation time/to dispatch any post activate commands. AINodeSmartObject* pNodeSmartObject = AINodeSmartObject::DynamicCast( pNode->GetHOBJECT() ); if( pNodeSmartObject ) { pNodeSmartObject->PostActivate(); } } } // Get the nodes smartobject and replace the action with the action // determined dynamically. The activity specifies the direction/etc. CAnimationProps Props = pSmartObject->Props; Props.Set( kAPG_Action, eAction ); // Set the animation to play. pAI->SetState( kState_Animate ); CAIStateAnimate* pAnimate = (CAIStateAnimate*)pAI->GetState(); pAnimate->SetAnimation( Props, !LOOP ); // Torso tracking. pAI->GetAIBlackBoard()->SetBBTargetTrackerFlags( kTrackerFlag_AimAt ); pAI->GetAIBlackBoard()->SetBBFaceTarget( false ); // Ignore the AIs radius when validating movement encoding, as this // animation should be fit to the geometry by level designers. pAI->GetAIBlackBoard()->SetBBMovementEncodeUseRadius( false ); }
bool CAIActionSurpriseAttackLaunch::ValidateContextPreconditions( CAI* pAI, CAIWorldState& wsWorldStateGoal, bool bIsPlanning ) { if ( !super::ValidateContextPreconditions( pAI, wsWorldStateGoal, bIsPlanning ) ) { return false; } // Verify the node has a smartobject. (if it doesn't, this node will // not be used. This is an level design error, but an easy one to make, // so report it. if ( NULL == GetAtNodeSmartObjectRecord( *pAI->GetAIWorldState(), pAI->GetHOBJECT() ) ) { return false; } // AI is either not at a surprise node, or the node does not have a // valid action. if ( kAP_Invalid == GetAtNodeAttackProp( *pAI->GetAIWorldState(), pAI->GetHOBJECT(), pAI->GetAIBlackBoard()->GetBBTargetObject() ) ) { return false; } // Fail if the AI is not facing the node. SAIWORLDSTATE_PROP* pAtNodeProp = pAI->GetAIWorldState()->GetWSProp( kWSK_AtNode, pAI->GetHOBJECT() ); if ( !pAtNodeProp || ( !pAtNodeProp->hWSValue ) ) { return false; } AINode* pNode = AINode::HandleToObject( pAtNodeProp->hWSValue ); if ( !pNode ) { return false; } if ( pNode->GetFaceNode() ) { LTVector vNodeDir = pNode->GetNodeFaceDir(); vNodeDir.y = 0.0f; vNodeDir.Normalize(); if ( vNodeDir == LTVector::GetIdentity() ) { return false; } LTVector vAIForward = pAI->GetForwardVector(); vAIForward.y = 0.0f; vAIForward.Normalize(); if ( vAIForward == LTVector::GetIdentity() ) { return false; } if ( vAIForward.Dot( vNodeDir ) < 0.9999f ) { return false; } } // Success! return true; }