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 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 ); }