diff --git a/.gitignore b/.gitignore index b32fe5a..a9ab479 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index a563709..478527e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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" diff --git a/context/steamification.md b/context/steamification.md index 5db41a4..ed9332d 100644 --- a/context/steamification.md +++ b/context/steamification.md @@ -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] diff --git a/engine/MUNGA_L4/L4APP.H b/engine/MUNGA_L4/L4APP.H index 6791b4d..0b4ef12 100644 --- a/engine/MUNGA_L4/L4APP.H +++ b/engine/MUNGA_L4/L4APP.H @@ -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; } diff --git a/game/btl4main.cpp b/game/btl4main.cpp index dd923cf..cb747df 100644 --- a/game/btl4main.cpp +++ b/game/btl4main.cpp @@ -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). diff --git a/game/reconstructed/btl4fe.cpp b/game/reconstructed/btl4fe.cpp new file mode 100644 index 0000000..2ea0c6c --- /dev/null +++ b/game/reconstructed/btl4fe.cpp @@ -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 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 +#include +#include + +#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; +} diff --git a/game/reconstructed/btl4fe.hpp b/game/reconstructed/btl4fe.hpp new file mode 100644 index 0000000..344aacf --- /dev/null +++ b/game/reconstructed/btl4fe.hpp @@ -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