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;
}
Exemplo n.º 2
0
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()] ) );
	}
}
Exemplo n.º 3
0
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;
}
Exemplo n.º 4
0
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;
}
Exemplo n.º 5
0
// ----------------------------------------------------------------------- //
//
//	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 );
}
Exemplo n.º 6
0
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;
}
Exemplo n.º 7
0
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 );
	}
}
Exemplo n.º 8
0
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;
}
Exemplo n.º 9
0
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;
}
Exemplo n.º 10
0
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;
	}
}
Exemplo n.º 11
0
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;
}
Exemplo n.º 12
0
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() );
		}
	}
}
Exemplo n.º 13
0
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 );
}
Exemplo n.º 14
0
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()] ) );
		}
	}
}
Exemplo n.º 15
0
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;
}
Exemplo n.º 16
0
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;
}
Exemplo n.º 17
0
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;
}
Exemplo n.º 18
0
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();
			}
		}
	}
}
Exemplo n.º 19
0
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;
}
Exemplo n.º 20
0
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;
}
Exemplo n.º 21
0
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 );
	}
}
Exemplo n.º 22
0
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;
}
Exemplo n.º 23
0
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;
}
Exemplo n.º 24
0
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 );
}
Exemplo n.º 25
0
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;
}
Exemplo n.º 26
0
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();
		}
	}
}
Exemplo n.º 27
0
void CAIPathKnowledgeMgr::ClearPathKnowledge()
{
	m_mapPathKnowledge.clear();
	AITRACE( AIShowPaths, ( m_pAI->m_hObject, "Clearing cached PathKnowledge" ) );
}
Exemplo n.º 28
0
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;
	}
}