|
|
// NextBotInterface.cpp
// Implentation of system methods for NextBot interface
// Author: Michael Booth, May 2006
// Copyright (c) 2006 Turtle Rock Studios, Inc. - 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 );
if ( m_baseLocomotion ) delete m_baseLocomotion;
if ( m_baseBody ) delete m_baseBody;
if ( m_baseIntention ) delete m_baseIntention;
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 ) { // 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 0.0f;
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(); } } } }
|