//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #ifndef AI_BEHAVIOR_H #define AI_BEHAVIOR_H #include "ai_component.h" #include "ai_basenpc.h" #include "ai_default.h" #include "AI_Criteria.h" #include "networkvar.h" #include "delegates.h" #include "tier1/utlvector.h" #include "generic_classmap.h" #ifdef DEBUG #pragma warning(push) #include #pragma warning(pop) #pragma warning(disable:4290) #endif #if defined( _WIN32 ) #pragma once #endif //----------------------------------------------------------------------------- // CAI_Behavior... // // Purpose: The core component that defines a behavior in an NPC by selecting // schedules and running tasks // // Intended to be used as an organizational tool as well as a way // for various NPCs to share behaviors without sharing an inheritance // relationship, and without cramming those behaviors into the base // NPC class. //----------------------------------------------------------------------------- struct AIChannelScheduleState_t { AIChannelScheduleState_t() { memset( this, 0, sizeof( *this ) ); } bool bActive; CAI_Schedule * pSchedule; int idealSchedule; int failSchedule; int iCurTask; TaskStatus_e fTaskStatus; float timeStarted; float timeCurTaskStarted; AI_TaskFailureCode_t taskFailureCode; bool bScheduleWasInterrupted; DECLARE_SIMPLE_DATADESC(); }; //----------------------------------------------------------------------------- // Purpose: Base class defines interface to behaviors and provides bridging // methods //----------------------------------------------------------------------------- class CAI_BehaviorBase : public CAI_Component, public IAI_BehaviorBridge { DECLARE_CLASS( CAI_BehaviorBase, CAI_Component ) public: CAI_BehaviorBase(CAI_BaseNPC *pOuter = NULL) : CAI_Component(pOuter), m_pBackBridge(NULL) { m_bAllocated = false; } void SetAllocated( ) { m_bAllocated = true; } bool IsAllocated( ) { return m_bAllocated; } #define AI_GENERATE_BEHAVIOR_BRIDGES #include "ai_behavior_template.h" #define AI_GENERATE_BASE_METHODS #include "ai_behavior_template.h" virtual const char *GetClassNameV() { return ""; } virtual const char *GetName() = 0; virtual bool DeleteOnHostDestroy() { return m_bAllocated; } // @QUESTION: should switch to reference count? virtual bool KeyValue( const char *szKeyName, const char *szValue ) { return false; } bool IsRunning() { Assert( GetOuter() ); return ( GetOuter()->GetPrimaryBehavior() == this ); } virtual bool CanSelectSchedule() { return true; } virtual void BeginScheduleSelection() {} virtual void EndScheduleSelection() {} void SetBackBridge( IAI_BehaviorBridge *pBackBridge ) { Assert( m_pBackBridge == NULL || pBackBridge == NULL ); m_pBackBridge = pBackBridge; } virtual void Precache() {} virtual void Spawn() {} virtual void UpdateOnRemove() {} virtual void Event_Killed( const CTakeDamageInfo &info ) {} virtual void CleanupOnDeath( CBaseEntity *pCulprit, bool bFireDeathOutput ) {} virtual void OnChangeHintGroup( string_t oldGroup, string_t newGroup ) {} void BridgeOnStartSchedule( int scheduleType ); int BridgeSelectSchedule(); bool BridgeSelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode, int *pResult ); bool BridgeStartTask( const Task_t *pTask ); bool BridgeRunTask( const Task_t *pTask); int BridgeTranslateSchedule( int scheduleType ); bool BridgeGetSchedule( int localScheduleID, CAI_Schedule **ppResult ); bool BridgeTaskName(int taskID, const char **); virtual void BuildScheduleTestBits() {} virtual void BuildScheduleTestBitsNotActive() {} virtual void GatherConditions(); virtual void GatherConditionsNotActive() { return; } // Override this and your behavior will call this in place of GatherConditions() when your behavior is NOT the active one. virtual void OnUpdateShotRegulator() {} virtual float GetJumpGravity() const; virtual bool IsJumpLegal( const Vector &startPos, const Vector &apex, const Vector &endPos, float maxUp, float maxDown, float maxDist ) const; virtual bool MovementCost( int moveType, const Vector &vecStart, const Vector &vecEnd, float *pCost ); virtual void OnChangeActiveWeapon( CBaseCombatWeapon *pOldWeapon, CBaseCombatWeapon *pNewWeapon ) {}; virtual CAI_ClassScheduleIdSpace *GetClassScheduleIdSpace(); virtual int DrawDebugTextOverlays( int text_offset ); virtual bool ShouldNPCSave() { return true; } virtual int Save( ISave &save ); virtual int Restore( IRestore &restore ); virtual void OnRestore() {} static void SaveBehaviors(ISave &save, CAI_BehaviorBase *pCurrentBehavior, CAI_BehaviorBase **ppBehavior, int nBehaviors, bool bTestIfNPCSave = true ); static int RestoreBehaviors(IRestore &restore, CAI_BehaviorBase **ppBehavior, int nBehaviors, bool bTestIfNPCSave = true ); // returns index of "current" behavior, or -1 public: // // Secondary schedule channel support // void StartChannel( int channel ); void StopChannel( int channel ); void MaintainChannelSchedules(); void MaintainSchedule( int channel ); void SetSchedule( int channel, CAI_Schedule *pNewSchedule ); bool SetSchedule( int channel, int localScheduleID ); void ClearSchedule( int channel, const char *szReason ); CAI_Schedule *GetCurSchedule( int channel ); bool IsCurSchedule( int channel, int schedId, bool fIdeal = true ); virtual void OnScheduleChange( int channel ); virtual void OnStartSchedule( int channel, int scheduleType ); virtual int SelectSchedule( int channel ); virtual int SelectFailSchedule( int channel, int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode ); virtual int TranslateSchedule( int channel, int scheduleType ) { return scheduleType; } virtual void StartTask( int channel, const Task_t *pTask ); virtual void RunTask( int channel, const Task_t *pTask ); const Task_t *GetCurTask( void ) { return BaseClass::GetCurTask(); } const Task_t *GetCurTask( int channel ); bool TaskIsComplete( int channel ) { return ( m_ScheduleChannels[channel].fTaskStatus == TASKSTATUS_COMPLETE ); } int TaskIsComplete() { return BaseClass::TaskIsComplete(); } virtual void TaskFail( AI_TaskFailureCode_t code ) { BaseClass::TaskFail( code ) ; } void TaskFail( const char *pszGeneralFailText ) { BaseClass::TaskFail( pszGeneralFailText ); } void TaskComplete( bool fIgnoreSetFailedCondition = false ) { BaseClass::TaskComplete( fIgnoreSetFailedCondition ); } virtual void TaskFail( int channel, AI_TaskFailureCode_t code ); void TaskFail( int channel, const char *pszGeneralFailText ) { TaskFail( channel, MakeFailCode( pszGeneralFailText ) ); } void TaskComplete( int channel, bool fIgnoreSetFailedCondition = false ); private: bool IsScheduleValid( AIChannelScheduleState_t *pScheduleState ); CAI_Schedule *GetNewSchedule( int channel ); CAI_Schedule *GetFailSchedule( AIChannelScheduleState_t *pScheduleState ); const Task_t *GetTask( AIChannelScheduleState_t *pScheduleState ); void SaveChannels( ISave &save ); void RestoreChannels( IRestore &restore ); CUtlVector m_ScheduleChannels; protected: int GetNpcState() { return GetOuter()->m_NPCState; } virtual void OnStartSchedule( int scheduleType ); virtual int SelectSchedule(); virtual int SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode ); virtual void StartTask( const Task_t *pTask ); virtual void RunTask( const Task_t *pTask ); virtual int TranslateSchedule( int scheduleType ); virtual CAI_Schedule *GetSchedule(int schedule); virtual const char *GetSchedulingErrorName(); bool IsCurSchedule( int schedId, bool fIdeal = true ); CAI_Hint * GetHintNode() { return GetOuter()->GetHintNode(); } const CAI_Hint *GetHintNode() const { return GetOuter()->GetHintNode(); } void SetHintNode( CAI_Hint *pHintNode ) { GetOuter()->SetHintNode( pHintNode ); } void ClearHintNode( float reuseDelay = 0.0 ) { GetOuter()->ClearHintNode( reuseDelay ); } string_t GetHintGroup() { return GetOuter()->GetHintGroup(); } void ClearHintGroup() { GetOuter()->ClearHintGroup(); } void SetHintGroup( string_t name ) { GetOuter()->SetHintGroup( name ); } // For now, only support simple behavior stack: DELEGATE_TO_OBJECT_0V( BehaviorBridge_GatherConditions, m_pBackBridge ); DELEGATE_TO_OBJECT_0( int, BehaviorBridge_SelectSchedule, m_pBackBridge ); DELEGATE_TO_OBJECT_1( int, BehaviorBridge_TranslateSchedule, int, m_pBackBridge ); protected: // Used by derived classes to chain a task to a task that might not be the // one they are currently handling: void ChainStartTask( int task, float taskData = 0 ); void ChainRunTask( int task, float taskData = 0 ); protected: bool NotifyChangeBehaviorStatus( bool fCanFinishSchedule = false ); bool HaveSequenceForActivity( Activity activity ) { return GetOuter()->HaveSequenceForActivity( activity ); } //--------------------------------- // // These allow derived classes to implement custom schedules // static CAI_GlobalScheduleNamespace *GetSchedulingSymbols() { return CAI_BaseNPC::GetSchedulingSymbols(); } static bool LoadSchedules() { return true; } virtual bool IsBehaviorSchedule( int scheduleType ) { return false; } CAI_Navigator * GetNavigator() { return GetOuter()->GetNavigator(); } CAI_Motor * GetMotor() { return GetOuter()->GetMotor(); } CAI_TacticalServices * GetTacticalServices() { return GetOuter()->GetTacticalServices(); } bool m_fOverrode; IAI_BehaviorBridge *m_pBackBridge; bool m_bAllocated; public: static CGenericClassmap< CAI_BehaviorBase > m_BehaviorClasses; private: DECLARE_DATADESC(); }; #define LINK_BEHAVIOR_TO_CLASS( localName, className ) \ static CAI_BehaviorBase *C##className##Factory( void ) \ { \ return static_cast< CAI_BehaviorBase * >( new className ); \ }; \ class C##localName##Foo \ { \ public: \ C##localName##Foo( void ) \ { \ CAI_BehaviorBase::m_BehaviorClasses.Add( #localName, #className, \ sizeof( className ),&C##className##Factory ); \ } \ }; \ static C##localName##Foo g_C##localName##Foo; #define LINK_BEHAVIOR_TO_CLASSNAME( className ) \ static CAI_BehaviorBase *C##className##Factory( void ) \ { \ return static_cast< CAI_BehaviorBase * >( new className ); \ }; \ class C##className##Foo \ { \ public: \ C##className##Foo( void ) \ { \ CAI_BehaviorBase::m_BehaviorClasses.Add( ##className::GetClassName(), #className, \ sizeof( className ),&C##className##Factory ); \ } \ }; \ static C##className##Foo g_C##className##Foo; //----------------------------------------------------------------------------- // Purpose: Template provides provides back bridge to owning class and // establishes namespace settings //----------------------------------------------------------------------------- template class CAI_Behavior : public CAI_ComponentWithOuter { public: DECLARE_CLASS_NOFRIEND( CAI_Behavior, NPC_CLASS ); enum { NEXT_TASK = ID_SPACE_OFFSET, NEXT_SCHEDULE = ID_SPACE_OFFSET, NEXT_CONDITION = ID_SPACE_OFFSET, NEXT_CHANNEL = ID_SPACE_OFFSET, }; void SetCondition( int condition ) { if ( condition >= ID_SPACE_OFFSET && condition < ID_SPACE_OFFSET + 10000 ) // it's local to us condition = GetClassScheduleIdSpace()->ConditionLocalToGlobal( condition ); this->GetOuter()->SetCondition( condition ); } bool HasCondition( int condition ) { if ( condition >= ID_SPACE_OFFSET && condition < ID_SPACE_OFFSET + 10000 ) // it's local to us condition = GetClassScheduleIdSpace()->ConditionLocalToGlobal( condition ); return this->GetOuter()->HasCondition( condition ); } bool HasInterruptCondition( int condition ) { if ( condition >= ID_SPACE_OFFSET && condition < ID_SPACE_OFFSET + 10000 ) // it's local to us condition = GetClassScheduleIdSpace()->ConditionLocalToGlobal( condition ); return this->GetOuter()->HasInterruptCondition( condition ); } void ClearCondition( int condition ) { if ( condition >= ID_SPACE_OFFSET && condition < ID_SPACE_OFFSET + 10000 ) // it's local to us condition = GetClassScheduleIdSpace()->ConditionLocalToGlobal( condition ); this->GetOuter()->ClearCondition( condition ); } protected: CAI_Behavior(NPC_CLASS *pOuter = NULL) : CAI_ComponentWithOuter(pOuter) { } static CAI_GlobalScheduleNamespace *GetSchedulingSymbols() { return NPC_CLASS::GetSchedulingSymbols(); } virtual CAI_ClassScheduleIdSpace *GetClassScheduleIdSpace() { return this->GetOuter()->GetClassScheduleIdSpace(); } static CAI_ClassScheduleIdSpace &AccessClassScheduleIdSpaceDirect() { return NPC_CLASS::AccessClassScheduleIdSpaceDirect(); } private: virtual bool IsBehaviorSchedule( int scheduleType ) { return ( scheduleType >= ID_SPACE_OFFSET && scheduleType < ID_SPACE_OFFSET + 10000 ); } }; //----------------------------------------------------------------------------- // Purpose: The common instantiation of the above template //----------------------------------------------------------------------------- typedef CAI_Behavior<> CAI_SimpleBehavior; //----------------------------------------------------------------------------- // Purpose: Base class for AIs that want to act as a host for CAI_Behaviors // NPCs aren't required to use this, but probably want to. //----------------------------------------------------------------------------- template class CAI_BehaviorHostBase : public BASE_NPC { DECLARE_CLASS( CAI_BehaviorHostBase, BASE_NPC ); protected: CAI_BehaviorHostBase() { } }; template class CAI_BehaviorHost : public CAI_BehaviorHostBase { DECLARE_CLASS( CAI_BehaviorHost, CAI_BehaviorHostBase ); public: CAI_BehaviorHost() { } #define AI_GENERATE_BRIDGES #include "ai_behavior_template.h" #define AI_GENERATE_HOST_METHODS #include "ai_behavior_template.h" void CleanupOnDeath( CBaseEntity *pCulprit = NULL, bool bFireDeathOutput = true ); virtual int Save( ISave &save ); virtual int Restore( IRestore &restore ); // Bridges void Precache(); void UpdateOnRemove(); void Event_Killed( const CTakeDamageInfo &info ); void GatherConditions(); int SelectSchedule(); void KeepRunningBehavior(); int SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode ); void OnStartSchedule( int scheduleType ); int TranslateSchedule( int scheduleType ); void StartTask( const Task_t *pTask ); void RunTask( const Task_t *pTask ); CAI_Schedule * GetSchedule(int localScheduleID); const char * TaskName(int taskID); void BuildScheduleTestBits(); void BuildScheduleTestBitsNotActive(); void OnChangeHintGroup( string_t oldGroup, string_t newGroup ); void OnChangeActiveWeapon( CBaseCombatWeapon *pOldWeapon, CBaseCombatWeapon *pNewWeapon ); void OnRestore(); float GetJumpGravity() const; bool IsJumpLegal( const Vector &startPos, const Vector &apex, const Vector &endPos, float maxUp, float maxDown, float maxDist ) const; bool MovementCost( int moveType, const Vector &vecStart, const Vector &vecEnd, float *pCost ); //--------------------------------- protected: CAI_Schedule * GetNewSchedule(); CAI_Schedule * GetFailSchedule(); private: void BehaviorBridge_GatherConditions(); int BehaviorBridge_SelectSchedule(); int BehaviorBridge_TranslateSchedule( int scheduleType ); float BehaviorBridge_GetJumpGravity() const; bool BehaviorBridge_IsJumpLegal( const Vector &startPos, const Vector &apex, const Vector &endPos, float maxUp, float maxDown, float maxDist ) const; bool BehaviorBridge_MovementCost( int moveType, const Vector &vecStart, const Vector &vecEnd, float *pCost ); bool m_bCalledBehaviorSelectSchedule; }; //----------------------------------------------------------------------------- // The first frame a behavior begins schedule selection, it won't have had it's GatherConditions() // called. To fix this, BeginScheduleSelection() manually calls the new behavior's GatherConditions(), // but sets this global so that the baseclass GatherConditions() isn't called as well. extern bool g_bBehaviorHost_PreventBaseClassGatherConditions; //----------------------------------------------------------------------------- inline void CAI_BehaviorBase::BridgeOnStartSchedule( int scheduleType ) { int localId = AI_IdIsGlobal( scheduleType ) ? GetClassScheduleIdSpace()->ScheduleGlobalToLocal( scheduleType ) : scheduleType; OnStartSchedule( localId ); } //------------------------------------- inline int CAI_BehaviorBase::BridgeSelectSchedule() { int result = SelectSchedule(); if ( IsBehaviorSchedule( result ) ) return GetClassScheduleIdSpace()->ScheduleLocalToGlobal( result ); return result; } //------------------------------------- inline bool CAI_BehaviorBase::BridgeSelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode, int *pResult ) { m_fOverrode = true; int result = SelectFailSchedule( failedSchedule, failedTask, taskFailCode ); if ( m_fOverrode ) { if ( result != SCHED_NONE ) { if ( IsBehaviorSchedule( result ) ) *pResult = GetClassScheduleIdSpace()->ScheduleLocalToGlobal( result ); else *pResult = result; return true; } Warning( "An AI behavior is in control but has no recommended schedule\n" ); } return false; } //------------------------------------- inline bool CAI_BehaviorBase::BridgeStartTask( const Task_t *pTask ) { m_fOverrode = true; StartTask( pTask ); return m_fOverrode; } //------------------------------------- inline bool CAI_BehaviorBase::BridgeRunTask( const Task_t *pTask) { m_fOverrode = true; RunTask( pTask ); return m_fOverrode; } //------------------------------------- inline void CAI_BehaviorBase::ChainStartTask( int task, float taskData ) { Task_t tempTask = { task, taskData }; bool fPrevOverride = m_fOverrode; this->GetOuter()->StartTask( (const Task_t *)&tempTask ); m_fOverrode = fPrevOverride;; } //------------------------------------- inline void CAI_BehaviorBase::ChainRunTask( int task, float taskData ) { Task_t tempTask = { task, taskData }; bool fPrevOverride = m_fOverrode; this->GetOuter()->RunTask( (const Task_t *) &tempTask ); m_fOverrode = fPrevOverride;; } //------------------------------------- inline int CAI_BehaviorBase::BridgeTranslateSchedule( int scheduleType ) { int localId = AI_IdIsGlobal( scheduleType ) ? GetClassScheduleIdSpace()->ScheduleGlobalToLocal( scheduleType ) : scheduleType; int result = TranslateSchedule( localId ); return result; } //------------------------------------- inline bool CAI_BehaviorBase::BridgeGetSchedule( int localScheduleID, CAI_Schedule **ppResult ) { *ppResult = GetSchedule( localScheduleID ); return (*ppResult != NULL ); } //------------------------------------- inline bool CAI_BehaviorBase::BridgeTaskName( int taskID, const char **ppResult ) { if ( AI_IdIsLocal( taskID ) ) { *ppResult = GetSchedulingSymbols()->TaskIdToSymbol( GetClassScheduleIdSpace()->TaskLocalToGlobal( taskID ) ); return (*ppResult != NULL ); } return false; } //----------------------------------------------------------------------------- template inline void CAI_BehaviorHost::CleanupOnDeath( CBaseEntity *pCulprit, bool bFireDeathOutput ) { this->DeferSchedulingToBehavior( NULL ); for( int i = 0; i < this->m_Behaviors.Count(); i++ ) { this->m_Behaviors[i]->CleanupOnDeath( pCulprit, bFireDeathOutput ); } BaseClass::CleanupOnDeath( pCulprit, bFireDeathOutput ); } //------------------------------------- template inline void CAI_BehaviorHost::GatherConditions() { // Iterate over behaviors and call GatherConditionsNotActive() on each behavior // not currently active. for( int i = 0; i < this->m_Behaviors.Count(); i++ ) { if( this->m_Behaviors[i] != this->m_pPrimaryBehavior ) { this->m_Behaviors[i]->GatherConditionsNotActive(); } } if ( this->m_pPrimaryBehavior ) this->m_pPrimaryBehavior->GatherConditions(); else BaseClass::GatherConditions(); } //------------------------------------- template inline void CAI_BehaviorHost::BehaviorBridge_GatherConditions() { if ( g_bBehaviorHost_PreventBaseClassGatherConditions ) return; BaseClass::GatherConditions(); } //------------------------------------- template inline void CAI_BehaviorHost::OnStartSchedule( int scheduleType ) { if ( this->m_pPrimaryBehavior ) this->m_pPrimaryBehavior->BridgeOnStartSchedule( scheduleType ); BaseClass::OnStartSchedule( scheduleType ); } //------------------------------------- template inline int CAI_BehaviorHost::BehaviorBridge_SelectSchedule() { return BaseClass::SelectSchedule(); } //------------------------------------- template inline CAI_Schedule *CAI_BehaviorHost::GetNewSchedule() { m_bCalledBehaviorSelectSchedule = false; CAI_Schedule *pResult = BaseClass::GetNewSchedule(); if ( !m_bCalledBehaviorSelectSchedule && this->m_pPrimaryBehavior ) this->DeferSchedulingToBehavior( NULL ); return pResult; } //------------------------------------- template inline CAI_Schedule *CAI_BehaviorHost::GetFailSchedule() { m_bCalledBehaviorSelectSchedule = false; CAI_Schedule *pResult = BaseClass::GetFailSchedule(); if ( !m_bCalledBehaviorSelectSchedule && this->m_pPrimaryBehavior ) this->DeferSchedulingToBehavior( NULL ); return pResult; } //------------------------------------- template inline int CAI_BehaviorHost::BehaviorBridge_TranslateSchedule( int scheduleType ) { return BaseClass::TranslateSchedule( scheduleType ); } //------------------------------------- template inline int CAI_BehaviorHost::TranslateSchedule( int scheduleType ) { if ( this->m_pPrimaryBehavior ) { return this->m_pPrimaryBehavior->BridgeTranslateSchedule( scheduleType ); } return BaseClass::TranslateSchedule( scheduleType ); } //------------------------------------- template inline int CAI_BehaviorHost::SelectSchedule() { m_bCalledBehaviorSelectSchedule = true; if ( this->m_pPrimaryBehavior ) { return this->m_pPrimaryBehavior->BridgeSelectSchedule(); } return BaseClass::SelectSchedule(); } //------------------------------------- template inline void CAI_BehaviorHost::KeepRunningBehavior() { if ( this->m_pPrimaryBehavior ) m_bCalledBehaviorSelectSchedule = true; } //------------------------------------- template inline int CAI_BehaviorHost::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode ) { m_bCalledBehaviorSelectSchedule = true; int result = 0; if ( this->m_pPrimaryBehavior && this->m_pPrimaryBehavior->BridgeSelectFailSchedule( failedSchedule, failedTask, taskFailCode, &result ) ) return result; return BaseClass::SelectFailSchedule( failedSchedule, failedTask, taskFailCode ); } //------------------------------------- template inline void CAI_BehaviorHost::StartTask( const Task_t *pTask ) { if ( this->m_pPrimaryBehavior && this->m_pPrimaryBehavior->BridgeStartTask( pTask ) ) return; BaseClass::StartTask( pTask ); } //------------------------------------- template inline void CAI_BehaviorHost::RunTask( const Task_t *pTask ) { if ( this->m_pPrimaryBehavior && this->m_pPrimaryBehavior->BridgeRunTask( pTask ) ) return; BaseClass::RunTask( pTask ); } //------------------------------------- template inline CAI_Schedule *CAI_BehaviorHost::GetSchedule(int localScheduleID) { CAI_Schedule *pResult; if ( this->m_pPrimaryBehavior && this->m_pPrimaryBehavior->BridgeGetSchedule( localScheduleID, &pResult ) ) return pResult; return BaseClass::GetSchedule( localScheduleID ); } //------------------------------------- template inline const char *CAI_BehaviorHost::TaskName(int taskID) { const char *pszResult = NULL; if ( this->m_pPrimaryBehavior && this->m_pPrimaryBehavior->BridgeTaskName( taskID, &pszResult ) ) return pszResult; return BaseClass::TaskName( taskID ); } //------------------------------------- template inline void CAI_BehaviorHost::BuildScheduleTestBits() { // Iterate over behaviors and call BuildScheduleTestBitsNotActive() on each behavior // not currently active. for( int i = 0; i < this->m_Behaviors.Count(); i++ ) { if( this->m_Behaviors[i] != this->m_pPrimaryBehavior ) { this->m_Behaviors[i]->BuildScheduleTestBitsNotActive(); } } if ( this->m_pPrimaryBehavior ) this->m_pPrimaryBehavior->BuildScheduleTestBits(); BaseClass::BuildScheduleTestBits(); } //------------------------------------- template inline void CAI_BehaviorHost::OnChangeHintGroup( string_t oldGroup, string_t newGroup ) { for( int i = 0; i < this->m_Behaviors.Count(); i++ ) { this->m_Behaviors[i]->OnChangeHintGroup( oldGroup, newGroup ); } BaseClass::OnChangeHintGroup( oldGroup, newGroup ); } //------------------------------------- template inline float CAI_BehaviorHost::BehaviorBridge_GetJumpGravity() const { return BaseClass::GetJumpGravity(); } //------------------------------------- template inline bool CAI_BehaviorHost::BehaviorBridge_IsJumpLegal( const Vector &startPos, const Vector &apex, const Vector &endPos, float maxUp, float maxDown, float maxDist ) const { return BaseClass::IsJumpLegal( startPos, apex, endPos, maxUp, maxDown, maxDist ); } //------------------------------------- template inline bool CAI_BehaviorHost::BehaviorBridge_MovementCost( int moveType, const Vector &vecStart, const Vector &vecEnd, float *pCost ) { return BaseClass::MovementCost( moveType, vecStart, vecEnd, pCost ); } //------------------------------------- template inline void CAI_BehaviorHost::OnChangeActiveWeapon( CBaseCombatWeapon *pOldWeapon, CBaseCombatWeapon *pNewWeapon ) { for( int i = 0; i < this->m_Behaviors.Count(); i++ ) { this->m_Behaviors[i]->OnChangeActiveWeapon( pOldWeapon, pNewWeapon ); } BaseClass::OnChangeActiveWeapon( pOldWeapon, pNewWeapon ); } //------------------------------------- template inline void CAI_BehaviorHost::OnRestore() { for( int i = 0; i < this->m_Behaviors.Count(); i++ ) { this->m_Behaviors[i]->OnRestore(); } BaseClass::OnRestore(); } //------------------------------------- template inline void CAI_BehaviorHost::Precache() { BaseClass::Precache(); for( int i = 0; i < this->m_Behaviors.Count(); i++ ) { this->m_Behaviors[i]->Precache(); } } //------------------------------------- template inline void CAI_BehaviorHost::UpdateOnRemove() { for( int i = 0; i < this->m_Behaviors.Count(); i++ ) { this->m_Behaviors[i]->UpdateOnRemove(); } BaseClass::UpdateOnRemove(); } //------------------------------------- template inline void CAI_BehaviorHost::Event_Killed( const CTakeDamageInfo &info ) { for( int i = 0; i < this->m_Behaviors.Count(); i++ ) { this->m_Behaviors[i]->Event_Killed( info ); } BaseClass::Event_Killed( info ); } //----------------------------------------------------------------------------- template inline float CAI_BehaviorHost::GetJumpGravity() const { // @HACKHACK float base = BaseClass::GetJumpGravity(); for( int i = 0; i < this->m_Behaviors.Count(); i++ ) { float current = this->m_Behaviors[i]->GetJumpGravity(); if ( current != base ) { return current; } } return BaseClass::GetJumpGravity(); } //------------------------------------- template inline bool CAI_BehaviorHost::IsJumpLegal( const Vector &startPos, const Vector &apex, const Vector &endPos, float maxUp, float maxDown, float maxDist ) const { // @HACKHACK bool base = BaseClass::IsJumpLegal( startPos, apex, endPos, maxUp, maxDown, maxDist ); for( int i = 0; i < this->m_Behaviors.Count(); i++ ) { bool current = this->m_Behaviors[i]->IsJumpLegal( startPos, apex, endPos, maxUp, maxDown, maxDist ); if ( current != base ) { return current; } } return base; } template inline bool CAI_BehaviorHost::MovementCost( int moveType, const Vector &vecStart, const Vector &vecEnd, float *pCost ) { // @HACKHACK bool base = BaseClass::MovementCost( moveType, vecStart, vecEnd, pCost ); for( int i = 0; i < this->m_Behaviors.Count(); i++ ) { bool current = this->m_Behaviors[i]->MovementCost( moveType, vecStart, vecEnd, pCost ); if ( current != base ) { return current; } } return base; } //------------------------------------- template inline int CAI_BehaviorHost::Save( ISave &save ) { int result = BaseClass::Save( save ); if ( result ) CAI_BehaviorBase::SaveBehaviors( save, this->m_pPrimaryBehavior, this->AccessBehaviors(), this->NumBehaviors() ); return result; } //------------------------------------- template inline int CAI_BehaviorHost::Restore( IRestore &restore ) { int result = BaseClass::Restore( restore ); if ( result ) { int iCurrent = CAI_BehaviorBase::RestoreBehaviors( restore, this->AccessBehaviors(), this->NumBehaviors() ); if ( iCurrent != -1 ) this->m_pPrimaryBehavior = this->AccessBehaviors()[iCurrent]; else this->m_pPrimaryBehavior = NULL; } return result; } //----------------------------------------------------------------------------- #endif // AI_BEHAVIOR_H