summaryrefslogtreecommitdiff
path: root/game/server/tf/bot/map_entities
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/tf/bot/map_entities
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/server/tf/bot/map_entities')
-rw-r--r--game/server/tf/bot/map_entities/tf_bot_generator.cpp470
-rw-r--r--game/server/tf/bot/map_entities/tf_bot_generator.h100
-rw-r--r--game/server/tf/bot/map_entities/tf_bot_hint.cpp128
-rw-r--r--game/server/tf/bot/map_entities/tf_bot_hint.h54
-rw-r--r--game/server/tf/bot/map_entities/tf_bot_hint_engineer_nest.cpp160
-rw-r--r--game/server/tf/bot/map_entities/tf_bot_hint_engineer_nest.h53
-rw-r--r--game/server/tf/bot/map_entities/tf_bot_hint_entity.cpp60
-rw-r--r--game/server/tf/bot/map_entities/tf_bot_hint_entity.h59
-rw-r--r--game/server/tf/bot/map_entities/tf_bot_hint_sentrygun.cpp42
-rw-r--r--game/server/tf/bot/map_entities/tf_bot_hint_sentrygun.h75
-rw-r--r--game/server/tf/bot/map_entities/tf_bot_hint_teleporter_exit.cpp19
-rw-r--r--game/server/tf/bot/map_entities/tf_bot_hint_teleporter_exit.h23
-rw-r--r--game/server/tf/bot/map_entities/tf_bot_proxy.cpp167
-rw-r--r--game/server/tf/bot/map_entities/tf_bot_proxy.h58
-rw-r--r--game/server/tf/bot/map_entities/tf_bot_roster.cpp107
-rw-r--r--game/server/tf/bot/map_entities/tf_bot_roster.h39
-rw-r--r--game/server/tf/bot/map_entities/tf_spawner.cpp163
-rw-r--r--game/server/tf/bot/map_entities/tf_spawner.h66
-rw-r--r--game/server/tf/bot/map_entities/tf_spawner_boss.cpp167
-rw-r--r--game/server/tf/bot/map_entities/tf_spawner_boss.h54
20 files changed, 2064 insertions, 0 deletions
diff --git a/game/server/tf/bot/map_entities/tf_bot_generator.cpp b/game/server/tf/bot/map_entities/tf_bot_generator.cpp
new file mode 100644
index 0000000..38fbc3b
--- /dev/null
+++ b/game/server/tf/bot/map_entities/tf_bot_generator.cpp
@@ -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 );
+}
+
+//------------------------------------------------------------------------------
diff --git a/game/server/tf/bot/map_entities/tf_bot_generator.h b/game/server/tf/bot/map_entities/tf_bot_generator.h
new file mode 100644
index 0000000..dfbc52a
--- /dev/null
+++ b/game/server/tf/bot/map_entities/tf_bot_generator.h
@@ -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
diff --git a/game/server/tf/bot/map_entities/tf_bot_hint.cpp b/game/server/tf/bot/map_entities/tf_bot_hint.cpp
new file mode 100644
index 0000000..b4dfa11
--- /dev/null
+++ b/game/server/tf/bot/map_entities/tf_bot_hint.cpp
@@ -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 );
+ }
+ }
+}
+
+
diff --git a/game/server/tf/bot/map_entities/tf_bot_hint.h b/game/server/tf/bot/map_entities/tf_bot_hint.h
new file mode 100644
index 0000000..4945145
--- /dev/null
+++ b/game/server/tf/bot/map_entities/tf_bot_hint.h
@@ -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
diff --git a/game/server/tf/bot/map_entities/tf_bot_hint_engineer_nest.cpp b/game/server/tf/bot/map_entities/tf_bot_hint_engineer_nest.cpp
new file mode 100644
index 0000000..7f21a71
--- /dev/null
+++ b/game/server/tf/bot/map_entities/tf_bot_hint_engineer_nest.cpp
@@ -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 );
+}
diff --git a/game/server/tf/bot/map_entities/tf_bot_hint_engineer_nest.h b/game/server/tf/bot/map_entities/tf_bot_hint_engineer_nest.h
new file mode 100644
index 0000000..9e5fdf5
--- /dev/null
+++ b/game/server/tf/bot/map_entities/tf_bot_hint_engineer_nest.h
@@ -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
diff --git a/game/server/tf/bot/map_entities/tf_bot_hint_entity.cpp b/game/server/tf/bot/map_entities/tf_bot_hint_entity.cpp
new file mode 100644
index 0000000..ffd2c74
--- /dev/null
+++ b/game/server/tf/bot/map_entities/tf_bot_hint_entity.cpp
@@ -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;
+}
diff --git a/game/server/tf/bot/map_entities/tf_bot_hint_entity.h b/game/server/tf/bot/map_entities/tf_bot_hint_entity.h
new file mode 100644
index 0000000..af0df70
--- /dev/null
+++ b/game/server/tf/bot/map_entities/tf_bot_hint_entity.h
@@ -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
diff --git a/game/server/tf/bot/map_entities/tf_bot_hint_sentrygun.cpp b/game/server/tf/bot/map_entities/tf_bot_hint_sentrygun.cpp
new file mode 100644
index 0000000..4cc804d
--- /dev/null
+++ b/game/server/tf/bot/map_entities/tf_bot_hint_sentrygun.cpp
@@ -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;
+}
diff --git a/game/server/tf/bot/map_entities/tf_bot_hint_sentrygun.h b/game/server/tf/bot/map_entities/tf_bot_hint_sentrygun.h
new file mode 100644
index 0000000..e2b0440
--- /dev/null
+++ b/game/server/tf/bot/map_entities/tf_bot_hint_sentrygun.h
@@ -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
diff --git a/game/server/tf/bot/map_entities/tf_bot_hint_teleporter_exit.cpp b/game/server/tf/bot/map_entities/tf_bot_hint_teleporter_exit.cpp
new file mode 100644
index 0000000..32beca2
--- /dev/null
+++ b/game/server/tf/bot/map_entities/tf_bot_hint_teleporter_exit.cpp
@@ -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 )
+{
+}
+
diff --git a/game/server/tf/bot/map_entities/tf_bot_hint_teleporter_exit.h b/game/server/tf/bot/map_entities/tf_bot_hint_teleporter_exit.h
new file mode 100644
index 0000000..a1af04a
--- /dev/null
+++ b/game/server/tf/bot/map_entities/tf_bot_hint_teleporter_exit.h
@@ -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
diff --git a/game/server/tf/bot/map_entities/tf_bot_proxy.cpp b/game/server/tf/bot/map_entities/tf_bot_proxy.cpp
new file mode 100644
index 0000000..560b341
--- /dev/null
+++ b/game/server/tf/bot/map_entities/tf_bot_proxy.cpp
@@ -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 );
+}
+
diff --git a/game/server/tf/bot/map_entities/tf_bot_proxy.h b/game/server/tf/bot/map_entities/tf_bot_proxy.h
new file mode 100644
index 0000000..6cdfbf4
--- /dev/null
+++ b/game/server/tf/bot/map_entities/tf_bot_proxy.h
@@ -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
diff --git a/game/server/tf/bot/map_entities/tf_bot_roster.cpp b/game/server/tf/bot/map_entities/tf_bot_roster.cpp
new file mode 100644
index 0000000..d07fc23
--- /dev/null
+++ b/game/server/tf/bot/map_entities/tf_bot_roster.cpp
@@ -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;
+}
diff --git a/game/server/tf/bot/map_entities/tf_bot_roster.h b/game/server/tf/bot/map_entities/tf_bot_roster.h
new file mode 100644
index 0000000..e3931ba
--- /dev/null
+++ b/game/server/tf/bot/map_entities/tf_bot_roster.h
@@ -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
diff --git a/game/server/tf/bot/map_entities/tf_spawner.cpp b/game/server/tf/bot/map_entities/tf_spawner.cpp
new file mode 100644
index 0000000..fa4f7c7
--- /dev/null
+++ b/game/server/tf/bot/map_entities/tf_spawner.cpp
@@ -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;
+ }
+ }
+}
diff --git a/game/server/tf/bot/map_entities/tf_spawner.h b/game/server/tf/bot/map_entities/tf_spawner.h
new file mode 100644
index 0000000..f57db89
--- /dev/null
+++ b/game/server/tf/bot/map_entities/tf_spawner.h
@@ -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
diff --git a/game/server/tf/bot/map_entities/tf_spawner_boss.cpp b/game/server/tf/bot/map_entities/tf_spawner_boss.cpp
new file mode 100644
index 0000000..4381dac
--- /dev/null
+++ b/game/server/tf/bot/map_entities/tf_spawner_boss.cpp
@@ -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
+
diff --git a/game/server/tf/bot/map_entities/tf_spawner_boss.h b/game/server/tf/bot/map_entities/tf_spawner_boss.h
new file mode 100644
index 0000000..13f247a
--- /dev/null
+++ b/game/server/tf/bot/map_entities/tf_spawner_boss.h
@@ -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