442 lines
13 KiB
C++
442 lines
13 KiB
C++
|
#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;i<pGameResource->GetMaxMarineResources();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;i<pGameResource->GetMaxMarineResources();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;i<pGameResource->GetMaxMarineResources();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 );
|
||
|
}
|
||
|
}
|