LTBOOL CAIGoalPatrol::HandleNameValuePair(const char *szName, const char *szValue) { ASSERT(szName && szValue); if ( !_stricmp(szName, "NODE") ) { AINode* pNode = g_pAINodeMgr->GetNode(szValue); AIASSERT1( pNode && (pNode->GetType() == kNode_Patrol), m_pAI->m_hObject, "CAIGoalPatrol::HandleNameValuePair: Could not find patrol node: %s", szValue ); if( pNode ) { SetPatrolNode( (AINodePatrol*)pNode ); } return LTTRUE; } else if ( !_stricmp(szName, "AWARENESS") ) { m_eAwareness = CAnimationMgrList::GetPropFromName( szValue ); AIASSERT( m_eAwareness != kAP_Invalid, m_pAI->m_hObject, "CAIGoalPatrol::HandleNameValuePair: Awareness is None" ); if( m_pAI->GetState()->GetStateType() == kState_HumanPatrol ) { CAIHumanStatePatrol* pStatePatrol = (CAIHumanStatePatrol*)(m_pAI->GetState()); pStatePatrol->SetAwareness( m_eAwareness ); } } return LTFALSE; }
void AICmdTail::Verify() { super::Verify(); uint32 cValidNodes = 0; for ( uint32 iNode = 0 ; iNode < kMaxNodes ; iNode++ ) { if ( !!m_ahstrNodes[iNode] ) { AINode* pNode = g_pAINodeMgr->GetNode(m_ahstrNodes[iNode]); if ( !pNode ) { Warn("AICmd \"%s\" - Tail node \"%s\" does not exist", ::ToString(m_hstrName), ::ToString(m_ahstrNodes[iNode])); } else if ( pNode->GetType() != AINode::eTypeTail ) { Warn("AICmd \"%s\" - Node \"%s\" is not a tail node", ::ToString(m_hstrName), ::ToString(m_ahstrNodes[iNode])); } else { cValidNodes++; } } } if ( cValidNodes < 3 ) { Warn("AICmd \"%s\" - %s valid tail nodes specified, need a minimum of 3", ::ToString(m_hstrName), ::ToString(cValidNodes)); } }
AINode* CAIGoalAttackProp::HandleGoalAttractors() { // Check if already attacking a prop. if( m_pAI->GetState()->GetStateType() != kState_HumanAttackProp ) { CAIHuman* pAIHuman = (CAIHuman*)m_pAI; if( pAIHuman->HasHolsterString() || pAIHuman->GetPrimaryWeapon()) { AIGBM_GoalTemplate* pTemplate = g_pAIGoalButeMgr->GetTemplate( GetGoalType() ); AIASSERT(pTemplate->cAttractors > 0, m_pAI->m_hObject, "CAIGoalAbstract::HandleGoalAttractors: Goal has no attractors."); // Check if attractors are triggering activateability. AINode* pNode; for(uint32 iAttractor=0; iAttractor < pTemplate->cAttractors; ++iAttractor) { pNode = g_pAINodeMgr->FindNearestNodeInRadius(m_pAI, pTemplate->aAttractors[iAttractor], m_pAI->GetPosition(), pTemplate->fAttractorDistSqr * m_fBaseImportance, LTTRUE); if(pNode != LTNULL) { HOBJECT hObject; if ( LT_OK == FindNamedObject(pNode->GetObject(), hObject) ) { Prop* pProp = (Prop*)g_pLTServer->HandleToObject(hObject); if(pProp->GetState() != kState_PropDestroyed) { AIASSERT(pNode->GetType() == kNode_UseObject, m_pAI->m_hObject, "CAIGoalAttackProp::HandleGoalAttractors: AINode is not of type UseObject."); m_hNode = pNode->m_hObject; SetCurToBaseImportance(); return pNode; } } // Disable node if prop has been destroyed. pNode->Disable(); } } } m_hNode = LTNULL; } return LTNULL; }
void AICmdAttackFromCover::Verify() { super::Verify(); if ( m_hstrNode ) { AINode* pNode = g_pAINodeMgr->GetNode(m_hstrNode); if ( !pNode ) { Warn("AICmd \"%s\" - Cover node \"%s\" does not exist", ::ToString(m_hstrName), ::ToString(m_hstrNode)); } else if ( pNode->GetType() != AINode::eTypeCover ) { Warn("AICmd \"%s\" - Node \"%s\" is not a cover node", ::ToString(m_hstrName), ::ToString(m_hstrNode)); } } else { Warn("AICmd \"%s\" - No cover node specified!", ::ToString(m_hstrName)); } }
void AICmdPanic::Verify() { super::Verify(); if ( m_hstrNode ) { AINode* pNode = g_pAINodeMgr->GetNode(m_hstrNode); if ( !pNode ) { Warn("AICmd \"%s\" - Panic node \"%s\" does not exist", ::ToString(m_hstrName), ::ToString(m_hstrNode)); } else if ( pNode->GetType() != AINode::eTypePanic ) { Warn("AICmd \"%s\" - Node \"%s\" is not a panic node", ::ToString(m_hstrName), ::ToString(m_hstrNode)); } } else { Warn("AICmd \"%s\" - No panic node specified!", ::ToString(m_hstrName)); } }
void AICmdPickupObject::Verify() { super::Verify(); if ( m_hstrNode ) { AINode* pNode = g_pAINodeMgr->GetNode(m_hstrNode); if ( !pNode ) { Warn("AICmd \"%s\" - PickupObject node \"%s\" does not exist", ::ToString(m_hstrName), ::ToString(m_hstrNode)); } else if ( pNode->GetType() != AINode::eTypeCover ) { Warn("AICmd \"%s\" - Node \"%s\" is not a pickupobject node", ::ToString(m_hstrName), ::ToString(m_hstrNode)); } } else { Warn("AICmd \"%s\" - No pickupobject node specified!", ::ToString(m_hstrName)); } }
void AICmdAssassinate::Verify() { super::Verify(); if ( m_hstrNode ) { AINode* pNode = g_pAINodeMgr->GetNode(m_hstrNode); if ( !pNode ) { Warn("AICmd \"%s\" - Assassinate node \"%s\" does not exist", ::ToString(m_hstrName), ::ToString(m_hstrNode)); } else if ( pNode->GetType() != AINode::eTypeAssassinate ) { Warn("AICmd \"%s\" - Node \"%s\" is not a assassinate node", ::ToString(m_hstrName), ::ToString(m_hstrNode)); } } else { Warn("AICmd \"%s\" - No assassinate node specified!", ::ToString(m_hstrName)); } }
bool CAIGoalIntro::IsWSSatisfied( CAIWorldState* pwsWorldState ) { // Intentionally do not call super::IsWSSatisfied(). // AI needs to pause at node for some amount of time. // Goal is not satisfied if we are not at the node. SAIWORLDSTATE_PROP* pProp = pwsWorldState->GetWSProp( kWSK_AtNode, m_pAI->m_hObject ); if( !( pProp && pProp->hWSValue && ( pProp->hWSValue == m_NodeCurrent.hNode ) ) ) { return false; } // Bail if not at an intro node. if( !IsAINode( pProp->hWSValue ) ) { return false; } AINode* pNode = (AINode*)g_pLTServer->HandleToObject( pProp->hWSValue ); if( pNode->GetType() != kNode_Intro ) { return false; } // Goal is satisfied if we have been at the node for the pause time. AINodeIntro* pNodeIntro = (AINodeIntro*)pNode; if( m_pAI->GetAIBlackBoard()->GetBBStateChangeTime() > g_pLTServer->GetTime() - pNodeIntro->GetPauseTime() ) { return false; } // Goal is satisfied. return true; }
void CAISensorNodeCombat::FilterNodesValidForFollow( AIVALID_NODE_LIST& lstValidNodes ) { // This is kindof a hack to be filtering nodes for follwing inside of this sensor. // Maybe eventually this could be moved into a separate sensor that updates every // frame, but knows to only really do anything when nodes of some types have been // refreshed. // Bail if we are not following anyone. CAIWMFact factTaskQuery; factTaskQuery.SetFactType( kFact_Task ); factTaskQuery.SetTaskType( kTask_Follow ); CAIWMFact* pFact = m_pAI->GetAIWorkingMemory()->FindWMFact( factTaskQuery ); if( !pFact ) { return; } static AINODE_LIST lstFollowNodes; lstFollowNodes.resize( 0 ); // Collect known follow nodes. AINodeFollow* pNodeFollow; AIWORKING_MEMORY_FACT_LIST::const_iterator itFact; const AIWORKING_MEMORY_FACT_LIST* pFactList = m_pAI->GetAIWorkingMemory()->GetFactList(); for( itFact = pFactList->begin(); itFact != pFactList->end(); ++itFact ) { // Ignore deleted facts. pFact = *itFact; if( pFact->IsDeleted() ) { continue; } if( ( pFact->GetFactType() == kFact_Node ) && ( pFact->GetNodeType() == kNode_Follow ) ) { pNodeFollow = (AINodeFollow*)g_pLTServer->HandleToObject( pFact->GetTargetObject() ); if( pNodeFollow ) { lstFollowNodes.push_back( pNodeFollow ); } } } // If we are standing at a node of the correct type, // add it to the list of potentially valid nodes. // Ordinarily, locked nodes are ommitted, but in the case of // following we need to consider the node we are at valid. SAIWORLDSTATE_PROP* pProp = m_pAI->GetAIWorldState()->GetWSProp( kWSK_AtNode, m_pAI->m_hObject ); if( pProp && pProp->hWSValue ) { AINode* pNode = (AINode*)g_pLTServer->HandleToObject( pProp->hWSValue ); if( pNode && ( pNode->GetLockingAI() == m_pAI->m_hObject ) && ( pNode->GetType() == m_pSensorRecord->eNodeType ) ) { SAIVALID_NODE ValidNode; ValidNode.hNode = pNode->m_hObject; ValidNode.fDistSqr = 0.f; lstValidNodes.push_back( ValidNode ); } } // Remove nodes from list that are not valid for follow. HOBJECT hNode; bool bNodeIsValidForFollow; AINODE_LIST::iterator itNode; AIVALID_NODE_LIST::iterator itValidNode = lstValidNodes.begin(); while( itValidNode != lstValidNodes.end() ) { hNode = itValidNode->hNode; // Find the node listed in a follow node's list. bNodeIsValidForFollow = false; for( itNode = lstFollowNodes.begin(); itNode != lstFollowNodes.end(); ++itNode ) { pNodeFollow = (AINodeFollow*)(*itNode); if( pNodeFollow && pNodeFollow->GetWaitingNodes() && pNodeFollow->GetWaitingNodes()->DoesContain( hNode ) ) { bNodeIsValidForFollow = true; break; } } // Remove invalid node from list. if( !bNodeIsValidForFollow ) { itValidNode = lstValidNodes.erase( itValidNode ); continue; } // Continue iterating over list. ++itValidNode; } }
void CAIGoalEscapeDanger::AvoidCoverNode() { // Avoid nodes where grenades have landed. SAIWORLDSTATE_PROP* pProp = m_pAI->GetAIWorldState()->GetWSProp( kWSK_AtNode, m_pAI->m_hObject ); if( pProp ) { AINode* pNode = (AINode*)g_pLTServer->HandleToObject( pProp->hWSValue ); if( pNode ) { static AINODE_LIST ClusteredNodeList; ClusteredNodeList.resize( 0 ); // Find all nodes in cluster where AI died. if( pNode->GetAINodeClusterID() != kNodeCluster_Invalid ) { g_pAINodeMgr->FindNodesInCluster( pNode->GetAINodeClusterID(), pNode->GetType(), &ClusteredNodeList ); } else { ClusteredNodeList.push_back( pNode ); } // Ensure all AI avoid all nodes in cluster. AINODE_LIST::iterator itNode; for( itNode = ClusteredNodeList.begin(); itNode != ClusteredNodeList.end(); ++itNode ) { pNode = *itNode; // An AI has been damaged at this node. CAIWMFact factQuery; factQuery.SetFactType(kFact_Knowledge); factQuery.SetKnowledgeType(kKnowledge_DamagedAtNode); factQuery.SetTargetObject(pNode->m_hObject); CAIWMFact* pFact = g_pAIWorkingMemoryCentral->FindWMFact( factQuery ); if (!pFact) { pFact = g_pAIWorkingMemoryCentral->CreateWMFact(kFact_Knowledge); // The AI was not actually damaged at the node yet, but we want // him to know to evacuate. pFact->SetDamage( DT_UNSPECIFIED, 0.f, LTVector( 0.f, 0.f, 0.f ) ); } if (pFact) { float fDelay = GetRandom( g_pAIDB->GetAIConstantsRecord()->fDamagedAtNodeAvoidanceTimeMin, g_pAIDB->GetAIConstantsRecord()->fDamagedAtNodeAvoidanceTimeMax ); pFact->SetKnowledgeType( kKnowledge_DamagedAtNode, 1.f ); pFact->SetTargetObject( pNode->m_hObject, 1.f ); pFact->SetTime( g_pLTServer->GetTime() + fDelay, 1.f ); } } } } }
void CAIActionReactToDanger::ActivateAction( CAI* pAI, CAIWorldState& wsWorldStateGoal ) { super::ActivateAction( pAI, wsWorldStateGoal ); // Bail if we are not aware of danger. CAIWMFact factQuery; factQuery.SetFactType( kFact_Danger ); CAIWMFact* pFact = pAI->GetAIWorkingMemory()->FindWMFact( factQuery ); if( !pFact ) { return; } // Set animate state. pAI->SetState( kState_Animate ); // Set flinch animation. CAnimationProps animProps; animProps.Set( kAPG_Posture, kAP_POS_Crouch ); animProps.Set( kAPG_Weapon, pAI->GetAIBlackBoard()->GetBBPrimaryWeaponProp() ); animProps.Set( kAPG_WeaponPosition, kAP_WPOS_Up ); animProps.Set( kAPG_Activity, kAP_ATVT_Distress ); animProps.Set( kAPG_Action, kAP_ACT_Idle ); CAIStateAnimate* pStateAnimate = (CAIStateAnimate*)( pAI->GetState() ); pStateAnimate->SetAnimation( animProps, !LOOP ); // Do NOT play a threat sound if threatened by a Turret. // Instead, allow turret targeting to play something appropriate. if( IsPlayer( pFact->GetSourceObject() ) ) { CPlayerObj* pPlayer = (CPlayerObj*)g_pLTServer->HandleToObject( pFact->GetSourceObject() ); if( pPlayer && pPlayer->GetTurret() ) { return; } } // Play threat sound. // "Fire!" if( IsAINode( pFact->GetSourceObject() ) ) { EnumAISoundType eAISound = kAIS_Danger; AINode* pNode = (AINode*)g_pLTServer->HandleToObject( pFact->GetSourceObject() ); if( pNode && pNode->GetType() == kNode_Stimulus ) { AINodeStimulus* pNodeStim = (AINodeStimulus*)pNode; if( pNodeStim ) { eAISound = pNodeStim->GetAISoundType(); } } g_pAISoundMgr->RequestAISound( pAI->m_hObject, eAISound, kAISndCat_Event, NULL, 0.f ); } // "Watch out grenade!" // "Shit!" else if( IsKindOf( pFact->GetSourceObject(), "CProjectile" ) ) { // Don't say anything if ally threw grenade. CProjectile* pProjectile = (CProjectile*)g_pLTServer->HandleToObject( pFact->GetSourceObject() ); if( pProjectile && IsCharacter( pProjectile->GetFiredFrom() ) ) { CCharacter *pChar = (CCharacter*)g_pLTServer->HandleToObject( pProjectile->GetFiredFrom() ); if( pChar && kCharStance_Like != g_pCharacterDB->GetStance( pAI->GetAlignment(), pChar->GetAlignment() ) ) { ENUM_AI_SQUAD_ID eSquad = g_pAICoordinator->GetSquadID( pAI->m_hObject ); CAISquad* pSquad = g_pAICoordinator->FindSquad( eSquad ); if( pSquad && pSquad->GetNumSquadMembers() > 1 ) { g_pAISoundMgr->RequestAISound( pAI->m_hObject, kAIS_GrenadeThreat, kAISndCat_Event, NULL, 0.f ); } else { g_pAISoundMgr->RequestAISound( pAI->m_hObject, kAIS_GrenadeThreatAlone, kAISndCat_Event, NULL, 0.f ); } } } } // "Shit" else { g_pAISoundMgr->RequestAISound( pAI->m_hObject, kAIS_GrenadeThreatAlone, kAISndCat_Event, NULL, 0.f ); } // Search for the source of the danger. LTVector vDangerPos = pFact->GetPos(); SearchForDangerOrigin( pAI, vDangerPos ); }
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; }