You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
537 lines
14 KiB
537 lines
14 KiB
// 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(); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|