god.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  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. // Filename: god.cpp
  20. // Author: Tom Spencer-Smith
  21. // Date: Dec 1998
  22. // Description: This class handles the creations of players and soldiers.
  23. //
  24. #include "god.h"
  25. #include "cnetwork.h"
  26. #include "playermanager.h"
  27. #include "gameobjmanager.h"
  28. #include "spawn.h"
  29. #include "playertype.h"
  30. #include "objlibrary.h"
  31. #include "soldierobserver.h"
  32. #include "gametype.h"
  33. #include "dialogtests.h"
  34. #include "combatgmode.h"
  35. #include "gameinitmgr.h"
  36. #include "scripts.h"
  37. #include "debug.h"
  38. #include "renegadedialogmgr.h"
  39. #include "cheatmgr.h"
  40. #include "wwmemlog.h"
  41. #include "dialogmgr.h"
  42. #include "encyclopediamgr.h"
  43. #include "wolgmode.h"
  44. #include "specialbuilds.h"
  45. #include "demosupport.h"
  46. /*
  47. **
  48. */
  49. typedef enum {
  50. GOD_STATE_UNINITIALIZED,
  51. GOD_STATE_MULTIPLAYER,
  52. GOD_STATE_EXITING,
  53. GOD_STATE_SINGLE_INIT,
  54. GOD_STATE_SINGLE_RUNNING,
  55. GOD_STATE_SINGLE_DEAD,
  56. } GodState;
  57. int cGod::State = GOD_STATE_UNINITIALIZED;
  58. InventoryClass cGod::LevelStartInventory;
  59. //-----------------------------------------------------------------------------
  60. enum {
  61. CHUNKID_VARIABLES = 112374,
  62. MICROCHUNK_STATE = 1,
  63. };
  64. //-----------------------------------------------------------------------------
  65. bool cGod::Save(ChunkSaveClass & csave)
  66. {
  67. csave.Begin_Chunk(CHUNKID_VARIABLES);
  68. WRITE_MICRO_CHUNK(csave, MICROCHUNK_STATE, State);
  69. csave.End_Chunk();
  70. return true;
  71. }
  72. //-----------------------------------------------------------------------------
  73. bool cGod::Load(ChunkLoadClass &cload)
  74. {
  75. while (cload.Open_Chunk()) {
  76. switch(cload.Cur_Chunk_ID()) {
  77. case CHUNKID_VARIABLES:
  78. while (cload.Open_Micro_Chunk()) {
  79. switch(cload.Cur_Micro_Chunk_ID()) {
  80. READ_MICRO_CHUNK(cload, MICROCHUNK_STATE, State);
  81. default:
  82. Debug_Say(( "Unrecognized cGod Variable chunkID\n" ));
  83. break;
  84. }
  85. cload.Close_Micro_Chunk();
  86. }
  87. break;
  88. default:
  89. Debug_Say(( "Unrecognized cPlayer chunkID\n" ));
  90. break;
  91. }
  92. cload.Close_Chunk();
  93. }
  94. return true;
  95. }
  96. //-----------------------------------------------------------------------------
  97. void cGod::Think(void)
  98. {
  99. WWASSERT(cNetwork::I_Am_Server());
  100. //if (The_Game()->IsIntermission.Is_True()) {
  101. WWASSERT(PTheGameData != NULL);
  102. if (The_Game()->IsIntermission.Is_True() ||
  103. cPlayerManager::Get_Player_Object_List()->Head() == NULL) {
  104. return;
  105. }
  106. if ( State == GOD_STATE_UNINITIALIZED ) {
  107. //XXX
  108. State = ( IS_MISSION ) ? GOD_STATE_SINGLE_INIT : GOD_STATE_MULTIPLAYER;
  109. }
  110. if ( State == GOD_STATE_SINGLE_INIT ) {
  111. WWASSERT( cPlayerManager::Get_Player_Object_List()->Head() != NULL );
  112. // Create a Commando for the Player
  113. SoldierGameObj * soldier = Create_Commando( cPlayerManager::Get_Player_Object_List()->Head()->Data() );
  114. const char * script_name = CombatManager::Get_Start_Script();
  115. ScriptClass* script = ScriptManager::Create_Script( script_name );
  116. if (script) {
  117. soldier->Add_Observer( script );
  118. }
  119. State = GOD_STATE_SINGLE_RUNNING;
  120. }
  121. // This code may need to get cleaned up
  122. if ( State == GOD_STATE_SINGLE_RUNNING ) {
  123. //If we just loaded, we may have a play and a solder, but they will not be linked.
  124. if (cPlayerManager::Get_Player_Object_List() != NULL &&
  125. cPlayerManager::Get_Player_Object_List()->Head() != NULL ) {
  126. cPlayer * p_player = cPlayerManager::Get_Player_Object_List()->Head()->Data();
  127. if ( p_player != NULL ) {
  128. SmartGameObj * p_soldier = GameObjManager::Find_Soldier_Of_Client_ID(p_player->Get_Id());
  129. if ( p_soldier && p_player->Get_GameObj() == NULL ) {
  130. // Remap
  131. p_soldier->Set_Player_Data( p_player );
  132. Debug_Say(( "Fixing up player data after load\n" ));
  133. }
  134. }
  135. }
  136. }
  137. DEMO_SECURITY_CHECK;
  138. //
  139. // Take a look through the player list and create commando bodies
  140. // for anyone who merits one
  141. //
  142. if ( State == GOD_STATE_MULTIPLAYER ) {
  143. for (
  144. SLNode<cPlayer> * objnode = cPlayerManager::Get_Player_Object_List()->Head();
  145. objnode;
  146. objnode = objnode->Next()) {
  147. cPlayer * p_player = objnode->Data();
  148. WWASSERT(p_player != NULL);
  149. if (p_player->Get_Is_Active().Is_False()) {
  150. continue;
  151. }
  152. if (p_player->Get_Is_In_Game().Is_False()) {
  153. continue;
  154. }
  155. SmartGameObj * p_soldier = GameObjManager::Find_Soldier_Of_Client_ID(p_player->Get_Id());
  156. if (p_soldier == NULL) {
  157. //
  158. // A disembodied player... give him a body
  159. //
  160. Create_Commando(p_player);
  161. }
  162. }
  163. }
  164. }
  165. //-----------------------------------------------------------------------------
  166. cPlayer * cGod::Create_Player(int client_id, const WideStringClass & name,
  167. int team_choice, unsigned long clanID, bool is_invulnerable)
  168. {
  169. WWMEMLOG(MEM_NETWORK);
  170. WWASSERT(cNetwork::I_Am_Server());
  171. WWASSERT(PTheGameData != NULL);
  172. WWASSERT(cPlayerManager::Count() < The_Game()->Get_Max_Players());
  173. //
  174. // Assign a player type
  175. //
  176. cPlayer * p_player = cPlayerManager::Find_Player(name);
  177. if (p_player != NULL) {
  178. //
  179. // I think this can happen when a player crashes out and then rejoins
  180. // before the server breaks his connection...
  181. //
  182. cNetwork::Delete_Player_Objects(p_player->Get_Id());
  183. } else {
  184. p_player = cPlayerManager::Find_Inactive_Player(name);
  185. }
  186. bool is_new = false;
  187. if (p_player == NULL) {
  188. p_player = new cPlayer();
  189. WWASSERT(p_player != NULL);
  190. is_new = true;
  191. p_player->Set_Name(name);
  192. p_player->Set_Id(client_id);
  193. p_player->Set_WOL_ClanID(clanID);
  194. int player_type = The_Game()->Choose_Player_Type(p_player, team_choice, false);
  195. p_player->Set_Player_Type(player_type);
  196. } else {
  197. p_player->Set_Id(client_id);
  198. p_player->Set_Is_In_Game(true);
  199. p_player->Set_Is_Waiting_For_Intermission(false);
  200. }
  201. p_player->Reset_Join_Time();
  202. p_player->Invulnerable.Set(is_invulnerable);
  203. p_player->Set_Is_Active(true);
  204. //
  205. // Tell everyone about this guy
  206. //
  207. if (is_new) {
  208. p_player->Init();
  209. GameModeClass* gameMode = GameModeManager::Find("WOL");
  210. if (gameMode && gameMode->Is_Active()) {
  211. WolGameModeClass* wolGame = reinterpret_cast<WolGameModeClass*>(gameMode);
  212. wolGame->Init_WOL_Player(p_player);
  213. }
  214. }
  215. return p_player;
  216. }
  217. //-----------------------------------------------------------------------------
  218. void cGod::Create_Ai_Player(void)
  219. {
  220. WWASSERT(PTheGameData != NULL);
  221. WWASSERT(cPlayerManager::Count() < The_Game()->Get_Max_Players());
  222. WWASSERT(cNetwork::I_Am_Server());
  223. //
  224. // For id, count downwards from -2.
  225. //
  226. int client_id = -1;//SmartGameObj::MOBIUS_CONTROL_OWNER;
  227. WideStringClass name;
  228. do {
  229. client_id--;
  230. WWASSERT(client_id > cPlayer::INVALID_ID);
  231. name.Format(L"Guard%d", -client_id);
  232. } while (cPlayerManager::Is_Player_Present(name));
  233. Create_Player(client_id, name, -1, 0);
  234. }
  235. //-----------------------------------------------------------------------------
  236. SoldierGameObj * cGod::Create_Commando(int client_id, int player_type/*, int model_num*/)
  237. {
  238. WWASSERT(cNetwork::I_Am_Server());
  239. WWASSERT(player_type >= PLAYERTYPE_NEUTRAL && player_type <= PLAYERTYPE_LAST);
  240. WWASSERT(PTheGameData != NULL);
  241. StringClass preset_name;
  242. preset_name.Format("Commando");
  243. if (IS_MISSION) {
  244. #ifndef MULTIPLAYERDEMO
  245. SpawnerClass * p_spawner = SpawnManager::Get_Primary_Spawner();
  246. if (p_spawner != NULL) {
  247. const DynamicVectorClass<int> & def_list = p_spawner->Get_Definition().Get_Spawn_Definition_ID_List();
  248. WWASSERT(def_list.Count() >= 1);
  249. PhysicalGameObjDef * p_def = (PhysicalGameObjDef *)DefinitionMgrClass::Find_Definition(def_list[0]);
  250. if (p_def != NULL) {
  251. preset_name.Format("%s", p_def->Get_Name());
  252. }
  253. }
  254. #endif // !MULTIPLAYERDEMO
  255. } else if (The_Game()->Is_Cnc() || The_Game()->Is_Skirmish()) {
  256. if (player_type == PLAYERTYPE_NOD) {
  257. preset_name.Format("CnC_Nod_Minigunner_0");
  258. } else {
  259. preset_name.Format("CnC_GDI_MiniGunner_0");
  260. }
  261. }
  262. WWASSERT(!preset_name.Is_Empty());
  263. PhysicalGameObj * p_phys_obj = ObjectLibraryManager::Create_Object(preset_name);
  264. WWASSERT(p_phys_obj != NULL);
  265. SoldierGameObj * p_soldier = p_phys_obj->As_SoldierGameObj();
  266. WWASSERT(p_soldier != NULL);
  267. WWASSERT(p_soldier->Peek_Physical_Object() != NULL);
  268. if (IS_SOLOPLAY) {
  269. // Setup initial health depending on difficulty level
  270. float max = 100.0f;
  271. switch ( CombatManager::Get_Difficulty_Level() ) {
  272. case 0: max = 200; break;
  273. case 1: max = 100; break;
  274. case 2: max = 75; break;
  275. };
  276. p_soldier->Get_Defense_Object()->Set_Health_Max( max );
  277. p_soldier->Get_Defense_Object()->Set_Health( max );
  278. p_soldier->Get_Defense_Object()->Set_Shield_Strength_Max( max );
  279. p_soldier->Get_Defense_Object()->Set_Shield_Strength( max );
  280. }
  281. Matrix3D transform;
  282. if (IS_MISSION && player_type == PLAYERTYPE_GDI) {
  283. transform = SpawnManager::Get_Primary_Spawn_Location();
  284. } else {
  285. transform = SpawnManager::Get_Multiplayer_Spawn_Location(player_type,p_soldier);
  286. }
  287. p_soldier->Set_Transform(transform);
  288. p_soldier->Set_Control_Owner(client_id);
  289. cPlayer * player = cPlayerManager::Find_Player( client_id );
  290. p_soldier->Set_Player_Data( player );
  291. if ( The_Game()->Remember_Inventory() ) {
  292. if (IS_MISSION) {
  293. cGod::Restore_Inventory( p_soldier );
  294. }
  295. }
  296. p_soldier->Set_Player_Type(player_type);
  297. //
  298. // TSS082901 - We cannot remove all observers - there may be scripts granting
  299. // initial weapons. Instead, make sure the presets don't UseInnateBehavior
  300. //
  301. //p_soldier->Remove_All_Observers();
  302. if (!p_soldier->Is_Human_Controlled()) {
  303. //
  304. // Add an observer to do innate AI
  305. //
  306. p_soldier->Set_Innate_Observer(new SoldierObserverClass);
  307. p_soldier->Add_Observer(p_soldier->Get_Innate_Observer());
  308. }
  309. //
  310. // Added this 090401
  311. //
  312. p_soldier->Start_Observers();
  313. The_Game()->Soldier_Added(p_soldier);
  314. if (cNetwork::I_Am_Client() && client_id == cNetwork::Get_My_Id()) {
  315. ActionParamsStruct parameters;
  316. WWASSERT(p_soldier->Get_Action() != NULL);
  317. p_soldier->Get_Action()->Follow_Input(parameters);
  318. CombatManager::Set_The_Star(p_soldier);
  319. //
  320. // Let the cheat manager apply its cheats to the new player
  321. //
  322. CheatMgrClass::Get_Instance()->Apply_Cheats();
  323. #ifdef WWDEBUG
  324. Reinitialize_Ai_On_Star();
  325. #endif // WWDEBUG
  326. }
  327. return p_soldier;
  328. }
  329. //-----------------------------------------------------------------------------
  330. SoldierGameObj * cGod::Create_Commando(cPlayer * p_player)
  331. {
  332. WWASSERT(cNetwork::I_Am_Server());
  333. WWASSERT(p_player != NULL);
  334. int client_id = p_player->Get_Id();
  335. int player_type = p_player->Get_Player_Type();
  336. //int model_num = p_player->Get_Model();
  337. return Create_Commando(client_id, player_type/*, model_num*/);
  338. }
  339. //-----------------------------------------------------------------------------
  340. void cGod::Create_Grunt(Vector3 & pos)
  341. {
  342. WWASSERT(cNetwork::I_Am_Server());
  343. int client_id = SmartGameObj::SERVER_CONTROL_OWNER;
  344. WWASSERT(PTheGameData != NULL);
  345. int player_type = The_Game()->Choose_Player_Type(NULL, -1, true);
  346. //int model_num = rand() % NUM_MP_PLAYABLE_MODELS;
  347. SoldierGameObj * p_soldier = Create_Commando(
  348. client_id, player_type/*, model_num*/);
  349. WWASSERT(p_soldier != NULL);
  350. p_soldier->Set_Position(pos);
  351. p_soldier->Perturb_Position();
  352. }
  353. //-----------------------------------------------------------------------------
  354. #ifdef WWDEBUG
  355. void cGod::Reinitialize_Ai_On_Star(void)
  356. {
  357. WWASSERT(cNetwork::I_Am_Client());
  358. SmartGameObj * p_my_soldier = GameObjManager::Find_Soldier_Of_Client_ID(cNetwork::Get_My_Id());
  359. if (p_my_soldier != NULL) {
  360. //
  361. // Remove any innate observers
  362. //
  363. const GameObjObserverList & observer_list = p_my_soldier->Get_Observers();
  364. for (int index = 0; index < observer_list.Count(); index++) {
  365. if (!stricmp(observer_list[index]->Get_Name(), "Innate Soldier")) {
  366. p_my_soldier->Remove_Observer(observer_list[index]);
  367. break; // probably not safe to continue
  368. }
  369. }
  370. cPlayer * p_player = cNetwork::Get_My_Player_Object();
  371. WWASSERT(p_player != NULL);
  372. ActionParamsStruct parameters;
  373. WWASSERT(p_my_soldier->Get_Action() != NULL);
  374. p_my_soldier->Get_Action()->Follow_Input(parameters);
  375. CombatManager::Set_Is_Star_Determining_Target(true);
  376. }
  377. }
  378. #endif // WWDEBUG
  379. //-----------------------------------------------------------------------------
  380. Matrix3D _StarRespawnTM;
  381. InventoryClass _DeathInventory;
  382. void cGod::Reset( void )
  383. {
  384. State = GOD_STATE_UNINITIALIZED;
  385. }
  386. void cGod::Exit( void )
  387. {
  388. State = GOD_STATE_EXITING;
  389. }
  390. void cGod::Star_Killed( void )
  391. {
  392. if ( State == GOD_STATE_SINGLE_RUNNING ) {
  393. State = GOD_STATE_SINGLE_DEAD;
  394. WWDEBUG_SAY(( "Star Killed\n" ));
  395. _DeathInventory.Store_Inventory( COMBAT_STAR );
  396. _StarRespawnTM = COMBAT_STAR->Get_Transform();
  397. DeathOptionsPopupClass * popup = new DeathOptionsPopupClass;
  398. popup->Start_Dialog();
  399. popup->Release_Ref();
  400. } else if ( State == GOD_STATE_MULTIPLAYER ) {
  401. if (GameModeManager::Find ("Combat")->Is_Active ()) {
  402. if (GameModeManager::Find ("Menu")->Is_Active ()) {
  403. GameModeManager::Find ("Menu")->Deactivate ();
  404. } else {
  405. DialogMgrClass::Flush_Dialogs ();
  406. }
  407. }
  408. }
  409. }
  410. void cGod::Respawn( void )
  411. {
  412. WWASSERT( State == GOD_STATE_SINGLE_DEAD );
  413. SoldierGameObj * soldier = Create_Commando( cPlayerManager::Get_Player_Object_List()->Head()->Data() );
  414. soldier->Set_Transform( _StarRespawnTM );
  415. _DeathInventory.Restore_Inventory( soldier );
  416. const char * script_name = CombatManager::Get_Respawn_Script();
  417. ScriptClass* script = ScriptManager::Create_Script( script_name );
  418. if (script) {
  419. soldier->Add_Observer( script );
  420. }
  421. State = GOD_STATE_SINGLE_RUNNING;
  422. }
  423. void cGod::Restart( void )
  424. {
  425. if ( State == GOD_STATE_SINGLE_DEAD ) {
  426. // WWASSERT( State == GOD_STATE_SINGLE_DEAD );
  427. State = GOD_STATE_SINGLE_RUNNING; // Incase we get a second call!
  428. ((CombatGameModeClass *)GameModeManager::Find("Combat"))->Core_Restart();
  429. //
  430. // Reset the player's stats
  431. //
  432. cPlayer *player_info = cPlayerManager::Get_Player_Object_List()->Head()->Data();
  433. if ( player_info != NULL ) {
  434. player_info->Stats_Reset();
  435. }
  436. SoldierGameObj * soldier = Create_Commando( player_info );
  437. const char * script_name = CombatManager::Get_Start_Script();
  438. ScriptClass* script = ScriptManager::Create_Script( script_name );
  439. if (script) {
  440. soldier->Add_Observer( script );
  441. }
  442. State = GOD_STATE_SINGLE_RUNNING; // restart makes it exit
  443. }
  444. }
  445. void cGod::Load_Game( void )
  446. {
  447. WWASSERT( State == GOD_STATE_SINGLE_DEAD );
  448. GameInitMgrClass::End_Game();
  449. RenegadeDialogMgrClass::Goto_Location (RenegadeDialogMgrClass::LOC_LOAD_GAME);
  450. }
  451. void cGod::Mission_Failed( void )
  452. {
  453. if ( State == GOD_STATE_SINGLE_RUNNING ) {
  454. State = GOD_STATE_SINGLE_DEAD;
  455. WWDEBUG_SAY(( "Mission Failed\n" ));
  456. FailedOptionsPopupClass * popup = new FailedOptionsPopupClass;
  457. popup->Start_Dialog();
  458. popup->Release_Ref();
  459. }
  460. }
  461. void cGod::Store_Inventory( SoldierGameObj * soldier )
  462. {
  463. LevelStartInventory.Store_Inventory( soldier );
  464. EncyclopediaMgrClass::Store_Data();
  465. return ;
  466. }
  467. void cGod::Restore_Inventory( SoldierGameObj * soldier )
  468. {
  469. LevelStartInventory.Restore_Inventory( soldier );
  470. EncyclopediaMgrClass::Restore_Data();
  471. return ;
  472. }
  473. void cGod::Reset_Inventory( void )
  474. {
  475. LevelStartInventory.Reset();
  476. }