- 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>
187 lines
12 KiB
Markdown
187 lines
12 KiB
Markdown
# 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)
|
||
```
|