// // 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 #ifdef CHEAT_KEYS static CDTimerClass 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