LOGIC.CPP 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  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: /CounterStrike/LOGIC.CPP 1 3/03/97 10:25a 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 : July 30, 1996 [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. * LogicClass::Detach -- Detatch the specified target from the logic system. *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "function.h"
  36. #include "logic.h"
  37. #include "vortex.h"
  38. static unsigned FramesPerSecond=0;
  39. #ifdef CHEAT_KEYS
  40. /***********************************************************************************************
  41. * LogicClass::Debug_Dump -- Displays logic class status to the mono screen. *
  42. * *
  43. * This is a debugging support routine. It displays the current state of the logic class *
  44. * to the monochrome monitor. It assumes that it is being called once per second. *
  45. * *
  46. * INPUT: none *
  47. * *
  48. * OUTPUT: none *
  49. * *
  50. * WARNINGS: Call this routine only once per second. *
  51. * *
  52. * HISTORY: *
  53. * 05/31/1994 JLB : Created. *
  54. * 01/26/1996 JLB : Prints game time value. *
  55. *=============================================================================================*/
  56. void LogicClass::Debug_Dump(MonoClass * mono) const
  57. {
  58. #define RECORDCOUNT 40
  59. #define RECORDHEIGHT 21
  60. static int _framecounter = 0;
  61. static bool first = true;
  62. if (first) {
  63. first = false;
  64. mono->Set_Cursor(0, 0);
  65. mono->Print(Text_String(TXT_DEBUG_STRESS));
  66. }
  67. //mono->Set_Cursor(0,0);mono->Printf("%d", AllowVoice);
  68. _framecounter++;
  69. mono->Set_Cursor(1, 1);mono->Printf("%ld", (long)Scen.Timer);
  70. mono->Set_Cursor(10, 1);mono->Printf("%3d", FramesPerSecond);
  71. mono->Set_Cursor(1, 3);mono->Printf("%02d:%02d:%02d", Scen.Timer / TICKS_PER_HOUR, (Scen.Timer % TICKS_PER_HOUR)/TICKS_PER_MINUTE, (Scen.Timer % TICKS_PER_MINUTE)/TICKS_PER_SECOND);
  72. mono->Set_Cursor(1, 11);mono->Printf("%3d", Units.Count());
  73. mono->Set_Cursor(1, 12);mono->Printf("%3d", Infantry.Count());
  74. mono->Set_Cursor(1, 13);mono->Printf("%3d", Aircraft.Count());
  75. mono->Set_Cursor(1, 14);mono->Printf("%3d", Vessels.Count());
  76. mono->Set_Cursor(1, 15);mono->Printf("%3d", Buildings.Count());
  77. mono->Set_Cursor(1, 16);mono->Printf("%3d", Terrains.Count());
  78. mono->Set_Cursor(1, 17);mono->Printf("%3d", Bullets.Count());
  79. mono->Set_Cursor(1, 18);mono->Printf("%3d", Anims.Count());
  80. mono->Set_Cursor(1, 19);mono->Printf("%3d", Teams.Count());
  81. mono->Set_Cursor(1, 20);mono->Printf("%3d", Triggers.Count());
  82. mono->Set_Cursor(1, 21);mono->Printf("%3d", TriggerTypes.Count());
  83. mono->Set_Cursor(1, 22);mono->Printf("%3d", Factories.Count());
  84. SpareTicks = min((long)SpareTicks, (long)TIMER_SECOND);
  85. /*
  86. ** CPU utilization record.
  87. */
  88. mono->Sub_Window(15, 1, 6, 11);
  89. mono->Scroll();
  90. mono->Set_Cursor(0, 10);
  91. mono->Printf("%3d%%", ((TIMER_SECOND-SpareTicks)*100) / TIMER_SECOND);
  92. /*
  93. ** Update the frame rate log.
  94. */
  95. mono->Sub_Window(22, 1, 6, 11);
  96. mono->Scroll();
  97. mono->Set_Cursor(0, 10);
  98. mono->Printf("%4d", FramesPerSecond);
  99. /*
  100. ** Update the findpath calc record.
  101. */
  102. mono->Sub_Window(50, 1, 6, 11);
  103. mono->Scroll();
  104. mono->Set_Cursor(0, 10);
  105. mono->Printf("%4d", PathCount);
  106. PathCount = 0;
  107. /*
  108. ** Update the cell redraw record.
  109. */
  110. mono->Sub_Window(29, 1, 6, 11);
  111. mono->Scroll();
  112. mono->Set_Cursor(0, 10);
  113. mono->Printf("%5d", CellCount);
  114. CellCount = 0;
  115. /*
  116. ** Update the target scan record.
  117. */
  118. mono->Sub_Window(36, 1, 6, 11);
  119. mono->Scroll();
  120. mono->Set_Cursor(0, 10);
  121. mono->Printf("%5d", TargetScan);
  122. TargetScan = 0;
  123. /*
  124. ** Sidebar redraw record.
  125. */
  126. mono->Sub_Window(43, 1, 6, 11);
  127. mono->Scroll();
  128. mono->Set_Cursor(0, 10);
  129. mono->Printf("%5d", SidebarRedraws);
  130. SidebarRedraws = 0;
  131. /*
  132. ** Update the CPU utilization chart.
  133. */
  134. mono->Sub_Window(15, 13, 63, 10);
  135. mono->Pan(1);
  136. mono->Sub_Window(15, 13, 64, 10);
  137. int graph = RECORDHEIGHT * fixed(TIMER_SECOND-SpareTicks, TIMER_SECOND);
  138. for (int row = 1; row < RECORDHEIGHT; row += 2) {
  139. static char _barchar[4] = {' ', 220, 0, 219};
  140. char str[2];
  141. int index = 0;
  142. index |= (graph >= row) ? 0x01 : 0x00;
  143. index |= (graph >= row+1) ? 0x02: 0x00;
  144. str[1] = '\0';
  145. str[0] = _barchar[index];
  146. mono->Text_Print(str, 62, 9-(row/2));
  147. }
  148. mono->Sub_Window();
  149. SpareTicks = 0;
  150. FramesPerSecond = 0;
  151. }
  152. #endif
  153. /***********************************************************************************************
  154. * LogicClass::AI -- Handles AI logic processing for game objects. *
  155. * *
  156. * This routine is used to perform the AI processing for all game objects. This includes *
  157. * all houses, factories, objects, and teams. *
  158. * *
  159. * INPUT: none *
  160. * *
  161. * OUTPUT: none *
  162. * *
  163. * WARNINGS: none *
  164. * *
  165. * HISTORY: *
  166. * 05/29/1994 JLB : Created. *
  167. * 12/17/1994 JLB : Must perform one complete pass rather than bailing early. *
  168. * 12/23/1994 JLB : Ensures that no object gets skipped if it was deleted. *
  169. *=============================================================================================*/
  170. void LogicClass::AI(void)
  171. {
  172. int index;
  173. FramesPerSecond++;
  174. /*
  175. ** Fading to B&W or color due to the chronosphere is handled here.
  176. */
  177. Scen.Do_Fade_AI();
  178. /*
  179. ** Handle any general timer trigger events.
  180. */
  181. for (LogicTriggerID = 0; LogicTriggerID < LogicTriggers.Count(); LogicTriggerID++) {
  182. TriggerClass * trig = LogicTriggers[LogicTriggerID];
  183. /*
  184. ** Global changed trigger event might be triggered.
  185. */
  186. if (Scen.IsGlobalChanged) {
  187. if (trig->Spring(TEVENT_GLOBAL_SET)) continue;
  188. if (trig->Spring(TEVENT_GLOBAL_CLEAR)) continue;
  189. }
  190. /*
  191. ** Bridge change event.
  192. */
  193. if (Scen.IsBridgeChanged) {
  194. if (trig->Spring(TEVENT_ALL_BRIDGES_DESTROYED)) continue;
  195. }
  196. /*
  197. ** General time expire trigger events can be sprung without warning.
  198. */
  199. if (trig->Spring(TEVENT_TIME)) continue;
  200. /*
  201. ** The mission timer expiration trigger event might spring if the timer is active
  202. ** but at a value of zero.
  203. */
  204. if (Scen.MissionTimer.Is_Active() && Scen.MissionTimer == 0) {
  205. if (trig->Spring(TEVENT_MISSION_TIMER_EXPIRED)) continue;
  206. }
  207. }
  208. if (Scen.MissionTimer.Is_Active()) {
  209. long secs = Scen.MissionTimer / TICKS_PER_SECOND;
  210. long mins = secs / 60;
  211. long hours = mins / 60;
  212. secs %= 60;
  213. mins %= 60;
  214. /*
  215. ** Speak mission timer reminders.
  216. */
  217. VoxType vox = VOX_NONE;
  218. if (Scen.MissionTimer == (1 * TICKS_PER_MINUTE)) vox = VOX_TIME_1;
  219. if (Scen.MissionTimer == (2 * TICKS_PER_MINUTE)) vox = VOX_TIME_2;
  220. if (Scen.MissionTimer == (3 * TICKS_PER_MINUTE)) vox = VOX_TIME_3;
  221. if (Scen.MissionTimer == (4 * TICKS_PER_MINUTE)) vox = VOX_TIME_4;
  222. if (Scen.MissionTimer == (5 * TICKS_PER_MINUTE)) vox = VOX_TIME_5;
  223. if (Scen.MissionTimer == (10 * TICKS_PER_MINUTE)) vox = VOX_TIME_10;
  224. if (Scen.MissionTimer == (20 * TICKS_PER_MINUTE)) vox = VOX_TIME_20;
  225. if (Scen.MissionTimer == (30 * TICKS_PER_MINUTE)) vox = VOX_TIME_30;
  226. if (Scen.MissionTimer == (40 * TICKS_PER_MINUTE)) vox = VOX_TIME_40;
  227. if (vox != VOX_NONE) {
  228. Speak(vox);
  229. Map.FlasherTimer = 7;
  230. }
  231. }
  232. /*
  233. ** Clean up any status values that were maintained only for logic trigger
  234. ** purposes.
  235. */
  236. if (Scen.MissionTimer.Is_Active() && Scen.MissionTimer == 0) {
  237. Scen.MissionTimer.Stop();
  238. Map.Flag_To_Redraw(true); // Used only to cause tabs to redraw in new state.
  239. }
  240. Scen.IsGlobalChanged = false;
  241. Scen.IsBridgeChanged = false;
  242. /*
  243. ** Shadow creeping back over time is handled here.
  244. */
  245. if (Special.IsShadowGrow && Rule.ShroudRate != 0 && Scen.ShroudTimer == 0) {
  246. Scen.ShroudTimer = TICKS_PER_MINUTE * Rule.ShroudRate;
  247. /*
  248. ** Do this for all players in Client/Server multiplayer. ST - 8/9/2019 10:23AM
  249. */
  250. if (Session.Type != GAME_GLYPHX_MULTIPLAYER) {
  251. Map.Encroach_Shadow(PlayerPtr);
  252. } else {
  253. for (int i=0 ; i<Session.Players.Count() ; i++) {
  254. HouseClass *player_ptr = HouseClass::As_Pointer(Session.Players[i]->Player.ID);
  255. if (player_ptr && player_ptr->IsHuman) {
  256. Map.Encroach_Shadow(player_ptr);
  257. }
  258. }
  259. }
  260. }
  261. /*
  262. ** Team AI is processed.
  263. */
  264. for (index = 0; index < Teams.Count(); index++) {
  265. Teams.Ptr(index)->AI();
  266. }
  267. /*
  268. ** If there's a time quake, handle it here.
  269. */
  270. if (TimeQuake) {
  271. Sound_Effect(VOC_KABOOM15);
  272. Shake_The_Screen(8);
  273. }
  274. ChronalVortex.AI();
  275. /*
  276. ** AI for all sentient objects is processed.
  277. */
  278. for (index = 0; index < Count(); index++) {
  279. ObjectClass * obj = (*this)[index];
  280. int count = Count();
  281. BStart(BENCH_AI);
  282. obj->AI();
  283. BEnd(BENCH_AI);
  284. if (TimeQuake && obj != NULL && obj->IsActive && !obj->IsInLimbo && obj->Strength) {
  285. int damage = (int)obj->Class_Of().MaxStrength * Rule.QuakeDamagePercent;
  286. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  287. if (TimeQuakeCenter) {
  288. if(::Distance(obj->As_Target(),TimeQuakeCenter)/256 < MTankDistance) {
  289. switch(obj->What_Am_I()) {
  290. case RTTI_INFANTRY:
  291. damage = QuakeInfantryDamage;
  292. break;
  293. case RTTI_BUILDING:
  294. damage = QuakeBuildingDamage * (int)obj->Class_Of().MaxStrength;
  295. break;
  296. default:
  297. damage = QuakeUnitDamage * (int)obj->Class_Of().MaxStrength;
  298. break;
  299. }
  300. if (damage) {
  301. obj->Clicked_As_Target(HOUSE_COUNT); // 2019/09/20 JAS - Added record of who clicked on the object, HOUSE_COUNT is used to mark for all houses
  302. new AnimClass(ANIM_MINE_EXP1, obj->Center_Coord());
  303. }
  304. obj->Take_Damage(damage, 0, WARHEAD_AP, 0, true);
  305. }
  306. } else {
  307. obj->Take_Damage(damage, 0, WARHEAD_AP, 0, true);
  308. }
  309. #else
  310. obj->Take_Damage(damage, 0, WARHEAD_AP, 0, true);
  311. #endif
  312. }
  313. /*
  314. ** If the object was destroyed in the process of performing its AI, then
  315. ** adjust the index so that no object gets skipped.
  316. */
  317. int count_diff = Count() - count;
  318. if (count_diff < 0) {
  319. index += count_diff;
  320. }
  321. }
  322. HouseClass::Recalc_Attributes();
  323. /*
  324. ** Map related logic is performed.
  325. */
  326. Map.Logic();
  327. /*
  328. ** Factory processing is performed.
  329. */
  330. for (index = 0; index < Factories.Count(); index++) {
  331. Factories.Ptr(index)->AI();
  332. }
  333. /*
  334. ** House processing is performed.
  335. */
  336. #ifdef FIXIT_VERSION_3
  337. if( Session.Type != GAME_NORMAL )
  338. {
  339. for (HousesType house = HOUSE_MULTI1; house < HOUSE_COUNT; house++) {
  340. HouseClass * hptr = HouseClass::As_Pointer(house);
  341. if (hptr && hptr->IsActive) {
  342. hptr->AI();
  343. }
  344. }
  345. }
  346. else
  347. {
  348. for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) {
  349. HouseClass * hptr = HouseClass::As_Pointer(house);
  350. if (hptr && hptr->IsActive) {
  351. hptr->AI();
  352. }
  353. }
  354. }
  355. #else // AI() is called redundantly 12 times in multiplayer games here. ajw
  356. for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) {
  357. HouseClass * hptr = HouseClass::As_Pointer(house);
  358. if (hptr && hptr->IsActive) {
  359. hptr->AI();
  360. }
  361. }
  362. #endif
  363. #ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse.
  364. if( Session.Type != GAME_NORMAL && Scen.AutoSonarTimer == 0 )
  365. {
  366. if( bAutoSonarPulse )
  367. {
  368. Map.Activate_Pulse();
  369. Sound_Effect(VOC_SONAR);
  370. bAutoSonarPulse = false;
  371. }
  372. #define AUTOSONAR_PERIOD TICKS_PER_SECOND * 40;
  373. Scen.AutoSonarTimer = AUTOSONAR_PERIOD;
  374. }
  375. #endif
  376. }
  377. /***********************************************************************************************
  378. * LogicClass::Detach -- Detatch the specified target from the logic system. *
  379. * *
  380. * This routine is called when the specified target object is about to be removed from the *
  381. * game system and all references to it must be severed. The only thing that the logic *
  382. * system looks for in this case is to see if the target refers to a trigger and if so, *
  383. * it scans through the trigger list and removes all references to it. *
  384. * *
  385. * INPUT: target -- The target to remove from the sytem. *
  386. * *
  387. * OUTPUT: none *
  388. * *
  389. * WARNINGS: none *
  390. * *
  391. * HISTORY: *
  392. * 07/30/1996 JLB : Created. *
  393. *=============================================================================================*/
  394. void LogicClass::Detach(TARGET target, bool )
  395. {
  396. /*
  397. ** Remove any triggers from the logic trigger list.
  398. */
  399. if (Is_Target_Trigger(target)) {
  400. for (int index = 0; index < LogicTriggers.Count(); index++) {
  401. if (As_Trigger(target) == LogicTriggers[index]) {
  402. LogicTriggers.Delete(index);
  403. index--;
  404. }
  405. }
  406. }
  407. }
  408. /***********************************************************************************************
  409. * LogicClass::Clear_Recently_Created_Bits -- Clear out the indicators that objects were *
  410. * recently created *
  411. * *
  412. * INPUT: none *
  413. * *
  414. * OUTPUT: none *
  415. * *
  416. * WARNINGS: none *
  417. * *
  418. * HISTORY: *
  419. * 8/19/2019 5:47PM ST : Created. *
  420. *=============================================================================================*/
  421. void LogicClass::Clear_Recently_Created_Bits(void)
  422. {
  423. for (int index = 0; index < Count(); index++) {
  424. ObjectClass * obj = (*this)[index];
  425. obj->IsRecentlyCreated = false;
  426. }
  427. }