|
|
//========= Copyright <EFBFBD> 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 <typeinfo> |
|
|
#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<AIChannelScheduleState_t> 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 NPC_CLASS = CAI_BaseNPC, const int ID_SPACE_OFFSET = 100000> |
|
|
class CAI_Behavior : public CAI_ComponentWithOuter<NPC_CLASS, CAI_BehaviorBase> |
|
|
{ |
|
|
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<NPC_CLASS, CAI_BehaviorBase>(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 BASE_NPC> |
|
|
class CAI_BehaviorHostBase : public BASE_NPC |
|
|
{ |
|
|
DECLARE_CLASS( CAI_BehaviorHostBase, BASE_NPC ); |
|
|
|
|
|
protected: |
|
|
CAI_BehaviorHostBase() |
|
|
{ |
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
template <class BASE_NPC> |
|
|
class CAI_BehaviorHost : public CAI_BehaviorHostBase<BASE_NPC> |
|
|
{ |
|
|
DECLARE_CLASS( CAI_BehaviorHost, CAI_BehaviorHostBase<BASE_NPC> ); |
|
|
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 <class BASE_NPC> |
|
|
inline void CAI_BehaviorHost<BASE_NPC>::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 <class BASE_NPC> |
|
|
inline void CAI_BehaviorHost<BASE_NPC>::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 <class BASE_NPC> |
|
|
inline void CAI_BehaviorHost<BASE_NPC>::BehaviorBridge_GatherConditions() |
|
|
{ |
|
|
if ( g_bBehaviorHost_PreventBaseClassGatherConditions ) |
|
|
return; |
|
|
|
|
|
BaseClass::GatherConditions(); |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
template <class BASE_NPC> |
|
|
inline void CAI_BehaviorHost<BASE_NPC>::OnStartSchedule( int scheduleType ) |
|
|
{ |
|
|
if ( this->m_pPrimaryBehavior ) |
|
|
this->m_pPrimaryBehavior->BridgeOnStartSchedule( scheduleType ); |
|
|
BaseClass::OnStartSchedule( scheduleType ); |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
template <class BASE_NPC> |
|
|
inline int CAI_BehaviorHost<BASE_NPC>::BehaviorBridge_SelectSchedule() |
|
|
{ |
|
|
return BaseClass::SelectSchedule(); |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
template <class BASE_NPC> |
|
|
inline CAI_Schedule *CAI_BehaviorHost<BASE_NPC>::GetNewSchedule() |
|
|
{ |
|
|
m_bCalledBehaviorSelectSchedule = false; |
|
|
CAI_Schedule *pResult = BaseClass::GetNewSchedule(); |
|
|
if ( !m_bCalledBehaviorSelectSchedule && this->m_pPrimaryBehavior ) |
|
|
this->DeferSchedulingToBehavior( NULL ); |
|
|
return pResult; |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
template <class BASE_NPC> |
|
|
inline CAI_Schedule *CAI_BehaviorHost<BASE_NPC>::GetFailSchedule() |
|
|
{ |
|
|
m_bCalledBehaviorSelectSchedule = false; |
|
|
CAI_Schedule *pResult = BaseClass::GetFailSchedule(); |
|
|
if ( !m_bCalledBehaviorSelectSchedule && this->m_pPrimaryBehavior ) |
|
|
this->DeferSchedulingToBehavior( NULL ); |
|
|
return pResult; |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
template <class BASE_NPC> |
|
|
inline int CAI_BehaviorHost<BASE_NPC>::BehaviorBridge_TranslateSchedule( int scheduleType ) |
|
|
{ |
|
|
return BaseClass::TranslateSchedule( scheduleType ); |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
template <class BASE_NPC> |
|
|
inline int CAI_BehaviorHost<BASE_NPC>::TranslateSchedule( int scheduleType ) |
|
|
{ |
|
|
if ( this->m_pPrimaryBehavior ) |
|
|
{ |
|
|
return this->m_pPrimaryBehavior->BridgeTranslateSchedule( scheduleType ); |
|
|
} |
|
|
return BaseClass::TranslateSchedule( scheduleType ); |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
template <class BASE_NPC> |
|
|
inline int CAI_BehaviorHost<BASE_NPC>::SelectSchedule() |
|
|
{ |
|
|
m_bCalledBehaviorSelectSchedule = true; |
|
|
if ( this->m_pPrimaryBehavior ) |
|
|
{ |
|
|
return this->m_pPrimaryBehavior->BridgeSelectSchedule(); |
|
|
} |
|
|
|
|
|
return BaseClass::SelectSchedule(); |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
template <class BASE_NPC> |
|
|
inline void CAI_BehaviorHost<BASE_NPC>::KeepRunningBehavior() |
|
|
{ |
|
|
if ( this->m_pPrimaryBehavior ) |
|
|
m_bCalledBehaviorSelectSchedule = true; |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
template <class BASE_NPC> |
|
|
inline int CAI_BehaviorHost<BASE_NPC>::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 <class BASE_NPC> |
|
|
inline void CAI_BehaviorHost<BASE_NPC>::StartTask( const Task_t *pTask ) |
|
|
{ |
|
|
if ( this->m_pPrimaryBehavior && this->m_pPrimaryBehavior->BridgeStartTask( pTask ) ) |
|
|
return; |
|
|
BaseClass::StartTask( pTask ); |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
template <class BASE_NPC> |
|
|
inline void CAI_BehaviorHost<BASE_NPC>::RunTask( const Task_t *pTask ) |
|
|
{ |
|
|
if ( this->m_pPrimaryBehavior && this->m_pPrimaryBehavior->BridgeRunTask( pTask ) ) |
|
|
return; |
|
|
BaseClass::RunTask( pTask ); |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
template <class BASE_NPC> |
|
|
inline CAI_Schedule *CAI_BehaviorHost<BASE_NPC>::GetSchedule(int localScheduleID) |
|
|
{ |
|
|
CAI_Schedule *pResult; |
|
|
if ( this->m_pPrimaryBehavior && this->m_pPrimaryBehavior->BridgeGetSchedule( localScheduleID, &pResult ) ) |
|
|
return pResult; |
|
|
return BaseClass::GetSchedule( localScheduleID ); |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
template <class BASE_NPC> |
|
|
inline const char *CAI_BehaviorHost<BASE_NPC>::TaskName(int taskID) |
|
|
{ |
|
|
const char *pszResult = NULL; |
|
|
if ( this->m_pPrimaryBehavior && this->m_pPrimaryBehavior->BridgeTaskName( taskID, &pszResult ) ) |
|
|
return pszResult; |
|
|
return BaseClass::TaskName( taskID ); |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
template <class BASE_NPC> |
|
|
inline void CAI_BehaviorHost<BASE_NPC>::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 <class BASE_NPC> |
|
|
inline void CAI_BehaviorHost<BASE_NPC>::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 <class BASE_NPC> |
|
|
inline float CAI_BehaviorHost<BASE_NPC>::BehaviorBridge_GetJumpGravity() const |
|
|
{ |
|
|
return BaseClass::GetJumpGravity(); |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
template <class BASE_NPC> |
|
|
inline bool CAI_BehaviorHost<BASE_NPC>::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 <class BASE_NPC> |
|
|
inline bool CAI_BehaviorHost<BASE_NPC>::BehaviorBridge_MovementCost( int moveType, const Vector &vecStart, const Vector &vecEnd, float *pCost ) |
|
|
{ |
|
|
return BaseClass::MovementCost( moveType, vecStart, vecEnd, pCost ); |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
template <class BASE_NPC> |
|
|
inline void CAI_BehaviorHost<BASE_NPC>::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 <class BASE_NPC> |
|
|
inline void CAI_BehaviorHost<BASE_NPC>::OnRestore() |
|
|
{ |
|
|
for( int i = 0; i < this->m_Behaviors.Count(); i++ ) |
|
|
{ |
|
|
this->m_Behaviors[i]->OnRestore(); |
|
|
} |
|
|
BaseClass::OnRestore(); |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
template <class BASE_NPC> |
|
|
inline void CAI_BehaviorHost<BASE_NPC>::Precache() |
|
|
{ |
|
|
BaseClass::Precache(); |
|
|
for( int i = 0; i < this->m_Behaviors.Count(); i++ ) |
|
|
{ |
|
|
this->m_Behaviors[i]->Precache(); |
|
|
} |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
template <class BASE_NPC> |
|
|
inline void CAI_BehaviorHost<BASE_NPC>::UpdateOnRemove() |
|
|
{ |
|
|
for( int i = 0; i < this->m_Behaviors.Count(); i++ ) |
|
|
{ |
|
|
this->m_Behaviors[i]->UpdateOnRemove(); |
|
|
} |
|
|
BaseClass::UpdateOnRemove(); |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
template <class BASE_NPC> |
|
|
inline void CAI_BehaviorHost<BASE_NPC>::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 <class BASE_NPC> |
|
|
inline float CAI_BehaviorHost<BASE_NPC>::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 <class BASE_NPC> |
|
|
inline bool CAI_BehaviorHost<BASE_NPC>::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 <class BASE_NPC> |
|
|
inline bool CAI_BehaviorHost<BASE_NPC>::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 <class BASE_NPC> |
|
|
inline int CAI_BehaviorHost<BASE_NPC>::Save( ISave &save ) |
|
|
{ |
|
|
int result = BaseClass::Save( save ); |
|
|
if ( result ) |
|
|
CAI_BehaviorBase::SaveBehaviors( save, this->m_pPrimaryBehavior, this->AccessBehaviors(), this->NumBehaviors() ); |
|
|
return result; |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
template <class BASE_NPC> |
|
|
inline int CAI_BehaviorHost<BASE_NPC>::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
|
|
|
|