CnC_Remastered_Collection/TIBERIANDAWN/DEBUG.CPP
PG-SteveT 03416d24e1 Initial Source Code commit
Initial commit of original Tiberian Dawn and Red Alert source code converted to build as DLLs, and compatible with the release version of Command & Conquer Remastered.
2020-05-27 12:16:20 -07:00

691 lines
20 KiB
C++

//
// Copyright 2020 Electronic Arts Inc.
//
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
/* $Header: F:\projects\c&c\vcs\code\debug.cpv 2.17 16 Oct 1995 16:49:18 JOE_BOSTIC $ */
/***********************************************************************************************
*** 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 ***
***********************************************************************************************
* *
* Project Name : Command & Conquer *
* *
* File Name : DEBUG.CPP *
* *
* Programmer : Joe L. Bostic *
* *
* Start Date : September 10, 1993 *
* *
* Last Update : July 5, 1994 [JLB] *
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* Self_Regulate -- Regulates the logic timer to result in smooth animation. *
* Debug_Key -- Debug mode keyboard processing. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "function.h"
#include <stdarg.h>
#include <filepcx.h>
#include <io.h>
#ifdef CHEAT_KEYS
extern bool ScreenRecording;
/***********************************************************************************************
* Debug_Key -- Debug mode keyboard processing. *
* *
* If debugging is enabled, then this routine will be called for every keystroke that the *
* game doesn't recognize. These extra keys usually perform some debugging function. *
* *
* INPUT: input -- The key code that was pressed. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 10/07/1992 JLB : Created. *
*=============================================================================================*/
void Debug_Key(unsigned input)
{
static int map_x = -1;
static int map_y = -1;
static int map_width = -1;
static int map_height = -1;
if (!input || input & KN_BUTTON) return;
/*
** Processing of normal keystrokes.
*/
if (Debug_Flag) {
switch (input) {
case KN_L:
extern int NetMonoMode,NewMonoMode;
if (NetMonoMode)
NetMonoMode = 0;
else
NetMonoMode = 1;
NewMonoMode = 1;
break;
/*
** Start saving off screens
*/
case (int)KN_K|(int)KN_CTRL_BIT:
ScreenRecording = true;
break;
case KN_K:
//PG_TO_FIX
#if (0)
/*
** time to create a screen shot using the PCX code (if it works)
*/
{
GraphicBufferClass temp_page( SeenBuff.Get_Width(),
SeenBuff.Get_Height(),
NULL,
SeenBuff.Get_Width() * SeenBuff.Get_Height());
char filename[30];
SeenBuff.Blit(temp_page);
for (int lp = 0; lp < 99; lp ++) {
if (lp < 10) {
sprintf(filename, "scrsht0%d.pcx", lp);
} else {
sprintf(filename, "scrsht%d.pcx", lp);
}
if (access(filename, F_OK) == -1)
break;
}
Write_PCX_File(filename, temp_page, (unsigned char *)CurrentPalette);
//Map.Place_Random_Crate();
}
#endif
break;
case KN_P:
Keyboard::Clear();
while (!Keyboard::Check()) {
Self_Regulate();
Sound_Callback();
}
Keyboard::Clear();
break;
case KN_O:
{
AircraftClass * air = new AircraftClass(AIRCRAFT_ORCA, PlayerPtr->Class->House);
if (air) {
air->Altitude = 0;
air->Unlimbo(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), DIR_N);
}
}
break;
case (int)KN_B|(int)KN_ALT_BIT:
{
Debug_Instant_Build ^= 1;
}
break;
case KN_B:
{
AircraftClass * air = new AircraftClass(AIRCRAFT_HELICOPTER, PlayerPtr->Class->House);
if (air) {
air->Altitude = 0;
air->Unlimbo(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), DIR_N);
}
}
break;
case KN_T:
{
AircraftClass * air = new AircraftClass(AIRCRAFT_TRANSPORT, PlayerPtr->Class->House);
if (air) {
air->Altitude = 0;
air->Unlimbo(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), DIR_N);
}
}
break;
case KN_GRAVE:
new AnimClass(ANIM_ART_EXP1, Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()));
Explosion_Damage(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), 250, NULL, WARHEAD_HE);
break;
case KN_Z:
// new AnimClass(ANIM_LZ_SMOKE, Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()));
GDI_Ending();
break;
case KN_C:
Debug_Cheat = (Debug_Cheat == false);
PlayerPtr->IsRecalcNeeded = true;
PlayerPtr->Add_Nuke_Piece();
PlayerPtr->Add_Nuke_Piece();
PlayerPtr->Add_Nuke_Piece();
/*
** This placement might affect any prerequisite requirements for construction
** lists. Update the buildable options accordingly.
*/
if (!ScenarioInit) {
Map.Recalc();
for (int index = 0; index < Buildings.Count(); index++) {
Buildings.Ptr(index)->Update_Buildables();
}
}
break;
case (int)KN_Z|(int)KN_ALT_BIT:
if (map_x == -1) {
map_x = Map.MapCellX;
map_y = Map.MapCellY;
map_width = Map.MapCellWidth;
map_height = Map.MapCellHeight;
Map.MapCellX = 1;
Map.MapCellY = 1;
Map.MapCellWidth = 62;
Map.MapCellHeight = 62;
} else {
Map.MapCellX = map_x;
Map.MapCellY = map_y;
Map.MapCellWidth = map_width;
Map.MapCellHeight = map_height;
map_x = -1;
map_y = -1;
map_width = -1;
map_height = -1;
}
break;
#ifdef NEVER
case KN_G:
HouseClass::As_Pointer(HOUSE_GOOD)->Flag_Attach(Map.Click_Cell_Calc(Get_Mouse_X(), Get_Mouse_Y()));
break;
case KN_N:
HouseClass::As_Pointer(HOUSE_BAD)->Flag_Attach(Map.Click_Cell_Calc(Get_Mouse_X(), Get_Mouse_Y()));
break;
#endif
case KN_R:
if (CurrentObject.Count()) {
((TechnoClass *)CurrentObject[0])->IsCloakable = true;
}
break;
case KN_M:
if (Debug_Flag) {
if (MonoClass::Is_Enabled()) {
MonoClass::Disable();
} else {
MonoClass::Enable();
}
}
break;
case (int)KN_W|(int)KN_ALT_BIT:
PlayerPtr->Flag_To_Win();
break;
case (int)KN_L|(int)KN_ALT_BIT:
PlayerPtr->Flag_To_Lose();
break;
case KN_F:
Debug_Find_Path ^= 1;
break;
case KN_DELETE:
if (CurrentObject.Count()) {
Map.Recalc();
//CurrentObject[0]->Detach_All();
delete CurrentObject[0];
}
break;
case KN_D:
if (Teams.Ptr(0)) {
delete Teams.Ptr(0);
}
break;
case (int)KN_DELETE|(int)KN_SHIFT_BIT:
if (CurrentObject.Count()) {
Map.Recalc();
int damage = 50;
CurrentObject[0]->Take_Damage(damage, 0, WARHEAD_SA);
}
break;
case KN_INSERT:
if (CurrentObject.Count()) {
Map.PendingObject = &CurrentObject[0]->Class_Of();
if (Map.PendingObject) {
Map.PendingHouse = CurrentObject[0]->Owner();
Map.PendingObjectPtr = Map.PendingObject->Create_One_Of(HouseClass::As_Pointer(Map.PendingHouse));
if (Map.PendingObjectPtr) {
Map.Set_Cursor_Pos();
Map.Set_Cursor_Shape(Map.PendingObject->Occupy_List());
}
}
}
break;
#ifdef NEVER
case KN_1:
case KN_2:
case KN_3:
case KN_4:
case KN_5:
case KN_6:
case KN_7:
case KN_8:
case KN_9:
case KN_0:
MonoPage = (input & 0xFF) - KN_1;
MonoPage %= sizeof(MonoArray)/sizeof(MonoArray[0]);
MonoArray[MonoPage].View();
input = 0;
break;
#endif
#ifdef NEVER
case ((int)KN_F1 | (int)KN_SHIFT_BIT):
Special.IsBarOn = (Special.IsBarOn == false);
Map.Flag_To_Redraw(true);
break;
case ((int)KN_F1 | (int)KN_SHIFT_BIT): // quick load/save for debugging
if (!Save_Game(0,"Command & Conquer Save Game File")) {
CCMessageBox().Process("Error saving game!");
Prog_End();
exit(EXIT_SUCCESS);
}
break;
case ((int)KN_F2 | (int)KN_SHIFT_BIT): // quick load/save for debugging
if (!Load_Game(0)) {
CCMessageBox().Process("Error loading game!");
Prog_End();
exit(EXIT_SUCCESS);
}
break;
//#ifdef SCENARIO_EDITOR
case KN_F2: // enable/disable the map editor
Go_Editor(!Debug_Map);
break;
//#endif
#endif
#ifdef NEVER
case KN_F2: {
Debug_Map++;
Scenario_Editor();
Debug_Map--;
#ifdef NEVER
COORDINATE coord;
int index;
static COORDINATE _coords[] = {
0x00010001L,
0x00800080L,
0x00810081L,
0x00010081L,
0x00810001L,
0x00800081L,
0x00800001L,
0x00010080L,
0x00810080L,
0L
};
index = 0;
while (_coords[index]) {
coord = _coords[index++];
Mono_Printf("Spillage for %08lX = %d.\r", coord, Coord_Spillage_Number(coord));
}
Keyboard::Clear();
Keyboard::Get();
#endif
#ifdef NEVER
#define MAX_RADIUS 10
COORDINATE coord;
int x,y;
COORDINATE const *ptr;
int input;
int f1,f2;
TurnTrackType const *track;
#define XCENTER 160
#define YCENTER 100
for (;;) {
VisiblePage.Clear();
// Draw grid.
{
static int _gridx[] = {0,64,128,192,0,64,128,192,0,64,128,192};
static int _gridy[] = {0,0,0,0,64,64,64,64,128,128,128,128};
int index;
for (index = 0; index < 12; index++) {
LogicPage->Put_Pixel((_gridx[index]+XCENTER)-(32+64),(_gridy[index]+YCENTER)-(32+64), DKGRAY);
}
}
// Get facing #1.
LogicPage->Print("Facing #1 (0-7)?", 0, 0, WHITE, BLACK);
input = Keyboard::Get();
if (input == KA_ESC) break;
input -= KA_0;
input = Bound(input, 0, 7);
// input = MAX(input, 0);
// input = MIN(input, 7);
f1 = input;
Int_Print(f1, 100, 0, WHITE, BLACK);
// Get facing #2.
LogicPage->Print("Facing #2 (0-7)?", 0, 10, WHITE, BLACK);
input = Keyboard::Get();
if (input == KA_ESC) break;
input -= KA_0;
input = Bound(input, 0, 7);
// input = MAX(input, 0);
// input = MIN(input, 7);
f2 = input;
Int_Print(f2, 100, 10, WHITE, BLACK);
track = &TrackControl[f1][f2];
if (track->Track == 0) {
LogicPage->Print("Undefined track.", 0, 30, WHITE, BLACK);
} else {
int index; // Track index counter.
ptr = TrackPointers[track->Track-1];
index = 0;
while (ptr[index]) {
coord = Smooth_Turn(NULL, ptr[index], track->Flag);
x = (int)(coord & 0xFFFF);
y = (int)((coord >> 16) & 0xFFFF);
LogicPage->Put_Pixel(XCENTER + (x>>2), YCENTER + (y>>2), WHITE);
Delay(1);
index++;
}
}
input = Keyboard::Get();
if (input == KA_ESC) break;
}
Map.Flag_To_Redraw(true);
#endif
#ifdef NEVER
FILE *fh;
int index;
COORDINATE coord;
fh = fopen("diagonal.txt", "wt");
if (fh) {
fprintf(fh, "track 2\n");
coord = 0x0100FF00L;
for (index = 0; index <= 48; index++) {
fprintf(fh, "0x%08lXL\n", coord);
coord = Coord_Move(coord, 32, 11);
}
fprintf(fh, "\n\n");
fprintf(fh, "track 1\n");
coord = 0x01000000L;
for (index = 0; index <= 40; index++) {
fprintf(fh, "0x%08lXL\n", coord);
coord = Coord_Move(coord, 0, 11);
}
fprintf(fh, "\n\n");
fclose(fh);
}
#endif
#ifdef NEVER
FILE *fh;
int x,y,radius;
int radsize[MAX_RADIUS+2];
int count;
memset(radsize, 0, sizeof(radsize));
fh = fopen("Range.txt", "wt");
if (fh) {
fprintf(fh, "int const RadiusOffset[] = {\n");
for (radius = 0; radius <= MAX_RADIUS; radius++) {
fprintf(fh, "\t/* %-2d */\t", radius);
for (y = -MAX_RADIUS; y <= MAX_RADIUS; y++) {
for (x = -MAX_RADIUS; x <= MAX_RADIUS; x++) {
int xd,yd,dist;
xd = ABS(x);
yd = ABS(y);
if (xd > yd) {
dist = yd/2 + xd;
} else {
dist = xd/2 + yd;
}
if (dist == radius) {
dist = y*MAP_CELL_W + x;
if (y) {
if (y < 0) {
fprintf(fh, "(-MCW*%d)", ABS(y));
} else {
fprintf(fh, "(MCW*%d)", ABS(y));
}
fprintf(fh, "%c%d,", (x<0) ? '-' : '+', ABS(x));
} else {
fprintf(fh, "%d,", x);
}
radsize[radius]++;
}
}
}
fprintf(fh, "\n");
}
fprintf(fh, "};\n\n");
count = 0;
fprintf(fh, "int const RadiusCount[%d] = {", MAX_RADIUS+1);
for (radius = 0; radius <= MAX_RADIUS; radius++) {
count += radsize[radius];
fprintf(fh, "%d", count);
if (radius != MAX_RADIUS) {
fprintf(fh, ",");
}
}
fprintf(fh, "};\n");
fclose(fh);
}
#endif
}
break;
#endif
#ifdef NEVER
case ((int)KN_F3 | (int)KN_ALT_BIT): // quick load/save for debugging
Debug_Threat = (Debug_Threat == false);
Map.Flag_To_Redraw(true);
break;
#endif
case KN_F3:
Debug_Icon = (Debug_Icon == false);
Map.Flag_To_Redraw(true);
break;
/*
** Reveal entire map to player.
*/
case KN_F4:
if (GameToPlay == GAME_NORMAL) {
Debug_Unshroud = (Debug_Unshroud == false);
Map.Flag_To_Redraw(true);
}
break;
/*
** Shows sight and fire range in the form of circles emanating from the currently
** selected unit. The white circle is for sight range, the red circle is for
** fire range.
*/
case KN_F7:
if (CurrentObject.Count() && CurrentObject[0]->Is_Techno()) {
TechnoTypeClass const & ttype = (TechnoTypeClass const &)CurrentObject[0]->Class_Of();
int sight = ((int)ttype.SightRange)<<8;
int weapon = 0;
if (ttype.Primary != WEAPON_NONE) weapon = Weapons[ttype.Primary].Range;
Set_Logic_Page(SeenBuff);
COORDINATE center = CurrentObject[0]->Center_Coord();
COORDINATE center2 = CurrentObject[0]->Fire_Coord(0);
for (int r = 0; r < 255; r += 10) {
int x,y,x1,y1;
DirType r1 = (DirType)r;
DirType r2 = (DirType)((r+10) & 0xFF);
if (Map.Coord_To_Pixel(Coord_Move(center, r1, sight), x, y)) {
Map.Coord_To_Pixel(Coord_Move(center, r2, sight), x1, y1);
LogicPage->Draw_Line(x, y+8, x1, y1+8, WHITE);
}
if (Map.Coord_To_Pixel(Coord_Move(center2, r1, weapon), x, y)) {
Map.Coord_To_Pixel(Coord_Move(center2, r2, weapon), x1, y1);
LogicPage->Draw_Line(x, y+8, x1, y1+8, RED);
}
}
}
break;
case ((int)KN_F4 | (int)KN_CTRL_BIT):
Debug_Unshroud = (Debug_Unshroud == false);
Map.Flag_To_Redraw(true);
break;
#ifdef NEVER
case KN_F5:
Special.IsShowPath = (Special.IsShowPath == false);
//PlayerPtr->Credits += 1000;
break;
case KN_F6:
if (Map.In_Radar(XY_Cell(Map.MapCellX+5, Map.MapCellY - 1))) {
Mono_Printf("Arrrggggghhhhh!");
} else {
Mono_Printf("No Arrrggggghhhhh!");
}
break;
case ((int)KN_F9 | (int)KN_CTRL_BIT):
if (HouseClass::As_Pointer(HOUSE_GOOD))
(HouseClass::As_Pointer(HOUSE_GOOD))->Blowup_All();
break;
case ((int)KN_F10 | (int)KN_CTRL_BIT):
if (HouseClass::As_Pointer(HOUSE_BAD))
(HouseClass::As_Pointer(HOUSE_BAD))->Blowup_All();
break;
#endif
}
}
}
/***********************************************************************************************
* Self_Regulate -- Regulates the logic timer to result in smooth animation *
* *
* The self regulation process checks the number of frames displayed *
* per second and from this determines the amount of time to devote *
* to internal logic processing. By adjusting the time allotted to *
* internal processing, smooth animation can be maintained. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: In order for this routine to work properly it MUST be *
* called every display loop. *
* *
* HISTORY: *
* 07/31/1991 JLB : Created. *
* 07/05/1994 JLB : Handles new monochrome system. *
*=============================================================================================*/
#define UPDATE_INTERVAL TIMER_SECOND
void Self_Regulate(void)
{
static CountDownTimerClass DebugTimer(BT_SYSTEM);
static ObjectClass * _lastobject = 0;
if (!DebugTimer.Time()) {
DebugTimer.Set(UPDATE_INTERVAL);
if (MonoClass::Is_Enabled()) {
MonoClass *mono = MonoClass::Get_Current();
mono->Set_Default_Attribute(2);
switch (MonoPage) {
case 0:
mono = &MonoArray[0];
mono->Clear();
/*
** Display the status of the currently selected object.
*/
if (CurrentObject.Count()) {
_lastobject = CurrentObject[0];
}
if (_lastobject && !_lastobject->IsActive) {
_lastobject = 0;
}
if (_lastobject) {
_lastobject->Debug_Dump(mono);
}
Logic.Debug_Dump(mono);
mono->Set_Cursor(0, 20);
mono->Printf(
"Heap size:%10ld \r"
"Largest: %10ld \r"
"Ttl Free: %10ld \r"
"Frag: %10ld \r",
Heap_Size(MEM_NORMAL),
Ram_Free(MEM_NORMAL),
Total_Ram_Free(MEM_NORMAL),
Total_Ram_Free(MEM_NORMAL)-Ram_Free(MEM_NORMAL)
);
*MonoClass::Get_Current() = *mono;
break;
}
MonoArray[MonoPage] = *mono;
}
}
}
#endif