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 <noreply@anthropic.com>
This commit is contained in:
+186
@@ -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\<Map>\` | `resource\maps\<map>.mw4` | Terrain only: tile geometry, materials, HUD overview image |
|
||||
| **Mission** | `Content\Missions\<Name>\` | `resource\missions\<name>.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\<Map>\`
|
||||
|
||||
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 |
|
||||
|---|---|
|
||||
| `<Map>.build` | Pack manifest: `[resource\maps\<map>.mw4]` + `instance=` + `TexturePool=textures\maps\<Map>\textures.hint` |
|
||||
| `<Map>.instance` | One page: `model=<Map>.data`, `executionstate=AlwaysExecuteState` |
|
||||
| `<Map>.data` | `[gamedata]` `class=mechwarrior4::mwmap`, `grid=<Map>.erf`, `hudmap=hud\<map>` |
|
||||
| `AA..CC .erf/.bsp/.Material` | The terrain tile grid (3×3 for StormCanyon), geometry + BSP + per-tile material properties — **generated by MapCreator** |
|
||||
| `<map>.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\<Map>\*.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\<map>` needs an `[hud\<map>]` 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\<Name>\`
|
||||
|
||||
**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\<Name>\`.
|
||||
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 |
|
||||
|---|---|
|
||||
| `<Name>.build` | Pack manifest: `[resource\missions\<name>.mw4]` + `texturepool=<name>.hint`, `Instance=`, `directory=...\scripts`, `Soundpool=<name>.sounds`. **The `[resource\...]` page name is the published .mw4 path** — stock MW4 missions abbreviate (`CentralPark`→`cpark.mw4`); FireStorm ones keep the full name |
|
||||
| `<Name>.instance` | The hub: `model=<name>.data`, `map=maps\<map>\<map>.instance`, `props=<name>.contents`, `sky=`/`nightsky=` (an erf from `Content\Skies`), `missionbounds`/`warningbounds`, `nametable=`, `railgraph=`, `script=` |
|
||||
| `<Name>.data` | Environment: fog banks, weather effects, clip planes, `allowrespawn`, `heatsinkefficiency`, `isnightmission`, `lights=`/`nightlights=` (editor) |
|
||||
| `<Name>.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) |
|
||||
| `<Name>.ini` | Editor metadata per object (type=dropzone/helper, invulnerable, snaptoground…) (editor) |
|
||||
| `<Name>.table` | Name↔ID replication table (editor) |
|
||||
| `<Name>Graph.data`, `<Name>MoveGrid.data`, `<Name>RectGrid.data`, `<Name>AirMoveGrid.data` | AI rail graph + ground/air pathing grids (editor — [SaveRailGraph](Gameleap/code/mw4/Code/MW4GameEd2/GameInterface.cpp#L2130)) |
|
||||
| `<Name>.lights`, `<Name>night.lights`, `<Name>_lightmap.raw` | Day/night lighting + baked lightmap (editor) |
|
||||
| `Mission.bounds`, `Warning.bounds` | Playable-area polygons (editor) |
|
||||
| `<Name>.hint`, `<Name>.sounds` | Mission-local texture pool + sound pool |
|
||||
| `Scripts\*.abl` | Mission logic in ABL: a base `<Name>.abl`, `player.abl`, and **one script per supported game type** (`<Name>_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
|
||||
`<name>.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\<map>\<map>.build={
|
||||
missions\<name>\<name>.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\<Name>.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\<Name>_<type>.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\<Name>.mw4=resource\missions\<Name>.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\<name>\<name>.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\<map>.mw4` + `resource\missions\<name>.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 <MissionName> -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 (`<name>.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\<Map>\ (.build/.instance/.data + MapCreator tiles + tctd.ini)
|
||||
Content\textures\Maps\<Map>\ (map textures + own textures.hint)
|
||||
Content\textures\textures.hint ([hud\<map>] entry) + Content\textures\HUD\<map>.tga
|
||||
MISSION Content\Missions\<Name>\ (editor-authored: ~18 files, see Part 2)
|
||||
Content\Missions\<Name>\Scripts\*.abl (one per game type + player.abl)
|
||||
REGISTER Content\MechWarrior4.build (nest mission .build under map .build — manual)
|
||||
Resource\Missions\<Name>.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)
|
||||
```
|
||||
@@ -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_<Name>`), 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 (`<name>_cage.erf`).
|
||||
- Collision: `<Name>_Skeleton.obb` (hierarchical) + `<Name>_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 `<Name>.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\<name>.bmp` — mech-selection portrait (mechlab/lobby).
|
||||
- `hsh\MFD\<name>.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\<name>.bmp` and radar art under `hsh\radar\`.
|
||||
- `Content\textures\HUD\<name>.tga` — the in-cockpit damage-readout texture (paper-doll).
|
||||
- (Optional) footstep decal textures (`footsteps\<name>_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\<Name>\` plus a `<Name>_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 |
|
||||
|---|---|
|
||||
| `<Name>.data` | The chassis definition: `Class=MechWarrior4::Mech`, `MechID=$(M_<Name>)`, `TechType`, `NameIndex=$(IDS_<NAME>)`, tonnage/trade value, `CanLoadECM/AMS/JumpJets/...` flags, death entity, OBB refs, lighting, effects |
|
||||
| `<Name>.instance` | Runtime instance: pulls together Model/Armature/Subsystems/DamageObjects, power/armor/speed/heat ratings shown in UI |
|
||||
| `<Name>.armature` | Joint hierarchy: rotations/translations/child links for every `joint_*`/`site_*` |
|
||||
| `<Name>.contents` | Maps each joint to its part model in `armaturedata\*.data` (which reference the `.erf` meshes) |
|
||||
| `<Name>.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 |
|
||||
| `<Name>.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 |
|
||||
| `<Name>.engine` | Engine subsystem: heat sinks, upgrade steps (`MPSPerUpgrade`, `TonsPerUpgrade`) |
|
||||
| `<Name>.torso` | Torso twist/pitch joints, speeds, radii (uses `$()` macros from [MechTorso.defines](Gameleap/mw4/Content/Defines/MechTorso.defines)) |
|
||||
| `<Name>.animscript` | Animation state machine |
|
||||
| `armaturedata\`, `animation\`, `armaturevideo\` | Per-part `.data`, `.mw4anim` clips, support data |
|
||||
| `*.erf`, `*_DAM.erf`, `*.obb` | Geometry + collision (from Step 0) |
|
||||
|
||||
`$(M_<Name>)` and `$(IDS_<NAME>)` 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_<Name> <id>`, 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_<NAME>, topSpeed, tonnage, IDS_ML_CH_<class>, DNL_<tech>, IDS_ML_<NAME>_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\<name>`) + 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\<name>.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_<NAME> "<Display Name>"`, `IDS_ML_<NAME>_INFO "<mechlab blurb>"` | `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_<NAME>` | `MissionLang.dll`; ID number mirrored in [MissionLang.defines](Gameleap/mw4/Content/Defines/MissionLang.defines) (used by `NameIndex=$(IDS_<NAME>)` 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)** —
|
||||
`<Display Name>=Mechs\<Name>\<Name>.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 →
|
||||
`...\<Name>.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:<Name>
|
||||
data=mechs\<name>\<name>.engine
|
||||
data=mechs\<name>\<name>.torso
|
||||
data=mechs\<name>\<name>.data
|
||||
data=mechs\<name>_destroyed\<name>_destroyed.data
|
||||
armature=mechs\<name>\<name>.contents
|
||||
subsystems=mechs\<name>\<name>.subsystems
|
||||
damage=mechs\<name>\<name>.damage
|
||||
instance=mechs\<name>\<name>.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\<Name>\<part>.erf` line for **every** erf, including `_DAM` and the cage.
|
||||
5. **[props.build](Gameleap/mw4/Content/props.build)** (packs `resource\props.mw4`) —
|
||||
`File=Mechs\<Name>\<Name>.animscript`.
|
||||
6. **[textures/textures.hint](Gameleap/mw4/Content/textures/textures.hint)** — texture-pool
|
||||
entries: `[hud\<name>]` for the cockpit damage tga (put the tga in `Content\textures\HUD\`),
|
||||
plus body textures and optional `[footsteps\<name>_*]` entries.
|
||||
7. **`hsh\` bitmaps** — drop `hsh\Mechs\<name>.bmp`, `hsh\MFD\<name>.bmp`, `hsh\hud\<name>.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\<Name>\ + <Name>_destroyed\ (erf/obb/armature/anim + 9 text configs)
|
||||
hsh\Mechs|MFD|hud\<name>.bmp Content\textures\HUD\<name>.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)
|
||||
```
|
||||
@@ -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\<Map>` (MapCreator.exe tiles) vs Mission (scenario) = `Content\Missions\<Name>`
|
||||
(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\<Name>.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:
|
||||
|
||||
@@ -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 |
|
||||
|
||||
Reference in New Issue
Block a user