hostile-takeover/game/DebugHelpers.cpp
2016-08-31 22:43:27 -04:00

1837 lines
42 KiB
C++

#include "ht.h"
#ifdef DEBUG_HELPERS
#include <windowsx.h>
#ifdef GDIPLUS
#include <gdiplus.h>
using namespace Gdiplus;
struct GdiplusStartupInput s_siGdiPlus;
ULONG_PTR s_pulGdiPlusToken;
#endif
const int kcySmallFont = 13;
const int kcyLabel = kcySmallFont - 2;
const int kcxDebugWindow = 320 + 250;
const int kcyDebugWindow = 480;
const int kcxLogWindow = 450;
const int kcyButton = 16;
struct Toggle {
word vk;
char *sz;
bool f;
HWND hwnd;
};
#define kidShowSelectedUnitsOnly 0
#define kidShowStateMachineLog 1
#define kidShowHealth 2
#define kidShowState 3
#define kidShowAction 4
#define kidShowMuntFlags 5
#define kidShowUnitGroup 6
#define kidShowFiringRange 7
#define kidShowOccupied 8
#define kidShowGobPointer 9
#define kidShowMuntAggressiveness 10
#define kidDebugSelectedStateMachine 11
#define kidSuspendUpdates 12
#define kidShowCoordinates 13
#define kidShowDst 14
#define kidShowUpdaters 15
#define kidStep 16
#define kidToggleInvincibility 17
static Toggle s_atgl[] = {
{ 'T', "Only show selected units' info (t)", false, NULL },
{ 'L', "Show StateMachine log (l)", false, NULL },
{ 'H', "Show Unit health (h)", false, NULL },
{ 'S', "Show Unit state (s)", true, NULL },
{ 'A', "Show Mobile Unit actions (a)", true, NULL },
{ 'F', "Show Mobile Unit flags (f)", false, NULL },
{ 'G', "Show Unit group (g)", false, NULL },
{ 'I', "Show Unit firing range (i)", false, NULL },
{ 'O', "Show occupied tiles (o)", false, NULL },
{ 'B', "Show Gobs' memory address (b)", false, NULL },
{ 'E', "Show Unit aggressiveness (e)", true , NULL },
{ 'M', "Debug selected StateMachine (m)", false, NULL },
{ 'U', "Suspend Updates (u)", false, NULL },
{ 'C', "Show Coordinates (c)", false, NULL },
{ 'Q', "Show txDst, tyDst (q)", false, NULL },
{ 'Z', "Show Updaters", false, NULL },
{ VK_SPACE, "Single Step (spacebar)", false, (HWND)1 },
{ 'V', "Toggle invincibility", false, (HWND)1 },
// { 'D', "Debug selected Unit (d)", false, NULL },
};
class DebugViewer {
public:
DebugViewer(char *pszTitle, int x, int y, int cx, int cy);
~DebugViewer();
void ToggleVisibility();
void ScrollTo(int dx, int dy);
virtual void Update();
virtual void Paint(HDC hdc);
virtual void OnRightClick(int x, int y);
protected:
int m_xView, m_yView;
HWND m_hwnd;
};
class TriggerViewer : public DebugViewer {
public:
TriggerViewer();
virtual void Paint(HDC hdc);
virtual void OnRightClick(int x, int y);
};
class UnitGroupViewer : public DebugViewer {
public:
UnitGroupViewer();
virtual void Paint(HDC hdc);
};
class DelayedMessageViewer : public DebugViewer {
public:
DelayedMessageViewer();
virtual void Update();
virtual void Paint(HDC hdc);
};
class CommandQueueViewer : public DebugViewer {
public:
CommandQueueViewer();
virtual void Update();
virtual void Paint(HDC hdc);
};
static TriggerViewer *s_ptgrv;
static UnitGroupViewer *s_pugv;
static DelayedMessageViewer *s_pdmv;
static CommandQueueViewer *s_pcmdqv;
HWND s_hwndDebug;
bool gfBack;
extern RECT g_rcOldWindowPos;
static int s_dyLabel;
HFONT s_hfntSmall;
char Condition::s_szDebugHelpers[300];
char QualifiedNumber::s_szDebugHelpers[200];
char TriggerAction::s_szDebugHelpers[200];
char UnitGroupAction::s_szDebugHelpers[200];
// Forward declarations
LRESULT CALLBACK DebugWndProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp);
void UpdateControlStates();
void DebugHelperCommandHandler(WPARAM wp, LPARAM lp);
void DrawLabel(HDC hdc, int x, int y, char *psz);
void InitLog();
void ExitLog();
void DrawLog(HDC hdc);
//
void InitDebugHelpers()
{
WNDCLASS wc;
memset(&wc, 0, sizeof(wc));
wc.lpfnWndProc = DebugWndProc;
wc.hInstance = ghInst;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
wc.lpszClassName = "HostileTakeoverDebug";
RegisterClass(&wc);
POINT ptT = { 0, 0 };
HMONITOR hmon = MonitorFromPoint(ptT, MONITOR_DEFAULTTOPRIMARY);
MONITORINFOEX mi;
mi.cbSize = sizeof(mi);
GetMonitorInfo(hmon, &mi);
// Adjust so the client area is the size we want
dword dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPCHILDREN;
RECT rc;
SetRect(&rc, 0, 0, kcxDebugWindow, kcyDebugWindow);
AdjustWindowRect(&rc, dwStyle, false);
s_hwndDebug = CreateWindowEx(WS_EX_TOOLWINDOW, "HostileTakeoverDebug", "HT Debug",
dwStyle, mi.rcWork.left, mi.rcWork.bottom - (rc.bottom - rc.top), // g_rcOldWindowPos.top + 320 + (60 * 2 /* Graffiti area */) + 30 /* title bar */,
rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, ghInst, 0);
// Create checkboxes/buttons
int y = 0;
for (int i = 0; i < ARRAYSIZE(s_atgl); i++) {
s_atgl[i].hwnd = CreateWindow("BUTTON", s_atgl[i].sz, WS_VISIBLE | WS_CHILD | (s_atgl[i].hwnd == NULL ? BS_CHECKBOX : 0),
320, y, 250, kcyButton, s_hwndDebug, (HMENU)i, NULL, NULL);
y += kcyButton;
}
// Update their state
UpdateControlStates();
#ifdef GDIPLUS
// Initialize GDI+
GdiplusStartup(&s_pulGdiPlusToken, &s_siGdiPlus, NULL);
#endif
HFONT hfntSystem = (HFONT)GetStockObject(SYSTEM_FONT);
LOGFONT lf;
::GetObject(hfntSystem, sizeof(lf), &lf);
lf.lfWeight = FW_LIGHT;
lf.lfHeight = 6;
strcpy(lf.lfFaceName, "Helv");
s_hfntSmall = ::CreateFontIndirect(&lf);
InitLog();
s_ptgrv = new TriggerViewer();
s_pugv = new UnitGroupViewer();
s_pdmv = new DelayedMessageViewer();
s_pcmdqv = new CommandQueueViewer();
}
void ExitDebugHelpers()
{
DeleteObject(s_hfntSmall);
ExitLog();
DestroyWindow(s_hwndDebug);
#ifdef GDIPLUS
GdiplusShutdown(s_pulGdiPlusToken);
#endif
delete s_ptgrv;
delete s_pugv;
delete s_pdmv;
delete s_pcmdqv;
}
LRESULT CALLBACK DebugWndProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
extern void DebugHelperCommandHandler(WPARAM wp, LPARAM lp);
switch (wm) {
case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);
}
break;
case WM_COMMAND:
DebugHelperCommandHandler(wp, lp);
break;
case WM_CLOSE:
ShowWindow(hwnd, false);
return 1;
case WM_ERASEBKGND:
break;
}
return DefWindowProc(hwnd, wm, wp, lp);
}
void DebugHelperKeyHandler(word vk)
{
for (int i = 0; i < ARRAYSIZE(s_atgl); i++) {
if (vk == s_atgl[i].vk) {
DebugHelperCommandHandler(i, 0);
return;
}
}
switch (vk) {
case VK_TAB:
{
extern void show(bool fBackBuffer);
extern void hide();
if (IsWindowVisible(s_hwndDebug))
hide();
else
show(GetKeyState(VK_CONTROL) < 0);
}
break;
case VK_F1:
s_ptgrv->ToggleVisibility();
break;
case VK_F2:
s_pugv->ToggleVisibility();
break;
case VK_F3:
s_pdmv->ToggleVisibility();
break;
case VK_F4:
s_pcmdqv->ToggleVisibility();
break;
}
}
void DebugHelperCommandHandler(WPARAM wp, LPARAM lp)
{
extern bool gfSuspendUpdates;
extern bool gfSingleStep;
int id = LOWORD(wp);
Toggle *ptgl = &s_atgl[id];
ptgl->f = !ptgl->f;
switch (id) {
case kidSuspendUpdates:
gfSuspendUpdates = ptgl->f;
break;
case kidStep:
if (gfSuspendUpdates)
gfSingleStep = true;
break;
case kidToggleInvincibility:
for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) {
if ((pgob->GetFlags() & (kfGobSelected | kfGobUnit)) != (kfGobSelected | kfGobUnit))
continue;
UnitGob *punt = (UnitGob *)pgob;
punt->SetUnitFlags(punt->GetUnitFlags() ^ kfUnitInvulnerable);
}
break;
case kidShowStateMachineLog:
{
RECT rc;
GetWindowRect(s_hwndDebug, &rc);
int cx = rc.right - rc.left;
int cy = rc.bottom - rc.top;
if (ptgl->f) {
// Show
SetWindowPos(s_hwndDebug, NULL, 0, 0, cx + kcxLogWindow, cy, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
} else {
// Hide
SetWindowPos(s_hwndDebug, NULL, 0, 0, cx - kcxLogWindow, cy, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
}
}
break;
}
UpdateControlStates();
}
void UpdateControlStates()
{
for (int i = 0; i < ARRAYSIZE(s_atgl); i++)
SendMessage(s_atgl[i].hwnd, BM_SETCHECK, (WPARAM)(s_atgl[i].f ? BST_CHECKED : BST_UNCHECKED), 0);
}
// Debug helpers are named in lowercase to facilite easy use
void show(bool fBack)
{
extern void paint();
gfBack = fBack;
SetWindowText(s_hwndDebug, fBack ? "HT Debug - Back Buffer" : "HT Debug - Front Buffer");
ShowWindow(s_hwndDebug, TRUE);
paint();
}
void hide()
{
ShowWindow(s_hwndDebug, FALSE);
}
void showtile(TCoord tx, TCoord ty)
{
if (s_hwndDebug == NULL || !IsWindowVisible(s_hwndDebug))
return;
WCoord wxView, wyView;
gsim.GetViewPos(&wxView, &wyView);
short xView = PcFromUwc(wxView) & 0xfffe;
short yView = PcFromUwc(wyView) & 0xfffe;
int x = PcFromTc(tx) - xView;
int y = PcFromTc(ty) - yView;
HBRUSH hbr = CreateSolidBrush(RGB(255, 255, 100));
RECT rcT;
rcT.left = x;
rcT.top = y;
rcT.right = x + PcFromTc(1);
rcT.bottom = y + PcFromTc(1);
extern Display *gpdisp;
HDC hdc = GetDC(s_hwndDebug);
RECT rcT2;
rcT2 = rcT;
rcT2.right = rcT2.left + 1;
FillRect(hdc, &rcT2, hbr);
rcT2 = rcT;
rcT2.left = rcT2.right - 1;
FillRect(hdc, &rcT2, hbr);
rcT2 = rcT;
rcT2.bottom = rcT2.top + 1;
FillRect(hdc, &rcT2, hbr);
rcT2 = rcT;
rcT2.top = rcT2.bottom - 1;
FillRect(hdc, &rcT2, hbr);
ReleaseDC(s_hwndDebug, hdc);
DeleteObject(hbr);
}
// Side colors
#ifdef GDIPLUS
static Gdiplus::Color s_aclr[] = {
Gdiplus::Color(0, 0, 0),
Gdiplus::Color(40, 0, 116, 255),
Gdiplus::Color(40, 255, 32, 0),
Gdiplus::Color(40, 255, 228, 0),
Gdiplus::Color(40, 104, 255, 255)
};
#else
static COLORREF s_acr[] = {
RGB(0, 0, 0),
RGB(0, 116, 255),
RGB(255, 32, 0),
RGB(255, 228, 0),
RGB(104, 255, 255),
};
#endif
char *s_aszActions[] = {
"None",
"Guard",
"GuardVicinity",
"GuardArea",
"Move",
"Attack",
"HuntEnemies",
};
void paint()
{
extern Display *gpdisp;
if (s_hwndDebug == NULL || !IsWindowVisible(s_hwndDebug))
return;
ModeInfo mode;
gpdisp->GetMode(&mode);
DibBitmap *pbm = gfBack ? gpdisp->GetBackDib() : gpdisp->GetFrontDib();
Size sizDib;
pbm->GetSize(&sizDib);
// Create display surface with default palm palette
struct BitmapInfo // bi
{
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[256];
};
BitmapInfo bi;
memset(&bi, 0, sizeof(bi));
bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
bi.bmiHeader.biWidth = mode.cx;
bi.bmiHeader.biHeight = -mode.cy;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 8;
bi.bmiHeader.biCompression = BI_RGB;
bi.bmiHeader.biClrUsed = 256;
gpdisp->GetWindowsPalette(bi.bmiColors);
byte *pbT;
HBITMAP hbm = CreateDIBSection(gpdisp->m_hdcMem, (BITMAPINFO *)&bi, DIB_RGB_COLORS, (void **)&pbT, NULL, 0);
if (hbm == NULL)
return;
memcpy(pbT, pbm->GetBits(), sizDib.cx * sizDib.cy);
HBITMAP hbmSav = (HBITMAP)SelectObject(gpdisp->m_hdcMem, (HGDIOBJ)hbm);
HDC hdc = gpdisp->m_hdcMem;
HDC hdcScreen = GetDC(s_hwndDebug);
Assert(hdcScreen != NULL);
s_dyLabel = 0;
if (gsim.GetLevel() != NULL) {
// Set up a smaller font for displaying info
HFONT hfntOld = (HFONT)SelectObject(hdc, s_hfntSmall);
HFONT hfntScreenOld = (HFONT)SelectObject(hdcScreen, s_hfntSmall);
SetBkMode(hdc, TRANSPARENT);
SetBkColor(hdcScreen, GetSysColor(COLOR_APPWORKSPACE));
SetTextColor(hdcScreen, RGB(255, 255, 255));
//
WCoord wxView, wyView;
gsim.GetViewPos(&wxView, &wyView);
short xView = PcFromUwc(wxView) & 0xfffe;
short yView = PcFromUwc(wyView) & 0xfffe;
wxView = WcFromUpc(xView);
wyView = WcFromUpc(yView);
::Size siz;
ggame.GetPlayfieldSize(&siz);
::Rect rcVisible;
rcVisible.left = xView;
rcVisible.top = yView;
rcVisible.right = xView + siz.cx;
rcVisible.bottom = yView + siz.cy;
Gob *apgobVisible[512];
int cpgobVisible = ggobm.FindGobs(&rcVisible, apgobVisible, ARRAYSIZE(apgobVisible), gsim.GetLevel()->GetFogMap()->GetMapPtr());
if (s_atgl[kidShowOccupied].f) {
HBRUSH hbrMunt = CreateSolidBrush(RGB(0, 0, 0));
HBRUSH hbrStructure = CreateSolidBrush(RGB(0, 255, 255));
Size sizT;
ggame.GetPlayfieldSize(&sizT);
int ctxView = sizT.cx / gcxTile + 1;
int ctyView = sizT.cy / gcyTile + 1;
TerrainMap *ptrmap = gsim.GetLevel()->GetTerrainMap();
TCoord txView = TcFromWc(wxView);
TCoord tyView = TcFromWc(wyView);
for (TCoord ty = tyView; ty < tyView + ctyView; ty++) {
for (TCoord tx = txView; tx < txView + ctxView; tx++) {
int x = PcFromTc(tx) - xView;
int y = PcFromTc(ty) - yView;
if (ptrmap->TestFlags(tx, ty, 1, 1, kbfMobileUnit)) {
RECT rcT;
rcT.left = x;
rcT.top = y;
rcT.right = x + 2;
rcT.bottom = y + 2;
FillRect(hdc, &rcT, hbrMunt);
}
if (ptrmap->TestFlags(tx, ty, 1, 1, kbfStructure)) {
RECT rcT;
rcT.left = x + 4;
rcT.top = y;
rcT.right = x + 6;
rcT.bottom = y + 2;
FillRect(hdc, &rcT, hbrStructure);
}
}
}
DeleteObject(hbrStructure);
DeleteObject(hbrMunt);
}
if (s_atgl[kidShowFiringRange].f) {
for (int i = 0; i < cpgobVisible; i++) {
Gob *pgob = apgobVisible[i];
if ((pgob->GetFlags() & (kfGobUnit | kfGobActive)) != (kfGobUnit | kfGobActive))
continue;
UnitGob *punt = (UnitGob *)pgob;
TCoord tcRange = punt->GetConsts()->tcFiringRange;
if (tcRange == 0)
continue;
if (s_atgl[kidShowSelectedUnitsOnly].f && !(pgob->GetFlags() & kfGobSelected))
continue;
#ifdef GDIPLUS
Graphics gr(hdc);
SolidBrush br(s_aclr[pgob->GetSide()]);
gr.FillRectangle(&br, rc.left, rc.top, rc.Width(), rc.Height());
#else
HBRUSH hbr = CreateSolidBrush(s_acr[pgob->GetSide()]);
HBRUSH hbrOld = (HBRUSH)SelectObject(hdc, hbr);
TPoint tpt;
pgob->GetTilePosition(&tpt);
TRect trc;
trc.Set(tpt.tx - tcRange, tpt.ty - tcRange, tpt.tx + tcRange + 1, tpt.ty + tcRange + 1);
for (TCoord ty = trc.top; ty < trc.bottom; ty++) {
for (TCoord tx = trc.left; tx < trc.right; tx++) {
TCoord dtx = abs(tx - tpt.tx);
TCoord dty = abs(ty - tpt.ty);
if (gmpDistFromDxy[dtx][dty] <= tcRange) {
WRect wrc;
wrc.left = WcFromTc(tx);
wrc.right = wrc.left + kwcTile;
wrc.top = WcFromTc(ty);
wrc.bottom = wrc.top + kwcTile;
wrc.Offset(-wxView, -wyView);
::Rect rc;
rc.FromWorldRect(&wrc);
RECT rcT;
if (gmpDistFromDxy[abs(tx - tpt.tx - 1)][dty] > tcRange) {
rcT.left = rc.left;
rcT.top = rc.top;
rcT.right = rc.left + 2;
rcT.bottom = rc.bottom;
FillRect(hdc, &rcT, hbr);
}
if (gmpDistFromDxy[abs(tx - tpt.tx + 1)][dty] > tcRange) {
rcT.left = rc.right - 2;
rcT.top = rc.top;
rcT.right = rc.right;
rcT.bottom = rc.bottom;
FillRect(hdc, &rcT, hbr);
}
if (gmpDistFromDxy[dtx][abs(ty - tpt.ty - 1)] > tcRange) {
rcT.left = rc.left;
rcT.top = rc.top;
rcT.right = rc.right;
rcT.bottom = rc.top + 2;
FillRect(hdc, &rcT, hbr);
}
if (gmpDistFromDxy[dtx][abs(ty - tpt.ty + 1)] > tcRange) {
rcT.left = rc.left;
rcT.top = rc.bottom - 2;
rcT.right = rc.right;
rcT.bottom = rc.bottom;
FillRect(hdc, &rcT, hbr);
}
}
}
}
SelectObject(hdc, hbrOld);
DeleteObject(hbr);
#endif
}
}
if (s_atgl[kidShowHealth].f) {
for (int i = 0; i < cpgobVisible; i++) {
Gob *pgob = apgobVisible[i];
if (!(pgob->GetFlags() & kfGobUnit))
continue;
if (s_atgl[kidShowSelectedUnitsOnly].f && !(pgob->GetFlags() & kfGobSelected))
continue;
WRect wrc;
pgob->GetUIBounds(&wrc);
int x = PcFromWc(wrc.left - wxView);
int y = PcFromWc(wrc.top - wyView);
UnitGob *punt = (UnitGob *)pgob;
fix fxHealth = punt->GetHealth();
fix fxHealthMax = punt->GetConsts()->GetArmorStrength();
char szT[30];
sprintf(szT, "%d/%d", fxtoi(fxHealth), fxtoi(fxHealthMax));
DrawLabel(hdc, x, y, szT);
}
s_dyLabel += kcyLabel;
}
if (s_atgl[kidShowState].f) {
for (int i = 0; i < cpgobVisible; i++) {
extern char *gaszStateNames[];
Gob *pgob = apgobVisible[i];
if (!(pgob->GetFlags() & kfGobUnit))
continue;
if (s_atgl[kidShowSelectedUnitsOnly].f && !(pgob->GetFlags() & kfGobSelected))
continue;
WRect wrc;
pgob->GetUIBounds(&wrc);
int x = PcFromWc(wrc.left - wxView);
int y = PcFromWc(wrc.top - wyView);
UnitGob *punt = (UnitGob *)pgob;
DrawLabel(hdc, x, y, gaszStateNames[punt->m_st]);
}
s_dyLabel += kcyLabel;
}
if (s_atgl[kidShowAction].f) {
for (int i = 0; i < cpgobVisible; i++) {
Gob *pgob = apgobVisible[i];
if (!(pgob->GetFlags() & kfGobMobileUnit))
continue;
if (s_atgl[kidShowSelectedUnitsOnly].f && !(pgob->GetFlags() & kfGobSelected))
continue;
WRect wrc;
pgob->GetUIBounds(&wrc);
int x = PcFromWc(wrc.left - wxView);
int y = PcFromWc(wrc.top - wyView);
MobileUnitGob *pmunt = (MobileUnitGob *)pgob;
DrawLabel(hdc, x, y, s_aszActions[pmunt->m_mua]);
}
s_dyLabel += kcyLabel;
}
if (s_atgl[kidShowMuntFlags].f) {
for (int i = 0; i < cpgobVisible; i++) {
Gob *pgob = apgobVisible[i];
if (!(pgob->GetFlags() & kfGobMobileUnit))
continue;
if (s_atgl[kidShowSelectedUnitsOnly].f && !(pgob->GetFlags() & kfGobSelected))
continue;
MobileUnitGob *pmunt = (MobileUnitGob *)pgob;
word wfMunt = pmunt->GetMobileUnitFlags();
char szT[40];
szT[0] = 0;
if (wfMunt & kfMuntChaseEnemies)
strcat(szT, "c,");
if (wfMunt & kfMuntReturnFire)
strcat(szT, "rf,");
if (wfMunt & kfMuntRunAwayWhenHit)
strcat(szT, "ra,");
if (wfMunt & kfMuntMoveWait)
strcat(szT, "mw,");
if (wfMunt & kfMuntMoveWaitingNearby)
strcat(szT, "mwn,");
if (wfMunt & kfMuntPathPending)
strcat(szT, "pp,");
if (wfMunt & kfMuntCommandPending)
strcat(szT, "cp,");
if (wfMunt & kfMuntAttackEnemiesWhenMoving)
strcat(szT, "am,");
if (wfMunt & kfMuntAttackEnemiesWhenGuarding)
strcat(szT, "ag,");
if (wfMunt & kfMuntStuck)
strcat(szT, "stk,");
if (wfMunt & kfMuntAtReplicatorInput)
strcat(szT, "rep,");
// Trim trialing ','
int cch = strlen(szT);
if (cch > 0) {
cch--;
szT[cch] = 0;
}
if (cch > 0) {
WRect wrc;
pgob->GetUIBounds(&wrc);
int x = PcFromWc(wrc.left - wxView);
int y = PcFromWc(wrc.top - wyView);
DrawLabel(hdc, x, y, szT);
}
}
s_dyLabel += kcyLabel;
}
if (s_atgl[kidShowMuntAggressiveness].f) {
for (int i = 0; i < cpgobVisible; i++) {
Gob *pgob = apgobVisible[i];
if (!(pgob->GetFlags() & kfGobMobileUnit))
continue;
if (s_atgl[kidShowSelectedUnitsOnly].f && !(pgob->GetFlags() & kfGobSelected))
continue;
MobileUnitGob *pmunt = (MobileUnitGob *)pgob;
word wfMunt = pmunt->GetMobileUnitFlags() & kfMuntAggressivenessBits;
char *pszT;
switch (pmunt->GetMobileUnitFlags() & kfMuntAggressivenessBits) {
case (kfMuntRunAwayWhenHit):
pszT = "Coward";
break;
case (kfMuntReturnFire | kfMuntStayPut):
pszT = "SelfDefense";
break;
case (kfMuntReturnFire | kfMuntAttackEnemiesWhenGuarding | kfMuntAttackEnemiesWhenMoving):
pszT = "Defender";
break;
case (kfMuntReturnFire | kfMuntAttackEnemiesWhenGuarding | kfMuntAttackEnemiesWhenMoving | kfMuntChaseEnemies):
pszT = "Pitbull";
break;
case (kfMuntReturnFire | kfMuntAttackEnemiesWhenGuarding):
pszT = "Player";
break;
default:
pszT = "?unknown?";
break;
}
WRect wrc;
pgob->GetUIBounds(&wrc);
int x = PcFromWc(wrc.left - wxView);
int y = PcFromWc(wrc.top - wyView);
DrawLabel(hdc, x, y, pszT);
}
s_dyLabel += kcyLabel;
}
if (s_atgl[kidShowUnitGroup].f) {
for (int i = 0; i < cpgobVisible; i++) {
Gob *pgob = apgobVisible[i];
if (!(pgob->GetFlags() & kfGobMobileUnit))
continue;
if (s_atgl[kidShowSelectedUnitsOnly].f && !(pgob->GetFlags() & kfGobSelected))
continue;
WRect wrc;
pgob->GetUIBounds(&wrc);
int x = PcFromWc(wrc.left - wxView);
int y = PcFromWc(wrc.top - wyView);
// See if this unit is a member of any of the unit groups
UnitGroup *pug = gsim.GetLevel()->GetUnitGroupMgr()->GetUnitGroup(pgob->GetId());
if (pug != NULL) {
char *pszName = pug->GetName();
if (pszName != NULL)
DrawLabel(hdc, x, y, pszName);
}
}
s_dyLabel += kcyLabel;
}
if (s_atgl[kidShowGobPointer].f) {
for (int i = 0; i < cpgobVisible; i++) {
Gob *pgob = apgobVisible[i];
if (s_atgl[kidShowSelectedUnitsOnly].f && !(pgob->GetFlags() & kfGobSelected))
continue;
WRect wrc;
pgob->GetUIBounds(&wrc);
int x = PcFromWc(wrc.left - wxView);
int y = PcFromWc(wrc.top - wyView);
char szT[30];
sprintf(szT, "%lx", pgob);
DrawLabel(hdc, x, y, szT);
}
s_dyLabel += kcyLabel;
}
if (s_atgl[kidShowCoordinates].f) {
for (int i = 0; i < cpgobVisible; i++) {
Gob *pgob = apgobVisible[i];
if (s_atgl[kidShowSelectedUnitsOnly].f && !(pgob->GetFlags() & kfGobSelected))
continue;
WPoint wpt;
pgob->GetPosition(&wpt);
char szT[30];
sprintf(szT, "$%04lx,$%04lx", wpt.wx, wpt.wy);
WRect wrc;
pgob->GetUIBounds(&wrc);
int x = PcFromWc(wrc.left - wxView);
int y = PcFromWc(wrc.top - wyView);
DrawLabel(hdc, x, y, szT);
}
s_dyLabel += kcyLabel;
}
if (s_atgl[kidShowDst].f) {
for (int i = 0; i < cpgobVisible; i++) {
Gob *pgob = apgobVisible[i];
if (s_atgl[kidShowSelectedUnitsOnly].f && !(pgob->GetFlags() & kfGobSelected))
continue;
if (!(pgob->GetFlags() & kfGobMobileUnit))
continue;
MobileUnitGob *pmunt = (MobileUnitGob *)pgob;
char szT[30];
sprintf(szT, "$%02lx,$%02lx", pmunt->m_txDst, pmunt->m_tyDst);
WRect wrc;
pgob->GetUIBounds(&wrc);
int x = PcFromWc(wrc.left - wxView);
int y = PcFromWc(wrc.top - wyView);
DrawLabel(hdc, x, y, szT);
}
s_dyLabel += kcyLabel;
}
if (s_atgl[kidShowUpdaters].f) {
for (int i = 0; i < cpgobVisible; i++) {
Gob *pgob = apgobVisible[i];
if (s_atgl[kidShowSelectedUnitsOnly].f && !(pgob->GetFlags() & kfGobSelected))
continue;
if (!(pgob->GetFlags() & kfGobStateMachine))
continue;
if (pgob->m_unvl.m_cDecrements != 0)
continue;
Rect rcBounds;
pgob->GetClippingBounds(&rcBounds);
rcBounds.Offset(-PcFromUwc(wxView), -PcFromUwc(wyView));
RECT rc;
rc.left = rcBounds.left;
rc.top = rcBounds.top;
rc.right = rcBounds.right;
rc.bottom = rcBounds.bottom;
HBRUSH hbrWhite = (HBRUSH)GetStockObject(WHITE_BRUSH);
RECT rcT;
rcT = rc;
rcT.bottom = rcT.top + 2;
FillRect(hdc, &rcT, hbrWhite);
rcT = rc;
rcT.right = rc.left + 2;
FillRect(hdc, &rcT, hbrWhite);
rcT = rc;
rcT.left = rcT.right - 2;
FillRect(hdc, &rcT, hbrWhite);
rcT = rc;
rcT.top = rcT.bottom - 2;
FillRect(hdc, &rcT, hbrWhite);
}
}
#if 0
// UNDONE: not acted on anywhere yet
bool fDebugUnit = s_atgl[kidDebugSelectedUnit].f;
for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) {
dword ff = pgob->GetFlags();
if (fDebugUnit && (ff & kfGobSelected))
ff |= kfGobDebug;
else
ff &= ~kfGobDebug;
pgob->SetFlags(ff);
}
#endif
// Causes the StateMachine to break on each received message, excluding kmidReserved* (e.g., Update)
bool fDebugSM = s_atgl[kidDebugSelectedStateMachine].f;
for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) {
if (fDebugSM && (pgob->GetFlags() & kfGobSelected))
pgob->EnableDebug(true);
else
pgob->EnableDebug(false);
}
#define Print(sz) TextOut(hdcScreen, x, y += kcySmallFont, sz, strlen(sz))
int x = 322;
int y = kcyButton * ARRAYSIZE(s_atgl);
char szT[200];
sprintf(szT, "Update: %ld, Time: %ld sec, Gobs: %d ",
gsim.GetUpdateCount(), gsim.GetTickCount() / 100, ggobm.GetGobCount());
Print(szT);
Print("");
Player *pplr = NULL;
while (true) {
pplr = gplrm.GetNextPlayer(pplr);
if (pplr == NULL)
break;
sprintf(szT, "Side %d", pplr->GetSide());
Print(szT);
sprintf(szT, " credits/capacity: %d/%d ", pplr->GetCredits(), pplr->GetCapacity());
Print(szT);
sprintf(szT, " power demand/supply: %d/%d ", pplr->GetPowerDemand(), pplr->GetPowerSupply());
Print(szT);
}
SelectObject(hdc, hfntOld);
SelectObject(hdcScreen, hfntScreenOld);
{
void UpdateLog();
UpdateLog();
}
}
// Draw to screen, default scale
int cxMap = mode.cx * mode.nScale;
int cyMap = mode.cy * mode.nScale;
StretchBlt(hdcScreen, 0, 0, cxMap, cyMap, gpdisp->m_hdcMem, 0, 0, mode.cx, mode.cy, SRCCOPY);
HBITMAP hbmDelete = (HBITMAP)SelectObject(gpdisp->m_hdcMem, (HGDIOBJ)hbmSav);
DeleteObject((HGDIOBJ)hbmDelete);
ReleaseDC(s_hwndDebug, hdcScreen);
}
void DrawLabel(HDC hdc, int x, int y, char *psz)
{
int cch = strlen(psz);
SetTextColor(hdc, RGB(0, 0, 0));
TextOut(hdc, x + 1, y + 1 + s_dyLabel, psz, cch);
SetTextColor(hdc, RGB(255, 255, 255));
TextOut(hdc, x, y + s_dyLabel, psz, cch);
}
//
// Log
//
const int kcleMax = 5000;
const word kfLeString = 0x0001;
const word kfLeMessage = 0x0002;
const word kfLeStateChange = 0x0004;
struct LogEntry { // le
LogEntry *pleNext;
LogEntry *plePrev;
word wf;
long cUpdate;
StateMachineId smid;
Message msg;
State stOld;
State stNew;
char sz[200];
};
// Newest entries go to the head of the list
static LogEntry *s_pleHead, *s_pleTail;
static int s_cle;
void AddLogEntry(LogEntry *ple);
//
extern char *gaszMessageNames[];
extern char *gaszStateNames[];
void Log(StateMachine *psm, Message *pmsg)
{
LogEntry *ple = new LogEntry;
memset(ple, 0, sizeof(LogEntry));
ple->wf = kfLeMessage;
ple->cUpdate = gsim.GetUpdateCount();
ple->smid = gsmm.GetId(psm);
ple->msg = *pmsg;
char szT[100];
if (pmsg->smidSender == ksmidNull)
strcpy(szT, "null");
else if (psm == gsmm.GetStateMachine(pmsg->smidSender))
strcpy(szT, "self");
else {
StateMachine *psm = gsmm.GetStateMachine(pmsg->smidSender);
char *pszName = psm == NULL ? "GONE" : psm->GetName();
sprintf(szT, "%s.%lx.%d", pszName, gsmm.GetStateMachine(pmsg->smidSender), pmsg->smidSender);
}
char szT2[100];
if (pmsg->smidSender == ksmidNull)
szT2[0] = 0;
else
sprintf(szT2, " (from %s)", szT);
sprintf(ple->sz, "M u: %ld, %s.%lx.%d recv %s/%s%s",
ple->cUpdate, psm->GetName(), psm, ple->smid, gaszStateNames[psm->m_st],
gaszMessageNames[pmsg->mid], szT2);
AddLogEntry(ple);
}
void Log(StateMachine *psm, State stOld, State stNew)
{
LogEntry *ple = new LogEntry;
memset(ple, 0, sizeof(LogEntry));
ple->wf = kfLeStateChange;
ple->cUpdate = gsim.GetUpdateCount();
ple->smid = gsmm.GetId(psm);
ple->stOld = stOld;
ple->stNew = stNew;
sprintf(ple->sz, "S u: %ld, %s.%lx.%d set to %s (prev %s)",
ple->cUpdate, psm->GetName(), psm, ple->smid,
gaszStateNames[stNew], gaszStateNames[stOld]);
AddLogEntry(ple);
}
void Log(char *psz)
{
LogEntry *ple = new LogEntry;
memset(ple, 0, sizeof(LogEntry));
ple->wf = kfLeString;
AddLogEntry(ple);
}
void AddLogEntry(LogEntry *ple)
{
ple->pleNext = s_pleHead;
if (s_pleHead != NULL)
s_pleHead->plePrev = ple;
else
s_pleTail = ple;
s_pleHead = ple;
if (s_cle >= kcleMax) {
LogEntry *pleDel = s_pleTail;
s_pleTail->plePrev->pleNext = NULL;
s_pleTail = s_pleTail->plePrev;
delete pleDel;
} else {
s_cle++;
}
}
void InitLog()
{
s_pleHead = NULL;
}
void ClearLog()
{
LogEntry *ple = s_pleHead;
while (ple != NULL) {
LogEntry *pleNext = ple->pleNext;
delete ple;
ple = pleNext;
}
s_pleHead = NULL;
s_pleTail = NULL;
s_cle = 0;
}
// For immediate window
void OutputLog(int c)
{
for (LogEntry *ple = s_pleHead; ple != NULL; ple = ple->pleNext) {
if (c-- <= 0)
break;
OutputDebugString(ple->sz);
OutputDebugString("\n");
}
}
void ExitLog()
{
ClearLog();
}
void UpdateLog()
{
if (!s_atgl[kidShowStateMachineLog].f)
return;
HDC hdc = GetDC(s_hwndDebug);
HFONT hfntOld = (HFONT)SelectObject(hdc, s_hfntSmall);
DrawLog(hdc);
SelectObject(hdc, hfntOld);
ReleaseDC(s_hwndDebug, hdc);
}
void DrawLog(HDC hdc)
{
int x = kcxDebugWindow + 2;
int y = kcyDebugWindow - kcySmallFont;
for (LogEntry *ple = s_pleHead; ple != NULL && y >= 0; ple = ple->pleNext) {
// Filter unselected Gobs if we're in that mode
if (s_atgl[kidShowSelectedUnitsOnly].f) {
Gob *pgob = ggobm.GetGob(ple->smid);
if (pgob == NULL || !(pgob->GetFlags() & kfGobSelected))
continue;
}
RECT rc;
rc.left = x;
rc.top = y;
rc.right = x + kcxLogWindow;
rc.bottom = y + kcySmallFont;
ExtTextOut(hdc, x, y, ETO_OPAQUE, &rc, ple->sz, strlen(ple->sz), NULL);
y -= kcySmallFont;
}
// Clear any unused space above the displayed log entries
if (y > 0) {
RECT rc;
rc.left = kcxDebugWindow;
rc.top = 0;
rc.right = rc.left + kcxLogWindow;
rc.bottom = y + kcySmallFont;
HBRUSH hbr = CreateSolidBrush(GetBkColor(hdc));
FillRect(hdc, &rc, hbr);
DeleteObject(hbr);
}
}
//
// DebugViewer implementation
//
DebugViewer::DebugViewer(char *pszTitle, int x, int y, int cx, int cy)
{
LRESULT CALLBACK DebugViewerWndProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp);
static char s_szClass[] = "HostileTakeoverDebugViewer";
static bool s_fRegistered = false;
if (!s_fRegistered) {
WNDCLASS wc;
memset(&wc, 0, sizeof(wc));
wc.lpfnWndProc = DebugViewerWndProc;
wc.hInstance = ghInst;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
wc.lpszClassName = s_szClass;
RegisterClass(&wc);
s_fRegistered = true;
}
POINT ptT = { 0, 0 };
HMONITOR hmon = MonitorFromPoint(ptT, MONITOR_DEFAULTTOPRIMARY);
MONITORINFOEX mi;
mi.cbSize = sizeof(mi);
GetMonitorInfo(hmon, &mi);
// Right-align for negative x's
x = x < 0 ? mi.rcWork.right + x : mi.rcWork.left + x;
dword dwStyle = WS_OVERLAPPED | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
m_hwnd = CreateWindowEx(0, s_szClass, pszTitle,
dwStyle, x, y, cx, cy, NULL, NULL, ghInst, 0);
SetWindowLong(m_hwnd, GWL_USERDATA, (LONG)this);
m_xView = m_yView = 0;
}
DebugViewer::~DebugViewer()
{
DestroyWindow(m_hwnd);
}
void DebugViewer::OnRightClick(int x, int y)
{
}
LRESULT CALLBACK DebugViewerWndProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
static bool s_fMouseDown;
static int s_xMouseDown;
static int s_yMouseDown;
DebugViewer *pdbgv = (DebugViewer *)GetWindowLong(hwnd, GWL_USERDATA);
// extern void DebugHelperCommandHandler(WPARAM wp, LPARAM lp);
switch (wm) {
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
pdbgv->Paint(hdc);
EndPaint(hwnd, &ps);
}
break;
// case WM_COMMAND:
// DebugHelperCommandHandler(wp, lp);
// break;
case WM_LBUTTONDOWN:
s_fMouseDown = true;
s_xMouseDown = GET_X_LPARAM(lp);
s_yMouseDown = GET_Y_LPARAM(lp);
SetCapture(hwnd);
break;
case WM_MOUSEMOVE:
if (s_fMouseDown) {
int dx = s_xMouseDown - GET_X_LPARAM(lp);
int dy = s_yMouseDown - GET_Y_LPARAM(lp);
pdbgv->ScrollTo(dx, dy);
s_xMouseDown = GET_X_LPARAM(lp);
s_yMouseDown = GET_Y_LPARAM(lp);
}
break;
case WM_LBUTTONUP:
s_fMouseDown = false;
ReleaseCapture();
break;
case WM_RBUTTONDOWN:
pdbgv->OnRightClick(GET_X_LPARAM(lp), GET_Y_LPARAM(lp));
break;
case WM_CLOSE:
ShowWindow(hwnd, false);
return 1;
case WM_ERASEBKGND:
if (gsim.GetLevel() != NULL)
return 1;
break;
}
return DefWindowProc(hwnd, wm, wp, lp);
}
void DebugViewer::ToggleVisibility()
{
ShowWindow(m_hwnd, !IsWindowVisible(m_hwnd));
}
void DebugViewer::Update()
{
InvalidateRect(m_hwnd, NULL, false);
}
void DebugViewer::ScrollTo(int dx, int dy)
{
m_xView += dx;
if (m_xView < 0)
m_xView = 0;
m_yView += dy;
if (m_yView < 0)
m_yView = 0;
Update();
}
void DebugViewer::Paint(HDC hdc)
{
}
//
// Trigger Viewer implementation
//
const int kcxTriggerWindow = 650;
const int kcyTriggerWindow = 700;
TriggerViewer::TriggerViewer() :
DebugViewer("HT Triggers", 330, 0, kcxTriggerWindow, kcyTriggerWindow)
{
}
void UpdateTriggerViewer()
{
s_ptgrv->Update();
}
void TriggerViewer::Paint(HDC hdc)
{
Level *plvl = gsim.GetLevel();
if (plvl == NULL)
return;
HFONT hfntOld = (HFONT)SelectObject(hdc, s_hfntSmall);
HBRUSH hbr = (HBRUSH)GetStockObject(GRAY_BRUSH);
int x = -m_xView;
int y = -m_yView;
RECT rc;
// Display per-side triggers in the per-side specified order
TriggerMgr *ptgrm = plvl->GetTriggerMgr();
for (Side side = ksideNeutral; side < kcSides; side++) {
byte *pntgr = &ptgrm->m_mpSide2nTrigger[side][0];
for (; *pntgr != 0xff; pntgr++) {
Trigger *ptgr = &ptgrm->m_atgr[*pntgr];
TriggerAction *pactn = ptgr->m_apactnLast[side];
for (Condition *pcdn = ptgr->m_pcdn; pcdn != NULL; pcdn = pcdn->m_pcdnNext) {
char szT[500];
sprintf(szT, "%s", pcdn->ToString());
Assert(strlen(szT) < sizeof(szT));
COLORREF clr;
// Armed? (ready to be triggered)
if (ptgr->m_afArmed[side]) {
// Yes. Condition satisfied?
if (pcdn->SafeIsTrue(side))
clr = RGB(164, 0, 0); // yes
else
clr = RGB(0, 0, 0);
} else {
// Disarmed (already triggered)
clr = RGB(255, 0, 0);
}
// Action in progress
if (pactn != NULL)
clr = RGB(0, 255, 0);
SetTextColor(hdc, clr);
rc.left = x;
rc.top = y;
rc.right = kcxTriggerWindow;
rc.bottom = y + kcySmallFont;
ExtTextOut(hdc, x + 2, y, ETO_OPAQUE, &rc, szT, strlen(szT), NULL);
y += kcySmallFont;
}
if (pactn != NULL) {
char szT[200];
sprintf(szT, "> %s", pactn->ToString());
Assert(strlen(szT) < sizeof(szT));
SetTextColor(hdc, RGB(0, 164, 0));
rc.top = y;
rc.bottom = y + kcySmallFont;
ExtTextOut(hdc, x + 2, y, ETO_OPAQUE, &rc, szT, strlen(szT), NULL);
y += kcySmallFont;
}
rc.left = 0;
rc.top = y;
rc.right = kcxTriggerWindow;
rc.bottom = y + 2;
FillRect(hdc, &rc, hbr);
y += 2;
}
}
if (y < kcyTriggerWindow) {
rc.bottom = kcyTriggerWindow;
FillRect(hdc, &rc, hbr);
}
SelectObject(hdc, hfntOld);
}
void TriggerViewer::OnRightClick(int xClick, int yClick)
{
Level *plvl = gsim.GetLevel();
if (plvl == NULL)
return;
int x = -m_xView;
int y = -m_yView;
// Display per-side triggers in the per-side specified order
TriggerMgr *ptgrm = plvl->GetTriggerMgr();
for (Side side = ksideNeutral; side < kcSides; side++) {
byte *pntgr = &ptgrm->m_mpSide2nTrigger[side][0];
for (; *pntgr != 0xff; pntgr++) {
Trigger *ptgr = &ptgrm->m_atgr[*pntgr];
TriggerAction *pactn = ptgr->m_apactnLast[side];
for (Condition *pcdn = ptgr->m_pcdn; pcdn != NULL; pcdn = pcdn->m_pcdnNext)
y += kcySmallFont;
if (pactn != NULL)
y += kcySmallFont;
if (yClick <= y) {
ptgr->Execute(side, true);
return;
}
y += 2;
}
}
}
//
// Unit Group Viewer implementation
//
const int kcxUnitGroupWindow = 300;
const int kcyUnitGroupWindow = 900;
UnitGroupViewer::UnitGroupViewer() :
DebugViewer("HT Unit Groups", -kcxUnitGroupWindow, 0, kcxUnitGroupWindow, kcyUnitGroupWindow)
{
}
void UpdateUnitGroupViewer()
{
s_pugv->Update();
}
void UnitGroupViewer::Paint(HDC hdc)
{
Level *plvl = gsim.GetLevel();
if (plvl == NULL)
return;
HFONT hfntOld = (HFONT)SelectObject(hdc, s_hfntSmall);
HBRUSH hbr = (HBRUSH)GetStockObject(GRAY_BRUSH);
int x = -m_xView;
int y = -m_yView;
RECT rc;
rc.left = x;
rc.right = kcxUnitGroupWindow;
UnitGroupMgr *pugm = plvl->GetUnitGroupMgr();
int cug = pugm->m_cug;
for (int i = 0; i < cug; i++) {
char szT[500];
UnitGroup *pug = &pugm->m_aug[i];
COLORREF clr;
word wf = pug->GetFlags();
if (wf & kfUgActive) {
if (wf & kfUgNeedsUnit)
clr = RGB(0, 164, 0);
else
clr = RGB(0, 255, 0);
} else {
if (wf & kfUgActivatedBefore)
clr = RGB(164, 0, 0);
else
clr = RGB(0, 0, 0);
}
SetTextColor(hdc, clr);
char szT2[500];
szT2[0] = 0;
if (wf & kfUgRandomGroup) {
if (szT2[0] != 0)
strcat(szT2, ", ");
strcat(szT2, "random");
}
if (wf & kfUgCreateAtLevelLoad) {
if (szT2[0] != 0)
strcat(szT2, ", ");
strcat(szT2, "level load");
}
if (wf & kfUgReplaceGroup) {
if (szT2[0] != 0)
strcat(szT2, ", ");
strcat(szT2, "replace");
}
if (wf & kfUgSpawn) {
if (szT2[0] != 0)
strcat(szT2, ", ");
strcat(szT2, "spawn");
}
if (wf & kfUgLoopForever) {
if (szT2[0] != 0)
strcat(szT2, ", ");
strcat(szT2, "loop");
}
if (!(wf & kfUgNotRecentlyActivated)) {
if (szT2[0] != 0)
strcat(szT2, ", ");
strcat(szT2, "RA");
}
sprintf(szT, "%s (%s)", pug->GetName(), szT2);
rc.top = y;
rc.bottom = y + kcySmallFont;
ExtTextOut(hdc, x + 2, y, ETO_OPAQUE, &rc, szT, strlen(szT), NULL);
y += kcySmallFont;
if (pug->GetFlags() & kfUgActive) {
// Show all the group's members
int cule = pug->GetUnitCount();
UnitListEntry *ple = pug->GetUnitList();
for (int iule = 0; iule < cule; iule++, ple++) {
SetTextColor(hdc, clr);
rc.left = x;
rc.top = y;
rc.right = kcxUnitGroupWindow;
rc.bottom = y + kcySmallFont;
if (!(ple->bf & kfUleBuilt)) {
sprintf(szT, "%s (building...)", gapuntc[ple->ut]->szName);
} else {
MobileUnitGob *pmunt = (MobileUnitGob *)ggobm.GetGob(ple->gid);
char *pszT;
if (pmunt == NULL) {
SetTextColor(hdc, RGB(255, 0, 0));
pszT = "dead";
} else {
if (pmunt->GetFlags() & kfGobMobileUnit)
pszT = s_aszActions[pmunt->m_mua];
else
pszT = "no action";
}
sprintf(szT, "%s (%s)", gapuntc[ple->ut]->szName, pszT);
}
ExtTextOut(hdc, x + 10, y, ETO_OPAQUE, &rc, szT, strlen(szT), NULL);
y += kcySmallFont;
}
// Show the currently executing action (if any)
if (pug->m_pactnLast != NULL) {
SetTextColor(hdc, RGB(0, 164, 0));
rc.top = y;
rc.bottom = y + kcySmallFont;
sprintf(szT, "> %s", pug->m_pactnLast->ToString());
ExtTextOut(hdc, x + 2, y, ETO_OPAQUE, &rc, szT, strlen(szT), NULL);
y += kcySmallFont;
}
}
rc.top = y;
rc.bottom = y + 2;
FillRect(hdc, &rc, hbr);
y += 2;
}
if (y < kcyUnitGroupWindow) {
rc.bottom = kcyUnitGroupWindow;
FillRect(hdc, &rc, hbr);
}
SelectObject(hdc, hfntOld);
}
//
// DelayedMessage Viewer implementation
//
const int kcxDelayedMessageWindow = 300;
const int kcyDelayedMessageWindow = 900;
DelayedMessageViewer::DelayedMessageViewer() :
DebugViewer("HT Delayed Messages", -kcxDelayedMessageWindow, 0, kcxDelayedMessageWindow, kcyDelayedMessageWindow)
{
}
void UpdateDelayedMessageViewer()
{
s_pdmv->Update();
}
void DelayedMessageViewer::Update()
{
int cdmsg = 0;
for (DelayedMessage *pdmsg = gsmm.m_pdmsgHead; pdmsg != NULL; pdmsg = pdmsg->pdmsgNext, cdmsg++);
char szT[50];
sprintf(szT, "HT Delayed Messages [%d]", cdmsg);
SetWindowText(m_hwnd, szT);
DebugViewer::Update();
}
void DelayedMessageViewer::Paint(HDC hdc)
{
Level *plvl = gsim.GetLevel();
if (plvl == NULL)
return;
HFONT hfntOld = (HFONT)SelectObject(hdc, s_hfntSmall);
HBRUSH hbr = (HBRUSH)GetStockObject(GRAY_BRUSH);
int x = -m_xView;
int y = -m_yView;
RECT rc;
GetClientRect(m_hwnd, &rc);
rc.left = x;
for (DelayedMessage *pdmsg = gsmm.m_pdmsgHead; pdmsg != NULL; pdmsg = pdmsg->pdmsgNext) {
StateMachine *psmSender = gsmm.GetStateMachine(pdmsg->msg.smidSender);
StateMachine *psmReceiver = gsmm.GetStateMachine(pdmsg->msg.smidReceiver);
char *pszSender = psmSender != NULL ? psmSender->GetName() : "<none>";
char *pszReceiver = psmReceiver != NULL ? psmReceiver->GetName() : "<none>";
char szT[500];
sprintf(szT, "%s delayed [%d] from %s.%d to %s.%d", gaszMessageNames[pdmsg->msg.mid],
pdmsg->msg.tDelivery - gsim.GetTickCount(),
pszSender, pdmsg->msg.smidSender, pszReceiver, pdmsg->msg.smidReceiver);
rc.top = y;
rc.bottom = y + kcySmallFont;
ExtTextOut(hdc, x + 2, y, ETO_OPAQUE, &rc, szT, strlen(szT), NULL);
y += kcySmallFont;
}
if (y < kcyDelayedMessageWindow) {
rc.bottom = kcyDelayedMessageWindow;
FillRect(hdc, &rc, hbr);
}
SelectObject(hdc, hfntOld);
}
//
// CommandQueue Viewer implementation
//
const int kcxCommandQueueWindow = 300;
const int kcyCommandQueueWindow = 900;
CommandQueueViewer::CommandQueueViewer() :
DebugViewer("HT Command Queue", -kcxCommandQueueWindow, 0, kcxCommandQueueWindow, kcyCommandQueueWindow)
{
}
void UpdateCommandQueueViewer()
{
s_pcmdqv->Update();
}
void CommandQueueViewer::Update()
{
char szT[50];
sprintf(szT, "HT Command Queue [%d]", gcmdq.GetCount());
SetWindowText(m_hwnd, szT);
DebugViewer::Update();
}
void CommandQueueViewer::Paint(HDC hdc)
{
Level *plvl = gsim.GetLevel();
if (plvl == NULL)
return;
HFONT hfntOld = (HFONT)SelectObject(hdc, s_hfntSmall);
HBRUSH hbr = (HBRUSH)GetStockObject(GRAY_BRUSH);
int x = -m_xView;
int y = -m_yView;
RECT rc;
GetClientRect(m_hwnd, &rc);
rc.left = x;
Message *pmsg = gcmdq.GetFirst();
int cmsg = gcmdq.GetCount();
for (int i = 0; i < cmsg; i++, pmsg++) {
StateMachine *psmSender = gsmm.GetStateMachine(pmsg->smidSender);
StateMachine *psmReceiver = gsmm.GetStateMachine(pmsg->smidReceiver);
char *pszSender = psmSender != NULL ? psmSender->GetName() : "<none>";
char *pszReceiver = psmReceiver != NULL ? psmReceiver->GetName() : "<none>";
char szT[500];
sprintf(szT, "%s from %s.%d to %s.%d", gaszMessageNames[pmsg->mid],
pszSender, pmsg->smidSender, pszReceiver, pmsg->smidReceiver);
rc.top = y;
rc.bottom = y + kcySmallFont;
ExtTextOut(hdc, x + 2, y, ETO_OPAQUE, &rc, szT, strlen(szT), NULL);
y += kcySmallFont;
}
if (y < kcyCommandQueueWindow) {
rc.bottom = kcyCommandQueueWindow;
FillRect(hdc, &rc, hbr);
}
SelectObject(hdc, hfntOld);
}
// Helpers
static char s_szUnitMask[500];
char *PszFromUnitMask(UnitMask um)
{
char *psz = s_szUnitMask;
*psz = 0;
if ((um & kumInfantry) == kumInfantry) {
strcat(psz, "any infantry,");
um &= ~kumInfantry;
}
if ((um & kumVehicles) == kumVehicles) {
strcat(psz, "any vehicle,");
um &= ~kumVehicles;
}
if ((um & kumStructures) == kumStructures) {
strcat(psz, "any structure,");
um &= ~kumStructures;
}
for (int i = 0; i < kutMax; i++) {
if (um & (1UL << i)) {
strcat(psz, gapuntc[i]->szName);
strcat(psz, ",");
}
}
// Trim trialing ','
int cch = strlen(psz);
if (cch > 0)
psz[cch - 1] = 0;
return psz;
}
static char s_szCaSideMask[500];
char *PszFromCaSideMask(word wfCaSideMask)
{
char *psz = s_szCaSideMask;
*psz = 0;
if (wfCaSideMask & (1 << knCaSideAllSides))
strcat(psz, "all sides,");
if (wfCaSideMask & (1 << knCaSideAllies))
strcat(psz, "allies,");
if (wfCaSideMask & (1 << knCaSideEnemies))
strcat(psz, "enemies,");
if (wfCaSideMask & (1 << knCaSideCurrentSide))
strcat(psz, "current side,");
if (wfCaSideMask & (1 << knCaSideSide1))
strcat(psz, "side 1,");
if (wfCaSideMask & (1 << knCaSideSide2))
strcat(psz, "side 2,");
if (wfCaSideMask & (1 << knCaSideSide3))
strcat(psz, "side 3,");
if (wfCaSideMask & (1 << knCaSideSide4))
strcat(psz, "side 4,");
// Trim trialing ','
int cch = strlen(psz);
if (cch > 0)
psz[cch - 1] = 0;
return psz;
}
#endif // def DEBUG_HELPERS