You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
555 lines
15 KiB
555 lines
15 KiB
#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" |
|
#include "asw_objective_escape.h" |
|
#include "asw_director_control.h" |
|
#include "asw_mission_manager.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; |
|
extern ConVar asw_spawning_enabled; |
|
|
|
extern ConVar asw_horde_override; |
|
extern ConVar asw_wanderer_override; |
|
ConVar asw_horde_interval_min("asw_horde_interval_min", "45", FCVAR_CHEAT, "Min time between hordes" ); |
|
ConVar asw_horde_interval_max("asw_horde_interval_max", "65", FCVAR_CHEAT, "Min time between hordes" ); |
|
ConVar asw_horde_size_min("asw_horde_size_min", "9", FCVAR_CHEAT, "Min horde size" ); |
|
ConVar asw_horde_size_max("asw_horde_size_max", "14", FCVAR_CHEAT, "Max horde size" ); |
|
|
|
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_bFiredEscapeRoom = false; |
|
|
|
m_bHordeInProgress = false; |
|
m_bFinale = false; |
|
|
|
// take horde/wanderer settings from director control |
|
CASW_Director_Control* pControl = static_cast<CASW_Director_Control*>( gEntList.FindEntityByClassname( NULL, "asw_director_control" ) ); |
|
if ( pControl ) |
|
{ |
|
m_bWanderersEnabled = pControl->m_bWanderersStartEnabled; |
|
m_bHordesEnabled = pControl->m_bHordesStartEnabled; |
|
m_bDirectorControlsSpawners = pControl->m_bDirectorControlsSpawners; |
|
} |
|
else |
|
{ |
|
m_bWanderersEnabled = false; |
|
m_bHordesEnabled = false; |
|
m_bDirectorControlsSpawners = false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
void CASW_Director::Shutdown() |
|
{ |
|
|
|
} |
|
|
|
void CASW_Director::LevelInitPreEntity() |
|
{ |
|
if ( ASWSpawnManager() ) |
|
{ |
|
ASWSpawnManager()->LevelInitPreEntity(); |
|
} |
|
} |
|
|
|
void CASW_Director::LevelInitPostEntity() |
|
{ |
|
Init(); |
|
|
|
if ( ASWSpawnManager() ) |
|
{ |
|
ASWSpawnManager()->LevelInitPostEntity(); |
|
} |
|
} |
|
|
|
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(); |
|
|
|
if ( !asw_spawning_enabled.GetBool() ) |
|
return; |
|
|
|
UpdateHorde(); |
|
|
|
UpdateSpawningState(); |
|
|
|
bool bWanderersEnabled = m_bWanderersEnabled || asw_wanderer_override.GetBool(); |
|
if ( bWanderersEnabled ) |
|
{ |
|
UpdateWanderers(); |
|
} |
|
} |
|
|
|
// 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( "Escape" ) ) |
|
{ |
|
UpdateMarineInsideEscapeRoom( 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_director_debug.GetInt() > 0 ) |
|
{ |
|
if ( m_bHordeInProgress ) |
|
{ |
|
engine->Con_NPrintf( 11, "Horde in progress. Left to spawn = %d", ASWSpawnManager()->GetHordeToSpawn() ); |
|
} |
|
engine->Con_NPrintf( 12, "Next Horde due: %f", m_HordeTimer.GetRemainingTime() ); |
|
|
|
engine->Con_NPrintf( 15, "Awake aliens: %d\n", ASWSpawnManager()->GetAwakeAliens() ); |
|
engine->Con_NPrintf( 16, "Awake drones: %d\n", ASWSpawnManager()->GetAwakeDrones() ); |
|
} |
|
|
|
bool bHordesEnabled = m_bHordesEnabled || asw_horde_override.GetBool(); |
|
if ( !bHordesEnabled || !ASWSpawnManager() ) |
|
return; |
|
|
|
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 ); |
|
} |
|
if ( asw_director_debug.GetBool() ) |
|
{ |
|
Msg( "Will be spawning a horde in %f seconds\n", flDuration ); |
|
} |
|
m_HordeTimer.Start( flDuration ); |
|
} |
|
else if ( m_HordeTimer.IsElapsed() ) |
|
{ |
|
if ( ASWSpawnManager()->GetAwakeDrones() < 25 ) |
|
{ |
|
int iNumAliens = RandomInt( asw_horde_size_min.GetInt(), asw_horde_size_max.GetInt() ); |
|
|
|
if ( ASWSpawnManager()->AddHorde( iNumAliens ) ) |
|
{ |
|
if ( asw_director_debug.GetBool() ) |
|
{ |
|
Msg("Created horde of size %d\n", iNumAliens); |
|
} |
|
m_bHordeInProgress = true; |
|
|
|
if ( ASWGameRules() ) |
|
{ |
|
ASWGameRules()->BroadcastSound( "Spawner.Horde" ); |
|
} |
|
m_HordeTimer.Invalidate(); |
|
} |
|
else |
|
{ |
|
// if we failed to find a horde position, try again shortly. |
|
m_HordeTimer.Start( RandomFloat( 10.0f, 16.0f ) ); |
|
} |
|
} |
|
else |
|
{ |
|
// if there are currently too many awake aliens, then wait 10 seconds before trying again |
|
m_HordeTimer.Start( 10.0f ); |
|
} |
|
} |
|
} |
|
|
|
void CASW_Director::OnHordeFinishedSpawning() |
|
{ |
|
if ( asw_director_debug.GetBool() ) |
|
{ |
|
Msg("Horde finishes spawning\n"); |
|
} |
|
m_bHordeInProgress = false; |
|
} |
|
|
|
void CASW_Director::UpdateSpawningState() |
|
{ |
|
if ( m_bFinale ) // in finale, just keep spawning aliens forever |
|
{ |
|
m_bSpawningAliens = true; |
|
|
|
if ( asw_director_debug.GetBool() ) |
|
{ |
|
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::UpdateWanderers() |
|
{ |
|
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() ) |
|
{ |
|
if ( ASWSpawnManager()->GetAwakeDrones() < 20 ) |
|
{ |
|
ASWSpawnManager()->AddAlien(); |
|
} |
|
} |
|
} |
|
} |
|
|
|
// if director is controlling alien spawns, then mapper set spawners ask permission before spawning |
|
bool CASW_Director::CanSpawnAlien( CASW_Spawner *pSpawner ) |
|
{ |
|
if ( !m_bDirectorControlsSpawners ) |
|
return true; |
|
|
|
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? |
|
} |
|
|
|
void CASW_Director::StartFinale() |
|
{ |
|
m_bFinale = true; |
|
m_bHordesEnabled = true; |
|
m_bWanderersEnabled = true; |
|
DevMsg("Starting finale\n"); |
|
|
|
float flQuickStart = RandomFloat( 2.0f, 5.0f ); |
|
if ( m_HordeTimer.GetRemainingTime() > flQuickStart ) |
|
{ |
|
m_HordeTimer.Start( flQuickStart ); |
|
} |
|
} |
|
|
|
void CASW_Director::UpdateMarineInsideEscapeRoom( CASW_Marine *pMarine ) |
|
{ |
|
if ( m_bFiredEscapeRoom ) |
|
return; |
|
|
|
// Verify rules and mission manager are available. |
|
if ( !ASWGameRules() || !ASWGameRules()->GetMissionManager() ) |
|
return; |
|
|
|
// Don't send the escape room output if there are objectives uncompleted. |
|
CASW_Objective_Escape *pEscape = ASWGameRules()->GetMissionManager()->GetEscapeObjective(); |
|
if ( pEscape && !pEscape->OtherObjectivesComplete() ) |
|
return; |
|
|
|
// Tell all director controls to send an output |
|
CBaseEntity* pEntity = NULL; |
|
while ( ( pEntity = gEntList.FindEntityByClassname( pEntity, "asw_director_control" ) ) != NULL ) |
|
{ |
|
CASW_Director_Control* pControl = static_cast<CASW_Director_Control*>( pEntity ); |
|
pControl->OnEscapeRoomStart( pMarine ); |
|
} |
|
|
|
m_bFiredEscapeRoom = true; |
|
} |
|
|
|
void CASW_Director::OnMissionStarted() |
|
{ |
|
// if we have wanders turned on, spawn a couple of encounters |
|
if ( asw_wanderer_override.GetBool() && ASWGameRules() ) |
|
{ |
|
ASWSpawnManager()->SpawnRandomShieldbug(); |
|
|
|
int nParasites = 1; |
|
switch( ASWGameRules()->GetSkillLevel() ) |
|
{ |
|
case 1: nParasites = RandomInt( 4, 6 ); break; |
|
default: |
|
case 2: nParasites = RandomInt( 4, 6 ); break; |
|
case 3: nParasites = RandomInt( 5, 7 ); break; |
|
case 4: nParasites = RandomInt( 5, 9 ); break; |
|
case 5: nParasites = RandomInt( 5, 10 ); break; |
|
} |
|
while ( nParasites > 0 ) |
|
{ |
|
int nParasitesInThisPack = RandomInt( 3, 6 ); |
|
if ( ASWSpawnManager()->SpawnRandomParasitePack( nParasitesInThisPack ) ) |
|
{ |
|
nParasites -= nParasitesInThisPack; |
|
} |
|
else |
|
{ |
|
break; |
|
} |
|
} |
|
} |
|
} |