LOGIC.CPP 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. //
  2. // Copyright 2020 Electronic Arts Inc.
  3. //
  4. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free
  5. // software: you can redistribute it and/or modify it under the terms of
  6. // the GNU General Public License as published by the Free Software Foundation,
  7. // either version 3 of the License, or (at your option) any later version.
  8. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
  9. // in the hope that it will be useful, but with permitted additional restrictions
  10. // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
  11. // distributed with this program. You should have received a copy of the
  12. // GNU General Public License along with permitted additional restrictions
  13. // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
  14. /* $Header: F:\projects\c&c\vcs\code\logic.cpv 2.17 16 Oct 1995 16:50:52 JOE_BOSTIC $ */
  15. /***********************************************************************************************
  16. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  17. ***********************************************************************************************
  18. * *
  19. * Project Name : Command & Conquer *
  20. * *
  21. * File Name : LOGIC.CPP *
  22. * *
  23. * Programmer : Joe L. Bostic *
  24. * *
  25. * Start Date : September 27, 1993 *
  26. * *
  27. * Last Update : December 23, 1994 [JLB] *
  28. * *
  29. *---------------------------------------------------------------------------------------------*
  30. * Functions: *
  31. * LogicClass::AI -- Handles AI logic processing for game objects. *
  32. * LogicClass::Debug_Dump -- Displays logic class status to the mono screen. *
  33. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  34. #include "function.h"
  35. #include "logic.h"
  36. static unsigned FramesPerSecond=0;
  37. #ifdef CHEAT_KEYS
  38. static unsigned TotalFrames;
  39. static unsigned FPSDivider = 1;
  40. static unsigned AverageFramesPerSecond;
  41. /***********************************************************************************************
  42. * LogicClass::Debug_Dump -- Displays logic class status to the mono screen. *
  43. * *
  44. * This is a debugging support routine. It displays the current state of the logic class *
  45. * to the monochrome monitor. It assumes that it is being called once per second. *
  46. * *
  47. * INPUT: none *
  48. * *
  49. * OUTPUT: none *
  50. * *
  51. * WARNINGS: Call this routine only once per second. *
  52. * *
  53. * HISTORY: *
  54. * 05/31/1994 JLB : Created. *
  55. *=============================================================================================*/
  56. void LogicClass::Debug_Dump(MonoClass *mono) const
  57. {
  58. #define RECORDCOUNT 40
  59. #define RECORDHEIGHT 21
  60. static struct {
  61. int Graphic;
  62. } _record[RECORDCOUNT];
  63. static int _framecounter = 0;
  64. TotalFrames+= FramesPerSecond;
  65. AverageFramesPerSecond = TotalFrames/FPSDivider++;
  66. mono->Set_Cursor(21, 9);
  67. mono->Print(
  68. "ÚÄÄÄÄÄÄÄÄÄÄÂÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\r"
  69. "³Units.....³ ³Frame Rate: Avg: Frame: ³\r"
  70. "³Infantry..³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\r"
  71. "³Aircraft..³ ³ ³\r"
  72. "³Buildings.³ ³ ³\r"
  73. "³Terrain...³ Ã ´\r"
  74. "³Bullets...³ ³ ³\r"
  75. "³Anims.....³ ³ ³\r"
  76. "³Teams.....³ Ã Ä´\r"
  77. "³Triggers..³ ³ ³\r"
  78. "³Factories.³ ³ ³\r"
  79. "³ ³ Ã ´\r"
  80. "³ ³ ³ ³\r"
  81. "ÀÄÄÄÄÄÄÄÄÄÄÁÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄ´Spare CPU TimeÃÄÄÄÄÄÄÄÄÄÄÄÄÙ\r");
  82. _framecounter++;
  83. mono->Set_Cursor(70, 10);mono->Printf("%ld", Frame);
  84. if (ScenarioInit) {
  85. mono->Set_Cursor(21, 9);mono->Printf("%d", ScenarioInit);
  86. }
  87. mono->Set_Cursor(33, 10);mono->Printf("%3d", Units.Count());
  88. mono->Set_Cursor(33, 11);mono->Printf("%3d", Infantry.Count());
  89. mono->Set_Cursor(33, 12);mono->Printf("%3d", Aircraft.Count());
  90. mono->Set_Cursor(33, 13);mono->Printf("%3d", Buildings.Count());
  91. mono->Set_Cursor(33, 14);mono->Printf("%3d", Terrains.Count());
  92. mono->Set_Cursor(33, 15);mono->Printf("%3d", Bullets.Count());
  93. mono->Set_Cursor(33, 16);mono->Printf("%3d", Anims.Count());
  94. mono->Set_Cursor(33, 17);mono->Printf("%3d", Teams.Count());
  95. mono->Set_Cursor(33, 18);mono->Printf("%3d", Triggers.Count());
  96. mono->Set_Cursor(33, 19);mono->Printf("%3d", Factories.Count());
  97. mono->Set_Cursor(48, 10);mono->Printf("%d", FramesPerSecond);
  98. mono->Set_Cursor(58, 10);mono->Printf("%d", AverageFramesPerSecond);
  99. /*
  100. ** Advance to the next recorded performance record. If the record buffer
  101. ** is full then throw out the oldest record.
  102. */
  103. memcpy(&_record[0], &_record[1], sizeof(_record[0])*(RECORDCOUNT-1));
  104. /*
  105. ** Fill in the data for the current frame's performance record.
  106. */
  107. SpareTicks = MIN((long)SpareTicks, (long)TIMER_SECOND);
  108. _record[RECORDCOUNT-1].Graphic = Fixed_To_Cardinal(RECORDHEIGHT, Cardinal_To_Fixed(TIMER_SECOND, SpareTicks));
  109. /*
  110. ** Draw the bars across the performance record screen.
  111. */
  112. for (int column = 0; column < RECORDCOUNT; column++) {
  113. for (int row = 1; row < RECORDHEIGHT; row += 2) {
  114. static unsigned char _barchar[4] = {' ', 220, 0, 219};
  115. char str[2];
  116. int index = 0;
  117. index |= (_record[column].Graphic >= row) ? 0x01 : 0x00;
  118. index |= (_record[column].Graphic >= row+1) ? 0x02: 0x00;
  119. str[1] = '\0';
  120. str[0] = _barchar[index];
  121. mono->Text_Print(str, 37+column, 21-(row/2));
  122. }
  123. }
  124. SpareTicks = 0;
  125. FramesPerSecond = 0;
  126. }
  127. #endif
  128. /***********************************************************************************************
  129. * LogicClass::AI -- Handles AI logic processing for game objects. *
  130. * *
  131. * This routine is used to perform the AI processing for all game objects. This includes *
  132. * all houses, factories, objects, and teams. *
  133. * *
  134. * INPUT: none *
  135. * *
  136. * OUTPUT: none *
  137. * *
  138. * WARNINGS: none *
  139. * *
  140. * HISTORY: *
  141. * 05/29/1994 JLB : Created. *
  142. * 12/17/1994 JLB : Must perform one complete pass rather than bailing early. *
  143. * 12/23/1994 JLB : Esures that no object gets skipped if it was deleted. *
  144. *=============================================================================================*/
  145. void LogicClass::AI(void)
  146. {
  147. int index;
  148. FramesPerSecond++;
  149. /*
  150. ** Crate regeneration is handled here.
  151. */
  152. if (GameToPlay != GAME_NORMAL && CrateMaker && CrateTimer.Expired()) {
  153. Map.Place_Random_Crate();
  154. CrateTimer = TICKS_PER_MINUTE * Random_Pick(7, 15);
  155. }
  156. /*
  157. ** Team AI is processed.
  158. */
  159. for (index = 0; index < Teams.Count(); index++) {
  160. Teams.Ptr(index)->AI();
  161. }
  162. // Heap_Dump_Check( "After Team AI" );
  163. /*
  164. ** AI for all sentient objects is processed.
  165. */
  166. for (index = 0; index < Count(); index++) {
  167. ObjectClass * obj = (*this)[index];
  168. int count = Count();
  169. obj->AI();
  170. /*
  171. ** If the object was destroyed in the process of performing its AI, then
  172. ** adjust the index so that no object gets skipped.
  173. */
  174. int count_diff = Count() - count;
  175. if (count_diff < 0) {
  176. index += count_diff;
  177. }
  178. }
  179. // Heap_Dump_Check( "After Object AI" );
  180. /*
  181. ** A second pass through the sentient objects is required so that the appropriate scan
  182. ** bits will be set for the owner house.
  183. */
  184. for (index = 0; index < Units.Count(); index++) {
  185. UnitClass const * unit = Units.Ptr(index);
  186. if (unit->IsLocked && (GameToPlay != GAME_NORMAL || !unit->House->IsHuman || unit->IsDiscoveredByPlayer)) {
  187. unit->House->NewUScan |= (1L << unit->Class->Type);
  188. if (!unit->IsInLimbo) unit->House->NewActiveUScan |= (1L << unit->Class->Type);
  189. }
  190. }
  191. for (index = 0; index < Infantry.Count(); index++) {
  192. InfantryClass const * infantry = Infantry.Ptr(index);
  193. if (infantry->IsLocked && (GameToPlay != GAME_NORMAL || !infantry->House->IsHuman || infantry->IsDiscoveredByPlayer)) {
  194. infantry->House->NewIScan |= (1L << infantry->Class->Type);
  195. if (!infantry->IsInLimbo) infantry->House->NewActiveIScan |= (1L << infantry->Class->Type);
  196. }
  197. }
  198. for (index = 0; index < Aircraft.Count(); index++) {
  199. AircraftClass const * aircraft = Aircraft.Ptr(index);
  200. if (aircraft->IsLocked && (GameToPlay != GAME_NORMAL || !aircraft->House->IsHuman || aircraft->IsDiscoveredByPlayer)) {
  201. aircraft->House->NewAScan |= (1L << aircraft->Class->Type);
  202. if (!aircraft->IsInLimbo) aircraft->House->NewActiveAScan |= (1L << aircraft->Class->Type);
  203. }
  204. }
  205. for (index = 0; index < Buildings.Count(); index++) {
  206. BuildingClass const * building = Buildings.Ptr(index);
  207. if (building->IsLocked && (GameToPlay != GAME_NORMAL || !building->House->IsHuman || building->IsDiscoveredByPlayer)) {
  208. building->House->NewBScan |= (1L << building->Class->Type);
  209. if (!building->IsInLimbo) building->House->NewActiveBScan |= (1L << building->Class->Type);
  210. }
  211. }
  212. // Heap_Dump_Check( "After Object AI 2" );
  213. #ifdef USE_RA_AI
  214. //
  215. // Added for RA AI in TD. ST - 7/26/2019 10:56AM
  216. //
  217. HouseClass::Recalc_Attributes();
  218. #endif // USE_RA_AI
  219. /*
  220. ** Map related logic is performed.
  221. */
  222. Map.Logic();
  223. // Heap_Dump_Check( "After Map.Logic" );
  224. /*
  225. ** Factory processing is performed.
  226. */
  227. for (index = 0; index < Factories.Count(); index++) {
  228. Factories.Ptr(index)->AI();
  229. }
  230. // Heap_Dump_Check( "After Factory AI" );
  231. #if (1)
  232. /*
  233. ** Changed integrated from RA to only call AI on the houses that need it. Without this change, AI houses immediately
  234. ** become paranoid at the start of a multiplayer match
  235. ** ST - 10/30/2019 11:15AM
  236. */
  237. if (GameToPlay != GAME_NORMAL) {
  238. for (HousesType house = HOUSE_MULTI1; house < HOUSE_COUNT; house++) {
  239. HouseClass * hptr = HouseClass::As_Pointer(house);
  240. if (hptr && hptr->IsActive) {
  241. hptr->AI();
  242. }
  243. }
  244. HouseClass* neutral_house = HouseClass::As_Pointer(HOUSE_NEUTRAL);
  245. if (neutral_house && neutral_house->IsActive) {
  246. neutral_house->AI();
  247. }
  248. HouseClass* jp_house = HouseClass::As_Pointer(HOUSE_JP);
  249. if (jp_house && jp_house->IsActive) {
  250. jp_house->AI();
  251. }
  252. } else {
  253. for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) {
  254. HouseClass * hptr = HouseClass::As_Pointer(house);
  255. if (hptr && hptr->IsActive) {
  256. hptr->AI();
  257. }
  258. }
  259. }
  260. #else
  261. /*
  262. ** House processing is performed.
  263. */
  264. for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) {
  265. HouseClass * hptr = HouseClass::As_Pointer(house);
  266. if (hptr && hptr->IsActive) {
  267. hptr->AI();
  268. }
  269. }
  270. #endif
  271. // Heap_Dump_Check( "After House AI" );
  272. }
  273. /***********************************************************************************************
  274. * LogicClass::Clear_Recently_Created_Bits -- Clear out the indicators that objects were *
  275. * recently created *
  276. * *
  277. * INPUT: none *
  278. * *
  279. * OUTPUT: none *
  280. * *
  281. * WARNINGS: none *
  282. * *
  283. * HISTORY: *
  284. * 8/19/2019 5:47PM ST : Created. *
  285. *=============================================================================================*/
  286. void LogicClass::Clear_Recently_Created_Bits(void)
  287. {
  288. for (int index = 0; index < Count(); index++) {
  289. ObjectClass * obj = (*this)[index];
  290. obj->IsRecentlyCreated = false;
  291. }
  292. }