#include "cbase.h" #include "asw_arena.h" #include "asw_director.h" #include "asw_spawn_manager.h" #include "asw_gamerules.h" #include "asw_game_resource.h" #include "asw_marine_resource.h" #include "asw_marine.h" #include "asw_weapon.h" #include "ai_network.h" #include "missionchooser/iasw_mission_chooser.h" #include "missionchooser/iasw_random_missions.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" ConVar asw_director_debug("asw_director_debug", "0", FCVAR_CHEAT, "Displays director status on screen"); extern ConVar asw_intensity_far_range; ConVar asw_horde( "asw_horde", "0", FCVAR_CHEAT, "Should hordes spawn periodically?" ); ConVar asw_horde_interval_min("asw_horde_interval_min", "40", FCVAR_CHEAT, "Min time between hordes" ); ConVar asw_horde_interval_max("asw_horde_interval_max", "60", FCVAR_CHEAT, "Min time between hordes" ); ConVar asw_horde_size_min("asw_horde_size_min", "10", FCVAR_CHEAT, "Min horde size" ); ConVar asw_horde_size_max("asw_horde_size_max", "15", FCVAR_CHEAT, "Max horde size" ); ConVar asw_director_controls_spawns("asw_director_controls_spawns", "0", FCVAR_CHEAT, "If set, all spawners will be disabled and the director will place aliens"); ConVar asw_director_relaxed_min_time("asw_director_relaxed_min_time", "25", FCVAR_CHEAT, "Min time that director stops spawning aliens"); ConVar asw_director_relaxed_max_time("asw_director_relaxed_max_time", "40", FCVAR_CHEAT, "Max time that director stops spawning aliens"); ConVar asw_director_peak_min_time("asw_director_peak_min_time", "1", FCVAR_CHEAT, "Min time that director keeps spawning aliens when marine intensity has peaked"); ConVar asw_director_peak_max_time("asw_director_peak_max_time", "3", FCVAR_CHEAT, "Max time that director keeps spawning aliens when marine intensity has peaked"); ConVar asw_interval_min("asw_interval_min", "1.0f", FCVAR_CHEAT, "Director: Alien spawn interval will never go lower than this"); ConVar asw_interval_initial_min("asw_interval_initial_min", "5", FCVAR_CHEAT, "Director: Min time between alien spawns when first entering spawning state"); ConVar asw_interval_initial_max("asw_interval_initial_max", "7", FCVAR_CHEAT, "Director: Max time between alien spawns when first entering spawning state"); ConVar asw_interval_change_min("asw_interval_change_min", "0.9", FCVAR_CHEAT, "Director: Min scale applied to alien spawn interval each spawn"); ConVar asw_interval_change_max("asw_interval_change_max", "0.95", FCVAR_CHEAT, "Director: Max scale applied to alien spawn interval each spawn"); CASW_Director g_ASWDirector; CASW_Director* ASWDirector() { return &g_ASWDirector; } CASW_Director::CASW_Director( void ) : CAutoGameSystemPerFrame( "CASW_Director" ) { } CASW_Director::~CASW_Director() { } bool CASW_Director::Init() { m_bSpawningAliens = false; m_bReachedIntensityPeak = false; m_fTimeBetweenAliens = 0; m_AlienSpawnTimer.Invalidate(); m_SustainTimer.Invalidate(); m_HordeTimer.Invalidate(); m_IntensityUpdateTimer.Invalidate(); m_bInitialWait = true; m_bHordeInProgress = false; m_bFinale = false; return true; } void CASW_Director::Shutdown() { } void CASW_Director::LevelInitPreEntity() { if ( ASWSpawnManager() ) { ASWSpawnManager()->LevelInitPreEntity(); } } void CASW_Director::LevelInitPostEntity() { Init(); } void CASW_Director::FrameUpdatePreEntityThink() { } void CASW_Director::FrameUpdatePostEntityThink() { // only think when we're in-game if ( !ASWGameRules() || ASWGameRules()->GetGameState() != ASW_GS_INGAME ) return; UpdateIntensity(); if ( ASWSpawnManager() ) { ASWSpawnManager()->Update(); } UpdateMarineRooms(); UpdateHorde(); if ( asw_director_controls_spawns.GetBool() ) { UpdateSpawningState(); UpdateSpawning(); } } // randomly generated levels provide data about each room in the level // we check that here to react to special rooms void CASW_Director::UpdateMarineRooms() { CASW_Game_Resource *pGameResource = ASWGameResource(); if ( !pGameResource || !missionchooser || !missionchooser->RandomMissions()) return; for ( int i=0;iGetMaxMarineResources();i++ ) { CASW_Marine_Resource *pMR = pGameResource->GetMarineResource(i); if ( !pMR || !pMR->GetMarineEntity() || pMR->GetMarineEntity()->GetHealth() <= 0 ) continue; IASW_Room_Details* pRoom = missionchooser->RandomMissions()->GetRoomDetails( pMR->GetMarineEntity()->GetAbsOrigin() ); if ( !pRoom ) continue; if ( !m_bFinale && pRoom->HasTag( "Finale" ) ) { OnMarineEnteredExitRoom( pMR->GetMarineEntity() ); } } } // increase intensity as aliens are killed (particularly if they're close to the marines) void CASW_Director::Event_AlienKilled( CBaseEntity *pAlien, const CTakeDamageInfo &info ) { if ( !pAlien ) return; CASW_Game_Resource *pGameResource = ASWGameResource(); if ( !pGameResource ) return; bool bDangerous = pAlien->Classify() == CLASS_ASW_SHIELDBUG; // shieldbug bool bVeryDangerous = pAlien->Classify() == CLASS_ASW_QUEEN; // queen for ( int i=0;iGetMaxMarineResources();i++ ) { CASW_Marine_Resource *pMR = pGameResource->GetMarineResource(i); if ( !pMR ) continue; CASW_Marine *pMarine = pMR->GetMarineEntity(); if ( !pMarine || pMarine->GetHealth() <= 0 ) continue; CASW_Intensity::IntensityType stress = CASW_Intensity::MILD; if ( bVeryDangerous ) { stress = CASW_Intensity::EXTREME; } else if ( bDangerous ) { stress = CASW_Intensity::HIGH; } else { float distance = pMarine->GetAbsOrigin().DistTo( pAlien->GetAbsOrigin() ); if ( distance > asw_intensity_far_range.GetFloat() ) { stress = CASW_Intensity::MILD; } else { stress = CASW_Intensity::MODERATE; } } pMR->GetIntensity()->Increase( stress ); } ASWArena()->Event_AlienKilled( pAlien, info ); } // increase intensity as marines take damage void CASW_Director::MarineTookDamage( CASW_Marine *pMarine, const CTakeDamageInfo &info, bool bFriendlyFire ) { if ( !pMarine ) return; // friendly fire doesn't cause intensity increases if ( bFriendlyFire ) return; float flDamageRatio = info.GetDamage() / pMarine->GetHealth(); CASW_Intensity::IntensityType stress = CASW_Intensity::MILD; if ( flDamageRatio < 0.2f ) { stress = CASW_Intensity::MODERATE; } else if ( flDamageRatio < 0.5f ) { stress = CASW_Intensity::HIGH; } else { stress = CASW_Intensity::EXTREME; } if ( pMarine->GetMarineResource() ) { pMarine->GetMarineResource()->GetIntensity()->Increase( stress ); } } void CASW_Director::UpdateHorde() { if ( !asw_horde.GetBool() || !ASWSpawnManager() ) return; if ( asw_director_debug.GetInt() > 0 ) { if ( m_bHordeInProgress ) { engine->Con_NPrintf( 11, "Horde in progress. Left to spawn = %d", ASWSpawnManager()->GetHordeToSpawn() ); } else { engine->Con_NPrintf( 11, "Next Horde due: %f", m_HordeTimer.GetRemainingTime() ); } } if ( asw_director_controls_spawns.GetBool() && !m_bSpawningAliens ) return; if ( !m_bHordeInProgress ) { if ( !m_HordeTimer.HasStarted() ) { float flDuration = RandomFloat( asw_horde_interval_min.GetFloat(), asw_horde_interval_max.GetFloat() ); if ( m_bFinale ) { flDuration = RandomFloat( 5.0f, 10.0f ); } Msg( "Will be spawning a horde in %f seconds\n", flDuration ); m_HordeTimer.Start( flDuration ); } else if ( m_HordeTimer.IsElapsed() ) { int iNumAliens = RandomInt( asw_horde_size_min.GetInt(), asw_horde_size_max.GetInt() ); if ( ASWSpawnManager()->AddHorde( iNumAliens ) ) { Msg("Created horde of size %d\n", iNumAliens); m_bHordeInProgress = true; if ( ASWGameRules() ) { ASWGameRules()->BroadcastSound( "Spawner.Horde" ); } } else { m_HordeTimer.Invalidate(); } } } } void CASW_Director::OnHordeFinishedSpawning() { Msg("Horde finishes spawning\n"); m_bHordeInProgress = false; m_HordeTimer.Invalidate(); } void CASW_Director::UpdateSpawningState() { if ( m_bFinale ) // in finale, just keep spawning aliens forever { m_bSpawningAliens = true; engine->Con_NPrintf( 8, "%s: %f %s", m_bSpawningAliens ? "Spawning aliens" : "Relaxing", m_SustainTimer.HasStarted() ? m_SustainTimer.GetRemainingTime() : -1, "Finale" ); return; } //===================================================================================== // Main director rollercoaster logic // Spawns aliens until a peak intensity is reached, then gives the marines a breather //===================================================================================== if ( !m_bSpawningAliens ) // not spawning aliens, we're in a relaxed state { if ( !m_SustainTimer.HasStarted() ) { if ( GetMaxIntensity() < 1.0f ) // don't start our relax timer until the marines have left the peak { if ( m_bInitialWait ) // just do a short delay before starting combat at the beginning of a mission { m_SustainTimer.Start( RandomFloat( 3.0f, 16.0f ) ); m_bInitialWait = false; } else { m_SustainTimer.Start( RandomFloat( asw_director_relaxed_min_time.GetFloat(), asw_director_relaxed_max_time.GetFloat() ) ); } } } else if ( m_SustainTimer.IsElapsed() ) // TODO: Should check their intensity meters are below a certain threshold? Should probably also not wait if they run too far ahead { m_bSpawningAliens = true; m_bReachedIntensityPeak = false; m_SustainTimer.Invalidate(); m_fTimeBetweenAliens = 0; m_AlienSpawnTimer.Invalidate(); } } else // we're spawning aliens { if ( m_bReachedIntensityPeak ) { // hold the peak intensity for a while, then drop back to the relaxed state if ( !m_SustainTimer.HasStarted() ) { m_SustainTimer.Start( RandomFloat( asw_director_peak_min_time.GetFloat(), asw_director_peak_max_time.GetFloat() ) ); } else if ( m_SustainTimer.IsElapsed() ) { m_bSpawningAliens = false; m_SustainTimer.Invalidate(); } } else { if ( GetMaxIntensity() >= 1.0f ) { m_bReachedIntensityPeak = true; } } } if ( asw_director_debug.GetInt() > 0 ) { engine->Con_NPrintf( 8, "%s: %f %s", m_bSpawningAliens ? "Spawning aliens" : "Relaxing", m_SustainTimer.HasStarted() ? m_SustainTimer.GetRemainingTime() : -1, m_bReachedIntensityPeak ? "Peaked" : "Not peaked" ); } } void CASW_Director::UpdateSpawning() { if ( !m_bSpawningAliens ) { if ( asw_director_debug.GetInt() > 0 ) { engine->Con_NPrintf( 9, "Not spawning regular aliens" ); } return; } // spawn an alien every so often if ( !m_AlienSpawnTimer.HasStarted() || m_AlienSpawnTimer.IsElapsed() ) { if ( m_fTimeBetweenAliens == 0 ) { // initial time between alien spawns m_fTimeBetweenAliens = RandomFloat( asw_interval_initial_min.GetFloat(), asw_interval_initial_max.GetFloat() ); } else { // reduce the time by some random amount each interval m_fTimeBetweenAliens = MAX( asw_interval_min.GetFloat(), m_fTimeBetweenAliens * RandomFloat( asw_interval_change_min.GetFloat(), asw_interval_change_max.GetFloat() ) ); } if ( asw_director_debug.GetInt() > 0 ) { engine->Con_NPrintf( 9, "Regular spawn interval = %f", m_fTimeBetweenAliens ); } m_AlienSpawnTimer.Start( m_fTimeBetweenAliens ); if ( ASWSpawnManager() ) { ASWSpawnManager()->AddAlien(); } } } // if director is controlling alien spawns, then mapper set spawners ask permission before spawning bool CASW_Director::CanSpawnAlien( CASW_Spawner *pSpawner ) { return m_bSpawningAliens; } void CASW_Director::OnMarineStartedHack( CASW_Marine *pMarine, CBaseEntity *pComputer ) { CASW_Game_Resource *pGameResource = ASWGameResource(); if ( !pGameResource ) return; //Msg( " Marine started hack!\n" ); // reset intensity so we can have a big fight without relaxing immediately for ( int i=0;iGetMaxMarineResources();i++ ) { CASW_Marine_Resource *pMR = pGameResource->GetMarineResource(i); if ( !pMR ) continue; pMR->GetIntensity()->Reset(); } float flQuickStart = RandomFloat( 2.0f, 5.0f ); if ( m_HordeTimer.GetRemainingTime() > flQuickStart ) { m_HordeTimer.Start( flQuickStart ); } // TODO: Instead have some kind of 'is in a big fight' state? } // called when the first marine enters the exit room void CASW_Director::OnMarineEnteredExitRoom( CASW_Marine *pMarine ) { m_bFinale = true; Msg("Starting finale\n"); float flQuickStart = RandomFloat( 2.0f, 5.0f ); if ( m_HordeTimer.GetRemainingTime() > flQuickStart ) { m_HordeTimer.Start( flQuickStart ); } }