summaryrefslogtreecommitdiff
path: root/game/server/NextBot/NextBotInterface.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/server/NextBot/NextBotInterface.cpp')
-rw-r--r--game/server/NextBot/NextBotInterface.cpp537
1 files changed, 537 insertions, 0 deletions
diff --git a/game/server/NextBot/NextBotInterface.cpp b/game/server/NextBot/NextBotInterface.cpp
new file mode 100644
index 0000000..f802e10
--- /dev/null
+++ b/game/server/NextBot/NextBotInterface.cpp
@@ -0,0 +1,537 @@
+// NextBotInterface.cpp
+// Implentation of system methods for NextBot interface
+// Author: Michael Booth, May 2006
+//========= Copyright Valve Corporation, All rights reserved. ============//
+
+#include "cbase.h"
+
+#include "props.h"
+#include "fmtstr.h"
+#include "team.h"
+
+#include "NextBotInterface.h"
+#include "NextBotBodyInterface.h"
+#include "NextBotManager.h"
+
+#include "tier0/vprof.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+// development only, off by default for 360
+ConVar NextBotDebugHistory( "nb_debug_history", IsX360() ? "0" : "1", FCVAR_CHEAT, "If true, each bot keeps a history of debug output in memory" );
+
+//----------------------------------------------------------------------------------------------------------------
+INextBot::INextBot( void ) : m_debugHistory( MAX_NEXTBOT_DEBUG_HISTORY, 0 ) // CUtlVector: grow to max length, alloc 0 initially
+{
+ m_tickLastUpdate = -999;
+ m_id = -1;
+ m_componentList = NULL;
+ m_debugDisplayLine = 0;
+
+ m_immobileTimer.Invalidate();
+ m_immobileCheckTimer.Invalidate();
+ m_immobileAnchor = vec3_origin;
+
+ m_currentPath = NULL;
+
+ // register with the manager
+ m_id = TheNextBots().Register( this );
+}
+
+
+//----------------------------------------------------------------------------------------------------------------
+INextBot::~INextBot()
+{
+ ResetDebugHistory();
+
+ // tell the manager we're gone
+ TheNextBots().UnRegister( this );
+
+ // delete Intention first, since destruction of Actions may access other components
+ if ( m_baseIntention )
+ delete m_baseIntention;
+
+ if ( m_baseLocomotion )
+ delete m_baseLocomotion;
+
+ if ( m_baseBody )
+ delete m_baseBody;
+
+ if ( m_baseVision )
+ delete m_baseVision;
+}
+
+
+//----------------------------------------------------------------------------------------------------------------
+void INextBot::Reset( void )
+{
+ m_tickLastUpdate = -999;
+ m_debugType = 0;
+ m_debugDisplayLine = 0;
+
+ m_immobileTimer.Invalidate();
+ m_immobileCheckTimer.Invalidate();
+ m_immobileAnchor = vec3_origin;
+
+ for( INextBotComponent *comp = m_componentList; comp; comp = comp->m_nextComponent )
+ {
+ comp->Reset();
+ }
+}
+
+
+//----------------------------------------------------------------------------------------------------------------
+void INextBot::ResetDebugHistory( void )
+{
+ for ( int i=0; i<m_debugHistory.Count(); ++i )
+ {
+ delete m_debugHistory[i];
+ }
+
+ m_debugHistory.RemoveAll();
+}
+
+
+//----------------------------------------------------------------------------------------------------------------
+bool INextBot::BeginUpdate()
+{
+ if ( TheNextBots().ShouldUpdate( this ) )
+ {
+ TheNextBots().NotifyBeginUpdate( this );
+ return true;
+ }
+ return false;
+}
+
+//----------------------------------------------------------------------------------------------------------------
+void INextBot::EndUpdate( void )
+{
+ TheNextBots().NotifyEndUpdate( this );
+}
+
+//----------------------------------------------------------------------------------------------------------------
+void INextBot::Update( void )
+{
+ VPROF_BUDGET( "INextBot::Update", "NextBot" );
+
+ m_debugDisplayLine = 0;
+
+ if ( IsDebugging( NEXTBOT_DEBUG_ALL ) )
+ {
+ CFmtStr msg;
+ DisplayDebugText( msg.sprintf( "#%d", GetEntity()->entindex() ) );
+ }
+
+ UpdateImmobileStatus();
+
+ // update all components
+ for( INextBotComponent *comp = m_componentList; comp; comp = comp->m_nextComponent )
+ {
+ if ( comp->ComputeUpdateInterval() )
+ {
+ comp->Update();
+ }
+ }
+}
+
+
+//----------------------------------------------------------------------------------------------------------------
+void INextBot::Upkeep( void )
+{
+ VPROF_BUDGET( "INextBot::Upkeep", "NextBot" );
+
+ // do upkeep for all components
+ for( INextBotComponent *comp = m_componentList; comp; comp = comp->m_nextComponent )
+ {
+ comp->Upkeep();
+ }
+}
+
+
+//----------------------------------------------------------------------------------------------------------------
+bool INextBot::SetPosition( const Vector &pos )
+{
+ IBody *body = GetBodyInterface();
+ if (body)
+ {
+ return body->SetPosition( pos );
+ }
+
+ // fall back to setting raw entity position
+ GetEntity()->SetAbsOrigin( pos );
+ return true;
+}
+
+
+//----------------------------------------------------------------------------------------------------------------
+const Vector &INextBot::GetPosition( void ) const
+{
+ return const_cast< INextBot * >( this )->GetEntity()->GetAbsOrigin();
+}
+
+
+//----------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if given actor is our enemy
+ */
+bool INextBot::IsEnemy( const CBaseEntity *them ) const
+{
+ if ( them == NULL )
+ return false;
+
+ // this is not strictly correct, as spectators are not enemies
+ return const_cast< INextBot * >( this )->GetEntity()->GetTeamNumber() != them->GetTeamNumber();
+}
+
+
+//----------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if given actor is our friend
+ */
+bool INextBot::IsFriend( const CBaseEntity *them ) const
+{
+ if ( them == NULL )
+ return false;
+
+ return const_cast< INextBot * >( this )->GetEntity()->GetTeamNumber() == them->GetTeamNumber();
+}
+
+
+//----------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if 'them' is actually me
+ */
+bool INextBot::IsSelf( const CBaseEntity *them ) const
+{
+ if ( them == NULL )
+ return false;
+
+ return const_cast< INextBot * >( this )->GetEntity()->entindex() == them->entindex();
+}
+
+
+//----------------------------------------------------------------------------------------------------------------
+/**
+ * Components call this to register themselves with the bot that contains them
+ */
+void INextBot::RegisterComponent( INextBotComponent *comp )
+{
+ // add to head of singly linked list
+ comp->m_nextComponent = m_componentList;
+ m_componentList = comp;
+}
+
+
+//----------------------------------------------------------------------------------------------------------------
+bool INextBot::IsRangeLessThan( CBaseEntity *subject, float range ) const
+{
+ Vector botPos;
+ CBaseEntity *bot = const_cast< INextBot * >( this )->GetEntity();
+ if ( !bot || !subject )
+ return 0.0f;
+
+ bot->CollisionProp()->CalcNearestPoint( subject->WorldSpaceCenter(), &botPos );
+ float computedRange = subject->CollisionProp()->CalcDistanceFromPoint( botPos );
+ return computedRange < range;
+}
+
+
+//----------------------------------------------------------------------------------------------------------------
+bool INextBot::IsRangeLessThan( const Vector &pos, float range ) const
+{
+ Vector to = pos - GetPosition();
+ return to.IsLengthLessThan( range );
+}
+
+
+//----------------------------------------------------------------------------------------------------------------
+bool INextBot::IsRangeGreaterThan( CBaseEntity *subject, float range ) const
+{
+ Vector botPos;
+ CBaseEntity *bot = const_cast< INextBot * >( this )->GetEntity();
+ if ( !bot || !subject )
+ return true;
+
+ bot->CollisionProp()->CalcNearestPoint( subject->WorldSpaceCenter(), &botPos );
+ float computedRange = subject->CollisionProp()->CalcDistanceFromPoint( botPos );
+ return computedRange > range;
+}
+
+
+//----------------------------------------------------------------------------------------------------------------
+bool INextBot::IsRangeGreaterThan( const Vector &pos, float range ) const
+{
+ Vector to = pos - GetPosition();
+ return to.IsLengthGreaterThan( range );
+}
+
+
+//----------------------------------------------------------------------------------------------------------------
+float INextBot::GetRangeTo( CBaseEntity *subject ) const
+{
+ Vector botPos;
+ CBaseEntity *bot = const_cast< INextBot * >( this )->GetEntity();
+ if ( !bot || !subject )
+ return 0.0f;
+
+ bot->CollisionProp()->CalcNearestPoint( subject->WorldSpaceCenter(), &botPos );
+ float computedRange = subject->CollisionProp()->CalcDistanceFromPoint( botPos );
+ return computedRange;
+}
+
+
+//----------------------------------------------------------------------------------------------------------------
+float INextBot::GetRangeTo( const Vector &pos ) const
+{
+ Vector to = pos - GetPosition();
+ return to.Length();
+}
+
+
+//----------------------------------------------------------------------------------------------------------------
+float INextBot::GetRangeSquaredTo( CBaseEntity *subject ) const
+{
+ Vector botPos;
+ CBaseEntity *bot = const_cast< INextBot * >( this )->GetEntity();
+ if ( !bot || !subject )
+ return 0.0f;
+
+ bot->CollisionProp()->CalcNearestPoint( subject->WorldSpaceCenter(), &botPos );
+ float computedRange = subject->CollisionProp()->CalcDistanceFromPoint( botPos );
+ return computedRange * computedRange;
+}
+
+
+//----------------------------------------------------------------------------------------------------------------
+float INextBot::GetRangeSquaredTo( const Vector &pos ) const
+{
+ Vector to = pos - GetPosition();
+ return to.LengthSqr();
+}
+
+
+//----------------------------------------------------------------------------------------------------------------
+bool INextBot::IsDebugging( unsigned int type ) const
+{
+ if ( TheNextBots().IsDebugging( type ) )
+ {
+ return TheNextBots().IsDebugFilterMatch( this );
+ }
+
+ return false;
+}
+
+
+//----------------------------------------------------------------------------------------------------------------
+/**
+ * Return the name of this bot for debugging purposes
+ */
+const char *INextBot::GetDebugIdentifier( void ) const
+{
+ const int nameSize = 256;
+ static char name[ nameSize ];
+
+ Q_snprintf( name, nameSize, "%s(#%d)", const_cast< INextBot * >( this )->GetEntity()->GetClassname(), const_cast< INextBot * >( this )->GetEntity()->entindex() );
+
+ return name;
+}
+
+
+//----------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if we match the given debug symbol
+ */
+bool INextBot::IsDebugFilterMatch( const char *name ) const
+{
+ // compare debug identifier
+ if ( !Q_strnicmp( name, GetDebugIdentifier(), Q_strlen( name ) ) )
+ {
+ return true;
+ }
+
+ // compare team name
+ CTeam *team = GetEntity()->GetTeam();
+ if ( team && !Q_strnicmp( name, team->GetName(), Q_strlen( name ) ) )
+ {
+ return true;
+ }
+
+
+ return false;
+}
+
+
+//----------------------------------------------------------------------------------------------------------------
+/**
+ * There are some things we never want to climb on
+ */
+bool INextBot::IsAbleToClimbOnto( const CBaseEntity *object ) const
+{
+ if ( object == NULL || !const_cast<CBaseEntity *>(object)->IsAIWalkable() )
+ {
+ return false;
+ }
+
+ // never climb onto doors
+ if ( FClassnameIs( const_cast< CBaseEntity * >( object ), "prop_door*" ) || FClassnameIs( const_cast< CBaseEntity * >( object ), "func_door*" ) )
+ {
+ return false;
+ }
+
+ // ok to climb on this object
+ return true;
+}
+
+
+//----------------------------------------------------------------------------------------------------------------
+/**
+ * Can we break this object
+ */
+bool INextBot::IsAbleToBreak( const CBaseEntity *object ) const
+{
+ if ( object && object->m_takedamage == DAMAGE_YES )
+ {
+ if ( FClassnameIs( const_cast< CBaseEntity * >( object ), "func_breakable" ) &&
+ object->GetHealth() )
+ {
+ return true;
+ }
+
+ if ( FClassnameIs( const_cast< CBaseEntity * >( object ), "func_breakable_surf" ) )
+ {
+ return true;
+ }
+
+ if ( dynamic_cast< const CBreakableProp * >( object ) != NULL )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+//----------------------------------------------------------------------------------------------------------
+void INextBot::DisplayDebugText( const char *text ) const
+{
+ const_cast< INextBot * >( this )->GetEntity()->EntityText( m_debugDisplayLine++, text, 0.1 );
+}
+
+
+//--------------------------------------------------------------------------------------------------------
+void INextBot::DebugConColorMsg( NextBotDebugType debugType, const Color &color, const char *fmt, ... )
+{
+ bool isDataFormatted = false;
+
+ va_list argptr;
+ char data[ MAX_NEXTBOT_DEBUG_LINE_LENGTH ];
+
+ if ( developer.GetBool() && IsDebugging( debugType ) )
+ {
+ va_start(argptr, fmt);
+ Q_vsnprintf(data, sizeof( data ), fmt, argptr);
+ va_end(argptr);
+ isDataFormatted = true;
+
+ ConColorMsg( color, "%s", data );
+ }
+
+ if ( !NextBotDebugHistory.GetBool() )
+ {
+ if ( m_debugHistory.Count() )
+ {
+ ResetDebugHistory();
+ }
+ return;
+ }
+
+ // Don't bother with event data - it's spammy enough to overshadow everything else.
+ if ( debugType == NEXTBOT_EVENTS )
+ return;
+
+ if ( !isDataFormatted )
+ {
+ va_start(argptr, fmt);
+ Q_vsnprintf(data, sizeof( data ), fmt, argptr);
+ va_end(argptr);
+ isDataFormatted = true;
+ }
+
+ int lastLine = m_debugHistory.Count() - 1;
+ if ( lastLine >= 0 )
+ {
+ NextBotDebugLineType *line = m_debugHistory[lastLine];
+ if ( line->debugType == debugType && V_strstr( line->data, "\n" ) == NULL )
+ {
+ // append onto previous line
+ V_strncat( line->data, data, MAX_NEXTBOT_DEBUG_LINE_LENGTH );
+ return;
+ }
+ }
+
+ // Prune out an old line if needed, keeping a pointer to re-use the memory
+ NextBotDebugLineType *line = NULL;
+ if ( m_debugHistory.Count() == MAX_NEXTBOT_DEBUG_HISTORY )
+ {
+ line = m_debugHistory[0];
+ m_debugHistory.Remove( 0 );
+ }
+
+ // Add to debug history
+ if ( !line )
+ {
+ line = new NextBotDebugLineType;
+ }
+ line->debugType = debugType;
+ V_strncpy( line->data, data, MAX_NEXTBOT_DEBUG_LINE_LENGTH );
+ m_debugHistory.AddToTail( line );
+}
+
+
+//--------------------------------------------------------------------------------------------------------
+// build a vector of debug history of the given types
+void INextBot::GetDebugHistory( unsigned int type, CUtlVector< const NextBotDebugLineType * > *lines ) const
+{
+ if ( !lines )
+ return;
+
+ lines->RemoveAll();
+
+ for ( int i=0; i<m_debugHistory.Count(); ++i )
+ {
+ NextBotDebugLineType *line = m_debugHistory[i];
+ if ( line->debugType & type )
+ {
+ lines->AddToTail( line );
+ }
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------
+void INextBot::UpdateImmobileStatus( void )
+{
+ if ( m_immobileCheckTimer.IsElapsed() )
+ {
+ m_immobileCheckTimer.Start( 1.0f );
+
+ // if we haven't moved farther than this in 1 second, we're immobile
+ if ( ( GetEntity()->GetAbsOrigin() - m_immobileAnchor ).IsLengthGreaterThan( GetImmobileSpeedThreshold() ) )
+ {
+ // moved far enough, not immobile
+ m_immobileAnchor = GetEntity()->GetAbsOrigin();
+ m_immobileTimer.Invalidate();
+ }
+ else
+ {
+ // haven't escaped our anchor - we are immobile
+ if ( !m_immobileTimer.HasStarted() )
+ {
+ m_immobileTimer.Start();
+ }
+ }
+ }
+}
+