DEBUG.CPP (20828B)
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 9 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 10 // in the hope that it will be useful, but with permitted additional restrictions 11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 12 // distributed with this program. You should have received a copy of the 13 // GNU General Public License along with permitted additional restrictions 14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection 15 16 /* $Header: F:\projects\c&c\vcs\code\debug.cpv 2.17 16 Oct 1995 16:49:18 JOE_BOSTIC $ */ 17 /*********************************************************************************************** 18 *** 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 *** 19 *********************************************************************************************** 20 * * 21 * Project Name : Command & Conquer * 22 * * 23 * File Name : DEBUG.CPP * 24 * * 25 * Programmer : Joe L. Bostic * 26 * * 27 * Start Date : September 10, 1993 * 28 * * 29 * Last Update : July 5, 1994 [JLB] * 30 * * 31 *---------------------------------------------------------------------------------------------* 32 * Functions: * 33 * Self_Regulate -- Regulates the logic timer to result in smooth animation. * 34 * Debug_Key -- Debug mode keyboard processing. * 35 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 36 37 #include "function.h" 38 #include <stdarg.h> 39 #include <filepcx.h> 40 #include <io.h> 41 #ifdef CHEAT_KEYS 42 43 extern bool ScreenRecording; 44 45 /*********************************************************************************************** 46 * Debug_Key -- Debug mode keyboard processing. * 47 * * 48 * If debugging is enabled, then this routine will be called for every keystroke that the * 49 * game doesn't recognize. These extra keys usually perform some debugging function. * 50 * * 51 * INPUT: input -- The key code that was pressed. * 52 * * 53 * OUTPUT: none * 54 * * 55 * WARNINGS: none * 56 * * 57 * HISTORY: * 58 * 10/07/1992 JLB : Created. * 59 *=============================================================================================*/ 60 void Debug_Key(unsigned input) 61 { 62 static int map_x = -1; 63 static int map_y = -1; 64 static int map_width = -1; 65 static int map_height = -1; 66 67 if (!input || input & KN_BUTTON) return; 68 69 /* 70 ** Processing of normal keystrokes. 71 */ 72 if (Debug_Flag) { 73 74 switch (input) { 75 76 case KN_L: 77 extern int NetMonoMode,NewMonoMode; 78 if (NetMonoMode) 79 NetMonoMode = 0; 80 else 81 NetMonoMode = 1; 82 NewMonoMode = 1; 83 break; 84 85 /* 86 ** Start saving off screens 87 */ 88 case (int)KN_K|(int)KN_CTRL_BIT: 89 ScreenRecording = true; 90 break; 91 92 case KN_K: 93 //PG_TO_FIX 94 #if (0) 95 /* 96 ** time to create a screen shot using the PCX code (if it works) 97 */ 98 { 99 GraphicBufferClass temp_page( SeenBuff.Get_Width(), 100 SeenBuff.Get_Height(), 101 NULL, 102 SeenBuff.Get_Width() * SeenBuff.Get_Height()); 103 char filename[30]; 104 105 SeenBuff.Blit(temp_page); 106 for (int lp = 0; lp < 99; lp ++) { 107 if (lp < 10) { 108 sprintf(filename, "scrsht0%d.pcx", lp); 109 } else { 110 sprintf(filename, "scrsht%d.pcx", lp); 111 } 112 if (access(filename, F_OK) == -1) 113 break; 114 } 115 116 Write_PCX_File(filename, temp_page, (unsigned char *)CurrentPalette); 117 //Map.Place_Random_Crate(); 118 } 119 #endif 120 break; 121 122 case KN_P: 123 Keyboard::Clear(); 124 while (!Keyboard::Check()) { 125 Self_Regulate(); 126 Sound_Callback(); 127 } 128 Keyboard::Clear(); 129 break; 130 131 case KN_O: 132 { 133 AircraftClass * air = new AircraftClass(AIRCRAFT_ORCA, PlayerPtr->Class->House); 134 if (air) { 135 air->Altitude = 0; 136 air->Unlimbo(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), DIR_N); 137 } 138 } 139 break; 140 141 case (int)KN_B|(int)KN_ALT_BIT: 142 { 143 Debug_Instant_Build ^= 1; 144 } 145 break; 146 case KN_B: 147 { 148 AircraftClass * air = new AircraftClass(AIRCRAFT_HELICOPTER, PlayerPtr->Class->House); 149 if (air) { 150 air->Altitude = 0; 151 air->Unlimbo(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), DIR_N); 152 } 153 } 154 break; 155 156 case KN_T: 157 { 158 AircraftClass * air = new AircraftClass(AIRCRAFT_TRANSPORT, PlayerPtr->Class->House); 159 if (air) { 160 air->Altitude = 0; 161 air->Unlimbo(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), DIR_N); 162 } 163 } 164 break; 165 166 case KN_GRAVE: 167 new AnimClass(ANIM_ART_EXP1, Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y())); 168 Explosion_Damage(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), 250, NULL, WARHEAD_HE); 169 break; 170 171 case KN_Z: 172 // new AnimClass(ANIM_LZ_SMOKE, Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y())); 173 GDI_Ending(); 174 break; 175 176 case KN_C: 177 Debug_Cheat = (Debug_Cheat == false); 178 PlayerPtr->IsRecalcNeeded = true; 179 PlayerPtr->Add_Nuke_Piece(); 180 PlayerPtr->Add_Nuke_Piece(); 181 PlayerPtr->Add_Nuke_Piece(); 182 183 /* 184 ** This placement might affect any prerequisite requirements for construction 185 ** lists. Update the buildable options accordingly. 186 */ 187 if (!ScenarioInit) { 188 Map.Recalc(); 189 for (int index = 0; index < Buildings.Count(); index++) { 190 Buildings.Ptr(index)->Update_Buildables(); 191 } 192 } 193 break; 194 195 case (int)KN_Z|(int)KN_ALT_BIT: 196 if (map_x == -1) { 197 map_x = Map.MapCellX; 198 map_y = Map.MapCellY; 199 map_width = Map.MapCellWidth; 200 map_height = Map.MapCellHeight; 201 Map.MapCellX = 1; 202 Map.MapCellY = 1; 203 Map.MapCellWidth = 62; 204 Map.MapCellHeight = 62; 205 } else { 206 Map.MapCellX = map_x; 207 Map.MapCellY = map_y; 208 Map.MapCellWidth = map_width; 209 Map.MapCellHeight = map_height; 210 map_x = -1; 211 map_y = -1; 212 map_width = -1; 213 map_height = -1; 214 } 215 break; 216 217 #ifdef NEVER 218 case KN_G: 219 HouseClass::As_Pointer(HOUSE_GOOD)->Flag_Attach(Map.Click_Cell_Calc(Get_Mouse_X(), Get_Mouse_Y())); 220 break; 221 222 case KN_N: 223 HouseClass::As_Pointer(HOUSE_BAD)->Flag_Attach(Map.Click_Cell_Calc(Get_Mouse_X(), Get_Mouse_Y())); 224 break; 225 #endif 226 227 case KN_R: 228 if (CurrentObject.Count()) { 229 ((TechnoClass *)CurrentObject[0])->IsCloakable = true; 230 } 231 break; 232 233 case KN_M: 234 if (Debug_Flag) { 235 if (MonoClass::Is_Enabled()) { 236 MonoClass::Disable(); 237 } else { 238 MonoClass::Enable(); 239 } 240 } 241 break; 242 243 case (int)KN_W|(int)KN_ALT_BIT: 244 PlayerPtr->Flag_To_Win(); 245 break; 246 247 case (int)KN_L|(int)KN_ALT_BIT: 248 PlayerPtr->Flag_To_Lose(); 249 break; 250 251 case KN_F: 252 Debug_Find_Path ^= 1; 253 break; 254 255 case KN_DELETE: 256 if (CurrentObject.Count()) { 257 Map.Recalc(); 258 //CurrentObject[0]->Detach_All(); 259 delete CurrentObject[0]; 260 } 261 break; 262 263 case KN_D: 264 if (Teams.Ptr(0)) { 265 delete Teams.Ptr(0); 266 } 267 break; 268 269 case (int)KN_DELETE|(int)KN_SHIFT_BIT: 270 if (CurrentObject.Count()) { 271 Map.Recalc(); 272 int damage = 50; 273 CurrentObject[0]->Take_Damage(damage, 0, WARHEAD_SA); 274 } 275 break; 276 277 case KN_INSERT: 278 if (CurrentObject.Count()) { 279 Map.PendingObject = &CurrentObject[0]->Class_Of(); 280 if (Map.PendingObject) { 281 Map.PendingHouse = CurrentObject[0]->Owner(); 282 Map.PendingObjectPtr = Map.PendingObject->Create_One_Of(HouseClass::As_Pointer(Map.PendingHouse)); 283 if (Map.PendingObjectPtr) { 284 Map.Set_Cursor_Pos(); 285 Map.Set_Cursor_Shape(Map.PendingObject->Occupy_List()); 286 } 287 } 288 } 289 break; 290 291 #ifdef NEVER 292 case KN_1: 293 case KN_2: 294 case KN_3: 295 case KN_4: 296 case KN_5: 297 case KN_6: 298 case KN_7: 299 case KN_8: 300 case KN_9: 301 case KN_0: 302 MonoPage = (input & 0xFF) - KN_1; 303 MonoPage %= sizeof(MonoArray)/sizeof(MonoArray[0]); 304 MonoArray[MonoPage].View(); 305 input = 0; 306 break; 307 #endif 308 309 #ifdef NEVER 310 case ((int)KN_F1 | (int)KN_SHIFT_BIT): 311 Special.IsBarOn = (Special.IsBarOn == false); 312 Map.Flag_To_Redraw(true); 313 break; 314 315 case ((int)KN_F1 | (int)KN_SHIFT_BIT): // quick load/save for debugging 316 if (!Save_Game(0,"Command & Conquer Save Game File")) { 317 CCMessageBox().Process("Error saving game!"); 318 Prog_End(); 319 exit(EXIT_SUCCESS); 320 } 321 break; 322 323 case ((int)KN_F2 | (int)KN_SHIFT_BIT): // quick load/save for debugging 324 if (!Load_Game(0)) { 325 CCMessageBox().Process("Error loading game!"); 326 Prog_End(); 327 exit(EXIT_SUCCESS); 328 } 329 break; 330 331 //#ifdef SCENARIO_EDITOR 332 case KN_F2: // enable/disable the map editor 333 Go_Editor(!Debug_Map); 334 break; 335 //#endif 336 #endif 337 338 #ifdef NEVER 339 case KN_F2: { 340 Debug_Map++; 341 Scenario_Editor(); 342 Debug_Map--; 343 #ifdef NEVER 344 COORDINATE coord; 345 int index; 346 static COORDINATE _coords[] = { 347 0x00010001L, 348 0x00800080L, 349 0x00810081L, 350 0x00010081L, 351 0x00810001L, 352 0x00800081L, 353 0x00800001L, 354 0x00010080L, 355 0x00810080L, 356 0L 357 }; 358 index = 0; 359 while (_coords[index]) { 360 coord = _coords[index++]; 361 Mono_Printf("Spillage for %08lX = %d.\r", coord, Coord_Spillage_Number(coord)); 362 } 363 Keyboard::Clear(); 364 Keyboard::Get(); 365 366 #endif 367 368 #ifdef NEVER 369 #define MAX_RADIUS 10 370 COORDINATE coord; 371 int x,y; 372 COORDINATE const *ptr; 373 int input; 374 int f1,f2; 375 TurnTrackType const *track; 376 377 #define XCENTER 160 378 #define YCENTER 100 379 for (;;) { 380 VisiblePage.Clear(); 381 382 // Draw grid. 383 { 384 static int _gridx[] = {0,64,128,192,0,64,128,192,0,64,128,192}; 385 static int _gridy[] = {0,0,0,0,64,64,64,64,128,128,128,128}; 386 int index; 387 388 for (index = 0; index < 12; index++) { 389 LogicPage->Put_Pixel((_gridx[index]+XCENTER)-(32+64),(_gridy[index]+YCENTER)-(32+64), DKGRAY); 390 } 391 } 392 393 // Get facing #1. 394 LogicPage->Print("Facing #1 (0-7)?", 0, 0, WHITE, BLACK); 395 input = Keyboard::Get(); 396 if (input == KA_ESC) break; 397 input -= KA_0; 398 input = Bound(input, 0, 7); 399 // input = MAX(input, 0); 400 // input = MIN(input, 7); 401 f1 = input; 402 Int_Print(f1, 100, 0, WHITE, BLACK); 403 404 // Get facing #2. 405 LogicPage->Print("Facing #2 (0-7)?", 0, 10, WHITE, BLACK); 406 input = Keyboard::Get(); 407 if (input == KA_ESC) break; 408 input -= KA_0; 409 input = Bound(input, 0, 7); 410 // input = MAX(input, 0); 411 // input = MIN(input, 7); 412 f2 = input; 413 Int_Print(f2, 100, 10, WHITE, BLACK); 414 415 track = &TrackControl[f1][f2]; 416 if (track->Track == 0) { 417 LogicPage->Print("Undefined track.", 0, 30, WHITE, BLACK); 418 } else { 419 int index; // Track index counter. 420 421 ptr = TrackPointers[track->Track-1]; 422 index = 0; 423 while (ptr[index]) { 424 coord = Smooth_Turn(NULL, ptr[index], track->Flag); 425 426 x = (int)(coord & 0xFFFF); 427 y = (int)((coord >> 16) & 0xFFFF); 428 LogicPage->Put_Pixel(XCENTER + (x>>2), YCENTER + (y>>2), WHITE); 429 Delay(1); 430 index++; 431 } 432 433 } 434 input = Keyboard::Get(); 435 if (input == KA_ESC) break; 436 } 437 438 Map.Flag_To_Redraw(true); 439 #endif 440 #ifdef NEVER 441 FILE *fh; 442 int index; 443 COORDINATE coord; 444 445 fh = fopen("diagonal.txt", "wt"); 446 if (fh) { 447 448 fprintf(fh, "track 2\n"); 449 coord = 0x0100FF00L; 450 for (index = 0; index <= 48; index++) { 451 fprintf(fh, "0x%08lXL\n", coord); 452 coord = Coord_Move(coord, 32, 11); 453 } 454 fprintf(fh, "\n\n"); 455 456 fprintf(fh, "track 1\n"); 457 coord = 0x01000000L; 458 for (index = 0; index <= 40; index++) { 459 fprintf(fh, "0x%08lXL\n", coord); 460 coord = Coord_Move(coord, 0, 11); 461 } 462 fprintf(fh, "\n\n"); 463 464 fclose(fh); 465 } 466 #endif 467 #ifdef NEVER 468 FILE *fh; 469 int x,y,radius; 470 int radsize[MAX_RADIUS+2]; 471 int count; 472 473 memset(radsize, 0, sizeof(radsize)); 474 fh = fopen("Range.txt", "wt"); 475 if (fh) { 476 fprintf(fh, "int const RadiusOffset[] = {\n"); 477 478 for (radius = 0; radius <= MAX_RADIUS; radius++) { 479 480 fprintf(fh, "\t/* %-2d */\t", radius); 481 for (y = -MAX_RADIUS; y <= MAX_RADIUS; y++) { 482 for (x = -MAX_RADIUS; x <= MAX_RADIUS; x++) { 483 int xd,yd,dist; 484 485 xd = ABS(x); 486 yd = ABS(y); 487 if (xd > yd) { 488 dist = yd/2 + xd; 489 } else { 490 dist = xd/2 + yd; 491 } 492 if (dist == radius) { 493 dist = y*MAP_CELL_W + x; 494 495 if (y) { 496 if (y < 0) { 497 fprintf(fh, "(-MCW*%d)", ABS(y)); 498 } else { 499 fprintf(fh, "(MCW*%d)", ABS(y)); 500 } 501 fprintf(fh, "%c%d,", (x<0) ? '-' : '+', ABS(x)); 502 } else { 503 fprintf(fh, "%d,", x); 504 } 505 radsize[radius]++; 506 } 507 } 508 } 509 fprintf(fh, "\n"); 510 } 511 fprintf(fh, "};\n\n"); 512 513 count = 0; 514 fprintf(fh, "int const RadiusCount[%d] = {", MAX_RADIUS+1); 515 for (radius = 0; radius <= MAX_RADIUS; radius++) { 516 count += radsize[radius]; 517 fprintf(fh, "%d", count); 518 if (radius != MAX_RADIUS) { 519 fprintf(fh, ","); 520 } 521 } 522 fprintf(fh, "};\n"); 523 fclose(fh); 524 } 525 #endif 526 } 527 break; 528 #endif 529 530 #ifdef NEVER 531 case ((int)KN_F3 | (int)KN_ALT_BIT): // quick load/save for debugging 532 Debug_Threat = (Debug_Threat == false); 533 Map.Flag_To_Redraw(true); 534 break; 535 536 #endif 537 538 case KN_F3: 539 Debug_Icon = (Debug_Icon == false); 540 Map.Flag_To_Redraw(true); 541 break; 542 543 544 /* 545 ** Reveal entire map to player. 546 */ 547 case KN_F4: 548 if (GameToPlay == GAME_NORMAL) { 549 Debug_Unshroud = (Debug_Unshroud == false); 550 Map.Flag_To_Redraw(true); 551 } 552 break; 553 554 /* 555 ** Shows sight and fire range in the form of circles emanating from the currently 556 ** selected unit. The white circle is for sight range, the red circle is for 557 ** fire range. 558 */ 559 case KN_F7: 560 if (CurrentObject.Count() && CurrentObject[0]->Is_Techno()) { 561 TechnoTypeClass const & ttype = (TechnoTypeClass const &)CurrentObject[0]->Class_Of(); 562 int sight = ((int)ttype.SightRange)<<8; 563 int weapon = 0; 564 if (ttype.Primary != WEAPON_NONE) weapon = Weapons[ttype.Primary].Range; 565 Set_Logic_Page(SeenBuff); 566 COORDINATE center = CurrentObject[0]->Center_Coord(); 567 COORDINATE center2 = CurrentObject[0]->Fire_Coord(0); 568 569 for (int r = 0; r < 255; r += 10) { 570 int x,y,x1,y1; 571 DirType r1 = (DirType)r; 572 DirType r2 = (DirType)((r+10) & 0xFF); 573 574 if (Map.Coord_To_Pixel(Coord_Move(center, r1, sight), x, y)) { 575 Map.Coord_To_Pixel(Coord_Move(center, r2, sight), x1, y1); 576 LogicPage->Draw_Line(x, y+8, x1, y1+8, WHITE); 577 } 578 if (Map.Coord_To_Pixel(Coord_Move(center2, r1, weapon), x, y)) { 579 Map.Coord_To_Pixel(Coord_Move(center2, r2, weapon), x1, y1); 580 LogicPage->Draw_Line(x, y+8, x1, y1+8, RED); 581 } 582 } 583 } 584 break; 585 586 case ((int)KN_F4 | (int)KN_CTRL_BIT): 587 Debug_Unshroud = (Debug_Unshroud == false); 588 Map.Flag_To_Redraw(true); 589 break; 590 591 #ifdef NEVER 592 case KN_F5: 593 Special.IsShowPath = (Special.IsShowPath == false); 594 //PlayerPtr->Credits += 1000; 595 break; 596 597 case KN_F6: 598 if (Map.In_Radar(XY_Cell(Map.MapCellX+5, Map.MapCellY - 1))) { 599 Mono_Printf("Arrrggggghhhhh!"); 600 } else { 601 Mono_Printf("No Arrrggggghhhhh!"); 602 } 603 break; 604 605 case ((int)KN_F9 | (int)KN_CTRL_BIT): 606 if (HouseClass::As_Pointer(HOUSE_GOOD)) 607 (HouseClass::As_Pointer(HOUSE_GOOD))->Blowup_All(); 608 break; 609 610 case ((int)KN_F10 | (int)KN_CTRL_BIT): 611 if (HouseClass::As_Pointer(HOUSE_BAD)) 612 (HouseClass::As_Pointer(HOUSE_BAD))->Blowup_All(); 613 break; 614 #endif 615 } 616 617 } 618 } 619 620 621 /*********************************************************************************************** 622 * Self_Regulate -- Regulates the logic timer to result in smooth animation * 623 * * 624 * The self regulation process checks the number of frames displayed * 625 * per second and from this determines the amount of time to devote * 626 * to internal logic processing. By adjusting the time allotted to * 627 * internal processing, smooth animation can be maintained. * 628 * * 629 * INPUT: none * 630 * * 631 * OUTPUT: none * 632 * * 633 * WARNINGS: In order for this routine to work properly it MUST be * 634 * called every display loop. * 635 * * 636 * HISTORY: * 637 * 07/31/1991 JLB : Created. * 638 * 07/05/1994 JLB : Handles new monochrome system. * 639 *=============================================================================================*/ 640 #define UPDATE_INTERVAL TIMER_SECOND 641 void Self_Regulate(void) 642 { 643 static CountDownTimerClass DebugTimer(BT_SYSTEM); 644 static ObjectClass * _lastobject = 0; 645 646 if (!DebugTimer.Time()) { 647 DebugTimer.Set(UPDATE_INTERVAL); 648 649 if (MonoClass::Is_Enabled()) { 650 MonoClass *mono = MonoClass::Get_Current(); 651 mono->Set_Default_Attribute(2); 652 653 switch (MonoPage) { 654 case 0: 655 mono = &MonoArray[0]; 656 mono->Clear(); 657 658 /* 659 ** Display the status of the currently selected object. 660 */ 661 if (CurrentObject.Count()) { 662 _lastobject = CurrentObject[0]; 663 } 664 if (_lastobject && !_lastobject->IsActive) { 665 _lastobject = 0; 666 } 667 if (_lastobject) { 668 _lastobject->Debug_Dump(mono); 669 } 670 Logic.Debug_Dump(mono); 671 mono->Set_Cursor(0, 20); 672 mono->Printf( 673 "Heap size:%10ld \r" 674 "Largest: %10ld \r" 675 "Ttl Free: %10ld \r" 676 "Frag: %10ld \r", 677 Heap_Size(MEM_NORMAL), 678 Ram_Free(MEM_NORMAL), 679 Total_Ram_Free(MEM_NORMAL), 680 Total_Ram_Free(MEM_NORMAL)-Ram_Free(MEM_NORMAL) 681 ); 682 *MonoClass::Get_Current() = *mono; 683 break; 684 } 685 686 MonoArray[MonoPage] = *mono; 687 } 688 } 689 } 690 #endif