mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-14 04:31:05 +00:00
1
This commit is contained in:
@@ -0,0 +1,470 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_generator.cpp
|
||||
// Entity to spawn a collection of TFBots
|
||||
// Michael Booth, September 2009
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "tf_bot_generator.h"
|
||||
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/tf_bot_manager.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "tier3/tier3.h"
|
||||
#include "vgui/ILocalize.h"
|
||||
|
||||
extern ConVar tf_bot_prefix_name_with_difficulty;
|
||||
extern ConVar tf_bot_difficulty;
|
||||
|
||||
extern void CreateBotName( int iTeam, int iClassIndex, CTFBot::DifficultyType skill, char* pBuffer, int iBufferSize );
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
BEGIN_DATADESC( CTFBotGenerator )
|
||||
DEFINE_KEYFIELD( m_spawnCount, FIELD_INTEGER, "count" ),
|
||||
DEFINE_KEYFIELD( m_maxActiveCount, FIELD_INTEGER, "maxActive" ),
|
||||
DEFINE_KEYFIELD( m_spawnInterval, FIELD_FLOAT, "interval" ),
|
||||
DEFINE_KEYFIELD( m_className, FIELD_STRING, "class" ),
|
||||
DEFINE_KEYFIELD( m_teamName, FIELD_STRING, "team" ),
|
||||
DEFINE_KEYFIELD( m_actionPointName, FIELD_STRING, "action_point" ),
|
||||
DEFINE_KEYFIELD( m_initialCommand, FIELD_STRING, "initial_command" ),
|
||||
DEFINE_KEYFIELD( m_bSuppressFire, FIELD_BOOLEAN, "suppressFire" ),
|
||||
DEFINE_KEYFIELD( m_bDisableDodge, FIELD_BOOLEAN, "disableDodge" ),
|
||||
DEFINE_KEYFIELD( m_iOnDeathAction, FIELD_INTEGER, "actionOnDeath" ),
|
||||
DEFINE_KEYFIELD( m_bUseTeamSpawnpoint, FIELD_BOOLEAN, "useTeamSpawnPoint" ),
|
||||
DEFINE_KEYFIELD( m_difficulty, FIELD_INTEGER, "difficulty" ),
|
||||
DEFINE_KEYFIELD( m_bRetainBuildings, FIELD_BOOLEAN, "retainBuildings" ),
|
||||
DEFINE_KEYFIELD( m_bSpawnOnlyWhenTriggered, FIELD_BOOLEAN, "spawnOnlyWhenTriggered" ),
|
||||
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
|
||||
DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetSuppressFire", InputSetSuppressFire ),
|
||||
DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetDisableDodge", InputSetDisableDodge ),
|
||||
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetDifficulty", InputSetDifficulty ),
|
||||
DEFINE_INPUTFUNC( FIELD_STRING, "CommandGotoActionPoint", InputCommandGotoActionPoint ),
|
||||
|
||||
DEFINE_INPUTFUNC( FIELD_STRING, "SetAttentionFocus", InputSetAttentionFocus ),
|
||||
DEFINE_INPUTFUNC( FIELD_STRING, "ClearAttentionFocus", InputClearAttentionFocus ),
|
||||
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "SpawnBot", InputSpawnBot ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "RemoveBots", InputRemoveBots ),
|
||||
|
||||
DEFINE_OUTPUT( m_onSpawned, "OnSpawned" ),
|
||||
DEFINE_OUTPUT( m_onExpended, "OnExpended" ),
|
||||
DEFINE_OUTPUT( m_onBotKilled, "OnBotKilled" ),
|
||||
|
||||
DEFINE_THINKFUNC( GeneratorThink ),
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( bot_generator, CTFBotGenerator );
|
||||
|
||||
enum
|
||||
{
|
||||
kOnDeath_Respawn,
|
||||
kOnDeath_RemoveSelf,
|
||||
kOnDeath_MoveToSpectatorTeam,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
CTFBotGenerator::CTFBotGenerator( void )
|
||||
: m_bBotChoosesClass(false)
|
||||
, m_bSuppressFire(false)
|
||||
, m_bDisableDodge(false)
|
||||
, m_bUseTeamSpawnpoint(false)
|
||||
, m_bRetainBuildings(false)
|
||||
, m_bExpended(false)
|
||||
, m_iOnDeathAction(kOnDeath_RemoveSelf)
|
||||
, m_difficulty(CTFBot::UNDEFINED)
|
||||
, m_spawnCountRemaining(0)
|
||||
, m_bSpawnOnlyWhenTriggered(false)
|
||||
, m_bEnabled(true)
|
||||
{
|
||||
SetThink( NULL );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFBotGenerator::InputEnable( inputdata_t &inputdata )
|
||||
{
|
||||
m_bEnabled = true;
|
||||
|
||||
if ( m_bExpended )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetThink( &CTFBotGenerator::GeneratorThink );
|
||||
|
||||
if ( m_spawnCountRemaining )
|
||||
{
|
||||
// already generating - don't restart count
|
||||
return;
|
||||
}
|
||||
SetNextThink( gpGlobals->curtime );
|
||||
m_spawnCountRemaining = m_spawnCount;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFBotGenerator::InputDisable( inputdata_t &inputdata )
|
||||
{
|
||||
m_bEnabled = false;
|
||||
|
||||
// just stop thinking
|
||||
SetThink( NULL );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFBotGenerator::InputSetSuppressFire( inputdata_t &inputdata )
|
||||
{
|
||||
m_bSuppressFire = inputdata.value.Bool();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFBotGenerator::InputSetDisableDodge( inputdata_t &inputdata )
|
||||
{
|
||||
m_bDisableDodge = inputdata.value.Bool();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFBotGenerator::InputSetDifficulty( inputdata_t &inputdata )
|
||||
{
|
||||
m_difficulty = clamp( inputdata.value.Int(), (int) CTFBot::UNDEFINED, (int) CTFBot::EXPERT );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFBotGenerator::InputCommandGotoActionPoint( inputdata_t &inputdata )
|
||||
{
|
||||
CTFBotActionPoint *pActionPoint = dynamic_cast<CTFBotActionPoint *>( gEntList.FindEntityByName( NULL, inputdata.value.String() ) );
|
||||
if ( pActionPoint == NULL )
|
||||
{
|
||||
return;
|
||||
}
|
||||
for ( int i = 0; i < m_spawnedBotVector.Count(); )
|
||||
{
|
||||
CHandle< CTFBot > hBot = m_spawnedBotVector[i];
|
||||
if ( hBot == NULL )
|
||||
{
|
||||
m_spawnedBotVector.FastRemove(i);
|
||||
continue;
|
||||
}
|
||||
if ( hBot->GetTeamNumber() == TEAM_SPECTATOR )
|
||||
{
|
||||
m_spawnedBotVector.FastRemove(i);
|
||||
continue;
|
||||
}
|
||||
hBot->SetActionPoint( pActionPoint );
|
||||
hBot->OnCommandString( "goto action point" );
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFBotGenerator::InputSetAttentionFocus( inputdata_t &inputdata )
|
||||
{
|
||||
CBaseEntity *focus = gEntList.FindEntityByName( NULL, inputdata.value.String() );
|
||||
|
||||
if ( focus == NULL )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for( int i = 0; i < m_spawnedBotVector.Count(); )
|
||||
{
|
||||
CTFBot *bot = m_spawnedBotVector[i];
|
||||
|
||||
if ( !bot || bot->GetTeamNumber() == TEAM_SPECTATOR )
|
||||
{
|
||||
m_spawnedBotVector.FastRemove(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
bot->SetAttentionFocus( focus );
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFBotGenerator::InputClearAttentionFocus( inputdata_t &inputdata )
|
||||
{
|
||||
for( int i = 0; i < m_spawnedBotVector.Count(); )
|
||||
{
|
||||
CTFBot *bot = m_spawnedBotVector[i];
|
||||
|
||||
if ( !bot || bot->GetTeamNumber() == TEAM_SPECTATOR )
|
||||
{
|
||||
m_spawnedBotVector.FastRemove(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
bot->ClearAttentionFocus();
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFBotGenerator::InputSpawnBot( inputdata_t &inputdata )
|
||||
{
|
||||
if ( m_bEnabled )
|
||||
{
|
||||
SpawnBot();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFBotGenerator::InputRemoveBots( inputdata_t &inputdata )
|
||||
{
|
||||
for( int i = 0; i < m_spawnedBotVector.Count(); i++ )
|
||||
{
|
||||
CTFBot *pBot = m_spawnedBotVector[i];
|
||||
if ( pBot )
|
||||
{
|
||||
pBot->Remove();
|
||||
engine->ServerCommand( UTIL_VarArgs( "kickid %d\n", pBot->GetUserID() ) );
|
||||
}
|
||||
|
||||
m_spawnedBotVector.FastRemove(i);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFBotGenerator::OnBotKilled( CTFBot *pBot )
|
||||
{
|
||||
m_onBotKilled.FireOutput( pBot, this );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void CTFBotGenerator::Activate()
|
||||
{
|
||||
BaseClass::Activate();
|
||||
m_bBotChoosesClass = FStrEq( m_className.ToCStr(), "auto" );
|
||||
m_moveGoal = gEntList.FindEntityByName( NULL, m_actionPointName.ToCStr() );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFBotGenerator::GeneratorThink( void )
|
||||
{
|
||||
// still waiting for the real game to start?
|
||||
gamerules_roundstate_t roundState = TFGameRules()->State_Get();
|
||||
if ( roundState >= GR_STATE_TEAM_WIN || roundState < GR_STATE_PREROUND || TFGameRules()->IsInWaitingForPlayers() )
|
||||
{
|
||||
SetNextThink( gpGlobals->curtime + 1.0f );
|
||||
return;
|
||||
}
|
||||
|
||||
// create the bot finally...
|
||||
if ( !m_bSpawnOnlyWhenTriggered )
|
||||
{
|
||||
SpawnBot();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFBotGenerator::SpawnBot( void )
|
||||
{
|
||||
// did we exceed the max active count?
|
||||
for ( int i = 0; i < m_spawnedBotVector.Count(); )
|
||||
{
|
||||
CHandle< CTFBot > hBot = m_spawnedBotVector[i];
|
||||
if ( hBot == NULL )
|
||||
{
|
||||
m_spawnedBotVector.FastRemove(i);
|
||||
continue;
|
||||
}
|
||||
if ( hBot->GetTeamNumber() == TEAM_SPECTATOR )
|
||||
{
|
||||
m_spawnedBotVector.FastRemove(i);
|
||||
continue;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
if ( m_spawnedBotVector.Count() >= m_maxActiveCount )
|
||||
{
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
return;
|
||||
}
|
||||
|
||||
char name[256];
|
||||
CTFBot *bot = TheTFBots().GetAvailableBotFromPool();
|
||||
if ( bot == NULL )
|
||||
{
|
||||
CreateBotName( TEAM_UNASSIGNED, TF_CLASS_UNDEFINED, (CTFBot::DifficultyType)m_difficulty, name, sizeof(name) );
|
||||
bot = NextBotCreatePlayerBot< CTFBot >( name );
|
||||
}
|
||||
|
||||
if ( bot )
|
||||
{
|
||||
m_spawnedBotVector.AddToTail( bot );
|
||||
|
||||
#ifdef TF_RAID_MODE
|
||||
if ( TFGameRules()->IsRaidMode() )
|
||||
{
|
||||
bot->SetAttribute( CTFBot::IS_NPC );
|
||||
}
|
||||
#endif // TF_RAID_MODE
|
||||
|
||||
bot->SetSpawner( this );
|
||||
|
||||
if ( m_bUseTeamSpawnpoint == false )
|
||||
{
|
||||
bot->SetSpawnPoint( this );
|
||||
}
|
||||
|
||||
if ( m_bSuppressFire )
|
||||
{
|
||||
bot->SetAttribute( CTFBot::SUPPRESS_FIRE );
|
||||
}
|
||||
|
||||
if ( m_bRetainBuildings )
|
||||
{
|
||||
bot->SetAttribute( CTFBot::RETAIN_BUILDINGS );
|
||||
}
|
||||
|
||||
if ( m_bDisableDodge )
|
||||
{
|
||||
bot->SetAttribute( CTFBot::DISABLE_DODGE );
|
||||
}
|
||||
|
||||
if ( m_difficulty != CTFBot::UNDEFINED )
|
||||
{
|
||||
bot->SetDifficulty( (CTFBot::DifficultyType )m_difficulty );
|
||||
}
|
||||
|
||||
// propagate the generator's spawn flags into all bots generated
|
||||
bot->ClearBehaviorFlag( TFBOT_ALL_BEHAVIOR_FLAGS );
|
||||
bot->SetBehaviorFlag( m_spawnflags );
|
||||
|
||||
switch ( m_iOnDeathAction )
|
||||
{
|
||||
case kOnDeath_RemoveSelf:
|
||||
bot->SetAttribute( CTFBot::REMOVE_ON_DEATH );
|
||||
break;
|
||||
case kOnDeath_MoveToSpectatorTeam:
|
||||
bot->SetAttribute( CTFBot::BECOME_SPECTATOR_ON_DEATH );
|
||||
break;
|
||||
} // switch
|
||||
|
||||
bot->SetActionPoint( dynamic_cast<CTFBotActionPoint *>( m_moveGoal.Get() ) );
|
||||
|
||||
// pick a team and force the team change
|
||||
// HandleCommand_JoinTeam() may fail, but this should always succeed
|
||||
int iTeam = TEAM_UNASSIGNED;
|
||||
if ( FStrEq( m_teamName.ToCStr(), "auto" ) )
|
||||
{
|
||||
iTeam = bot->GetAutoTeam();
|
||||
}
|
||||
else if ( FStrEq( m_teamName.ToCStr(), "spectate" ) )
|
||||
{
|
||||
iTeam = TEAM_SPECTATOR;
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( int i = 0; i < TF_TEAM_COUNT; ++i )
|
||||
{
|
||||
COMPILE_TIME_ASSERT( TF_TEAM_COUNT == ARRAYSIZE( g_aTeamNames ) );
|
||||
if ( FStrEq( m_teamName.ToCStr(), g_aTeamNames[i] ) )
|
||||
{
|
||||
iTeam = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( iTeam == TEAM_UNASSIGNED )
|
||||
{
|
||||
iTeam = bot->GetAutoTeam();
|
||||
}
|
||||
bot->ChangeTeam( iTeam, false, false );
|
||||
|
||||
const char* pClassName = m_bBotChoosesClass ? bot->GetNextSpawnClassname() : m_className.ToCStr();
|
||||
bot->HandleCommand_JoinClass( pClassName );
|
||||
|
||||
// in training, reset the after the bot joins the class
|
||||
if ( TFGameRules()->IsInTraining() )
|
||||
{
|
||||
CTFBot::DifficultyType skill = bot->GetDifficulty();
|
||||
CreateBotName( iTeam, bot->GetPlayerClass()->GetClassIndex(), skill, name, sizeof(name) );
|
||||
engine->SetFakeClientConVarValue( bot->edict(), "name", name );
|
||||
}
|
||||
|
||||
if ( bot->IsAlive() == false )
|
||||
{
|
||||
bot->ForceRespawn();
|
||||
}
|
||||
|
||||
// make sure the bot is facing the right way.
|
||||
// @todo Tom Bui: for some reason it is still turning towards another direction...need to investigate
|
||||
bot->SnapEyeAngles( GetAbsAngles() );
|
||||
|
||||
if ( FStrEq( m_initialCommand.ToCStr(), "" ) == false )
|
||||
{
|
||||
// @note Tom Bui: we call Update() once here to make sure the bot is ready to receive commands
|
||||
bot->Update();
|
||||
bot->OnCommandString( m_initialCommand.ToCStr() );
|
||||
}
|
||||
m_onSpawned.FireOutput( bot, this );
|
||||
|
||||
--m_spawnCountRemaining;
|
||||
if ( m_spawnCountRemaining )
|
||||
{
|
||||
SetNextThink( gpGlobals->curtime + m_spawnInterval );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetThink( NULL );
|
||||
m_onExpended.FireOutput( this, this );
|
||||
m_bExpended = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
BEGIN_DATADESC( CTFBotActionPoint )
|
||||
DEFINE_KEYFIELD( m_stayTime, FIELD_FLOAT, "stay_time" ),
|
||||
DEFINE_KEYFIELD( m_desiredDistance, FIELD_FLOAT, "desired_distance" ),
|
||||
DEFINE_KEYFIELD( m_nextActionPointName, FIELD_STRING, "next_action_point" ),
|
||||
DEFINE_KEYFIELD( m_command, FIELD_STRING, "command" ),
|
||||
DEFINE_OUTPUT( m_onReachedActionPoint, "OnBotReached" ),
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( bot_action_point, CTFBotActionPoint );
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
CTFBotActionPoint::CTFBotActionPoint()
|
||||
: m_stayTime( 0.0f )
|
||||
, m_desiredDistance( 1.0f )
|
||||
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void CTFBotActionPoint::Activate()
|
||||
{
|
||||
BaseClass::Activate();
|
||||
m_moveGoal = gEntList.FindEntityByName( NULL, m_nextActionPointName.ToCStr() );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool CTFBotActionPoint::IsWithinRange( CBaseEntity *entity )
|
||||
{
|
||||
return ( entity->GetAbsOrigin() - GetAbsOrigin() ).IsLengthLessThan( m_desiredDistance );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void CTFBotActionPoint::ReachedActionPoint( CTFBot* pBot )
|
||||
{
|
||||
if ( FStrEq( m_command.ToCStr(), "" ) == false )
|
||||
{
|
||||
pBot->OnCommandString( m_command.ToCStr() );
|
||||
}
|
||||
m_onReachedActionPoint.FireOutput( pBot, this );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,100 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_generator.h
|
||||
// Entity to spawn a collection of TFBots
|
||||
// Michael Booth, September 2009
|
||||
|
||||
#ifndef TF_BOT_GENERATOR_H
|
||||
#define TF_BOT_GENERATOR_H
|
||||
|
||||
#include "bot/tf_bot.h"
|
||||
|
||||
|
||||
class CTFBotGenerator : public CPointEntity
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CTFBotGenerator, CPointEntity );
|
||||
DECLARE_DATADESC();
|
||||
|
||||
CTFBotGenerator( void );
|
||||
virtual ~CTFBotGenerator() { }
|
||||
|
||||
virtual void Activate();
|
||||
|
||||
void GeneratorThink( void );
|
||||
void SpawnBot( void );
|
||||
|
||||
// Input.
|
||||
void InputEnable( inputdata_t &inputdata );
|
||||
void InputDisable( inputdata_t &inputdata );
|
||||
void InputSetSuppressFire( inputdata_t &inputdata );
|
||||
void InputSetDisableDodge( inputdata_t &inputdata );
|
||||
void InputSetDifficulty( inputdata_t &inputdata );
|
||||
void InputCommandGotoActionPoint( inputdata_t &inputdata );
|
||||
void InputSetAttentionFocus( inputdata_t &inputdata );
|
||||
void InputClearAttentionFocus( inputdata_t &inputdata );
|
||||
void InputSpawnBot( inputdata_t &inputdata );
|
||||
void InputRemoveBots( inputdata_t &inputdata );
|
||||
|
||||
// Output
|
||||
void OnBotKilled( CTFBot *pBot );
|
||||
|
||||
private:
|
||||
bool m_bBotChoosesClass;
|
||||
bool m_bSuppressFire;
|
||||
bool m_bDisableDodge;
|
||||
bool m_bUseTeamSpawnpoint;
|
||||
bool m_bRetainBuildings;
|
||||
bool m_bExpended;
|
||||
int m_iOnDeathAction;
|
||||
int m_spawnCount;
|
||||
int m_spawnCountRemaining;
|
||||
int m_maxActiveCount;
|
||||
float m_spawnInterval;
|
||||
string_t m_className;
|
||||
string_t m_teamName;
|
||||
string_t m_actionPointName;
|
||||
string_t m_initialCommand;
|
||||
CHandle< CBaseEntity > m_moveGoal;
|
||||
int m_difficulty;
|
||||
bool m_bSpawnOnlyWhenTriggered;
|
||||
bool m_bEnabled;
|
||||
|
||||
COutputEvent m_onSpawned;
|
||||
COutputEvent m_onExpended;
|
||||
COutputEvent m_onBotKilled;
|
||||
|
||||
CUtlVector< CHandle< CTFBot > > m_spawnedBotVector;
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------
|
||||
//
|
||||
// Bot generator may have one of these as an argument, which
|
||||
// means "tell the bot I created to move here and do what this node says".
|
||||
// Things like "stay here", "move to <next task point>", "face towards <X>", "shoot at <Y>", etc
|
||||
//
|
||||
class CTFBotActionPoint : public CPointEntity
|
||||
{
|
||||
DECLARE_CLASS( CTFBotActionPoint, CPointEntity );
|
||||
public:
|
||||
DECLARE_DATADESC();
|
||||
|
||||
CTFBotActionPoint( void );
|
||||
virtual ~CTFBotActionPoint() { }
|
||||
|
||||
virtual void Activate();
|
||||
|
||||
bool IsWithinRange( CBaseEntity *entity );
|
||||
void ReachedActionPoint( CTFBot* pBot );
|
||||
|
||||
CHandle< CBaseEntity > m_moveGoal;
|
||||
|
||||
// reflected
|
||||
float m_stayTime;
|
||||
float m_desiredDistance;
|
||||
string_t m_nextActionPointName;
|
||||
string_t m_command;
|
||||
|
||||
COutputEvent m_onReachedActionPoint;
|
||||
};
|
||||
|
||||
#endif // TF_BOT_GENERATOR_H
|
||||
@@ -0,0 +1,128 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_hint.cpp
|
||||
// Designer-placed hint for TFBots
|
||||
|
||||
#include "cbase.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "tf_bot_hint.h"
|
||||
|
||||
BEGIN_DATADESC( CTFBotHint )
|
||||
DEFINE_KEYFIELD( m_team, FIELD_INTEGER, "team" ),
|
||||
DEFINE_KEYFIELD( m_hint, FIELD_INTEGER, "hint" ),
|
||||
DEFINE_KEYFIELD( m_isDisabled, FIELD_BOOLEAN, "StartDisabled" ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( func_tfbot_hint, CTFBotHint );
|
||||
|
||||
//
|
||||
// NOTE: For simplicity and runtime efficiency, this will not
|
||||
// play nice with nav area hints stored in the mesh,
|
||||
// nor will overlapping hints of the same type work well.
|
||||
//
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
CTFBotHint::CTFBotHint( void )
|
||||
{
|
||||
m_isDisabled = false;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
// Return true if this hint applies to the given entity
|
||||
bool CTFBotHint::IsFor( CTFBot *who ) const
|
||||
{
|
||||
if ( m_isDisabled )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( m_team > 0 && who->GetTeamNumber() != m_team )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
void CTFBotHint::Spawn( void )
|
||||
{
|
||||
BaseClass::Spawn();
|
||||
|
||||
SetSolid( SOLID_BSP );
|
||||
AddSolidFlags( FSOLID_NOT_SOLID );
|
||||
|
||||
SetMoveType( MOVETYPE_NONE );
|
||||
SetModel( STRING( GetModelName() ) );
|
||||
AddEffects( EF_NODRAW );
|
||||
SetCollisionGroup( COLLISION_GROUP_NONE );
|
||||
|
||||
VPhysicsInitShadow( false, false );
|
||||
|
||||
UpdateNavDecoration();
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
void CTFBotHint::UpdateOnRemove( void )
|
||||
{
|
||||
BaseClass::UpdateOnRemove();
|
||||
|
||||
UpdateNavDecoration();
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
void CTFBotHint::InputEnable( inputdata_t &inputdata )
|
||||
{
|
||||
m_isDisabled = false;
|
||||
UpdateNavDecoration();
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
void CTFBotHint::InputDisable( inputdata_t &inputdata )
|
||||
{
|
||||
m_isDisabled = true;
|
||||
UpdateNavDecoration();
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
void CTFBotHint::UpdateNavDecoration( void )
|
||||
{
|
||||
Extent extent;
|
||||
extent.Init( this );
|
||||
|
||||
CUtlVector< CTFNavArea * > overlapVector;
|
||||
TheNavMesh->CollectAreasOverlappingExtent( extent, &overlapVector );
|
||||
|
||||
int attributeBits = 0;
|
||||
switch( m_hint )
|
||||
{
|
||||
case HINT_SNIPER_SPOT:
|
||||
attributeBits = TF_NAV_SNIPER_SPOT;
|
||||
break;
|
||||
|
||||
case HINT_SENTRY_SPOT:
|
||||
attributeBits = TF_NAV_SENTRY_SPOT;
|
||||
break;
|
||||
}
|
||||
|
||||
for( int j=0; j<overlapVector.Count(); ++j )
|
||||
{
|
||||
if ( m_isDisabled )
|
||||
{
|
||||
overlapVector[j]->ClearAttributeTF( attributeBits );
|
||||
}
|
||||
else
|
||||
{
|
||||
overlapVector[j]->SetAttributeTF( attributeBits );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_hint.h
|
||||
// Designer-placed hint for TFBots
|
||||
|
||||
#ifndef TF_BOT_HINT_H
|
||||
#define TF_BOT_HINT_H
|
||||
|
||||
class CTFBot;
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* An entity that specifies TFBot behavior hints.
|
||||
*/
|
||||
class CTFBotHint : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
DECLARE_DATADESC();
|
||||
DECLARE_CLASS( CTFBotHint, CBaseEntity );
|
||||
|
||||
CTFBotHint( void );
|
||||
virtual ~CTFBotHint() { }
|
||||
|
||||
enum HintType
|
||||
{
|
||||
HINT_SNIPER_SPOT = 0,
|
||||
HINT_SENTRY_SPOT = 1,
|
||||
};
|
||||
|
||||
bool IsA( HintType type ) const;
|
||||
|
||||
bool IsFor( CTFBot *who ) const; // return true if this hint applies to the given entity
|
||||
|
||||
virtual void Spawn( void );
|
||||
virtual void UpdateOnRemove( void );
|
||||
|
||||
void InputEnable( inputdata_t &inputdata );
|
||||
void InputDisable( inputdata_t &inputdata );
|
||||
bool IsEnabled( void ) const { return !m_isDisabled; }
|
||||
|
||||
protected:
|
||||
int m_team;
|
||||
int m_hint;
|
||||
bool m_isDisabled;
|
||||
|
||||
void UpdateNavDecoration( void );
|
||||
};
|
||||
|
||||
inline bool CTFBotHint::IsA( HintType type ) const
|
||||
{
|
||||
return ( m_hint == type );
|
||||
}
|
||||
|
||||
|
||||
#endif // TF_BOT_HINT_H
|
||||
@@ -0,0 +1,160 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
#include "cbase.h"
|
||||
#include "tf_bot_hint_engineer_nest.h"
|
||||
#include "tf_obj.h"
|
||||
#include "tf_obj_teleporter.h"
|
||||
|
||||
IMPLEMENT_SERVERCLASS_ST( CTFBotHintEngineerNest, DT_TFBotHintEngineerNest )
|
||||
SendPropBool( SENDINFO(m_bHasActiveTeleporter) ),
|
||||
END_SEND_TABLE()
|
||||
|
||||
BEGIN_DATADESC( CTFBotHintEngineerNest )
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( bot_hint_engineer_nest, CTFBotHintEngineerNest );
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
CTFBotHintEngineerNest::CTFBotHintEngineerNest( void )
|
||||
{
|
||||
m_bHasActiveTeleporter = false;
|
||||
}
|
||||
|
||||
|
||||
void CTFBotHintEngineerNest::Spawn()
|
||||
{
|
||||
BaseClass::Spawn();
|
||||
|
||||
SetThink( &CTFBotHintEngineerNest::HintThink );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
}
|
||||
|
||||
|
||||
void CTFBotHintEngineerNest::HintThink()
|
||||
{
|
||||
// find sentry and teleporter hint
|
||||
for ( int i=0; i<ITFBotHintEntityAutoList::AutoList().Count(); ++i )
|
||||
{
|
||||
CBaseTFBotHintEntity *pHint = static_cast< CBaseTFBotHintEntity* >( ITFBotHintEntityAutoList::AutoList()[i] );
|
||||
if ( pHint->IsHintType( CBaseTFBotHintEntity::HINT_SENTRYGUN ) && pHint->GetEntityName() == GetEntityName() )
|
||||
{
|
||||
m_sentries.AddToTail( pHint );
|
||||
}
|
||||
else if ( pHint->IsHintType( CBaseTFBotHintEntity::HINT_TELEPORTER_EXIT ) && pHint->GetEntityName() == GetEntityName() )
|
||||
{
|
||||
m_teleporters.AddToTail( pHint );
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_sentries.Count() == 0 && m_teleporters.Count() == 0 )
|
||||
{
|
||||
AssertMsg( 0, "Must have a teleporter and/or a sentry hint with the same name." );
|
||||
Warning( "Must have a teleporter and/or a sentry hint with the same name.\n" );
|
||||
}
|
||||
|
||||
SetThink( &CTFBotHintEngineerNest::HintTeleporterThink );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
}
|
||||
|
||||
|
||||
void CTFBotHintEngineerNest::HintTeleporterThink()
|
||||
{
|
||||
bool bFoundActiveTeleporter = false;
|
||||
for ( int i=0; i<m_teleporters.Count(); ++i )
|
||||
{
|
||||
CBaseEntity* pOwner = m_teleporters[i]->GetOwnerEntity();
|
||||
if ( pOwner && pOwner->IsBaseObject() )
|
||||
{
|
||||
CObjectTeleporter *pTeleporter = assert_cast< CObjectTeleporter* >( pOwner );
|
||||
if ( pTeleporter )
|
||||
{
|
||||
bFoundActiveTeleporter |= !pTeleporter->IsBuilding();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update particle bool
|
||||
m_bHasActiveTeleporter = bFoundActiveTeleporter;
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
}
|
||||
|
||||
|
||||
bool CTFBotHintEngineerNest::IsStaleNest() const
|
||||
{
|
||||
for ( int i=0; i<m_sentries.Count(); ++i )
|
||||
{
|
||||
if ( m_sentries[i]->OwnerObjectHasNoOwner() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for ( int i=0; i<m_teleporters.Count(); ++i )
|
||||
{
|
||||
if ( m_teleporters[i]->OwnerObjectHasNoOwner() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void CTFBotHintEngineerNest::DetonateStaleNest()
|
||||
{
|
||||
DetonateObjectsFromHints( m_sentries );
|
||||
DetonateObjectsFromHints( m_teleporters );
|
||||
}
|
||||
|
||||
|
||||
void CTFBotHintEngineerNest::DetonateObjectsFromHints( const HintVector_t& hints )
|
||||
{
|
||||
for ( int i=0; i<hints.Count(); ++i )
|
||||
{
|
||||
if ( hints[i]->OwnerObjectHasNoOwner() )
|
||||
{
|
||||
CBaseObject* pObj = assert_cast< CBaseObject* >( hints[i]->GetOwnerEntity() );
|
||||
if ( pObj )
|
||||
{
|
||||
pObj->DetonateObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CBaseTFBotHintEntity* CTFBotHintEngineerNest::GetHint( const HintVector_t& hints ) const
|
||||
{
|
||||
if ( hints.Count() == 0 )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for ( int i=0; i<hints.Count(); ++i )
|
||||
{
|
||||
if ( hints[i]->OwnerObjectHasNoOwner() )
|
||||
{
|
||||
return hints[i];
|
||||
}
|
||||
}
|
||||
|
||||
int which = RandomInt( 0, hints.Count() - 1 );
|
||||
return hints[ which ];
|
||||
}
|
||||
|
||||
|
||||
CTFBotHintSentrygun* CTFBotHintEngineerNest::GetSentryHint() const
|
||||
{
|
||||
return (CTFBotHintSentrygun*)GetHint( m_sentries );
|
||||
}
|
||||
|
||||
|
||||
CTFBotHintTeleporterExit* CTFBotHintEngineerNest::GetTeleporterHint() const
|
||||
{
|
||||
return (CTFBotHintTeleporterExit*)GetHint( m_teleporters );
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
#ifndef TF_BOT_HINT_ENGINEER_NEST_H
|
||||
#define TF_BOT_HINT_ENGINEER_NEST_H
|
||||
|
||||
#include "tf_bot_hint_entity.h"
|
||||
|
||||
typedef CUtlVector< CHandle< CBaseTFBotHintEntity > > HintVector_t;
|
||||
|
||||
class CTFBotHintSentrygun;
|
||||
class CTFBotHintTeleporterExit;
|
||||
|
||||
class CTFBotHintEngineerNest : public CBaseTFBotHintEntity
|
||||
{
|
||||
DECLARE_CLASS( CTFBotHintEngineerNest, CBaseTFBotHintEntity );
|
||||
public:
|
||||
DECLARE_SERVERCLASS();
|
||||
DECLARE_DATADESC();
|
||||
|
||||
CTFBotHintEngineerNest( void );
|
||||
virtual ~CTFBotHintEngineerNest() { }
|
||||
|
||||
virtual void Spawn() OVERRIDE;
|
||||
|
||||
virtual HintType GetHintType() const OVERRIDE { return HINT_ENGINEER_NEST; }
|
||||
|
||||
virtual int UpdateTransmitState()
|
||||
{
|
||||
return SetTransmitState( FL_EDICT_ALWAYS );
|
||||
}
|
||||
|
||||
void HintThink();
|
||||
void HintTeleporterThink();
|
||||
|
||||
bool IsStaleNest() const;
|
||||
void DetonateStaleNest();
|
||||
|
||||
CTFBotHintSentrygun* GetSentryHint() const;
|
||||
CTFBotHintTeleporterExit* GetTeleporterHint() const;
|
||||
private:
|
||||
void DetonateObjectsFromHints( const HintVector_t& hints );
|
||||
CBaseTFBotHintEntity* GetHint( const HintVector_t& hints ) const;
|
||||
|
||||
HintVector_t m_sentries;
|
||||
HintVector_t m_teleporters;
|
||||
|
||||
CNetworkVar( bool, m_bHasActiveTeleporter );
|
||||
};
|
||||
|
||||
#endif // TF_BOT_HINT_ENGINEER_NEST_H
|
||||
@@ -0,0 +1,60 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
#include "cbase.h"
|
||||
#include "tf_bot_hint_entity.h"
|
||||
#include "tf_obj.h"
|
||||
#include "tf_player.h"
|
||||
|
||||
|
||||
BEGIN_DATADESC( CBaseTFBotHintEntity )
|
||||
DEFINE_KEYFIELD( m_isDisabled, FIELD_BOOLEAN, "StartDisabled" ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
|
||||
END_DATADESC()
|
||||
|
||||
IMPLEMENT_AUTO_LIST( ITFBotHintEntityAutoList );
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
CBaseTFBotHintEntity::CBaseTFBotHintEntity( void )
|
||||
: m_isDisabled( false ),
|
||||
m_hintType( HINT_INVALID )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool CBaseTFBotHintEntity::OwnerObjectHasNoOwner() const
|
||||
{
|
||||
CBaseEntity* pOwner = GetOwnerEntity();
|
||||
if ( pOwner && pOwner->IsBaseObject() )
|
||||
{
|
||||
CBaseObject *pObj = static_cast< CBaseObject* >( pOwner );
|
||||
if ( pObj->GetBuilder() == NULL )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !pObj->GetBuilder()->IsPlayerClass( TF_CLASS_ENGINEER ) )
|
||||
{
|
||||
AssertMsg( 0, "Object has an owner that's not engineer." );
|
||||
Warning( "Object has an owner that's not engineer." );
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool CBaseTFBotHintEntity::OwnerObjectFinishBuilding() const
|
||||
{
|
||||
CBaseEntity* pOwner = GetOwnerEntity();
|
||||
if ( pOwner && pOwner->IsBaseObject() )
|
||||
{
|
||||
CBaseObject *pObj = static_cast< CBaseObject* >( pOwner );
|
||||
return !pObj->IsBuilding();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
#ifndef TF_BOT_HINT_ENTITY_H
|
||||
#define TF_BOT_HINT_ENTITY_H
|
||||
|
||||
DECLARE_AUTO_LIST( ITFBotHintEntityAutoList );
|
||||
|
||||
class CBaseTFBotHintEntity : public CPointEntity, public ITFBotHintEntityAutoList
|
||||
{
|
||||
DECLARE_CLASS( CBaseTFBotHintEntity, CPointEntity );
|
||||
public:
|
||||
DECLARE_DATADESC();
|
||||
|
||||
CBaseTFBotHintEntity( void );
|
||||
virtual ~CBaseTFBotHintEntity() { }
|
||||
|
||||
enum HintType
|
||||
{
|
||||
HINT_INVALID = -1,
|
||||
HINT_TELEPORTER_EXIT,
|
||||
HINT_SENTRYGUN,
|
||||
HINT_ENGINEER_NEST,
|
||||
};
|
||||
virtual HintType GetHintType() const = 0;
|
||||
bool IsHintType( HintType hintType ) { return GetHintType() == hintType; }
|
||||
|
||||
bool OwnerObjectHasNoOwner() const;
|
||||
bool OwnerObjectFinishBuilding() const;
|
||||
|
||||
bool IsEnabled() const;
|
||||
void InputEnable( inputdata_t &inputdata );
|
||||
void InputDisable( inputdata_t &inputdata );
|
||||
|
||||
private:
|
||||
|
||||
bool m_isDisabled;
|
||||
HintType m_hintType;
|
||||
};
|
||||
|
||||
|
||||
inline void CBaseTFBotHintEntity::InputEnable( inputdata_t &inputdata )
|
||||
{
|
||||
m_isDisabled = false;
|
||||
}
|
||||
|
||||
inline void CBaseTFBotHintEntity::InputDisable( inputdata_t &inputdata )
|
||||
{
|
||||
m_isDisabled = true;
|
||||
}
|
||||
|
||||
inline bool CBaseTFBotHintEntity::IsEnabled() const
|
||||
{
|
||||
return !m_isDisabled;
|
||||
}
|
||||
|
||||
#endif // TF_BOT_HINT_ENTITY_H
|
||||
@@ -0,0 +1,42 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_hint_sentrygun.cpp
|
||||
// Designer-placed hint for bot sentry placement
|
||||
// Michael Booth, October 2009
|
||||
|
||||
#include "cbase.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "tf_bot_hint_sentrygun.h"
|
||||
|
||||
|
||||
BEGIN_DATADESC( CTFBotHintSentrygun )
|
||||
DEFINE_KEYFIELD( m_isSticky, FIELD_BOOLEAN, "sticky" ),
|
||||
DEFINE_OUTPUT( m_outputOnSentryGunDestroyed, "OnSentryGunDestroyed" ),
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( bot_hint_sentrygun, CTFBotHintSentrygun );
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
CTFBotHintSentrygun::CTFBotHintSentrygun( void )
|
||||
: m_isSticky( false )
|
||||
, m_iUseCount( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFBotHintSentrygun::OnSentryGunDestroyed( CBaseEntity *pEntity )
|
||||
{
|
||||
m_outputOnSentryGunDestroyed.FireOutput( pEntity, pEntity );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool CTFBotHintSentrygun::IsAvailableForSelection( CTFPlayer *pRequestingPlayer ) const
|
||||
{
|
||||
// sentry hint is eligible as long as there is no owner (or the owner is no longer an engineer)
|
||||
// if the hint is enabled and the hint is not in use and it is on the same team as me
|
||||
if ( ( GetPlayerOwner() == NULL || !GetPlayerOwner()->IsPlayerClass( TF_CLASS_ENGINEER ) ) &&
|
||||
( IsEnabled() && IsInUse() == false && InSameTeam( pRequestingPlayer ) ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_hint_sentrygun.h
|
||||
// Designer-placed hint for bot sentry placement
|
||||
// Michael Booth, October 2009
|
||||
|
||||
#ifndef TF_BOT_HINT_SENTRYGUN_H
|
||||
#define TF_BOT_HINT_SENTRYGUN_H
|
||||
|
||||
#include "tf_bot_hint_entity.h"
|
||||
|
||||
class CTFPlayer;
|
||||
|
||||
class CTFBotHintSentrygun : public CBaseTFBotHintEntity
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CTFBotHintSentrygun, CBaseTFBotHintEntity );
|
||||
DECLARE_DATADESC();
|
||||
|
||||
CTFBotHintSentrygun( void );
|
||||
virtual ~CTFBotHintSentrygun() { }
|
||||
|
||||
bool IsSticky() const;
|
||||
bool IsInUse() const;
|
||||
|
||||
CTFPlayer *GetPlayerOwner() const;
|
||||
void SetPlayerOwner( CTFPlayer *pPlayerOwner );
|
||||
|
||||
void IncrementUseCount();
|
||||
void DecrementUseCount();
|
||||
|
||||
void OnSentryGunDestroyed( CBaseEntity *pBaseEntity );
|
||||
|
||||
bool IsAvailableForSelection( CTFPlayer *pRequestingPlayer ) const;
|
||||
|
||||
virtual HintType GetHintType() const OVERRIDE { return HINT_SENTRYGUN; }
|
||||
|
||||
private:
|
||||
bool m_isSticky;
|
||||
int m_iUseCount;
|
||||
COutputEvent m_outputOnSentryGunDestroyed;
|
||||
|
||||
CHandle< CTFPlayer > m_playerOwner;
|
||||
};
|
||||
|
||||
inline bool CTFBotHintSentrygun::IsSticky() const
|
||||
{
|
||||
return m_isSticky;
|
||||
}
|
||||
|
||||
inline bool CTFBotHintSentrygun::IsInUse() const
|
||||
{
|
||||
return m_iUseCount != 0;
|
||||
}
|
||||
|
||||
inline CTFPlayer *CTFBotHintSentrygun::GetPlayerOwner() const
|
||||
{
|
||||
return m_playerOwner;
|
||||
}
|
||||
|
||||
inline void CTFBotHintSentrygun::SetPlayerOwner( CTFPlayer *pPlayerOwner )
|
||||
{
|
||||
m_playerOwner = pPlayerOwner;
|
||||
}
|
||||
|
||||
inline void CTFBotHintSentrygun::IncrementUseCount()
|
||||
{
|
||||
++m_iUseCount;
|
||||
}
|
||||
|
||||
inline void CTFBotHintSentrygun::DecrementUseCount()
|
||||
{
|
||||
--m_iUseCount;
|
||||
}
|
||||
|
||||
#endif // TF_BOT_HINT_SENTRYGUN_H
|
||||
@@ -0,0 +1,19 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_hint_teleporter_exit.cpp
|
||||
// Designer-placed hint for bot teleporter exit placement
|
||||
// Michael Booth, May 2010
|
||||
|
||||
#include "cbase.h"
|
||||
#include "tf_bot_hint_teleporter_exit.h"
|
||||
|
||||
|
||||
BEGIN_DATADESC( CTFBotHintTeleporterExit )
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( bot_hint_teleporter_exit, CTFBotHintTeleporterExit );
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
CTFBotHintTeleporterExit::CTFBotHintTeleporterExit( void )
|
||||
{
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_hint_teleporter_exit.h
|
||||
// Designer-placed hint for bot teleporter exit placement
|
||||
// Michael Booth, May 2010
|
||||
|
||||
#ifndef TF_BOT_HINT_TELEPORTER_EXIT_H
|
||||
#define TF_BOT_HINT_TELEPORTER_EXIT_H
|
||||
|
||||
#include "tf_bot_hint_entity.h"
|
||||
|
||||
class CTFBotHintTeleporterExit : public CBaseTFBotHintEntity
|
||||
{
|
||||
DECLARE_CLASS( CTFBotHintTeleporterExit, CBaseTFBotHintEntity );
|
||||
public:
|
||||
DECLARE_DATADESC();
|
||||
|
||||
CTFBotHintTeleporterExit( void );
|
||||
virtual ~CTFBotHintTeleporterExit() { }
|
||||
|
||||
virtual HintType GetHintType() const OVERRIDE { return HINT_TELEPORTER_EXIT; }
|
||||
};
|
||||
|
||||
#endif // TF_BOT_HINT_TELEPORTER_EXIT_H
|
||||
@@ -0,0 +1,167 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_proxy.cpp
|
||||
// A Hammer entity that spawns a TFBot and relays events to/from it
|
||||
// Michael Booth, November 2009
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "bot/tf_bot.h"
|
||||
#include "tf_bot_proxy.h"
|
||||
#include "tf_bot_generator.h"
|
||||
|
||||
|
||||
BEGIN_DATADESC( CTFBotProxy )
|
||||
DEFINE_KEYFIELD( m_botName, FIELD_STRING, "bot_name" ),
|
||||
DEFINE_KEYFIELD( m_className, FIELD_STRING, "class" ),
|
||||
DEFINE_KEYFIELD( m_teamName, FIELD_STRING, "team" ),
|
||||
DEFINE_KEYFIELD( m_respawnInterval, FIELD_FLOAT, "respawn_interval" ),
|
||||
DEFINE_KEYFIELD( m_actionPointName, FIELD_STRING, "action_point" ),
|
||||
DEFINE_KEYFIELD( m_spawnOnStart, FIELD_STRING, "spawn_on_start" ),
|
||||
|
||||
DEFINE_INPUTFUNC( FIELD_STRING, "SetTeam", InputSetTeam ),
|
||||
DEFINE_INPUTFUNC( FIELD_STRING, "SetClass", InputSetClass ),
|
||||
DEFINE_INPUTFUNC( FIELD_STRING, "SetMovementGoal", InputSetMovementGoal ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Spawn", InputSpawn ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Delete", InputDelete ),
|
||||
|
||||
DEFINE_OUTPUT( m_onSpawned, "OnSpawned" ),
|
||||
DEFINE_OUTPUT( m_onInjured, "OnInjured" ),
|
||||
DEFINE_OUTPUT( m_onKilled, "OnKilled" ),
|
||||
DEFINE_OUTPUT( m_onAttackingEnemy, "OnAttackingEnemy" ),
|
||||
DEFINE_OUTPUT( m_onKilledEnemy, "OnKilledEnemy" ),
|
||||
|
||||
DEFINE_THINKFUNC( Think ),
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( bot_proxy, CTFBotProxy );
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
CTFBotProxy::CTFBotProxy( void )
|
||||
{
|
||||
V_strcpy_safe( m_botName, "TFBot" );
|
||||
V_strcpy_safe( m_teamName, "auto" );
|
||||
V_strcpy_safe( m_className, "auto" );
|
||||
m_bot = NULL;
|
||||
m_moveGoal = NULL;
|
||||
SetThink( NULL );
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFBotProxy::Think( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFBotProxy::InputSetTeam( inputdata_t &inputdata )
|
||||
{
|
||||
const char *teamName = inputdata.value.String();
|
||||
if ( teamName && teamName[0] )
|
||||
{
|
||||
V_strcpy_safe( m_teamName, teamName );
|
||||
|
||||
// if m_bot exists, tell it to change team
|
||||
if ( m_bot != NULL )
|
||||
{
|
||||
m_bot->HandleCommand_JoinTeam( m_teamName );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFBotProxy::InputSetClass( inputdata_t &inputdata )
|
||||
{
|
||||
const char *className = inputdata.value.String();
|
||||
if ( className && className[0] )
|
||||
{
|
||||
V_strcpy_safe( m_className, className );
|
||||
|
||||
// if m_bot exists, tell it to change class
|
||||
if ( m_bot != NULL )
|
||||
{
|
||||
m_bot->HandleCommand_JoinClass( m_className );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFBotProxy::InputSetMovementGoal( inputdata_t &inputdata )
|
||||
{
|
||||
const char *entityName = inputdata.value.String();
|
||||
if ( entityName && entityName[0] )
|
||||
{
|
||||
m_moveGoal = dynamic_cast< CTFBotActionPoint * >( gEntList.FindEntityByName( NULL, entityName ) );
|
||||
|
||||
// if m_bot exists, tell it to move to the new action point
|
||||
if ( m_bot != NULL )
|
||||
{
|
||||
m_bot->SetActionPoint( (CTFBotActionPoint *)m_moveGoal.Get() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFBotProxy::InputSpawn( inputdata_t &inputdata )
|
||||
{
|
||||
m_bot = NextBotCreatePlayerBot< CTFBot >( m_botName );
|
||||
if ( m_bot != NULL )
|
||||
{
|
||||
m_bot->SetSpawnPoint( this );
|
||||
m_bot->SetAttribute( CTFBot::REMOVE_ON_DEATH );
|
||||
m_bot->SetAttribute( CTFBot::IS_NPC );
|
||||
|
||||
m_bot->SetActionPoint( (CTFBotActionPoint *)m_moveGoal.Get() );
|
||||
|
||||
m_bot->HandleCommand_JoinTeam( m_teamName );
|
||||
m_bot->HandleCommand_JoinClass( m_className );
|
||||
|
||||
m_onSpawned.FireOutput( m_bot, m_bot );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFBotProxy::InputDelete( inputdata_t &inputdata )
|
||||
{
|
||||
if ( m_bot != NULL )
|
||||
{
|
||||
engine->ServerCommand( UTIL_VarArgs( "kickid %d\n", m_bot->GetUserID() ) );
|
||||
m_bot = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFBotProxy::OnInjured( void )
|
||||
{
|
||||
m_onInjured.FireOutput( this, this );
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFBotProxy::OnKilled( void )
|
||||
{
|
||||
m_onKilled.FireOutput( this, this );
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFBotProxy::OnAttackingEnemy( void )
|
||||
{
|
||||
m_onAttackingEnemy.FireOutput( this, this );
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFBotProxy::OnKilledEnemy( void )
|
||||
{
|
||||
m_onKilledEnemy.FireOutput( this, this );
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_proxy.h
|
||||
// A Hammer entity that spawns a TFBot and relays events to/from it
|
||||
// Michael Booth, November 2009
|
||||
|
||||
#ifndef TF_BOT_PROXY_H
|
||||
#define TF_BOT_PROXY_H
|
||||
|
||||
|
||||
class CTFBot;
|
||||
class CTFBotActionPoint;
|
||||
|
||||
|
||||
class CTFBotProxy : public CPointEntity
|
||||
{
|
||||
DECLARE_CLASS( CTFBotProxy, CPointEntity );
|
||||
public:
|
||||
DECLARE_DATADESC();
|
||||
|
||||
CTFBotProxy( void );
|
||||
virtual ~CTFBotProxy() { }
|
||||
|
||||
void Think( void );
|
||||
|
||||
// Input
|
||||
void InputSetTeam( inputdata_t &inputdata );
|
||||
void InputSetClass( inputdata_t &inputdata );
|
||||
void InputSetMovementGoal( inputdata_t &inputdata );
|
||||
void InputSpawn( inputdata_t &inputdata );
|
||||
void InputDelete( inputdata_t &inputdata );
|
||||
|
||||
void OnInjured( void );
|
||||
void OnKilled( void );
|
||||
void OnAttackingEnemy( void );
|
||||
void OnKilledEnemy( void );
|
||||
|
||||
protected:
|
||||
// Output
|
||||
COutputEvent m_onSpawned;
|
||||
COutputEvent m_onInjured;
|
||||
COutputEvent m_onKilled;
|
||||
COutputEvent m_onAttackingEnemy;
|
||||
COutputEvent m_onKilledEnemy;
|
||||
|
||||
char m_botName[64];
|
||||
char m_className[64];
|
||||
char m_teamName[64];
|
||||
|
||||
string_t m_spawnOnStart;
|
||||
string_t m_actionPointName;
|
||||
float m_respawnInterval;
|
||||
|
||||
CHandle< CTFBot > m_bot;
|
||||
CHandle< CTFBotActionPoint > m_moveGoal;
|
||||
};
|
||||
|
||||
|
||||
#endif // TF_BOT_PROXY_H
|
||||
@@ -0,0 +1,107 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_roster.cpp
|
||||
// entity that dictates what classes a bot can choose when spawning
|
||||
// Tom Bui, April 2010
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "tf_shareddefs.h"
|
||||
#include "bot/map_entities/tf_bot_roster.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
BEGIN_DATADESC( CTFBotRoster )
|
||||
DEFINE_KEYFIELD( m_teamName, FIELD_STRING, "team" ),
|
||||
DEFINE_KEYFIELD( m_bAllowClassChanges, FIELD_BOOLEAN, "allowClassChanges" ),
|
||||
DEFINE_KEYFIELD( m_bAllowedClasses[TF_CLASS_SCOUT], FIELD_BOOLEAN, "allowScout" ),
|
||||
DEFINE_KEYFIELD( m_bAllowedClasses[TF_CLASS_SNIPER], FIELD_BOOLEAN, "allowSniper" ),
|
||||
DEFINE_KEYFIELD( m_bAllowedClasses[TF_CLASS_SOLDIER], FIELD_BOOLEAN, "allowSoldier" ),
|
||||
DEFINE_KEYFIELD( m_bAllowedClasses[TF_CLASS_DEMOMAN], FIELD_BOOLEAN, "allowDemoman" ),
|
||||
DEFINE_KEYFIELD( m_bAllowedClasses[TF_CLASS_MEDIC], FIELD_BOOLEAN, "allowMedic" ),
|
||||
DEFINE_KEYFIELD( m_bAllowedClasses[TF_CLASS_HEAVYWEAPONS], FIELD_BOOLEAN, "allowHeavy" ),
|
||||
DEFINE_KEYFIELD( m_bAllowedClasses[TF_CLASS_PYRO], FIELD_BOOLEAN, "allowPyro" ),
|
||||
DEFINE_KEYFIELD( m_bAllowedClasses[TF_CLASS_SPY], FIELD_BOOLEAN, "allowSpy" ),
|
||||
DEFINE_KEYFIELD( m_bAllowedClasses[TF_CLASS_ENGINEER], FIELD_BOOLEAN, "allowEngineer" ),
|
||||
|
||||
DEFINE_INPUTFUNC( FIELD_STRING, "SetTeam", InputSetTeam ),
|
||||
DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetAllowScout", InputSetAllowScout ),
|
||||
DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetAllowSniper", InputSetAllowSniper ),
|
||||
DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetAllowSoldier", InputSetAllowSoldier ),
|
||||
DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetAllowDemoman", InputSetAllowDemoman ),
|
||||
DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetAllowMedic", InputSetAllowMedic ),
|
||||
DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetAllowHeavy", InputSetAllowHeavy ),
|
||||
DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetAllowPyro", InputSetAllowPyro ),
|
||||
DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetAllowSpy", InputSetAllowSpy ),
|
||||
DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetAllowEngineer", InputSetAllowEngineer ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( bot_roster, CTFBotRoster );
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
CTFBotRoster::CTFBotRoster()
|
||||
{
|
||||
memset( m_bAllowedClasses, 0, sizeof( m_bAllowedClasses ) );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void CTFBotRoster::InputSetAllowScout( inputdata_t &inputdata )
|
||||
{
|
||||
m_bAllowedClasses[TF_CLASS_SCOUT] = inputdata.value.Bool();
|
||||
}
|
||||
|
||||
void CTFBotRoster::InputSetAllowSniper( inputdata_t &inputdata )
|
||||
{
|
||||
m_bAllowedClasses[TF_CLASS_SNIPER] = inputdata.value.Bool();
|
||||
}
|
||||
|
||||
void CTFBotRoster::InputSetAllowSoldier( inputdata_t &inputdata )
|
||||
{
|
||||
m_bAllowedClasses[TF_CLASS_SOLDIER] = inputdata.value.Bool();
|
||||
}
|
||||
|
||||
void CTFBotRoster::InputSetAllowDemoman( inputdata_t &inputdata )
|
||||
{
|
||||
m_bAllowedClasses[TF_CLASS_DEMOMAN] = inputdata.value.Bool();
|
||||
}
|
||||
|
||||
void CTFBotRoster::InputSetAllowMedic( inputdata_t &inputdata )
|
||||
{
|
||||
m_bAllowedClasses[TF_CLASS_MEDIC] = inputdata.value.Bool();
|
||||
}
|
||||
|
||||
void CTFBotRoster::InputSetAllowHeavy( inputdata_t &inputdata )
|
||||
{
|
||||
m_bAllowedClasses[TF_CLASS_HEAVYWEAPONS] = inputdata.value.Bool();
|
||||
}
|
||||
|
||||
void CTFBotRoster::InputSetAllowPyro( inputdata_t &inputdata )
|
||||
{
|
||||
m_bAllowedClasses[TF_CLASS_PYRO] = inputdata.value.Bool();
|
||||
}
|
||||
|
||||
void CTFBotRoster::InputSetAllowSpy( inputdata_t &inputdata )
|
||||
{
|
||||
m_bAllowedClasses[TF_CLASS_SPY] = inputdata.value.Bool();
|
||||
}
|
||||
|
||||
void CTFBotRoster::InputSetAllowEngineer( inputdata_t &inputdata )
|
||||
{
|
||||
m_bAllowedClasses[TF_CLASS_ENGINEER] = inputdata.value.Bool();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool CTFBotRoster::IsClassAllowed( int iBotClass ) const
|
||||
{
|
||||
return iBotClass > TF_CLASS_UNDEFINED && iBotClass < TF_LAST_NORMAL_CLASS && m_bAllowedClasses[iBotClass];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool CTFBotRoster::IsClassChangeAllowed() const
|
||||
{
|
||||
return m_bAllowClassChanges;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_roster.h
|
||||
// entity that dictates what classes a bot can choose when spawning
|
||||
// Tom Bui, April 2010
|
||||
|
||||
#ifndef TF_BOT_ROSTER_H
|
||||
#define TF_BOT_ROSTER_H
|
||||
|
||||
class CTFBotRoster : public CPointEntity
|
||||
{
|
||||
DECLARE_CLASS( CTFBotRoster, CPointEntity );
|
||||
public:
|
||||
DECLARE_DATADESC();
|
||||
|
||||
CTFBotRoster( void );
|
||||
virtual ~CTFBotRoster() {}
|
||||
|
||||
// input
|
||||
void InputSetAllowScout( inputdata_t &inputdata );
|
||||
void InputSetAllowSniper( inputdata_t &inputdata );
|
||||
void InputSetAllowSoldier( inputdata_t &inputdata );
|
||||
void InputSetAllowDemoman( inputdata_t &inputdata );
|
||||
void InputSetAllowMedic( inputdata_t &inputdata );
|
||||
void InputSetAllowHeavy( inputdata_t &inputdata );
|
||||
void InputSetAllowPyro( inputdata_t &inputdata );
|
||||
void InputSetAllowSpy( inputdata_t &inputdata );
|
||||
void InputSetAllowEngineer( inputdata_t &inputdata );
|
||||
|
||||
// misc.
|
||||
bool IsClassAllowed( int iBotClass ) const;
|
||||
bool IsClassChangeAllowed() const;
|
||||
|
||||
public:
|
||||
string_t m_teamName;
|
||||
bool m_bAllowClassChanges;
|
||||
bool m_bAllowedClasses[TF_LAST_NORMAL_CLASS];
|
||||
};
|
||||
|
||||
#endif // TF_BOT_ROSTER_H
|
||||
@@ -0,0 +1,163 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_spawner.cpp
|
||||
// Entity to spawn one or more templatized entities
|
||||
// Michael Booth, April 2011
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "tf_gamerules.h"
|
||||
#include "bot/map_entities/tf_spawner.h"
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
BEGIN_DATADESC( CTFSpawner )
|
||||
DEFINE_KEYFIELD( m_spawnCount, FIELD_INTEGER, "count" ),
|
||||
DEFINE_KEYFIELD( m_maxActiveCount, FIELD_INTEGER, "maxActive" ),
|
||||
DEFINE_KEYFIELD( m_spawnInterval, FIELD_FLOAT, "interval" ),
|
||||
DEFINE_KEYFIELD( m_templateName, FIELD_STRING, "template" ),
|
||||
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Reset", InputReset ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
|
||||
|
||||
DEFINE_OUTPUT( m_onExpended, "OnExpended" ),
|
||||
DEFINE_OUTPUT( m_onSpawned, "OnSpawned" ),
|
||||
DEFINE_OUTPUT( m_onKilled, "OnKilled" ),
|
||||
|
||||
DEFINE_THINKFUNC( SpawnerThink ),
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( tf_spawner, CTFSpawner );
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
CTFSpawner::CTFSpawner( void )
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFSpawner::Reset( void )
|
||||
{
|
||||
m_bExpended = false;
|
||||
m_spawnCountRemaining = 0;
|
||||
m_spawnedVector.RemoveAll();
|
||||
SetThink( NULL );
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFSpawner::InputReset( inputdata_t &inputdata )
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFSpawner::InputEnable( inputdata_t &inputdata )
|
||||
{
|
||||
if ( m_bExpended )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetThink( &CTFSpawner::SpawnerThink );
|
||||
|
||||
if ( m_spawnCountRemaining )
|
||||
{
|
||||
// already generating - don't restart count
|
||||
return;
|
||||
}
|
||||
|
||||
SetNextThink( gpGlobals->curtime );
|
||||
m_spawnCountRemaining = m_spawnCount;
|
||||
|
||||
m_template = dynamic_cast< CTFSpawnTemplate * >( gEntList.FindEntityByName( NULL, m_templateName ) );
|
||||
if ( m_template == NULL )
|
||||
{
|
||||
Warning( "%s failed to find template named '%s'\n", GetClassname(), STRING( m_templateName ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFSpawner::InputDisable( inputdata_t &inputdata )
|
||||
{
|
||||
// just stop thinking
|
||||
SetThink( NULL );
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFSpawner::OnKilled( CBaseEntity *dead )
|
||||
{
|
||||
m_onKilled.FireOutput( dead, this );
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFSpawner::SpawnerThink( void )
|
||||
{
|
||||
// still waiting for the real game to start?
|
||||
gamerules_roundstate_t roundState = TFGameRules()->State_Get();
|
||||
if ( roundState >= GR_STATE_TEAM_WIN || roundState < GR_STATE_PREROUND || TFGameRules()->IsInWaitingForPlayers() )
|
||||
{
|
||||
SetNextThink( gpGlobals->curtime + 1.0f );
|
||||
return;
|
||||
}
|
||||
|
||||
// clean up destroyed children
|
||||
for ( int i = 0; i < m_spawnedVector.Count(); )
|
||||
{
|
||||
CHandle< CBaseEntity > child = m_spawnedVector[i];
|
||||
|
||||
if ( child == NULL )
|
||||
{
|
||||
m_spawnedVector.FastRemove(i);
|
||||
m_onKilled.FireOutput( this, this );
|
||||
continue;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
if ( m_spawnedVector.Count() >= m_maxActiveCount )
|
||||
{
|
||||
// reached max simultanous active count
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( m_template == NULL )
|
||||
{
|
||||
// nothing to spawn!
|
||||
return;
|
||||
}
|
||||
|
||||
// spawn the entity
|
||||
CBaseEntity *child = m_template->Instantiate();
|
||||
if ( child )
|
||||
{
|
||||
m_spawnedVector.AddToTail( child );
|
||||
|
||||
child->SetAbsOrigin( GetAbsOrigin() );
|
||||
child->SetAbsAngles( GetAbsAngles() );
|
||||
child->SetOwnerEntity( this );
|
||||
|
||||
DispatchSpawn( child );
|
||||
m_onSpawned.FireOutput( child, this );
|
||||
|
||||
--m_spawnCountRemaining;
|
||||
if ( m_spawnCountRemaining )
|
||||
{
|
||||
SetNextThink( gpGlobals->curtime + m_spawnInterval );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetThink( NULL );
|
||||
m_onExpended.FireOutput( this, this );
|
||||
m_bExpended = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_spawner.h
|
||||
// Entity to spawn one or more templatized entities
|
||||
// Michael Booth, April 2011
|
||||
|
||||
#ifndef TF_SPAWNER_H
|
||||
#define TF_SPAWNER_H
|
||||
|
||||
//--------------------------------------------------------
|
||||
/**
|
||||
* Each particular type of entity the tf_spawner can create
|
||||
* has an associated template (derived from this class)
|
||||
* which defines its spawning location and initial properties.
|
||||
*/
|
||||
class CTFSpawnTemplate : public CPointEntity
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CTFSpawnTemplate, CPointEntity );
|
||||
|
||||
virtual ~CTFSpawnTemplate() { }
|
||||
|
||||
virtual CBaseEntity *Instantiate( void ) const = 0; // spawn an instance of this template
|
||||
};
|
||||
|
||||
|
||||
//--------------------------------------------------------
|
||||
class CTFSpawner : public CPointEntity
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CTFSpawner, CPointEntity );
|
||||
DECLARE_DATADESC();
|
||||
|
||||
CTFSpawner( void );
|
||||
virtual ~CTFSpawner() { }
|
||||
|
||||
void SpawnerThink( void );
|
||||
|
||||
// Input.
|
||||
void InputReset( inputdata_t &inputdata );
|
||||
void InputEnable( inputdata_t &inputdata );
|
||||
void InputDisable( inputdata_t &inputdata );
|
||||
|
||||
// Output
|
||||
void OnKilled( CBaseEntity *dead );
|
||||
|
||||
private:
|
||||
void Reset( void );
|
||||
|
||||
bool m_bExpended;
|
||||
int m_spawnCount;
|
||||
int m_spawnCountRemaining;
|
||||
int m_maxActiveCount;
|
||||
float m_spawnInterval;
|
||||
|
||||
string_t m_templateName;
|
||||
CHandle< CTFSpawnTemplate > m_template;
|
||||
|
||||
COutputEvent m_onSpawned;
|
||||
COutputEvent m_onExpended;
|
||||
COutputEvent m_onKilled;
|
||||
|
||||
CUtlVector< CHandle< CBaseEntity > > m_spawnedVector;
|
||||
};
|
||||
|
||||
|
||||
#endif // TF_SPAWNER_H
|
||||
@@ -0,0 +1,167 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_spawner_boss.cpp
|
||||
// Entity to spawn a Boss
|
||||
// Michael Booth, February 2011
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#ifdef OBSOLETE_USE_BOSS_ALPHA
|
||||
|
||||
#ifdef TF_RAID_MODE
|
||||
|
||||
#include "tf_gamerules.h"
|
||||
#include "tf_spawner_boss.h"
|
||||
#include "bot_npc/bot_npc.h"
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
BEGIN_DATADESC( CTFSpawnerBoss )
|
||||
DEFINE_KEYFIELD( m_spawnCount, FIELD_INTEGER, "count" ),
|
||||
DEFINE_KEYFIELD( m_maxActiveCount, FIELD_INTEGER, "maxActive" ),
|
||||
DEFINE_KEYFIELD( m_spawnInterval, FIELD_FLOAT, "interval" ),
|
||||
DEFINE_KEYFIELD( m_teamName, FIELD_STRING, "team" ),
|
||||
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
|
||||
|
||||
DEFINE_OUTPUT( m_onSpawned, "OnSpawned" ),
|
||||
DEFINE_OUTPUT( m_onExpended, "OnExpended" ),
|
||||
DEFINE_OUTPUT( m_onBotKilled, "OnBotKilled" ),
|
||||
DEFINE_OUTPUT( m_onBotStunned, "OnBotStunned" ),
|
||||
|
||||
DEFINE_THINKFUNC( SpawnerThink ),
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( tf_spawner_boss, CTFSpawnerBoss );
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
CTFSpawnerBoss::CTFSpawnerBoss( void )
|
||||
{
|
||||
m_isExpended = false;
|
||||
m_spawnCountRemaining = 0;
|
||||
|
||||
SetThink( NULL );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFSpawnerBoss::InputEnable( inputdata_t &inputdata )
|
||||
{
|
||||
if ( m_isExpended )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetThink( &CTFSpawnerBoss::SpawnerThink );
|
||||
|
||||
if ( m_spawnCountRemaining )
|
||||
{
|
||||
// already generating - don't restart count
|
||||
return;
|
||||
}
|
||||
SetNextThink( gpGlobals->curtime );
|
||||
m_spawnCountRemaining = m_spawnCount;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFSpawnerBoss::InputDisable( inputdata_t &inputdata )
|
||||
{
|
||||
// just stop thinking
|
||||
SetThink( NULL );
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFSpawnerBoss::OnBotKilled( CBotNPC *pBot )
|
||||
{
|
||||
m_onBotKilled.FireOutput( pBot, this );
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFSpawnerBoss::OnBotStunned( CBotNPC *pBot )
|
||||
{
|
||||
m_onBotStunned.FireOutput( pBot, this );
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CTFSpawnerBoss::SpawnerThink( void )
|
||||
{
|
||||
// still waiting for the real game to start?
|
||||
gamerules_roundstate_t roundState = TFGameRules()->State_Get();
|
||||
if ( roundState >= GR_STATE_TEAM_WIN || roundState < GR_STATE_PREROUND || TFGameRules()->IsInWaitingForPlayers() )
|
||||
{
|
||||
SetNextThink( gpGlobals->curtime + 1.0f );
|
||||
return;
|
||||
}
|
||||
|
||||
// remove invalid handles from our collection
|
||||
int i = 0;
|
||||
while( i < m_spawnedBotVector.Count() )
|
||||
{
|
||||
CHandle< CBotNPC > hBot = m_spawnedBotVector[i];
|
||||
if ( hBot == NULL )
|
||||
{
|
||||
m_spawnedBotVector.FastRemove(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
if ( m_spawnedBotVector.Count() >= m_maxActiveCount )
|
||||
{
|
||||
// maximum count reached - can't spawn any more
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
return;
|
||||
}
|
||||
|
||||
// spawn a bot
|
||||
CBotNPC *bot = (CBotNPC *)CreateEntityByName( "bot_boss" );
|
||||
if ( bot )
|
||||
{
|
||||
m_spawnedBotVector.AddToTail( bot );
|
||||
|
||||
int iTeam = TEAM_UNASSIGNED;
|
||||
if ( FStrEq( m_teamName.ToCStr(), "red" ) )
|
||||
{
|
||||
iTeam = TF_TEAM_RED;
|
||||
}
|
||||
else if ( FStrEq( m_teamName.ToCStr(), "blue" ) )
|
||||
{
|
||||
iTeam = TF_TEAM_BLUE;
|
||||
}
|
||||
bot->ChangeTeam( iTeam );
|
||||
|
||||
// match bot facing to that of spawner
|
||||
bot->SetAbsAngles( GetAbsAngles() );
|
||||
|
||||
bot->SetAbsOrigin( GetAbsOrigin() );
|
||||
|
||||
bot->SetSpawner( this );
|
||||
|
||||
DispatchSpawn( bot );
|
||||
|
||||
m_onSpawned.FireOutput( bot, this );
|
||||
|
||||
--m_spawnCountRemaining;
|
||||
if ( m_spawnCountRemaining )
|
||||
{
|
||||
SetNextThink( gpGlobals->curtime + m_spawnInterval );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetThink( NULL );
|
||||
m_onExpended.FireOutput( this, this );
|
||||
m_isExpended = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // TF_RAID_MODE
|
||||
|
||||
#endif // #ifdef OBSOLETE_USE_BOSS_ALPHA
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_spawner_boss.h
|
||||
// Entity to spawn a Boss
|
||||
// Michael Booth, February 2011
|
||||
|
||||
#ifndef TF_SPAWNER_BOSS_H
|
||||
#define TF_SPAWNER_BOSS_H
|
||||
|
||||
#ifdef OBSOLETE_USE_BOSS_ALPHA
|
||||
|
||||
#ifdef TF_RAID_MODE
|
||||
|
||||
class CBotNPC;
|
||||
|
||||
class CTFSpawnerBoss : public CPointEntity
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CTFSpawnerBoss, CPointEntity );
|
||||
DECLARE_DATADESC();
|
||||
|
||||
CTFSpawnerBoss( void );
|
||||
virtual ~CTFSpawnerBoss() { }
|
||||
|
||||
void SpawnerThink( void );
|
||||
|
||||
// Input.
|
||||
void InputEnable( inputdata_t &inputdata );
|
||||
void InputDisable( inputdata_t &inputdata );
|
||||
|
||||
// Output
|
||||
void OnBotKilled( CBotNPC *pBot );
|
||||
void OnBotStunned( CBotNPC *pBot );
|
||||
|
||||
private:
|
||||
bool m_isExpended;
|
||||
int m_spawnCount;
|
||||
int m_spawnCountRemaining;
|
||||
int m_maxActiveCount;
|
||||
float m_spawnInterval;
|
||||
string_t m_teamName;
|
||||
|
||||
COutputEvent m_onSpawned;
|
||||
COutputEvent m_onExpended;
|
||||
COutputEvent m_onBotKilled;
|
||||
COutputEvent m_onBotStunned;
|
||||
|
||||
CUtlVector< CHandle< CBotNPC > > m_spawnedBotVector;
|
||||
};
|
||||
|
||||
#endif // TF_RAID_MODE
|
||||
|
||||
#endif // OBSOLETE_USE_BOSS_ALPHA
|
||||
|
||||
#endif // TF_SPAWNER_BOSS_H
|
||||
Reference in New Issue
Block a user