Files
firestorm/ADDING-A-MECH.md
CydandClaude Fable 5 e84d6e603c 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>
2026-07-01 21:15:52 -05:00

17 KiB
Raw Permalink Blame History

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/ 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/):

  • Geometry .erf files, one per body part, plus _DAM.erf damaged variants (see 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/ 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).
  • 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 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 hardpointsOmniSlots=/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)
<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 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 — 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 (#included 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 — 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.

File Table Content
Mech.cpp:339 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
mechspecs.cpp:4 g_aMechSpecs[NoMechID] { DNL_<NAME>, topSpeed, tonnage, IDS_ML_CH_<class>, DNL_<tech>, IDS_ML_<NAME>_INFO }
huddamage.cpp:55 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 texuv2[65][11][4], offset2[65][11][2] Same paper-doll zones for the target MFD bmp (bump the 65s)
render.cpp:546 mechnames[] Base filename list for the 2D art atlas (hsh\mfd\<name>.bmp)
MWApplication.cpp:18447 s_paMechNames[] Name list used by the CTCL/dedicated path
GameLobby.cpp:26 + 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 + resource.h (scriptstrings project) DNL_<NAME> "<Display Name>", IDS_ML_<NAME>_INFO "<mechlab blurb>" ScriptStrings.dll; IDs mirrored in Content/ShellScripts/ScriptStrings.h for the scripts
MissionLangScript.rc + resource.h (MissionLang project) IDS_<NAME> MissionLang.dll; ID number mirrored in 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<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 — same key → ...\<Name>.instance. (The .mpt MechPak variants of both tables are commented out in core.build — the .tbls are live; keep the .mpts in sync only if reviving MekPak support.)
  3. 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 .tbls.)
  4. textures.build (packs resource\textures.mw4) — one File=Mechs\<Name>\<part>.erf line for every erf, including _DAM and the cage.
  5. props.build (packs resource\props.mw4) — File=Mechs\<Name>\<Name>.animscript.
  6. 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/ (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.
  3. Redeploy the gamebuild-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)