Front end: local mission-egg builder + front-end mode (Workstream B foundation)
The core 'single-player without the operator console' capability: a
zero-argument launch now builds its own mission and plays.
- game/reconstructed/btl4fe.{hpp,cpp}: the BattleTech catalog (8 maps,
8 mechs + variants, colors) and BTFeMission_WriteEgg -- emits the exact
console egg format (verified against content/MP.EGG): [mission],
[ordinals] + the 4 static rank-place plasma bitmaps, [pilots],
per-pilot loadout sections, and [largebitmap]/[smallbitmap] with
GDI-rendered 128x32 / 64x16 pilot-name plasma bitmaps.
- L4APP.H: SetEggNotationFileName / SetNetworkCommonFlatAddress setters
(from RP412) so the front end feeds the standard -egg load path.
- btl4main.cpp WinMain: front-end mode -- no -egg and no -net builds
frontend.egg locally (BTFrontEnd_Run) and boots it. -egg/-net runs
are untouched.
Verified: no-args launch -> '[frontend] built frontend.egg' -> mission
loads and runs; the generated egg is structurally identical to MP.EGG
(32x32/16x16 name bitmaps with real glyph pixels, all sections present).
Remaining in Phase 5 (deferred): the interactive on-screen catalog menu,
the in-process LocalConsole marshal (mission clock + StopMission at
expiry + single-binary loop), and the score-intake/results screen (new
for BT -- no mission-end flow exists yet). (Phase 5 of the roadmap)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,3 +21,6 @@ Thumbs.db
|
||||
# Steam AppID override: created by hand next to the exe for testing under
|
||||
# Spacewar (480) until BT412 has its own AppID. Never shipped in dist.
|
||||
steam_appid.txt
|
||||
|
||||
# front-end-built mission egg (written each launch in front-end mode)
|
||||
/content/frontend.egg
|
||||
|
||||
@@ -264,6 +264,7 @@ add_library(bt410_l4 STATIC
|
||||
"game/reconstructed/ammobin.cpp"
|
||||
"game/reconstructed/btdirect.cpp"
|
||||
"game/reconstructed/btl4app.cpp"
|
||||
"game/reconstructed/btl4fe.cpp"
|
||||
"game/reconstructed/btl4galm.cpp"
|
||||
"game/reconstructed/btl4gau2.cpp"
|
||||
"game/reconstructed/btl4gau3.cpp"
|
||||
|
||||
@@ -106,7 +106,31 @@ pattern). Forked at BT411 `4e72f0c` (2026-07-14).
|
||||
`NetTransport_Get()`); (2) `BT412_STEAM=ON` config + build (compile-check of the
|
||||
Steam transport TU against the vendored SDK; a live Steam session needs the Phase 6
|
||||
lobby + 3 machines).
|
||||
- Phases 5–7: Phase 5 (front end + LocalConsole marshal) next.
|
||||
- **Phase 5 (front end) — FOUNDATION DONE 2026-07-14; marshal/menu/results IN PROGRESS.**
|
||||
Landed the local **mission-egg builder** — the core "single-player without the
|
||||
operator console" capability:
|
||||
- `game/reconstructed/btl4fe.{hpp,cpp}`: catalogs (8 maps, 8 mechs + variants,
|
||||
colors), a `BTFeMission` config struct, and `BTFeMission_WriteEgg` that emits the
|
||||
exact console egg format (verified against `content/MP.EGG`): `[mission]`,
|
||||
`[ordinals]` + the 4 static rank-place plasma bitmaps, `[pilots]`, per-pilot
|
||||
loadout sections, `[largebitmap]`/`[smallbitmap]` with **GDI-rendered** 128×32 /
|
||||
64×16 pilot-name plasma bitmaps, and the role models.
|
||||
- Two engine setters ported from RP412's L4APP.H (`SetEggNotationFileName`,
|
||||
`SetNetworkCommonFlatAddress`) so the front end feeds the standard `-egg` path.
|
||||
- `game/btl4main.cpp` WinMain: **front-end mode** — no `-egg` and no `-net` builds
|
||||
`frontend.egg` locally and points the engine at it (`BTFrontEnd_Run`). `-egg`/`-net`
|
||||
runs are untouched.
|
||||
Verified: zero-argument launch → `[frontend] built frontend.egg` → mission loads and
|
||||
runs (ticks, gait, targeting); the generated egg is structurally identical to MP.EGG
|
||||
(32×32/16×16 name bitmaps with real glyph pixels, all sections present).
|
||||
**Remaining in Phase 5** (each substantial, deferred): interactive on-screen catalog
|
||||
menu (RP412 `RPL4FE` is 1536 lines of render-loop UI); the in-process **LocalConsole
|
||||
marshal** thread (mission clock + `StopMissionMessage` at expiry + single-binary
|
||||
menu→race→results→menu loop); and the **score-intake + results screen**, which is
|
||||
genuinely NEW for BT — BT has no working mission-end flow (mission-review msgID 0x18
|
||||
is a stub, `scoreAward=0`); v1 should collect kills/deaths from the live Comm-MFD
|
||||
`pilotList` tally at stop (see [[gauges-hud]], [[multiplayer]]).
|
||||
- Phases 6–7: Steam lobby (`btl4lobby`) + packaging remain.
|
||||
|
||||
## The seams (what plugs in where) [T0 unless noted]
|
||||
|
||||
|
||||
@@ -81,6 +81,12 @@ public:
|
||||
static bool GetFullscreen() { return mFullscreen; }
|
||||
static Logical GetSeeSolids() { return seeSolids; }
|
||||
static unsigned long GetNetworkCommonFlatAddress() { return networkCommonFlatAddress; }
|
||||
// BT412: the in-game front end (btl4fe) builds a mission egg locally and
|
||||
// feeds it through the standard -egg load path; a lobby member points the
|
||||
// engine at its listen port. These setters replace the command-line-only
|
||||
// path the arcade console used.
|
||||
static void SetNetworkCommonFlatAddress(unsigned long address) { networkCommonFlatAddress = address; }
|
||||
static void SetEggNotationFileName(const char *name) { eggNotationFileName = name; }
|
||||
static CString GetEggNotationFileName() { return eggNotationFileName; }
|
||||
static CString GetSpoolFileName() { return spoolFileName; }
|
||||
static CString GetRIOPlaybackFileName() { return rioPlaybackFileName; }
|
||||
|
||||
@@ -350,6 +350,30 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
return 1;
|
||||
}
|
||||
|
||||
//
|
||||
// FRONT-END MODE (BT412 Phase 5): no egg and no network address on the
|
||||
// command line means there is no operator console to hand us a mission.
|
||||
// Build one locally -- the in-game front end assembles a mission egg from
|
||||
// the default loadout and points the standard -egg load path at it. The
|
||||
// interactive on-screen menu and the in-process marshal layer on top of
|
||||
// this (docs/BT412-ROADMAP.md Phase 5). `-egg`/`-net` runs are untouched.
|
||||
//
|
||||
{
|
||||
const char *cmdline_egg = L4Application::GetEggNotationFileName();
|
||||
const int no_egg = (!cmdline_egg || !strlen(cmdline_egg));
|
||||
const int no_net = (L4Application::GetNetworkCommonFlatAddress() == 0);
|
||||
if (no_egg && no_net)
|
||||
{
|
||||
extern int BTFrontEnd_Run();
|
||||
if (BTFrontEnd_Run())
|
||||
std::cout << "[frontend] built frontend.egg from the default loadout"
|
||||
<< std::endl << std::flush;
|
||||
else
|
||||
std::cout << "[frontend] egg build FAILED -- falling through"
|
||||
<< std::endl << std::flush;
|
||||
}
|
||||
}
|
||||
|
||||
Start_Registering();
|
||||
|
||||
// Create the main window (single 800x600 view for dev; pod multi-monitor is Phase 8).
|
||||
|
||||
@@ -0,0 +1,352 @@
|
||||
//===========================================================================//
|
||||
// btl4fe.cpp -- BT412 in-game front end: local mission-egg builder.
|
||||
//
|
||||
// See btl4fe.hpp. This module is self-contained (no engine types) so it can
|
||||
// build the egg text before the mission machinery exists; it only writes a
|
||||
// NotationFile the standard -egg path then loads.
|
||||
//===========================================================================//
|
||||
// WIN32_LEAN_AND_MEAN keeps <windows.h> from pulling the legacy winsock.h;
|
||||
// the engine chain (via l4app.hpp) brings winsock2.h, and the two collide.
|
||||
// GDI (used below for the plasma name bitmaps) is unaffected. Same preamble
|
||||
// as game/btl4main.cpp.
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define _WIN32_WINNT 0x0500
|
||||
#define WINVER 0x0500
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "btl4fe.hpp"
|
||||
#include "l4app.hpp" // L4Application::SetEggNotationFileName
|
||||
|
||||
//---------------------------------------------------------------------------//
|
||||
// Catalogs -- the BattleTech content the console's RPConfig.xml named.
|
||||
// Maps: the 8 in BTL4.RES. Mechs: the base 8 + the known variants.
|
||||
//---------------------------------------------------------------------------//
|
||||
const char *const kBTMaps[] =
|
||||
{
|
||||
"cavern", "grass", "rav", "polar3", "polar4", "arena1", "arena2", "dbase", 0
|
||||
};
|
||||
|
||||
const char *const kBTMechs[] =
|
||||
{
|
||||
"avatar", "bhk1", "loki", "madcat", "owens", "sunder", "thor", "vulture",
|
||||
"ava1", "lok1", "lok2", "mad1", "mad2", "own1", "snd1", "thr1", "vul1",
|
||||
"blkhawk", 0
|
||||
};
|
||||
|
||||
const char *const kBTColors[] =
|
||||
{
|
||||
"White", "Red", "Blue", "Green", "Yellow", "Orange", "Purple", "Cyan", 0
|
||||
};
|
||||
|
||||
int BTCatalogCount(const char *const *list)
|
||||
{
|
||||
int n = 0;
|
||||
while (list[n] != 0) ++n;
|
||||
return n;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------//
|
||||
// The four rank-ordinal plasma bitmaps (1st..4th place), 128x32, MSB-first
|
||||
// hex, 4 pixels per nibble. These are static content -- the same graphics
|
||||
// the console shipped in every egg's [ordinals] block (verified against
|
||||
// content/MP.EGG). The pilot NAME bitmaps are GDI-rendered per mission.
|
||||
//---------------------------------------------------------------------------//
|
||||
namespace
|
||||
{
|
||||
const char *const kOrdinal[4][32] =
|
||||
{
|
||||
{ // 1st
|
||||
"00000000000000000000000000000000","00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000","00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000","00000000000000000000000000000000",
|
||||
"000038000003C000001FF00000000F00","0000F8000003C000003FF80000000F00",
|
||||
"0001F8000003C00000707C0000000F00","0001F8000003C00000603C0000000F00",
|
||||
"00007801FC0FF00000003C3DF807FF00","00007803FE0FF00000003C3FFC0FFF00",
|
||||
"00007803C703C00000003C3E3C1F0F00","000078078303C0000000783C1E1E0F00",
|
||||
"000078078003C0000000783C1E1E0F00","00007807C003C0000000F03C1E1E0F00",
|
||||
"00007807F003C0000001E03C1E1E0F00","00007803FC03C0000003C03C1E1E0F00",
|
||||
"00007801FE03C0000007803C1E1E0F00","000078007F03C000000F003C1E1E0F00",
|
||||
"000078001F03C000001E003C1E1E0F00","000078000F03C000003C003C1E1E0F00",
|
||||
"000078060F03C0000078003C1E1E0F00","000078071E03E0000078003C1E1F1F00",
|
||||
"00007803FE01F000007FFC3C1E0FFF00","00007801FC00F000007FFC3C1E07EF00",
|
||||
"00000000000000000000000000000000","00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000","00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000","00000000000000000000000000000000",
|
||||
},
|
||||
{ // 2nd
|
||||
"00000000000000000000000000000000","00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000","00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000","00000000000000000000000000000000",
|
||||
"001FFF0000003C0000003C03C0780000","001FFF0000003C0000007C03C0780000",
|
||||
"00000E0000003C000000FC03C0780000","00003C0000003C000001BC03C0780000",
|
||||
"0000700F3E1FFC000003BC0FF07BF000","0001E00F7E3FFC0000073C0FF07FF800",
|
||||
"0003800FFE7C3C0000063C03C07C7800","0007F80FFE783C00000C3C03C0783C00",
|
||||
"0007FE0F80783C0000183C03C0783C00","00001E0F00783C0000383C03C0783C00",
|
||||
"00000F0F00783C0000703C03C0783C00","00000F0F00783C00007FFF03C0783C00",
|
||||
"00000F0F00783C00007FFF03C0783C00","00000F0F00783C0000003C03C0783C00",
|
||||
"00000F0F00783C0000003C03C0783C00","00000F0F00783C0000003C03C0783C00",
|
||||
"00180F0F00783C0000003C03C0783C00","001C1F0F007C7C0000003C03E0783C00",
|
||||
"000FFE0F003FFC0000003C01F0783C00","0007FC0F001FBC0000003C00F0783C00",
|
||||
"00000000000000000000000000000000","00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000","00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000","00000000000000000000000000000000",
|
||||
},
|
||||
{ // 3rd
|
||||
"00000000000000000000000000000000","00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000","00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000","00000000000000000000000000000000",
|
||||
"001FFF03C07800000000FC03C0780000","001FFF03C07800000003FC03C0780000",
|
||||
"001E0003C07800000007C003C0780000","001E0003C0780000000F0003C0780000",
|
||||
"001E000FF07BF000000F000FF07BF000","001E000FF07FF800001E000FF07FF800",
|
||||
"001E0003C07C7800001E0003C07C7800","001FFC03C0783C00001EFC03C0783C00",
|
||||
"001FFE03C0783C00001FFE03C0783C00","00001F03C0783C00001F1F03C0783C00",
|
||||
"00000F03C0783C00001E0F03C0783C00","00000F03C0783C00001E0F03C0783C00",
|
||||
"00000F03C0783C00001E0F03C0783C00","00000F03C0783C00001E0F03C0783C00",
|
||||
"00000F03C0783C00001E0F03C0783C00","00000F03C0783C00001E0F03C0783C00",
|
||||
"00180F03C0783C00001E0F03C0783C00","001C1F03E0783C00001F1F03E0783C00",
|
||||
"000FFE01F0783C00000FFE01F0783C00","0007FC00F0783C000007FC00F0783C00",
|
||||
"00000000000000000000000000000000","00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000","00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000","00000000000000000000000000000000",
|
||||
},
|
||||
{ // 4th
|
||||
"00000000000000000000000000000000","00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000","00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000","00000000000000000000000000000000",
|
||||
"001FFF03C07800000007FC03C0780000","001FFF03C0780000000FFE03C0780000",
|
||||
"00000F03C0780000001F1F03C0780000","00000F03C0780000001E0F03C0780000",
|
||||
"00000F0FF07BF000001E0F0FF07BF000","00001F0FF07FF800001E0F0FF07FF800",
|
||||
"00001E03C07C7800001E0F03C07C7800","00003E03C0783C00001E0F03C0783C00",
|
||||
"00003C03C0783C00000F1E03C0783C00","00003C03C0783C000007FC03C0783C00",
|
||||
"00007803C0783C000007FC03C0783C00","00007803C0783C00000F1E03C0783C00",
|
||||
"00007803C0783C00001E0F03C0783C00","0000F003C0783C00001E0F03C0783C00",
|
||||
"0000F003C0783C00001E0F03C0783C00","0000F003C0783C00001E0F03C0783C00",
|
||||
"0000F003C0783C00001E0F03C0783C00","0000F003E0783C00000F1E03E0783C00",
|
||||
"0000F001F0783C00000FFE01F0783C00","0000F000F0783C000007FC00F0783C00",
|
||||
"00000000000000000000000000000000","00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000","00000000000000000000000000000000",
|
||||
"00000000000000000000000000000000","00000000000000000000000000000000",
|
||||
},
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------//
|
||||
// GDI-render `text` centered into a monochrome plasma bitmap and emit
|
||||
// it as `rows` lines of `width/4` hex chars, MSB-first (the exact
|
||||
// [BitMap] format). width/height are the plasma glyph box (128x32 for
|
||||
// the large name, 64x16 for the small). A top-down 1bpp DIB packs bit
|
||||
// 7 of byte 0 as the leftmost pixel -- the same bit order as the egg
|
||||
// hex -- so the readback is a direct nibble-to-hex conversion.
|
||||
//-------------------------------------------------------------------//
|
||||
void RenderNameBitmap(const char *text, int width, int height, FILE *out)
|
||||
{
|
||||
HDC screen = GetDC(NULL);
|
||||
HDC dc = CreateCompatibleDC(screen);
|
||||
ReleaseDC(NULL, screen);
|
||||
|
||||
// Top-down monochrome DIB: negative height, biBitCount 1.
|
||||
struct { BITMAPINFOHEADER h; RGBQUAD c[2]; } bi;
|
||||
memset(&bi, 0, sizeof(bi));
|
||||
bi.h.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bi.h.biWidth = width;
|
||||
bi.h.biHeight = -height; // top-down
|
||||
bi.h.biPlanes = 1;
|
||||
bi.h.biBitCount = 1;
|
||||
bi.h.biCompression = BI_RGB;
|
||||
bi.c[0].rgbRed = bi.c[0].rgbGreen = bi.c[0].rgbBlue = 0; // index 0 = off
|
||||
bi.c[1].rgbRed = bi.c[1].rgbGreen = bi.c[1].rgbBlue = 255; // index 1 = lit
|
||||
|
||||
void *bits = 0;
|
||||
HBITMAP dib = CreateDIBSection(dc, (BITMAPINFO *)&bi, DIB_RGB_COLORS, &bits, NULL, 0);
|
||||
HGDIOBJ oldBmp = SelectObject(dc, dib);
|
||||
|
||||
RECT rect = { 0, 0, width, height };
|
||||
SetBkColor(dc, RGB(0, 0, 0));
|
||||
SetTextColor(dc, RGB(255, 255, 255));
|
||||
ExtTextOutA(dc, 0, 0, ETO_OPAQUE, &rect, "", 0, NULL); // clear to off
|
||||
|
||||
// A small readable font that fits the glyph box height.
|
||||
HFONT font = CreateFontA(
|
||||
height >= 32 ? 24 : 12, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE,
|
||||
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
|
||||
NONANTIALIASED_QUALITY, FF_DONTCARE | DEFAULT_PITCH, "Arial");
|
||||
HGDIOBJ oldFont = SelectObject(dc, font);
|
||||
SetBkMode(dc, TRANSPARENT);
|
||||
|
||||
SIZE sz;
|
||||
GetTextExtentPoint32A(dc, text, (int)strlen(text), &sz);
|
||||
int tx = (width - sz.cx) / 2; if (tx < 0) tx = 0;
|
||||
int ty = (height - sz.cy) / 2; if (ty < 0) ty = 0;
|
||||
TextOutA(dc, tx, ty, text, (int)strlen(text));
|
||||
GdiFlush();
|
||||
|
||||
// Emit: each DIB row is width bits, padded to a 4-byte (32-bit)
|
||||
// boundary. The egg wants width/4 hex chars per row (no padding),
|
||||
// so read the first width bits and repack.
|
||||
const int strideBytes = ((width + 31) / 32) * 4;
|
||||
const unsigned char *base = (const unsigned char *)bits;
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
const unsigned char *row = base + (size_t)y * strideBytes;
|
||||
fprintf(out, "bitmap=");
|
||||
for (int nib = 0; nib < width / 4; ++nib)
|
||||
{
|
||||
int value = 0;
|
||||
for (int b = 0; b < 4; ++b)
|
||||
{
|
||||
int px = nib * 4 + b;
|
||||
int byte = px >> 3;
|
||||
int bit = 7 - (px & 7); // MSB-first
|
||||
if (row[byte] & (1 << bit))
|
||||
value |= (1 << (3 - b));
|
||||
}
|
||||
fputc("0123456789ABCDEF"[value], out);
|
||||
}
|
||||
fputc('\n', out);
|
||||
}
|
||||
|
||||
SelectObject(dc, oldFont);
|
||||
DeleteObject(font);
|
||||
SelectObject(dc, oldBmp);
|
||||
DeleteObject(dib);
|
||||
DeleteDC(dc);
|
||||
}
|
||||
|
||||
void WriteOrdinalBlock(FILE *out, int index /*1..4*/)
|
||||
{
|
||||
fprintf(out, "[Ordinal::BitMap::%d]\n", index);
|
||||
for (int r = 0; r < 32; ++r)
|
||||
fprintf(out, "bitmap=%s\n", kOrdinal[index - 1][r]);
|
||||
fprintf(out, "x=128\ny=32\nwidth=8\n");
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------//
|
||||
// Defaults: a solo race on grass, one local pilot in a Black Hawk.
|
||||
//---------------------------------------------------------------------------//
|
||||
void BTFeMission_Default(BTFeMission *mission)
|
||||
{
|
||||
memset(mission, 0, sizeof(*mission));
|
||||
strcpy(mission->map, "grass");
|
||||
strcpy(mission->scenario, "freeforall");
|
||||
strcpy(mission->timeOfDay, "day");
|
||||
strcpy(mission->weather, "clear");
|
||||
mission->temperature = 27;
|
||||
mission->lengthSeconds = 600;
|
||||
mission->pilotCount = 1;
|
||||
|
||||
BTFePilot *p = &mission->pilots[0];
|
||||
strcpy(p->address, "200.0.0.96"); // solo local (matches DEV.EGG)
|
||||
strcpy(p->name, "Pilot");
|
||||
strcpy(p->vehicle, "bhk1");
|
||||
strcpy(p->color, "White");
|
||||
strcpy(p->badge, "VGL");
|
||||
strcpy(p->patch, "Yellow");
|
||||
strcpy(p->dropzone, "one");
|
||||
p->bitmapindex = 1;
|
||||
p->advancedDamage = 1;
|
||||
p->loadzones = 1;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------//
|
||||
// Write the egg.
|
||||
//---------------------------------------------------------------------------//
|
||||
int BTFeMission_WriteEgg(const BTFeMission *mission, const char *path)
|
||||
{
|
||||
FILE *out = 0;
|
||||
if (fopen_s(&out, path, "wb") != 0 || out == 0)
|
||||
return 0;
|
||||
|
||||
// [mission]
|
||||
fprintf(out, "[mission]\n");
|
||||
fprintf(out, "adventure=BattleTech\n");
|
||||
fprintf(out, "map=%s\n", mission->map);
|
||||
fprintf(out, "scenario=%s\n", mission->scenario);
|
||||
fprintf(out, "time=%s\n", mission->timeOfDay);
|
||||
fprintf(out, "weather=%s\n", mission->weather);
|
||||
fprintf(out, "temperature=%d\n", mission->temperature);
|
||||
fprintf(out, "length=%d\n", mission->lengthSeconds);
|
||||
|
||||
// [ordinals] -- the four rank-place plasma graphics.
|
||||
fprintf(out, "[ordinals]\n");
|
||||
for (int i = 1; i <= 4; ++i)
|
||||
fprintf(out, "bitmap=Ordinal::BitMap::%d\n", i);
|
||||
for (int i = 1; i <= 4; ++i)
|
||||
WriteOrdinalBlock(out, i);
|
||||
|
||||
// [pilots] -- the ordered mesh roster.
|
||||
fprintf(out, "[pilots]\n");
|
||||
for (int i = 0; i < mission->pilotCount; ++i)
|
||||
fprintf(out, "pilot=%s\n", mission->pilots[i].address);
|
||||
|
||||
// Per-pilot loadout sections.
|
||||
for (int i = 0; i < mission->pilotCount; ++i)
|
||||
{
|
||||
const BTFePilot *p = &mission->pilots[i];
|
||||
fprintf(out, "[%s]\n", p->address);
|
||||
fprintf(out, "hostType=0\n");
|
||||
fprintf(out, "advancedDamage=%d\n", p->advancedDamage);
|
||||
fprintf(out, "loadzones=%d\n", p->loadzones);
|
||||
fprintf(out, "name=%s\n", p->name);
|
||||
fprintf(out, "bitmapindex=%d\n", p->bitmapindex);
|
||||
fprintf(out, "experience=expert\n");
|
||||
fprintf(out, "badge=%s\n", p->badge);
|
||||
fprintf(out, "patch=%s\n", p->patch);
|
||||
fprintf(out, "role=Role::Default\n");
|
||||
fprintf(out, "dropzone=%s\n", p->dropzone);
|
||||
fprintf(out, "vehicle=%s\n", p->vehicle);
|
||||
fprintf(out, "vehicleValue=1000\n");
|
||||
fprintf(out, "color=%s\n", p->color);
|
||||
}
|
||||
|
||||
// [largebitmap] / [smallbitmap] -- the plasma name readouts, one per
|
||||
// pilot, GDI-rendered from the pilot name.
|
||||
fprintf(out, "[largebitmap]\n");
|
||||
for (int i = 0; i < mission->pilotCount; ++i)
|
||||
fprintf(out, "bitmap=BitMap::Large::%s\n", mission->pilots[i].name);
|
||||
for (int i = 0; i < mission->pilotCount; ++i)
|
||||
{
|
||||
fprintf(out, "[BitMap::Large::%s]\n", mission->pilots[i].name);
|
||||
RenderNameBitmap(mission->pilots[i].name, 128, 32, out);
|
||||
fprintf(out, "x=128\ny=32\nwidth=8\n");
|
||||
}
|
||||
|
||||
fprintf(out, "[smallbitmap]\n");
|
||||
for (int i = 0; i < mission->pilotCount; ++i)
|
||||
fprintf(out, "bitmap=BitMap::Small::%s\n", mission->pilots[i].name);
|
||||
for (int i = 0; i < mission->pilotCount; ++i)
|
||||
{
|
||||
fprintf(out, "[BitMap::Small::%s]\n", mission->pilots[i].name);
|
||||
RenderNameBitmap(mission->pilots[i].name, 64, 16, out);
|
||||
fprintf(out, "x=64\ny=16\nwidth=4\n");
|
||||
}
|
||||
|
||||
// Role models (referenced by each pilot's role= field).
|
||||
fprintf(out, "[Role::Default]\n");
|
||||
fprintf(out, "model=dfltrole\n");
|
||||
fprintf(out, "[Role::NoReturn]\n");
|
||||
fprintf(out, "model=noretun\n");
|
||||
|
||||
fclose(out);
|
||||
return 1;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------//
|
||||
// Front-end mode entry. Builds the default solo egg into frontend.egg and
|
||||
// points the engine at it via the standard -egg path. The interactive menu
|
||||
// (catalog selection on-screen) and the marshal are layered on top of this.
|
||||
//---------------------------------------------------------------------------//
|
||||
int BTFrontEnd_Run()
|
||||
{
|
||||
BTFeMission mission;
|
||||
BTFeMission_Default(&mission);
|
||||
|
||||
const char *egg_path = "frontend.egg";
|
||||
if (!BTFeMission_WriteEgg(&mission, egg_path))
|
||||
return 0;
|
||||
|
||||
L4Application::SetEggNotationFileName(egg_path);
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
//===========================================================================//
|
||||
// btl4fe.hpp -- BT412 in-game front end: local mission-egg builder.
|
||||
//
|
||||
// Replaces the external operator console for solo / host play: a race is
|
||||
// configured from an in-process catalog and the mission egg is built LOCALLY
|
||||
// (the exact NotationFile the console used to stream), then fed through the
|
||||
// standard -egg load path (L4Application::SetEggNotationFileName).
|
||||
//
|
||||
// This is the Workstream B foundation (docs/BT412-ROADMAP.md Phase 5); the
|
||||
// interactive on-screen menu, the LocalConsole marshal, and the results
|
||||
// screen build on top of this egg builder.
|
||||
//===========================================================================//
|
||||
#ifndef BTL4FE_HPP
|
||||
#define BTL4FE_HPP
|
||||
|
||||
//---------------------------------------------------------------------------//
|
||||
// Catalogs (the console's RPConfig.xml equivalent for BattleTech).
|
||||
//---------------------------------------------------------------------------//
|
||||
extern const char *const kBTMaps[]; // NULL-terminated
|
||||
extern const char *const kBTMechs[]; // NULL-terminated (base + variants)
|
||||
extern const char *const kBTColors[]; // NULL-terminated
|
||||
extern int BTCatalogCount(const char *const *list);
|
||||
|
||||
//---------------------------------------------------------------------------//
|
||||
// One participant's loadout.
|
||||
//---------------------------------------------------------------------------//
|
||||
struct BTFePilot
|
||||
{
|
||||
char address[32]; // "ip[:port]" -- the mesh roster key
|
||||
char name[24]; // pilot name (also the plasma name bitmap)
|
||||
char vehicle[16]; // mech, e.g. "bhk1"
|
||||
char color[16]; // "White" / "Red" / ...
|
||||
char badge[8]; // e.g. "VGL"
|
||||
char patch[16]; // e.g. "Yellow"
|
||||
char dropzone[8]; // "one" ...
|
||||
int bitmapindex; // 1..N (which ordinal/name slot)
|
||||
int advancedDamage; // 0/1
|
||||
int loadzones; // 0/1
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------//
|
||||
// A whole mission before it becomes an egg.
|
||||
//---------------------------------------------------------------------------//
|
||||
struct BTFeMission
|
||||
{
|
||||
char map[16]; // e.g. "cavern"
|
||||
char scenario[16]; // "freeforall"
|
||||
char timeOfDay[8]; // "day" / "night"
|
||||
char weather[16]; // "clear" ...
|
||||
int temperature; // e.g. 27
|
||||
int lengthSeconds; // mission time limit
|
||||
int pilotCount;
|
||||
BTFePilot pilots[8];
|
||||
};
|
||||
|
||||
// Fill a mission with sensible solo defaults (map=grass, one local pilot).
|
||||
void BTFeMission_Default(BTFeMission *mission);
|
||||
|
||||
// Write the mission as an egg NotationFile to `path`. Returns True on success.
|
||||
// Emits [mission] / [ordinals]+4 rank bitmaps / [pilots] / per-pilot sections /
|
||||
// [largebitmap]+[smallbitmap] (GDI-rendered pilot names) / role models -- the
|
||||
// exact structure the console streamed (verified against content/MP.EGG).
|
||||
int BTFeMission_WriteEgg(const BTFeMission *mission, const char *path);
|
||||
|
||||
//---------------------------------------------------------------------------//
|
||||
// Front-end mode: no -egg, no -net on the command line. Build a solo egg
|
||||
// from the default loadout and point the engine at it. Returns True if it
|
||||
// took over (an egg was built + set), False to fall through to the old path.
|
||||
//---------------------------------------------------------------------------//
|
||||
int BTFrontEnd_Run();
|
||||
|
||||
#endif // BTL4FE_HPP
|
||||
Reference in New Issue
Block a user