This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/Combat/spawn.cpp

1091 lines
32 KiB
C++
Raw Permalink Normal View History

/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Commando *
* *
* $Archive:: /Commando/Code/Combat/spawn.cpp $*
* *
* $Author:: Tom_s $*
* *
* $Modtime:: 11/19/01 10:25a $*
* *
* $Revision:: 107 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "spawn.h"
#include "wwhack.h"
#include "persistfactory.h"
#include "combatchunkid.h"
#include "simpledefinitionfactory.h"
#include "debug.h"
#include "definitionmgr.h"
#include "soldier.h"
#include "combat.h"
#include "playertype.h"
#include "gameobjmanager.h"
#include "soldierobserver.h"
#include "crandom.h"
#include "wwaudio.h"
#include "assets.h"
#include "objlibrary.h"
#include "movephys.h"
#include "scripts.h"
#include "wwmemlog.h"
#include "gametype.h"
#include "combatmaterialeffectmanager.h"
#include "transitioneffect.h"
#include "humanphys.h"
//
// Class statics
//
bool _Allow_Killing_Hibernating_Spawn = true;
#define AUTO_SPAWN_CHECK_DELAY 2.0f
/*
** SpawnDefClass
*/
DECLARE_FORCE_LINK( Spawner )
SimplePersistFactoryClass<SpawnerDefClass, CHUNKID_SPAWNER_DEF> _SpawnerDefPersistFactory;
DECLARE_DEFINITION_FACTORY(SpawnerDefClass, CLASSID_SPAWNER_DEF, "Spawner") _SpawnerDefDefFactory;
SpawnerDefClass::SpawnerDefClass( void ) :
PlayerType( PLAYERTYPE_NEUTRAL ),
SpawnMax( -1 ), // unlimited
SpawnDelay( 10 ),
SpawnDelayVariation( 0 ),
IsPrimary( false ),
IsSoldierStartup( false ),
PostVisualSpawnDelay( 0 ),
SpecialEffectsObjID( 0 ),
GotoSpawnerPos( false ),
GotoSpawnerPosPriority( 30 ),
TeleportFirstSpawn( true ),
StartsDisabled( false ),
KillHibernatingSpawn( true ),
ApplySpawnMaterialEffect( true ),
IsMultiplayWeaponSpawner( false )
{
DEFIDLIST_PARAM( SpawnerDefClass, SpawnDefinitionIDList, CLASSID_GAME_OBJECTS );
#ifdef PARAM_EDITING_ON
EnumParameterClass *param;
param = new EnumParameterClass( (int*)&PlayerType );
param->Set_Name ("PlayerType");
param->Add_Value ( "Unteamed", PLAYERTYPE_RENEGADE );
param->Add_Value ( "Nod", PLAYERTYPE_NOD );
param->Add_Value ( "GDI", PLAYERTYPE_GDI );
GENERIC_EDITABLE_PARAM(SpawnerDefClass,param)
#endif
EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_INT, SpawnMax );
EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_FLOAT, SpawnDelay );
EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_FLOAT, SpawnDelayVariation );
EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_BOOL, IsPrimary );
EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_BOOL, IsSoldierStartup );
EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_BOOL, GotoSpawnerPos );
EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_FLOAT, GotoSpawnerPosPriority );
EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_BOOL, TeleportFirstSpawn );
EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_BOOL, StartsDisabled );
EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_BOOL, KillHibernatingSpawn );
EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_BOOL, ApplySpawnMaterialEffect );
EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_BOOL, IsMultiplayWeaponSpawner );
GENERIC_DEFID_PARAM( SpawnerDefClass, SpecialEffectsObjID, CLASSID_GAME_OBJECT_DEF_SPECIAL_EFFECTS);
EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_FLOAT, PostVisualSpawnDelay );
SCRIPTLIST_PARAM (SpawnerDefClass, "Scripts", ScriptNameList, ScriptParameterList);
}
uint32 SpawnerDefClass::Get_Class_ID( void ) const
{
return CLASSID_SPAWNER_DEF;
}
PersistClass * SpawnerDefClass::Create( void ) const
{
SpawnerClass * obj = new SpawnerClass;
obj->Init( *this );
return obj;
}
enum {
CHUNKID_DEF_PARENT = 1013991542,
CHUNKID_DEF_VARIABLES,
MICROCHUNKID_DEF_DEFINITION_ID = 1,
XXXMICROCHUNKID_DEF_IS_COMMANDO_STARTING_POINT,
MICROCHUNKID_DEF_PLAYER_TYPE,
MICROCHUNKID_DEF_SPAWN_MAX,
MICROCHUNKID_DEF_SPAWN_DELAY,
XXXMICROCHUNKID_DEF_AUTO_SPAWN_RADIUS,
XXXMICROCHUNKID_DEF_ONE_AT_A_TIME,
MICROCHUNKID_DEF_SPAWN_DELAY_VARIATION,
MICROCHUNKID_DEF_IS_PRIMARY,
XXXMICROCHUNKID_DEF_EFFECT_MODEL_NAME,
XXXMICROCHUNKID_DEF_EFFECT_ANIMATION_NAME,
XXXMICROCHUNKID_DEF_SOUND_ID,
MICROCHUNKID_DEF_POST_EFFECT_SPAWN_DELAY,
MICROCHUNKID_DEF_SPECIAL_EFFECTS_OBJ_ID,
MICROCHUNKID_DEF_IS_SOLDIER_STARTUP,
MICROCHUNKID_DEF_GOTO_SPAWNER_POS,
MICROCHUNKID_DEF_TELEPORT_FIRST_SPAWN,
MICROCHUNKID_DEF_SCRIPT_NAME,
MICROCHUNKID_DEF_SCRIPT_PARAMETERS,
MICROCHUNKID_DEF_STARTS_DISABLED,
MICROCHUNKID_DEF_KILL_HIBERNATING_SPAWN,
MICROCHUNKID_DEF_GOTO_SPAWNER_POS_PRIORITY,
MICROCHUNKID_DEF_APPLY_SPAWN_MATERIAL_EFFECT,
MICROCHUNKID_DEF_IS_MULTIPLAY_WEAPON_SPAWNER
};
bool SpawnerDefClass::Save( ChunkSaveClass &csave )
{
int i;
csave.Begin_Chunk( CHUNKID_DEF_PARENT );
DefinitionClass::Save( csave );
csave.End_Chunk();
csave.Begin_Chunk( CHUNKID_DEF_VARIABLES );
for ( i = 0; i < SpawnDefinitionIDList.Count(); i++ ) {
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_DEFINITION_ID, SpawnDefinitionIDList[i] );
}
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_PLAYER_TYPE, PlayerType );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_SPAWN_MAX, SpawnMax );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_SPAWN_DELAY, SpawnDelay );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_SPAWN_DELAY_VARIATION, SpawnDelayVariation );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_IS_PRIMARY, IsPrimary );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_IS_SOLDIER_STARTUP, IsSoldierStartup );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_SPECIAL_EFFECTS_OBJ_ID, SpecialEffectsObjID );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_POST_EFFECT_SPAWN_DELAY, PostVisualSpawnDelay );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_GOTO_SPAWNER_POS, GotoSpawnerPos );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_GOTO_SPAWNER_POS_PRIORITY,GotoSpawnerPosPriority );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_TELEPORT_FIRST_SPAWN, TeleportFirstSpawn );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_STARTS_DISABLED, StartsDisabled );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_KILL_HIBERNATING_SPAWN, KillHibernatingSpawn );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_APPLY_SPAWN_MATERIAL_EFFECT, ApplySpawnMaterialEffect );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_IS_MULTIPLAY_WEAPON_SPAWNER, IsMultiplayWeaponSpawner );
WWASSERT( ScriptNameList.Count() == ScriptParameterList.Count() );
for ( i = 0; i < ScriptNameList.Count(); i++ ) {
WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_DEF_SCRIPT_NAME, ScriptNameList[i] );
WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_DEF_SCRIPT_PARAMETERS, ScriptParameterList[i] );
}
csave.End_Chunk();
return true;
}
bool SpawnerDefClass::Load( ChunkLoadClass &cload )
{
WWASSERT( ScriptNameList.Count() == ScriptParameterList.Count() );
WWASSERT( SpawnDefinitionIDList.Count() == 0 );
StringClass str;
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case CHUNKID_DEF_PARENT:
DefinitionClass::Load( cload );
break;
case CHUNKID_DEF_VARIABLES:
while (cload.Open_Micro_Chunk()) {
switch(cload.Cur_Micro_Chunk_ID()) {
case MICROCHUNKID_DEF_DEFINITION_ID:
{
int def_id;
cload.Read(&def_id,sizeof(def_id));
SpawnDefinitionIDList.Add( def_id );
}
break;
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_PLAYER_TYPE, PlayerType );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_SPAWN_MAX, SpawnMax );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_SPAWN_DELAY, SpawnDelay );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_SPAWN_DELAY_VARIATION, SpawnDelayVariation );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_IS_PRIMARY, IsPrimary );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_IS_SOLDIER_STARTUP, IsSoldierStartup );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_POST_EFFECT_SPAWN_DELAY, PostVisualSpawnDelay );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_SPECIAL_EFFECTS_OBJ_ID, SpecialEffectsObjID );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_GOTO_SPAWNER_POS, GotoSpawnerPos );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_GOTO_SPAWNER_POS_PRIORITY, GotoSpawnerPosPriority );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_TELEPORT_FIRST_SPAWN, TeleportFirstSpawn );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_STARTS_DISABLED, StartsDisabled );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_KILL_HIBERNATING_SPAWN, KillHibernatingSpawn );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_APPLY_SPAWN_MATERIAL_EFFECT, ApplySpawnMaterialEffect );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_IS_MULTIPLAY_WEAPON_SPAWNER, IsMultiplayWeaponSpawner );
case MICROCHUNKID_DEF_SCRIPT_NAME:
LOAD_MICRO_CHUNK_WWSTRING( cload, str );
ScriptNameList.Add( str );
break;
case MICROCHUNKID_DEF_SCRIPT_PARAMETERS:
LOAD_MICRO_CHUNK_WWSTRING( cload, str );
ScriptParameterList.Add( str );
break;
default:
Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__));
break;
}
cload.Close_Micro_Chunk();
}
break;
default:
Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
break;
}
cload.Close_Chunk();
}
WWASSERT( ScriptNameList.Count() == ScriptParameterList.Count() );
return true;
}
const PersistFactoryClass & SpawnerDefClass::Get_Factory( void ) const
{
return _SpawnerDefPersistFactory;
}
/*
** SpawnClass
*/
SimplePersistFactoryClass<SpawnerClass, CHUNKID_SPAWNER> _SpawnerPersistFactory;
const PersistFactoryClass & SpawnerClass::Get_Factory (void) const
{
return _SpawnerPersistFactory;
}
SpawnerClass::SpawnerClass( void ) :
ID( 0 ),
TM( 1 ),
SpawnTM( 1 ),
Definition( NULL ),
SpawnCount( 0 ),
SpawnDelayTimer( 0 ),
Enabled( true )
{
SpawnManager::Add_Spawner( this );
}
SpawnerClass::~SpawnerClass( void )
{
SpawnManager::Remove_Spawner( this );
}
void SpawnerClass::Init( const SpawnerDefClass & definition )
{
Definition = &definition;
Enabled = !definition.StartsDisabled;
}
enum {
CHUNKID_PARENT = 1014991053,
CHUNKID_VARIABLES,
CHUNKID_LAST_SPAWN,
MICROCHUNKID_ID = 1,
MICROCHUNKID_TM,
MICROCHUNKID_DEFINITION_ID,
MICROCHUNKID_SPAWN_COUNT,
MICROCHUNKID_SPAWN_DELAY_TIMER,
MICROCHUNKID_ENABLED,
MICROCHUNKID_SPAWN_POINT_ENTRY,
XXXMICROCHUNKID_REAL_SPAWN_TIMER,
MICROCHUNKID_SPAWN_TM,
MICROCHUNKID_SCRIPT_NAME,
MICROCHUNKID_SCRIPT_PARAMETERS,
};
bool SpawnerClass::Save( ChunkSaveClass & csave )
{
csave.Begin_Chunk( CHUNKID_PARENT );
PersistClass::Save( csave );
csave.End_Chunk();
csave.Begin_Chunk( CHUNKID_VARIABLES );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ID, ID );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TM, TM );
int id = Get_Definition().Get_ID();
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEFINITION_ID, id );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SPAWN_COUNT, SpawnCount );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SPAWN_DELAY_TIMER, SpawnDelayTimer );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ENABLED, Enabled );
for ( int i = 0; i < SpawnPointList.Count(); i++ ) {
Matrix3D tm = SpawnPointList[i];
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SPAWN_POINT_ENTRY, tm );
}
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SPAWN_TM, SpawnTM );
WWASSERT( ScriptNameList.Count() == ScriptParameterList.Count() );
for ( i = 0; i < ScriptNameList.Count(); i++ ) {
WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_SCRIPT_NAME, ScriptNameList[i] );
WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_SCRIPT_PARAMETERS, ScriptParameterList[i] );
}
csave.End_Chunk();
if ( LastSpawn.Get_Ptr() != NULL ) {
csave.Begin_Chunk( CHUNKID_LAST_SPAWN );
LastSpawn.Save( csave );
csave.End_Chunk();
}
return true;
}
bool SpawnerClass::Load( ChunkLoadClass & cload )
{
WWASSERT( ScriptNameList.Count() == ScriptParameterList.Count() );
StringClass str;
Matrix3D tm;
WWASSERT( SpawnPointList.Length() == 0 );
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case CHUNKID_PARENT:
PersistClass::Load( cload );
break;
case CHUNKID_VARIABLES:
while (cload.Open_Micro_Chunk()) {
switch(cload.Cur_Micro_Chunk_ID()) {
READ_MICRO_CHUNK( cload, MICROCHUNKID_ID, ID );
READ_MICRO_CHUNK( cload, MICROCHUNKID_TM, TM );
READ_MICRO_CHUNK( cload, MICROCHUNKID_SPAWN_COUNT, SpawnCount );
READ_MICRO_CHUNK( cload, MICROCHUNKID_SPAWN_DELAY_TIMER, SpawnDelayTimer );
READ_MICRO_CHUNK( cload, MICROCHUNKID_ENABLED, Enabled );
READ_MICRO_CHUNK( cload, MICROCHUNKID_SPAWN_TM, SpawnTM );
case MICROCHUNKID_DEFINITION_ID:
int definition_id;
LOAD_MICRO_CHUNK( cload, definition_id );
WWASSERT( Definition == NULL );
Definition = (const SpawnerDefClass *)DefinitionMgrClass::Find_Definition( definition_id );
WWASSERT( Definition != NULL );
break;
case MICROCHUNKID_SPAWN_POINT_ENTRY:
LOAD_MICRO_CHUNK( cload, tm );
SpawnPointList.Add( tm );
break;
case MICROCHUNKID_SCRIPT_NAME:
LOAD_MICRO_CHUNK_WWSTRING( cload, str );
ScriptNameList.Add( str );
break;
case MICROCHUNKID_SCRIPT_PARAMETERS:
LOAD_MICRO_CHUNK_WWSTRING( cload, str );
ScriptParameterList.Add( str );
break;
default:
Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__));
break;
}
cload.Close_Micro_Chunk();
}
break;
case CHUNKID_LAST_SPAWN:
LastSpawn.Load( cload );
break;
default:
Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
break;
}
cload.Close_Chunk();
}
WWASSERT( ScriptNameList.Count() == ScriptParameterList.Count() );
return true;
}
bool SpawnerClass::Determine_Spawn_TM( PhysicalGameObj * obj )
{
bool spawnable = false;
MoveablePhysClass * phys_obj = NULL;
if ( obj->Peek_Physical_Object() ) {
phys_obj = obj->Peek_Physical_Object()->As_MoveablePhysClass();
}
// If we have spawn points, try and find one where the object can be placed
if ( SpawnPointList.Count() > 0 ) {
// Find human player nearest spawner
Vector3 nearest_human_player_pos( 0,0,0 );
float commando_range = 10000000;
SLNode<SmartGameObj> *objnode;
for ( objnode = GameObjManager::Get_Smart_Game_Obj_List()->Head(); objnode; objnode = objnode->Next()) {
SoldierGameObj * soldier = objnode->Data()->As_SoldierGameObj();
if ( soldier && soldier->Is_Human_Controlled() ) {
Vector3 pos;
soldier->Get_Position( &pos );
pos -= TM.Get_Translation();
float range = pos.Length();
if ( range < commando_range ) {
soldier->Get_Position( &nearest_human_player_pos );
commando_range = range;
}
}
}
// Find a safe spawnable point furthest from the commando
float best_range = 0;
// For every spawn point
for ( int index = 0; index < SpawnPointList.Count(); index++ ) {
bool is_safe = true; // Assume safe
Matrix3D safe_spot = SpawnPointList[index];
if ( phys_obj ) {
is_safe = phys_obj->Can_Teleport_And_Stand( SpawnPointList[index], &safe_spot );
}
if ( is_safe ) {
// check range for furthest from commando
Vector3 diff = safe_spot.Get_Translation() - nearest_human_player_pos;
float range = diff.Length();
if ( range > best_range ) {
best_range = range;
SpawnTM = safe_spot;
spawnable = true;
}
}
}
} else {
// Lets see if we can spawn at TM..
// Only check teleport for armed objs
if ( phys_obj && obj->As_ArmedGameObj() != NULL ) {
Matrix3D safe_spot = TM;
if ( phys_obj->Can_Teleport_And_Stand( TM, &safe_spot ) ) {
SpawnTM = safe_spot;
spawnable = true;
}
} else {
SpawnTM = TM;
spawnable = true;
}
}
if ( spawnable ) {
obj->Set_Transform( SpawnTM );
}
return spawnable;
}
PhysicalGameObj * SpawnerClass::Create_Spawned_Object( int obj_id )
{
WWMEMLOG(MEM_GAMEDATA);
int spawn_id = obj_id;
if ( obj_id == -1 ) {
/*
** Randomly select an object to spawn
*/
int spawn_count = Get_Definition().SpawnDefinitionIDList.Count();
if ( spawn_count > 0 ) {
int spawn_index = FreeRandom.Get_Int( spawn_count );
spawn_id = Get_Definition().SpawnDefinitionIDList[ spawn_index ];
}
}
/*
** Create the object
*/
PhysicalGameObj * obj = NULL;
if ( spawn_id != -1 ) {
PhysicalGameObjDef * def = (PhysicalGameObjDef *)DefinitionMgrClass::Find_Definition( spawn_id );
if ( def == NULL ) {
Debug_Say(( "Spawner %s failed to create a spawn id %d\n", Get_Definition().Get_Name(), spawn_id ));
}
WWASSERT( def );
if ( def != NULL ) {
obj = (PhysicalGameObj *)def->Create();
if ( obj == NULL ) {
Debug_Say(( "Spawner Failed to create %s\n", def->Get_Name() ));
}
}
}
/*
** If the definition calls for it, add a material effect to the object
*/
if ((obj != NULL) && (Get_Definition().ApplySpawnMaterialEffect)) {
PhysClass * physobj = obj->Peek_Physical_Object();
if (physobj != NULL) {
TransitionEffectClass * effect = CombatMaterialEffectManager::Get_Spawn_Effect();
physobj->Add_Effect_To_Me(effect);
REF_PTR_RELEASE(effect);
}
}
return obj;
}
void SpawnerClass::Check_Auto_Spawn( float dtime )
{
if ( LastSpawn.Get_Ptr() == NULL ) { // If our last spawn is gone
bool spawn_ok = false;
if ( Get_Definition().SpawnMax < 0 ||
SpawnCount < Get_Definition().SpawnMax ) {
spawn_ok = true;
}
if ( Enabled && spawn_ok ) {
if ( SpawnDelayTimer > 0 ) {
SpawnDelayTimer -= dtime;
}
if ( SpawnDelayTimer <= 0 ) {
Spawn();
}
}
} else if ( Get_Definition().KillHibernatingSpawn && _Allow_Killing_Hibernating_Spawn ) {
// If our last spawn is alive, but hibernating
PhysicalGameObj * spawn = (PhysicalGameObj *)LastSpawn.Get_Ptr();
if ( spawn->Is_Hibernating() ) {
spawn->Set_Delete_Pending(); // Kill em
SpawnCount--;
SpawnDelayTimer = 0; // no delay to bring him back
#if 0
if ( Enabled ) {
Debug_Say(( "Killing Hibernating Spawn from an Enabled spawner (id %d)\n", Get_ID() ));
} else {
Debug_Say(( "Killing Hibernating Spawn (spawner id %d)\n", Get_ID() ));
}
#endif
}
}
}
PhysicalGameObj * SpawnerClass::Spawn_Object( int obj_def_id )
{
return Spawn( obj_def_id );
}
bool SpawnerClass::Can_Spawn_Object( int obj_def_id )
{
bool retval = false;
//
// Loop over all the objects this spawner can create
//
const DynamicVectorClass<int> &def_list = Definition->Get_Spawn_Definition_ID_List ();
for (int index = 0; index < def_list.Count (); index ++) {
//
// Is this the object we're looking for?
//
if ( def_list[index] == obj_def_id ) {
retval = true;
break;
}
}
return retval;
}
PhysicalGameObj * SpawnerClass::Spawn( int obj_id )
{
WWASSERT( CombatManager::I_Am_Server() );
// Pick what will be spawned
PhysicalGameObj * obj = Create_Spawned_Object( obj_id );
WWASSERT( obj );
// Choose a spawn location
bool spawnable = Determine_Spawn_TM( obj );
if ( !spawnable ) {
Debug_Say(( "Couldn't find a place to spawn this object ID %d\n", Get_ID() ));
obj->Set_Delete_Pending();
return NULL;
}
obj->Start_Observers();
if ( Get_Definition().GotoSpawnerPos ) {
// Teleport the first spawn
if ( Get_Definition().TeleportFirstSpawn && SpawnCount == 0 ) {
obj->Set_Transform( TM );
} else {
// Give initial goto command / don't let him hibernate
SoldierGameObj * soldier = obj->As_SoldierGameObj();
if ( soldier != NULL ) {
ActionParamsStruct parameters;
parameters.Priority = Get_Definition().GotoSpawnerPosPriority;
parameters.ObserverID = 0;
parameters.MoveLocation = TM.Get_Translation();
parameters.MoveSpeed = 1;
parameters.MoveCrouched = false;
parameters.MoveArrivedDistance = 1;
soldier->Get_Action()->Goto( parameters );
soldier->Reset_Hibernating();
}
}
}
// Keep track of the last spawned object
LastSpawn = obj;
// Create the special effects game object that will be responsible for playing
// an animation and sound
if ( Get_Definition().SpecialEffectsObjID != 0 ) {
PhysicalGameObj *effect_obj = ObjectLibraryManager::Create_Object( Get_Definition().SpecialEffectsObjID );
if ( effect_obj != NULL ) {
effect_obj->Set_Transform( SpawnTM );
}
}
// Setup the spawning variables
SpawnCount++;
float variation = Get_Definition().SpawnDelayVariation/2;
SpawnDelayTimer = Get_Definition().SpawnDelay + FreeRandom.Get_Float( -variation, variation );
int i;
// Start Def Scripts
WWASSERT( Get_Definition().ScriptNameList.Count() == Get_Definition().ScriptParameterList.Count() );
for ( i = 0; i < Get_Definition().ScriptNameList.Count(); i++ ) {
ScriptClass* script = ScriptManager::Create_Script( Get_Definition().ScriptNameList[i] );
if (script) {
script->Set_Parameters_String( Get_Definition().ScriptParameterList[i] );
obj->Add_Observer(script);
}
}
// Start Instance Scripts
WWASSERT( ScriptNameList.Count() == ScriptParameterList.Count() );
for ( i = 0; i < ScriptNameList.Count(); i++ ) {
ScriptClass* script = ScriptManager::Create_Script( ScriptNameList[i] );
if (script) {
script->Set_Parameters_String( ScriptParameterList[i] );
obj->Add_Observer(script);
}
}
return obj;
}
void SpawnerClass::Add_Script( const char * script_name, const char * script_parameter )
{
WWASSERT( ScriptNameList.Count() == ScriptParameterList.Count() );
ScriptNameList.Add( script_name );
ScriptParameterList.Add( script_parameter );
}
/*
** SpawnManager
*/
DynamicVectorClass<SpawnerClass*> SpawnManager::SpawnerList;
float SpawnManager::AutoSpawnTimer = AUTO_SPAWN_CHECK_DELAY;
/*
** Instance of SpawnManager that exists just to free memory at game end when it gets destructed.
*/
static SpawnManager JustForLeaks;
void SpawnManager::Add_Spawner( SpawnerClass * spawner )
{
WWASSERT(spawner != NULL);
SpawnerList.Add( spawner );
}
void SpawnManager::Remove_Spawner( SpawnerClass * spawner )
{
SpawnerList.Delete( spawner );
}
void SpawnManager::Remove_All_Spawners( void )
{
while ( SpawnerList.Count() ) {
delete SpawnerList[0];
}
}
/*
**
*/
enum {
CHUNKID_SPAWNER_DATA = 1014991133,
CHUNKID_SPAWNER_VARIABLES,
MICROCHUNKID_SPAWNER_AUTO_SPAWN_TIMER = 1,
};
bool SpawnManager::Save( ChunkSaveClass &csave )
{
csave.Begin_Chunk( CHUNKID_SPAWNER_VARIABLES );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SPAWNER_AUTO_SPAWN_TIMER, AutoSpawnTimer );
csave.End_Chunk();
for ( int i = 0; i < SpawnerList.Count(); i++ ) {
csave.Begin_Chunk( CHUNKID_SPAWNER_DATA );
SpawnerList[i]->Save( csave );
csave.End_Chunk();
}
return true;
}
bool SpawnManager::Load( ChunkLoadClass &cload )
{
Remove_All_Spawners();
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case CHUNKID_SPAWNER_VARIABLES:
while (cload.Open_Micro_Chunk()) {
switch(cload.Cur_Micro_Chunk_ID()) {
READ_MICRO_CHUNK( cload, MICROCHUNKID_SPAWNER_AUTO_SPAWN_TIMER, AutoSpawnTimer );
default:
Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__));
break;
}
cload.Close_Micro_Chunk();
}
break;
case CHUNKID_SPAWNER_DATA:
{
SpawnerClass * spawner = new SpawnerClass();
spawner->Load( cload );
break;
}
default:
Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
break;
}
cload.Close_Chunk();
}
return true;
}
/*
**
*/
bool SpawnManager::Spawner_Exists( int player_type )
{
for ( int i = 0; i < SpawnerList.Count(); i++ ) {
if ( SpawnerList[i]->Get_Definition().PlayerType == player_type ) {
return true;
}
}
return false;
}
Matrix3D SpawnManager::Get_Primary_Spawn_Location( void )
{
int index = -1;
for (int i = 0; i < SpawnerList.Count(); i++) {
if ( SpawnerList[i]->Get_Definition().IsPrimary &&
SpawnerList[i]->Get_Definition().IsSoldierStartup ) {
index = i;
break;
}
}
if (index == -1) {
Debug_Say(("Get_Primary_Spawn_Location: failed to find suitable spawner, return origin.\n"));
return Matrix3D(1);
} else {
return SpawnerList[index]->Get_TM();
}
}
Matrix3D SpawnManager::Get_Multiplayer_Spawn_Location( int player_type, SoldierGameObj * soldier )
{
if (player_type == PLAYERTYPE_NEUTRAL) {
//
// Use renegade spawner
//
player_type = PLAYERTYPE_RENEGADE;
}
//
// Find out how many suitable spawners there are
//
int suitable_spawners = 0;
for ( int i = 0; i < SpawnerList.Count(); i++ ) {
if ( !SpawnerList[i]->Get_Definition().IsPrimary &&
SpawnerList[i]->Get_Definition().IsSoldierStartup &&
SpawnerList[i]->Get_Definition().PlayerType == player_type ) {
suitable_spawners++;
}
}
Matrix3D tm;
if (suitable_spawners == 0) {
Debug_Say(("Get_Multiplayer_Spawn_Location: failed to find suitable spawner, return origin.\n"));
tm = Matrix3D(1);
} else {
//
// Randomly choose one of the suitable spawners
//
int i;
int selected_spawner = rand() % suitable_spawners;
int start_index = 0;
int count = 0;
for ( i = 0; i < SpawnerList.Count(); i++ )
{
if ( !SpawnerList[i]->Get_Definition().IsPrimary &&
SpawnerList[i]->Get_Definition().IsSoldierStartup &&
SpawnerList[i]->Get_Definition().PlayerType == player_type )
{
if (count == selected_spawner)
{
// As a fallback, we will always return the first transform
// that was selected.
tm = SpawnerList[i]->Get_TM();
start_index = i;
break;
}
count++;
}
}
//
// Loop through the entire list of spawners, starting with the one
// we just selected, until we find a clear spawn point for this soldier
//
Phys3Class * phys_obj = soldier->Peek_Human_Phys();
if (phys_obj != NULL) {
for ( i = 0; i < SpawnerList.Count(); i++) {
// Wrap around the list if needed:
int index = (i + start_index) % SpawnerList.Count();
if ( !SpawnerList[index]->Get_Definition().IsPrimary &&
SpawnerList[index]->Get_Definition().IsSoldierStartup &&
SpawnerList[index]->Get_Definition().PlayerType == player_type )
{
if (phys_obj->Can_Teleport(SpawnerList[index]->Get_TM(),true)) {
// Return the first good spawn point we find!
return SpawnerList[index]->Get_TM();
}
}
}
// If we fall through to here, no clear spawn points were found, need more spawners!
WWDEBUG_SAY(("Failed to find clear multiplayer spawn point for object: %s\r\n",phys_obj->Peek_Model()->Get_Name()));
}
}
// Fallback - return the spawn point we randomly chose.
return tm;
}
Matrix3D SpawnManager::Get_Ctf_Spawn_Location(int team)
{
WWASSERT(team == PLAYERTYPE_NOD || team == PLAYERTYPE_GDI);
StringClass spawner_name;
if (team == PLAYERTYPE_NOD) {
spawner_name = "Ctf_Pedestal_Nod";
} else {
spawner_name = "Ctf_Pedestal_GDI";
}
int index = -1;
for (int i = 0; i < SpawnerList.Count(); i++) {
if ( !spawner_name.Compare(SpawnerList[i]->Get_Definition().Get_Name()) ) {
index = i;
break;
}
}
//WWASSERT(index != -1);
if (index == -1)
{
Debug_Say(("*** Fatal Media Error: CTF spawner(s) missing from level.\n"));
DIE;
}
return SpawnerList[index]->Get_TM();
}
SpawnerClass * SpawnManager::Get_Primary_Spawner( void )
{
int index = -1;
for (int i = 0; i < SpawnerList.Count(); i++) {
if ( SpawnerList[i]->Get_Definition().IsPrimary &&
SpawnerList[i]->Get_Definition().IsSoldierStartup ) {
index = i;
break;
}
}
if (index == -1) {
Debug_Say(("Get_Primary_Spawner: failed to find suitable spawner, returning NULL.\n"));
return NULL;
} else {
return SpawnerList[index];
}
}
void SpawnManager::Update( void )
{
AutoSpawnTimer += TimeManager::Get_Frame_Seconds();
if ( CombatManager::I_Am_Server() ) {
if (AutoSpawnTimer >= AUTO_SPAWN_CHECK_DELAY ) {
// Let each Spawner Check for Auto Spwan
for ( int i = 0; i < SpawnerList.Count(); i++ ) {
//
// Soldiers are only spawned in missions (SP, coop)
//
if (!SpawnerList[i]->Get_Definition().IsSoldierStartup ||
//CombatManager::Is_Mission()) {
IS_SOLOPLAY) {
SpawnerList[i]->Check_Auto_Spawn( AutoSpawnTimer );
}
}
AutoSpawnTimer = 0;
}
}
}
PhysicalGameObj * SpawnManager::Spawner_Trigger( int id )
{
for ( int i = 0; i < SpawnerList.Count(); i++ ) {
if ( SpawnerList[i]->Get_ID() == id ) {
return SpawnerList[i]->Spawn();
}
}
return NULL;
}
void SpawnManager::Spawner_Enable( int id, bool enable )
{
for ( int i = 0; i < SpawnerList.Count(); i++ ) {
if ( SpawnerList[i]->Get_ID() == id ) {
if ( enable ) {
// force an immeadiate spawn check
AutoSpawnTimer = AUTO_SPAWN_CHECK_DELAY;
}
SpawnerList[i]->Enable( enable );
}
}
}
void SpawnManager::Display_Unused_Spawners( void )
{
Debug_Say(( "Unused Spawner Display....\n" ));
for ( int i = 0; i < SpawnerList.Count(); i++ ) {
if ( SpawnerList[i]->SpawnCount == 0 ) {
Debug_Say(( "Spawner %d never Spawned\n", SpawnerList[i]->Get_ID() ));
}
}
}
bool SpawnManager::Toggle_Allow_Killing_Hibernating_Spawn( void )
{
_Allow_Killing_Hibernating_Spawn = !_Allow_Killing_Hibernating_Spawn;
return _Allow_Killing_Hibernating_Spawn;
}
/***********************************************************************************************
* SpawnManager::~SpawnManager -- Class destructor *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 6/14/2001 2:27PM ST : Created *
*=============================================================================================*/
SpawnManager::~SpawnManager(void)
{
Remove_All_Spawners();
}