718 lines
21 KiB
C++
718 lines
21 KiB
C++
#include "cbase.h"
|
|
#include "asw_spawn_manager.h"
|
|
#include "asw_marine.h"
|
|
#include "asw_marine_resource.h"
|
|
#include "asw_game_resource.h"
|
|
#include "iasw_spawnable_npc.h"
|
|
#include "asw_player.h"
|
|
#include "ai_network.h"
|
|
#include "ai_waypoint.h"
|
|
#include "ai_node.h"
|
|
#include "asw_director.h"
|
|
#include "asw_util_shared.h"
|
|
#include "asw_path_utils.h"
|
|
#include "asw_trace_filter_doors.h"
|
|
#include "asw_objective_escape.h"
|
|
#include "triggers.h"
|
|
#include "datacache/imdlcache.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
CASW_Spawn_Manager g_Spawn_Manager;
|
|
CASW_Spawn_Manager* ASWSpawnManager() { return &g_Spawn_Manager; }
|
|
|
|
#define CANDIDATE_ALIEN_HULL 11 // TODO: have this use the hull of the alien type we're spawning a horde of?
|
|
#define MARINE_NEAR_DISTANCE 740.0f
|
|
|
|
extern ConVar asw_director_debug;
|
|
ConVar asw_horde_min_distance("asw_horde_min_distance", "800", FCVAR_CHEAT, "Minimum distance away from the marines the horde can spawn" );
|
|
ConVar asw_horde_max_distance("asw_horde_max_distance", "1500", FCVAR_CHEAT, "Maximum distance away from the marines the horde can spawn" );
|
|
ConVar asw_max_alien_batch("asw_max_alien_batch", "10", FCVAR_CHEAT, "Max number of aliens spawned in a horde batch" );
|
|
ConVar asw_batch_interval("asw_batch_interval", "5", FCVAR_CHEAT, "Time between successive batches spawning in the same spot");
|
|
ConVar asw_candidate_interval("asw_candidate_interval", "1.0", FCVAR_CHEAT, "Interval between updating candidate spawning nodes");
|
|
ConVar asw_horde_class( "asw_horde_class", "asw_drone", FCVAR_CHEAT, "Alien class used when spawning hordes" );
|
|
|
|
// TODO: Notify the director when a horde is all killed?
|
|
// - currently you could try to spawn a 2nd horde while the first is still sitting out in the world
|
|
|
|
CASW_Spawn_Manager::CASW_Spawn_Manager()
|
|
{
|
|
|
|
}
|
|
|
|
CASW_Spawn_Manager::~CASW_Spawn_Manager()
|
|
{
|
|
|
|
}
|
|
|
|
// ==================================
|
|
// == Master list of alien classes ==
|
|
// ==================================
|
|
|
|
// NOTE: If you add new entries to this list, update the asw_spawner choices in swarm.fgd.
|
|
// Do not rearrange the order or you will be changing what spawns in all the maps.
|
|
|
|
ASW_Alien_Class_Entry g_Aliens[]=
|
|
{
|
|
ASW_Alien_Class_Entry( "asw_drone", HULL_MEDIUMBIG ),
|
|
ASW_Alien_Class_Entry( "asw_buzzer", HULL_TINY_CENTERED ),
|
|
ASW_Alien_Class_Entry( "asw_parasite", HULL_TINY ),
|
|
ASW_Alien_Class_Entry( "asw_shieldbug", HULL_LARGE ),
|
|
ASW_Alien_Class_Entry( "asw_grub", HULL_TINY ),
|
|
ASW_Alien_Class_Entry( "asw_drone_jumper", HULL_MEDIUMBIG ),
|
|
ASW_Alien_Class_Entry( "asw_harvester", HULL_HUMAN ),
|
|
ASW_Alien_Class_Entry( "asw_parasite_defanged", HULL_TINY ),
|
|
ASW_Alien_Class_Entry( "asw_queen", HULL_TINY ),
|
|
ASW_Alien_Class_Entry( "asw_boomer", HULL_HUMAN ),
|
|
ASW_Alien_Class_Entry( "asw_ranger", HULL_HUMAN ),
|
|
ASW_Alien_Class_Entry( "asw_mortarbug", HULL_LARGE ),
|
|
ASW_Alien_Class_Entry( "asw_shaman", HULL_LARGE ),
|
|
};
|
|
|
|
// Array indices of drones. Used by carnage mode.
|
|
const int g_nDroneClassEntry = 0;
|
|
const int g_nDroneJumperClassEntry = 5;
|
|
|
|
int CASW_Spawn_Manager::GetNumAlienClasses()
|
|
{
|
|
return NELEMS( g_Aliens );
|
|
}
|
|
|
|
ASW_Alien_Class_Entry* CASW_Spawn_Manager::GetAlienClass( int i )
|
|
{
|
|
Assert( i >= 0 && i < NELEMS( g_Aliens ) );
|
|
return &g_Aliens[ i ];
|
|
}
|
|
|
|
void CASW_Spawn_Manager::LevelInitPreEntity()
|
|
{
|
|
// init alien classes
|
|
for ( int i = 0; i < GetNumAlienClasses(); i++ )
|
|
{
|
|
GetAlienClass( i )->m_iszAlienClass = AllocPooledString( GetAlienClass( i )->m_pszAlienClass );
|
|
}
|
|
}
|
|
|
|
void CASW_Spawn_Manager::LevelInitPostEntity()
|
|
{
|
|
m_vecHordePosition = vec3_origin;
|
|
m_angHordeAngle = vec3_angle;
|
|
m_batchInterval.Invalidate();
|
|
m_CandidateUpdateTimer.Invalidate();
|
|
m_iHordeToSpawn = 0;
|
|
m_iAliensToSpawn = 0;
|
|
|
|
m_northCandidateNodes.Purge();
|
|
m_southCandidateNodes.Purge();
|
|
|
|
FindEscapeTriggers();
|
|
}
|
|
|
|
// finds all trigger_multiples linked to asw_objective_escape entities
|
|
void CASW_Spawn_Manager::FindEscapeTriggers()
|
|
{
|
|
m_EscapeTriggers.Purge();
|
|
|
|
// go through all escape objectives
|
|
CBaseEntity* pEntity = NULL;
|
|
while ( (pEntity = gEntList.FindEntityByClassname( pEntity, "asw_objective_escape" )) != NULL )
|
|
{
|
|
CASW_Objective_Escape* pObjective = dynamic_cast<CASW_Objective_Escape*>(pEntity);
|
|
if ( !pObjective )
|
|
continue;
|
|
|
|
const char *pszEscapeTargetName = STRING( pObjective->GetEntityName() );
|
|
|
|
CBaseEntity* pOtherEntity = NULL;
|
|
while ( (pOtherEntity = gEntList.FindEntityByClassname( pOtherEntity, "trigger_multiple" )) != NULL )
|
|
{
|
|
CTriggerMultiple *pTrigger = dynamic_cast<CTriggerMultiple*>( pOtherEntity );
|
|
if ( !pTrigger )
|
|
continue;
|
|
|
|
bool bAdded = false;
|
|
CBaseEntityOutput *pOutput = pTrigger->FindNamedOutput( "OnTrigger" );
|
|
if ( pOutput )
|
|
{
|
|
CEventAction *pAction = pOutput->GetFirstAction();
|
|
while ( pAction )
|
|
{
|
|
if ( !Q_stricmp( STRING( pAction->m_iTarget ), pszEscapeTargetName ) )
|
|
{
|
|
bAdded = true;
|
|
m_EscapeTriggers.AddToTail( pTrigger );
|
|
break;
|
|
}
|
|
pAction = pAction->m_pNext;
|
|
}
|
|
}
|
|
|
|
if ( !bAdded )
|
|
{
|
|
pOutput = pTrigger->FindNamedOutput( "OnStartTouch" );
|
|
if ( pOutput )
|
|
{
|
|
CEventAction *pAction = pOutput->GetFirstAction();
|
|
while ( pAction )
|
|
{
|
|
if ( !Q_stricmp( STRING( pAction->m_iTarget ), pszEscapeTargetName ) )
|
|
{
|
|
bAdded = true;
|
|
m_EscapeTriggers.AddToTail( pTrigger );
|
|
break;
|
|
}
|
|
pAction = pAction->m_pNext;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
Msg("Spawn manager found %d escape triggers\n", m_EscapeTriggers.Count() );
|
|
}
|
|
|
|
|
|
void CASW_Spawn_Manager::Update()
|
|
{
|
|
if ( m_iHordeToSpawn > 0 )
|
|
{
|
|
if ( m_vecHordePosition != vec3_origin && ( !m_batchInterval.HasStarted() || m_batchInterval.IsElapsed() ) )
|
|
{
|
|
int iToSpawn = MIN( m_iHordeToSpawn, asw_max_alien_batch.GetInt() );
|
|
int iSpawned = SpawnAlienBatch( asw_horde_class.GetString(), iToSpawn, m_vecHordePosition, m_angHordeAngle, MARINE_NEAR_DISTANCE );
|
|
m_iHordeToSpawn -= iSpawned;
|
|
if ( m_iHordeToSpawn <= 0 )
|
|
{
|
|
ASWDirector()->OnHordeFinishedSpawning();
|
|
m_vecHordePosition = vec3_origin;
|
|
}
|
|
else if ( iSpawned == 0 ) // if we failed to spawn any aliens, then try to find a new horde location
|
|
{
|
|
if ( !FindHordePosition() ) // if we failed to find a new location, just abort this horde
|
|
{
|
|
m_iHordeToSpawn = 0;
|
|
ASWDirector()->OnHordeFinishedSpawning();
|
|
m_vecHordePosition = vec3_origin;
|
|
}
|
|
}
|
|
m_batchInterval.Start( asw_batch_interval.GetFloat() );
|
|
}
|
|
}
|
|
|
|
if ( m_iAliensToSpawn > 0 )
|
|
{
|
|
if ( SpawnAlientAtRandomNode() )
|
|
m_iAliensToSpawn--;
|
|
}
|
|
}
|
|
|
|
void CASW_Spawn_Manager::AddAlien()
|
|
{
|
|
m_iAliensToSpawn++;
|
|
}
|
|
|
|
bool CASW_Spawn_Manager::SpawnAlientAtRandomNode()
|
|
{
|
|
UpdateCandidateNodes();
|
|
|
|
// decide if the alien is going to come from behind or in front
|
|
bool bNorth = RandomFloat() < 0.7f;
|
|
if ( m_northCandidateNodes.Count() <= 0 )
|
|
{
|
|
bNorth = false;
|
|
}
|
|
else if ( m_southCandidateNodes.Count() <= 0 )
|
|
{
|
|
bNorth = true;
|
|
}
|
|
|
|
CUtlVector<int> &candidateNodes = bNorth ? m_northCandidateNodes : m_southCandidateNodes;
|
|
|
|
if ( candidateNodes.Count() <= 0 )
|
|
return false;
|
|
|
|
const char *szAlienClass = "asw_drone";
|
|
Vector vecMins, vecMaxs;
|
|
GetAlienBounds( szAlienClass, vecMins, vecMaxs );
|
|
|
|
int iMaxTries = 10;
|
|
for ( int i=0 ; i<iMaxTries ; i++ )
|
|
{
|
|
int iChosen = RandomInt( 0, candidateNodes.Count() - 1);
|
|
CAI_Node *pNode = GetNetwork()->GetNode( candidateNodes[iChosen] );
|
|
if ( !pNode )
|
|
continue;
|
|
|
|
float flDistance = 0;
|
|
CASW_Marine *pMarine = dynamic_cast<CASW_Marine*>(UTIL_ASW_NearestMarine( pNode->GetPosition( CANDIDATE_ALIEN_HULL ), flDistance ));
|
|
if ( !pMarine )
|
|
return false;
|
|
|
|
// check if there's a route from this node to the marine(s)
|
|
AI_Waypoint_t *pRoute = ASWPathUtils()->BuildRoute( pNode->GetPosition( CANDIDATE_ALIEN_HULL ), pMarine->GetAbsOrigin(), NULL, 100 );
|
|
if ( !pRoute )
|
|
continue;
|
|
|
|
if ( bNorth && UTIL_ASW_DoorBlockingRoute( pRoute, true ) )
|
|
{
|
|
DeleteRoute( pRoute );
|
|
continue;
|
|
}
|
|
|
|
Vector vecSpawnPos = pNode->GetPosition( CANDIDATE_ALIEN_HULL ) + Vector( 0, 0, 32 );
|
|
if ( ValidSpawnPoint( vecSpawnPos, vecMins, vecMaxs, true, MARINE_NEAR_DISTANCE ) )
|
|
{
|
|
if ( SpawnAlienAt( szAlienClass, vecSpawnPos, vec3_angle ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
DeleteRoute( pRoute );
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CASW_Spawn_Manager::AddHorde( int iHordeSize )
|
|
{
|
|
m_iHordeToSpawn = iHordeSize;
|
|
|
|
if ( m_vecHordePosition == vec3_origin )
|
|
{
|
|
if ( !FindHordePosition() )
|
|
{
|
|
Msg("Error: Failed to find horde position\n");
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if ( asw_director_debug.GetBool() )
|
|
{
|
|
NDebugOverlay::Cross3D( m_vecHordePosition, 50.0f, 255, 128, 0, true, 40.0f );
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
CAI_Network* CASW_Spawn_Manager::GetNetwork()
|
|
{
|
|
return g_pBigAINet;
|
|
}
|
|
|
|
void CASW_Spawn_Manager::UpdateCandidateNodes()
|
|
{
|
|
// don't update too frequently
|
|
if ( m_CandidateUpdateTimer.HasStarted() && !m_CandidateUpdateTimer.IsElapsed() )
|
|
return;
|
|
|
|
m_CandidateUpdateTimer.Start( asw_candidate_interval.GetFloat() );
|
|
|
|
if ( !GetNetwork() || !GetNetwork()->NumNodes() )
|
|
{
|
|
m_vecHordePosition = vec3_origin;
|
|
Msg("Error: Can't spawn hordes as this map has no node network\n");
|
|
return;
|
|
}
|
|
|
|
CASW_Game_Resource *pGameResource = ASWGameResource();
|
|
if ( !pGameResource )
|
|
return;
|
|
|
|
Vector vecSouthMarine = vec3_origin;
|
|
Vector vecNorthMarine = vec3_origin;
|
|
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;
|
|
|
|
if ( vecSouthMarine == vec3_origin || vecSouthMarine.y > pMarine->GetAbsOrigin().y )
|
|
{
|
|
vecSouthMarine = pMarine->GetAbsOrigin();
|
|
}
|
|
if ( vecNorthMarine == vec3_origin || vecNorthMarine.y < pMarine->GetAbsOrigin().y )
|
|
{
|
|
vecNorthMarine = pMarine->GetAbsOrigin();
|
|
}
|
|
}
|
|
if ( vecSouthMarine == vec3_origin || vecNorthMarine == vec3_origin ) // no live marines
|
|
return;
|
|
|
|
int iNumNodes = GetNetwork()->NumNodes();
|
|
m_northCandidateNodes.Purge();
|
|
m_southCandidateNodes.Purge();
|
|
for ( int i=0 ; i<iNumNodes; i++ )
|
|
{
|
|
CAI_Node *pNode = GetNetwork()->GetNode( i );
|
|
if ( !pNode || pNode->GetType() != NODE_GROUND )
|
|
continue;
|
|
|
|
Vector vecPos = pNode->GetPosition( CANDIDATE_ALIEN_HULL );
|
|
|
|
// find the nearest marine to this node
|
|
float flDistance = 0;
|
|
CASW_Marine *pMarine = dynamic_cast<CASW_Marine*>(UTIL_ASW_NearestMarine( vecPos, flDistance ));
|
|
if ( !pMarine )
|
|
return;
|
|
|
|
if ( flDistance > asw_horde_max_distance.GetFloat() || flDistance < asw_horde_min_distance.GetFloat() )
|
|
continue;
|
|
|
|
// check node isn't in an exit trigger
|
|
bool bInsideEscapeArea = false;
|
|
for ( int d=0; d<m_EscapeTriggers.Count(); d++ )
|
|
{
|
|
if ( m_EscapeTriggers[d]->CollisionProp()->IsPointInBounds( vecPos ) )
|
|
{
|
|
bInsideEscapeArea = true;
|
|
break;
|
|
}
|
|
}
|
|
if ( bInsideEscapeArea )
|
|
continue;
|
|
|
|
if ( vecPos.y >= vecSouthMarine.y )
|
|
{
|
|
m_northCandidateNodes.AddToTail( i );
|
|
}
|
|
if ( vecPos.y <= vecNorthMarine.y )
|
|
{
|
|
m_southCandidateNodes.AddToTail( i );
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CASW_Spawn_Manager::FindHordePosition()
|
|
{
|
|
// need to find a suitable place from which to spawn a horde
|
|
// this place should:
|
|
// - be far enough away from the marines so the whole horde can spawn before the marines get there
|
|
// - should have a clear path to the marines
|
|
|
|
UpdateCandidateNodes();
|
|
|
|
// decide if the horde is going to come from behind or in front
|
|
bool bNorth = RandomFloat() < 0.7f;
|
|
if ( m_northCandidateNodes.Count() <= 0 )
|
|
{
|
|
bNorth = false;
|
|
}
|
|
else if ( m_southCandidateNodes.Count() <= 0 )
|
|
{
|
|
bNorth = true;
|
|
}
|
|
|
|
CUtlVector<int> &candidateNodes = bNorth ? m_northCandidateNodes : m_southCandidateNodes;
|
|
|
|
if ( candidateNodes.Count() <= 0 )
|
|
return false;
|
|
|
|
int iMaxTries = 10;
|
|
for ( int i=0 ; i<iMaxTries ; i++ )
|
|
{
|
|
int iChosen = RandomInt( 0, candidateNodes.Count() - 1);
|
|
CAI_Node *pNode = GetNetwork()->GetNode( candidateNodes[iChosen] );
|
|
if ( !pNode )
|
|
continue;
|
|
|
|
float flDistance = 0;
|
|
CASW_Marine *pMarine = dynamic_cast<CASW_Marine*>(UTIL_ASW_NearestMarine( pNode->GetPosition( CANDIDATE_ALIEN_HULL ), flDistance ));
|
|
if ( !pMarine )
|
|
return false;
|
|
|
|
// check if there's a route from this node to the marine(s)
|
|
AI_Waypoint_t *pRoute = ASWPathUtils()->BuildRoute( pNode->GetPosition( CANDIDATE_ALIEN_HULL ), pMarine->GetAbsOrigin(), NULL, 100 );
|
|
if ( !pRoute )
|
|
continue;
|
|
|
|
if ( bNorth && UTIL_ASW_DoorBlockingRoute( pRoute, true ) )
|
|
{
|
|
DeleteRoute( pRoute );
|
|
continue;
|
|
}
|
|
|
|
m_vecHordePosition = pNode->GetPosition( CANDIDATE_ALIEN_HULL ) + Vector( 0, 0, 32 );
|
|
DeleteRoute( pRoute );
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CASW_Spawn_Manager::LineBlockedByGeometry( const Vector &vecSrc, const Vector &vecEnd )
|
|
{
|
|
trace_t tr;
|
|
UTIL_TraceLine( vecSrc,
|
|
vecEnd, MASK_SOLID_BRUSHONLY,
|
|
NULL, COLLISION_GROUP_NONE, &tr );
|
|
|
|
return ( tr.fraction != 1.0f );
|
|
}
|
|
|
|
bool CASW_Spawn_Manager::GetAlienBounds( const char *szAlienClass, Vector &vecMins, Vector &vecMaxs )
|
|
{
|
|
int nCount = GetNumAlienClasses();
|
|
for ( int i = 0 ; i < nCount; i++ )
|
|
{
|
|
if ( !Q_stricmp( szAlienClass, GetAlienClass( i )->m_pszAlienClass ) )
|
|
{
|
|
vecMins = NAI_Hull::Mins( GetAlienClass( i )->m_nHullType );
|
|
vecMaxs = NAI_Hull::Maxs (GetAlienClass( i )->m_nHullType );
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CASW_Spawn_Manager::GetAlienBounds( string_t iszAlienClass, Vector &vecMins, Vector &vecMaxs )
|
|
{
|
|
int nCount = GetNumAlienClasses();
|
|
for ( int i = 0 ; i < nCount; i++ )
|
|
{
|
|
if ( iszAlienClass == GetAlienClass( i )->m_iszAlienClass )
|
|
{
|
|
vecMins = NAI_Hull::Mins( GetAlienClass( i )->m_nHullType );
|
|
vecMaxs = NAI_Hull::Maxs (GetAlienClass( i )->m_nHullType );
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// spawn a group of aliens at the target point
|
|
int CASW_Spawn_Manager::SpawnAlienBatch( const char* szAlienClass, int iNumAliens, const Vector &vecPosition, const QAngle &angle, float flMarinesBeyondDist )
|
|
{
|
|
int iSpawned = 0;
|
|
bool bCheckGround = true;
|
|
Vector vecMins = NAI_Hull::Mins(HULL_MEDIUMBIG);
|
|
Vector vecMaxs = NAI_Hull::Maxs(HULL_MEDIUMBIG);
|
|
GetAlienBounds( szAlienClass, vecMins, vecMaxs );
|
|
|
|
float flAlienWidth = vecMaxs.x - vecMins.x;
|
|
float flAlienDepth = vecMaxs.y - vecMins.y;
|
|
|
|
// spawn one in the middle
|
|
if ( ValidSpawnPoint( vecPosition, vecMins, vecMaxs, bCheckGround, flMarinesBeyondDist ) )
|
|
{
|
|
if ( SpawnAlienAt( szAlienClass, vecPosition, angle ) )
|
|
iSpawned++;
|
|
}
|
|
|
|
// try to spawn a 5x5 grid of aliens, starting at the centre and expanding outwards
|
|
Vector vecNewPos = vecPosition;
|
|
for ( int i=1; i<=5 && iSpawned < iNumAliens; i++ )
|
|
{
|
|
// spawn aliens along top of box
|
|
for ( int x=-i; x<=i && iSpawned < iNumAliens; x++ )
|
|
{
|
|
vecNewPos = vecPosition;
|
|
vecNewPos.x += x * flAlienWidth;
|
|
vecNewPos.y -= i * flAlienDepth;
|
|
if ( !LineBlockedByGeometry( vecPosition, vecNewPos) && ValidSpawnPoint( vecNewPos, vecMins, vecMaxs, bCheckGround, flMarinesBeyondDist ) )
|
|
{
|
|
if ( SpawnAlienAt( szAlienClass, vecNewPos, angle ) )
|
|
iSpawned++;
|
|
}
|
|
}
|
|
|
|
// spawn aliens along bottom of box
|
|
for ( int x=-i; x<=i && iSpawned < iNumAliens; x++ )
|
|
{
|
|
vecNewPos = vecPosition;
|
|
vecNewPos.x += x * flAlienWidth;
|
|
vecNewPos.y += i * flAlienDepth;
|
|
if ( !LineBlockedByGeometry( vecPosition, vecNewPos) && ValidSpawnPoint( vecNewPos, vecMins, vecMaxs, bCheckGround, flMarinesBeyondDist ) )
|
|
{
|
|
if ( SpawnAlienAt( szAlienClass, vecNewPos, angle ) )
|
|
iSpawned++;
|
|
}
|
|
}
|
|
|
|
// spawn aliens along left of box
|
|
for ( int y=-i + 1; y<i && iSpawned < iNumAliens; y++ )
|
|
{
|
|
vecNewPos = vecPosition;
|
|
vecNewPos.x -= i * flAlienWidth;
|
|
vecNewPos.y += y * flAlienDepth;
|
|
if ( !LineBlockedByGeometry( vecPosition, vecNewPos) && ValidSpawnPoint( vecNewPos, vecMins, vecMaxs, bCheckGround, flMarinesBeyondDist ) )
|
|
{
|
|
if ( SpawnAlienAt( szAlienClass, vecNewPos, angle ) )
|
|
iSpawned++;
|
|
}
|
|
}
|
|
|
|
// spawn aliens along right of box
|
|
for ( int y=-i + 1; y<i && iSpawned < iNumAliens; y++ )
|
|
{
|
|
vecNewPos = vecPosition;
|
|
vecNewPos.x += i * flAlienWidth;
|
|
vecNewPos.y += y * flAlienDepth;
|
|
if ( !LineBlockedByGeometry( vecPosition, vecNewPos) && ValidSpawnPoint( vecNewPos, vecMins, vecMaxs, bCheckGround, flMarinesBeyondDist ) )
|
|
{
|
|
if ( SpawnAlienAt( szAlienClass, vecNewPos, angle ) )
|
|
iSpawned++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return iSpawned;
|
|
}
|
|
|
|
CBaseEntity* CASW_Spawn_Manager::SpawnAlienAt(const char* szAlienClass, const Vector& vecPos, const QAngle &angle)
|
|
{
|
|
CBaseEntity *pEntity = NULL;
|
|
pEntity = CreateEntityByName( szAlienClass );
|
|
CAI_BaseNPC *pNPC = dynamic_cast<CAI_BaseNPC*>(pEntity);
|
|
|
|
if ( pNPC )
|
|
{
|
|
pNPC->AddSpawnFlags( SF_NPC_FALL_TO_GROUND );
|
|
}
|
|
|
|
// Strip pitch and roll from the spawner's angles. Pass only yaw to the spawned NPC.
|
|
QAngle angles = angle;
|
|
angles.x = 0.0;
|
|
angles.z = 0.0;
|
|
pEntity->SetAbsOrigin( vecPos );
|
|
pEntity->SetAbsAngles( angles );
|
|
UTIL_DropToFloor( pEntity, MASK_SOLID );
|
|
|
|
IASW_Spawnable_NPC* pSpawnable = dynamic_cast<IASW_Spawnable_NPC*>(pEntity);
|
|
ASSERT(pSpawnable);
|
|
if ( !pSpawnable )
|
|
{
|
|
Warning("NULL Spawnable Ent in CASW_Spawn_Manager::SpawnAlienAt! AlienClass = %s\n", szAlienClass);
|
|
UTIL_Remove(pEntity);
|
|
return NULL;
|
|
}
|
|
|
|
DispatchSpawn( pEntity );
|
|
pEntity->Activate();
|
|
|
|
// give our aliens the orders
|
|
pSpawnable->SetAlienOrders(AOT_MoveToNearestMarine, vec3_origin, NULL);
|
|
|
|
return pEntity;
|
|
}
|
|
|
|
bool CASW_Spawn_Manager::ValidSpawnPoint( const Vector &vecPosition, const Vector &vecMins, const Vector &vecMaxs, bool bCheckGround, float flMarineNearDistance )
|
|
{
|
|
// check if we can fit there
|
|
trace_t tr;
|
|
UTIL_TraceHull( vecPosition,
|
|
vecPosition + Vector( 0, 0, 1 ),
|
|
vecMins,
|
|
vecMaxs,
|
|
MASK_NPCSOLID,
|
|
NULL,
|
|
COLLISION_GROUP_NONE,
|
|
&tr );
|
|
|
|
if( tr.fraction != 1.0 )
|
|
return false;
|
|
|
|
// check there's ground underneath this point
|
|
if ( bCheckGround )
|
|
{
|
|
UTIL_TraceHull( vecPosition + Vector( 0, 0, 1 ),
|
|
vecPosition - Vector( 0, 0, 64 ),
|
|
vecMins,
|
|
vecMaxs,
|
|
MASK_NPCSOLID,
|
|
NULL,
|
|
COLLISION_GROUP_NONE,
|
|
&tr );
|
|
|
|
if( tr.fraction == 1.0 )
|
|
return false;
|
|
}
|
|
|
|
if ( flMarineNearDistance > 0 )
|
|
{
|
|
CASW_Game_Resource* pGameResource = ASWGameResource();
|
|
float distance = 0.0f;
|
|
for ( int i=0 ; i < pGameResource->GetMaxMarineResources() ; i++ )
|
|
{
|
|
CASW_Marine_Resource* pMR = pGameResource->GetMarineResource(i);
|
|
if ( pMR && pMR->GetMarineEntity() && pMR->GetMarineEntity()->GetHealth() > 0 )
|
|
{
|
|
distance = pMR->GetMarineEntity()->GetAbsOrigin().DistTo( vecPosition );
|
|
if ( distance < flMarineNearDistance )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CASW_Spawn_Manager::DeleteRoute( AI_Waypoint_t *pWaypointList )
|
|
{
|
|
while ( pWaypointList )
|
|
{
|
|
AI_Waypoint_t *pPrevWaypoint = pWaypointList;
|
|
pWaypointList = pWaypointList->GetNext();
|
|
delete pPrevWaypoint;
|
|
}
|
|
}
|
|
|
|
|
|
// creates a batch of aliens at the mouse cursor
|
|
void asw_alien_batch_f( const CCommand& args )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
|
|
bool allowPrecache = CBaseEntity::IsPrecacheAllowed();
|
|
CBaseEntity::SetAllowPrecache( true );
|
|
|
|
// find spawn point
|
|
CASW_Player* pPlayer = ToASW_Player(UTIL_GetCommandClient());
|
|
if (!pPlayer)
|
|
return;
|
|
CASW_Marine *pMarine = pPlayer->GetMarine();
|
|
if (!pMarine)
|
|
return;
|
|
trace_t tr;
|
|
Vector forward;
|
|
|
|
AngleVectors( pMarine->EyeAngles(), &forward );
|
|
UTIL_TraceLine(pMarine->EyePosition(),
|
|
pMarine->EyePosition() + forward * 300.0f,MASK_SOLID,
|
|
pMarine, COLLISION_GROUP_NONE, &tr );
|
|
if ( tr.fraction != 0.0 )
|
|
{
|
|
// trace to the floor from this spot
|
|
Vector vecSrc = tr.endpos;
|
|
tr.endpos.z += 12;
|
|
UTIL_TraceLine( vecSrc + Vector(0, 0, 12),
|
|
vecSrc - Vector( 0, 0, 512 ) ,MASK_SOLID,
|
|
pMarine, COLLISION_GROUP_NONE, &tr );
|
|
|
|
ASWSpawnManager()->SpawnAlienBatch( "asw_parasite", 25, tr.endpos, vec3_angle );
|
|
}
|
|
|
|
CBaseEntity::SetAllowPrecache( allowPrecache );
|
|
}
|
|
static ConCommand asw_alien_batch("asw_alien_batch", asw_alien_batch_f, "Creates a batch of aliens at the cursor", FCVAR_GAMEDLL | FCVAR_CHEAT);
|
|
|
|
|
|
void asw_alien_horde_f( const CCommand& args )
|
|
{
|
|
if ( args.ArgC() < 2 )
|
|
{
|
|
Msg("supply horde size!\n");
|
|
return;
|
|
}
|
|
if ( !ASWSpawnManager()->AddHorde( atoi(args[1]) ) )
|
|
{
|
|
Msg("Failed to add horde\n");
|
|
}
|
|
}
|
|
static ConCommand asw_alien_horde("asw_alien_horde", asw_alien_horde_f, "Creates a horde of aliens somewhere nearby", FCVAR_GAMEDLL | FCVAR_CHEAT); |