LTBOOL CAIGoalDisappearReappearEvasive::HandleNameValuePair(const char *szName, const char *szValue) { ASSERT(szName && szValue); if ( !_stricmp(szName, "NOW") ) { AITRACE( AIShowGoals, ( m_pAI->m_hObject, "GoalDisappearReappearEvasive: NOW=1" ) ); m_bForceDisappear = LTTRUE; m_bRequiresImmediateResponse = LTTRUE; SetCurToBaseImportance(); return LTTRUE; } else if ( !_stricmp(szName, "OVERRIDEDIST") ) { m_fReappearDistOverride = (LTFLOAT)atof( szValue ); AITRACE( AIShowGoals, ( m_pAI->m_hObject, "GoalDisappearReappearEvasive: OVERRIDEDIST=%.2f", m_fReappearDistOverride ) ); return LTTRUE; } else if ( !_stricmp(szName, "DELAY") ) { m_fReappearDelay = (LTFLOAT)atof( szValue ); AITRACE( AIShowGoals, ( m_pAI->m_hObject, "GoalDisappearReappearEvasive: DELAY=%.2f", m_fReappearDelay ) ); return LTTRUE; } return LTFALSE; }
void CAIGoalMgr::Term(bool bDestroyAll) { // Delete all goals. // Use RemoveGoal so that all goal removals follow the same // code path. When SetGoalSet is called, it may call Term() // to clear the goals. Calling RemoveGoal ensures that there // are no dangling pointers. CAIGoalAbstract* pGoal; AIGOAL_LIST::iterator it = m_lstGoals.begin(); while( it != m_lstGoals.end() ) { pGoal = (*it); if( !bDestroyAll && pGoal->IsPermanentGoal() ) { ++it; } else { AITRACE( AIShowGoals, ( m_pAI->m_hObject, "Removing Goal %s.", s_aszGoalTypes[pGoal->GetGoalType()] ) ); RemoveGoal( pGoal->GetGoalType() ); it = m_lstGoals.begin(); } } for( it = m_lstGoals.begin(); it != m_lstGoals.end(); ++it ) { pGoal = (*it); AITRACE( AIShowGoals, ( m_pAI->m_hObject, "Goal %s is Permanent. NOT removing.", s_aszGoalTypes[pGoal->GetGoalType()] ) ); } }
LTBOOL CAIGoalTalk::HandleNameValuePair(const char *szName, const char *szValue) { ASSERT(szName && szValue); if( super::HandleNameValuePair(szName, szValue) ) { return LTTRUE; } if ( !_stricmp(szName, "MOOD") ) { m_eMood = CAnimationMgrList::GetPropFromName( szValue ); return LTTRUE; } else if ( !_stricmp(szName, "FACETIME") ) { m_fFaceTime = (LTFLOAT)atof(szValue); return LTTRUE; } // Retrigger tells the AI to retrigger the dialogue object // once he is in the radius. else if ( !_stricmp(szName, "RETRIGGER") ) { AITRACE( AIShowGoals, ( m_pAI->m_hObject, "CAIGoalTalk: RETRIGGER=1" ) ); m_bRetriggerDialogue = IsTrueChar(*szValue); return LTTRUE; } // Disposable tells the AI to remove the Talk goal // once he is out of the radius. else if ( !_stricmp(szName, "DISPOSABLE") ) { AITRACE( AIShowGoals, ( m_pAI->m_hObject, "CAIGoalTalk: DISPOSABLE=1" ) ); m_bDisposableDialogue = IsTrueChar(*szValue); return LTTRUE; } else if ( !_stricmp(szName, "GESTURE") ) { FREE_HSTRING( m_hstrGesture ); m_hstrGesture = g_pLTServer->CreateString( szValue ); return LTTRUE; } else if ( !_stricmp(szName, "MOVEMENT") ) { m_eMovement = CAnimationMgrList::GetPropFromName( szValue ); return LTTRUE; } return LTFALSE; }
bool CAIActivityAbstract::ActivateActivity() { // Set expiration time for activity, to ensure it // bails if something goes awry. m_fActivityActivateTime = g_pLTServer->GetTime(); if( m_fActivityTimeOut > 0.f ) { m_fActivityExpirationTime = m_fActivityActivateTime + m_fActivityTimeOut; } // Announce squad members activating activity. HOBJECT hAI; for( uint32 iPotential=0; iPotential < m_cPotentialParticipants; ++iPotential ) { hAI = m_aPotentialParticipants[iPotential]; if( hAI ) { AITRACE( AIShowActivities, ( hAI, "Activating activity %s", s_aszActivityTypes[GetActivityClassType()] ) ); } } return true; }
// ----------------------------------------------------------------------- // // // ROUTINE: CAIGoalCatch::ActivateGoal // // PURPOSE: Activate goal. // // ----------------------------------------------------------------------- // void CAIGoalCatch::ActivateGoal(void) { super::ActivateGoal(); if (m_hStimulusTarget==LTNULL) { SetCurImportance(0); AITRACE(AIShowGoals, ( m_pAI->m_hObject, "Aborting Goal %s\n", s_aszGoalTypes[GetGoalType()] ) ); return; } // Ignore senses other than see enemy. m_pAI->SetCurSenseFlags( kSense_SeeEnemy | kSense_SeeDangerousProjectile | kSense_SeeCatchableProjectile ); m_pGoalMgr->LockGoal(this); m_pAI->SetState( kState_HumanCatch ); // Set state specific information.. CAIHumanStateCatch* pCatchInstance = (CAIHumanStateCatch*)m_pAI->GetState(); // Save the object we intend to catch pCatchInstance->SetObjectToCatch( m_hStimulusTarget ); // Set the animations to use pCatchInstance->SetAnimationSequence( kAP_StartCatch, kAP_HoldCatch, kAP_EndCatch ); }
LTBOOL CAISenseRecorderAbstract::IncreaseStimulation(AISenseRecord* pSenseRecord, LTFLOAT fRateModifier) { AIBM_Stimulus* pStimulus = pSenseRecord->pAIBM_Last_Stimulus; // Stimulation increase rate depends on alertness. LTFLOAT fStimulationIncreaseRate = m_pSensing->IsAlert() ? (pStimulus->fStimulationIncreaseRateAlert) : (pStimulus->fStimulationIncreaseRateUnalert); // Current stimulation is either max, or some formula. Whichever is less. pSenseRecord->fCurStimulation = Min<LTFLOAT>( (pStimulus->rngStimulationThreshhold.GetMax()), pSenseRecord->fCurStimulation + (m_pSensing->GetSenseUpdateRate() * fStimulationIncreaseRate * fRateModifier)); // Keep track of the max stimulation, so when can detect coming down // from full stimulation. if(pSenseRecord->fCurStimulation > pSenseRecord->fMaxStimulation) { pSenseRecord->fMaxStimulation = pSenseRecord->fCurStimulation; } AITRACE(AIShowSenses, (m_pSensing->GetSensingObject(), "IncreaseStimulution of %s to %f\n", SenseToString(pSenseRecord->eSenseType), pSenseRecord->fCurStimulation) ); // Return true if sense should be handled. if(pSenseRecord->fCurStimulation == pStimulus->rngStimulationThreshhold.GetMax()) { return LTTRUE; } return LTFALSE; }
void CAIActionAttackGrenade::SetNextThrowTime( CAI* pAI ) { // Coordinate frequency of grenade attacks across all AIs. if( !pAI->HasTarget( kTarget_Character | kTarget_Object ) ) { return; } float fDelay = GetRandom( pAI->GetBrain()->GetAttackGrenadeThrowTimeMin(), pAI->GetBrain()->GetAttackGrenadeThrowTimeMax() ); double fTime = g_pLTServer->GetTime() + LOWER_BY_DIFFICULTY( fDelay ); AITRACE( AIShowGoals, ( pAI->m_hObject, "NOW: %.2f NEXT: %.2f", g_pLTServer->GetTime(), fTime ) ); // Update the global fact tracking grenade throwing frequency, or create // it if it does not yet exist. CAIWMFact factQuery; factQuery.SetFactType(kFact_Knowledge); factQuery.SetKnowledgeType(kKnowledge_NextGrenadeTime); CAIWMFact* pFact = g_pAIWorkingMemoryCentral->FindWMFact(factQuery); if (!pFact) { pFact = g_pAIWorkingMemoryCentral->CreateWMFact(kFact_Knowledge); pFact->SetKnowledgeType( kKnowledge_NextGrenadeTime, 1.f ); } if (pFact) { pFact->SetTime( fTime ); } }
CAIPathMgr::EnumPathBuildStatus CAIPathKnowledgeMgr::GetPathKnowledge(AIVolume *pDestVolume) { // Existing path knowledge is only valid while our index matches // the global index. if( m_nPathKnowledgeIndex != g_pAIPathMgr->GetPathKnowledgeIndex() ) { ClearPathKnowledge(); m_nPathKnowledgeIndex = g_pAIPathMgr->GetPathKnowledgeIndex(); return CAIPathMgr::kPath_Unknown; } // Search for existing knowledge of paths to the dest volume. AIPATH_KNOWLEDGE_MAP::iterator it = m_mapPathKnowledge.find( pDestVolume ); // Return existing knowledge. if( it != m_mapPathKnowledge.end() ) { if( it->second == CAIPathMgr::kPath_NoPathFound ) { AITRACE( AIShowPaths, ( m_pAI->m_hObject, "Returning cached NoPathFound for %s", pDestVolume->GetName() ) ); } return it->second; } // No knowledge exists. return CAIPathMgr::kPath_Unknown; }
LTBOOL CAISenseRecorderAbstract::DecreaseStimulation(AISenseRecord* pSenseRecord, LTFLOAT fRateModifier) { AIBM_Stimulus* pStimulus = pSenseRecord->pAIBM_Last_Stimulus; // Stick at max until HandleSense handles it. // if(pSenseRecord->fCurStimulation >= pStimulus->rngStimulationThreshhold.GetMax()) // { // return LTFALSE; // } LTFLOAT fLastStimulation = pSenseRecord->fCurStimulation; // Stimulation decrease rate depends on alertness. LTFLOAT fStimulationDecreaseRate = m_pSensing->IsAlert() ? (pStimulus->fStimulationDecreaseRateAlert) : (pStimulus->fStimulationDecreaseRateUnalert); // Current stimulation is either 0, or some formula. Whichever is more. pSenseRecord->fCurStimulation = Max<LTFLOAT>(0.0f, pSenseRecord->fCurStimulation - (m_pSensing->GetSenseUpdateRate() * fStimulationDecreaseRate * fRateModifier)); AITRACE(AIShowSenses, (m_pSensing->GetSensingObject(), "DecreaseStimulution of %s to %f\n", SenseToString(pSenseRecord->eSenseType), pSenseRecord->fCurStimulation) ); // If stimulation has dropped below the partial stimulation // threshold, it was a false stimulation. if( ( pStimulus->nFalseStimulationLimit > 0 ) && ( pSenseRecord->fMaxStimulation < pStimulus->rngStimulationThreshhold.GetMax() ) && ( pSenseRecord->fCurStimulation < pStimulus->rngStimulationThreshhold.GetMin() ) && ( fLastStimulation >= pStimulus->rngStimulationThreshhold.GetMin() ) ) { ++(pSenseRecord->cFalseStimulation); AITRACE(AIShowSenses, (m_pSensing->GetSensingObject(), "FalseStimulution %d/%d of %s\n", pSenseRecord->cFalseStimulation, pStimulus->nFalseStimulationLimit, SenseToString(pSenseRecord->eSenseType)) ); return LTTRUE; } if( pSenseRecord->fCurStimulation == 0.f ) { pSenseRecord->fMaxStimulation = 0.f; } return LTFALSE; }
void CAIGoalAbstractUseObject::SetStateUseObject() { m_pAI->SetState( GetUseObjectState() ); AINodeUseObject* pNodeUseObject = (AINodeUseObject*)g_pLTServer->HandleToObject(m_hNodeUseObject); if( !pNodeUseObject ) { AIASSERT( NULL, m_pAI->m_hObject, "CAIGoalAbstractUseObject::SetStateUseObject: AINodeUseObject is NULL."); return; } CAIHumanStateUseObject* pStateUseObject = (CAIHumanStateUseObject*)(m_pAI->GetState()); pStateUseObject->StateHandlesNodeLocking( LTFALSE ); // Bail if there is any problem setting the node. if( !pStateUseObject->SetNode(pNodeUseObject) ) { AITRACE(AIShowGoals, ( m_pAI->m_hObject, "Unable to set UseObject node %s.", ::ToString( pNodeUseObject->GetName() ) ) ); m_hLastNodeUseObject = pNodeUseObject->m_hObject; CompleteUseObject(); return; } pStateUseObject->SetWeaponPosition(m_eWeaponPosition); pStateUseObject->SetRequireBareHands( m_bRequireBareHands ); pStateUseObject->SetAllowDialogue(m_bAllowDialogue); pStateUseObject->TurnOnLights( m_bTurnOnLights ); pStateUseObject->TurnOffLights( m_bTurnOffLights ); // Find command string for AINodeType matching UseObject. AIGBM_GoalTemplate* pTemplate = g_pAIGoalButeMgr->GetTemplate( GetGoalType() ); AIASSERT(pTemplate->cAttractors > 0, m_pAI->m_hObject, "CAIGoalAbstractUseObject::ActivateGoal: Goal has no attractors."); HSTRING hstrCmd; for(uint32 iAttractor=0; iAttractor < pTemplate->cAttractors; ++iAttractor) { hstrCmd = pNodeUseObject->GetSmartObjectCommand(pTemplate->aAttractors[iAttractor]); if(hstrCmd != LTNULL) { pStateUseObject->SetSmartObjectCommand(hstrCmd); break; } } // Ensure that node is setup to be requested type of attractor. if( !hstrCmd ) { AIASSERT( 0, m_pAI->m_hObject, "CAIGoalAbstractUseObject::ActivateGoal: No command string found for attractors."); m_fCurImportance = 0.f; } }
LTBOOL CAIGoalTalk::HandleGoalSenseTrigger(AISenseRecord* pSenseRecord) { // If the dialogue was disposable, and AI sensed anything, // remove the Talk goal. if( m_bDisposableDialogue ) { AITRACE( AIShowGoals, ( m_pAI->m_hObject, "Disposing Talk Goal." ) ); m_pGoalMgr->DeleteGoalNextUpdate( kGoal_Talk ); } return LTFALSE; }
void CAIGoalAbstractSearch::SetStateSearch() { // Reset default senses, except for SeeAllyDisturbance. m_pAI->SetCurSenseFlags( 0xffffffff & ~kSense_SeeAllyDisturbance ); // Cannot search without a weapon. CAIHuman* pAIHuman = (CAIHuman*)m_pAI; if( !pAIHuman->GetCurrentWeapon() ) { m_pGoalMgr->LockGoal(this); m_pAI->SetState( kState_HumanDraw ); return; } // Start searching. m_pAI->SetState( kState_HumanSearch ); if( IsCharacter( m_hStimulusSource ) ) { m_pAI->Target(m_hStimulusSource); } m_pGoalMgr->LockGoal(this); CAIHumanStateSearch* pSearchState = (CAIHumanStateSearch*)m_pAI->GetState(); if( pSearchState ) { pSearchState->SetPause(LTTRUE); pSearchState->SetEngage(m_bEngageSearch); pSearchState->SetFace(m_bFaceSearch); // If any optional search regions have been specified, search them. // Otherwise, search the AIs current region. if( !m_lstRegions.empty() ) { AIRegion* pRegion = (AIRegion*)g_pLTServer->HandleToObject( m_lstRegions.front() ); if( pRegion ) { AITRACE( AIShowGoals, ( m_pAI->m_hObject, "Searching region %s", pRegion->GetName() ) ); } pSearchState->SetDestRegion( m_lstRegions.front() ); m_lstRegions.erase( m_lstRegions.begin() ); } } }
void Alarm::CreateRegionLists() { LTVector vPos; LTFLOAT fSearchY = 64.f; g_pLTServer->GetObjectPos( m_hObject, &vPos ); AIVolume* pContainingVolume = g_pAIVolumeMgr->FindContainingVolume( LTNULL, vPos, eAxisAll, fSearchY, LTNULL ); if( pContainingVolume ) { AITRACE( AIShowAlarms, ( m_hObject, "In AIVolume: %s", pContainingVolume->GetName() ) ); } else { char szName[64]; g_pLTServer->GetObjectName( m_hObject, szName, sizeof(szName) ); AIError( "INVALID ALARM: Alarm '%s' is not in an AIVolume!", szName ); } AITRACE( AIShowAlarms, ( m_hObject, "Setting Alert Regions: %s", ::ToString( m_hstrAlertRegions ) ) ); AITRACE( AIShowAlarms, ( m_hObject, "Setting Respond Regions: %s", ::ToString( m_hstrRespondRegions ) ) ); AITRACE( AIShowAlarms, ( m_hObject, "Setting Search Regions: %s", ::ToString( m_hstrSearchRegions ) ) ); CreateRegionList( m_hstrAlertRegions, &m_lstAlertRegions ); CreateRegionList( m_hstrRespondRegions, &m_lstRespondRegions ); CreateRegionList( m_hstrSearchRegions, &m_lstSearchRegions ); }
void CAIActivityAbstract::DeactivateActivity() { m_fNextActivityUpdateTime = g_pLTServer->GetTime() + m_fActivityUpdateRate; // Announce squad members deactivating activity. HOBJECT hAI; for( uint32 iPotential=0; iPotential < m_cPotentialParticipants; ++iPotential ) { hAI = m_aPotentialParticipants[iPotential]; if( hAI ) { AITRACE( AIShowActivities, ( hAI, "Deactivating activity %s", s_aszActivityTypes[GetActivityClassType()] ) ); } } }
LTBOOL CAIGoalGoto::HandleNameValuePair(const char *szName, const char *szValue) { ASSERT(szName && szValue); if ( !_stricmp(szName, "NODE") ) { AITRACE( AIShowGoals, ( m_pAI->m_hObject, "GOTO setting node=%s", szValue ) ); AINode* pNode = g_pAINodeMgr->GetNode( szValue ); if( pNode ) { m_hDestNode = pNode->m_hObject; // If Goal was already active (walking to previous goto node) // Reset the goal. if( m_pGoalMgr->IsCurGoal( this ) && ( m_pAI->GetState()->GetStateType() == kState_HumanGoto ) ) { CAIHumanStateGoto* pGoto = (CAIHumanStateGoto*)m_pAI->GetState(); pGoto->SetDestNode( m_hDestNode ); } } else { AIASSERT1( 0, m_pAI->m_hObject, "CAIGoalGoto::HandleNameValuePair: Cannot find node '%s'", szValue ); } return LTTRUE; } else if ( !_stricmp(szName, "MOVEMENT") ) { m_eMovement = CAnimationMgrList::GetPropFromName( szValue ); return LTTRUE; } else if ( !_stricmp(szName, "AWARENESS") ) { m_eAwareness = CAnimationMgrList::GetPropFromName( szValue ); return LTTRUE; } return LTFALSE; }
LTBOOL CAIPathKnowledgeMgr::RegisterPathKnowledge(AIVolume *pDestVolume, CAIPathMgr::EnumPathBuildStatus eStatus) { // Existing path knowledge is only valid while our index matches // the global index. if( m_nPathKnowledgeIndex != g_pAIPathMgr->GetPathKnowledgeIndex() ) { ClearPathKnowledge(); m_nPathKnowledgeIndex = g_pAIPathMgr->GetPathKnowledgeIndex(); } // Search for existing knowledge of paths to the dest volume. AIPATH_KNOWLEDGE_MAP::iterator it = m_mapPathKnowledge.find( pDestVolume ); // Register a new piece of path knowledge. if( it == m_mapPathKnowledge.end() ) { if( eStatus == CAIPathMgr::kPath_NoPathFound ) { AITRACE( AIShowPaths, ( m_pAI->m_hObject, "Registering NoPathFound for %s", pDestVolume->GetName() ) ); } m_mapPathKnowledge.insert( AIPATH_KNOWLEDGE_MAP::value_type( pDestVolume, eStatus ) ); return LTTRUE; } // There is a problem if the status has changed! // The status of a dest volume should only change if something has happened to // the connectivity: e.g. a door was locked/unlocked, a volume was enabled/disabled, // an AI used a volume flagged as OnlyJumpDown, etc. // In these cases, Path Knowledge should be cleared. if( it->second != eStatus ) { AIASSERT( 0, m_pAI->m_hObject, "CAIPathKnowledgeMgr::RegisterPathKnowledge: Volume status has changed!" ); return LTFALSE; } return LTTRUE; }
LTBOOL CAIGoalAbstractStimulated::HandleDamage(const DamageStruct& damage) { // Stimuluated by whomever shot me. m_hStimulusSource = damage.hDamager; // Update the alarm level with new stimulus. AIBM_Stimulus* pStimulus = g_pAIButeMgr->GetStimulus( kStim_EnemyWeaponImpactVisible ); if( pStimulus ) { m_eSenseType = pStimulus->eSenseType; m_hStimulusTarget = LTNULL; m_pAI->IncrementAlarmLevel( pStimulus->nAlarmLevel ); AITRACE( AIShowGoals, ( m_pAI->m_hObject, "AI was shot!" ) ); } // Always return false to allow normal damage handling. return LTFALSE; }
void CAIPlan::ActivatePlan( CAI* pAI ) { if( !pAI ) { return; } m_pAI = pAI; m_fPlanActivationTime = g_pLTServer->GetTime(); CAIPlanStep* pPlanStep = m_lstAIPlanSteps[0]; if( pPlanStep ) { CAIActionAbstract* pAction = g_pAIActionMgr->GetAIAction( pPlanStep->eAIAction ); if( pAction ) { // Action's preconditions are not met. Bail. if( !pAction->ValidateContextPreconditions( m_pAI, pPlanStep->wsWorldState, !IS_PLANNING ) ) { AIASSERT2( 0, m_pAI->m_hObject, "Failed to activate plan due to first actions failed context preconditions: %s (%.2f)", s_aszActionTypes[pAction->GetActionRecord()->eActionType], pAction->GetActionRecord()->fActionCost ); m_pAI->GetAIBlackBoard()->SetBBInvalidatePlan( true ); return; } AITRACE( AIShowActions, ( m_pAI->m_hObject, "Activating Action: %s (%.2f)", s_aszActionTypes[pAction->GetActionRecord()->eActionType], pAction->GetActionRecord()->fActionCost ) ); pAction->ActivateAction( m_pAI, pPlanStep->wsWorldState ); // If action was immediately complete, advance to another action. if( pAction->IsActionComplete( m_pAI ) ) { AdvancePlan(); } } } }
LTBOOL CAIGoalGuard::HandleNameValuePair(const char *szName, const char *szValue) { AIASSERT(szName && szValue, m_pAI->m_hObject, "CAIGoalGuard::HandleNameValuePair: Name or value is NULL."); if( super::HandleNameValuePair(szName, szValue) ) { return LTTRUE; } if ( !_stricmp(szName, "NODE") ) { // If Goal was already active (walking to previous guard node) // Reset the goal. if( m_pGoalMgr->IsCurGoal( this ) ) { m_pAI->SetState( kState_HumanIdle ); } AINode* pNode = g_pAINodeMgr->GetNode(szValue); if( pNode ) { SetGuardNode( pNode ); RecalcImportance(); AITRACE( AIShowGoals, ( m_pAI->m_hObject, "CAIGoal%s: NODE=%s", s_aszGoalTypes[GetGoalType()], ::ToString( pNode->GetName() ) ) ); } else { AIError( "%s Cannot find node! CAIGoal%s: NODE=%s", m_pAI->GetName(), s_aszGoalTypes[GetGoalType()], szValue ); } return LTTRUE; } return LTFALSE; }
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 CAIGoalMgr::UpdateGoalRelevances( bool bReplan ) { bool bRequiresHigherInterruptPriority = false; bool bGoalChanged = false; if( m_pAI->GetAnimationContext()->IsTransitioning() ) { bRequiresHigherInterruptPriority = true; } // Some actions may be interrupted. if( (m_pCurGoal ) && ( m_pAI->GetAnimationContext()->IsLocked() ) && ( !m_pCurGoal->IsPlanInterruptible() ) ) { bRequiresHigherInterruptPriority = true; } CAIGoalAbstract* pGoalMax = FindMostRelevantGoal( true ); // Set the new current goal, which is the // goal with the highest relevance that can // formulate a valid plan of actions. while( pGoalMax ) { // If the pGoalMax has the same relevance as the current goal, prefer the // current to avoid abandoning a goal prematurely when there isn't anything // better to do. if ( (m_pCurGoal && pGoalMax) && (m_pCurGoal != pGoalMax) && (m_pCurGoal->GetGoalRelevance() == pGoalMax->GetGoalRelevance())) { pGoalMax = m_pCurGoal; } // Goal has not changed. // No planning necessary. if( ( pGoalMax == m_pCurGoal ) && ( !bReplan ) && ( !m_pCurGoal->ReplanRequired() ) ) { break; } // Do not reactivate the same goal while transitioning. if( ( pGoalMax == m_pCurGoal ) && ( m_pAI->GetAnimationContext()->IsTransitioning() ) && ( !m_pCurGoal->CanReactivateDuringTransitions() ) ) { break; } // Goal must interrupt to be valid (ie damage response which must // be played). if ( bRequiresHigherInterruptPriority && pGoalMax && m_pCurGoal && ( pGoalMax != m_pCurGoal ) && ( pGoalMax->GetInterruptPriority() <= m_pCurGoal->GetInterruptPriority() ) ) { pGoalMax->ClearGoalRelevance(); } // Failed to meet random probability of activation. else if( ( pGoalMax->GetActivateChance() < 1.f ) && ( pGoalMax->GetActivateChance() < GetRandom(0.0f, 1.0f) ) ) { pGoalMax->ClearGoalRelevance(); } // Activate the goal if we formulated // a valid plan. else if( pGoalMax->BuildPlan() ) { if( m_pCurGoal ) { m_pCurGoal->DeactivateGoal(); AITRACE( AIShowGoals, ( m_pAI->m_hObject, "Deactivating Goal: %s", s_aszGoalTypes[m_pCurGoal->GetGoalType()] ) ); } m_pCurGoal = pGoalMax; AITRACE( AIShowGoals, ( m_pAI->m_hObject, "Activating Goal: %s (%.2f)", s_aszGoalTypes[m_pCurGoal->GetGoalType()], m_pCurGoal->GetGoalRelevance() ) ); m_pCurGoal->ActivateGoal(); m_pCurGoal->ActivatePlan(); bGoalChanged = true; break; } // Could not build a valid plan. else { pGoalMax->HandleBuildPlanFailure(); } pGoalMax = FindMostRelevantGoal( false ); } // No max goal was found, so the AI has no current goal or state. if( bReplan && !pGoalMax && !bRequiresHigherInterruptPriority && !m_pAI->GetAnimationContext()->IsTransitioning() ) { m_pCurGoal = NULL; m_pAI->ClearState(); } // Clear the SelectAction flag from the blackboard. // If the Goal required a interrupt, and the Goal did not change, // then do not clear the SelectAction flag. if( bRequiresHigherInterruptPriority && !bGoalChanged ) { return; } else { m_pAI->GetAIBlackBoard()->SetBBSelectAction( false ); } }
bool CAIPlanner::BuildPlan( CAI* pAI, CAIGoalAbstract* pGoal ) { //track our performance ///CTimedSystemBlock TimingBlock(g_tsAIPlanner); // Initialize the planner. m_AStarMapPlanner.InitAStarMapPlanner( pAI ); m_AStarGoalPlanner.InitAStarGoalPlanner( pAI, &m_AStarMapPlanner, pGoal ); // Set the start of the search to -1, indicating that // the search starts from the AIGoal rather than from // an AIAction. m_AStar.SetAStarSource( (ENUM_AStarNodeID)-1 ); // Run the AStar machine to search for a valid plan // to satisfy the AIGoal. AITRACE( AIShowPlanner, ( pAI->m_hObject, "Planner starting AStar for Goal '%s'...", s_aszGoalTypes[pGoal->GetGoalType()] ) ); m_AStar.RunAStar( pAI ); // If after the search the current node is NULL, then no // valid plan was found. CAIAStarNodePlanner* pNode = (CAIAStarNodePlanner*)( m_AStar.GetAStarNodeCur() ); if( !pNode ) { AITRACE( AIShowPlanner, ( pAI->m_hObject, "No plan found." ) ); return false; } // Create a new plan. CAIPlan* pPlan = AI_FACTORY_NEW( CAIPlan ); AITRACE( AIShowPlanner, ( pAI->m_hObject, "Found plan:" ) ); // Iterate over nodes in the planner's search path, // and add them to the plan. EnumAIActionType eAction; CAIPlanStep* pPlanStep; AIPLAN_STEP_LIST::iterator itPlan = pPlan->m_lstAIPlanSteps.end(); while( pNode ) { // If the AIAction is Invalid, this is the final node. eAction = m_AStarMapPlanner.ConvertID_AStarNode2AIAction( pNode->eAStarNodeID ); if( eAction == kAct_InvalidType ) { break; } // Create a new plan step. pPlanStep = AI_FACTORY_NEW( CAIPlanStep ); // Set the AIAction for this plan step. AITRACE( AIShowPlanner, ( pAI->m_hObject, " Action: %s", s_aszActionTypes[eAction] ) ); pPlanStep->eAIAction = eAction; // Advance the plan to the next node. pNode = (CAIAStarNodePlanner*)( pNode->pAStarParent ); // Copy the world state from the node. // This is the world state that should be achieved // taking this step of the plan. pPlanStep->wsWorldState.CopyWorldState( pNode->wsWorldStateGoal ); // Add the new step to the plan. pPlan->m_lstAIPlanSteps.push_back( pPlanStep ); } // Set the new plan for the AIGoal. pGoal->SetAIPlan( pPlan ); // Successfully built a plan. return true; }
bool CAIPlan::AdvancePlan() { CAIActionAbstract* pAction; CAIPlanStep* pPlanStep; // Continue advancing the plan until we find // one that can activate and is not immediately // complete. while( 1 ) { // Apply Effects to context. // By default, Actions do not have any effect on the real world. // They only affect the planners representation of the world. // This gives more control of where WorldState variables are set, // or allows them to never be set. // (e.g. AtTargetPos is never really true in the real world, // because targets more continuously.) pPlanStep = m_lstAIPlanSteps[m_iPlanStep]; if( pPlanStep ) { pAction = g_pAIActionMgr->GetAIAction( pPlanStep->eAIAction ); if( pAction ) { pAction->ApplyContextEffect( m_pAI, m_pAI->GetAIWorldState(), &( pPlanStep->wsWorldState ) ); pAction->DeactivateAction( m_pAI ); } } // Advance the step. // Bail if no more steps. ++m_iPlanStep; if( m_iPlanStep >= m_lstAIPlanSteps.size() ) { return false; } pPlanStep = m_lstAIPlanSteps[m_iPlanStep]; if( pPlanStep ) { pAction = g_pAIActionMgr->GetAIAction( pPlanStep->eAIAction ); if( pAction ) { // Action's preconditions are not met. Bail. if( !pAction->ValidateContextPreconditions( m_pAI, pPlanStep->wsWorldState, !IS_PLANNING ) ) { return false; } // Bail if action is not immediately complete. AITRACE( AIShowActions, ( m_pAI->m_hObject, "Activating Action: %s (%.2f)", s_aszActionTypes[pAction->GetActionRecord()->eActionType], pAction->GetActionRecord()->fActionCost ) ); pAction->ActivateAction( m_pAI, pPlanStep->wsWorldState ); if( !pAction->IsActionComplete( m_pAI ) ) { return true; } // Deactivate action if it was immediately complete. pAction->DeactivateAction( m_pAI ); } } } // Something is wrong. return false; }
void CAIGoalMgr::SelectRelevantGoal() { //track our performance CTimedSystemBlock TimingBlock(g_tsAIGoalSelection); // // Determine if the goals should be updated, and if so, if replanning is // required. // bool bUpdateGoals = false; bool bForceReplanning = false; if ( m_pCurGoal ) { // Test for an invalid plan. if ( !m_pCurGoal->IsPlanValid() ) { AITRACE( AIShowGoals, ( m_pAI->m_hObject, "Plan is invalid for Goal: %s.", s_aszGoalTypes[m_pCurGoal->GetGoalType()] ) ); bUpdateGoals = true; bForceReplanning = true; } // Test for a satisfied plan. else if ( m_pCurGoal->IsWSSatisfied( m_pAI->GetAIWorldState() ) ) { AITRACE( AIShowGoals, ( m_pAI->m_hObject, "Goal is satisfied: %s.", s_aszGoalTypes[m_pCurGoal->GetGoalType()] ) ); bUpdateGoals = true; bForceReplanning = true; } } // // Test for a request to reselect actions. // if ( m_pAI->GetAIBlackBoard()->GetBBSelectAction() ) { bUpdateGoals = true; } // // Plan has been invalidated, so immediately force a replan. // if ( m_pAI->GetAIBlackBoard()->GetBBInvalidatePlan() ) { AITRACE( AIShowGoals, ( m_pAI->m_hObject, "Honoring forced replanning request." ) ); bUpdateGoals = true; bForceReplanning = true; } // // Perform the actual goal updating/replanning if requested // if ( bUpdateGoals ) { UpdateGoalRelevances( bForceReplanning ); } // Clear the forced replanning flag. // Clear this AFTER updating goal relevances. Some // legacy code depends on the state of the InvalidatePlan flag. // (e.g. AINavMeshLinkCrawl). m_pAI->GetAIBlackBoard()->SetBBInvalidatePlan( false ); }
bool Alarm::OnTrigger(HOBJECT hSender, const CParsedMsg &cMsg) { static CParsedMsg::CToken s_cTok_Activate(s_szActivate); static CParsedMsg::CToken s_cTok_Lock(s_szLock); static CParsedMsg::CToken s_cTok_Unlock(s_szUnlock); if ( cMsg.GetArg(0) == s_cTok_Activate ) { // If the alarm is activated from a command, the sender is // NULL (dammit!). So treat it as player-activated. // If the alarm is activated by something other than AI // (e.g. a command object) consider it player activated. if( !IsAI( hSender ) ) { CPlayerObj *pPlayer = g_pCharacterMgr->FindPlayer(); hSender = pPlayer->m_hObject; } HOBJECT hStimulus = hSender; if( IsPlayer(hSender) ) { // Run the alarm's player-activate command. if( m_hstrPlayerActivateCommand ) { const char *szCmd = g_pLTServer->GetStringData( m_hstrPlayerActivateCommand ); if( g_pCmdMgr->IsValidCmd( szCmd ) ) { g_pCmdMgr->Process( szCmd, m_hObject, m_hObject ); } } } else { // The stimulus is the target of the AI who activated the alarm. CAI* pAI = (CAI*)g_pLTServer->HandleToObject(hSender); hStimulus = pAI->GetTarget()->GetObject(); } // Ensure that lists of Alert and Respond regions are set up. if( ( m_fRegionsGroupRadius == 0.f ) && ( ( m_lstAlertRegions.size() > 0 ) || ( m_lstRespondRegions.size() > 0 ) || ( m_lstSearchRegions.size() > 0 ) ) ) { CalculateRegionsGroupRadius(); } // Place an alarm stimulus. // The stimulus position and radius are set to values that encompass // all of the regions affected by the alarm. g_pAIStimulusMgr->RegisterStimulus(kStim_EnemyAlarmSound, hStimulus, m_hObject, m_vRegionsGroupCenter, m_fRegionsGroupRadius, m_fAlarmSoundTime); AITRACE( AIShowAlarms, ( m_hObject, "Triggering alarm" ) ); } else if ( cMsg.GetArg(0) == s_cTok_Lock ) { m_bLocked = LTTRUE; } else if ( cMsg.GetArg(0) == s_cTok_Unlock ) { m_bLocked = LTFALSE; } else return Prop::OnTrigger(hSender, cMsg); return true; }
void CAISenseRecorderAbstract::HandleSenses(uint32 nCycle) { AISENSE_RECORD_MAP::iterator it; for(it = m_mapSenseRecords.begin(); it != m_mapSenseRecords.end(); ++it) { AISenseRecord* pSenseRecord = it->second; AIBM_Stimulus* pAIBM_Stimulus = pSenseRecord->pAIBM_Last_Stimulus; // If pAIBM_Stimulus is NULL, this sense has never been stimulated. if(pAIBM_Stimulus == LTNULL) { continue; } // Check for senses that were not updated this cycle. // Ignore senses that were not updated this cycle because // of a delayed stimulus. if((pSenseRecord->nCycle != nCycle) && (pSenseRecord->fReactionDelayTimer == 0.f)) { // Decrease the simulation, if there is any. if(pSenseRecord->fCurStimulation > 0.f) { LTBOOL bFalseStimulation = DecreaseStimulation(pSenseRecord, 1.0f); // If not a false stimulation, do not let the state handle the sense. if(!bFalseStimulation) { continue; } // If we have hit the false stimulation limit, treat it as a real stimulation. if( pSenseRecord->cFalseStimulation >= pAIBM_Stimulus->nFalseStimulationLimit ) { pSenseRecord->cFalseStimulation = 0; pSenseRecord->fCurStimulation = pAIBM_Stimulus->rngStimulationThreshhold.GetMax(); pSenseRecord->fMaxStimulation = pAIBM_Stimulus->rngStimulationThreshhold.GetMax(); AITRACE(AIShowSenses, (m_pSensing->GetSensingObject(), "Hit FalseStimulution Limit %d of %s\n", pAIBM_Stimulus->nFalseStimulationLimit, SenseToString(pSenseRecord->eSenseType)) ); } // Treat other false stimulations as other senses, // if an optional FalseStimulusSense was specified. else if( pAIBM_Stimulus->eFalseStimulusSense != kSense_InvalidType ) { AISenseRecord* pFalseStimulationSense = GetSense( pAIBM_Stimulus->eFalseStimulusSense ); if( pFalseStimulationSense ) { pFalseStimulationSense->pAIBM_Last_Stimulus = pAIBM_Stimulus; pFalseStimulationSense->hLastStimulusSource = pSenseRecord->hLastStimulusSource; pFalseStimulationSense->vLastStimulusPos = pSenseRecord->vLastStimulusPos; pFalseStimulationSense->eLastStimulusID = pSenseRecord->eLastStimulusID; pFalseStimulationSense->fCurStimulation = pAIBM_Stimulus->rngStimulationThreshhold.GetMax(); pFalseStimulationSense->fMaxStimulation = pAIBM_Stimulus->rngStimulationThreshhold.GetMax(); pFalseStimulationSense->fLastStimulationTime = pSenseRecord->fLastStimulationTime; pFalseStimulationSense->nLastStimulusAlarmLevel = 1; pFalseStimulationSense->nCycle = nCycle; } } } } // Invalidate hiding spots if AI can already see player. if( ( pSenseRecord->eSenseType == kSense_SeeEnemy ) && IsPlayer( pSenseRecord->hLastStimulusSource ) && IsAI( m_pSensing->GetSensingObject() ) && ( pSenseRecord->fCurStimulation >= 0.5f ) ) { CPlayerObj* pPlayer = (CPlayerObj*)g_pLTServer->HandleToObject( pSenseRecord->hLastStimulusSource ); CAI* pAI = (CAI*)g_pLTServer->HandleToObject( m_pSensing->GetSensingObject() ); if( pPlayer && pAI ) { pPlayer->SetVisibleToEnemyAI( pAI, true ); } } // Check if we're fully stimulated. if(pSenseRecord->fCurStimulation >= pAIBM_Stimulus->rngStimulationThreshhold.GetMax()) { // Wait for, or increment, ReactionDelay. if(pSenseRecord->fReactionDelayTimer >= pSenseRecord->fReactionDelayTime) { m_pSensing->HandleSenseTrigger( pSenseRecord ); pSenseRecord->fReactionDelayTimer = 0.f; pSenseRecord->fReactionDelayTime = 0.f; } else pSenseRecord->fReactionDelayTimer += m_pSensing->GetSenseUpdateRate(); } } }
void CAIPathKnowledgeMgr::ClearPathKnowledge() { m_mapPathKnowledge.clear(); AITRACE( AIShowPaths, ( m_pAI->m_hObject, "Clearing cached PathKnowledge" ) ); }
void CAIGoalMountedFlashlight::RecalcImportance() { if( m_pAI->IsFirstUpdate() ) { return; } // Create a flashlight model if we have a ranged weapon // (because it is mounted on the gun). if( !m_bCheckedForRangedWeapon ) { if( HasRangedWeapon() ) { m_bHasRangedWeapon = LTTRUE; } m_bCheckedForRangedWeapon = LTTRUE; } // Ignore flashlight if we do not know our current volume, // or do not have a flashlight. if( !( m_bHasRangedWeapon && m_pAI->HasCurrentVolume() ) ) { return; } AIVolume* pVolume = m_pAI->GetCurrentVolume(); // Volume is lit, so turn off flashlight. // Turn off light if affected by special damage (e.g. knocked out). // Turn off light if ranged weapon is not currently armed. if( m_bLightOn && ( pVolume->IsLit() || m_pAI->GetDamageFlags() || !m_pAI->HasWeapon( kAIWeap_Ranged ) ) ) { char szDetach[128]; sprintf( szDetach, "%s Light", KEY_DETACH ); SendTriggerMsgToObject( m_pAI, m_pAI->m_hObject, LTFALSE, szDetach ); AITRACE( AIShowGoals, ( m_pAI->m_hObject, "Flashlight OFF" ) ); m_pAI->PlaySound( FLASHLIGHT_SOUND ); m_bLightOn = LTFALSE; } // Volume is dark, so turn on flashlight. // Do not turn on a light if affected by special damage (e.g. knocked out). // Do not turn on light if ranged weapon is not currently armed. else if( !m_bLightOn && !pVolume->IsLit() && !m_pAI->GetDamageFlags() && m_pAI->HasWeapon( kAIWeap_Ranged ) ) { char szAttachment[128]; sprintf( szAttachment, "%s Light (%s)", KEY_ATTACH, FLASHLIGHT_ATTACHMENT ); SendTriggerMsgToObject( m_pAI, m_pAI->m_hObject, LTFALSE, szAttachment ); AITRACE( AIShowGoals, ( m_pAI->m_hObject, "Flashlight ON" ) ); m_pAI->PlaySound( FLASHLIGHT_SOUND ); m_bLightOn = LTTRUE; } }