//========= 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; } } }