// NextBotInterface.h // Interface for NextBot // Author: Michael Booth, May 2006 // Copyright (c) 2006 Turtle Rock Studios, Inc. - All Rights Reserved #ifndef _NEXT_BOT_INTERFACE_H_ #define _NEXT_BOT_INTERFACE_H_ #include "NextBot/NextBotKnownEntity.h" #include "NextBotComponentInterface.h" #include "NextBotLocomotionInterface.h" #include "NextBotBodyInterface.h" #include "NextBotIntentionInterface.h" #include "NextBotVisionInterface.h" #include "NextBotDebug.h" class CBaseCombatCharacter; class PathFollower; //---------------------------------------------------------------------------------------------------------------- /** * A general purpose filter interface for various bot systems */ class INextBotFilter { public: virtual bool IsSelected( const CBaseEntity *candidate ) const = 0; // return true if this entity passes the filter }; //---------------------------------------------------------------------------------------------------------------- class INextBot : public INextBotEventResponder { public: INextBot( void ); virtual ~INextBot(); int GetBotId() const; bool BeginUpdate(); void EndUpdate(); virtual void Reset( void ); // (EXTEND) reset to initial state virtual void Update( void ); // (EXTEND) update internal state virtual void Upkeep( void ); // (EXTEND) lightweight update guaranteed to occur every server tick void FlagForUpdate( bool b = true ); bool IsFlaggedForUpdate(); int GetTickLastUpdate() const; void SetTickLastUpdate( int ); virtual bool IsRemovedOnReset( void ) const { return true; } // remove this bot when the NextBot manager calls Reset virtual CBaseCombatCharacter *GetEntity( void ) const = 0; virtual class NextBotCombatCharacter *GetNextBotCombatCharacter( void ) const { return NULL; } #ifdef TERROR virtual class SurvivorBot *MySurvivorBotPointer() const { return NULL; } #endif // interfaces are never NULL - return base no-op interfaces at a minimum virtual ILocomotion * GetLocomotionInterface( void ) const; virtual IBody * GetBodyInterface( void ) const; virtual IIntention * GetIntentionInterface( void ) const; virtual IVision * GetVisionInterface( void ) const; /** * Attempt to change the bot's position. Return true if successful. */ virtual bool SetPosition( const Vector &pos ); virtual const Vector &GetPosition( void ) const; // get the global position of the bot /** * Friend/enemy/neutral queries */ virtual bool IsEnemy( const CBaseEntity *them ) const; // return true if given entity is our enemy virtual bool IsFriend( const CBaseEntity *them ) const; // return true if given entity is our friend virtual bool IsSelf( const CBaseEntity *them ) const; // return true if 'them' is actually me /** * Can we climb onto this entity? */ virtual bool IsAbleToClimbOnto( const CBaseEntity *object ) const; /** * Can we break this entity? */ virtual bool IsAbleToBreak( const CBaseEntity *object ) const; /** * Sometimes we want to pass through other NextBots. OnContact() will always * be invoked, but collision resolution can be skipped if this * method returns false. */ virtual bool IsAbleToBlockMovementOf( const INextBot *botInMotion ) const { return true; } /** * Should we ever care about noticing physical contact with this entity? */ virtual bool ShouldTouch( const CBaseEntity *object ) const { return true; } /** * This immobile system is used to track the global state of "am I actually moving or not". * The OnStuck() event is only emitted when following a path, and paths can be recomputed, etc. */ virtual bool IsImmobile( void ) const; // return true if we haven't moved in awhile virtual float GetImmobileDuration( void ) const; // how long have we been immobile virtual void ClearImmobileStatus( void ); virtual float GetImmobileSpeedThreshold( void ) const; // return units/second below which this actor is considered "immobile" /** * Get the last PathFollower we followed. This method gives other interfaces a * single accessor to the most recent Path being followed by the myriad of * different PathFollowers used in the various behaviors the bot may be doing. */ virtual const PathFollower *GetCurrentPath( void ) const; virtual void SetCurrentPath( const PathFollower *path ); virtual void NotifyPathDestruction( const PathFollower *path ); // this PathFollower is going away, which may or may not be ours // between distance utility methods virtual bool IsRangeLessThan( CBaseEntity *subject, float range ) const; virtual bool IsRangeLessThan( const Vector &pos, float range ) const; virtual bool IsRangeGreaterThan( CBaseEntity *subject, float range ) const; virtual bool IsRangeGreaterThan( const Vector &pos, float range ) const; virtual float GetRangeTo( CBaseEntity *subject ) const; virtual float GetRangeTo( const Vector &pos ) const; virtual float GetRangeSquaredTo( CBaseEntity *subject ) const; virtual float GetRangeSquaredTo( const Vector &pos ) const; // event propagation virtual INextBotEventResponder *FirstContainedResponder( void ) const; virtual INextBotEventResponder *NextContainedResponder( INextBotEventResponder *current ) const; virtual bool IsDebugging( unsigned int type ) const; // return true if this bot is debugging any of the given types virtual const char *GetDebugIdentifier( void ) const; // return the name of this bot for debugging purposes virtual bool IsDebugFilterMatch( const char *name ) const; // return true if we match the given debug symbol virtual void DisplayDebugText( const char *text ) const; // show a line of text on the bot in the world void DebugConColorMsg( NextBotDebugType debugType, const Color &color, const char *fmt, ... ); enum { MAX_NEXTBOT_DEBUG_HISTORY = 100, MAX_NEXTBOT_DEBUG_LINE_LENGTH = 256, }; struct NextBotDebugLineType { NextBotDebugType debugType; char data[ MAX_NEXTBOT_DEBUG_LINE_LENGTH ]; }; void GetDebugHistory( unsigned int type, CUtlVector< const NextBotDebugLineType * > *lines ) const; // build a vector of debug history of the given types //------------------------------------------------------------------------------ private: friend class INextBotComponent; void RegisterComponent( INextBotComponent *comp ); // components call this to register themselves with the bot that contains them INextBotComponent *m_componentList; // the first component const PathFollower *m_currentPath; // the path we most recently followed int m_id; bool m_bFlaggedForUpdate; int m_tickLastUpdate; unsigned int m_debugType; mutable int m_debugDisplayLine; Vector m_immobileAnchor; CountdownTimer m_immobileCheckTimer; IntervalTimer m_immobileTimer; void UpdateImmobileStatus( void ); mutable ILocomotion *m_baseLocomotion; mutable IBody *m_baseBody; mutable IIntention *m_baseIntention; mutable IVision *m_baseVision; //mutable IAttention *m_baseAttention; // Debugging info void ResetDebugHistory( void ); CUtlVector< NextBotDebugLineType * > m_debugHistory; }; inline const PathFollower *INextBot::GetCurrentPath( void ) const { return m_currentPath; } inline void INextBot::SetCurrentPath( const PathFollower *path ) { m_currentPath = path; } inline void INextBot::NotifyPathDestruction( const PathFollower *path ) { if ( m_currentPath == path ) m_currentPath = NULL; } inline ILocomotion *INextBot::GetLocomotionInterface( void ) const { // these base interfaces are lazy-allocated (instead of being fully instanced classes) for two reasons: // 1) so the memory is only used if needed // 2) so the component is registered properly if ( m_baseLocomotion == NULL ) { m_baseLocomotion = new ILocomotion( const_cast< INextBot * >( this ) ); } return m_baseLocomotion; } inline IBody *INextBot::GetBodyInterface( void ) const { if ( m_baseBody == NULL ) { m_baseBody = new IBody( const_cast< INextBot * >( this ) ); } return m_baseBody; } inline IIntention *INextBot::GetIntentionInterface( void ) const { if ( m_baseIntention == NULL ) { m_baseIntention = new IIntention( const_cast< INextBot * >( this ) ); } return m_baseIntention; } inline IVision *INextBot::GetVisionInterface( void ) const { if ( m_baseVision == NULL ) { m_baseVision = new IVision( const_cast< INextBot * >( this ) ); } return m_baseVision; } inline int INextBot::GetBotId() const { return m_id; } inline void INextBot::FlagForUpdate( bool b ) { m_bFlaggedForUpdate = b; } inline bool INextBot::IsFlaggedForUpdate() { return m_bFlaggedForUpdate; } inline int INextBot::GetTickLastUpdate() const { return m_tickLastUpdate; } inline void INextBot::SetTickLastUpdate( int tick ) { m_tickLastUpdate = tick; } inline bool INextBot::IsImmobile( void ) const { return m_immobileTimer.HasStarted(); } inline float INextBot::GetImmobileDuration( void ) const { return m_immobileTimer.GetElapsedTime(); } inline void INextBot::ClearImmobileStatus( void ) { m_immobileTimer.Invalidate(); m_immobileAnchor = GetEntity()->GetAbsOrigin(); } inline float INextBot::GetImmobileSpeedThreshold( void ) const { return 30.0f; } inline INextBotEventResponder *INextBot::FirstContainedResponder( void ) const { return m_componentList; } inline INextBotEventResponder *INextBot::NextContainedResponder( INextBotEventResponder *current ) const { return static_cast< INextBotComponent * >( current )->m_nextComponent; } #endif // _NEXT_BOT_INTERFACE_H_