From e84d6e603c115c333f157807ce486ee0a73862ad Mon Sep 17 00:00:00 2001 From: Cyd Date: Wed, 1 Jul 2026 21:15:52 -0500 Subject: [PATCH] Document mech + map/mission workflows; README folder map + clone warning - ADDING-A-MECH.md: full workflow for adding a 'Mech chassis (MSL ADD MECH touch points, positional Mech IDs, scriptaddmech.xls generator, hardpoints in .damage, rebuild/repack/deploy steps) - ADDING-A-MAP.md: full workflow for maps (MapCreator terrain) vs missions (MW4Ed2-authored), .nfo MP registration, user vs stock routes, pitfalls - CLAUDE.md: reference pointers to both new docs - README.md: annotated folder map; warning to read RECOVERY.md before cloning Co-Authored-By: Claude Fable 5 --- ADDING-A-MAP.md | 186 ++++++++++++++++++++++++++++++++++ ADDING-A-MECH.md | 259 +++++++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 20 ++++ README.md | 54 ++++++++++ 4 files changed, 519 insertions(+) create mode 100644 ADDING-A-MAP.md create mode 100644 ADDING-A-MECH.md diff --git a/ADDING-A-MAP.md b/ADDING-A-MAP.md new file mode 100644 index 00000000..769ddab7 --- /dev/null +++ b/ADDING-A-MAP.md @@ -0,0 +1,186 @@ +# Adding a new map / mission — full workflow + +How to add a new playable map (terrain) and mission (scenario) to BattleTech: FireStorm +(MW4/Gameleap 5.03 engine). Reconstructed 2026-07-01 from the repo. Companion doc: +[ADDING-A-MECH.md](ADDING-A-MECH.md). + +## The big picture — maps are NOT like mechs + +Adding a 'Mech touches compiled code and a dozen parallel index arrays. Adding a map/mission +touches **none of that**: there are no `MSL ADD MAP` markers, no Mech-ID-style positional +tables, and **no exe rebuild**. Maps and missions are discovered at runtime by **file +enumeration** and registered by **per-mission files**, so the whole job is content + packaging. + +Two separate things, often confused: + +| Thing | Lives in | Packs to | What it is | +|---|---|---|---| +| **Map** | `Content\Maps\\` | `resource\maps\.mw4` | Terrain only: tile geometry, materials, HUD overview image | +| **Mission** | `Content\Missions\\` | `resource\missions\.mw4` | A playable scenario **on** a map: placed objects, drop zones, AI nav, scripts, game types | + +Several missions can share one map (e.g. `Maps\Minerl03` hosts both `Cantina` and +`CantinaSiege`; `Maps\Reduex` hosts `Reduex` + `ReduexSiege`). + +**The original mod-toolkit documentation survives** in +[_UNUSED/Gameleap/EditorDocs/](_UNUSED/Gameleap/EditorDocs/): *Map Creation Tutorial.doc*, +*Simple Map Creation Tutorial.doc*, *Terrain Generation.doc*, *Map Creator.rtf*, +*Material_Map.rtf*, *NFO File Editor.rtf*, *Editor_Tutorial.rtf*, plus the ABL tutorials +(*ABL_Mission_Tutorial.rtf*, *ABL_Brain_Tutorial.rtf*, *ABL_Orders.rtf*) and sample +heightfield/colormap/detailmap TGAs. Read those for tool operation; this doc covers how the +pieces wire into *this* repo. + +**The tools ship in the dev tree root** (`Gameleap\mw4\`): `MapCreator.exe`/`MapCreatora.exe` +(terrain generation), `Tctd.exe` (terrain color/texture data), `NFOEditor.exe` (MP `.nfo` +registration), `ERF Deluxe.exe` (erf inspection), and the mission editor `MW4Ed2.exe` +(launch via `Gameleap\mw4\run-editor.bat` — fully working per CLAUDE.md STEP 8/9). + +--- + +## Part 1 — The MAP (terrain): `Content\Maps\\` + +Source art is a set of TGAs (heightfield, colormap, detail map, material map — see the +*Terrain Generation* / *Material_Map* docs and their sample TGAs). **MapCreator** turns them +into the terrain tile set; **Tctd** generates `tctd.ini`. + +Using [Maps/StormCanyon/](Gameleap/mw4/Content/Maps/StormCanyon/) as the template: + +| File | Purpose | +|---|---| +| `.build` | Pack manifest: `[resource\maps\.mw4]` + `instance=` + `TexturePool=textures\maps\\textures.hint` | +| `.instance` | One page: `model=.data`, `executionstate=AlwaysExecuteState` | +| `.data` | `[gamedata]` `class=mechwarrior4::mwmap`, `grid=.erf`, `hudmap=hud\` | +| `AA..CC .erf/.bsp/.Material` | The terrain tile grid (3×3 for StormCanyon), geometry + BSP + per-tile material properties — **generated by MapCreator** | +| `.erf` | The terrain grid referenced by `grid=` | +| `tctd.ini` | Terrain color/texture data — **generated by Tctd.exe** | + +Plus two texture registrations: +1. **Map textures**: `Content\textures\Maps\\*.tga` with their **own** + `textures.hint` in that folder (referenced by the map `.build`'s `TexturePool=`). +2. **HUD overview image**: the `.data`'s `hudmap=hud\` needs an `[hud\]` entry in + the master [Content/textures/textures.hint](Gameleap/mw4/Content/textures/textures.hint) + with the TGA in `Content\textures\HUD\` (packs into `textures.mw4`). + +--- + +## Part 2 — The MISSION: `Content\Missions\\` + +**Work in the editor.** `MW4Ed2` creates a new mission by cloning +`Content\Missions\EditorTemplate\` (scripts, sounds, lights — +[GameInterface.cpp:1352](Gameleap/code/mw4/Code/MW4GameEd2/GameInterface.cpp#L1352)) onto a +map you pick, and on save writes all the files below back into `Content\Missions\\`. +File roles, using [Missions/StormCanyon/](Gameleap/mw4/Content/Missions/StormCanyon/) as the +template ("(editor)" = generated/maintained by MW4Ed2; hand-edit only the rest): + +| File | Purpose | +|---|---| +| `.build` | Pack manifest: `[resource\missions\.mw4]` + `texturepool=.hint`, `Instance=`, `directory=...\scripts`, `Soundpool=.sounds`. **The `[resource\...]` page name is the published .mw4 path** — stock MW4 missions abbreviate (`CentralPark`→`cpark.mw4`); FireStorm ones keep the full name | +| `.instance` | The hub: `model=.data`, `map=maps\\.instance`, `props=.contents`, `sky=`/`nightsky=` (an erf from `Content\Skies`), `missionbounds`/`warningbounds`, `nametable=`, `railgraph=`, `script=` | +| `.data` | Environment: fog banks, weather effects, clip planes, `allowrespawn`, `heatsinkefficiency`, `isnightmission`, `lights=`/`nightlights=` (editor) | +| `.contents` | Every placed object. Includes the **drop zones**: `[dropteam1..4]` + `[playerstart]` sections (`Model=Misc\DropZone\DropZone.data`, `DropPoints=DropZones\DropTeamN.contents`) — per-team spawn-point sets; this is the "≥N drop points per map" constraint from the MP-cap plan (editor) | +| `.ini` | Editor metadata per object (type=dropzone/helper, invulnerable, snaptoground…) (editor) | +| `.table` | Name↔ID replication table (editor) | +| `Graph.data`, `MoveGrid.data`, `RectGrid.data`, `AirMoveGrid.data` | AI rail graph + ground/air pathing grids (editor — [SaveRailGraph](Gameleap/code/mw4/Code/MW4GameEd2/GameInterface.cpp#L2130)) | +| `.lights`, `night.lights`, `_lightmap.raw` | Day/night lighting + baked lightmap (editor) | +| `Mission.bounds`, `Warning.bounds` | Playable-area polygons (editor) | +| `.hint`, `.sounds` | Mission-local texture pool + sound pool | +| `Scripts\*.abl` | Mission logic in ABL: a base `.abl`, `player.abl`, and **one script per supported game type** (`_Destruction.ABL`, `_TeamDestruction`, `_Attrition`, `_CTF`, …) referenced from the `.nfo` (see Part 3). ABL docs in EditorDocs | + +--- + +## Part 3 — Registration (how the game finds it) + +There are **two routes**, and FireStorm's own maps (Freezer, StormCanyon, Nazca…) use Route B: + +### Route A — user mission (zero manifest edits) +The editor can save/publish straight to `Resource\UserMissions\`: the compiled +`.mw4` + `.nfo` + a 112×112 overview `.tga` +([GameInterface.cpp:1443](Gameleap/code/mw4/Code/MW4GameEd2/GameInterface.cpp#L1443)). +User missions are **auto-discovered**: the MP host list enumerates +`Resource\UserMissions\*.nfo` and Instant Action enumerates `Resource\UserMissions\*.mw4` +([MW4Shell.cpp:4331](Gameleap/code/mw4/Code/MW4/MW4Shell.cpp#L4331)). Nothing else to edit. +Good for prototyping; the deployed game (`C:\VWE\firestorm\MW4`) currently ships 0 of these. + +### Route B — stock mission (first-class FireStorm content) +1. **[Content/MechWarrior4.build](Gameleap/mw4/Content/MechWarrior4.build)** — add the + nested pair (mission inside its map, expressing the dependency): + ``` + maps\\.build={ + missions\\.build={ + } + } + ``` + (If the map already exists, add only the mission line inside its block.) The editor has an + `AddToBuildFile()` for this but its call is **commented out** + ([GameInterface.cpp:1453](Gameleap/code/mw4/Code/MW4GameEd2/GameInterface.cpp#L1453)) — + edit by hand. +2. **`Resource\Missions\.nfo`** — the **multiplayer registration**, a loose text file + (never packed; the deploy copies `Resource\` wholesale). The shell enumerates + `Resource\Missions\*.nfo` ([MW4Shell.cpp:12546](Gameleap/code/mw4/Code/MW4/MW4Shell.cpp#L12546)) + and reads: `[info]` (`longname` = lobby display name, `author`), and `[scenarios]` — one + nested `[server]` page per offered game type with `name`, `ruleset=$(CustomDestruction)` + etc. (constants from [Content/GameTypes.h](Gameleap/mw4/Content/GameTypes.h)), + `teamAllowed`/`teamcount`, `scenariotext`, and the matching `scriptname=...\Scripts\_.abl`. + Copy [StormCanyon.nfo](Gameleap/mw4/Resource/Missions/StormCanyon.nfo) as the template + (or use `NFOEditor.exe`; *NFO File Editor.rtf* documents it). +3. **[Tables/missionnames.tbl](Gameleap/mw4/Content/Tables/missionnames.tbl)** — the + resource-name redirect table (packed into `core.mw4`). Stock MW4 rows map long names to + abbreviated .mw4 names; FireStorm rows are self-mappings. Add + `resource\missions\.mw4=resource\missions\.mw4` under `// BTFRSTRM Missions`. +4. **Instant Action (optional)** — the IA mission list = campaign-defined missions + user + missions ([GetMissionNames, MW4Shell.cpp:4297](Gameleap/code/mw4/Code/MW4/MW4Shell.cpp#L4297)). + To surface a stock mission in IA, add a page to + [Campaigns/InstantAction.Campaign](Gameleap/mw4/Content/Campaigns/InstantAction.Campaign) + (`[content\missions\\.instance]` with `Status=Displayed`, display-name string, + rollover text, map thumbnail). Campaign files pack via `props.build` `Campaign=` lines. +5. **Sky** — reuse an existing sky from `Content\Skies` (already packed in `skies.mw4`); + a new sky means a new entry in `Content\skies.build`. Beware the STEP 8 lesson: the + `.instance` `sky=` path must match the erf filename exactly (case/name). + +--- + +## Part 4 — Build, deploy, verify + +1. **No exe rebuild.** Repack resources with `build-env\build-resources.ps1` (wraps + `MW4pro.exe -build`; LAB_ONLY builds only — see + [build-env/RESOURCE-BUILD.md](build-env/RESOURCE-BUILD.md)). Output: + `resource\maps\.mw4` + `resource\missions\.mw4`, plus `textures.mw4`/`core.mw4` + if you touched `textures.hint`/`missionnames.tbl`. +2. **Redeploy** with `build-env\deploy-mw4.ps1` (copies `Resource\` — including the `.nfo` — + and the repacked packages into `C:\VWE\firestorm\MW4`). +3. **Verify**: + - `MW4Ed2.exe -report` — headless load of the mission (same path the editor + uses; catches broken refs without clicking through the UI). + - Open it in the editor: drop zones present for every team, AI grids regenerate cleanly. + - Host a LAN game: mission appears in the host list under each declared game type with the + `.nfo` `longname`; all teams spawn at their `dropteam*` zones; HUD overview map shows. + - If IA-registered: appears in the Instant Action mission dropdown. + +### Known pitfalls (all hit during STEP 7/8 content curation — the editor STOPs hard) +- **The editor loads every mission/map at startup** (`ObjMan->BuildResources()`) and LAB + builds STOP fatally on the first defect — one broken mission breaks editor startup for + everything. +- **Name consistency is everything**: the folder name, the `[page]` name in + `.build`/`.instance`, and the file basenames must agree (the `_backup`-dir and + `PhoenixPalaceSTB` incidents). `Map=` in the mission `.instance` must point at a map that + actually exists (the `arctic01`/`lunar02` dangling refs). +- The mission `.instance` `Model=` must reference the `[gamedata]`-class page (`.data`), + not some other page (`hill.contents` incident). +- `.mw4` package names in `.build` page headers are the *published* identity — changing one + orphans the old `.dep`/`.mw4` in `Resource\`. + +## Quick-reference: every touch point (Route B stock mission on a new map) + +``` +TERRAIN Content\Maps\\ (.build/.instance/.data + MapCreator tiles + tctd.ini) + Content\textures\Maps\\ (map textures + own textures.hint) + Content\textures\textures.hint ([hud\] entry) + Content\textures\HUD\.tga +MISSION Content\Missions\\ (editor-authored: ~18 files, see Part 2) + Content\Missions\\Scripts\*.abl (one per game type + player.abl) +REGISTER Content\MechWarrior4.build (nest mission .build under map .build — manual) + Resource\Missions\.nfo (MP game types; loose file, not packed) + Content\Tables\missionnames.tbl (self-mapping row; packs into core.mw4) + Content\Campaigns\InstantAction.Campaign (optional, for IA list) +BUILD build-env\build-resources.ps1 → deploy-mw4.ps1 +TOOLS MapCreator.exe / Tctd.exe / NFOEditor.exe / MW4Ed2.exe (run-editor.bat) +DOCS _UNUSED\Gameleap\EditorDocs\ (original tutorials: map creation, terrain, NFO, ABL) +``` diff --git a/ADDING-A-MECH.md b/ADDING-A-MECH.md new file mode 100644 index 00000000..a6c236e8 --- /dev/null +++ b/ADDING-A-MECH.md @@ -0,0 +1,259 @@ +# Adding a new 'Mech chassis — full workflow + +How to add a new playable 'Mech chassis to BattleTech: FireStorm (MW4/Gameleap 5.03 engine). +Reconstructed 2026-07-01 from the repo itself; the previous maintainer ("MSL", MekTek-era) +marked **every registration point** with the comment **`// MSL ADD MECH`** — when in doubt: + +``` +grep -ri "MSL ADD MECH" Gameleap\ +``` + +(~41 files; the authoritative checklist is the union of those sites, which this doc walks through.) + +## The big picture + +A chassis is spread across **three layers**, and all three must agree on one number — the +**Mech ID** (`M_`), an index into parallel arrays everywhere: + +| Layer | What lives there | What it takes to apply changes | +|---|---|---| +| Engine code (`Gameleap\code`) | ID defines, per-mech stat/UI tables, string resources | Rebuild `MW4.exe` (+ `MW4pro.exe`, `ScriptStrings.dll`, `MissionLang.dll`) | +| Content source (`Gameleap\mw4\Content`) | The mech's asset folder, tables, build manifests, shell scripts | Repack resources (`-build` via a LAB_ONLY exe) + redeploy | +| Loose runtime assets (`Gameleap\mw4\hsh`) | 2D bitmaps (selection image, target-MFD, HUD) | Copy files (loaded from disk, not packed) | + +Two spreadsheets in [BTFrstrm/](BTFrstrm/) support the process: +- **`MechInfo_5.04.xls`** — the design source of truth (stats for every chassis). +- **`scriptaddmech.xls`** (Frank Galatis) — a **generator workbook** with one sheet per target file + (`MechLabHeaders`, `Conlobby.script`, `Netlobby.script`, `chassis.script`, `infobox.script`, + `MechBay_Main.script`, `weapons.script`). It emits the tab-delimited per-mech rows you paste + into those files, keeping all the index-aligned arrays consistent. Use it — hand-editing 10+ + parallel arrays is how IDs drift. + +### ⚠️ Mech IDs are positional — decide placement first + +Mech IDs are assigned **alphabetically** today (`M_Annihilator`=0 … `M_Zeus`=64, `NoMechID`=65), +and every table below — the `.tbl` files, `mechspecs.cpp`, `huddamage.cpp`, `coord.cpp`, +shell-script arrays — is a **parallel array indexed by that ID**. Two options when adding: + +1. **Insert alphabetically and renumber everything after it** (what MSL did between releases — + compare `MechLabHeaders_BK.h` where `M_Argus`=0 vs. current =4). Cleanest UI ordering; the + generator xls exists precisely to make the renumber safe. Cost: saved player variants and + old netgame compatibility break (acceptable between FireStorm releases). +2. **Append at the end** (new mech = old `NoMechID`). Preserves existing IDs, but the chassis + list is no longer alphabetical and every list/table must still grow by one. + +Either way, **all clients in multiplayer must run the same tables** — the mech index travels +over the wire (`m_nMechIndex`). + +--- + +## Step 0 — Produce the art (external pipeline) + +This is the long pole. Everything else is bookkeeping. + +**3D assets** (per body part, exported from 3ds Max via the plugins in +[Gameleap/mw4/Binaries/3DSPlug-ins/](Gameleap/mw4/Binaries/3DSPlug-ins/)): +- Geometry `.erf` files, one per body part, **plus `_DAM.erf` damaged variants** (see + [Gameleap/mw4/Content/Mechs/Argus/](Gameleap/mw4/Content/Mechs/Argus/) — note its parts are + named `m3_*`: part meshes can be shared/reused across chassis). +- A cockpit **cage** erf (`_cage.erf`). +- Collision: `_Skeleton.obb` (hierarchical) + `_Skeleton_SOLID.obb`. +- Skeleton/armature with the standard joint naming (`joint_luarm`, `joint_torsobelow`, …) and + **weapon-mount sites** (`site_lmissileport`, `site_rugunport`, `site_light`, `site_eyepoint`, …). +- Animations: `animation\*.mw4anim` (walk/run/fall/getup set) + a `.animscript` state script. +- Textures registered in the texture pool (see Step 4). + +**2D assets** (this is what the [Finished HUDS from J&J/](Finished%20HUDS%20from%20J&J/) folder +holds for ~13 pending chassis — MFD + Radar art): +- `hsh\Mechs\.bmp` — mech-selection portrait (mechlab/lobby). +- `hsh\MFD\.bmp` — target-damage MFD image (engine tiles these into a 1024×1024 atlas at + 128×128 per mech — [render.cpp:1375](Gameleap/code/CoreTech/Libraries/GameOS/render.cpp#L1375)). +- `hsh\hud\.bmp` and radar art under `hsh\radar\`. +- `Content\textures\HUD\.tga` — the in-cockpit damage-readout texture (paper-doll). +- (Optional) footstep decal textures (`footsteps\_default`, `_dirt`, … in the texture pool). + +**Practical route:** clone the folder of an existing chassis with the same body plan, get it +in-game end-to-end, then swap in real art piece by piece. + +--- + +## Step 1 — Create the content folder + +`Gameleap\mw4\Content\Mechs\\` plus a `_destroyed\` sibling (the wreck model has its +own smaller `.data`/geometry). Files, using +[Argus](Gameleap/mw4/Content/Mechs/Argus/) as the template (all text, INI-style, `!Include`-able): + +| File | Purpose | +|---|---| +| `.data` | The chassis definition: `Class=MechWarrior4::Mech`, `MechID=$(M_)`, `TechType`, `NameIndex=$(IDS_)`, tonnage/trade value, `CanLoadECM/AMS/JumpJets/...` flags, death entity, OBB refs, lighting, effects | +| `.instance` | Runtime instance: pulls together Model/Armature/Subsystems/DamageObjects, power/armor/speed/heat ratings shown in UI | +| `.armature` | Joint hierarchy: rotations/translations/child links for every `joint_*`/`site_*` | +| `.contents` | Maps each joint to its part model in `armaturedata\*.data` (which reference the `.erf` meshes) | +| `.subsystems` | **Default loadout**: `[Engine]`, `[Armor]` (zone multipliers), `[Sensor]`, `[Torso]`, heat sinks, and the stock weapons — each weapon section names its `WeaponSubsystems\...` model, mount `Site=`, `InternalLocation=`, ammo | +| `.damage` | Damage zones per body part: armor values, splash scaling, **and the mechlab hardpoints** — `OmniSlots=`/`BeamSlots=`/`MissileSlots=`/`ProjectileSlots=` per zone define what the mechlab lets you fit | +| `.engine` | Engine subsystem: heat sinks, upgrade steps (`MPSPerUpgrade`, `TonsPerUpgrade`) | +| `.torso` | Torso twist/pitch joints, speeds, radii (uses `$()` macros from [MechTorso.defines](Gameleap/mw4/Content/Defines/MechTorso.defines)) | +| `.animscript` | Animation state machine | +| `armaturedata\`, `animation\`, `armaturevideo\` | Per-part `.data`, `.mw4anim` clips, support data | +| `*.erf`, `*_DAM.erf`, `*.obb` | Geometry + collision (from Step 0) | + +`$(M_)` and `$(IDS_)` resolve because +[ProjectSpecific.include](Gameleap/mw4/Content/ProjectSpecific.include) pulls in +`Defines\MissionLang.defines` and `ShellScripts\MechLabHeaders.h` for all content parsing. + +--- + +## Step 2 — Assign the Mech ID (three places, must match EXACTLY) + +The header itself warns: *"IF YOU CHANGE THESE, YOU MUST ALSO CHANGE THE CONSTANTS IN +MWCONST.ABI!!! YOU MUST ALSO CHANGE THE CHASSIS LOOKUP TABLE IN MECH.HPP/MECH.CPP"*. + +1. **[MechLabHeaders.h](Gameleap/code/mw4/Code/MW4/MechLabHeaders.h#L112)** — add + `#define M_ `, bump `LastMechID` and `NoMechID` (and note `M_CameraShip`/ + `EmptyMechID` float above `LastMechID` automatically). This file exists as **byte-identical + copies** that must stay in sync: + - `Gameleap\code\mw4\Code\MW4\MechLabHeaders.h` (compiled into the game) + - [Gameleap/mw4/Content/ShellScripts/MechLabHeaders.h](Gameleap/mw4/Content/ShellScripts/MechLabHeaders.h) + (`#include`d by shell scripts and by content `.data` parsing) + - `Gameleap\mw4\Content\ShellScriptsDev\MechLabHeaders.h` (dev-scripts mirror) + - `Gameleap\code\mw4\Code\mw4print\MechLabHeaders.h` (the mw4print tool keeps its own copy) +2. **[mwconst.abi](Gameleap/mw4/Content/ABLScripts/mwconst.abi#L470)** — the same `M_*` constants + for the ABL scripting language (AI/bots use `GetMechType()` against these). +3. **Engine lookup tables** — Step 3 below. + +--- + +## Step 3 — Engine code tables (rebuild required) + +All in `Gameleap\code`, each marked `// MSL ADD MECH`; every array is sized by +`NoMechID`/`LastMechID+1`, so the compiler catches a missed row once the define is bumped. +There is also a runtime guard: `gosASSERT(NoMechID == (LastMechID - FirstMechID + 1))` at +[MW4Shell.cpp:14213](Gameleap/code/mw4/Code/MW4/MW4Shell.cpp#L14213). + +| File | Table | Content | +|---|---|---| +| [Mech.cpp:339](Gameleap/code/mw4/Code/MW4/Mech.cpp#L339) | `Mech::s_mechSlots[NoMechID]` | **Execution slot** — memory/animation slot; new mechs *share* a slot with a similar chassis (e.g. Argus shares with Catapult). Slot enum in [MW4.hpp:228](Gameleap/code/mw4/Code/MW4/MW4.hpp#L228) | +| [mechspecs.cpp:4](Gameleap/code/mw4/Code/MW4/mechspecs.cpp#L4) | `g_aMechSpecs[NoMechID]` | `{ DNL_, topSpeed, tonnage, IDS_ML_CH_, DNL_, IDS_ML__INFO }` | +| [huddamage.cpp:55](Gameleap/code/mw4/Code/MW4/huddamage.cpp#L55) | `texturename[]`, `texuv[][11][4]`, `offset[][11][2]` | Cockpit damage paper-doll: texture-pool name (`hud\`) + UV rects/offsets for the 11 damage zones of your HUD tga | +| [coord.cpp:2](Gameleap/code/CoreTech/Libraries/GameOS/coord.cpp#L2) | `texuv2[65][11][4]`, `offset2[65][11][2]` | Same paper-doll zones for the **target MFD** bmp (bump the `65`s) | +| [render.cpp:546](Gameleap/code/CoreTech/Libraries/GameOS/render.cpp#L546) | `mechnames[]` | Base filename list for the 2D art atlas (`hsh\mfd\.bmp`) | +| [MWApplication.cpp:18447](Gameleap/code/mw4/Code/MW4/MWApplication.cpp#L18447) | `s_paMechNames[]` | Name list used by the CTCL/dedicated path | +| [GameLobby.cpp:26](Gameleap/code/mw4/Code/mw4dedicatedui/GameLobby.cpp#L26) + `DedicatedUI.hpp` | `mechlist[64][256]` etc. | Dedicated-server UI mech list sizing | + +**String resources** (two DLL projects + their content-side mirrors): + +| Where | Add | Feeds | +|---|---|---| +| [StringResource.rc](Gameleap/code/mw4/Code/scriptstrings/StringResource.rc) + `resource.h` (scriptstrings project) | `DNL_ ""`, `IDS_ML__INFO ""` | `ScriptStrings.dll`; IDs mirrored in [Content/ShellScripts/ScriptStrings.h](Gameleap/mw4/Content/ShellScripts/ScriptStrings.h) for the scripts | +| [MissionLangScript.rc](Gameleap/code/mw4/Code/MissionLang/MissionLangScript.rc) + `resource.h` (MissionLang project) | `IDS_` | `MissionLang.dll`; ID number mirrored in [MissionLang.defines](Gameleap/mw4/Content/Defines/MissionLang.defines) (used by `NameIndex=$(IDS_)` in the `.data`) | + +--- + +## Step 4 — Content registration (tables, build manifests, texture pool) + +All in `Gameleap\mw4\Content`, all marked `// MSL ADD MECH`: + +1. **[Tables/MechChassisTable.tbl](Gameleap/mw4/Content/Tables/MechChassisTable.tbl)** — + `=Mechs\\.data`. **Row order = Mech ID order** (parallel array; + the mechlab reads class/tech per row via `TBL_GetResNames`). +2. **[Tables/MechTable.tbl](Gameleap/mw4/Content/Tables/MechTable.tbl)** — same key → + `...\.instance`. (The `.mpt` MechPak variants of both tables are **commented out** in + `core.build` — the `.tbl`s are live; keep the `.mpt`s in sync only if reviving MekPak support.) +3. **[core.build](Gameleap/mw4/Content/core.build)** (packs `resource\core.mw4`) — the mech block: + ``` + //FireStorm: + data=mechs\\.engine + data=mechs\\.torso + data=mechs\\.data + data=mechs\_destroyed\_destroyed.data + armature=mechs\\.contents + subsystems=mechs\\.subsystems + damage=mechs\\.damage + instance=mechs\\.instance + ``` + (The `MWTable=` lines near the end of core.build already pull in the two `.tbl`s.) +4. **[textures.build](Gameleap/mw4/Content/textures.build)** (packs `resource\textures.mw4`) — + one `File=Mechs\\.erf` line for **every** erf, including `_DAM` and the cage. +5. **[props.build](Gameleap/mw4/Content/props.build)** (packs `resource\props.mw4`) — + `File=Mechs\\.animscript`. +6. **[textures/textures.hint](Gameleap/mw4/Content/textures/textures.hint)** — texture-pool + entries: `[hud\]` for the cockpit damage tga (put the tga in `Content\textures\HUD\`), + plus body textures and optional `[footsteps\_*]` entries. +7. **`hsh\` bitmaps** — drop `hsh\Mechs\.bmp`, `hsh\MFD\.bmp`, `hsh\hud\.bmp` + (loose files, loaded from disk at runtime — no packing step). + +--- + +## Step 5 — Shell scripts (mechlab + lobby UI) + +In [Gameleap/mw4/Content/ShellScripts/](Gameleap/mw4/Content/ShellScripts/) (and mirrored in +`ShellScriptsDev\` if you use the dev shell). These ship as text with the deployment and are +parsed at runtime — no exe rebuild, but a redeploy. **Generate the rows with +`BTFrstrm\scriptaddmech.xls`** — each sheet matches one file: + +| Script | Per-mech data to extend | +|---|---| +| `MechBay\chassis.script` | `mech_num`, `class[]` (weight class per ID), tech arrays | +| `MechBay\gs_chassis.script` | `mech_num`, `create_limit` (arrays fill via `TBL_GetResNames` callback from the .tbl) | +| `MechBay\weapons.script` | `mech_num`, hardpoint label arrays (`special1_label[]`, …) and on-screen locations per zone (`head_location[]`, `leftarm_location[]`, …) | +| `MechBay\armor.script` | `mech_num`, armor-bar screen positions per zone (`head_bar[]`, …) | +| `MechBay\infobox.script` | `LAST_MECH_INFO_ID` + `temp_mech_id[]` info-string ↔ mech-ID mapping | +| `MechBay\MechBay_main.script` | `mech_name[]` (localized `DNL_*` per ID) | +| `ConLobby.script` | `MAX_ALLOWED_MECHS`, allowed-mech roster arrays, `ROOKIEMECH*` picks | +| `NetLobby.script` | `mechlist[]` / `chassis_names[]` (localized names, index-aligned) | + +--- + +## Step 6 — Rebuild and repack + +1. **Rebuild code** (VC6 — see `CLAUDE.md` STEP 3): + ``` + msdev Gameleap\code\mw4\Code\MechWarrior4.dsw /MAKE "MW4Application - Win32 Release" /OUT build.log + msdev Gameleap\code\mw4\Code\MechWarrior4.dsw /MAKE "MW4Application - Win32 Profile" /OUT buildpro.log + ``` + Release gives the shipping `MW4.exe`; **Profile gives `MW4pro.exe`, which you need because + only LAB_ONLY builds can run `-build`**. `ScriptStrings.dll` / `MissionLang.dll` rebuild as + dependencies. +2. **Repack resources** — run `build-env\build-resources.ps1` (wraps `MW4pro.exe -build`; also + handles moving DDrawCompat's `ddraw.dll` aside). The `.dep` files make it incremental; + `core.mw4`, `textures.mw4`, `props.mw4` repack because their `.build` manifests changed. + Full details: [build-env/RESOURCE-BUILD.md](build-env/RESOURCE-BUILD.md). +3. **Redeploy the game** — `build-env\deploy-mw4.ps1` (fresh `C:\VWE\firestorm\MW4` with new + exe + DLLs + repacked `resource\*.mw4` + shellscripts + `hsh\`). +4. The **editor** needs no changes (it reads the same content tree in place), but a repacked + `core.mw4` means re-running it will pick the new chassis up for missions. + +## Step 7 — Verification checklist + +1. Game launches (the `gosASSERT` on ID-count consistency fires immediately if a list is off). +2. Mechlab: chassis appears in the list with correct name/class/tech/tonnage; info text shows; + hardpoints match the `.damage` slot counts; default loadout matches `.subsystems`; armor + screen bars sit on the artwork. +3. Instant action: mech spawns, animates (walk/run/fall/getup), weapons fire from the right + sites, cockpit damage paper-doll regions light up correctly (the `texuv`/`offset` UVs), + targeting another instance shows the MFD image. +4. Destroyed: kill it — wreck (`_destroyed`) appears. +5. Multiplayer smoke test: name shows in lobby/roster (`NetLobby` arrays), both clients agree. +6. Editor: place the mech in a mission, `-report` runs clean. + +## Quick-reference: every touch point + +``` +ART Content\Mechs\\ + _destroyed\ (erf/obb/armature/anim + 9 text configs) + hsh\Mechs|MFD|hud\.bmp Content\textures\HUD\.tga +IDS code\mw4\Code\MW4\MechLabHeaders.h Content\ShellScripts\MechLabHeaders.h + Content\ShellScriptsDev\MechLabHeaders.h code\mw4\Code\mw4print\MechLabHeaders.h + Content\ABLScripts\mwconst.abi +CODE MW4\Mech.cpp (exec slots) MW4\mechspecs.cpp (stats) + MW4\huddamage.cpp (paper-doll UVs) GameOS\coord.cpp (MFD UVs) + GameOS\render.cpp (2D art list) MW4\MWApplication.cpp (name list) + mw4dedicatedui\GameLobby.cpp|DedicatedUI.hpp +STRINGS scriptstrings\StringResource.rc|resource.h Content\ShellScripts\ScriptStrings.h + MissionLang\MissionLangScript.rc|resource.h Content\Defines\MissionLang.defines +TABLES Content\Tables\MechChassisTable.tbl Content\Tables\MechTable.tbl +MANIFESTS Content\core.build Content\textures.build + Content\props.build Content\textures\textures.hint +SCRIPTS ShellScripts\MechBay\{chassis,gs_chassis,weapons,armor,infobox,MechBay_main}.script + ShellScripts\{ConLobby,NetLobby}.script (+ ShellScriptsDev mirrors) +TOOLS BTFrstrm\scriptaddmech.xls (row generator) BTFrstrm\MechInfo_5.04.xls (design stats) +``` diff --git a/CLAUDE.md b/CLAUDE.md index a6279c01..4b3da304 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -509,6 +509,26 @@ with the full UI + DDrawCompat viewport (Game View/Overview/Object Manager/Resou 58 packages, fullscreen native DDraw). Old launch path `C:\VWE\firestorm\MW4Editor\...` is superseded by `C:\VWE\firestorm\Gameleap\mw4\run-editor.bat`. +## 📋 Reference: adding a new 'Mech chassis (documented 2026-07-01) +Full workflow in **`ADDING-A-MECH.md`** (repo root). Key facts: every registration point is +marked `// MSL ADD MECH` (~41 files); Mech IDs are positional/alphabetical parallel-array +indices spanning code + tables + shell scripts; `BTFrstrm\scriptaddmech.xls` is a generator +workbook for the per-mech rows; mechlab hardpoints live in the mech's `.damage` (`OmniSlots=` +etc.), default loadout in `.subsystems`; 2D HUD/MFD bmps load loose from `hsh\`; a new chassis +needs MW4.exe rebuild + resource repack + redeploy. `Finished HUDS from J&J\` = pending MFD/ +radar art for ~13 chassis. + +## 📋 Reference: adding a new map/mission (documented 2026-07-01) +Full workflow in **`ADDING-A-MAP.md`** (repo root). Key facts: unlike mechs, NO code changes / +positional arrays / exe rebuild — maps+missions are discovered by enumeration. Map (terrain) = +`Content\Maps\` (MapCreator.exe tiles) vs Mission (scenario) = `Content\Missions\` +(authored in MW4Ed2, which clones `EditorTemplate`). Registration: nest mission .build under its +map in `MechWarrior4.build` (manual — editor's AddToBuildFile() call is commented out), MP game +types via loose `Resource\Missions\.nfo`, `missionnames.tbl` self-row, optional +`InstantAction.Campaign` page for the IA list; user missions (`Resource\UserMissions\*.mw4+.nfo`, +editor-published) are auto-discovered with zero manifest edits. Original toolkit tutorials +(map creation/terrain/NFO/ABL) live in `_UNUSED\Gameleap\EditorDocs\`. + ## 📋 Reference: raising the MP player cap 16 → 24 (investigated 2026-06-24; NOT planned) Captured for the future; **we are not tackling this in the foreseeable future.** Question was whether `ConLobby.script`'s `MAX_ROSTER_COUNT 16` could be 24. Findings: diff --git a/README.md b/README.md index ca4d3fa7..cd43138a 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,64 @@ # BattleTech: FireStorm — workspace layout +> ⚠️ **Setting up a new machine? Read `RECOVERY.md` BEFORE cloning this repo.** +> The clone only comes out byte-exact if long paths, `core.autocrlf=false`, and Git LFS are +> configured *first* — cloning without them silently corrupts line endings, truncates deep +> VC98/MFC paths, and leaves LFS placeholders instead of the real binaries. + This is the working tree for the FireStorm total-conversion built on the MechWarrior 4 (Gameleap / GameOS) engine. Only **three** trees feed the build/deploy; everything else is output (regenerable), design reference, or archived clutter. See `CLAUDE.md` for the full build/runtime reconstruction notes. +## Folder map + +``` +C:\VWE\firestorm\ +├─ Gameleap\ The MW4/Gameleap 5.03 engine + game (the two live trees) +│ ├─ code\ ⭐ SOURCE CODE — build mw4\Code\MechWarrior4.dsw in VC6 +│ │ ├─ CoreTech\ Reusable engine layer: GameOS, MLR renderer, gosFX, +│ │ │ GOSScript, Stuff, Network, blade + engine tools +│ │ ├─ mw4\Code\ The game itself: MW4 lib (AI, mechlab, HUD, shell), +│ │ │ MW4Application (→MW4.exe), MW4GameEd2 (mission editor), +│ │ │ scriptstrings/MissionLang (string DLLs), dedicated UI +│ │ ├─ mw4\Libraries\ mw4-local libs: Adept, stlport (build first), MLR, +│ │ │ Compost, ImageLib, gosfx, server, stuff … +│ │ ├─ mw4\Binaries\ 3DS Max export plug-ins (mech/prop .erf pipeline) +│ │ └─ rel.bin\ pro.bin\ Build OUTPUT: Release (MW4.exe, MW4pro.exe) / Profile +│ │ arm.bin\ dbg.bin\ (editor MW4Ed2.exe) / Armor / Debug — regenerable +│ └─ mw4\ ⭐ GAME DATA source tree (editor also runs here in place +│ │ via run-editor.bat; tool exes MapCreator/Tctd/NFOEditor…) +│ ├─ Content\ All content sources: Mechs\, Maps\, Missions\, Skies\, +│ │ Weapons\, WeaponSubsystems\, Subsystems\, Buildings\, +│ │ Vehicles\, Effects\, textures\, Campaigns\, Tables\, +│ │ Defines\, ABLScripts\ (mission/AI language), +│ │ ShellScripts\ (menu/mechlab UI), *.build manifests +│ ├─ Resource\ Packed .mw4 packages (output of `MW4pro -build`) + +│ │ Missions\*.nfo (MP registration) + UserMissions\ +│ ├─ hsh\ Loose 2D art loaded at runtime: mech portraits, MFD +│ │ target images, HUD/radar bitmaps, decals, fonts +│ ├─ Assets\ Stats\ Runtime support data (cursors, world stats) +│ └─ Movies\ fonts\ Notes\ Cinematics / font sources / dev notes (not deployed) +├─ build-env\ ⭐ TOOLCHAIN — self-contained VC6 (VisualStudio6\), +│ │ DX 7.0a + DX Media 6 SDKs, stlnative\ headers, +│ │ ddrawcompat\ (editor viewport fix), .reg env files, +│ │ build/deploy scripts (see below), build logs +├─ MW4\ Runnable game deploy (output of deploy-mw4.ps1) — +│ regenerable; copy this to production +├─ BTFrstrm\ FireStorm design data: MechInfo_5.04.xls (stat source +│ of truth), scriptaddmech.xls (add-mech row generator) +├─ Finished HUDS from J&J\ Per-mech HUD source art (MFD + Radar) awaiting +│ integration, ~13 chassis +├─ _UNUSED\ Archived clutter — nothing read by the build (EXCEPT +│ worth knowing: Gameleap\EditorDocs\ = original map/ +│ terrain/NFO/ABL tutorials) +├─ CLAUDE.md Full build/runtime reconstruction notes (the history) +├─ README.md This file — orientation + fresh-machine restore +├─ RECOVERY.md Disaster-recovery notes +├─ ADDING-A-MECH.md Workflow: add a new 'Mech chassis +└─ ADDING-A-MAP.md Workflow: add a new map/mission +``` + ## The folders that matter (build inputs) | Folder | Role | Notes |