spawn.cpp 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** Confidential - Westwood Studios ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : Commando *
  23. * *
  24. * $Archive:: /Commando/Code/Combat/spawn.cpp $*
  25. * *
  26. * $Author:: Tom_s $*
  27. * *
  28. * $Modtime:: 11/19/01 10:25a $*
  29. * *
  30. * $Revision:: 107 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "spawn.h"
  36. #include "wwhack.h"
  37. #include "persistfactory.h"
  38. #include "combatchunkid.h"
  39. #include "simpledefinitionfactory.h"
  40. #include "debug.h"
  41. #include "definitionmgr.h"
  42. #include "soldier.h"
  43. #include "combat.h"
  44. #include "playertype.h"
  45. #include "gameobjmanager.h"
  46. #include "soldierobserver.h"
  47. #include "crandom.h"
  48. #include "wwaudio.h"
  49. #include "assets.h"
  50. #include "objlibrary.h"
  51. #include "movephys.h"
  52. #include "scripts.h"
  53. #include "wwmemlog.h"
  54. #include "gametype.h"
  55. #include "combatmaterialeffectmanager.h"
  56. #include "transitioneffect.h"
  57. #include "humanphys.h"
  58. //
  59. // Class statics
  60. //
  61. bool _Allow_Killing_Hibernating_Spawn = true;
  62. #define AUTO_SPAWN_CHECK_DELAY 2.0f
  63. /*
  64. ** SpawnDefClass
  65. */
  66. DECLARE_FORCE_LINK( Spawner )
  67. SimplePersistFactoryClass<SpawnerDefClass, CHUNKID_SPAWNER_DEF> _SpawnerDefPersistFactory;
  68. DECLARE_DEFINITION_FACTORY(SpawnerDefClass, CLASSID_SPAWNER_DEF, "Spawner") _SpawnerDefDefFactory;
  69. SpawnerDefClass::SpawnerDefClass( void ) :
  70. PlayerType( PLAYERTYPE_NEUTRAL ),
  71. SpawnMax( -1 ), // unlimited
  72. SpawnDelay( 10 ),
  73. SpawnDelayVariation( 0 ),
  74. IsPrimary( false ),
  75. IsSoldierStartup( false ),
  76. PostVisualSpawnDelay( 0 ),
  77. SpecialEffectsObjID( 0 ),
  78. GotoSpawnerPos( false ),
  79. GotoSpawnerPosPriority( 30 ),
  80. TeleportFirstSpawn( true ),
  81. StartsDisabled( false ),
  82. KillHibernatingSpawn( true ),
  83. ApplySpawnMaterialEffect( true ),
  84. IsMultiplayWeaponSpawner( false )
  85. {
  86. DEFIDLIST_PARAM( SpawnerDefClass, SpawnDefinitionIDList, CLASSID_GAME_OBJECTS );
  87. #ifdef PARAM_EDITING_ON
  88. EnumParameterClass *param;
  89. param = new EnumParameterClass( (int*)&PlayerType );
  90. param->Set_Name ("PlayerType");
  91. param->Add_Value ( "Unteamed", PLAYERTYPE_RENEGADE );
  92. param->Add_Value ( "Nod", PLAYERTYPE_NOD );
  93. param->Add_Value ( "GDI", PLAYERTYPE_GDI );
  94. GENERIC_EDITABLE_PARAM(SpawnerDefClass,param)
  95. #endif
  96. EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_INT, SpawnMax );
  97. EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_FLOAT, SpawnDelay );
  98. EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_FLOAT, SpawnDelayVariation );
  99. EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_BOOL, IsPrimary );
  100. EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_BOOL, IsSoldierStartup );
  101. EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_BOOL, GotoSpawnerPos );
  102. EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_FLOAT, GotoSpawnerPosPriority );
  103. EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_BOOL, TeleportFirstSpawn );
  104. EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_BOOL, StartsDisabled );
  105. EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_BOOL, KillHibernatingSpawn );
  106. EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_BOOL, ApplySpawnMaterialEffect );
  107. EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_BOOL, IsMultiplayWeaponSpawner );
  108. GENERIC_DEFID_PARAM( SpawnerDefClass, SpecialEffectsObjID, CLASSID_GAME_OBJECT_DEF_SPECIAL_EFFECTS);
  109. EDITABLE_PARAM( SpawnerDefClass, ParameterClass::TYPE_FLOAT, PostVisualSpawnDelay );
  110. SCRIPTLIST_PARAM (SpawnerDefClass, "Scripts", ScriptNameList, ScriptParameterList);
  111. }
  112. uint32 SpawnerDefClass::Get_Class_ID( void ) const
  113. {
  114. return CLASSID_SPAWNER_DEF;
  115. }
  116. PersistClass * SpawnerDefClass::Create( void ) const
  117. {
  118. SpawnerClass * obj = new SpawnerClass;
  119. obj->Init( *this );
  120. return obj;
  121. }
  122. enum {
  123. CHUNKID_DEF_PARENT = 1013991542,
  124. CHUNKID_DEF_VARIABLES,
  125. MICROCHUNKID_DEF_DEFINITION_ID = 1,
  126. XXXMICROCHUNKID_DEF_IS_COMMANDO_STARTING_POINT,
  127. MICROCHUNKID_DEF_PLAYER_TYPE,
  128. MICROCHUNKID_DEF_SPAWN_MAX,
  129. MICROCHUNKID_DEF_SPAWN_DELAY,
  130. XXXMICROCHUNKID_DEF_AUTO_SPAWN_RADIUS,
  131. XXXMICROCHUNKID_DEF_ONE_AT_A_TIME,
  132. MICROCHUNKID_DEF_SPAWN_DELAY_VARIATION,
  133. MICROCHUNKID_DEF_IS_PRIMARY,
  134. XXXMICROCHUNKID_DEF_EFFECT_MODEL_NAME,
  135. XXXMICROCHUNKID_DEF_EFFECT_ANIMATION_NAME,
  136. XXXMICROCHUNKID_DEF_SOUND_ID,
  137. MICROCHUNKID_DEF_POST_EFFECT_SPAWN_DELAY,
  138. MICROCHUNKID_DEF_SPECIAL_EFFECTS_OBJ_ID,
  139. MICROCHUNKID_DEF_IS_SOLDIER_STARTUP,
  140. MICROCHUNKID_DEF_GOTO_SPAWNER_POS,
  141. MICROCHUNKID_DEF_TELEPORT_FIRST_SPAWN,
  142. MICROCHUNKID_DEF_SCRIPT_NAME,
  143. MICROCHUNKID_DEF_SCRIPT_PARAMETERS,
  144. MICROCHUNKID_DEF_STARTS_DISABLED,
  145. MICROCHUNKID_DEF_KILL_HIBERNATING_SPAWN,
  146. MICROCHUNKID_DEF_GOTO_SPAWNER_POS_PRIORITY,
  147. MICROCHUNKID_DEF_APPLY_SPAWN_MATERIAL_EFFECT,
  148. MICROCHUNKID_DEF_IS_MULTIPLAY_WEAPON_SPAWNER
  149. };
  150. bool SpawnerDefClass::Save( ChunkSaveClass &csave )
  151. {
  152. int i;
  153. csave.Begin_Chunk( CHUNKID_DEF_PARENT );
  154. DefinitionClass::Save( csave );
  155. csave.End_Chunk();
  156. csave.Begin_Chunk( CHUNKID_DEF_VARIABLES );
  157. for ( i = 0; i < SpawnDefinitionIDList.Count(); i++ ) {
  158. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_DEFINITION_ID, SpawnDefinitionIDList[i] );
  159. }
  160. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_PLAYER_TYPE, PlayerType );
  161. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_SPAWN_MAX, SpawnMax );
  162. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_SPAWN_DELAY, SpawnDelay );
  163. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_SPAWN_DELAY_VARIATION, SpawnDelayVariation );
  164. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_IS_PRIMARY, IsPrimary );
  165. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_IS_SOLDIER_STARTUP, IsSoldierStartup );
  166. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_SPECIAL_EFFECTS_OBJ_ID, SpecialEffectsObjID );
  167. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_POST_EFFECT_SPAWN_DELAY, PostVisualSpawnDelay );
  168. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_GOTO_SPAWNER_POS, GotoSpawnerPos );
  169. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_GOTO_SPAWNER_POS_PRIORITY,GotoSpawnerPosPriority );
  170. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_TELEPORT_FIRST_SPAWN, TeleportFirstSpawn );
  171. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_STARTS_DISABLED, StartsDisabled );
  172. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_KILL_HIBERNATING_SPAWN, KillHibernatingSpawn );
  173. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_APPLY_SPAWN_MATERIAL_EFFECT, ApplySpawnMaterialEffect );
  174. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_IS_MULTIPLAY_WEAPON_SPAWNER, IsMultiplayWeaponSpawner );
  175. WWASSERT( ScriptNameList.Count() == ScriptParameterList.Count() );
  176. for ( i = 0; i < ScriptNameList.Count(); i++ ) {
  177. WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_DEF_SCRIPT_NAME, ScriptNameList[i] );
  178. WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_DEF_SCRIPT_PARAMETERS, ScriptParameterList[i] );
  179. }
  180. csave.End_Chunk();
  181. return true;
  182. }
  183. bool SpawnerDefClass::Load( ChunkLoadClass &cload )
  184. {
  185. WWASSERT( ScriptNameList.Count() == ScriptParameterList.Count() );
  186. WWASSERT( SpawnDefinitionIDList.Count() == 0 );
  187. StringClass str;
  188. while (cload.Open_Chunk()) {
  189. switch(cload.Cur_Chunk_ID()) {
  190. case CHUNKID_DEF_PARENT:
  191. DefinitionClass::Load( cload );
  192. break;
  193. case CHUNKID_DEF_VARIABLES:
  194. while (cload.Open_Micro_Chunk()) {
  195. switch(cload.Cur_Micro_Chunk_ID()) {
  196. case MICROCHUNKID_DEF_DEFINITION_ID:
  197. {
  198. int def_id;
  199. cload.Read(&def_id,sizeof(def_id));
  200. SpawnDefinitionIDList.Add( def_id );
  201. }
  202. break;
  203. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_PLAYER_TYPE, PlayerType );
  204. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_SPAWN_MAX, SpawnMax );
  205. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_SPAWN_DELAY, SpawnDelay );
  206. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_SPAWN_DELAY_VARIATION, SpawnDelayVariation );
  207. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_IS_PRIMARY, IsPrimary );
  208. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_IS_SOLDIER_STARTUP, IsSoldierStartup );
  209. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_POST_EFFECT_SPAWN_DELAY, PostVisualSpawnDelay );
  210. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_SPECIAL_EFFECTS_OBJ_ID, SpecialEffectsObjID );
  211. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_GOTO_SPAWNER_POS, GotoSpawnerPos );
  212. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_GOTO_SPAWNER_POS_PRIORITY, GotoSpawnerPosPriority );
  213. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_TELEPORT_FIRST_SPAWN, TeleportFirstSpawn );
  214. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_STARTS_DISABLED, StartsDisabled );
  215. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_KILL_HIBERNATING_SPAWN, KillHibernatingSpawn );
  216. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_APPLY_SPAWN_MATERIAL_EFFECT, ApplySpawnMaterialEffect );
  217. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_IS_MULTIPLAY_WEAPON_SPAWNER, IsMultiplayWeaponSpawner );
  218. case MICROCHUNKID_DEF_SCRIPT_NAME:
  219. LOAD_MICRO_CHUNK_WWSTRING( cload, str );
  220. ScriptNameList.Add( str );
  221. break;
  222. case MICROCHUNKID_DEF_SCRIPT_PARAMETERS:
  223. LOAD_MICRO_CHUNK_WWSTRING( cload, str );
  224. ScriptParameterList.Add( str );
  225. break;
  226. default:
  227. Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__));
  228. break;
  229. }
  230. cload.Close_Micro_Chunk();
  231. }
  232. break;
  233. default:
  234. Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
  235. break;
  236. }
  237. cload.Close_Chunk();
  238. }
  239. WWASSERT( ScriptNameList.Count() == ScriptParameterList.Count() );
  240. return true;
  241. }
  242. const PersistFactoryClass & SpawnerDefClass::Get_Factory( void ) const
  243. {
  244. return _SpawnerDefPersistFactory;
  245. }
  246. /*
  247. ** SpawnClass
  248. */
  249. SimplePersistFactoryClass<SpawnerClass, CHUNKID_SPAWNER> _SpawnerPersistFactory;
  250. const PersistFactoryClass & SpawnerClass::Get_Factory (void) const
  251. {
  252. return _SpawnerPersistFactory;
  253. }
  254. SpawnerClass::SpawnerClass( void ) :
  255. ID( 0 ),
  256. TM( 1 ),
  257. SpawnTM( 1 ),
  258. Definition( NULL ),
  259. SpawnCount( 0 ),
  260. SpawnDelayTimer( 0 ),
  261. Enabled( true )
  262. {
  263. SpawnManager::Add_Spawner( this );
  264. }
  265. SpawnerClass::~SpawnerClass( void )
  266. {
  267. SpawnManager::Remove_Spawner( this );
  268. }
  269. void SpawnerClass::Init( const SpawnerDefClass & definition )
  270. {
  271. Definition = &definition;
  272. Enabled = !definition.StartsDisabled;
  273. }
  274. enum {
  275. CHUNKID_PARENT = 1014991053,
  276. CHUNKID_VARIABLES,
  277. CHUNKID_LAST_SPAWN,
  278. MICROCHUNKID_ID = 1,
  279. MICROCHUNKID_TM,
  280. MICROCHUNKID_DEFINITION_ID,
  281. MICROCHUNKID_SPAWN_COUNT,
  282. MICROCHUNKID_SPAWN_DELAY_TIMER,
  283. MICROCHUNKID_ENABLED,
  284. MICROCHUNKID_SPAWN_POINT_ENTRY,
  285. XXXMICROCHUNKID_REAL_SPAWN_TIMER,
  286. MICROCHUNKID_SPAWN_TM,
  287. MICROCHUNKID_SCRIPT_NAME,
  288. MICROCHUNKID_SCRIPT_PARAMETERS,
  289. };
  290. bool SpawnerClass::Save( ChunkSaveClass & csave )
  291. {
  292. csave.Begin_Chunk( CHUNKID_PARENT );
  293. PersistClass::Save( csave );
  294. csave.End_Chunk();
  295. csave.Begin_Chunk( CHUNKID_VARIABLES );
  296. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ID, ID );
  297. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TM, TM );
  298. int id = Get_Definition().Get_ID();
  299. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEFINITION_ID, id );
  300. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SPAWN_COUNT, SpawnCount );
  301. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SPAWN_DELAY_TIMER, SpawnDelayTimer );
  302. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ENABLED, Enabled );
  303. for ( int i = 0; i < SpawnPointList.Count(); i++ ) {
  304. Matrix3D tm = SpawnPointList[i];
  305. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SPAWN_POINT_ENTRY, tm );
  306. }
  307. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SPAWN_TM, SpawnTM );
  308. WWASSERT( ScriptNameList.Count() == ScriptParameterList.Count() );
  309. for ( i = 0; i < ScriptNameList.Count(); i++ ) {
  310. WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_SCRIPT_NAME, ScriptNameList[i] );
  311. WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_SCRIPT_PARAMETERS, ScriptParameterList[i] );
  312. }
  313. csave.End_Chunk();
  314. if ( LastSpawn.Get_Ptr() != NULL ) {
  315. csave.Begin_Chunk( CHUNKID_LAST_SPAWN );
  316. LastSpawn.Save( csave );
  317. csave.End_Chunk();
  318. }
  319. return true;
  320. }
  321. bool SpawnerClass::Load( ChunkLoadClass & cload )
  322. {
  323. WWASSERT( ScriptNameList.Count() == ScriptParameterList.Count() );
  324. StringClass str;
  325. Matrix3D tm;
  326. WWASSERT( SpawnPointList.Length() == 0 );
  327. while (cload.Open_Chunk()) {
  328. switch(cload.Cur_Chunk_ID()) {
  329. case CHUNKID_PARENT:
  330. PersistClass::Load( cload );
  331. break;
  332. case CHUNKID_VARIABLES:
  333. while (cload.Open_Micro_Chunk()) {
  334. switch(cload.Cur_Micro_Chunk_ID()) {
  335. READ_MICRO_CHUNK( cload, MICROCHUNKID_ID, ID );
  336. READ_MICRO_CHUNK( cload, MICROCHUNKID_TM, TM );
  337. READ_MICRO_CHUNK( cload, MICROCHUNKID_SPAWN_COUNT, SpawnCount );
  338. READ_MICRO_CHUNK( cload, MICROCHUNKID_SPAWN_DELAY_TIMER, SpawnDelayTimer );
  339. READ_MICRO_CHUNK( cload, MICROCHUNKID_ENABLED, Enabled );
  340. READ_MICRO_CHUNK( cload, MICROCHUNKID_SPAWN_TM, SpawnTM );
  341. case MICROCHUNKID_DEFINITION_ID:
  342. int definition_id;
  343. LOAD_MICRO_CHUNK( cload, definition_id );
  344. WWASSERT( Definition == NULL );
  345. Definition = (const SpawnerDefClass *)DefinitionMgrClass::Find_Definition( definition_id );
  346. WWASSERT( Definition != NULL );
  347. break;
  348. case MICROCHUNKID_SPAWN_POINT_ENTRY:
  349. LOAD_MICRO_CHUNK( cload, tm );
  350. SpawnPointList.Add( tm );
  351. break;
  352. case MICROCHUNKID_SCRIPT_NAME:
  353. LOAD_MICRO_CHUNK_WWSTRING( cload, str );
  354. ScriptNameList.Add( str );
  355. break;
  356. case MICROCHUNKID_SCRIPT_PARAMETERS:
  357. LOAD_MICRO_CHUNK_WWSTRING( cload, str );
  358. ScriptParameterList.Add( str );
  359. break;
  360. default:
  361. Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__));
  362. break;
  363. }
  364. cload.Close_Micro_Chunk();
  365. }
  366. break;
  367. case CHUNKID_LAST_SPAWN:
  368. LastSpawn.Load( cload );
  369. break;
  370. default:
  371. Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
  372. break;
  373. }
  374. cload.Close_Chunk();
  375. }
  376. WWASSERT( ScriptNameList.Count() == ScriptParameterList.Count() );
  377. return true;
  378. }
  379. bool SpawnerClass::Determine_Spawn_TM( PhysicalGameObj * obj )
  380. {
  381. bool spawnable = false;
  382. MoveablePhysClass * phys_obj = NULL;
  383. if ( obj->Peek_Physical_Object() ) {
  384. phys_obj = obj->Peek_Physical_Object()->As_MoveablePhysClass();
  385. }
  386. // If we have spawn points, try and find one where the object can be placed
  387. if ( SpawnPointList.Count() > 0 ) {
  388. // Find human player nearest spawner
  389. Vector3 nearest_human_player_pos( 0,0,0 );
  390. float commando_range = 10000000;
  391. SLNode<SmartGameObj> *objnode;
  392. for ( objnode = GameObjManager::Get_Smart_Game_Obj_List()->Head(); objnode; objnode = objnode->Next()) {
  393. SoldierGameObj * soldier = objnode->Data()->As_SoldierGameObj();
  394. if ( soldier && soldier->Is_Human_Controlled() ) {
  395. Vector3 pos;
  396. soldier->Get_Position( &pos );
  397. pos -= TM.Get_Translation();
  398. float range = pos.Length();
  399. if ( range < commando_range ) {
  400. soldier->Get_Position( &nearest_human_player_pos );
  401. commando_range = range;
  402. }
  403. }
  404. }
  405. // Find a safe spawnable point furthest from the commando
  406. float best_range = 0;
  407. // For every spawn point
  408. for ( int index = 0; index < SpawnPointList.Count(); index++ ) {
  409. bool is_safe = true; // Assume safe
  410. Matrix3D safe_spot = SpawnPointList[index];
  411. if ( phys_obj ) {
  412. is_safe = phys_obj->Can_Teleport_And_Stand( SpawnPointList[index], &safe_spot );
  413. }
  414. if ( is_safe ) {
  415. // check range for furthest from commando
  416. Vector3 diff = safe_spot.Get_Translation() - nearest_human_player_pos;
  417. float range = diff.Length();
  418. if ( range > best_range ) {
  419. best_range = range;
  420. SpawnTM = safe_spot;
  421. spawnable = true;
  422. }
  423. }
  424. }
  425. } else {
  426. // Lets see if we can spawn at TM..
  427. // Only check teleport for armed objs
  428. if ( phys_obj && obj->As_ArmedGameObj() != NULL ) {
  429. Matrix3D safe_spot = TM;
  430. if ( phys_obj->Can_Teleport_And_Stand( TM, &safe_spot ) ) {
  431. SpawnTM = safe_spot;
  432. spawnable = true;
  433. }
  434. } else {
  435. SpawnTM = TM;
  436. spawnable = true;
  437. }
  438. }
  439. if ( spawnable ) {
  440. obj->Set_Transform( SpawnTM );
  441. }
  442. return spawnable;
  443. }
  444. PhysicalGameObj * SpawnerClass::Create_Spawned_Object( int obj_id )
  445. {
  446. WWMEMLOG(MEM_GAMEDATA);
  447. int spawn_id = obj_id;
  448. if ( obj_id == -1 ) {
  449. /*
  450. ** Randomly select an object to spawn
  451. */
  452. int spawn_count = Get_Definition().SpawnDefinitionIDList.Count();
  453. if ( spawn_count > 0 ) {
  454. int spawn_index = FreeRandom.Get_Int( spawn_count );
  455. spawn_id = Get_Definition().SpawnDefinitionIDList[ spawn_index ];
  456. }
  457. }
  458. /*
  459. ** Create the object
  460. */
  461. PhysicalGameObj * obj = NULL;
  462. if ( spawn_id != -1 ) {
  463. PhysicalGameObjDef * def = (PhysicalGameObjDef *)DefinitionMgrClass::Find_Definition( spawn_id );
  464. if ( def == NULL ) {
  465. Debug_Say(( "Spawner %s failed to create a spawn id %d\n", Get_Definition().Get_Name(), spawn_id ));
  466. }
  467. WWASSERT( def );
  468. if ( def != NULL ) {
  469. obj = (PhysicalGameObj *)def->Create();
  470. if ( obj == NULL ) {
  471. Debug_Say(( "Spawner Failed to create %s\n", def->Get_Name() ));
  472. }
  473. }
  474. }
  475. /*
  476. ** If the definition calls for it, add a material effect to the object
  477. */
  478. if ((obj != NULL) && (Get_Definition().ApplySpawnMaterialEffect)) {
  479. PhysClass * physobj = obj->Peek_Physical_Object();
  480. if (physobj != NULL) {
  481. TransitionEffectClass * effect = CombatMaterialEffectManager::Get_Spawn_Effect();
  482. physobj->Add_Effect_To_Me(effect);
  483. REF_PTR_RELEASE(effect);
  484. }
  485. }
  486. return obj;
  487. }
  488. void SpawnerClass::Check_Auto_Spawn( float dtime )
  489. {
  490. if ( LastSpawn.Get_Ptr() == NULL ) { // If our last spawn is gone
  491. bool spawn_ok = false;
  492. if ( Get_Definition().SpawnMax < 0 ||
  493. SpawnCount < Get_Definition().SpawnMax ) {
  494. spawn_ok = true;
  495. }
  496. if ( Enabled && spawn_ok ) {
  497. if ( SpawnDelayTimer > 0 ) {
  498. SpawnDelayTimer -= dtime;
  499. }
  500. if ( SpawnDelayTimer <= 0 ) {
  501. Spawn();
  502. }
  503. }
  504. } else if ( Get_Definition().KillHibernatingSpawn && _Allow_Killing_Hibernating_Spawn ) {
  505. // If our last spawn is alive, but hibernating
  506. PhysicalGameObj * spawn = (PhysicalGameObj *)LastSpawn.Get_Ptr();
  507. if ( spawn->Is_Hibernating() ) {
  508. spawn->Set_Delete_Pending(); // Kill em
  509. SpawnCount--;
  510. SpawnDelayTimer = 0; // no delay to bring him back
  511. #if 0
  512. if ( Enabled ) {
  513. Debug_Say(( "Killing Hibernating Spawn from an Enabled spawner (id %d)\n", Get_ID() ));
  514. } else {
  515. Debug_Say(( "Killing Hibernating Spawn (spawner id %d)\n", Get_ID() ));
  516. }
  517. #endif
  518. }
  519. }
  520. }
  521. PhysicalGameObj * SpawnerClass::Spawn_Object( int obj_def_id )
  522. {
  523. return Spawn( obj_def_id );
  524. }
  525. bool SpawnerClass::Can_Spawn_Object( int obj_def_id )
  526. {
  527. bool retval = false;
  528. //
  529. // Loop over all the objects this spawner can create
  530. //
  531. const DynamicVectorClass<int> &def_list = Definition->Get_Spawn_Definition_ID_List ();
  532. for (int index = 0; index < def_list.Count (); index ++) {
  533. //
  534. // Is this the object we're looking for?
  535. //
  536. if ( def_list[index] == obj_def_id ) {
  537. retval = true;
  538. break;
  539. }
  540. }
  541. return retval;
  542. }
  543. PhysicalGameObj * SpawnerClass::Spawn( int obj_id )
  544. {
  545. WWASSERT( CombatManager::I_Am_Server() );
  546. // Pick what will be spawned
  547. PhysicalGameObj * obj = Create_Spawned_Object( obj_id );
  548. WWASSERT( obj );
  549. // Choose a spawn location
  550. bool spawnable = Determine_Spawn_TM( obj );
  551. if ( !spawnable ) {
  552. Debug_Say(( "Couldn't find a place to spawn this object ID %d\n", Get_ID() ));
  553. obj->Set_Delete_Pending();
  554. return NULL;
  555. }
  556. obj->Start_Observers();
  557. if ( Get_Definition().GotoSpawnerPos ) {
  558. // Teleport the first spawn
  559. if ( Get_Definition().TeleportFirstSpawn && SpawnCount == 0 ) {
  560. obj->Set_Transform( TM );
  561. } else {
  562. // Give initial goto command / don't let him hibernate
  563. SoldierGameObj * soldier = obj->As_SoldierGameObj();
  564. if ( soldier != NULL ) {
  565. ActionParamsStruct parameters;
  566. parameters.Priority = Get_Definition().GotoSpawnerPosPriority;
  567. parameters.ObserverID = 0;
  568. parameters.MoveLocation = TM.Get_Translation();
  569. parameters.MoveSpeed = 1;
  570. parameters.MoveCrouched = false;
  571. parameters.MoveArrivedDistance = 1;
  572. soldier->Get_Action()->Goto( parameters );
  573. soldier->Reset_Hibernating();
  574. }
  575. }
  576. }
  577. // Keep track of the last spawned object
  578. LastSpawn = obj;
  579. // Create the special effects game object that will be responsible for playing
  580. // an animation and sound
  581. if ( Get_Definition().SpecialEffectsObjID != 0 ) {
  582. PhysicalGameObj *effect_obj = ObjectLibraryManager::Create_Object( Get_Definition().SpecialEffectsObjID );
  583. if ( effect_obj != NULL ) {
  584. effect_obj->Set_Transform( SpawnTM );
  585. }
  586. }
  587. // Setup the spawning variables
  588. SpawnCount++;
  589. float variation = Get_Definition().SpawnDelayVariation/2;
  590. SpawnDelayTimer = Get_Definition().SpawnDelay + FreeRandom.Get_Float( -variation, variation );
  591. int i;
  592. // Start Def Scripts
  593. WWASSERT( Get_Definition().ScriptNameList.Count() == Get_Definition().ScriptParameterList.Count() );
  594. for ( i = 0; i < Get_Definition().ScriptNameList.Count(); i++ ) {
  595. ScriptClass* script = ScriptManager::Create_Script( Get_Definition().ScriptNameList[i] );
  596. if (script) {
  597. script->Set_Parameters_String( Get_Definition().ScriptParameterList[i] );
  598. obj->Add_Observer(script);
  599. }
  600. }
  601. // Start Instance Scripts
  602. WWASSERT( ScriptNameList.Count() == ScriptParameterList.Count() );
  603. for ( i = 0; i < ScriptNameList.Count(); i++ ) {
  604. ScriptClass* script = ScriptManager::Create_Script( ScriptNameList[i] );
  605. if (script) {
  606. script->Set_Parameters_String( ScriptParameterList[i] );
  607. obj->Add_Observer(script);
  608. }
  609. }
  610. return obj;
  611. }
  612. void SpawnerClass::Add_Script( const char * script_name, const char * script_parameter )
  613. {
  614. WWASSERT( ScriptNameList.Count() == ScriptParameterList.Count() );
  615. ScriptNameList.Add( script_name );
  616. ScriptParameterList.Add( script_parameter );
  617. }
  618. /*
  619. ** SpawnManager
  620. */
  621. DynamicVectorClass<SpawnerClass*> SpawnManager::SpawnerList;
  622. float SpawnManager::AutoSpawnTimer = AUTO_SPAWN_CHECK_DELAY;
  623. /*
  624. ** Instance of SpawnManager that exists just to free memory at game end when it gets destructed.
  625. */
  626. static SpawnManager JustForLeaks;
  627. void SpawnManager::Add_Spawner( SpawnerClass * spawner )
  628. {
  629. WWASSERT(spawner != NULL);
  630. SpawnerList.Add( spawner );
  631. }
  632. void SpawnManager::Remove_Spawner( SpawnerClass * spawner )
  633. {
  634. SpawnerList.Delete( spawner );
  635. }
  636. void SpawnManager::Remove_All_Spawners( void )
  637. {
  638. while ( SpawnerList.Count() ) {
  639. delete SpawnerList[0];
  640. }
  641. }
  642. /*
  643. **
  644. */
  645. enum {
  646. CHUNKID_SPAWNER_DATA = 1014991133,
  647. CHUNKID_SPAWNER_VARIABLES,
  648. MICROCHUNKID_SPAWNER_AUTO_SPAWN_TIMER = 1,
  649. };
  650. bool SpawnManager::Save( ChunkSaveClass &csave )
  651. {
  652. csave.Begin_Chunk( CHUNKID_SPAWNER_VARIABLES );
  653. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SPAWNER_AUTO_SPAWN_TIMER, AutoSpawnTimer );
  654. csave.End_Chunk();
  655. for ( int i = 0; i < SpawnerList.Count(); i++ ) {
  656. csave.Begin_Chunk( CHUNKID_SPAWNER_DATA );
  657. SpawnerList[i]->Save( csave );
  658. csave.End_Chunk();
  659. }
  660. return true;
  661. }
  662. bool SpawnManager::Load( ChunkLoadClass &cload )
  663. {
  664. Remove_All_Spawners();
  665. while (cload.Open_Chunk()) {
  666. switch(cload.Cur_Chunk_ID()) {
  667. case CHUNKID_SPAWNER_VARIABLES:
  668. while (cload.Open_Micro_Chunk()) {
  669. switch(cload.Cur_Micro_Chunk_ID()) {
  670. READ_MICRO_CHUNK( cload, MICROCHUNKID_SPAWNER_AUTO_SPAWN_TIMER, AutoSpawnTimer );
  671. default:
  672. Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__));
  673. break;
  674. }
  675. cload.Close_Micro_Chunk();
  676. }
  677. break;
  678. case CHUNKID_SPAWNER_DATA:
  679. {
  680. SpawnerClass * spawner = new SpawnerClass();
  681. spawner->Load( cload );
  682. break;
  683. }
  684. default:
  685. Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
  686. break;
  687. }
  688. cload.Close_Chunk();
  689. }
  690. return true;
  691. }
  692. /*
  693. **
  694. */
  695. bool SpawnManager::Spawner_Exists( int player_type )
  696. {
  697. for ( int i = 0; i < SpawnerList.Count(); i++ ) {
  698. if ( SpawnerList[i]->Get_Definition().PlayerType == player_type ) {
  699. return true;
  700. }
  701. }
  702. return false;
  703. }
  704. Matrix3D SpawnManager::Get_Primary_Spawn_Location( void )
  705. {
  706. int index = -1;
  707. for (int i = 0; i < SpawnerList.Count(); i++) {
  708. if ( SpawnerList[i]->Get_Definition().IsPrimary &&
  709. SpawnerList[i]->Get_Definition().IsSoldierStartup ) {
  710. index = i;
  711. break;
  712. }
  713. }
  714. if (index == -1) {
  715. Debug_Say(("Get_Primary_Spawn_Location: failed to find suitable spawner, return origin.\n"));
  716. return Matrix3D(1);
  717. } else {
  718. return SpawnerList[index]->Get_TM();
  719. }
  720. }
  721. Matrix3D SpawnManager::Get_Multiplayer_Spawn_Location( int player_type, SoldierGameObj * soldier )
  722. {
  723. if (player_type == PLAYERTYPE_NEUTRAL) {
  724. //
  725. // Use renegade spawner
  726. //
  727. player_type = PLAYERTYPE_RENEGADE;
  728. }
  729. //
  730. // Find out how many suitable spawners there are
  731. //
  732. int suitable_spawners = 0;
  733. for ( int i = 0; i < SpawnerList.Count(); i++ ) {
  734. if ( !SpawnerList[i]->Get_Definition().IsPrimary &&
  735. SpawnerList[i]->Get_Definition().IsSoldierStartup &&
  736. SpawnerList[i]->Get_Definition().PlayerType == player_type ) {
  737. suitable_spawners++;
  738. }
  739. }
  740. Matrix3D tm;
  741. if (suitable_spawners == 0) {
  742. Debug_Say(("Get_Multiplayer_Spawn_Location: failed to find suitable spawner, return origin.\n"));
  743. tm = Matrix3D(1);
  744. } else {
  745. //
  746. // Randomly choose one of the suitable spawners
  747. //
  748. int i;
  749. int selected_spawner = rand() % suitable_spawners;
  750. int start_index = 0;
  751. int count = 0;
  752. for ( i = 0; i < SpawnerList.Count(); i++ )
  753. {
  754. if ( !SpawnerList[i]->Get_Definition().IsPrimary &&
  755. SpawnerList[i]->Get_Definition().IsSoldierStartup &&
  756. SpawnerList[i]->Get_Definition().PlayerType == player_type )
  757. {
  758. if (count == selected_spawner)
  759. {
  760. // As a fallback, we will always return the first transform
  761. // that was selected.
  762. tm = SpawnerList[i]->Get_TM();
  763. start_index = i;
  764. break;
  765. }
  766. count++;
  767. }
  768. }
  769. //
  770. // Loop through the entire list of spawners, starting with the one
  771. // we just selected, until we find a clear spawn point for this soldier
  772. //
  773. Phys3Class * phys_obj = soldier->Peek_Human_Phys();
  774. if (phys_obj != NULL) {
  775. for ( i = 0; i < SpawnerList.Count(); i++) {
  776. // Wrap around the list if needed:
  777. int index = (i + start_index) % SpawnerList.Count();
  778. if ( !SpawnerList[index]->Get_Definition().IsPrimary &&
  779. SpawnerList[index]->Get_Definition().IsSoldierStartup &&
  780. SpawnerList[index]->Get_Definition().PlayerType == player_type )
  781. {
  782. if (phys_obj->Can_Teleport(SpawnerList[index]->Get_TM(),true)) {
  783. // Return the first good spawn point we find!
  784. return SpawnerList[index]->Get_TM();
  785. }
  786. }
  787. }
  788. // If we fall through to here, no clear spawn points were found, need more spawners!
  789. WWDEBUG_SAY(("Failed to find clear multiplayer spawn point for object: %s\r\n",phys_obj->Peek_Model()->Get_Name()));
  790. }
  791. }
  792. // Fallback - return the spawn point we randomly chose.
  793. return tm;
  794. }
  795. Matrix3D SpawnManager::Get_Ctf_Spawn_Location(int team)
  796. {
  797. WWASSERT(team == PLAYERTYPE_NOD || team == PLAYERTYPE_GDI);
  798. StringClass spawner_name;
  799. if (team == PLAYERTYPE_NOD) {
  800. spawner_name = "Ctf_Pedestal_Nod";
  801. } else {
  802. spawner_name = "Ctf_Pedestal_GDI";
  803. }
  804. int index = -1;
  805. for (int i = 0; i < SpawnerList.Count(); i++) {
  806. if ( !spawner_name.Compare(SpawnerList[i]->Get_Definition().Get_Name()) ) {
  807. index = i;
  808. break;
  809. }
  810. }
  811. //WWASSERT(index != -1);
  812. if (index == -1)
  813. {
  814. Debug_Say(("*** Fatal Media Error: CTF spawner(s) missing from level.\n"));
  815. DIE;
  816. }
  817. return SpawnerList[index]->Get_TM();
  818. }
  819. SpawnerClass * SpawnManager::Get_Primary_Spawner( void )
  820. {
  821. int index = -1;
  822. for (int i = 0; i < SpawnerList.Count(); i++) {
  823. if ( SpawnerList[i]->Get_Definition().IsPrimary &&
  824. SpawnerList[i]->Get_Definition().IsSoldierStartup ) {
  825. index = i;
  826. break;
  827. }
  828. }
  829. if (index == -1) {
  830. Debug_Say(("Get_Primary_Spawner: failed to find suitable spawner, returning NULL.\n"));
  831. return NULL;
  832. } else {
  833. return SpawnerList[index];
  834. }
  835. }
  836. void SpawnManager::Update( void )
  837. {
  838. AutoSpawnTimer += TimeManager::Get_Frame_Seconds();
  839. if ( CombatManager::I_Am_Server() ) {
  840. if (AutoSpawnTimer >= AUTO_SPAWN_CHECK_DELAY ) {
  841. // Let each Spawner Check for Auto Spwan
  842. for ( int i = 0; i < SpawnerList.Count(); i++ ) {
  843. //
  844. // Soldiers are only spawned in missions (SP, coop)
  845. //
  846. if (!SpawnerList[i]->Get_Definition().IsSoldierStartup ||
  847. //CombatManager::Is_Mission()) {
  848. IS_SOLOPLAY) {
  849. SpawnerList[i]->Check_Auto_Spawn( AutoSpawnTimer );
  850. }
  851. }
  852. AutoSpawnTimer = 0;
  853. }
  854. }
  855. }
  856. PhysicalGameObj * SpawnManager::Spawner_Trigger( int id )
  857. {
  858. for ( int i = 0; i < SpawnerList.Count(); i++ ) {
  859. if ( SpawnerList[i]->Get_ID() == id ) {
  860. return SpawnerList[i]->Spawn();
  861. }
  862. }
  863. return NULL;
  864. }
  865. void SpawnManager::Spawner_Enable( int id, bool enable )
  866. {
  867. for ( int i = 0; i < SpawnerList.Count(); i++ ) {
  868. if ( SpawnerList[i]->Get_ID() == id ) {
  869. if ( enable ) {
  870. // force an immeadiate spawn check
  871. AutoSpawnTimer = AUTO_SPAWN_CHECK_DELAY;
  872. }
  873. SpawnerList[i]->Enable( enable );
  874. }
  875. }
  876. }
  877. void SpawnManager::Display_Unused_Spawners( void )
  878. {
  879. Debug_Say(( "Unused Spawner Display....\n" ));
  880. for ( int i = 0; i < SpawnerList.Count(); i++ ) {
  881. if ( SpawnerList[i]->SpawnCount == 0 ) {
  882. Debug_Say(( "Spawner %d never Spawned\n", SpawnerList[i]->Get_ID() ));
  883. }
  884. }
  885. }
  886. bool SpawnManager::Toggle_Allow_Killing_Hibernating_Spawn( void )
  887. {
  888. _Allow_Killing_Hibernating_Spawn = !_Allow_Killing_Hibernating_Spawn;
  889. return _Allow_Killing_Hibernating_Spawn;
  890. }
  891. /***********************************************************************************************
  892. * SpawnManager::~SpawnManager -- Class destructor *
  893. * *
  894. * *
  895. * *
  896. * INPUT: Nothing *
  897. * *
  898. * OUTPUT: Nothing *
  899. * *
  900. * WARNINGS: None *
  901. * *
  902. * HISTORY: *
  903. * 6/14/2001 2:27PM ST : Created *
  904. *=============================================================================================*/
  905. SpawnManager::~SpawnManager(void)
  906. {
  907. Remove_All_Spawners();
  908. }