Beispiel #1
0
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;
}
Beispiel #2
0
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;
}