//========= 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 <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 > *GetBehaviorClasses();
private:
	static CGenericClassmap< CAI_BehaviorBase > *GetBehaviorClassesInternal();

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::GetBehaviorClasses()->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::GetBehaviorClasses()->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