CnC_Remastered_Collection/REDALERT/DEBUG.CPP

558 lines
21 KiB
C++
Raw Normal View History

//
// 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: /CounterStrike/DEBUG.CPP 1 3/03/97 10:24a 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 18, 1996 [JLB] *
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* Self_Regulate -- Regulates the logic timer to result in smooth animation. *
* Debug_Key -- Debug mode keyboard processing. *
* Bench_Time -- Convert benchmark timer into descriptive string. *
* Benchmarks -- Display the performance tracking benchmarks. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "function.h"
#include "vortex.h"
#include <stdarg.h>
#ifdef CHEAT_KEYS
static CDTimerClass<SystemTimerClass> DebugTimer;
int VortexFrame = -1;
/***********************************************************************************************
* 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_BACKSPACE:
if (ChronalVortex.Is_Active()) {
ChronalVortex.Disappear();
} else {
int xxxx = Get_Mouse_X() + Map.TacPixelX;
int yyyy = Get_Mouse_Y() + Map.TacPixelY;
CELL cell = Map.DisplayClass::Click_Cell_Calc(xxxx,yyyy);
ChronalVortex.Appear ( Cell_Coord (cell) );
}
break;
#ifdef WIN32
case KN_J:
Debug_MotionCapture = true;
break;
#ifdef OBSOLETE
case KN_K:
/*
** time to create a screen shot using the PCX code (if it works)
*/
if (!Debug_MotionCapture) {
GraphicBufferClass temp_page( SeenBuff.Get_Width(),
SeenBuff.Get_Height(),
NULL,
SeenBuff.Get_Width() * SeenBuff.Get_Height());
CDFileClass file;
char filename[30];
SeenBuff.Blit(temp_page);
for (int lp = 0; lp < 99; lp ++) {
sprintf(filename, "scrsht%02d.pcx", lp);
file.Set_Name(filename);
if (!file.Is_Available()) break;
}
file.Cache(200000);
Write_PCX_File(file, temp_page, & GamePalette);
Sound_Effect(VOC_BEEP);
}
break;
#endif
#endif
case KN_P:
{
for (SpecialWeaponType spc = SPC_FIRST; spc < SPC_COUNT; spc++) {
PlayerPtr->SuperWeapon[spc].Enable(true, true);
PlayerPtr->SuperWeapon[spc].Forced_Charge(true);
Map.Add(RTTI_SPECIAL, spc);
Map.Column[1].Flag_To_Redraw();
}
}
break;
case KN_I:
{
Map.Flash_Power();
Map.Flash_Money();
}
break;
case KN_O:
{
AircraftClass * air = new AircraftClass(AIRCRAFT_HIND, PlayerPtr->Class->House);
if (air) {
air->Height = 0;
air->Unlimbo(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), DIR_N);
}
}
break;
case KN_B:
{
AircraftClass * air = new AircraftClass(AIRCRAFT_LONGBOW, PlayerPtr->Class->House);
if (air) {
air->Height = 0;
air->Unlimbo(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), DIR_N);
}
}
break;
case KN_GRAVE:
{
WarheadType warhead = Random_Pick(WARHEAD_HE, WARHEAD_FIRE);
COORDINATE coord = Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y());
int damage = 1000;
new AnimClass(Combat_Anim(damage, warhead, Map[coord].Land_Type()), coord);
Explosion_Damage(coord, damage, NULL, warhead);
}
break;
case KN_C:
Debug_Cheat = (Debug_Cheat == false);
PlayerPtr->IsRecalcNeeded = true;
/*
** 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 = MAP_CELL_W-2;
Map.MapCellHeight = MAP_CELL_H-2;
} 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;
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_DELETE:
if (CurrentObject.Count()) {
Map.Recalc();
//CurrentObject[0]->Detach_All();
if (CurrentObject[0]->What_Am_I() == RTTI_BUILDING) {
((BuildingClass *)CurrentObject[0])->Sell_Back(1);
} else {
ObjectClass * object = CurrentObject[0];
object->Unselect();
object->Limbo();
delete object;
}
}
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;
case KN_LBRACKET:
case KN_F11:
if (MonoPage == DMONO_FIRST) {
MonoPage = DMonoType(DMONO_COUNT-1);
} else {
MonoPage = DMonoType(MonoPage - 1);
}
DebugTimer = 0;
break;
case KN_RBRACKET:
case KN_F12:
MonoPage = DMonoType(MonoPage + 1);
if (MonoPage == DMONO_COUNT) {
MonoPage = DMONO_FIRST;
}
DebugTimer = 0;
break;
case KN_V:
case KN_F3:
Debug_Icon = (Debug_Icon == false);
Map.Flag_To_Redraw(true);
break;
/*
** Reveal entire map to player.
*/
// case KN_F4:
// if (Session.Type == 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.PrimaryWeapon != NULL) weapon = ttype.PrimaryWeapon->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;
default:
break;
}
}
}
/***********************************************************************************************
* Bench_Time -- Convert benchmark timer into descriptive string. *
* *
* This routine will take the values of the benchmark timer specified and build a string *
* that displays the average time each event consumed as well as the ranking of how much *
* time that event took (total) during the tracking duration (one second?). *
* *
* INPUT: btype -- The benchmark to convert to a descriptive string. *
* *
* OUTPUT: Returns with a pointer to the descriptive string of the benchmark specified. *
* *
* WARNINGS: The value returned is a pointer to a static buffer. As such, it is only valid *
* until the next time that this routine is called. *
* *
* HISTORY: *
* 07/18/1996 JLB : Created. *
*=============================================================================================*/
static char const * Bench_Time(BenchType btype)
{
static char buffer[32];
int rootcount = Benches[BENCH_GAME_FRAME].Count();
if (rootcount == 0) rootcount = 1;
int roottime = Benches[BENCH_GAME_FRAME].Value();
int count = Benches[btype].Count();
int time = Benches[btype].Value();
if (count > 0 && count * time > roottime * rootcount) time = roottime / count;
int percent = 0;
if (roottime != 0 && rootcount != 0) {
percent = ((count * time) * 99) / (roottime * rootcount);
}
if (percent > 99) percent = 99;
sprintf(buffer, "%-2d%% %7d", percent, time);
return(buffer);
}
/***********************************************************************************************
* Benchmarks -- Display the performance tracking benchmarks. *
* *
* This will display the benchmarks for the various processes that are being tracked. The *
* display will indicate the fraction that each process is consuming out of the entire *
* process time as well as the time consumed by each individual event. The total fraction *
* is useful for determing what should be optimized. The individual time is useful for *
* guaging the effectiveness of optimization changes. *
* *
* INPUT: mono -- Pointer to the monochrome screen that the display will use. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 07/18/1996 JLB : Created. *
*=============================================================================================*/
static void Benchmarks(MonoClass * mono)
{
static bool _first = true;
if (_first) {
_first = false;
mono->Clear();
mono->Set_Cursor(0, 0);
mono->Print(Text_String(TXT_DEBUG_PERFORMANCE));
if (Benches == NULL) {
mono->Set_Cursor(20, 15);
mono->Printf(TXT_NO_PENTIUM);
}
}
if (Benches != NULL) {
mono->Set_Cursor(1, 2);mono->Printf("%s", Bench_Time(BENCH_FINDPATH));
mono->Set_Cursor(1, 4);mono->Printf("%s", Bench_Time(BENCH_GREATEST_THREAT));
mono->Set_Cursor(1, 6);mono->Printf("%s", Bench_Time(BENCH_AI));
mono->Set_Cursor(1, 8);mono->Printf("%s", Bench_Time(BENCH_PCP));
mono->Set_Cursor(1, 10);mono->Printf("%s", Bench_Time(BENCH_EVAL_OBJECT));
mono->Set_Cursor(1, 12);mono->Printf("%s", Bench_Time(BENCH_EVAL_CELL));
mono->Set_Cursor(1, 14);mono->Printf("%s", Bench_Time(BENCH_EVAL_WALL));
mono->Set_Cursor(1, 16);mono->Printf("%s", Bench_Time(BENCH_MISSION));
mono->Set_Cursor(14, 2);mono->Printf("%s", Bench_Time(BENCH_CELL));
mono->Set_Cursor(14, 4);mono->Printf("%s", Bench_Time(BENCH_OBJECTS));
mono->Set_Cursor(14, 6);mono->Printf("%s", Bench_Time(BENCH_ANIMS));
mono->Set_Cursor(27, 2);mono->Printf("%s", Bench_Time(BENCH_PALETTE));
mono->Set_Cursor(40, 2);mono->Printf("%s", Bench_Time(BENCH_GSCREEN_RENDER));
mono->Set_Cursor(40, 4);mono->Printf("%s", Bench_Time(BENCH_SIDEBAR));
mono->Set_Cursor(40, 6);mono->Printf("%s", Bench_Time(BENCH_RADAR));
mono->Set_Cursor(40, 8);mono->Printf("%s", Bench_Time(BENCH_TACTICAL));
mono->Set_Cursor(40, 10);mono->Printf("%s", Bench_Time(BENCH_POWER));
mono->Set_Cursor(40, 12);mono->Printf("%s", Bench_Time(BENCH_SHROUD));
mono->Set_Cursor(40, 14);mono->Printf("%s", Bench_Time(BENCH_TABS));
mono->Set_Cursor(40, 16);mono->Printf("%s", Bench_Time(BENCH_BLIT_DISPLAY));
mono->Set_Cursor(66, 2);mono->Printf("%7d", Benches[BENCH_RULES].Value());
mono->Set_Cursor(66, 4);mono->Printf("%7d", Benches[BENCH_SCENARIO].Value());
for (BenchType index = BENCH_FIRST; index < BENCH_COUNT; index++) {
if (index != BENCH_RULES && index != BENCH_SCENARIO) Benches[index].Reset();
}
}
}
/***********************************************************************************************
* 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 ObjectClass * _lastobject = 0;
static bool _first=true;
if (DebugTimer == 0) {
DebugTimer = UPDATE_INTERVAL;
if (MonoClass::Is_Enabled()) {
if (_first) {
_first = false;
for (DMonoType index = DMONO_FIRST; index < DMONO_COUNT; index++) {
MonoArray[index].Clear();
}
}
/*
** Always update the stress tracking mono display even if it
** currently isn't visible.
*/
Logic.Debug_Dump(&MonoArray[DMONO_STRESS]);
MonoClass * mono = &MonoArray[MonoPage];
mono->Set_Default_Attribute(MonoClass::NORMAL);
mono->View();
switch (MonoPage) {
case DMONO_EVENTS:
Benchmarks(mono);
break;
case DMONO_OBJECT:
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);
}
break;
case DMONO_STRESS:
#ifdef OBSOLETE
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)
);
#endif
break;
case DMONO_HOUSE:
mono->Clear();
if (CurrentObject.Count()) {
_lastobject = CurrentObject[0];
}
if (_lastobject && !_lastobject->IsActive) {
_lastobject = 0;
}
if (_lastobject && _lastobject->Is_Techno()) {
((TechnoClass *)_lastobject)->House->Debug_Dump(mono);
}
break;
default:
break;
}
mono->Set_Cursor(0, 0);
}
}
}
#endif