DEBUG.CPP 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. /*
  2. ** Command & Conquer Red Alert(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. /* $Header: /CounterStrike/DEBUG.CPP 1 3/03/97 10:24a Joe_bostic $ */
  19. /***********************************************************************************************
  20. *** 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 ***
  21. ***********************************************************************************************
  22. * *
  23. * Project Name : Command & Conquer *
  24. * *
  25. * File Name : DEBUG.CPP *
  26. * *
  27. * Programmer : Joe L. Bostic *
  28. * *
  29. * Start Date : September 10, 1993 *
  30. * *
  31. * Last Update : July 18, 1996 [JLB] *
  32. * *
  33. *---------------------------------------------------------------------------------------------*
  34. * Functions: *
  35. * Self_Regulate -- Regulates the logic timer to result in smooth animation. *
  36. * Debug_Key -- Debug mode keyboard processing. *
  37. * Bench_Time -- Convert benchmark timer into descriptive string. *
  38. * Benchmarks -- Display the performance tracking benchmarks. *
  39. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  40. #include "function.h"
  41. #include "vortex.h"
  42. #include <stdarg.h>
  43. #ifdef CHEAT_KEYS
  44. static CDTimerClass<SystemTimerClass> DebugTimer;
  45. int VortexFrame = -1;
  46. /***********************************************************************************************
  47. * Debug_Key -- Debug mode keyboard processing. *
  48. * *
  49. * If debugging is enabled, then this routine will be called for every keystroke that the *
  50. * game doesn't recognize. These extra keys usually perform some debugging function. *
  51. * *
  52. * INPUT: input -- The key code that was pressed. *
  53. * *
  54. * OUTPUT: none *
  55. * *
  56. * WARNINGS: none *
  57. * *
  58. * HISTORY: *
  59. * 10/07/1992 JLB : Created. *
  60. *=============================================================================================*/
  61. void Debug_Key(unsigned input)
  62. {
  63. static int map_x = -1;
  64. static int map_y = -1;
  65. static int map_width = -1;
  66. static int map_height = -1;
  67. if (!input || input & KN_BUTTON) return;
  68. /*
  69. ** Processing of normal keystrokes.
  70. */
  71. if (Debug_Flag) {
  72. switch (input) {
  73. case KN_BACKSPACE:
  74. if (ChronalVortex.Is_Active()) {
  75. ChronalVortex.Disappear();
  76. } else {
  77. int xxxx = Get_Mouse_X() + Map.TacPixelX;
  78. int yyyy = Get_Mouse_Y() + Map.TacPixelY;
  79. CELL cell = Map.DisplayClass::Click_Cell_Calc(xxxx,yyyy);
  80. ChronalVortex.Appear ( Cell_Coord (cell) );
  81. }
  82. break;
  83. #ifdef WIN32
  84. case KN_J:
  85. Debug_MotionCapture = true;
  86. break;
  87. #ifdef OBSOLETE
  88. case KN_K:
  89. /*
  90. ** time to create a screen shot using the PCX code (if it works)
  91. */
  92. if (!Debug_MotionCapture) {
  93. GraphicBufferClass temp_page( SeenBuff.Get_Width(),
  94. SeenBuff.Get_Height(),
  95. NULL,
  96. SeenBuff.Get_Width() * SeenBuff.Get_Height());
  97. CDFileClass file;
  98. char filename[30];
  99. SeenBuff.Blit(temp_page);
  100. for (int lp = 0; lp < 99; lp ++) {
  101. sprintf(filename, "scrsht%02d.pcx", lp);
  102. file.Set_Name(filename);
  103. if (!file.Is_Available()) break;
  104. }
  105. file.Cache(200000);
  106. Write_PCX_File(file, temp_page, & GamePalette);
  107. Sound_Effect(VOC_BEEP);
  108. }
  109. break;
  110. #endif
  111. #endif
  112. case KN_P:
  113. {
  114. for (SpecialWeaponType spc = SPC_FIRST; spc < SPC_COUNT; spc++) {
  115. PlayerPtr->SuperWeapon[spc].Enable(true, true);
  116. PlayerPtr->SuperWeapon[spc].Forced_Charge(true);
  117. Map.Add(RTTI_SPECIAL, spc);
  118. Map.Column[1].Flag_To_Redraw();
  119. }
  120. }
  121. break;
  122. case KN_I:
  123. {
  124. Map.Flash_Power();
  125. Map.Flash_Money();
  126. }
  127. break;
  128. case KN_O:
  129. {
  130. AircraftClass * air = new AircraftClass(AIRCRAFT_HIND, PlayerPtr->Class->House);
  131. if (air) {
  132. air->Height = 0;
  133. air->Unlimbo(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), DIR_N);
  134. }
  135. }
  136. break;
  137. case KN_B:
  138. {
  139. AircraftClass * air = new AircraftClass(AIRCRAFT_LONGBOW, PlayerPtr->Class->House);
  140. if (air) {
  141. air->Height = 0;
  142. air->Unlimbo(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), DIR_N);
  143. }
  144. }
  145. break;
  146. case KN_GRAVE:
  147. {
  148. WarheadType warhead = Random_Pick(WARHEAD_HE, WARHEAD_FIRE);
  149. COORDINATE coord = Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y());
  150. int damage = 1000;
  151. new AnimClass(Combat_Anim(damage, warhead, Map[coord].Land_Type()), coord);
  152. Explosion_Damage(coord, damage, NULL, warhead);
  153. }
  154. break;
  155. case KN_C:
  156. Debug_Cheat = (Debug_Cheat == false);
  157. PlayerPtr->IsRecalcNeeded = true;
  158. /*
  159. ** This placement might affect any prerequisite requirements for construction
  160. ** lists. Update the buildable options accordingly.
  161. */
  162. if (!ScenarioInit) {
  163. Map.Recalc();
  164. for (int index = 0; index < Buildings.Count(); index++) {
  165. Buildings.Ptr(index)->Update_Buildables();
  166. }
  167. }
  168. break;
  169. case (int)KN_Z|(int)KN_ALT_BIT:
  170. if (map_x == -1) {
  171. map_x = Map.MapCellX;
  172. map_y = Map.MapCellY;
  173. map_width = Map.MapCellWidth;
  174. map_height = Map.MapCellHeight;
  175. Map.MapCellX = 1;
  176. Map.MapCellY = 1;
  177. Map.MapCellWidth = MAP_CELL_W-2;
  178. Map.MapCellHeight = MAP_CELL_H-2;
  179. } else {
  180. Map.MapCellX = map_x;
  181. Map.MapCellY = map_y;
  182. Map.MapCellWidth = map_width;
  183. Map.MapCellHeight = map_height;
  184. map_x = -1;
  185. map_y = -1;
  186. map_width = -1;
  187. map_height = -1;
  188. }
  189. break;
  190. case KN_M:
  191. if (Debug_Flag) {
  192. if (MonoClass::Is_Enabled()) {
  193. MonoClass::Disable();
  194. } else {
  195. MonoClass::Enable();
  196. }
  197. }
  198. break;
  199. case (int)KN_W|(int)KN_ALT_BIT:
  200. PlayerPtr->Flag_To_Win();
  201. break;
  202. case (int)KN_L|(int)KN_ALT_BIT:
  203. PlayerPtr->Flag_To_Lose();
  204. break;
  205. case KN_DELETE:
  206. if (CurrentObject.Count()) {
  207. Map.Recalc();
  208. //CurrentObject[0]->Detach_All();
  209. if (CurrentObject[0]->What_Am_I() == RTTI_BUILDING) {
  210. ((BuildingClass *)CurrentObject[0])->Sell_Back(1);
  211. } else {
  212. ObjectClass * object = CurrentObject[0];
  213. object->Unselect();
  214. object->Limbo();
  215. delete object;
  216. }
  217. }
  218. break;
  219. case (int)KN_DELETE|(int)KN_SHIFT_BIT:
  220. if (CurrentObject.Count()) {
  221. Map.Recalc();
  222. int damage = 50;
  223. CurrentObject[0]->Take_Damage(damage, 0, WARHEAD_SA);
  224. }
  225. break;
  226. case KN_INSERT:
  227. if (CurrentObject.Count()) {
  228. Map.PendingObject = &CurrentObject[0]->Class_Of();
  229. if (Map.PendingObject) {
  230. Map.PendingHouse = CurrentObject[0]->Owner();
  231. Map.PendingObjectPtr = Map.PendingObject->Create_One_Of(HouseClass::As_Pointer(Map.PendingHouse));
  232. if (Map.PendingObjectPtr) {
  233. Map.Set_Cursor_Pos();
  234. Map.Set_Cursor_Shape(Map.PendingObject->Occupy_List());
  235. }
  236. }
  237. }
  238. break;
  239. case KN_LBRACKET:
  240. case KN_F11:
  241. if (MonoPage == DMONO_FIRST) {
  242. MonoPage = DMonoType(DMONO_COUNT-1);
  243. } else {
  244. MonoPage = DMonoType(MonoPage - 1);
  245. }
  246. DebugTimer = 0;
  247. break;
  248. case KN_RBRACKET:
  249. case KN_F12:
  250. MonoPage = DMonoType(MonoPage + 1);
  251. if (MonoPage == DMONO_COUNT) {
  252. MonoPage = DMONO_FIRST;
  253. }
  254. DebugTimer = 0;
  255. break;
  256. case KN_V:
  257. case KN_F3:
  258. Debug_Icon = (Debug_Icon == false);
  259. Map.Flag_To_Redraw(true);
  260. break;
  261. /*
  262. ** Reveal entire map to player.
  263. */
  264. // case KN_F4:
  265. // if (Session.Type == GAME_NORMAL) {
  266. // Debug_Unshroud = (Debug_Unshroud == false);
  267. // Map.Flag_To_Redraw(true);
  268. // }
  269. // break;
  270. /*
  271. ** Shows sight and fire range in the form of circles emanating from the currently
  272. ** selected unit. The white circle is for sight range, the red circle is for
  273. ** fire range.
  274. */
  275. case KN_F7:
  276. if (CurrentObject.Count() && CurrentObject[0]->Is_Techno()) {
  277. TechnoTypeClass const & ttype = (TechnoTypeClass const &)CurrentObject[0]->Class_Of();
  278. int sight = ((int)ttype.SightRange)<<8;
  279. int weapon = 0;
  280. if (ttype.PrimaryWeapon != NULL) weapon = ttype.PrimaryWeapon->Range;
  281. Set_Logic_Page(SeenBuff);
  282. COORDINATE center = CurrentObject[0]->Center_Coord();
  283. COORDINATE center2 = CurrentObject[0]->Fire_Coord(0);
  284. for (int r = 0; r < 255; r += 10) {
  285. int x,y,x1,y1;
  286. DirType r1 = (DirType)r;
  287. DirType r2 = (DirType)((r+10) & 0xFF);
  288. if (Map.Coord_To_Pixel(Coord_Move(center, r1, sight), x, y)) {
  289. Map.Coord_To_Pixel(Coord_Move(center, r2, sight), x1, y1);
  290. LogicPage->Draw_Line(x, y+8, x1, y1+8, WHITE);
  291. }
  292. if (Map.Coord_To_Pixel(Coord_Move(center2, r1, weapon), x, y)) {
  293. Map.Coord_To_Pixel(Coord_Move(center2, r2, weapon), x1, y1);
  294. LogicPage->Draw_Line(x, y+8, x1, y1+8, RED);
  295. }
  296. }
  297. }
  298. break;
  299. case ((int)KN_F4 | (int)KN_CTRL_BIT):
  300. Debug_Unshroud = (Debug_Unshroud == false);
  301. Map.Flag_To_Redraw(true);
  302. break;
  303. default:
  304. break;
  305. }
  306. }
  307. }
  308. /***********************************************************************************************
  309. * Bench_Time -- Convert benchmark timer into descriptive string. *
  310. * *
  311. * This routine will take the values of the benchmark timer specified and build a string *
  312. * that displays the average time each event consumed as well as the ranking of how much *
  313. * time that event took (total) during the tracking duration (one second?). *
  314. * *
  315. * INPUT: btype -- The benchmark to convert to a descriptive string. *
  316. * *
  317. * OUTPUT: Returns with a pointer to the descriptive string of the benchmark specified. *
  318. * *
  319. * WARNINGS: The value returned is a pointer to a static buffer. As such, it is only valid *
  320. * until the next time that this routine is called. *
  321. * *
  322. * HISTORY: *
  323. * 07/18/1996 JLB : Created. *
  324. *=============================================================================================*/
  325. static char const * Bench_Time(BenchType btype)
  326. {
  327. static char buffer[32];
  328. int rootcount = Benches[BENCH_GAME_FRAME].Count();
  329. if (rootcount == 0) rootcount = 1;
  330. int roottime = Benches[BENCH_GAME_FRAME].Value();
  331. int count = Benches[btype].Count();
  332. int time = Benches[btype].Value();
  333. if (count > 0 && count * time > roottime * rootcount) time = roottime / count;
  334. int percent = 0;
  335. if (roottime != 0 && rootcount != 0) {
  336. percent = ((count * time) * 99) / (roottime * rootcount);
  337. }
  338. if (percent > 99) percent = 99;
  339. sprintf(buffer, "%-2d%% %7d", percent, time);
  340. return(buffer);
  341. }
  342. /***********************************************************************************************
  343. * Benchmarks -- Display the performance tracking benchmarks. *
  344. * *
  345. * This will display the benchmarks for the various processes that are being tracked. The *
  346. * display will indicate the fraction that each process is consuming out of the entire *
  347. * process time as well as the time consumed by each individual event. The total fraction *
  348. * is useful for determing what should be optimized. The individual time is useful for *
  349. * guaging the effectiveness of optimization changes. *
  350. * *
  351. * INPUT: mono -- Pointer to the monochrome screen that the display will use. *
  352. * *
  353. * OUTPUT: none *
  354. * *
  355. * WARNINGS: none *
  356. * *
  357. * HISTORY: *
  358. * 07/18/1996 JLB : Created. *
  359. *=============================================================================================*/
  360. static void Benchmarks(MonoClass * mono)
  361. {
  362. static bool _first = true;
  363. if (_first) {
  364. _first = false;
  365. mono->Clear();
  366. mono->Set_Cursor(0, 0);
  367. mono->Print(Text_String(TXT_DEBUG_PERFORMANCE));
  368. if (Benches == NULL) {
  369. mono->Set_Cursor(20, 15);
  370. mono->Printf(TXT_NO_PENTIUM);
  371. }
  372. }
  373. if (Benches != NULL) {
  374. mono->Set_Cursor(1, 2);mono->Printf("%s", Bench_Time(BENCH_FINDPATH));
  375. mono->Set_Cursor(1, 4);mono->Printf("%s", Bench_Time(BENCH_GREATEST_THREAT));
  376. mono->Set_Cursor(1, 6);mono->Printf("%s", Bench_Time(BENCH_AI));
  377. mono->Set_Cursor(1, 8);mono->Printf("%s", Bench_Time(BENCH_PCP));
  378. mono->Set_Cursor(1, 10);mono->Printf("%s", Bench_Time(BENCH_EVAL_OBJECT));
  379. mono->Set_Cursor(1, 12);mono->Printf("%s", Bench_Time(BENCH_EVAL_CELL));
  380. mono->Set_Cursor(1, 14);mono->Printf("%s", Bench_Time(BENCH_EVAL_WALL));
  381. mono->Set_Cursor(1, 16);mono->Printf("%s", Bench_Time(BENCH_MISSION));
  382. mono->Set_Cursor(14, 2);mono->Printf("%s", Bench_Time(BENCH_CELL));
  383. mono->Set_Cursor(14, 4);mono->Printf("%s", Bench_Time(BENCH_OBJECTS));
  384. mono->Set_Cursor(14, 6);mono->Printf("%s", Bench_Time(BENCH_ANIMS));
  385. mono->Set_Cursor(27, 2);mono->Printf("%s", Bench_Time(BENCH_PALETTE));
  386. mono->Set_Cursor(40, 2);mono->Printf("%s", Bench_Time(BENCH_GSCREEN_RENDER));
  387. mono->Set_Cursor(40, 4);mono->Printf("%s", Bench_Time(BENCH_SIDEBAR));
  388. mono->Set_Cursor(40, 6);mono->Printf("%s", Bench_Time(BENCH_RADAR));
  389. mono->Set_Cursor(40, 8);mono->Printf("%s", Bench_Time(BENCH_TACTICAL));
  390. mono->Set_Cursor(40, 10);mono->Printf("%s", Bench_Time(BENCH_POWER));
  391. mono->Set_Cursor(40, 12);mono->Printf("%s", Bench_Time(BENCH_SHROUD));
  392. mono->Set_Cursor(40, 14);mono->Printf("%s", Bench_Time(BENCH_TABS));
  393. mono->Set_Cursor(40, 16);mono->Printf("%s", Bench_Time(BENCH_BLIT_DISPLAY));
  394. mono->Set_Cursor(66, 2);mono->Printf("%7d", Benches[BENCH_RULES].Value());
  395. mono->Set_Cursor(66, 4);mono->Printf("%7d", Benches[BENCH_SCENARIO].Value());
  396. for (BenchType index = BENCH_FIRST; index < BENCH_COUNT; index++) {
  397. if (index != BENCH_RULES && index != BENCH_SCENARIO) Benches[index].Reset();
  398. }
  399. }
  400. }
  401. /***********************************************************************************************
  402. * Self_Regulate -- Regulates the logic timer to result in smooth animation *
  403. * *
  404. * The self regulation process checks the number of frames displayed *
  405. * per second and from this determines the amount of time to devote *
  406. * to internal logic processing. By adjusting the time allotted to *
  407. * internal processing, smooth animation can be maintained. *
  408. * *
  409. * INPUT: none *
  410. * *
  411. * OUTPUT: none *
  412. * *
  413. * WARNINGS: In order for this routine to work properly it MUST be *
  414. * called every display loop. *
  415. * *
  416. * HISTORY: *
  417. * 07/31/1991 JLB : Created. *
  418. * 07/05/1994 JLB : Handles new monochrome system. *
  419. *=============================================================================================*/
  420. #define UPDATE_INTERVAL TIMER_SECOND
  421. void Self_Regulate(void)
  422. {
  423. static ObjectClass * _lastobject = 0;
  424. static bool _first=true;
  425. if (DebugTimer == 0) {
  426. DebugTimer = UPDATE_INTERVAL;
  427. if (MonoClass::Is_Enabled()) {
  428. if (_first) {
  429. _first = false;
  430. for (DMonoType index = DMONO_FIRST; index < DMONO_COUNT; index++) {
  431. MonoArray[index].Clear();
  432. }
  433. }
  434. /*
  435. ** Always update the stress tracking mono display even if it
  436. ** currently isn't visible.
  437. */
  438. Logic.Debug_Dump(&MonoArray[DMONO_STRESS]);
  439. MonoClass * mono = &MonoArray[MonoPage];
  440. mono->Set_Default_Attribute(MonoClass::NORMAL);
  441. mono->View();
  442. switch (MonoPage) {
  443. case DMONO_EVENTS:
  444. Benchmarks(mono);
  445. break;
  446. case DMONO_OBJECT:
  447. mono->Clear();
  448. /*
  449. ** Display the status of the currently selected object.
  450. */
  451. if (CurrentObject.Count()) {
  452. _lastobject = CurrentObject[0];
  453. }
  454. if (_lastobject && !_lastobject->IsActive) {
  455. _lastobject = 0;
  456. }
  457. if (_lastobject) {
  458. _lastobject->Debug_Dump(mono);
  459. }
  460. break;
  461. case DMONO_STRESS:
  462. #ifdef OBSOLETE
  463. mono->Set_Cursor(0, 20);
  464. mono->Printf(
  465. "Heap size:%10ld \r"
  466. "Largest: %10ld \r"
  467. "Ttl Free: %10ld \r"
  468. "Frag: %10ld \r",
  469. Heap_Size(MEM_NORMAL),
  470. Ram_Free(MEM_NORMAL),
  471. Total_Ram_Free(MEM_NORMAL),
  472. Total_Ram_Free(MEM_NORMAL)-Ram_Free(MEM_NORMAL)
  473. );
  474. #endif
  475. break;
  476. case DMONO_HOUSE:
  477. mono->Clear();
  478. if (CurrentObject.Count()) {
  479. _lastobject = CurrentObject[0];
  480. }
  481. if (_lastobject && !_lastobject->IsActive) {
  482. _lastobject = 0;
  483. }
  484. if (_lastobject && _lastobject->Is_Techno()) {
  485. ((TechnoClass *)_lastobject)->House->Debug_Dump(mono);
  486. }
  487. break;
  488. default:
  489. break;
  490. }
  491. mono->Set_Cursor(0, 0);
  492. }
  493. }
  494. }
  495. #endif