|
|
|
@@ -2,6 +2,7 @@
|
|
|
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
|
|
#include "rpl4fe.h"
|
|
|
|
|
#include "rpl4console.h"
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string>
|
|
|
|
@@ -154,6 +155,12 @@ namespace
|
|
|
|
|
FEState *gFE = NULL;
|
|
|
|
|
int gLastMissionSeconds = 0;
|
|
|
|
|
|
|
|
|
|
// carried across races so cycling back to the menu keeps the
|
|
|
|
|
// player's loadout (and names the results screen's score rows)
|
|
|
|
|
int gPersistSelection[GroupCount];
|
|
|
|
|
Logical gHavePersist = False;
|
|
|
|
|
char gLastPilotName[24] = "Pilot";
|
|
|
|
|
|
|
|
|
|
const COLORREF kGreenBright = RGB(64, 255, 64);
|
|
|
|
|
const COLORREF kGreenDim = RGB(24, 140, 24);
|
|
|
|
|
const COLORREF kBlack = RGB(0, 0, 0);
|
|
|
|
@@ -588,8 +595,15 @@ Logical
|
|
|
|
|
{
|
|
|
|
|
FEState fe;
|
|
|
|
|
memset(&fe, 0, sizeof(fe));
|
|
|
|
|
strcpy(fe.pilotName, "Pilot");
|
|
|
|
|
fe.selection[GroupLength] = 2; // 5:00
|
|
|
|
|
strcpy(fe.pilotName, gLastPilotName);
|
|
|
|
|
if (gHavePersist)
|
|
|
|
|
{
|
|
|
|
|
memcpy(fe.selection, gPersistSelection, sizeof(fe.selection));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
fe.selection[GroupLength] = 2; // 5:00
|
|
|
|
|
}
|
|
|
|
|
gFE = &fe;
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------
|
|
|
|
@@ -688,6 +702,9 @@ Logical
|
|
|
|
|
{
|
|
|
|
|
strcpy(fe.pilotName, "Pilot");
|
|
|
|
|
}
|
|
|
|
|
strcpy(gLastPilotName, fe.pilotName);
|
|
|
|
|
memcpy(gPersistSelection, fe.selection, sizeof(gPersistSelection));
|
|
|
|
|
gHavePersist = True;
|
|
|
|
|
|
|
|
|
|
std::string egg;
|
|
|
|
|
egg.reserve(16384);
|
|
|
|
@@ -727,6 +744,295 @@ int
|
|
|
|
|
return gLastMissionSeconds;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//########################################################################
|
|
|
|
|
//######################## RPL4FrontEnd_ShowResults ######################
|
|
|
|
|
//########################################################################
|
|
|
|
|
|
|
|
|
|
namespace
|
|
|
|
|
{
|
|
|
|
|
struct ResultRow
|
|
|
|
|
{
|
|
|
|
|
char name[32];
|
|
|
|
|
int score;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct ResultsState
|
|
|
|
|
{
|
|
|
|
|
HWND window;
|
|
|
|
|
HFONT textFont;
|
|
|
|
|
HFONT titleFont;
|
|
|
|
|
ResultRow rows[16];
|
|
|
|
|
int rowCount;
|
|
|
|
|
RECT continueRect;
|
|
|
|
|
Logical dismissed;
|
|
|
|
|
Logical closed;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ResultsState *gRS = NULL;
|
|
|
|
|
|
|
|
|
|
const char *PlaceName(int place)
|
|
|
|
|
{
|
|
|
|
|
static const char *kPlaces[] = { "1ST", "2ND", "3RD", "4TH" };
|
|
|
|
|
static char other[8];
|
|
|
|
|
if (place < 4)
|
|
|
|
|
{
|
|
|
|
|
return kPlaces[place];
|
|
|
|
|
}
|
|
|
|
|
sprintf(other, "%dTH", place + 1);
|
|
|
|
|
return other;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PaintResults(ResultsState *rs)
|
|
|
|
|
{
|
|
|
|
|
PAINTSTRUCT ps;
|
|
|
|
|
HDC hdc = BeginPaint(rs->window, &ps);
|
|
|
|
|
|
|
|
|
|
RECT client;
|
|
|
|
|
GetClientRect(rs->window, &client);
|
|
|
|
|
|
|
|
|
|
HDC mem = CreateCompatibleDC(hdc);
|
|
|
|
|
HBITMAP surface = CreateCompatibleBitmap(hdc, client.right, client.bottom);
|
|
|
|
|
HBITMAP old_surface = (HBITMAP) SelectObject(mem, surface);
|
|
|
|
|
|
|
|
|
|
FillRect(mem, &client, (HBRUSH) GetStockObject(BLACK_BRUSH));
|
|
|
|
|
SetBkMode(mem, TRANSPARENT);
|
|
|
|
|
|
|
|
|
|
SelectObject(mem, rs->titleFont);
|
|
|
|
|
SetTextColor(mem, kGreenBright);
|
|
|
|
|
RECT title = client;
|
|
|
|
|
title.top = client.bottom / 28;
|
|
|
|
|
title.bottom = title.top + client.bottom / 10;
|
|
|
|
|
DrawTextA(mem, "RACE RESULTS", -1, &title,
|
|
|
|
|
DT_CENTER | DT_TOP | DT_SINGLELINE);
|
|
|
|
|
|
|
|
|
|
SelectObject(mem, rs->textFont);
|
|
|
|
|
int row_h = client.bottom / 16;
|
|
|
|
|
int top = client.bottom / 4;
|
|
|
|
|
int left = client.right / 3;
|
|
|
|
|
int right = 2 * client.right / 3;
|
|
|
|
|
char text[48];
|
|
|
|
|
for (int i = 0; i < rs->rowCount; ++i)
|
|
|
|
|
{
|
|
|
|
|
RECT row;
|
|
|
|
|
row.left = left;
|
|
|
|
|
row.right = right;
|
|
|
|
|
row.top = top + i * row_h;
|
|
|
|
|
row.bottom = row.top + row_h;
|
|
|
|
|
|
|
|
|
|
SetTextColor(mem, (i == 0) ? kGreenBright : kGreenDim);
|
|
|
|
|
DrawTextA(mem, PlaceName(i), -1, &row,
|
|
|
|
|
DT_LEFT | DT_VCENTER | DT_SINGLELINE);
|
|
|
|
|
|
|
|
|
|
RECT name = row;
|
|
|
|
|
name.left += (right - left) / 5;
|
|
|
|
|
DrawTextA(mem, rs->rows[i].name, -1, &name,
|
|
|
|
|
DT_LEFT | DT_VCENTER | DT_SINGLELINE);
|
|
|
|
|
|
|
|
|
|
sprintf(text, "%d", rs->rows[i].score);
|
|
|
|
|
DrawTextA(mem, text, -1, &row,
|
|
|
|
|
DT_RIGHT | DT_VCENTER | DT_SINGLELINE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HBRUSH frame_brush = CreateSolidBrush(kGreenBright);
|
|
|
|
|
FrameRect(mem, &rs->continueRect, frame_brush);
|
|
|
|
|
DeleteObject(frame_brush);
|
|
|
|
|
SetTextColor(mem, kGreenBright);
|
|
|
|
|
DrawTextA(mem, "C O N T I N U E", -1, &rs->continueRect,
|
|
|
|
|
DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
|
|
|
|
|
|
|
|
|
BitBlt(hdc, 0, 0, client.right, client.bottom, mem, 0, 0, SRCCOPY);
|
|
|
|
|
|
|
|
|
|
SelectObject(mem, old_surface);
|
|
|
|
|
DeleteObject(surface);
|
|
|
|
|
DeleteDC(mem);
|
|
|
|
|
EndPaint(rs->window, &ps);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LRESULT CALLBACK
|
|
|
|
|
ResultsWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
|
|
|
{
|
|
|
|
|
ResultsState *rs = gRS;
|
|
|
|
|
|
|
|
|
|
switch (message)
|
|
|
|
|
{
|
|
|
|
|
case WM_PAINT:
|
|
|
|
|
if (rs != NULL)
|
|
|
|
|
{
|
|
|
|
|
PaintResults(rs);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case WM_LBUTTONDOWN:
|
|
|
|
|
if (rs != NULL)
|
|
|
|
|
{
|
|
|
|
|
POINT point = { (int)(short) LOWORD(lParam), (int)(short) HIWORD(lParam) };
|
|
|
|
|
if (PtInRect(&rs->continueRect, point))
|
|
|
|
|
{
|
|
|
|
|
rs->dismissed = True;
|
|
|
|
|
PostMessageA(rs->window, WM_NULL, 0, 0);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case WM_ERASEBKGND:
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return DefWindowProcA(hwnd, message, wParam, lParam);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Logical
|
|
|
|
|
RPL4FrontEnd_ShowResults(
|
|
|
|
|
HINSTANCE instance,
|
|
|
|
|
HWND main_window
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
int result_count = RPL4LocalConsole_ResultCount();
|
|
|
|
|
if (result_count <= 0)
|
|
|
|
|
{
|
|
|
|
|
return True; // nothing to show (first boot)
|
|
|
|
|
}
|
|
|
|
|
if (!IsWindow(main_window))
|
|
|
|
|
{
|
|
|
|
|
return False;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ResultsState rs;
|
|
|
|
|
memset(&rs, 0, sizeof(rs));
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------
|
|
|
|
|
// Intake, then sort descending by score. Single-player results
|
|
|
|
|
// carry the pilot's own name; with more pods the roster will map
|
|
|
|
|
// host IDs to Steam personas (until then: pod number).
|
|
|
|
|
//---------------------------------------------------------------
|
|
|
|
|
for (int i = 0; i < result_count && rs.rowCount < 16; ++i)
|
|
|
|
|
{
|
|
|
|
|
int host_ID, score;
|
|
|
|
|
if (!RPL4LocalConsole_GetResult(i, &host_ID, &score))
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
ResultRow *row = &rs.rows[rs.rowCount++];
|
|
|
|
|
if (result_count == 1)
|
|
|
|
|
{
|
|
|
|
|
sprintf(row->name, "%s", gLastPilotName);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
sprintf(row->name, "POD %d", host_ID);
|
|
|
|
|
}
|
|
|
|
|
row->score = score;
|
|
|
|
|
}
|
|
|
|
|
for (int i = 1; i < rs.rowCount; ++i)
|
|
|
|
|
{
|
|
|
|
|
ResultRow key = rs.rows[i];
|
|
|
|
|
int j = i - 1;
|
|
|
|
|
while (j >= 0 && rs.rows[j].score < key.score)
|
|
|
|
|
{
|
|
|
|
|
rs.rows[j + 1] = rs.rows[j];
|
|
|
|
|
--j;
|
|
|
|
|
}
|
|
|
|
|
rs.rows[j + 1] = key;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static Logical class_registered = False;
|
|
|
|
|
if (!class_registered)
|
|
|
|
|
{
|
|
|
|
|
class_registered = True;
|
|
|
|
|
WNDCLASSA window_class;
|
|
|
|
|
memset(&window_class, 0, sizeof(window_class));
|
|
|
|
|
window_class.style = CS_HREDRAW | CS_VREDRAW;
|
|
|
|
|
window_class.lpfnWndProc = ResultsWndProc;
|
|
|
|
|
window_class.hInstance = instance;
|
|
|
|
|
window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
|
|
|
window_class.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
|
|
|
|
|
window_class.lpszClassName = "RPResults";
|
|
|
|
|
RegisterClassA(&window_class);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RECT client;
|
|
|
|
|
GetClientRect(main_window, &client);
|
|
|
|
|
|
|
|
|
|
rs.window = CreateWindowExA(
|
|
|
|
|
0, "RPResults", "",
|
|
|
|
|
WS_CHILD | WS_VISIBLE,
|
|
|
|
|
0, 0, client.right, client.bottom,
|
|
|
|
|
main_window, NULL, instance, NULL);
|
|
|
|
|
if (rs.window == NULL)
|
|
|
|
|
{
|
|
|
|
|
return False;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int text_h = client.bottom / 40;
|
|
|
|
|
if (text_h < 16) text_h = 16;
|
|
|
|
|
if (text_h > 24) text_h = 24;
|
|
|
|
|
rs.textFont = CreateFontA(-(text_h * 3 / 2), 0, 0, 0, FW_NORMAL,
|
|
|
|
|
FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS,
|
|
|
|
|
CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_MODERN,
|
|
|
|
|
"Consolas");
|
|
|
|
|
rs.titleFont = CreateFontA(-(text_h * 2), 0, 0, 0, FW_BOLD,
|
|
|
|
|
FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS,
|
|
|
|
|
CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_MODERN,
|
|
|
|
|
"Consolas");
|
|
|
|
|
|
|
|
|
|
int row_h = client.bottom / 36;
|
|
|
|
|
if (row_h < 18) row_h = 18;
|
|
|
|
|
if (row_h > 30) row_h = 30;
|
|
|
|
|
int col_w = client.right / 5;
|
|
|
|
|
rs.continueRect.left = (client.right - col_w) / 2;
|
|
|
|
|
rs.continueRect.right = rs.continueRect.left + col_w;
|
|
|
|
|
rs.continueRect.top = client.bottom - 3 * row_h;
|
|
|
|
|
rs.continueRect.bottom = client.bottom - row_h;
|
|
|
|
|
|
|
|
|
|
gRS = &rs;
|
|
|
|
|
SetFocus(rs.window);
|
|
|
|
|
InvalidateRect(rs.window, NULL, TRUE);
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------
|
|
|
|
|
// Modal until CONTINUE (or any key) - same shape as the menu loop
|
|
|
|
|
//---------------------------------------------------------------
|
|
|
|
|
MSG msg;
|
|
|
|
|
while (!rs.dismissed && !rs.closed)
|
|
|
|
|
{
|
|
|
|
|
BOOL result = GetMessageA(&msg, NULL, 0, 0);
|
|
|
|
|
if (result <= 0)
|
|
|
|
|
{
|
|
|
|
|
rs.closed = True;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (msg.message == WM_QUIT ||
|
|
|
|
|
(msg.message == WM_SYSCOMMAND && msg.wParam == SC_CLOSE))
|
|
|
|
|
{
|
|
|
|
|
rs.closed = True;
|
|
|
|
|
}
|
|
|
|
|
if (msg.message == WM_CLOSE && msg.hwnd == main_window)
|
|
|
|
|
{
|
|
|
|
|
rs.closed = True;
|
|
|
|
|
}
|
|
|
|
|
if (msg.message == WM_KEYDOWN &&
|
|
|
|
|
(msg.wParam == VK_RETURN || msg.wParam == VK_SPACE ||
|
|
|
|
|
msg.wParam == VK_ESCAPE))
|
|
|
|
|
{
|
|
|
|
|
rs.dismissed = True;
|
|
|
|
|
}
|
|
|
|
|
TranslateMessage(&msg);
|
|
|
|
|
DispatchMessageA(&msg);
|
|
|
|
|
if (!IsWindow(main_window))
|
|
|
|
|
{
|
|
|
|
|
rs.closed = True;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DestroyWindow(rs.window);
|
|
|
|
|
DeleteObject(rs.textFont);
|
|
|
|
|
DeleteObject(rs.titleFont);
|
|
|
|
|
gRS = NULL;
|
|
|
|
|
|
|
|
|
|
return !rs.closed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//########################################################################
|
|
|
|
|
// Ordinal plasma graphics (verbatim from the console / TEST.EGG)
|
|
|
|
|
//########################################################################
|
|
|
|
|