#include "cbase.h" #include "baseentity.h" #include "asw_spawner.h" //#include "asw_simpleai_senses.h" #include "asw_marine.h" #include "asw_gamerules.h" #include "asw_marine_resource.h" #include "asw_game_resource.h" #include "entityapi.h" #include "entityoutput.h" #include "props.h" #include "asw_alien.h" #include "asw_buzzer.h" #include "asw_director.h" #include "asw_fail_advice.h" #include "asw_spawn_manager.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" LINK_ENTITY_TO_CLASS( asw_spawner, CASW_Spawner ); //ConVar asw_uber_drone_chance("asw_uber_drone_chance", "0.25f", FCVAR_CHEAT, "Chance of an uber drone spawning when playing in uber mode"); extern ConVar asw_debug_spawners; extern ConVar asw_drone_health; ConVar asw_spawning_enabled( "asw_spawning_enabled", "1", FCVAR_CHEAT, "If set to 0, asw_spawners won't spawn aliens" ); BEGIN_DATADESC( CASW_Spawner ) DEFINE_KEYFIELD( m_nMaxLiveAliens, FIELD_INTEGER, "MaxLiveAliens" ), DEFINE_KEYFIELD( m_nNumAliens, FIELD_INTEGER, "NumAliens" ), DEFINE_KEYFIELD( m_bInfiniteAliens, FIELD_BOOLEAN, "InfiniteAliens" ), DEFINE_KEYFIELD( m_flSpawnInterval, FIELD_FLOAT, "SpawnInterval" ), DEFINE_KEYFIELD( m_flSpawnIntervalJitter, FIELD_FLOAT, "SpawnIntervalJitter" ), DEFINE_KEYFIELD( m_AlienClassNum, FIELD_INTEGER, "AlienClass" ), DEFINE_KEYFIELD( m_SpawnerState, FIELD_INTEGER, "SpawnerState" ), DEFINE_INPUTFUNC( FIELD_VOID, "SpawnOneAlien", InputSpawnAlien ), DEFINE_INPUTFUNC( FIELD_VOID, "StartSpawning", InputStartSpawning ), DEFINE_INPUTFUNC( FIELD_VOID, "StopSpawning", InputStopSpawning ), DEFINE_INPUTFUNC( FIELD_VOID, "ToggleSpawning", InputToggleSpawning ), DEFINE_OUTPUT( m_OnAllSpawned, "OnAllSpawned" ), DEFINE_OUTPUT( m_OnAllSpawnedDead, "OnAllSpawnedDead" ), DEFINE_FIELD( m_nCurrentLiveAliens, FIELD_INTEGER ), DEFINE_FIELD( m_AlienClassName, FIELD_STRING ), DEFINE_THINKFUNC( SpawnerThink ), END_DATADESC() CASW_Spawner::CASW_Spawner() { m_hAlienOrderTarget = NULL; } CASW_Spawner::~CASW_Spawner() { } void CASW_Spawner::InitAlienClassName() { if ( m_AlienClassNum < 0 || m_AlienClassNum >= ASWSpawnManager()->GetNumAlienClasses() ) { m_AlienClassNum = 0; } m_AlienClassName = ASWSpawnManager()->GetAlienClass( m_AlienClassNum )->m_iszAlienClass; } void CASW_Spawner::Spawn() { InitAlienClassName(); BaseClass::Spawn(); m_flSpawnIntervalJitter /= 100.0f; m_flSpawnIntervalJitter = clamp(m_flSpawnIntervalJitter, 0, 100); SetSolid( SOLID_NONE ); m_nCurrentLiveAliens = 0; // trigger any begin state stuff SetSpawnerState(m_SpawnerState); } void CASW_Spawner::Precache() { BaseClass::Precache(); InitAlienClassName(); const char *pszNPCName = STRING( m_AlienClassName ); if ( !pszNPCName || !pszNPCName[0] ) { Warning("asw_spawner %s has no specified alien-to-spawn classname.\n", STRING(GetEntityName()) ); } else { UTIL_PrecacheOther( pszNPCName ); } } IASW_Spawnable_NPC* CASW_Spawner::SpawnAlien( const char *szAlienClassName, const Vector &vecHullMins, const Vector &vecHullMaxs ) { IASW_Spawnable_NPC *pSpawnable = BaseClass::SpawnAlien( szAlienClassName, vecHullMins, vecHullMaxs ); if ( pSpawnable ) { m_nCurrentLiveAliens++; if (!m_bInfiniteAliens) { m_nNumAliens--; if (m_nNumAliens <= 0) { SpawnedAllAliens(); } } else { ASWFailAdvice()->OnAlienSpawnedInfinite(); } } return pSpawnable; } bool CASW_Spawner::CanSpawn( const Vector &vecHullMins, const Vector &vecHullMaxs ) { if ( !asw_spawning_enabled.GetBool() ) return false; // too many alive already? if (m_nMaxLiveAliens>0 && m_nCurrentLiveAliens>=m_nMaxLiveAliens) return false; // have we run out? if (!m_bInfiniteAliens && m_nNumAliens<=0) return false; return BaseClass::CanSpawn( vecHullMins, vecHullMaxs ); } // called when we've spawned all the aliens we can, // spawner should go to sleep void CASW_Spawner::SpawnedAllAliens() { m_OnAllSpawned.FireOutput( this, this ); SetSpawnerState(SST_Finished); // disables think functions and so on } void CASW_Spawner::AlienKilled( CBaseEntity *pVictim ) { BaseClass::AlienKilled( pVictim ); m_nCurrentLiveAliens--; // If we're here, we're getting erroneous death messages from children we haven't created AssertMsg( m_nCurrentLiveAliens >= 0, "asw_spawner receiving child death notice but thinks has no children\n" ); if ( m_nCurrentLiveAliens <= 0 ) { // See if we've exhausted our supply of NPCs if (!m_bInfiniteAliens && m_nNumAliens <= 0 ) { if (asw_debug_spawners.GetBool()) Msg("%d OnAllSpawnedDead (%s)\n", entindex(), STRING(GetEntityName())); // Signal that all our children have been spawned and are now dead m_OnAllSpawnedDead.FireOutput( this, this ); } } } // mission started void CASW_Spawner::MissionStart() { if (asw_debug_spawners.GetBool()) Msg("Spawner mission start, always inf=%d infinitealiens=%d\n", HasSpawnFlags( ASW_SF_ALWAYS_INFINITE ), m_bInfiniteAliens ); // remove infinite spawns on easy mode if ( !HasSpawnFlags( ASW_SF_ALWAYS_INFINITE ) && ASWGameRules() && ASWGameRules()->GetSkillLevel() == 1 && m_bInfiniteAliens) { m_bInfiniteAliens = false; if (m_nNumAliens < 8) m_nNumAliens = 8; if (asw_debug_spawners.GetBool()) Msg(" removed infinite and set num aliens to %d\n", m_nNumAliens); } if (m_SpawnerState == SST_StartSpawningWhenMissionStart) SetSpawnerState(SST_Spawning); } void CASW_Spawner::SetSpawnerState(SpawnerState_t newState) { m_SpawnerState = newState; // begin state stuff if (m_SpawnerState == SST_Spawning) { SetThink ( &CASW_Spawner::SpawnerThink ); SetNextThink( gpGlobals->curtime ); } else if (m_SpawnerState == SST_Finished) { // Disable this forever. Don't kill it because it still gets death notices SetThink( NULL ); SetUse( NULL ); } else if (m_SpawnerState == SST_WaitForInputs) { SetThink( NULL ); // stop thinking } } void CASW_Spawner::SpawnerThink() { // calculate jitter float fInterval = random->RandomFloat(1.0f - m_flSpawnIntervalJitter, 1.0f + m_flSpawnIntervalJitter) * m_flSpawnInterval; SetNextThink( gpGlobals->curtime + fInterval ); if ( ASWDirector() && ASWDirector()->CanSpawnAlien( this ) ) { SpawnAlien( STRING( m_AlienClassName ), GetAlienMins(), GetAlienMaxs() ); } } // ===================== // Inputs // ===================== void CASW_Spawner::SpawnOneAlien() { SpawnAlien( STRING( m_AlienClassName ), GetAlienMins(), GetAlienMaxs() ); } void CASW_Spawner::InputSpawnAlien( inputdata_t &inputdata ) { if (m_SpawnerState == SST_WaitForInputs) { if ( ASWDirector() && ASWDirector()->CanSpawnAlien( this ) ) { SpawnOneAlien(); } } } void CASW_Spawner::InputStartSpawning( inputdata_t &inputdata ) { if (m_SpawnerState == SST_WaitForInputs) SetSpawnerState(SST_Spawning); } void CASW_Spawner::InputStopSpawning( inputdata_t &inputdata ) { if (m_SpawnerState == SST_Spawning) SetSpawnerState(SST_WaitForInputs); } void CASW_Spawner::InputToggleSpawning( inputdata_t &inputdata ) { if (m_SpawnerState == SST_Spawning) SetSpawnerState(SST_WaitForInputs); else if (m_SpawnerState == SST_WaitForInputs) SetSpawnerState(SST_Spawning); } const Vector& CASW_Spawner::GetAlienMins() { return NAI_Hull::Mins( ASWSpawnManager()->GetAlienClass( m_AlienClassNum )->m_nHullType ); } const Vector& CASW_Spawner::GetAlienMaxs() { return NAI_Hull::Maxs( ASWSpawnManager()->GetAlienClass( m_AlienClassNum )->m_nHullType ); } bool CASW_Spawner::ApplyCarnageMode( float fScaler, float fInvScaler ) { if ( m_AlienClassNum == g_nDroneClassEntry || m_AlienClassNum == g_nDroneJumperClassEntry ) { Msg( "[%d] Found a spawner set to spawn drones or drone jumpers\n", entindex()); Msg( " previous numaliens is %d max live is %d interval %f\n", m_nNumAliens, m_nMaxLiveAliens, m_flSpawnInterval ); m_nNumAliens *= fScaler; m_nMaxLiveAliens *= fScaler; m_flSpawnInterval *= fInvScaler; Msg( " Set its numaliens to %d max live to %d interval %f\n", m_nNumAliens, m_nMaxLiveAliens, m_flSpawnInterval ); return true; } Msg( "[%d] Found a spawner but it's not set to spawn drones or drone jumpers\n", entindex() ); return false; } void ASW_ApplyCarnage_f(float fScaler) { if ( fScaler <= 0 ) fScaler = 1.0f; float fInvScaler = 1.0f / fScaler; int iNewHealth = fInvScaler * 80.0f; // note: boosted health a bit here so this mode is harder than normal asw_drone_health.SetValue(iNewHealth); CBaseEntity* pEntity = NULL; int iSpawnersChanged = 0; while ((pEntity = gEntList.FindEntityByClassname( pEntity, "asw_spawner" )) != NULL) { CASW_Spawner* pSpawner = dynamic_cast(pEntity); if (pSpawner) { if ( pSpawner->ApplyCarnageMode( fScaler, fInvScaler ) ) { iSpawnersChanged++; } } } } void asw_carnage_f(const CCommand &args) { if ( args.ArgC() < 2 ) { Msg( "Please supply a scale\n" ); } ASW_ApplyCarnage_f( atof( args[1] ) ); } ConCommand asw_carnage( "asw_carnage", asw_carnage_f, "Scales the number of aliens each spawner will put out", FCVAR_CHEAT );