Author SHA1 Message Date
dicion 2ba63b0268 Rebuild of RC4 from windows build machine to sync latest updates. 2026-08-15 13:42:41 -05:00
546df652ba Publish V5.1.0b_RC4: repair options.ini lost to file virtualization
First RC4 build, synced back from the Windows VC6 machine. NOT READY FOR
EXTERNAL TESTING -- see "Test status" below.

Problem reported after the RC3 test night: options.ini was ignored when the
game lived in c:\mw4510b3 but worked in c:\mw4, and mw4print misbehaved in
the same folder.

Root cause: Windows UAC file virtualization
-------------------------------------------
Neither the folder name nor any hardcoded path was involved. options.ini is
opened by bare relative name (MW4Shell.cpp:818) and GameOS sets the working
directory to the running exe's own folder (ImageHlp.cpp:773/797, from
_pgmptr), so the file always follows MW4.exe. The decisive observation was
that the game RAN from the new folder: Resource\*.mw4 resolve through the
same relative mechanism, so the working directory was demonstrably correct
and the file was being found.

Windows virtualizes a process when ALL of: 32-bit, no UAC manifest,
non-elevated, and writing somewhere the user cannot. Neither MW4.exe nor
mw4print.exe carried a requestedExecutionLevel (VC6 predates manifests), so
writes to options.ini were redirected to %LOCALAPPDATA%\VirtualStore\<folder>
-- and once that shadow copy exists, reads are served from it too. Nothing
reports an error at any point. That explains every symptom at once, including
the owner's key observation that the game never rewrote the file at all.

c:\mw4 worked because Windows 10's default ACL on C:\ grants CREATOR OWNER
full control over folders you create yourself, while a folder created by an
elevated process is not user-writable.

Changes
-------
* Manifests (asInvoker) added to MW4Application (MW4.exe / MW4pro.exe),
  mw4print, Launcher and autoconfig: a <project>.manifest plus
  `1 24 "<project>.manifest"` in the matching .rc. Deliberately minimal --
  no supportedOS (would change OS version reporting and compatibility
  shimming), no dpiAware (the DWM8And16BitMitigation layer owns that), no
  comctl32 v6 dependency (would restyle the UI and is the classic XP
  side-by-side breakage).

* MW4_WarnIfFolderNotWritable() in MW4Application.cpp, called from
  GetGameOSEnvironment. Probes with a delete-on-close temp file and, on
  failure, names the folder and prints the icacls fix. Placed in
  GetGameOSEnvironment deliberately: it runs BEFORE EnterFullScreenMode
  (WinMain.cpp:545 vs :552), so it cannot reproduce the STEP 7
  modal-dialog-over-DirectDraw hang. Without it the manifest would merely
  convert silent success into silent failure.

* mw4print/dbexport.cpp: "libmysql.dll not found" was reported for three
  distinct failures and GetLastError was never called. libmysql.dll imports
  VCRUNTIME140.dll -- the only one of its 16 imports absent from a stock
  Windows 10 (the 11 api-ms-win-crt-* are the OS UCRT). Windows returns
  ERROR_MOD_NOT_FOUND for a missing dependency, the same code as for a
  missing file, hence "not found" for a file plainly sitting there. Now
  reports four cases separately and names the redistributable. Also fixed a
  pre-existing missing strncpy null-termination on the caller's buffer.

* Ships Gameleap\mw4\vcruntime140.dll (14.44.35211.0, x86, Microsoft-signed,
  extracted from the official VC++ 2015-2022 redistributable) so no runtime
  install is needed on a pod. deploy-mw4.ps1 copies it automatically as a
  root *.dll. NOTE: it was added after this build, so it is NOT in MW4\ in
  this commit -- it lands on the next sync-to-windows plus deploy.

* Version bumped to V5.1.0b_RC4: console title, release-notes pair, testing
  checklist (all renamed), README.

* Docs: RELEASE-NOTES-5.1.0b_RC4.{md,html} gained a writable-folder section,
  expanded known issues and a new checklist step 4. The testing checklist
  gained sections 23-24, including explicit Windows XP verification.
  OPTIONS-INI.md documents the writable-folder requirement and that the
  notation reader strips // and /* */ at parse time, so pasted comments --
  such as the fully commented [RookieMission] template in the release notes
  -- never survive a rewrite. CLAUDE.md STEP 15 records the investigation.

Windows XP
----------
Expected transparent: virtualization is Vista+, and XP ignores the asm.v3
trustInfo element. No <dependency> is declared, which is the usual way a
manifest breaks an XP app. The residual risk is VC6's rc.exe emitting a
malformed manifest resource, which XP's stricter side-by-side loader would
reject at launch. Checklist section 23 covers this. UNVERIFIED.

Test status -- NOT READY FOR EXTERNAL TESTING
---------------------------------------------
Verified on the build machine:
  - VC6 rc.exe accepts `1 24 "x.manifest"`; full build, 0 errors
  - manifest confirmed embedded in rel.bin\MW4.exe and MW4\MW4.exe
  - console launches, title reads V5.1.0b4, Super6 rotation correct
  - no false-positive warning on a normal, writable install
  - negative case passes: with write denied the dialog appears BEFORE the
    display initialises, names the folder, and the game continues on OK

Not yet verified anywhere:
  - exclusive fullscreen with the DWM8And16BitMitigation shim and a manifest
    present -- the one change that could regress a working pod
  - gos-displays.txt and MFD / cameraship role assignment
  - options.ini round trip ([automaticmode] / [RookieMission])
  - mw4print libmysql messages and the shipped vcruntime140.dll
  - anything at all on Windows XP

Also includes the rebuilt binaries and repacked props.mw4 from that build.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-15 12:26:20 -05:00
777f338c31 Document the paper-doll loader, its format law, and art-replacement checks
The guide covered authoring and coordinates but not the code that consumes the
result, which is where the constraints actually come from. Adds what an agent
or a human needs to work on this system without rediscovering it.

New: "How the runtime loads and draws the art". The call chain from
huddamage.cpp texturename[] through CMFD_Device::LoadDamageTexture /
CRadar_Device::LoadRadarDamageTexture / CMR_Device::LoadMRTargetTexture into
CreateATextureFromBitmap, with the path each device builds.

The important part is the format law. CreateATextureFromBitmap uses the raw
palette INDEX as both brightness and alpha and never looks the palette up:

    wA = data[x];
    WORD wBit = (WORD)((wA<<8)&0xF000|0x0FFF);
    *bits++ = (WORD)((wA>0)?wBit:0);

So a doll must be 8-bit with an identity greyscale palette, alpha is the top
nibble (index < 16 invisible, 0 dropped), and >8bpp is unsupported. A file can
look perfect in a viewer and still be wrong: the Battlemaster radar art arrived
with a 131-entry optimised palette running opposite to brightness, which would
have rendered near-black and mostly transparent. The rule explicitly does NOT
extend to hsh/MFD tiles or hsh/Mechs portraits, which load through GDI.

Audited every doll the code actually references rather than the directory:
65/65 correct on both displays, the only absence being the commented-out Dasher
entry at huddamage.cpp:81, which pairs with its commented-out coord.cpp rows.
Scoping matters -- hsh/hud holds ~197 files but only ~65 are dolls, so a
folder-wide audit produces 130 false positives.

New: "Replacing art for an existing chassis" -- per-zone bounds, reassembly,
and spill. Re-shading inside an unchanged box is safe; a moved piece silently
scrambles the doll and is invisible until something takes damage in game.

Also records hsh/mr_texture.bmp as the game's only chassis-independent doll,
and that hudchat.cpp:853's claim that it holds no image is wrong.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-09 20:45:13 -05:00
877d1b8cfa Install the updated Battlemaster score-sheet and radar art
Two files supplied. One was a direct drop-in, the other was not.

battlemaster_print.bmp -> hsh/Mechs/battlemaster.bmp, copied byte for byte.
recscore.cpp:2959 builds hsh\mechs\<name>.bmp and loads it with Win32
LoadImage, then StretchBlts it onto the printed score sheet, so GDI resolves
the palette and any palette layout works. The existing portrait was itself a
254-entry non-identity palette, confirming this path does not care.

battlemaster_radar.bmp -> hsh/radar/hud/battlemaster.bmp, CONVERTED first.
This one could not be dropped in as supplied. render.cpp:1653
LoadRadarDamageTexture -> CreateATextureFromFile -> CreateATextureFromBitmap
reads the raw palette INDEX and uses it directly as both intensity and alpha:

    wA = data[x];
    WORD wBit = (WORD)((wA<<8)&0xF000|0x0FFF);
    *bits++ = (WORD)((wA>0)?wBit:0);

The palette is never looked up, so these dolls must carry an identity
greyscale palette where index == grey level. Every existing doll in hsh/hud
and hsh/radar/hud is mode L, which satisfies that.

The supplied file had an optimised 131-entry palette that is close to inverted
against index order: index 1 is pure white, but the engine would have read it
as level 1 -- near black with alpha nibble 0, i.e. invisible. Peak brightness
would have rendered at 129/255 with alpha capped at 8 of 15. It would have
looked broken, not subtly wrong.

Converted to mode L, which resolves each index through the palette. Verified
the converted pixels are identical to the supplied artwork, the palette is now
identity, and the header matches the other dolls (512x512, 8bpp, 1078).

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-09 20:28:23 -05:00
a6165387ed Rebuild the generic target-MFD tile from the cameraship armour doll
The tile I authored earlier was wrong in kind, not just in detail: I composed
it from the generic damage-doll zone sheet, which yielded a flat white
silhouette rather than a shaded greyscale paper doll, and mirrored the arms
while assembling it.

The game already ships a proper generic doll. hsh/mr_texture.bmp holds a
shaded, articulated mech -- head, centre and side torsos, both arms with gun
ports, both legs -- in its top-left corner, with hsh/mr_texturea.bmp as the
greyscale mask companion. render.cpp:1276-1277 loads both into CMR_Device,
which is the cameraship Map/Armour screen. mr_texture carries 126 grey levels
against mr_texturea's 29, so the former is the shaded art and is used here.

The doll occupies columns 0-55; the shield glyph at 66-78 is excluded. Scaled
to fill 120x120 with a 4px margin. Header now matches the shipped tiles
exactly: 8bpp, 1078-byte data offset, same size as annihilator.bmp.

Note hudchat.cpp:853 claims "there is no actual image on mr_texture.bmp &
mr_texturea.bmp". That is wrong, and is probably why this asset stayed
unnoticed.

The in-cockpit doll Content/textures/HUD/generic.tga is deliberately left
alone.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-09 20:17:24 -05:00
652f4d6d09 Record the Python project config VS Code wrote for MECHSPECSHEET
Side-effect of configuring a Python environment while building the mech spec
tooling. Harmless to keep: the scripts are standard-library only and need no
venv, so this just stops VS Code re-prompting.

Worth knowing that this same file sets files.eol to CRLF workspace-wide, which
is right for the game content but also applies to newly created files -- it is
why the MECHSPECSHEET scripts were born with CRLF shebangs and had to be
normalised to LF before they would execute directly.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-09 20:11:18 -05:00
653265c928 Add MECHSPECSHEET: generate the stock-mech spec sheet from the source tree
Turns the hand-maintained 5.0.7D mech specification spreadsheet into something
generated from Content/, so the numbers come from the data the game loads
rather than being transcribed. 65 stock chassis, 55 columns, every non-weapon
column populated.

Standard library only. An .xlsx is a zip of XML and only sheet1.xml is
rewritten, so the template's formatting and drawings survive untouched.

Two findings worth recording, both in the README:

The Armor block's per-zone values in .subsystems are TONS, not multipliers.
MechLab.cpp GetCurrentMechArmorData does tonnage = points / points_per, so
installed points = tons x points-per-ton, with the rates in
Subsystems/Armor.data (Standard 32, Ferro 38, Reflective/Reactive 30,
Solarian 60). Battlemaster = 13.0 x 32 = 416 points. Cross-checked against
MechLab.cpp:796 armor_bar = (TotalArmor/535)*100: 50 of 65 chassis land within
5 points of their stored ArmorRating. An earlier pass summed MaxArmorValue from
.damage instead, which gives armour CAPACITY (1024 for the Battlemaster), not
what is fitted.

The Gladiator mounts six Clan Medium Pulse Lasers in its right arm, so the
template's three arm slots were silently dropping three weapons. Maximum usage
was measured per location across all 65 chassis before widening anything: only
the right arm overflowed. Right_Arm_4/5/6 inserted, trailing columns shifted
+3. The generator now warns by name if any location overflows again.

Judgement calls, all documented:
  - columns 10-15 are "installed in the stock loadout", read from .subsystems,
    not the CanLoad* flags in .data. The template sample marks the Battlemaster
    n for ECM/AMS/LAMS/Beagle even though it can load all four.
  - column 7 follows the data (Standard) over the sample (Ferro-Fibrous).
  - rating bars are pulled from .instance rather than recomputed. MechLab
    recalculates them live while editing, so stored and computed can drift;
    15 chassis diverge by more than 5 points, Fafnir worst at 38.

verify-mech-specs.py runs five checks. The load-bearing one calibrates the
generated Battlemaster row against the template's hand-made sample: those 11
weapon cells are an independent statement of the loadout, and they match
exactly, which is the evidence that .subsystems is being read correctly.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-09 20:09:24 -05:00
7cd1d51e08 Revert the jenner2c .instance page rename: variants bind to that name
Saved mechlab variants reference their chassis by the .instance PAGE name, so
renaming [jenner_iic1] to [jenner2c] orphaned V4H's three Jenner variants
(Jenner2c 1, Jenner2c 4, Jenner2c Pharaoh). Each stores 'jenner_iic1' in its
{Mech} blob and carries a jenner_iic1{Subsystem} entry.

I renamed it in a67d1c00 for consistency, calling it the sole outlier among 89
chassis. It was not an outlier, it was load-bearing, and the earlier V4H notes
compounded the error by recording those three variants as "broken/orphaned --
chassis jenner_iic1 does not exist even in V4H". It does: it is this page.

Evidence the binding is the page name and not the folder or filename:
Content/Mechs/Blackhawk declares [nova] and its nine Black Hawk variants all
reference 'nova'. Matching is case-insensitive -- [Loki] is referenced as
'loki', [Ryoken] as 'ryoken' -- but the name itself must match. Checking every
offset in the 344-byte {Mech} blob found no position holding the positional
MechID for all 59 sampled chassis, so variants store no index at all and the
alphabetical renumbering from adding six chassis cannot disturb them.

The filename renames jenner_2c.* -> jenner2c.* are kept; nothing references
those. The champion_stroyed and thunderbolt_destroyed.obb renames are also
unaffected, being reachable only from their own .data. The other five new
chassis already declare the page names their variants expect.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-09 18:39:59 -05:00
1466e54d6f Publish V5.1.0b_RC3: rebuilt binaries, repacked resources, redeployed game
Build-machine feedback commit. Everything here is generated output pulled back
from the Windows VC6 machine with build-env/sync-from-windows.sh, and it is the
first RC3 binaries and data package drop.

Binaries (Release + Profile + editor):
  rel.bin/MW4.exe      3,682,304  (was 3,637,248)
  rel.bin/MW4pro.exe   4,431,872
  pro.bin/MW4Ed2.exe   5,320,770
plus the full library set, helper exes and DLLs, and 1771 per-project VC6
intermediates.

This build carries the code fixes that RC3 exists to validate:
  - target-MFD atlas raised to 1024x2048 (2dea1779). The old 1024x1024 atlas
    held 64 tiles for a 65-entry mechnames[], so targeting a Zeus wrapped to
    tile 0 and drew the Annihilator.
  - mechnames[5] assassinii -> assassin2, and the behemothii texture, which
    STOPped Release outright when flying a Behemoth II.
  - J&J MFD/Radar damage-zone mappings (ef4e014). coord.cpp is #included by
    DXRasterizer.cpp, so those corrections only become live in this rebuild.
  - -tident rewritten onto GDI windows (c0daa2d9).
  - mw4print build fix (6c8650fa).

Resources: 58 packages repacked, including textures.mw4 (261.4 MB). Verified
the 46 new textures.hint pages are actually present in the package -- @achp0,
@agrf0, @ajec0, @amar0, @athu0, the 15 footstep pages and hud\generic all
resolve inside the #VBD directory. Those pages are the one part of the six
staged chassis that is NOT inert; the mechs themselves remain unregistered.

Deployed game (MW4/): 49 files, including the 22 repaired damage dolls, which
are now byte-identical to their sources in Gameleap/mw4/hsh. 21 come from
ef4e014 and one, Atlas.bmp, from 7d71d2a7; both commits changed only the source
tree and explicitly left the deploy to be regenerated here, so it had been
stale on those since RC1. Also new: the six chassis portraits and the three
generic HUD images, confirming deploy-mw4.ps1 picked up the new hsh art.

Deliberately excluded from the pull: dgVoodoo2 at the deploy root (Win10/11
only, breaks the XP pods; the tracked MW4/dgvoodoo2_files/ staging copy is
unaffected), MW4/Resource/Variants/, and the per-launch gos-displays.txt and
gos-fps.txt.

Not yet verified: this is a build result, not a test result. RC3 sections 18
and 19 still need hardware runs -- targeting a Zeus, flying a Behemoth II in
the RELEASE build (Profile masks it with the placeholder texture), -tident
across four monitors, and the MFD/Radar dolls on physical displays.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-09 18:04:36 -05:00
c054f7bcb4 Anchor sync-from-windows dgVoodoo2 excludes to the deploy root
The exclude patterns had no leading slash, so rsync matched them on basename at
any depth. That blocked MW4/dgvoodoo2_files/ as well -- a tracked staging copy,
committed since RC1, that pod owners install by hand on Win10/11 and which must
keep mirroring. Only a live dgVoodoo2 at the MW4/ root is dangerous, because
that is the copy the game would actually load.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-09 18:04:16 -05:00
a57be4bd12 Add sync-from-windows.sh to pull build outputs back from the build machine
sync-to-windows.sh pushes sources out; there was no inverse, so every build
had to be retrieved by hand. This pulls back only GENERATED artefacts:
the linked output dirs (rel/dbg/pro/arm/ice.bin), the per-project VC6
intermediates, packed Resource/*.mw4 + *.dep, the MW4/ deploy, the binaries
deployed into Gameleap/mw4, and the build logs. Source, content and assets
are never pulled, so a stale copy on the build machine cannot clobber local
edits made since the last push.

Two things it has to get right, both found by running it:

It does not use rsync -a. The CIFS mount reports every file as 755 while git
tracks these as 100644, so preserving permissions would flip the exec bit on
every file pulled -- a spurious mode change on ~2000 paths. It uses
-rlt --chmod=F664,D775 instead. Verified: 1994 files pulled, 0 mode changes.

It excludes dgVoodoo2 from the MW4/ deploy (DDraw/D3D8/D3D9/D3DImm.dll,
dgVoodoo.conf, dgVoodooCpl.exe). That is a Win10/11-only prerequisite which
must never enter the repo because it breaks the XP pods; it was removed
deliberately in 0ceba9c7. A naive pull dragged all six straight back in.
This was being stripped by hand before every push back.

--delete is opt-in rather than default: a pull with --delete can destroy
mirrored history if the build machine is missing something. Excluded files
are protected from deletion, so Resource/*.tga and *.nfo cannot be removed
by it. There is also a --dry-run, and a warning if the pull targets already
hold uncommitted work.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-09 17:54:38 -05:00
42fe0a7b6a Update the six chassis STATUS files to record what was installed
These are the per-chassis completeness trackers, and they still described the
pre-install state: header said STAGED, jenner2c listed jenner_2c.* filenames
that no longer exist, and every one carried the "REVIEW MOVEMENT TYPE" item
that has since been decided.

Each file now has three sections. "installed" lists exactly what landed for
that chassis and what is shared with the other five. "applied" records the
data decisions and, for dasher and marauder, records that nothing was changed
and why - a deliberate no-change is a result worth tracking, not an omission.
"still to do" keeps the registration steps, renumbered without the settled
movement-type item, and adds a final step to delete the STATUS file once the
chassis is registered.

Per-chassis notes worth having written down:
  - dasher is the only one that added zero textures.hint pages; it reuses the
    existing @adas0-5 skins, footsteps and the Dasher_destroyed wreck.
  - dasher and marauder keep LeftJumpJetSiteName=';' as decompiled, where our
    own five LEGMOVETYPE chassis leave it empty. Inert while CanLoadJumpJets
    is No, and recorded as a residual rather than quietly edited.
  - jenner2c is the only one of the six with its own animscript; the other
    five borrow dragon, hauptmann or loki.
  - jenner2c's decision block now records that our copy was deleted rather
    than overwritten, and why.

STATUS files are LF-only and stay that way; they are notes, not engine input.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-09 17:42:05 -05:00
8e859007c8 Author the two generic HUD images for the six new chassis
Decisions 1 and 2 settled on one shared damage doll and one shared target-MFD
tile for all six chassis. V4H supplied the two 512x512 dolls (installed in the
previous commit) but neither of these, so both are ours.

Content/textures/HUD/generic.tga (128x128 RGBA) is the in-cockpit doll. It is
an exact 4:1 downscale of hsh/hud/generic.bmp, alpha carrying the zone mask and
RGB flat shading with a one-pixel dark edge. The doll is exactly 4x this size,
so keeping its layout means the cockpit texuv/offset rows are the doll
rectangles divided by four -- no new coordinate authoring in the registration
phase. Header is byte-for-byte the shipped shape: type 2, 32bpp, 65580 bytes.

hsh/MFD/generic.bmp (120x120, 8bpp) is the target-MFD tile: a whole-mech front
silhouette composed from the doll's own eight zone blobs (two legs, two arms,
two side torsos, centre torso, head) rather than drawn from scratch, so it
stays consistent with the damage doll used for the same chassis. Structure
matches hsh/MFD/annihilator.bmp (8bpp, 1078-byte data offset).

Also adds the [hud\generic] page to textures.hint, held back from the previous
commit until the texture existed so a repack could not reference a missing
source. textures.hint is now 4747 pages, 46 of them new, still pure ASCII with
consistent CRLF.

Note the cockpit texture is deliberately flat-shaded rather than a rendered
mech; it reads correctly and is zone-accurate, but it is placeholder-grade next
to the hand-painted per-chassis art and can be replaced without touching any
coordinates.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-09 17:22:59 -05:00
a67d1c00d2 Install six V4H chassis as content assets (no registration yet)
Lands the art and data for Champion, Dasher, Griffin, Jenner IIC, Marauder
and Thunderbolt. Nothing is registered in core.build, the code arrays or the
shell scripts yet, so this commit is inert at runtime: packing is manifest
driven, the editor never enumerates Content\Mechs, and deploy drops Content\*.
The one exception is textures.hint, which is not inert -- textures.build has
no explicit .tga lines and packs via texturepool=textures\textures.hint, so
these pages will be picked up by the next textures.mw4 repack.

Contents:
  - 11 chassis/wreck folders (658 files), 30 skin TGAs, 15 footstep TGAs
  - 6 mech portraits (800x250, 24bpp; hsh/Mechs already mixes 8/24bpp)
  - generic damage dolls into hsh/hud and hsh/radar/hud
  - 45 textures.hint pages, copied verbatim from the Annihilator template
    (30 skin mip pages + 15 footstep pages); Dasher needed none, its
    @adas* and footstep pages already exist

Our own Jenner IIC was deleted first rather than copied over. Only 2 of its
84 files shared a path with the V4H set, so a copy would have left 78 stale
j2c_* files behind.

Decisions applied:
  - jump jets: LEGJUMPMOVETYPE on champion, griffin, jenner2c, thunderbolt.
    Dasher and Marauder declare CanLoadJumpJets=No and are left alone.
  - Champion gets its own wreck instead of borrowing Loki's.
  - Renames to canonical stems: jenner_2c.* -> jenner2c.*,
    champion_stroyed.* -> champion_destroyed.*,
    thunderbolt_destroyed.obb -> thunderbolt_destroyed_solid.obb.
    All internal references updated to match.
  - jenner2c.instance page [jenner_iic1] -> [jenner2c]. Every one of the 89
    shipped mechs and all five staged siblings name the page after the
    chassis; this was the sole outlier.
  - thunderbolt.data footstep typo thunerbolt_dirt -> thunderbolt_dirt.

Verified: 1217/1217 file references resolve, 273 text files are pure ASCII
with consistent CRLF (the staged .data files arrived with an LF-only
Shadow={} block), no hint page duplicates a existing name, and all 278
binaries are stored as LFS pointers.

Still to come: Content/textures/HUD/generic.tga and hsh/MFD/generic.bmp must
be authored, then the [hud\generic] page, then the registration phase.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-09 17:20:24 -05:00
c0daa2d920 Rewrite -tident: it painted one screen and left the rest blank
Reported from a four-monitor pod: -tident filled the main screen green and did
nothing to the other three.

The first version opened each DirectDraw device with DDSCL_NORMAL and drew on
that device's primary surface. Under DDSCL_NORMAL a secondary device's primary
surface is not that monitor's framebuffer - it either fails to create or
resolves to the desktop primary - so every device painted the SAME screen, each
over the last. Green is acrFill[1], so device 1 painted last and devices 2 and 3
failed outright, which matches the report exactly.

Now does what the Windows Identify button does: one borderless topmost GDI
window per monitor, positioned from the rectangle that device's HMONITOR
reports, with a real message pump so the windows actually receive WM_PAINT.
Nothing takes exclusive mode and no display mode is changed, which was the point
of the original design and still holds.

Two things fall out of the rewrite:

* Devices resolving to the same rectangle are now grouped behind one window
  labelled with both numbers ("1 & 2") instead of overpainting each other. That
  was a parked item in CLAUDE.md - it happens when a driver presents one monitor
  as two devices, which is common on a single-screen test PC.
* A device reporting no HMONITOR is the primary display driver alias and now
  falls back to the primary monitor rectangle rather than being lost.

gos-displays.txt gains a painted size/position line per monitor, a
"shares a monitor with device N" line, and a summary count, so a failure to show
anything is visible in the log instead of silent.

Docs: this matters because RC2 told pod owners to run -tident and it did not
work. Added to the RC3 change list, corrected the now-obsolete known issue about
single-monitor overpainting, and rewrote checklist section 12 to lead with the
regression - all four monitors must be covered.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-09 17:04:22 -05:00
1684c3fbd2 Bump to V5.1.0b_RC3 and document everything since the RC2 build
The binaries and packages committed as RC2 in 8bfaf9b9 no longer match the
source tree: MW4.exe, mw4print.exe and three resource packages have all changed
since. RC2's own notes warn about exactly this situation with RC1, so this is a
version bump rather than an edit in place.

Console title -> V5.1.0b3, with a new dated line in the ConLobby.script
changelog block; the existing lines are left alone. That string lives in
props.mw4, so it only appears on a pod after the repack, which is what makes it
a usable "did this machine get the new content" check.

Release notes and checklist renamed RC2 -> RC3. The notes' front matter was
restructured rather than having a third block stacked on it: "Already testing
RC2? Here is what is new in RC3" now leads, followed by "What was new in RC2"
for anyone jumping two versions.

New in the notes, written for pod owners rather than engineers:

* Damage displays corrected on 22 'Mechs, described as the two faults an
  operator would actually notice -- the figure was mirrored, and the zones did
  not line up with the artwork -- plus the note that Rifleman and Battlemaster
  had been using another chassis' layout entirely.
* The target picture on the MFD showed the wrong 'Mech. Includes the
  gos-displays.txt line to look for, and what it means if the card refuses the
  larger atlas and falls back.
* High Explosive charges now fire, how to use them (weapon group, not eject),
  the NARC side effect, and the advice to refit the charge on saved variants.
* Three content faults corrected. Cauldron Born is explicitly flagged as
  behaviour-unchanged so nobody hunts for a difference that is not there.
* mw4print -debug.
* A warning that this build changes artwork as well as the executable, and
  upgrade step 2 now says to copy the whole folder. An old hsh\ with a new
  MW4.exe gives wrong damage displays, which is the trap this build creates.

Checklist gained sections 18-22, 88 -> 133 checkboxes, all marked as never
having been run on hardware:

* 18 names every one of the 22 chassis individually for both displays, and
  makes handedness an explicit check, since the mirroring is the fault that
  reads as "slightly odd" rather than obviously broken. Calls out that the
  Atlas MFD doll was regenerated and so replaces shipped art.
* 19 leads with targeting a Zeus, and requires flying a Behemoth II in the
  RELEASE build -- that fault was fatal in Release and masked in Profile by the
  placeholder texture, so testing it with MW4pro.exe proves nothing.
* 20 includes the NARC ammo-count regression check.
* 21 frames the content fixes as "confirm nothing moved".
* 22 covers mw4print -debug.

The build-requirements header was materially wrong and has been rewritten. It
named props.mw4 only; core.mw4 and textures.mw4 now change too, each must be
deleted before repacking because the packer carries stale entries forward and
never drops deleted ones, coord.cpp is #included by DXRasterizer.cpp so a
mapping change needs a full exe rebuild, and the 21 changed hsh\ dolls are
loose files that arrive only via deploy-mw4.ps1.

All three documents remain pure ASCII with CRLF endings so they open correctly
in Notepad on a pod; the HTML was checked for balanced tags.

Nothing here covers the six staged V4H chassis. They are not in the source tree.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-09 16:08:57 -05:00
6c8650fa98 mw4print: declare the -debug globals in mw4print.cpp
Release build failed:

  mw4print.cpp(60) : error C2065: 'g_bDebugLog' : undeclared identifier
  mw4print.cpp(61) : error C2065: 'DbgLog' : undeclared identifier

3256c103 added the -debug switch and put both symbols in mw4dummy.h, but
mw4dummy.cpp is #included into ChildView.cpp rather than compiled on its own,
and mw4print.cpp is a separate translation unit including only stdafx.h,
mw4print.h and MainFrm.h. It therefore saw neither declaration. ChildView.cpp
and recscore.cpp compiled because they pull in mw4dummy.h directly.

Declared locally rather than including mw4dummy.h. That header is a game-source
shim: it #defines MString to CString, Scalar to float, Verify and gosASSERT to
nothing, and declares stub ReplicatorID / MemoryStream classes plus the Adept
damage-type enum. It exists so game sources can be compiled into the printer,
and it has no business reaching the MFC application code. mw4print.cpp already
reached AssetsDirectory1 the same way, so this follows the file's own
convention rather than introducing one.

recscore.cpp also calls DbgLog and is listed in the .dsp, but it carries
"# PROP Exclude_From_Build 1" and is only ever compiled through ChildView.cpp,
so mw4print.cpp was the single gap.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-09 16:08:33 -05:00
2dea177909 Fix the 2D target-MFD mech image: atlas capacity and two name mismatches
Three separate defects, all the same class: a per-mech image is looked up by
string, the lookup misses, and nothing says so.  Two were pre-existing and
live; all three would have been inherited by the six V4H chassis.

1. The atlas was full, so mech id 64 showed another mech.

HSH_CreateMFDTextures built a 1024x1024 texture holding one 128x128 tile per
chassis, 8 per row, indexed by mech id -- 64 tiles for 65 names.  Zeus (id 64)
addressed y=1024.  On write, DrawBitmapToSurface blits through a GDI DC, so the
blit fell outside the clip rect and was discarded.  On read, DrawTexture2
divides by tw/th, giving v = 1.000..1.117; the MFD device never sets
D3DTSS_ADDRESS so it is D3D7's default WRAP, and that range wrapped to
0.000..0.117 -- exactly tile 0.  Targeting a Zeus therefore displayed the
Annihilator: not garbage, not black, a clean picture of the wrong mech, which
is why it went unnoticed.

The atlas is now 1024x2048 (128 tiles).  Tile origins are unchanged and the
draw derives UVs from pixel coordinates over tw/th, so the taller surface
re-normalises automatically: all 64 other chassis sample byte-identical
regions, and only Zeus moves -- from the Annihilator's pixels to its own.

CreatePixelFormatTexture returns NULL on failure and SetTexture(0,NULL) renders
untextured rather than crashing, so on a card that caps texture dimensions at
1024 an unguarded change would have silently blanked all 65 images.  It now
falls back to the original size, which restores exactly the previous behaviour,
and says which size it got in gos-displays.txt.  The granted height travels on
the device as m_nMechAtlasH because mode 4 gives each of the two panels its own
IDirectDraw7 and swaps the texture between them.

A capacity guard now logs and skips a tile past the end instead of writing it
off-surface and reading back someone else's picture, so this cannot recur
silently when the roster grows.

2. Assassin II had no target image at all.

5813aeb6 renamed hsh/MFD/assassinii.bmp to assassin2.bmp to match
texturename[], but mechnames[] is a separate list and still said "assassinii",
so the tile was never written and the mech showed whatever the freshly created
texture happened to contain.  mechnames[5] is now "assassin2"; the two arrays
agree on all 65 ids.

3. Behemoth II's cockpit doll texture was missing, and that one is fatal.

texturename[12] is "behemothii" but the texture was named behemoth2.  It was
absent from the loose tree, from textures.hint and from the packed
textures.mw4.  HUDDamage::Reset -> AddTexture -> MLRTexturePool::Add ->
ResourceImagePool::LoadImageGOS resolves content\textures\hud\behemothii.tga,
and on a miss LAB_ONLY substitutes a placeholder while Release does
STOP("Texture ... could not be found!").  m_MechID there is the player's own
mech, so flying a Behemoth II in a Release build should have been a hard stop --
masked in Profile by the placeholder.  Three of the four layers already said
"behemothii", so the texture was the outlier: behemoth2.tga is renamed to
behemothii.tga and its hint page follows.  The art is unchanged; it is
byte-identical to behemoth.tga, since Behemoth II deliberately inherits
Behemoth's doll.

Also removed the orphans left by 5813aeb6 -- hsh/hud/assassinii.bmp,
hsh/radar/hud/assassinii.bmp, Content/textures/HUD/Assassinii.tga and the
[hud\assassinii] pool page.  Nothing referenced them.  The two stray dolls were
not identical to the assassin2 pair (0.5% and 9.5% of pixels), so coord.cpp row
5 was scored against both first: identical results (MFD 0.925, Radar 0.934),
i.e. same component geometry with faint re-encode noise.  hsh/Mechs/assassin
ii.bmp is deliberately kept -- portraits are keyed by the localised display
name, not by texturename[].

Verified: every string-keyed per-mech art lookup now resolves for all 65
chassis -- mechnames[] into hsh/MFD, and texturename[] into hsh/hud,
hsh/radar/hud and Content/textures/HUD -- with zero disagreements between the
two arrays.  The other behemoth2 entries in textures.hint are the mech skin and
footstep pages and are untouched; a global rename there would have broken the
skin.

Needs a Release and Profile rebuild for the code, and a textures.mw4 repack for
the hint and art changes.  Worth confirming on hardware by targeting a Zeus and
an Assassin II, and by flying a Behemoth II in the Release build.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-09 15:44:41 -05:00
42a66157ea Add PHOTOMODE.md: shelved design for automatic mech photo capture
Records the research behind a proposed capture mode for producing clean head-on
mech renders as source art for the damage paper dolls. Nothing is implemented;
this exists so the investigation does not have to be repeated.

The key finding is that the capture already exists and ships. MechView
(-mechview / -mv, documented in -help) bakes a 25-frame turntable of the
MechLab mech into a 2048x2048 atlas and plays it back on the pod's radar screen.
That bake renders the mech alone, on a solid background, at stepped rotations,
and reads it out of the back buffer - which is exactly the capture wanted. It
just writes a texture rather than files, and is gated behind CTCL.

Two things confirmed from source that remove the main uncertainties:

  - MechLab::UpdateDisplay() is only s_MechCamera->DrawScene(true), so the
    capture draws no UI at all; and gos_SetupViewport already takes the
    background colour as a parameter.
  - MechLab exposes GetChassisCount() and SetMech(), so a batch mode can walk
    the whole roster without any interaction.

Proposed shape is "MW4.exe -photoall": boots, writes photos\<chassis>.bmp for
all 65 chassis, exits. No hotkeys or new UI. A new MechLab tab was considered
and rejected - tabs need shell-script plus callback work, and the tab's own UI
would be on screen during the capture. Recommended route into MechLab is to
bypass the shell and construct it directly, as MW4Shell.cpp:4693 does, rather
than adding a shellStart mode.

The only genuinely new code identified is a ~40 line BMP writer; the tree has
no screenshot facility at all. Everything else is wiring together routines that
already run in the shipped game.

Open questions needing a real run are recorded: per-chassis camera framing, and
which yaw is actually head-on. Constraints noted: run windowed for 32-bit colour
(fullscreen is bitdepth 16), and refuse to arm when -mechview or CTCL owns the
render hook, since MW4Shell.cpp:1623 asserts a single owner.

All 21 source references in the document were verified against the tree.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-09 14:04:42 -05:00
7d71d2a771 Regenerate the Atlas MFD paper doll from its Radar art
The Atlas was the worst-fitting mapping left in the tree at 0.795. Its Radar
sheet is detailed and correct, but its MFD sheet is a much cruder silhouette
with loose boxes and pieces that do not meet, so no coordinate edit could fix
it - the artwork itself was the limit.

Because the two authoring pipelines are 1024->320 and 1024->400, the MFD is
exactly 0.8x the Radar. That makes the whole MFD sheet derivable: threshold the
Radar art, fill each component's interior holes to get a solid silhouette,
scale by 0.8, and scale texuv3/offset3 by the same factor. The pieces are then
spread outward from the CT centre so nothing collides, with CT and HD anchored
and each L/R pair mirrored exactly.

Reviewed adjustments on top of that: torsos and legs down 3, arms down 5, S2
down and right 3, and S1 reseated into the notch in RT.

Three things worth recording, all now in MFD-RADAR-MAPPINGS.md:

  - Collisions must be tested on PIXELS, not bounding boxes. These silhouettes
    are concave, so a piece can nest into a neighbour's notch while the boxes
    overlap heavily. The S1 position that the box test rejected outright has
    zero touching pixels.
  - The right leg carried a hole the left leg did not. Replacing its art with a
    horizontally flipped copy of the left leg makes them identical - 7612 px
    each - and the placement is mirrored about the CT centre to match.
  - The CT art under HD plus a 1px ring is blacked out, so the head reads as a
    separate part rather than a patch on the torso. Verified as "CT pixels
    within 1px of HD == 0".

S1 and S2 remain deliberately asymmetric (22x20 and 21x50, different places).
That is faithful to the Radar and to the chassis, which declares both specials
with their own erf assets.

  atlas mfd  0.795 -> 0.956, no cross-piece clipping, no zone overlaps
  whole tree 130 combinations, mean 0.950, minimum now 0.851

Note this replaces shipped art with generated art, unlike the earlier commits
which restored intent. It is reversible via git. Confirm it on a physical MFD
before applying the same technique to the other crude sheets (thor, loki,
bushwacker, solitaire and similar all sit in the 0.85-0.90 band).

Requires a Windows VC6 rebuild of MW4.exe; loose hsh art needs no repack.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-09 13:37:08 -05:00
a26ff967d8 Repair the four broken Radar damage mappings; document the findings
The whole-tree audit after the previous commit found four Radar rows scoring
0.266-0.638 against their artwork, with boxes bounding blank space. All four are
chassis whose Radar sets J&J never delivered, so the rows had never been checked
against the art. They are pre-existing, not regressions.

With each chassis's MFD now correct, the Radar mapping is derived from it rather
than measured by hand: source boxes come from the Radar art's own connected
components, zone identity is matched from the MFD by normalised position plus a
size-consistency term, and placement scales each piece's ART CENTRE by ~1.25.

Two details that matter and are easy to get wrong. Position-only matching swaps
CT and HD, because a head and a torso can sit at similar normalised positions;
the size term fixes it. And scaling a piece's corner rather than its centre
misplaces it whenever the Radar piece is a different proportion from its MFD
counterpart - the Longbow's Radar CT includes the pelvis while its MFD CT does
not, which put CT 23px off centre until centre-scaling was used.

  kodiak   0.266 -> 0.962
  longbow  0.379 -> 0.959
  argus    0.457 -> 0.956
  fafnir   0.638 -> 0.963

Longbow additionally had a phantom special zone. The chassis declares no
Special1Internal, no joint_specialone and ships no *_specialone.erf, yet both
displays mapped S1. On the Radar that piece is the cockpit canopy - it matches
the CT's recess exactly - so it is remapped to HD and S1 is cleared. On the MFD
the S1 piece is the pelvis, a genuinely different component, and is deliberately
retained so the pelvis still renders; it simply never takes damage.

Whole tree after this change: 130 mech/display combinations, mean 0.949, median
0.955, minimum 0.795, none below 0.75 (was four).

MFD-RADAR-MAPPINGS.md is corrected and expanded. Three things it stated were
wrong and had caused real defects:

  - it instructed storing the unexploded rect in texuv and the exploded origin
    in offset, which is backwards and broke all 21 imported rows;
  - it cited Behemoth MFD x=281 and Black Hawk Radar x=85 as intentional odd
    coordinates to preserve, when both are transcription errors bounding blank
    space (218 transposed, and a lost leading digit of 285);
  - it recorded all 19 supplied BMPs as pixel-identical to the runtime art, when
    all 19 differ by 6-35% because the art had never been installed.

Added: the handedness rule and its one-line assertion; the .damage cross-check
for special zones; the derivation method above; a catalogue of every verified
defect; and a tooling-pitfalls section covering blob thresholds hiding small
parts, unreliable hole detection on anti-aliased art, ink coverage not implying
correct placement, and overlap audits needing blob ownership to avoid ~107 false
positives. It also notes that generate_comparison_maps.py compares coord.cpp
only to the measurement files and never to the artwork, so it reported "19
exact" throughout every one of these failures.

Requires a Windows VC6 rebuild of MW4.exe and hardware testing on physical
MFD/Radar displays.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-09 13:14:32 -05:00
ef4e014066 Install J&J MFD/Radar art and repair the damage-zone mappings
The STEP 13 install put the J&J data into coord.cpp with two systemic faults.
Both are fixed here, along with eight transcription errors in the source files.

texuv/offset were transposed. texuv2/texuv3 must hold the EXPLODED-layout box
and offset2/offset3 the assembled placement point; the previous install had them
the other way round, so no rectangle bounded its own artwork.

Left and right were mirrored. The engine draws the doll facing the viewer, so
the mech's right side must appear on the viewer's left for damage to map to the
side actually hit. All 19 J&J sets were authored from the mech's own viewpoint.
Confirmed by handedness audit: every display with J&J data was mirrored and every
display without it was correct, so the fault came in with that import. The seven
untouched displays (argus/fafnir/flea/gladiator/kodiak/longbow radar, hellspawn
MFD) are original game data and are left alone.

The art itself had never actually been installed - all 19 files differ from the
supplied exploded BMPs by 6-35% of pixels, despite MFD-RADAR-MAPPINGS.md claiming
they were pixel-identical. Sources are normalised to 512x512 8-bit greyscale to
match the existing hsh format. behemothii art is resynced from behemoth since
mech ID 12 inherits ID 11's coordinates.

Source-data errors found and corrected during review:

  annihilator radar RA  dropped digit in y1        iou 0.13 -> 0.96
  assassin2   radar CT  omitted the pelvis flare   iou 0.51 -> 1.00
  behemoth    mfd   LA  x 281, transposed from 218 iou 0.25 -> 0.81
  blackhawk   mfd   RT  transposed leading pair    iou 0.13 -> 0.99
  blackhawk   mfd   HD  box only 4px tall          iou -> 0.80
  blackhawk   radar LT  lost leading digit of x0   iou 0.27 -> 0.97
  blackhawk   radar LA  box clipped 2 rows of art
  Fafnir      mfd   RT, LA  boxes overlapped the neighbouring piece

Plus 15 reviewed placement corrections: mirrored pairs levelled on art extents
rather than box edges (box padding differs between mirrored pieces), Behemoth
torso/arm alignment, Kodiak head moved into its cockpit slot, and Warhammer CT
shifted 19px left, which also re-centred its head.

All 19 sets were reviewed individually against the exploded sheet and the
assembled result. Lowest set mean IoU is 0.851; arrays remain 65x11 and the file
remains CRLF-only.

Requires a Windows VC6 rebuild of MW4.exe (DXRasterizer.cpp includes coord.cpp)
and hardware testing on physical MFD/Radar displays. MW4/hsh is deliberately not
updated; it is regenerated by deploy-mw4.ps1 on the build machine.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-09 12:34:38 -05:00
1a0c178759 Repair MFD/Radar damage mappings for all seven borrowed-row chassis
Rebuilds texuv2/offset2/texuv3/offset3 for Archer, Ares, Battlemaster,
Battlemaster IIc, Rifleman, Sunder and Victor. Every one had rows copied
verbatim from another chassis, so the game sampled the wrong regions of
their damage art and assembled the pieces into a foreign layout.

Requires a Windows VC6 Release/Profile rebuild - DXRasterizer.cpp includes
coord.cpp directly - plus a physical MFD/Radar hardware test.

Result (mean IoU of each texuv rect against its own art)

  chassis           MFD           Radar
  archer            0.23 -> 0.85  0.19 -> 0.86
  ares              0.51 -> 0.80  0.35 -> 0.81
  battlemaster      0.14 -> 0.77  0.22 -> 0.91
  battlemaster IIc  0.18 -> 0.83  0.25 -> 0.97
  rifleman          0.14 -> 0.86  0.25 -> 0.87
  sunder            0.28 -> 0.96  0.86 (radar was already correct)
  victor            0.33 -> 0.85  0.44 -> 0.86
  mad cat (control, untouched)    0.88 / 0.88

Every remaining sub-0.55 entry in the file is now the separate 2026-08-07
J&J batch; no other chassis is misaligned.

Scale of the errors
-------------------
These were not small drifts. Archer's RT was 132 px wide and swallowed an
arm plus a missile rack, where the actual rack is 46 px. Sunder's RT and
LT pointed at y200-306, down in the legs, while the real racks sit at
y38-144. Rifleman's CT indexed empty black space.

Method
------
texuv comes from the art: each zone's rectangle is the bounding box of its
component blob, x1/y1 + 1. Zone labels were confirmed visually chassis by
chassis - an automatic classifier trained on the healthy rows reproduces
only ~70% of them, because zone placement is conventional but not
guaranteed.

offset is the assembled-doll layout and cannot be derived from the art. It
is built by starting from the exploded view (offset = the piece's own art
position), freezing every y, anchoring CT, and sliding limbs horizontally
until exactly 3 clear pixels separate neighbouring white pixels. Pieces are
compared per row rather than by box edges, so boxes may overlap while the
artwork stays clear. Two constraints are required or the solver misbehaves:
a piece must never cross the CT centre line, and it must be free to move
outward as well as inward.

Radar is done first and the MFD derived from it, because the MFD art is a
reduction of the radar art - scaling down is more accurate than scaling up.
Every MFD y is taken as mfd_CT_y + (radar_y - radar_CT_y) * CT height ratio;
reusing the MFD art rows directly puts the torsos ~27 px too high, as Victor
showed.

Per-chassis notes
-----------------
- Battlemaster IIc is NOT the same art as Battlemaster despite appearances:
  11 components vs 9, only 6 bounding boxes matching. The IIc fuses each arm
  with its weapon into a single piece and has no S2 at all. Its shared zones
  reuse Battlemaster's geometry unchanged; only the arms differ, anchored by
  their inner-top corner so the wider Clan arms grow outward.
- Sunder's radar was already correct and is untouched; only the MFD moved.
- Ares needed a different approach entirely - 16 art fragments for 10 zones,
  with both specials populated. The content data settled it: ares.damage
  lists the zone pages, ares.subsystems shows Special1 = 2 Beams and
  Special2 = 3 Missiles (identifying the six-tube rack as S2 and the
  two-bolt mount as S1), and Ares.contents shows CenterTorso owns both
  joint_centertorsorear and joint_hipbelow, so CT is the torso column and
  the hip merged. Detail rings inside the limbs are separate blobs but not
  separate zones, and were discarded.

Head placement
--------------
The CT art usually contains a purpose-cut slot the same size as the HD
piece, so seating the head is a pure vertical move with x unchanged. Archer
radar is exactly 13x11 for a 13x11 head. Where no slot exists - Rifleman
radar, Victor - the head is placed from the counterpart display's
proportional position instead.

File remains CRLF-only; all four arrays verified at 65 rows x 11 zones.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-09 10:58:00 -05:00
dicion 9092b78dbf Correct STEP 14 deployment notes for the High Explosive fix
Record the actual packages for the two edited content files: HighExplosive.data
is packed by core.build into core.mw4, not props.mw4, and weapons.script by
props.build into props.mw4. build-resources.ps1 repacks on newer sources, so both
are picked up automatically; the distinction only matters for selective repacks.

Also document that restoring the NARC Beacon ammo-round display is an intended
consequence of moving the highexplosive UI flag from ID 84 to 87, note that
authored .subsystems loadouts need an explicit AmmoCount=1 because Weapon_Tool
defaults ammoCount to -1, and record that a sweep of every weapon subsystem found
High Explosive to be the only one whose starting-ammo formula truncated to zero.
2026-08-08 23:05:05 -05:00
dicion cd10398f71 Fix High Explosive firing from MechLab
Correct the High Explosive ammo key and ensure positive-capacity weapons receive at least one starting round and ammo per pack. Apply MechLab special handling to weapon ID 87 and document activation, stacking, validation, and deployment requirements.
2026-08-08 22:46:42 -05:00
3e48718330 Ignore Python bytecode; untrack two stale committed .pyc files
Adds __pycache__/ and *.pyc to the root .gitignore, and removes from
version control the two bytecode files that had been committed under
build-env/__pycache__/.

The repo had no ignore rule for bytecode anywhere except
MW4COMPARE/tools/.gitignore, which I added with the decompiler toolchain
and which only covers that subtree. So running any script under
build-env/ leaves an untracked .pyc behind; extract-mw4.cpython-312.pyc
appeared while the decompiler was reusing build-env/extract-mw4.py for
.mw4 container parsing.

Untracked:
  build-env/__pycache__/replay-mr.cpython-314.pyc
  build-env/__pycache__/variant-to-subsystems.cpython-314.pyc

These are generated artefacts, and the filename carries the interpreter
tag - both are cpython-314 while this machine runs 3.12, so they are
stale bytecode that no interpreter here will load and that will never be
refreshed in place. Removed with `git rm --cached`, so both files remain
on disk and simply stop being version-controlled; they regenerate on
import as needed.

Note a .gitignore rule does not affect already-tracked files, which is
why the explicit untracking was required in the same change.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-08 16:50:39 -05:00
ad49ec708f Fix Rifleman MFD/Radar damage mappings; document the authoring pipelines
Repairs Rifleman's external MFD and Radar paper-doll source rectangles,
removes the dead commented-out Dasher rows, and records the canonical
authoring pipelines that had never been written down.

Requires a Windows VC6 Release/Profile rebuild - DXRasterizer.cpp
includes coord.cpp directly - plus a physical MFD/Radar hardware test.
The Linux workflow validates data and geometry only.

How the mapping was validated
-----------------------------
Ground truth is Mad Cat (ID 40), whose mapping is known-good in game and
user-confirmed. It establishes the invariant:

  texuv2/texuv3 rectangles must bound the component blobs in the runtime
  BMP, and the rule is exact - texuv = blob bounding box with x1,y1 + 1.

offset2/offset3 are on-screen placement and deliberately do NOT align to
the BMP; overlaying them against the art is meaningless.

Scoring each rectangle by IoU against the nearest art blob gives a clean
bimodal split: healthy chassis 0.78-0.99 (the single sub-0.5 zone is
normally HD, which has no blob of its own), broken chassis below 0.5.

Rifleman
--------
Its texuv2 row was a verbatim copy of Mad Cat's, so CT landed in empty
space, S1/S2 enclosed nothing and the legs hung off the art. Its texuv3
row was independently misaligned.

  MFD    0.14  -> 0.86
  Radar  0.25  -> 0.87
  (Mad Cat control, unchanged: 0.88)

Zone labels were confirmed by eye, not inferred - an automatic classifier
trained on the 42 healthy chassis reproduces only ~70% of labels, because
zone placement is conventional but not guaranteed. HD is the small 3-bar
element under the centre torso, recovered by merging three sub-300px
fragments that the blob threshold had discarded.

Note the Radar layout is NOT a mirror of the MFD one: the legs move up
into the middle row and the wide S1 slab moves from the top to the
bottom. Radar S2 is now zeroed; the previous {186,10,312,78} had no
corresponding art.

Dasher rows
-----------
Dropped the four commented-out M_Dasher rows from all four arrays. Dasher
is not in the 65-ID roster, and a commented row is an active hazard: any
tool that parses these arrays or texturename[] without stripping comments
first picks it up and shifts every later chassis by one. That exact bug
produced a bogus "id41 madcat" during this work.
(huddamage.cpp still has a commented "hud\\dasher" entry - same hazard,
left for a separate change.)

MFD-RADAR-MAPPINGS.md
---------------------
Adds the project owner's verbatim MFD and Radar authoring pipelines as
the authority, plus the fact that coord.cpp is edited via the GameOS
project's "External Dependencies" folder in the VC6 IDE.

Records the distinction that caused a wrong diagnosis during this work:
Radar expands its canvas to 512 BEFORE the unexploded view is saved, so
Radar coordinates are measured in 512 space, whereas MFD expands to 512
AFTER all coordinates are recorded, so MFD coordinates are in 340 space.
Also MFD separates sections with a 4x4 black line while Radar outlines
with 2x2 white, both rectangles are measured on the unexploded view, and
the exploded view saved in indexed mode is the runtime BMP.

File remains CRLF-only; all four arrays verified at 65 rows x 11 zones.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-08 16:48:29 -05:00
83478b7666 Add MW4COMPARE: .mw4 decompiler toolchain and V4H comparison harness
Tooling built to recover editable source for six 'Mech chassis that exist
in the parallel FS_Build_V4H build but not in this repo. Reverse-engineers
every compiled record type in the .mw4 package format back to the .data /
.instance / .subsystems / .damage / .contents / .torso / .engine /
.armature sources the content pipeline consumes.

Nothing here is wired into the game build. It is a standalone analysis
harness run from Linux.

Package format
--------------
"#VBD" container. Directory records are [len][name][FILETIME][origSize]
[storedSize][offset], payload base at dword 0x0C. A record is stored raw
when storedSize == origSize, otherwise LZW (9->12-bit LSB-first codes,
256=clear, 257=EOF, dict from 258), per Database.cpp:451.

GameModel records are flat /Zp4 structs following the C++ inheritance
chain Entity(0) -> Mover(28) -> MWObject(80) -> Vehicle(664) -> Mech(756),
1636 bytes total. CreateMessage records follow Replicator -> Entity ->
Mover -> MWMover -> MWObject -> Vehicle -> Mech from start=16 (the
undeclared Connection__Message header), ending at 341 and padded to 344.

tools/decompile/
----------------
  datamap.py        header-driven layout engine; CHAIN + ANCHORS
                    {Vehicle:664, Mech:756} assert the struct offsets
  mw4msg.py         CreateMessage reader/walker
  data.py           .data      constants.py  define/table symbol resolution
  damage.py         .damage    contents.py   .contents
  smallmodel.py     .torso + .engine         instance.py  .instance
  armature.py / armature_parts.py  .armature + armaturedata/armaturevideo
  assembly.py       joint hierarchy renderer
  make_generic_doll.py  builds generic MFD/Radar damage dolls
  verify_*.py       per-type round-trip verifiers

Verified round-trip across all 64 shared chassis:
  .armature      2938/2976 pages     .subsystems  7579/7585 keys
  .data map      6071/6071 values    .data trip   8291/8306 keys
  .damage        6605/6605 keys      .contents    7480/7480 keys
  .torso+.engine 1280/1280 keys      .instance     896/896 keys, 64/64 pages
  armature_parts 1202/1202 .data, 1149/1202 .video

Layout-discovery lessons (documented in DECOMPILING.md)
-------------------------------------------------------
- Never let a field map be discovered by the values that verify it. A
  value-matching pass reported 4288/4288 while mis-assigning 34 keys. The
  map was rebuilt from header declaration order, anchored on uniquely
  resolved fields.
- Read the factory, not the data. 12 .data fields and 5 Torso fields are
  declared plain Stuff::Scalar but multiplied by Radians_Per_Degree in
  Mech_Tool.cpp:889 / Torso_Tool.cpp.
- Strip typedefs before walking a header. A stray `typedef int AttributeID;`
  masked a missing ClassID - two 4-byte errors cancelling out, caught only
  by the ANCHORS assertion.
- A verifier that silently narrows its own input reports success. Braced
  blocks must be hidden before splitting pages, replacing both CR and LF,
  because a `Shadow={...}` block contains a line reading `[shadow]` and
  splitlines() also splits on bare CR.
- NSWIZZLE is undefined, so the #else branch is live and orders members
  differently. bool is 1 byte; char x[MaxStringLength] is 256.
- V4H carries stale Mech IDs (their Atlas is 5, ours 6), so 64 of 65 shared
  chassis are off by one; --retarget-ids emits $(M_<Chassis>)/$(IDS_<Chassis>).

reports/ holds generated diffs. The two ~5 MB manifest-*.tsv intermediates
are gitignored; regenerate everything with run-comparison.sh.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-08 16:47:57 -05:00
e088555b96 Fix content defects found while decompiling V4H packages
Three unrelated source-data bugs surfaced while round-tripping our own
Content/Mechs tree against the decompiled V4H packages. All three were
found because a decompiler verifier disagreed with the shipped data, not
by inspection.

Hellspawn / Sunder: unterminated section headers
-------------------------------------------------
  hellspawn.subsystems:101   [HeatSink10  ->  [HeatSink10]
  sunder.subsystems:120      [HeatSink16  ->  [HeatSink16]

The notation parser keys a page on the bracketed name. With the closing
bracket missing the page name is malformed, so that heat sink's block is
not registered as its own page and its Model/ExecutionState/
InternalLocation keys are absorbed by the preceding page. Net effect: one
heat sink silently missing from each mech (Hellspawn LeftTorso, Sunder
LeftArm), and the preceding sink's location keys overwritten.

CauldronBorn: undefined macro in TwistRadius
--------------------------------------------
  cauldronborn.torso:15      TwistRadius=$(OBSTUSE_TRADIUS)  ->  100

OBSTUSE_TRADIUS is a typo and is defined nowhere in the define tables;
the intended symbol is OBTUSE_TRADIUS (=140). An undefined macro does not
fail loudly - the factory substitutes its own default - so this shipped
for years as a silent fallback.

Resolved to the literal 100 rather than $(OBTUSE_TRADIUS) because the
compiled .torso record in the shipped package holds 100.0f, i.e. the
factory default that has always been in effect. Using 140 would change
long-standing behaviour; 100 preserves it and makes it explicit. Verified
against the packaged record: verify_smallmodel now reports 1280/1280 keys.

Lesson recorded: an undefined macro is not a build error here. Check what
the compiled package actually holds before "correcting" a symbol name.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-08 16:47:29 -05:00
77e19a723c mw4print: document known issues and research notes
Captures all findings from the 2026-08-07 debugging session:
print flow diagram, DoPrint error codes, which commits changed what,
hypotheses explored/ruled out, and next steps once the -debug log
is available.

Co-authored-by: Claude Sonnet 4.6 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-07 21:55:35 -05:00
3256c103a2 mw4print: add -debug flag for step-by-step diagnostic logging
Adds runtime diagnostic logging to track down why printing is broken.
Pass -debug (or /debug) on the command line; all output goes to
mw4print-debug.txt next to the exe (append mode, timestamped lines).
No effect on normal operation when the flag is absent.

What is logged:
- Startup: AssetsDirectory1 path and full command line
- OnCreate: DB config loaded, timer created
- DoCopyData / OnFileOpen: each file queued for printing with filename
- OnTimer: which file is being processed each second, LoadPR result
- DoPrint: nPlayers count, PrintDlg call + result (on failure logs
  CommDlgExtendedError hex code), DB export call/result, SetupFonts
  result, StartDoc result, EndDoc vs AbortDoc, final return code

The CommDlgExtendedError value on a PrintDlg failure will identify the
root cause (e.g. CDERR_NODEFAULTPRN = no default printer configured).

Co-authored-by: Claude Sonnet 4.6 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-07 21:53:02 -05:00
dicion 10301ee5dd Document external MFD and radar mapping work 2026-08-07 20:56:58 -05:00
dicion deafc2b01b Make Behemoth II inherit validated display mappings
Copy Behemoth's validated texuv2, offset2, texuv3, and offset3 rows from Mech ID 11 to Behemoth II at Mech ID 12. Both variants intentionally share the same external MFD and Radar geometry.

Add an inherited-mapping assertion to the J&J comparison generator so future runs fail if any Behemoth II coordinate row diverges from Behemoth. Record the passing inheritance check in the generated summary.

Document the inherited variant relationship and effective mapping count in MFD-RADAR-MAPPINGS.md.

Compare hud/behemothii.bmp and radar/hud/behemothii.bmp against the corresponding Behemoth runtime assets. Both 512x512 grayscale images are already pixel-identical, so no hsh art files are copied or replaced.
2026-08-07 20:52:00 -05:00
dicion 9755803949 Fix external MFD and radar damage mappings
Install the validated J&J coordinates for 19 external display sets across 13 chassis. Update all affected texuv2/offset2 and texuv3/offset3 rows while preserving the 65-mech positional layout and coord.cpp CRLF encoding.

Add a reusable Pillow comparison generator, 38 red/green review maps, and a generated summary. The final audit reports 19 exact mappings, zero differences, zero input warnings, and seven display sets without complete supplied inputs.

Normalize four unambiguous measurement transcription issues: Behemoth MFD LT 1742 to 174, Behemoth Radar CT punctuation and blank S2, and Fafnir MFD LL punctuation.

Document the complete authoring and runtime workflow, including legacy tuple normalization, MFD/Radar scaling, odd-coordinate handling, canonical runtime names, pixel-versus-byte BMP comparison, validation checks, rebuild requirements, and the final installed-set inventory.

Compare every supplied exploded runtime BMP against Gameleap/mw4/hsh. All 19 are already pixel-identical, so no runtime art files are replaced.
2026-08-07 20:46:32 -05:00
dicion 13675c8cf4 move important documentation to main folder. 2026-08-07 13:38:39 -05:00
8bfaf9b9ad Publish V5.1.0b_RC2: rebuilt binaries, repacked props, RC2 documentation
RC2 build, verified to launch on the Windows box and copied back into the
deployment at MW4/. Supersedes RC1 (a0331e78), which predates everything in
sections 12-17 of the test checklist.

Binaries and content
--------------------
* MW4.exe / MW4pro.exe rebuilt from CoreTech GameOS + MW4Application. Carries
  the display work that landed after the RC1 build: -tident, the full
  gos-displays.txt start-up trace with the CLASH and CTCL-type reports, the
  HSH_EnterFullScreen2 panel re-entry fix, the -tmr cameraship ladder, and the
  updated -help text.
* Launcher / autoconfig / mw4print / ctcls / MissionLang / ScriptStrings
  relinked in the same pass.
* props.mw4 + props.dep fully repacked (deleted first, not incremental), so the
  23-entry time list, the restored 7-minute default and the V5.1.0b2 console
  title are actually in the package rather than only in the source tree.
* mw4-help.txt regenerated from the new exe; the diff is the proof the built
  binary carries the documentation changes.

Documentation, renamed to RC2
-----------------------------
* RELEASE-NOTES-5.1.0b_RC1.{md,html} -> ..._RC2.{md,html}, both hand-maintained
  in step, ASCII + CRLF so they open correctly in Notepad on a pod.
  - New "Already testing RC1?" block at the top. RC1's notes already described
    -tident and the CLASH report, but the RC1 BINARY does not contain them, so
    anyone comparing the two needed that stated explicitly.
  - Time limits corrected to 23 entries (1-15, 20, 25, 30, 45, 60, 120, 180,
    240) with the 7-minute default restoration called out.
  - New section on the cameraship Map/Armor screen: background but no overlays
    is -ctcltype 2 on a cameraship, not a video card. Includes the
    "CTCL type =" log line and -tmr 3 as the follow-up check.
  - -fps description corrected: the per-second column is a 5% low and the 1% /
    0.1% lows are in the session summary. The old text described behaviour that
    had already been changed.
  - Switch table gained -tmr; known issues gained stereo-only audio and the
    single-monitor -tident caveat; upgrade checklist now names V5.1.0b2.
* testing-checklist-5.1.0b1.txt -> testing-checklist-5.1.0b_RC2.txt, with a
  build-requirements header and new sections 12-17 covering -tident, the
  display trace (including a deliberate -tmon clash to exercise the CLASH
  report), the panel re-entry fix, cameraship/-tmr, 240-minute missions and a
  -help verification pass.
* OPTIONS-INI.md: TimeList_Index is documented as no longer driving the console
  lobby default (the script uses a literal since the list was expanded) while
  TimeList_Value remains live; added a table of the files the game writes next
  to MW4.exe. Also repaired the CP949/CP1252 damage in that file - it carried
  literal 0xA1 0xE6 arrows, 0x97 em dashes and ~20 '?' characters where dashes
  had been lost. Now pure ASCII.
* README.md points at the RC2 notes.

Source
------
MW4Application.cpp help text: -fps now matches what gos-fps.txt actually
prints, and -ctcltype states that a cameraship must use 3 and what goes wrong
when it does not. Audited all 80 switches parsed in the file against the help
array - none missing, and no game-facing switch is parsed anywhere else.

Deployment housekeeping
-----------------------
* dgVoodoo.conf: ScalingMode = stretched_ar, which is the setting the release
  notes require and which fails SILENTLY when wrong. FPSLimit was left at 20
  from bench testing and would have shipped a 20 fps cap to every Win10/11 pod
  - raised to 60.
* banner.txt reset from a test string to the shipped placeholder.
* Removed two stray screen000*.bmp captures and the duplicate dbstruct.txt
  (db_schema.sql is the current name, per b4089291).
* .gitignore: gos-displays.txt and gos-fps.txt are truncated on every launch
  and are per-machine, so they are no longer mirrored.

Not yet done: multi-monitor pod testing of this binary. -tident, the re-entry
fix and -tmr have not been exercised on real MFD hardware - that is what the
new checklist sections are for.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-07 13:07:43 -05:00
9bf49a8084 Bump console title to V5.1.0b2
Console lobby now reads "BattleTech Console V5.1.0b2", covering the
240-minute time limit work and the default-time fix on this branch.

Also corrects the testing checklist, whose section 5 still asserted the
V5.1.0b1 title and an 18-entry time list -- both would now fail on the
bench. Added a check for the 7-minute default, which was silently
landing on 4 before this branch's TIME_LIST_DEFAULT fix.

Content only, no MW4.exe rebuild. ConLobby.script lives in props.mw4, so
this needs build-env\build-resources.ps1 to appear in game.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-06 16:22:17 -05:00
136e2ff05c Extend MP mission time limit to 240 min (from LYLT 3514acf6)
Time-limit pick lists go to 23 entries: 1-15, 20, 25, 30, 45, 60, 120,
180, 240. Content + console only, no MW4.exe rebuild -- nothing between
the dropdown and the mission timer clamps the value, and m_gameLength is
8 bits on the wire (MWApplication.cpp:790), so 255 was always the ceiling.

Hand-applied rather than merged. LYLT reverted aa500be7 (9aa317ea) and
built on the original 9-entry list, while this branch carries the fixed
18-entry version from 456e1978, so the two sides have different bases.
Merging the branch would also drag in that revert of the Battlemaster and
Behemoth loadout work.

Two deliberate deviations from 3514acf6:

- Its max_displayed restructure is NOT taken. Both sides independently
  reached the same logic (i==5 -> 10, i==6 -> 4, else 16), but LYLT wrote
  the outer else without braces. That bare else-then-if is the construct
  behind the null-reference console lobby crash in the aa500be7
  regression; 456e1978 already fixed it here. Ours is kept.
- HostLobbyMission.script was untouched on this branch so LYLT's version
  is taken wholesale, but its else block (i==5 / i==11) is re-braced for
  the same reason before it can bite the PC host lobby.

Fixes a real bug this branch already had: TIME_LIST_DEFAULT was still the
C++ global g_nTimeList_Index (3), which indexed "7" in the old 9-entry
list but indexes "4" in the expanded one -- the default mission time had
silently become 4 minutes. Now the literal 6, which is "7" in both the
18- and 23-entry lists. Kept as a literal so the script stays independent
of the exe; the reset-to-defaults path matches by value (g_nTimeList_Value
= 7) and needed no change.

The rest scales on its own: drop_list_size[5], the doh loop bounds and the
nselected fallbacks in ConLobbyMission all already used TIME_LIST_COUNT /
TIME_LIST_DEFAULT symbolically. The DEMO_CODE list (11 entries, default 5)
is untouched. Verified 23 contiguous entries and balanced braces in both
files, no unbraced else-if left, CRLF and us-ascii preserved.

Not yet repacked: these live in props.mw4, so run build-env\build-resources.ps1
on the Windows box, deleting resource\props.mw4 + props.dep first to force a
full repack (incremental carries stale entries forward). Not run on a real bay.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-06 14:51:01 -05:00
020dc04612 Add HD-AUDIO.md: multi-channel audio investigation notes
Research only, no code changes. Captures why the pods play in stereo and
what it would take to drive a 4-speaker (quad) rig on Win10/11.

Key findings:
- "Channels" is ambiguous in this codebase: 32 mixer voices (Adept splits
  them 1 VO / 2 music / 1 Betty / 6 mechanical / 21 spatialized SFX) versus
  the speaker output, which is hardcoded stereo at 22050 Hz.
- The blocker is DS3DALG_HRTF_LIGHT, a two-speaker head model, requested
  unconditionally because dwFreeHw3DAllBuffers is always 0 on Vista+ where
  DirectSound hardware acceleration no longer exists. hardwaremixing=true
  is therefore inert, and the EAX reverb path has been dead since XP.
- GameOS has a full gosAudio_SetSpeakerConfig API that nothing ever calls.
  GetSpeakerConfig also has no 5.1 case, so it returns 0 on such systems.
- Cheapest route is DSOAL (drop-in dsound.dll over OpenAL Soft) plus
  hardwaremixing=true, which re-enables the existing hardware-3D branch
  with no code change. Verified in source that Libraries.cpp:376 calls
  LoadLibrary("dsound.dll") unqualified, and dsound.dll is not a KnownDLL,
  so a local copy next to MW4.exe wins over System32.
- Same deployment rule as dgVoodoo2: per-machine Win10/11 prerequisite,
  must NOT be committed, XP pods need the native dsound.dll.

Also documents the reverted fallback design (a third NO_VIRTUALIZATION
branch, a -tspk override and a gos-audio.txt trace) as a re-implementation
recipe, plus a verification plan and caveats.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-06 14:50:42 -05:00
b3c5ecfe56 CLAUDE.md: note two -tident/clash-check gaps found on a single-monitor VM
Parked, not fixed. Diagnostics-only and impossible on a real pod, but the
second one produces a confidently wrong "all clear" on exactly the kind of
machine someone would bench-test with, so it is worth recording.

Both stem from one physical monitor being exposed as two DirectDraw
devices. The NULL-device merge is gated on NumMonitors>=2 - deliberately,
it is a multi-monitor fixup - so a single-monitor box keeps the alias in
slot 0 alongside the real device.

1. -tident overpaints itself. Device 0 (NULL alias) and device 1 (the real
   output) resolve to the same screen, so it paints red/"1" and then
   immediately green/"2" over the top. Observed on a VM as a brief flash
   before the second number appears. Behaving as written; there simply is
   not a second screen to paint. Should detect devices sharing an HMONITOR
   and label them together rather than painting sequentially.

2. The consistency check compares device INDICES, not resolved monitors.
   On the same VM it reported "No duplicate device assignments" while
   main -> device 0 (primary alias) and radar -> device 1 (\\.\DISPLAY49)
   were the same physical monitor - the exact collision the check exists
   to predict. Fix would be to treat a device with no HMONITOR as the
   Windows primary and compare resolved HMONITORs.

Context for the VM in question: RDP session (Microsoft Remote Display
Adapter, 1870x985) with the Microsoft Basic Render Driver and no real GPU,
dgVoodoo2 masquerading as its default emulated NVIDIA device. MFD modes
cannot work there regardless - only two DirectDraw devices exist and one
of them is an alias.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-05 17:37:09 -05:00
22495d7245 Docs: research notes on making -window work for all display modes
Research only. Nothing implemented, nothing scheduled. Captured now so the
investigation does not have to be repeated when we come back to it.

New file: WINDOWED-MODE.md (repo root), indexed from README.md and the
CLAUDE.md next-steps list.

Goal being assessed
-------------------
Make -window work for every pod configuration, so a full pod (main + radar
+ MFDs, or a cameraship pair) can be tested on a single monitor as a set
of moveable windows. Target order: console (already works), cameraship,
-tmfds 1, -tmfds 4. -tmfds 3 deliberately out of scope.

Headline finding
----------------
-window does NOT fail for the MFD and cameraship modes - it silently
DISABLES them, and the reason is a single line:

  MW4Application.cpp:1373
    use_shgui = Environment.fullScreen ? 1 : 0;

use_shgui is the master switch for the entire secondary-panel subsystem
and is derived from fullscreen, so with -window it is 0 and
DXRasterizer.cpp:1131 never calls HSH_EnterFullScreen2(). No panel is ever
created. Console mode only appears to work windowed because it has no
panels to lose.

What the document covers
------------------------
* Why it behaves the way it does today, with the exact gating code.
* What already exists in our favour: the main display is a working
  windowed reference implementation (primary + clipper + offscreen
  backbuffer, presented with Blt); the clipper wrappers are already in
  use; and none of the panel DRAWING code would change, because panels
  render to an offscreen target and composite - they are indifferent to
  whether the present is a Flip or a Blt.
* The six things that must change, with quoted code: decouple use_shgui;
  windowed variants of CHSH_Device::InitFirst (DDSCL_NORMAL, no
  SetDisplayMode, per-panel HWND) and InitSecond (plain primary + clipper
  + offscreen backbuffer, D3D device on the offscreen); one window per
  panel; 8 Flip->Blt sites in WinMain.cpp; and explicit frame pacing.
* Frame pacing is called out as the item that will actually bite: sh_step
  advances once per frame and drives MFD channel cycling, so an uncapped
  windowed rig would not faithfully represent pod behaviour. Same root
  cause as the known mechlab fast-spin bug.
* Reference tables: every panel class with its InitFirst location, device
  slot and resolution; the SwapRightState member-swap mechanism that any
  windowed work must keep intact; and the full lifecycle/gating map.
* Suggested phasing, with radar-only as the decisive Phase 1 experiment.

The risk that decides it
------------------------
Windowed D3D7 device creation is per-GPU, and this would need four
windowed devices in one process. Precedent runs both ways: the mission
editor hit DDERR_INVALIDOBJECT creating a windowed D3D device on Win11 and
needed DDrawCompat (STEP 8), while the game's own windowed main display
succeeds on the W4100. Nothing has proven four. If it fails, the fallback
means rewriting the panel RENDER path, not just its present - a
substantially bigger job.

Why this may be worth more than a test convenience
--------------------------------------------------
Windowed mode removes exclusive-mode contention entirely, which is the
whole reason dgVoodoo2 is currently mandatory for every multi-display
configuration on Windows 10/11 (STEP 10). If windowed panels work
natively, that is a route to dropping the dgVoodoo2 prerequisite WITHOUT
breaking the XP pods, since it needs no external DLL on either OS. Same
groundwork as the borderless-windowed migration already parked in STEP 10.

Note on citations
-----------------
All 22 file:line references were derived directly and verified to resolve
to the expected symbol. An exploratory pass had produced line numbers that
were substantially wrong (CHSH_Device::InitFirst reported at 717, actually
627; IsMultimonitorAvaliable at 2178, actually 2961), and a claim that the
radar renders at 480x640 rotated - the code passes 640,480 like the
others. Worth knowing before trusting generated citations in a document
intended to outlive the session.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-05 17:31:09 -05:00
4e04fd1fb2 Displays: -tident identify mode, panel re-entry fix, desktop-order log
Follow-up to the gos-displays.txt enumeration trace. The expanded log
arrived from the Intel tester and settled the failure - and disproved the
hypothesis it was written to test.

What the log showed
-------------------
The consistency check printed "No duplicate device assignments", so the
suspected -tmon clash was NOT the cause. The actual evidence was that
HSH_EnterFullScreen2 runs TWICE: the first entry brings every panel up
DD_OK, the second fails on the same device with
DDERR_EXCLUSIVEMODEALREADYSET and then DDERR_NOCOOPERATIVELEVELSET.

Two independent causes, one operational and one a real defect.

Cause 1 - Windows Display Settings numbers are not DirectDraw indices
--------------------------------------------------------------------
The operator set -tmon by reading the numbers off the Display Settings
arrangement diagram. On that machine all three numbering systems differ:

  Settings 3 -> \\.\DISPLAY1 -> device 0   (primary, 800x600, main)
  Settings 4 -> \\.\DISPLAY4 -> device 3   (USB adapter, radar)
  Settings 2 -> \\.\DISPLAY2 -> device 1   (mfd1)
  Settings 1 -> \\.\DISPLAY3 -> device 2   (mfd2)

A permutation with two accidental fixed points - no derivable rule, and
the Settings ordinal is not exposed by any documented API, so it cannot
be translated in code. -tmon 1,4,2,3 worked first try (user-confirmed).
This is why Windows ships an Identify button rather than publishing the
mapping.

Cause 2 - panels re-opened without being released (REAL BUG, all machines)
-------------------------------------------------------------------------
EnterFullScreenMode() calls HSH_EnterFullScreen2() on every mode change
and every lost-front-buffer recovery, but the teardown
HSH_DirectDrawRelease2() was only wired to DirectDrawRelease(), i.e. full
shutdown. CHSH_Device::InitFirst() therefore overwrote pDD with a fresh
IDirectDraw7 while the previous one still held exclusive fullscreen,
leaking it and its exclusive claim for the life of the process.

It only bites when a panel sits on the Windows primary: secondary outputs
grant exclusive mode again, the primary does not. Every working pod
happens to have the main display on the primary, so no panel is ever
there - which is the whole reason this looked hardware-specific.

FIX: HSH_EnterFullScreen2() now releases first when hsh_initialized ||
hsh_mrdev_initialized. Tagged [panelreinit]. Reuses the existing teardown,
which already restores the display mode, drops the coop level and clears
the flags for both the MFD/radar and cameraship paths.

New: -tident, the game's own Identify
-------------------------------------
MW4.exe -tident [3..120, default 20] fills every display with a distinct
colour and prints, huge, the number to type into -tmon, plus its device
index and the role currently assigned to it. Then exits.

Deliberately uses DDSCL_NORMAL and paints via GDI on the primary surface:
no exclusive mode, no display mode change. Taking exclusive fullscreen on
several devices at once is the very failure being diagnosed, and a
diagnostic that trips over that fault is worthless - this works even on a
pod where the MFD modes are broken.

It opens the real DirectDraw devices rather than positioning GDI windows
by HMONITOR, so it proves the device-index -> physical-output association
through the same path the panels use. Positioning by DirectDraw's own
reported HMONITOR would be circular.

Implemented as IdentifyDisplays() in VideoCard.cpp, called at the end of
FindVideoCards() followed by ExitProcess(0). Everything it paints is also
written to gos-displays.txt, so the mapping survives even if a monitor is
dead.

Also added: desktop-order block
-------------------------------
gos-displays.txt now prints the monitors sorted left-to-right by desktop
position with their device indices, which maps directly onto the picture
in Display Settings. For the reporting machine it reads out as
-tmon 1,4,2,3 with no derivation required.

Documentation
-------------
* -help: new -tident entry. The -tmon text now states outright that these
  are NOT Display Settings numbers and points at -tident. Its old
  "-tmon 1,2,3,4" example was actively inviting the mistake that caused
  this report, so that section was rewritten rather than appended to.
* Release notes (md + html, both hand-maintained): -tident section with
  sample output and the reason it has to exist; -tmon warning; new
  sections for the CLASH report and the re-entry fix in operator terms;
  switch-table row; expanded log description with the copy-before-relaunch
  warning; upgrade-checklist step to run -tident once after upgrading.
* CLAUDE.md: STEP 12 with the full engineering record, including the
  three-way numbering table and both causes.
* OPTIONS-INI.md: videodriverindex note now points at -tident.

Cost
----
-tident is opt-in and exits immediately after. The desktop-order block is
a handful of extra startup writes. Nothing added is reachable from the
frame loop.

Testing
-------
-tmon 1,4,2,3 confirmed working on the reporting machine, which validates
the diagnosis. The -tident and re-entry changes are NOT yet built or run;
both need a rebuild of MW4.exe (Release + Profile) as they touch CoreTech
GameOS. Worth checking on the W4100 bench first that
CreateSurface(PRIMARYSURFACE) under DDSCL_NORMAL succeeds on secondary
devices through dgVoodoo2 - if a panel comes up blank the log names the
failing call.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-05 15:00:37 -05:00
2e85cd2066 GameOS: full display enumeration trace in gos-displays.txt
Motivation
----------
A tester's 3-display Intel integrated-graphics box (main + 2 MFDs on the
iGPU, radar on a USB adapter) failed MFD bring-up with the second MFD
panel returning DDERR_EXCLUSIVEMODEALREADYSET, while the same build works
on a W4100 and on a Quadro. The old report showed only the final device
list and role numbers, which was not enough to tell whether the cause was
device count, capability, index shifting, or a duplicate assignment.

The report now traces every stage of display discovery in the order the
game performs it, so the cause can be read off the file instead of
inferred.

What gets logged
----------------
* Header: executable path, full command line, and the requested -tmfds /
  -tmon values already converted from the 1-based command-line form to
  the 0-based DirectDraw indices the code uses. That conversion is an
  easy off-by-one to miss: "-tmon 1,..." means device 0.

* Windows desktop topology via EnumDisplayDevicesA + EnumDisplaySettingsA
  - every \\.\DISPLAYn adapter, its friendly name, attached/primary
  state and current mode. Stated explicitly so it can be compared
  against the DirectDraw order, which is NOT the same ordering.

* Stage 1, enumeration callbacks: each callback as it fires, with driver
  description, driver name, GUID, and the HMONITOR resolved through
  GetMonitorInfoA to \\.\DISPLAYn plus desktop rect and primary flag.
  Entries with no HMONITOR are named as the primary-display-driver alias,
  noting that NumMonitors is not incremented for them.

* Stage 2, capability check: per device ACCEPTED (with assigned index and
  whether a HAL was found) or REJECTED.

* Device table printed THREE times - before the NULL-device merge, after
  it, and final. Each entry shows description, driver, DeviceGUID,
  guidDeviceIdentifier, vendor/device IDs, hardware-rasterizer flag and
  its monitor. Followed by an adapter grouping listing which device
  indices belong to the same physical card, since guidDeviceIdentifier
  identifies the ADAPTER and not the output - which is precisely why a
  single card and a mixed iGPU + USB adapter setup enumerate differently.

* Stage 3, the NULL-device merge: whether the NumMonitors>=2 guard
  passed, which device matched the primary's adapter GUID, and every
  index shift printed individually. If nothing matches, it says so
  loudly - slot 0 then remains the alias and any role pointed at device 0
  lands on the Windows primary monitor.

* Stages 4-7, role selection: span / main / radar / MFD searches, each
  printing the device chosen AND the reason every skipped device was
  skipped. Then the -tmon overrides, each showing what auto-detection had
  chosen and what the override replaced it with, or why it was rejected.

* Final summary: role -> device -> physical monitor, plus a consistency
  check that reports two roles landing on one device as an explicit
  CLASH, with the explanation that the second panel will fail with
  DDERR_EXCLUSIVEMODEALREADYSET. That is the suspected failure on the
  Intel box and nothing in the old log pointed at it.

Fixes
-----
* Monitor/device desync: the NULL-device merge shifted DeviceArray but
  nothing tracked which monitor each slot drove. g_ahDevMonitor[] is now
  shifted in lockstep; without this every monitor attribution after a
  merge would be off by one, i.e. confidently wrong rather than absent.

* HMONITOR is now retained. The Ex enumeration callback is the only place
  the device-to-monitor association is available and it was being
  discarded, so videoDevices gained an hMonitor field and BufferDevice
  takes it.

Behaviour
---------
No functional change. Every condition, code path and assignment is
unchanged - only logging was added, plus the monitor-array shift above.
-tmon still applies each slot independently and still does not reject
duplicates; the duplicate is now merely visible. Whether to make it an
error is a separate decision, pending test results.

Cost
----
Startup only. Nothing added is reachable from the frame loop, so there is
no runtime cost of any kind. Roughly 255 writes for a 4-monitor pod, at
~25-130 ms total on a warm cache - inside the noise of FindVideoCards()
itself, which creates DirectDraw objects and enumerates every display
mode per device. Worst case is antivirus on-access scanning of each
CreateFile, which could reach ~1 s; a folder exclusion removes it.

Lines remain open-append-close so the report survives a hard crash during
panel init, which is exactly when it is most needed. gos-fps.txt is the
buffered log; this one deliberately is not.

Portability
-----------
GetMonitorInfoA and EnumDisplayDevicesA are resolved with GetProcAddress
and the MONITORINFOEXA / DISPLAY_DEVICEA layouts are declared locally, so
nothing depends on the 1998 SDK headers carrying multimon support. If the
APIs are unavailable the report degrades gracefully instead of failing to
build. ENUM_CURRENT_SETTINGS is defined defensively.

Testing
-------
Not yet built. Requires a rebuild of MW4.exe (Release + Profile) as this
is CoreTech GameOS. Note the report is truncated on every launch - a
failing run's log must be copied before relaunching.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-08-05 10:07:11 -05:00
dicion e912c58843 README: rewrite with project background and history
Add Background section covering what FireStorm is, the full VWE /
BattleTech Center history from ESP (1988) through Tesla II FireStorm
(2002) and community continuation, and a summary of V5.1.x changes
since the 5.07D official release.

Reorganize build instructions under a dedicated 'Building from source'
heading. Demote Outputs / Design / _UNUSED from top-level headings to
subsections under the layout section.
2026-08-02 19:57:18 -05:00
dicion 07750da2fa Delete daf52e2c-8107-4f40-88df-a2744c8861f6.mr 2026-07-31 09:39:20 -05:00
24ce46ab6d Cameraship Map/Armor diagnostics: -tmr ladder, draw counter, CTCL type log
Investigating a field report that the cameraship secondary armor/score/map
screen showed its background BMP but no overlay graphics "on certain video
cards". The video card was a red herring: the machine was launched with
-ctcltype 2 (game pod) instead of 3 (cameraship), and the overlay block in
hudchat.cpp is gated on CTCL_GetType()==_ECTCL_CameraShip, so it never ran.

This is hard to spot because the two gates are independent. HSH_EnterFullScreen2
and CMR_Device open the screen and paint its background based purely on whether
a spare secondary monitor exists, and never consult the CTCL role. A wrong
-ctcltype therefore yields a screen that lights up, shows the correct artwork
and renders nothing, which is visually identical to a DirectDraw failure. The
same background-only screen also appears on a game pod when an MFD mode finds no
dual-head span and falls through to the single-secondary mr_device path.

Diagnostics added:

- render.cpp: log the CTCL type in gos-displays.txt from HSH_EnterFullScreen2,
  via the existing g_pfnCTCL_GetType hook so GameOS gains no game-code
  dependency. Always logged, and states in words whether overlays will be drawn.
- render.cpp/.hpp: CHSH_Device::m_nDrawCalls, incremented in DrawQuad,
  DrawThickFrame and DrawTexture, reset in CMR_Device::BeginScene and reported
  for the first 5 frames from CMR_Device::EndScene when -tmr is active. Zero
  draws proves the overlay code never ran; non-zero with a blank screen would
  mean it ran and drew invisibly. This distinction is what settled the case.
- render.cpp: log CMR_Device surface creation plus the overlay texture's
  dimensions, depth and channel masks.
- New -tmr <0-3> switch (MW4Application.cpp, documented in -help): 0 normal,
  1 background blit with DDBLTFAST_WAIT|DDBLTFAST_NOCOLORKEY, 2 Clear instead of
  the background blit, 3 as 2 plus alpha blending off and an untextured magenta
  DrawQuad.

Mode 3 drew magenta and mode 2 was pure black, which cleared the 3D device, the
flip and the background blit in two runs; the draw counter then reported 0 and
pointed straight at the gate. Documented as STEP 11 in CLAUDE.md, including the
field triage rule: read "CTCL type =" in gos-displays.txt before suspecting
drivers.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-26 11:59:56 -05:00
dicion b40892910b fix filename for db structure file to match the new naming convention. 2026-07-26 10:35:29 -05:00
0f901f71d5 Release notes: document which -tmon positions apply in each MFD mode
-tmon has four positions (main, radar, MFD1, MFD2) but only the first two do
anything in the spanned modes. Positions 3 and 4 write to the mode 4 MFD device
slots, which -tmfds 1 and -tmfds 3 never read -- they drive both MFDs from one
wide display instead. The switch accepts them either way, so an operator could
reasonably assume they had taken effect.

Also states what was previously undocumented: the spanned MFD display itself
cannot be assigned with -tmon at all. It is chosen automatically as the first
display advertising a 1280x480 mode, which is a distinctive enough signature that
it normally lands correctly. Noted that it can be added if anyone needs to force it.

Adds a per-mode table, a pointer to gos-displays.txt for diagnosing a display that
lands in the wrong place, and a qualifier on the -tmon row of the switch summary.

No code changes -- documentation only.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-26 10:23:42 -05:00
7fc9089aad Clarify dgVoodoo2 scope: any second display, cameraship included
The dgVoodoo2 requirement was written as an MFD-mode problem. It is not. The
limitation is per SECONDARY DISPLAY, not per feature, so it applies to every mode
that lights up a second monitor -- including cameraship mode, which uses two
displays and no MFDs at all.

Console mode is the only configuration that does not need dgVoodoo2, because it is
single-display.

This matches the code: IsMultimonitorAvaliable() returns false for
_ECTCL_CameraShip, so cameraship falls through to IsSecondaryMonitorAvaliable()
and the single-secondary mr_device path -- which is still a second exclusive
IDirectDraw7, and therefore still refused by modern Windows for exactly the same
reason as the MFD panels.

Release notes updated in all three places it appears -- section heading and opening,
the known-issues entry, and upgrade checklist step 4 -- so an owner running
cameraship pods cannot read the requirement as "only applies to MFD setups" and
skip it. CLAUDE.md STEP 10 and repo memory corrected likewise, since both stated
the narrower "-tmfds 1/3/4" scope.

Both release-notes formats regenerated; still plain ASCII with CRLF.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-26 10:20:10 -05:00
7f7f98d992 Release notes: soften dgVoodoo2 wording, note -tbaud is hardware-limited
Two wording corrections from the project owner, plus repair of three characters
left mangled by the earlier CP949 encoding problem.

1. dgVoodoo2 known-issue entry. "This is a Windows limitation, not a bug we can
   fix" overstated it -- it reads as permanent. Replaced with a statement of what
   dgVoodoo2 actually does (works around a limitation on exclusive full-screen use
   of multiple displays on platforms after Windows XP) and that a future release
   may address it. The borderless-windowed design assessed in CLAUDE.md STEP 10 is
   exactly such a route, so leaving the door open is the accurate framing.

2. -tbaud. The notes implied any rate in the 9600-921600 range simply works. The
   software will set any of them, but the achievable rate is bounded by hardware at
   both ends: the serial UART in the pod PC and the RIO or replica RIO board. Added
   that, with the symptoms of an unsupported rate (garbled input, dropped buttons,
   no response) and the advice to step down. Without it an owner could conclude a
   high rate is broken in the game when it is the UART or board refusing it.

Also repaired three leftovers from the CP949 encoding issue: two em dashes at ends
of lines that the earlier pass missed because it only matched dashes surrounded by
spaces, and the warning glyph on the MySQL export note, which had become a literal
"??" and is now a WARNING: label. It renders as a styled warning callout in the
HTML alongside the XP one, so both hazards look like hazards.

Both formats regenerated and verified: 0 non-ASCII bytes, 0 remaining artefacts,
2 warning and 5 note callouts in the HTML.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-26 10:16:00 -05:00
a0331e78d2 Compiled Build 5.1.0b_RC1 Files
Deployed game tree copied back from the Windows build machine, containing the
compiled 5.1.0b_RC1 binaries and every asset the deploy produces. 846 files:
21 added, 825 modified.

Freshly built binaries: MW4.exe, Launcher.exe, autoconfig.exe, ctcls.dll,
MissionLang.dll, ScriptStrings.dll, Language.dll and the rest of the runtime DLL
set, carrying everything merged this cycle -- the 16-pilots-plus-cameraship launch
fix, the MFD mode 4 split-display support and its stutter fix, -tmon, -tcoop, -fps,
-tbaud, the Load File console lobby feature, configurable Rookie Mission, and the
English Language.dll build.

New in the deployment:
- set-appcompat.bat / set-appcompat.ps1 -- the one-click AppCompat shim installer,
  now shipped by deploy-mw4.ps1 so any copy of an install can repair its own
  registration (the layer is keyed on the exe's full path).
- libmysql.dll -- required by mw4print 2.0's MySQL export, late-bound at runtime.
- Four hsh art files restored from 5.0.7D: MFD/assassin2.bmp, Mechs/battlemaster
  iic.bmp, Mechs/behemoth ii.bmp, Mechs/mad cat mkii.bmp.
- banner.txt, dbstruct.txt -- mw4print banner text and the exported DB structure
  reference.

dgVoodoo2 is included as MW4/dgvoodoo2_files/ -- deliberately in a subfolder and
NOT alongside the executable. Nothing loads from there, so the files are available
for a pod owner to install on Windows 10/11 without being active by default. This
keeps XP pods safe: they use native DirectDraw, and having dgVoodoo2 loose in the
game folder would break them. Do not move these files up a level in the repo.

Runtime leftovers from testing on the build machine are included as well
(DDrawCompat logs, DebugLog.txt, mw4-help.txt, two screenshots), consistent with
this repo's stated purpose as a full disaster-recovery mirror in which nothing is
excluded on purpose.

All binaries and art are stored via Git LFS per .gitattributes; verified that
MW4.exe, libmysql.dll, the dgVoodoo2 DLLs and the screenshots staged as LFS
pointers rather than raw blobs.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-25 19:52:43 -05:00
1b931d5fb8 Correct dgVoodoo2 guidance: never ship it, XP pods must not have it
Two corrections from the project owner.

1. dgVoodoo2 must NEVER be added to the repo or the deploy script.

The pod fleet is mixed. XP pods use native DirectDraw and need nothing extra --
installing dgVoodoo2 on them BREAKS them. Only Windows 10/11 pods need it, and
only for the MFD modes. It is therefore an installed-per-machine, OS-dependent
prerequisite that the pod owner sets up, not a build artifact.

This reverses the recommendation added in the previous commit, which suggested
versioning the working dgVoodoo.conf and having deploy-mw4.ps1 place it. That
advice was wrong: it would have pushed a Win10-only dependency onto every XP
machine. dgVoodoo.conf and dgVoodooCpl.exe were removed in 0ceba9c7 deliberately
and must stay out.

- CLAUDE.md STEP 10: the "TODO: commit it" note is replaced with the reasoning,
  flagged so nobody re-adds the files later thinking it was an oversight.
- CLAUDE.md Next steps: that TODO is removed. The borderless-windowed item now
  notes it is the only route that removes the dgVoodoo2 prerequisite WITHOUT
  breaking the XP pods, since it needs no external DLL on either OS.
- Release notes gain an explicit warning callout: dgVoodoo2 is not part of the
  build and must not be installed on XP pods, with the advice to configure it per
  machine rather than inside the game folder that gets copied around. Without this
  an owner with a mixed fleet could reasonably have copied a working Win10 install
  onto an XP pod and broken it.

2. Version numbering is intentional, not a mismatch.

V5.1.0b<n> denotes beta build n; these are handed out as release candidates for
testing. The "b" is dropped and it becomes 5.1.0 when the release is final. So the
console string V5.1.0b1 alongside a release named V5.1.0b_RC1 is expected, and the
earlier suggestion to reconcile them is withdrawn.

Both release-notes formats regenerated; the XP warning renders as a warning
callout in the HTML. Both remain plain ASCII with CRLF.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-25 19:47:26 -05:00
10dab8f2d5 Add V5.1.0b_RC1 end-user release notes (markdown + HTML)
Release notes covering the 58 commits since dbcf4052 (-tbaud), written for pod
owners rather than developers: what changed, what the defaults are if they touch
nothing, and how to switch on anything new.

Two formats. The HTML is generated from the markdown so the two cannot drift, and
carries the same inline styling as BTFrstrm/autoconfig-file-spec.html -- no external
files, opens in any browser, prints cleanly. Both are plain ASCII with CRLF so they
open correctly in Notepad on a pod.

Structure leads with the two things that will otherwise generate support traffic:
running set-appcompat.bat after install (the AppCompat layer is keyed on the install
path, and its absence produces the misleading "Another application is preventing use
of full screen mode"), and the dgVoodoo2 scaling mode, which fails silently when set
wrong. Every command line switch is listed with an explicit "if you leave it off"
column.

Facts checked against the source while writing, and corrected:

- [RookieMission] key names. The first draft documented RookieMission=, RookieGameType=
  and so on -- the internal variable names. The game actually reads MissionName,
  GameType, TimeLimit, Visibility, Weather, TimeOfDay, Radar, HeatOn, FriendlyFire,
  SplashDamage, UnlimitedAmmo, WeaponJam, AdvanceMode, ArmorMode. Anyone following the
  draft would have edited options.ini and seen no effect with nothing to explain why.
  The section now carries the full annotated block verbatim from options.ini, and all
  14 documented keys are machine-verified against MW4Shell.cpp.

- [automaticmode] needs TWO keys, not one. automaticmode=1 shows the Load File button;
  automaticfile=<path> tells it what to load. Without the second, CTCL_LoadAutoFile
  returns immediately -- the button appears and silently does nothing. Both are now
  documented in a table, with the path rules (bare filename resolves next to MW4.exe,
  or give a full path).

- dgVoodoo2 is required on Windows 10/11 for EVERY MFD mode, including the original
  spanned display (-tmfds 1 and 3), not only the new split mode (-tmfds 4). The first
  draft implied it was specific to the new feature, which would have led owners running
  the span to skip it.

- The mw4print MySQL export is flagged as included but not yet tested against a live
  server. Everything else in the notes is confirmed working by the project owner.
  libmysql.dll ships in the deployment (Gameleap/mw4, kept by the deploy's root *.dll
  rule and not on the skip list), so there is nothing for owners to download.

Also repaired 23 em dashes that had been written as literal '?' characters, including
in headings -- a side effect of the workspace saving new files as CP949, which cannot
represent them. Both files are now pure ASCII.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-25 19:22:54 -05:00
2f4ed244ea Fix -fps logger measuring its own overhead; buffer output, fix percentiles
First real run of -fps (222 seconds, 4-monitor bench, in-mission) showed a
rock-solid 60.0 fps average but a worst frame of ~24ms in 82% of seconds, with a
median of 24.4ms and a minimum of 23.2ms. That regularity was the tell: 60Hz vsync
intervals are 16.7 / 33.3 / 50.0 ms, so 24ms is not a multiple of anything and had
no business recurring once per second with that consistency.

Cause: the logger was measuring itself. The per-second line was written straight to
the file with WriteFile on the render thread, and the frame duration is recorded
BEFORE the line is emitted -- so the write's cost landed in the *next* frame's
measurement, which belongs to the next second's bucket. Result: exactly one
inflated frame per second, indefinitely. Holding the handle open (as the first
version did) was not enough; a single WriteFile is still several milliseconds when
something like antivirus is in the path.

Three fixes:

1. Buffered output. Lines accumulate in a 128 KB static buffer (~2000 rows, about
   33 minutes at one row per second) and are flushed only when the buffer fills or
   at exit via atexit(). A normal measurement session now performs no file writes
   at all while running. Trade-off, deliberately accepted: a hard crash loses the
   un-flushed tail. gos-displays.txt remains the crash-survivable log; gos-fps.txt
   is a measurement instrument and must not perturb what it measures.

2. Meaningful percentiles. A "1% low" over 60 samples per second computes
   nFrames/100 = 0, which was clamped to 1 -- so the column was literally
   1000/worst_frame, i.e. the worst-frame column restated in different units.
   Verified against the log: worst 24.1ms -> 41.5 fps, exactly the "1% low" printed.
   Per-second is now a 5% low (worst 3 of 60), which is a number that actually
   differs from the worst frame. True whole-session 1% and 0.1% lows are computed
   at exit from a 0.5ms-bucket frame time histogram (2000 buckets, 0-1000ms) and
   printed in a new session summary along with total frames, elapsed time, average,
   and total hitches. The histogram gives exact-enough percentiles over an entire
   session without retaining every frame time.

3. Carry-over. A single frame longer than one second (a level load: the log shows
   4338ms and 4799ms frames) left the accumulator above the 1000ms threshold, so
   the next few frames each emitted their own bogus one-frame row -- visible in the
   log as rows reporting "1 frame, 1339 fps". The excess is now discarded.

Also adds #include <stdlib.h> to WinMain.cpp for atexit(); it was not reachable
through pch.hpp. GOS_FpsAtExit is declared __cdecl: GameOS builds with /Gz, which
makes __stdcall the default calling convention, but atexit() takes a __cdecl
callback -- without it VC6 rejects the call with C2664.

What the run did establish, and still stands: 60 frames per second sustained for
~150 seconds with no rhythmic multi-hitch pattern, so the mode 4 split-MFD stutter
fix (0a657b59) is holding. Genuine dropped frames (>40ms) occurred in 18% of
seconds, including clusters of 80-90ms; those are real and unaffected by this fix.
Re-measure after this change to see the true baseline.

CLAUDE.md STEP 10 updated: the previous claim that holding the handle open kept the
logger from perturbing the measurement was wrong, and is replaced with what was
actually observed.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-25 18:43:42 -05:00
9c67dddd83 Display diagnostics + -fps/-tcoop; native multi-monitor MFD ruled out
Four-monitor MFD bring-up on the new bench (MR_new: AMD FirePro W4100, 4 outputs,
Win10). Adds permanent display diagnostics, a Release-capable frame pacing logger,
a self-locating AppCompat shim installer, and settles -- empirically -- whether
dgVoodoo2 can be dropped for the multi-monitor MFD modes. It cannot.

WHY THIS WAS HARD
-----------------
CHSH_Device::InitFirst/InitSecond discarded EVERY HRESULT (SetCooperativeLevel,
SetDisplayMode, CreateSurface, GetAttachedSurface, QueryInterface, CreateDevice)
and returned true unconditionally. A panel that failed to open produced no error,
no crash and no log entry -- the monitor just stayed on the desktop. SPEW is
compiled out of shipping builds, so none of it was visible. Restoring that
visibility is what unblocked everything else.

DIAGNOSTICS ADDED (keep these)
------------------------------
* VideoCard.cpp -- LogDisplayDevices() writes gos-displays.txt next to the exe:
  NumDevices/NumHWDevices/NumMonitors, every DirectDraw device with its
  hw_rasterization flag, the role assignment (FullScreenDevice / g_nNonDualHead /
  g_nDualHead / g_nDualHead2 / g_nMFD1 / g_nMFD2), per-slot -tmon APPLIED/REJECTED
  (previously silent), and whether mode 4's "BOTH mfd1 and mfd2" requirement is met.
* render.cpp -- HSH_LogInit()/HSH_CheckHR()/HSH_HRName() log every DirectDraw call
  in the panel init path with its HRESULT decoded by name (27 DDERR_* codes, all
  verified present and collision-free against build-env/dx7asdk/include/ddraw.h).
  Each line is opened/appended/closed individually so the log survives a crash.
* WinMain.cpp -- GOS_LogFrameRate() writes gos-fps.txt: per second, frame count,
  average fps, 1% low (mean of the worst 1% of frames), worst frame in ms, and a
  count of frames exceeding 2x average ("hitches"). Average fps alone cannot
  distinguish 60fps from 60fps-with-a-dropped-frame-every-second; the 1% low can.
  The engine's own FrameRate readout is #ifdef LAB_ONLY (MWMission.cpp) so it only
  exists in MW4pro.exe; this works in Release, which is what runs on the pods.

NEW SWITCHES (both documented in -help)
---------------------------------------
* -fps    Enable the frame pacing report. Off by default: the gate is the first
          statement in GOS_LogFrameRate, so an unflagged run does no arithmetic
          and does not even create the file. The file handle is held open for the
          process lifetime -- opening/closing it every second would put a syscall
          of unpredictable latency on the render thread, i.e. the measurement tool
          perturbing what it measures.
* -tcoop <0-5>  Selects the SetCooperativeLevel form used by the radar/MFD panels.
          0 = legacy (unchanged shipped behaviour, remains the default).
          Exists so every candidate fix could be compared on real hardware without
          a rebuild between attempts.

CRASH-SAFETY FIX
----------------
hsh_initialized was set unconditionally after panel init, so a failed panel left
null surfaces and a null IDirect3DDevice7 behind and the per-frame path called
straight through them. Now:
  - all four InitSecond overrides (CMR/CRadar/CMFD/CMFDRight) bail on base failure,
  - CMFD_Device::InitFirst reports its real result instead of always returning true,
  - hsh_initialized is only set when the panels genuinely came up.
A display failure now leaves the game running without MFDs instead of bombing to
desktop. Crash signature for the record: `call [ecx+0x44]` with ECX=0 is
IDirectDrawSurface7::GetDC on a never-created surface (vtable offset confirmed
against the DX7 header), reported as "Attempt to read from address 0x00000044".

APPCOMPAT SHIM INSTALLER (new)
------------------------------
build-env/set-appcompat.ps1 + set-appcompat.bat. Self-locating via $PSScriptRoot:
applies DWM8And16BitMitigation to the MW4 executables sitting next to it, wherever
that install lives. HKCU always, HKLM too when elevated (the HKLM value format
differs -- it carries a leading "$" marker -- so the two must not be interchanged).
Verifies by reading back; detects the HIGHDPIAWARE-only entry that SUPPRESSES the
automatic shim; supports -Remove and -WhatIfOnly. deploy-mw4.ps1 now ships both
files into every deployment.

This matters because the layer is keyed on the executable's FULL PATH -- any copy
of an install to another folder or machine silently loses it, and the resulting
error is actively misleading (see below).

WHAT WE LEARNED
---------------
* The AppCompat shim SYNTHESISES 16-bit display modes. Proved directly: the crash
  dump shows "16 bit modes :" EMPTY without it and fully populated with it. MW4
  renders at bitdepth=16 and modern GPUs expose no 16-bit modes at all.
* Without the shim, GameOS reports "Another application is preventing use of full
  screen mode" (GOS_DXRASTERIZER_NOFULLSCREEN, DXRasterizer.cpp ~1125). That is a
  catch-all fired after every SetDisplayMode attempt fails -- it even scans for
  NetMeeting -- and it sends you looking for a conflicting program that does not
  exist. The real cause is the missing shim.
* Exclusive fullscreen DOES work on Win10 with the system ddraw.dll and dgVoodoo2
  physically removed, once the shim is applied to that exe path.
* Windowed mode works natively with no shim at all: the windowed path sets
  Environment.bitDepth = DesktopBpp (32), so there is no mode switch. Verified for
  the console/shell; a full mission windowed is still untested.
* Native DirectDraw enumerates all four W4100 outputs, so dgVoodoo2 was never
  needed for device enumeration.
* The engine is 4:3 ONLY. ImageHlp.cpp ~464 asserts the complete supported set:
  640x480, 512x384, 800x600, 960x720, 1024x768, 1280x1024 (5:4), 1600x1200. No
  16:9 mode and no aspect correction anywhere in the codebase. On a 16:9 monitor
  the scaler must adapt: plain stretch distorts geometry, keep-aspect pillarboxes.
* -2dt is not a recognised switch anywhere in the codebase, despite appearing in
  production ctcl.ini launch lines. Completely inert.
* NumHWDevices (5) can exceed NumDevices (4): it counts D3D device-enumeration
  callbacks, and an adapter exposing both a HAL and a T&L HAL yields two. Benign.

WHAT WE TRIED AND WHY IT FAILED
-------------------------------
The panel cooperative-level call was genuinely wrong -- a latent 2002 bug. Every
panel asked to be BOTH the process focus window AND its own device window, on the
one shared hWindow, after the main device had already taken exclusive mode on it.
The main device (DXRasterizer.cpp ~1027) already uses the correct two-call idiom
(SETFOCUSWINDOW alone, then EXCLUSIVE|FULLSCREEN) -- tagged //sanghoon, the same
author. The panels never were.

Results on real hardware, no dgVoodoo2, shim applied, -tmfds 4:

  -tcoop 0  SETFOCUSWINDOW|CREATEDEVICEWINDOW|ALLOWREBOOT|EXCLUSIVE|FULLSCREEN
            -> DDERR_EXCLUSIVEMODEALREADYSET
  -tcoop 1  CREATEDEVICEWINDOW|EXCLUSIVE|FULLSCREEN (no focus claim)
            -> DDERR_INVALIDPARAMS (CREATEDEVICEWINDOW needs a focus window)
  -tcoop 2  SETFOCUSWINDOW, then CREATEDEVICEWINDOW|EXCLUSIVE|FULLSCREEN
            -> DDERR_INVALIDPARAMS
  -tcoop 3  SETFOCUSWINDOW, then EXCLUSIVE|FULLSCREEN
            -> first panel collides, but that collision STEALS exclusive mode from
               the main display, after which panels 2 and 3 fully initialise (the
               radar reached CreateDevice(HAL) = DD_OK -- a secondary panel running
               entirely on native DirectDraw). Side effect: the desktop was left at
               1920x1080 16bpp. Not viable.
  -tcoop 4  EXCLUSIVE|FULLSCREEN only          -> EXCLUSIVEMODEALREADYSET, all panels
  -tcoop 5  ALLOWREBOOT|EXCLUSIVE|FULLSCREEN   -> EXCLUSIVEMODEALREADYSET, all panels

CONCLUSION: on modern Windows only ONE DirectDraw object per process may hold
exclusive fullscreen. The main display takes it; every secondary panel is refused.
XP allowed multiple. dgVoodoo2 allows it because it is a full reimplementation of
ddraw and is not bound by that rule -- it is not papering over a bug we can fix.

=> dgVoodoo2 CANNOT be removed for -tmfds 1/3/4 by correcting these flags. The
default stays -tcoop 0. The switch is retained because it is how this was settled
and it will re-settle it on different hardware.

The only native path is a borderless windowed panel design (DDSCL_NORMAL + clipper
per monitor, no exclusive mode anywhere). Assessment and staged plan are recorded
in CLAUDE.md STEP 10; not started.

WORKING 4-MONITOR CONFIG (with dgVoodoo2)
-----------------------------------------
dgVoodoo2 Scaling mode MUST be "Stretched, Keep Aspect Ratio". Plain "Stretched"
fails silently: main and radar go fullscreen black, both MFD monitors keep showing
the desktop, and every DirectDraw call still returns DD_OK -- the devices are alive
but dgVoodoo2 never drives those outputs. Diagnosed with a temporary per-panel
colour-flash test (since removed), which also established that device index maps
1:1 to physical monitor on this bench, so -tmon 1,2,3,4 equals auto-detection.
Confirmed working end to end: all three secondary panels present, full mission
played.

KNOWN GAP: the working dgVoodoo.conf is still not versioned in the repo (removed
in 0ceba9c7), so a fresh deploy will reproduce the silent MFD failure.

Behaviour with no new switches supplied is unchanged from the previous build
except on failure paths, which now degrade gracefully instead of crashing.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-25 18:22:31 -05:00
f402e5becc Add OPTIONS-INI.md reference
options.ini had no documentation. This documents every section and key, compiled
by reading the actual read sites rather than by describing the shipped file, so
that settings which look active but are not are identified as such.

Covers [graphics options], [sound options], [special commands], [server],
[joystick], [Cameraship Params], [Battle Tech Misc], [RookieMission] and
[battle tech print], plus how the file is loaded, which parts the game writes
back, and the per-role options-game/cam/mr variants.

Notable findings, all verified against source:

- The entire [network options] section is DEAD. None of connectiontype,
  connectionspeed, packetsize, defaultconnection, playericon or teamicon is read
  by any code; the connection type actually used comes from the multiplayer
  connection wizard. These are stock MW4 leftovers.

- Several [Battle Tech Misc] keys have names that do not describe what they do,
  and the real meanings are now recorded: RuleBook sets g_nMechVariant, DawnWar
  sets g_nMechLabOp, BiggieSizeIt sets g_nMechPodNum (the console lobby's larger
  roster flag), and CanYouHearTheFootSteps sets g_nBlackMech.

- The shipped options.ini misspells two keys. It contains secmissionreplay and
  secmissionreport, but the code reads SecsMissionReplay and SecsMissionReport.
  The shipped values are therefore ignored and the compiled defaults apply.

- Three keys are read and then immediately overridden, so editing them does
  nothing: videodriverindex (device forced to 0), huddamagemode and
  hudtargetdamagemode (both forced false). Their GetEntry calls are commented out.

- [special commands] killgame is a self-clearing kill switch: if true at startup
  the game rewrites it to false, saves options.ini and exits immediately.

- maxplayers/maxbots are serialised to clients as 5-bit fields, so 31 is the
  maximum usable value; cross-referenced to RAISING-PLAYER-CAP.md.

Also records why a typo produces no diagnostic: GetEntry returns false and the
compiled default is kept silently. Page and key lookup were confirmed to be
case-insensitive (NotationFile::FindPage lowercases; Page::FindNote uses
_stricmp), so the capitalisation differences between the shipped file and the
code are harmless -- the misspellings above are not.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-25 08:54:28 -05:00
308fe041d4 Make -help a complete command line reference
MW4.exe accepts 76 command line switches. Only 27 of them appeared in the old
-help output, that output went to the debug log rather than to the user, and it
did not stop the game from launching afterwards -- so in practice the switches
were undocumented.

-help now writes a full, categorised reference to mw4-help.txt next to the
executable, opens it in Notepad, and returns from WinMain without starting the
game. If Notepad cannot be launched, a message box reports where the file was
written. The check runs immediately after the command line is lower-cased at the
top of WinMain, before any subsystem is initialised.

A file plus a viewer was chosen over a message box because MW4 is a GUI-subsystem
application with no console, and 76 switches with real descriptions do not fit
legibly in a dialog.

The reference documents every switch actually parsed, grouped as: display and
video, audio and plasma display, pod hardware and arcade (CTCL), zoom and field
of view, multiplayer and network, logging and diagnostics, development and test
builds, and other. Each entry records the accepted value range where the parser
enforces one -- for example -tbaud 9600-921600, -armorlevel 0-4, -tmfds 0-4,
-zmfovb 0.01-0.5, and the -zmtime special case where 0 means instant.

Two switches are listed under "recognised but inactive" so their behaviour is not
misrepresented: -join, whose consuming line is commented out, and -noabzug, which
is only parsed inside a disabled code path. The LAB-only switches are marked as
accepted and ignored in Release builds.

The old partial SPEWALWAYS list in GetGameOSEnvironment was removed so there is a
single maintained reference rather than two that can drift apart. A comment there
points at the new one.

Also adds an explicit #include <stdio.h>; this translation unit previously had no
direct stdio use and relied on transitive inclusion.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-25 08:54:10 -05:00
0009f868eb RAISING-PLAYER-CAP.md: sharpen the drop-zone wording
The previous correction overstated the case by implying the code reading was
wrong outright. It was not. The engine really does place surplus 'Mechs on
already-occupied spawn points -- two 'Mechs dropped on the same spot -- exactly
as reading the code suggests.

The only wrong part was the predicted consequence. The original draft said those
players "silently fail to spawn". They do spawn; the collision system then pushes
the stacked 'Mechs apart within a second or two, costing some minor contact
damage, and play continues normally.

Reworded Layer 7 and the known-traps entry to separate the two claims: the
spawn-point reuse is real and code-predictable, the failure-to-spawn conclusion
was not.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-24 23:50:12 -05:00
33c28cf816 RAISING-PLAYER-CAP.md: correct the drop-zone analysis
The original draft claimed that players beyond a map's available drop zones
"silently fail to spawn", and called per-map drop-zone authoring the true gating
task for raising the player cap. That was inferred from reading the code and is
wrong.

Corrected from real pod testing: when there are more 'Mechs than drop zones, the
surplus 'Mechs spawn on top of each other. They clip and collide briefly, take
some minor damage, then separate and play normally. It resolves itself within
seconds.

So a drop-zone shortfall is a quality-of-experience issue, not a functional
failure. It does not block raising the cap and should not gate the schedule.
Adding start points to busy maps is still worth doing eventually -- overlapping
spawns are untidy and hand out free chip damage -- but it can happen at any point
and never needs to be complete.

Updated accordingly: the TL;DR table, Layer 7, the implementation order (drop
zones moved from step 5 to last and marked optional), the verification checklist,
and the known-traps list.

The biggest remaining non-code task is now the lobby pod-grid and scoreboard
layout rework.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-24 23:49:01 -05:00
a41dfb4aa8 Add RAISING-PLAYER-CAP.md reference
Captures the full audit of every player-count limit in the codebase, done while
tracking down the "16 pilots + 1 cameraship" launch failure. Research only --
nothing in it is implemented.

Key finding: the practical ceiling without a wire-format change is 31, not 32.
m_maxPlayers and m_maxBots are serialized as 5-bit fields in
NetMissionParameters, so 32 truncates to 0. This supersedes the "32" figure in
the existing CLAUDE.md plan sections.

Documents, with file references:
- what is NOT a limit (Adept::Maximum_Players is 255, connectionID is a BYTE,
  DirectPlay imposes nothing, and there is no 32-bit player bitmask)
- the 5-bit serialization ceiling and what widening it would cost
- the compiled defaults in CTCL_DefaultHostSetup that actually gate connections
- the CTCL roster arrays, including that g_aPlayerInfos[20] has NO bounds check
  in CTCL_AddPlayer and that ctcl.h is duplicated across ~6 directories
- MAX_LANCEMATES 16 for bots
- the lobby script constants and the pod-grid UI work
- scoreboard/radar/review layout work
- per-map drop zones, which is the real gating task and produces silent spawn
  failures when short
- the O(n^2) replication cost, reframed as verify-don't-assume on modern hardware

Also records the failure signatures to expect, so a future attempt recognises
them quickly: silent launch hang from a count mismatch, silent non-spawn from
missing drop zones, and 5-bit truncation looking like "max players became zero".

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-24 23:40:14 -05:00
9ba2d594b1 Add -tmon switch to hard-assign display devices
Display roles are auto-detected in FindVideoCards(): the main view takes the
first hardware-rasterizing device that is not a 1280x480-capable span, the radar
takes the next, and mode 4's two MFD panels take the two after that. That works
until DirectDraw enumerates the adapters in an unexpected order, at which point
the wrong content appears on the wrong monitor with no way to correct it.

Adds an override that keeps auto-detection as the default:

    -tmon <main>,<radar>,<mfd1>,<mfd2>

Values are 1-based, so the normal case is "-tmon 1,2,3,4". A value of 0 leaves
that slot auto-detected, so "-tmon 2,1,0,0" swaps only main and radar. Separators
may be ',' '/' or ':'. mfd1/mfd2 are only meaningful with -tmfds 4. Omitting the
switch entirely preserves existing behaviour exactly.

Implementation:

- Parsed in MW4Application's WinMain alongside -tmfds. That runs before GameOS
  calls FindVideoCards(), so the values are in place for device selection.
- Stored in g_naMonitorOverride[4] (VideoCard.cpp), extern'd in MW4Application.cpp.
- ApplyMonitorOverride() validates the index against NumDevices, so a stale -tmon
  on a machine with fewer monitors falls back to auto-detection rather than
  breaking startup.
- Ordering matters and is deliberate: main and radar are applied BEFORE the mode 4
  MFD search so that search still skips whichever devices the operator picked;
  mfd1/mfd2 are applied after it. Partial overrides therefore compose correctly.

Role map: main = Environment.FullScreenDevice, radar = g_nNonDualHead,
mfd1 = g_nMFD1, mfd2 = g_nMFD2. The mode 1 span (g_nDualHead) is intentionally
not overridable -- it is detected by 1280x480 mode support, which only the span
card advertises, so that detection is reliable.

Also adds a SPEW line logging the final role -> device map and device count.
Note this is only visible in Profile/LAB builds; SPEW compiles out in Release
(gos2X/Gos.h), so in Release the order is determined by observation.

Verified: compiles clean, console launches. Multi-monitor testing pending.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-24 23:39:59 -05:00
0a657b5998 Fix constant rhythmic stutter in MFD mode 4 (split dual 640x480)
-tmfds 4 splits the 1280x480 MFD span into two independent 640x480 monitors so
the span hardware is no longer required. It rendered correctly but stuttered
rhythmically and constantly, making the game unplayable. -tmfds 1 on the same
binary was flawless.

Root cause: a cross-DirectDraw-object texture read, twice every 7-frame cycle.

CHSH_Device::InitFirst with no pOtherHSHD peer creates its OWN IDirectDraw7 (via
wDirectDrawCreateEx on that monitor's device GUID) and InitSecond creates its OWN
primary flip chain and IDirect3DDevice7. CMFDRight_Device::InitFirst passes no
peer, so the right MFD is an entirely separate DirectDraw object.

EndChannel then did, for channels 3-4:

    target->pD3DDevice->SetTexture(0, pDDSTarget);

where target->pD3DDevice belonged to the RIGHT device but pDDSTarget was the LEFT
device's render-target texture. The old code comment asserted "both devices are
on the same GPU so VRAM textures are mutually accessible" -- that premise is
wrong. In DirectDraw 7 a surface belongs to the IDirectDraw7 that created it, not
to the physical GPU, so it is not a valid texture on another object's D3D device.

The MFDs still displayed, which means the runtime was emulating the access with a
VRAM -> system-memory readback and re-upload of the 1024x512 16-bit render target.
That forces a full GPU pipeline stall, and it happened on channels 3 and 4 (that
is, sh_step 5 and 6) -- twice per 7-frame cycle, on the same GPU drawing the main
view. Hence a fixed-period hitch in the whole game, forever.

This also explains why the earlier stagger work (eaa5fd3, BeginSceneRight) did not
help: it only moved a Flip from sh_step 0 to 1, and the flips already used
DDFLIP_DONOTWAIT|DDFLIP_NOVSYNC and never blocked. The flips were never the
problem.

Fix: give the right device everything it draws with, and render channels 3-4
entirely on it.

- New HSH_CreateMFDTextures() builds the mech image atlas and the MFD sprite atlas
  on a caller-supplied IDirectDraw7. Both devices now call it, so each owns a
  complete independent texture set. CMFD_Device::InitSecond was refactored onto it.

- CMFDRight_Device gained its own pDDSMechTexture / pDDSDamageTexture /
  pDDSTargetTexture plus a Release() override, and its InitSecond now sets up
  tw/th and the material/render state exactly like the left device.

- New CMFD_Device::SwapRightState() exchanges this object's DATA members with the
  right device's. BeginChannel swaps in when channel >= 3 in mode 4; EndChannel
  swaps back. This routes all existing drawing to the correct monitor without
  touching the ~233 mfd_device.* call sites in hudchat/huddamage/hudweapon/
  GUIRadarManager. The vtable pointer is deliberately never swapped, so virtual
  dispatch is unaffected; CHSHFont has no virtual functions so its array is
  swapped bytewise to avoid ctor/dtor side effects on a temporary.

- EndChannel's composite is now a single path for all modes. Mode 4 composites
  full 640 width at x=0 (each device is a standalone panel); modes 1-3 keep the
  half-width (ch/3)*w packing into one backbuffer.

Side effects: startup builds the 65-bitmap mech atlas twice (once per device), and
VRAM use rises by a few MB. Modes 0-3 are behaviourally unchanged.

Known cosmetic leftover, deliberately not changed: huddamage.cpp lines ~1319 and
~1901 call LoadTargetTexture outside the channel-3 block, so those loads land on
the left device and go unused. The in-channel call at ~2209 runs every frame in
that branch and correctly populates the right device's copy, so behaviour is
correct -- it is just a redundant load on target change.

Requires rebuild: MW4.exe (GameOS changes recompile the engine library).
Verified: compiles clean, console launches. Two-monitor testing pending.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-24 23:39:42 -05:00
29aee4f71b Fix 16 pilots + 1 cameraship failing to launch
A full 16-'Mech roster plus a cameraship silently refused to launch: the console
sat in nLaunchState 3 ("loading") forever with no crash and no error. 15 mechs +
camera worked, and 16 mechs with no camera worked.

Two independent bugs, both counting the cameraship against the 16 'Mech slots.

1. Session capacity (broke all-human rosters)

   CTCL_DefaultHostSetup derived the camera reserve from
   CTCL_GetTeslaCountAll() - CTCL_GetTeslaCount(). Those counters read the CTCL
   tesla table, which is only populated when CTCL_IsConsoleOrCOOP() is true --
   i.e. only on the console. But the machine that creates the network session is
   the cameraship pod (CTCL_DoMission sets g_nServer = nCameraship, and that pod
   runs CTCL_DoCreateGame -> CTCL_DefaultHostSetup(0) -> Mech4CreateGame ->
   gos_CreateGame(..., Environment.NetworkMaxPlayers, ...)).

   On that pod both counters return 0, so the reserve collapsed to +0 and the
   session was created with dwMaxPlayers = 16. The 17th connection was refused,
   CTCL_CheckServerReady never saw nCount == g_nTeslas + 1, and the launch hung.

   Fixed by reserving with a constant, MW4_CAMERASHIP_RESERVE (4, matching
   MAX_CAMERAS), which is valid on every machine regardless of the tesla table.

2. Bot admission (broke any roster containing bots)

   MW4Shell::AddBot rejects when (player_count + bot_count) >= m_maxPlayers.
   player_count is the DirectPlay player count, which includes the cameraship
   connection, so with m_maxPlayers = 16 the last bot was silently refused.
   g_nBOTs then never matched the connected lancemates and the same readiness
   check spun forever.

   Fixed by subtracting cameraship participants from player_count under CTCL,
   via a new CTCL_CountCameraShipsInGame() helper (non-bot entries with
   m_nMechIndex == 0). Cameraships hold a network slot but pilot no 'Mech, so
   they must not consume a 'Mech slot.

Also applies the constant reserve in the PLAYER_LIMIT_PARAMETER path, guarded by
!CTCL_IsNone() so a standalone non-pod host keeps its exact configured limit.

This supersedes commit f76dc05f, which had the right formula but evaluated it on
the console rather than on the pod that actually creates the session.

Note for later: m_maxPlayers is serialized in only 5 bits (MWApplication.cpp),
so 31 is the hard ceiling for any future player-cap work. See
RAISING-PLAYER-CAP.md.

Requires rebuild: MW4.exe (Release + Profile). No script or resource changes.
Verified: compiles clean, console launches. Pod testing pending.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-24 23:39:16 -05:00
dicion 515adc2413 mw4print: persist banner text via banner.txt
- Add banner.txt load/save helpers in banner settings dialog

- Load banner from banner.txt first, with options.ini BannerText fallback

- Fix options.ini path join to include missing path separator
2026-07-24 21:56:24 -05:00
dicion ff650f7bec Load File: finalize first-click deterministic apply, add NoReturn, and align docs
- Stabilize Load File first-click behavior across mission/map/options/slots.

- Fix map sequencing by rebuilding game-type scenario list before mission lookup.

- Resolve mission-name miss to map index 0 fallback for selected game type.

- Fix decal handling: map INI decal IDs to dropdown indices, clamp invalid indices, display actual decal IDs in UI labels, and avoid redraw-time decal overwrite.

- Fix option apply timing and UI visibility issues, including Weapon Jam refresh.

- Add NoReturn support end-to-end: parse/store in MW4Shell auto globals, expose script variable, and apply to respawn/no-return mission params and UI checkbox.

- Update autoconfig spec to reflect actual parser/default/fallback behavior and add NoReturn examples.
2026-07-24 21:42:54 -05:00
dicion e5561181bb Fix MAIL_LOAD_AUTO_MISSION crash: defer initialize(this) to sender==this self-mail
'gui_objects have no parent' crash at line 907: initialize(this) cannot be
called from sender==@conlobby@ context (external mail). Fix:

1. Replace initialize(this)+mail(-9998,parent) in MAIL_LOAD_AUTO_MISSION
   with mail(MAIL_LOAD_AUTO_MISSION_DONE=-8888, this) -- a self-mail.

2. Add MAIL_LOAD_AUTO_MISSION_DONE handler in sender==this block, which
   safely calls initialize(this)+mail(-9998,parent) from the correct context.

Also repaired a corrupt duplicate MAIL_PREVIOUS_MISSION_PARAMS block
that was left orphaned by an earlier edit (the original body of
MAIL_LOAD_AUTO_MISSION had been inserted there when the handler was
moved from sender==this to sender==@conlobby@).

No rebuild required (script-only).
2026-07-24 11:09:50 -05:00
dicion 06de4534da BTFrstrm: add Load File test ini files (FFA Coliseum + 4-team KotH CentralPark) 2026-07-24 11:01:11 -05:00
dicion 23284b18b0 Fix MAIL_LOAD_AUTO_MISSION: move handler to sender==@conlobby@ block
Handler was inside 'if (sender == this)' but MAIL_LOAD_AUTO_MISSION is sent
from ConLobby (sender != this), so it never fired. All game params (FriendlyFire,
SplashDamage, UnlimitedAmmo, WeaponJam, AdvanceMode, ArmorMode, etc.) were
silently ignored on every Load File click.

Fix: move the ~70-line handler to the 'if (sender == @conlobby@)' block,
alongside MAIL_SET_ROOKIE_MISSION and MAIL_PREVIOUS_MISSION — where all
ConLobby-originated mails are handled. No rebuild required (script only).
2026-07-24 10:56:57 -05:00
dicion 62dc0952b1 BTFrstrm: add Load File test configs (FFA Coliseum, Team KoH CentralPark) 2026-07-24 10:46:24 -05:00
dicion 9a9f2d0df2 autoconfig-file-spec.html: add TeamAllowed/TeamCount to example; fix caveat #3 2026-07-24 10:37:37 -05:00
dicion cd2fe73c88 Fix Load File: separate Auto globals; fix team/FFA double-press
Issue 1 — double press to fix team/FFA display:
  cur_team_val is updated by ConLobbyMission's -9998 signal which was async.
  Fix: call SetNetworkMissionParamater(team_allowed, ...) directly in ConLobby
  before the slot loop, then re-read cur_team_val via CTCL_GetTeamParams.
  This is synchronous so the slot display is correct on the first click.
  New file keys: TeamAllowed=0/1, TeamCount=2 (default 2 teams).

Issue 2 — Default button broken after Load File click:
  CTCL_LoadAutoFile was overwriting g_nRookieGameType/g_szRookieMission etc.
  so the Default button loaded the last auto-file params instead of defaults.
  Fix: 14 new dedicated g_nAutoXxx/g_szAutoMission globals. CTCL_LoadAutoFile
  populates ONLY these. Rookie Mission globals are never touched.
  New MAIL_LOAD_AUTO_MISSION (-6666) sent to ConLobbyMission applies the Auto
  globals (mirrors MAIL_SET_ROOKIE_MISSION_PARAMS but uses g_nAutoXxx).
  ConLobby reads game type/mission from Auto globals directly into the
  ConLobbyMission dropdowns (@ConLobbyMission@o_game_options[N].nselected).

Files changed:
  MW4Shell.cpp: 16 new globals, StartUp/ShutDown registration, CTCL_LoadAutoFile
  ConLobby.script: MAIL_LOAD_AUTO_MISSION define, updated handler
  ConLobbyMission.script: MAIL_LOAD_AUTO_MISSION define + handler
  autoconfig-file-spec.html: document TeamAllowed + TeamCount fields
Rebuild required: MW4.exe (Release + Profile).
2026-07-24 10:35:11 -05:00
dicion 3f2d79a038 BTFrstrm: add weapon location spreadsheets (Special1/2 and rear-facing) 2026-07-24 10:18:06 -05:00
dicion c3d82d78c9 Load File: hide button when automaticmode!=1; fix team/skin double-click
1. Expose g_bAutomaticMode as gosScript variable so ConLobby can check
   it at init time. o_load_file.state is set to 3 (disabled/hidden) if
   automaticmode != 1 in options.ini. Button is fully visible and active
   only when the feature is intentionally enabled.
   Rebuild required: MW4.exe (Release + Profile).

2. Remove cur_team_val conditional for team/skin slot assignment.
   Previously, only one of o_team[k] or o_skins[k] was set depending on
   cur_team_val at click time, but MAIL_SET_ROOKIE_MISSION propagates the
   new game type asynchronously -- cur_team_val would not reflect the
   file's GameType until the next frame, requiring a second click.
   Fix: always set both o_team[k] and o_skins[k] unconditionally. The
   mission launch code uses whichever is relevant for the active mode;
   the other is harmlessly ignored. No rebuild (script-only).
2026-07-24 10:08:21 -05:00
dicion 17ca966473 Fix Load File mech lookup: use stock_array to get chassis name
mech[j] in the flat sorted array hits variant entries (e.g. 'Assassin2 A'
sorts before 'AssassinII' alphabetically, pushing all subsequent chassis
indices off by 1 or more). The script's stock_array[] maps each chassis
index -> its actual position in the flat mech[] array, bypassing variant
entries.

Fix: mech[allowed_mechs[j]] -> mech[stock_array[allowed_mechs[j]]]

Only stock (chassis) names are supported in Mech= field. Operators can
adjust variants manually after Load File is clicked. No rebuild required
(script-only change).
2026-07-24 10:02:13 -05:00
dicion 7852269dc7 Fix Load File crash: VALUEPARM for literal field arg in CTCL_GetAutoSlotInt
Script calls callback(CTCL_GetAutoSlotInt, k, 0/1/2/3) where 0-3 are
integer literals. The script engine passes literals as (void*)N directly
(not as pointers), so INTPARM(1) = *((int*)data[1]) dereferences NULL
when field=0, producing the 'Attempt to read from NULL' STOP.

Fix: VALUEPARM(1) = (int)data[1] reads the value without dereferencing.
k (data[0]) remains INTPARM because it is a script variable (passed as
a pointer to the variable's storage, not a literal).

Also add exists(@ConLobbyMission@) guard before MAIL_SET_ROOKIE_MISSION
for defensive safety if the sub-script is not running.

Rebuild required: MW4.exe (Release + Profile).
2026-07-24 09:31:51 -05:00
dicion 76121f1c68 BTFrstrm: add autoconfig-file-spec.html (Load File format reference) 2026-07-24 09:13:37 -05:00
dicion c33465611f Add [automaticmode] Load File button to console lobby
options.ini [automaticmode] section:
  automaticmode=1
  automaticfile=c:\path\to\config.ini

Right-click was considered then dropped in favor of a dedicated button
at 467,510 (below Pick Cond., left of Reprint).

C++ (MW4Shell.cpp):
- SAutoFileSlot struct + g_aAutoSlots[16], g_bAutomaticMode, g_szAutomaticFile globals
- [automaticmode] ini read at StartUp
- CTCL_LoadAutoFile: checks file exists, reads [mission] page into existing
  g_nRookieXxx globals + [slot0]..[slot15] pages into g_aAutoSlots[]; returns 1 if loaded
- CTCL_GetAutoSlotName(out_str, k): pilot name for slot k
- CTCL_GetAutoSlotMech(out_str, k): mech display name for slot k
- CTCL_GetAutoSlotInt(k, field): Type/Team/Skin/Decal for slot k (fields 0-3)
- Register/unregister all 4 callbacks in StartUp/ShutDown

Script (ConLobby.script):
- o_load_file button at 467, 510
- Handler: CTCL_LoadAutoFile -> if loaded, sends MAIL_SET_ROOKIE_MISSION to
  ConLobbyMission (game options), clears all slots, then applies per-slot data
  in a loop (pilot names, mech by display-name lookup in allowed_mechs[], team,
  skin, decal). USE_ALLOWED_MECHS/non-ALLOWED_MECHS both handled via #if.

File not consumed (stays on disk); external app overwrites for next load.
Rebuild required: MW4.exe (Release + Profile).
2026-07-24 08:44:57 -05:00
dicion fccdc2dee4 CLAUDE.md: document 5813aeb6 through 0344418a work (2026-07-23)
- hsh/ BMP canonical renames (MFD + Mechs, commit 5813aeb6)
- RookieMission configurable defaults via options.ini (MW4Shell.cpp + ConLobbyMission.script, 5813aeb6)
- Mechlab turn rate label correction (StringResource.rc, 5813aeb6)
- BTFrstrm design docs: MechDependencyTree.docx + Special_Zones.docx (840bc96c)
- mech_loadouts.md: MechEditor data sources, field conversions, hsh naming reference (0344418a)
2026-07-23 22:18:43 -05:00
dicion 0344418aae mech_loadouts.md: document MechEditor data sources, conversions, hsh naming (2026-07-23) 2026-07-23 21:56:14 -05:00
dicion 520a860414 Update testing checklist through 2026-07-23 (RookieMission, turnrate label, hsh renames) 2026-07-23 21:47:08 -05:00
dicion 5813aeb6e9 Fix missing asset files with ones from 5.0.7D 2026-07-23 21:36:27 -05:00
dicion 840bc96cc1 Documentation! 2026-07-23 16:16:45 -05:00
dicion 72e1e59d8e CLAUDE.md: document eaa5fd3 through 5.1.0b-in-progress work
- MFD mode 4 right-device stagger fix (eaa5fd3): cycle diagram, files touched
- Linux→Windows rsync workflow: sync-to-windows.sh (55b9bfc5)
- ddraw.dll removed from repo; build-resources.ps1 moves it aside (0ceba9c7, 24825ff3)
- ConLobby V5.1.0b1 / Super6 6-mech rotation from Highlight (c768f7c4)
- CRIOMAIN.CPP Korean translation + RIO poll timeout scaling; CRLF hazard note;
  min/max undeclared in VC6 in this TU (16fca6c4, a712002f)
- 16 pilots + 1 cameraship: NetworkMaxPlayers formula, fall-through break warning (f76dc05f)
2026-07-19 19:55:46 -05:00
dicion b591bae273 add checklist of changes for testing! 2026-07-19 19:18:09 -05:00
dicion a712002fec CRIOMAIN.CPP: fix min/max undeclared identifier (VC6)
min() and max() are not in scope in this translation unit under VC6.
Replace with explicit ternary clamping expression; no new headers needed.
2026-07-19 19:00:08 -05:00
dicion f76dc05f46 Support 16 pilots + 1 cameraship in multiplayer
MW4Shell.cpp:
- CTCL_DefaultHostSetup (non-coop): replaced hardcoded
  Environment.NetworkMaxPlayers=16 with
  params->m_maxPlayers + (CTCL_GetTeslaCountAll() - CTCL_GetTeslaCount())
  so DirectPlay reserves one extra slot per installed cameraship.
  CTCL_GetTeslaCountAll() - CTCL_GetTeslaCount() = camera-only seat count.
- SetNetworkMissionParamater / PLAYER_LIMIT_PARAMETER: applied the same
  camera-slot formula when the host changes the player limit at runtime.
  Also restored the gos_NetServerCommands(gos_Commend_UpdateMaxPlayers)
  call (was accidentally dropped) and the missing break that caused
  fall-through into JOIN_IN_PROGRESS_PARAMETER.
- COOP branch: no change (capped at 9+bots; camera seats not needed there).

ConLobby.script:
- Raised the launch-guard cap from nTempPlayerCount > 16 to > 17,
  allowing the 17th connection (the cameraship) to not trigger the
  'Too many player/bots' error.
2026-07-19 18:54:38 -05:00
dicion 16fca6c4f1 CRIOMAIN.CPP: translate Korean comments to English, clean UTF-8
Translated all EUC-KR/CP949 Korean developer comments (~35 lines) to
English. File re-saved as UTF-8 (was CP949/EUC-KR on disk).

Also included: g_dwRIOPollTimeout formula that scales WaitForMultipleObjects
timeout by baud rate: clamp(ceil(480000/baud), 5, 50) ms.

Key translations:
- Developer markers (sanghoon/hyun)
- Packet receive loop comments
- ACK/NAK handling comments
- Thread/COM init comments
- Button table comments
- Joystick pedal/throttle diagram comments

Decorative diamond markers (◆) stripped from case labels and section
dividers. Unicode arrows (← →) in diagram comments replaced with ASCII.
2026-07-19 18:52:30 -05:00
c768f7c46b Bump console version to V5.1.0b1; merge Highlight Super6 changes
Version bumped to V5.1.0b1 to align with the 5.1.0b-in-progress branch name and
the goal of producing a tested release candidate.

Incorporated manually-tested script changes from Buddy 'Highlight' Taylor of
MechCorps (MCHL), who expanded the default mech pool from Fab4 to Super6:
- ROOKIEMECH defines extended to 6 entries (added Archer ID=1, Warhammer ID=62)
- 16-slot default mech assignments updated to cycle through all 6 Super6 mechs
- Right-click mech randomizer expanded from random(0,3) to random(0,5) to
  include Archer and Warhammer in the pool

Changelog entries for Cyd (06/24/26), MCHL (06/27/26), and RT (07/19/26)
added to the script header.

Co-authored-by: Claude Sonnet 4.6 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-19 17:19:08 -05:00
dicion 24825ff396 more ddraw handling fixes for profiler. ugh. 2026-07-19 16:41:45 -05:00
dicion 0ceba9c778 ddraw.dll breaking mw4pro windowed mode 2026-07-19 16:28:51 -05:00
55b9bfc521 Add sync-to-windows.sh: rsync script for Linux→Windows build machine
Syncs all source, content, toolchain, and assets needed to build and test the game on the Windows machine at /vwe/firestorm. Excludes generated build outputs (bin dirs, *.mw4, *.dep), .git/LFS, _UNUSED, and the MW4 deploy dir.

Co-authored-by: Claude Sonnet 4.6 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-19 15:40:24 -05:00
106bb5b59f Document mech_loadouts audit data
Update mech_loadouts.csv with derived playability, hardpoint, facing, and loadout annotations, and add mech_loadouts.md as the catch-up reference for future updates.

Co-authored-by: Claude Sonnet 4.6 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-19 14:36:37 -05:00
e45a67a8fe mfdsplit: mech loadouts, time list, build fixes, Korean→English, mw4print v2.0
Battlemaster stock loadouts (Content\Mechs\Battlemaster\battlemaster.subsystems):
- Replace lone MediumPulseLaser with full IS stock: PPC (Special2, group 1),
  6×ML (3 RT + 3 LT, group 1), 2×MG (LA, 200 rds, group 1), SRM6 (Special1,
  15 rds, group 2).

Battlemaster Clan 2C (battlemaster2c.subsystems):
- Replace ClanMediumPulseLaser with: ER PPC (RA, group 1), 6×ER ML (3 RT + 3
  LT, group 1), 2×Clan Gauss (LA, 16 rds each, group 1), Clan SSRM6 (Special1,
  15 rds, group 2).

Behemoth / Behemoth2 (.subsystems):
- Move Gauss rifles from weapon group 3 → group 1 (3 occurrences each).

Resource builder (build-resources.ps1):
- Always run with -window (windowed + DDrawCompat). Fullscreen native DDraw
  fails on VMs with the generic Microsoft display adapter.
- Remove dgVoodoo2 D3D interceptors (D3D8/D3D9/D3DImm.dll) from Gameleap\mw4:
  they silently break the builder (process exits 0 without packing anything).
  Script also defensively moves any such files aside via $dgvMoved block.
- Remove dgVoodoo.conf and dgVoodooCpl.exe (abandoned experiment, no longer used).
- Expected: 'Hardware Error: not compatible with MechWarrior 4' dialog at end
  of build on VMs -- click OK, packages are built correctly regardless.

CLAUDE.md: updated with mfdsplit branch notes covering all 2026-07-18 work.

Co-authored-by: Claude Sonnet 4.6 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-18 16:22:03 -05:00
456e197822 Fix ConLobbyMission time list (18 entries) and build-resources ddraw handling
ConLobbyMission.script:
- Expand MP time-limit dropdown from 9 to 18 entries (1-15, 20, 25, 30 min)
  with max_displayed=10 so the list scrolls cleanly.
- Fix bare else-if syntax (missing braces) that caused null-reference crash
  on console lobby load when this was merged from main.
- Fix i==5 vs i==6 max_displayed assignment for time vs radar dropdowns.

build-resources.ps1:
- DDrawCompat's ddraw.dll is fatal to MW4pro.exe in FULLSCREEN but works
  in WINDOWED mode. Don't move it aside; instead run the builder with
  -window when ddraw.dll is present. This fixes builds on VMs with generic
  display adapters (no hardware DirectDraw) and avoids the EnterWindowMode
  CreateSurface crash on Win10/11 without a functioning DWM shim.
- Also temporarily set options.ini bitdepth=32 when running without any
  ddraw interceptor (bare-metal fallback), restored in finally block.
- Remove stale comment about ddraw being fatal; update interceptor detection
  to correctly identify DDrawCompat vs dgVoodoo2 via dgVoodoo.conf presence.

Co-authored-by: Claude Sonnet 4.6 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-18 15:39:42 -05:00
a45be8044c mw4print: bump to version 2.0, update copyright year to 2026
Co-authored-by: Claude Sonnet 4.6 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-18 14:42:11 -05:00
9f3a50443a mw4print: bump version to 2.0, update copyright year to 2026
Co-authored-by: Claude Sonnet 4.6 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-18 14:39:15 -05:00
dicion 47000d6cf2 text version of db structure for reference 2026-07-18 14:10:51 -05:00
dicion 195db56b1e mw4print: add db_schema.sql documenting MySQL export structure 2026-07-18 14:10:04 -05:00
dicion b98cb87ef3 mw4print: MySQL export, configurable banner text
MySQL database export (dbexport.h / dbexport.cpp):
- New files dbexport.h / dbexport.cpp implement late-bound MySQL export.
  libmysql.dll is loaded at runtime via LoadLibrary/GetProcAddress so no
  MySQL SDK is required at compile time; the app runs normally if the DLL
  is absent.
- After each print job, match data is exported to a MySQL server before
  PrintDlg() is called: one row in 'match', one row per player in
  'player_result', one row per attacker/victim pair in 'pvp'.  An optional
  'event' table records every individual SRecScore entry (off by default).
  Tables are created automatically (CREATE TABLE IF NOT EXISTS) on first
  connect.
- Config stored in mw4print.ini (app directory), section [MySQLExport]:
  Enabled, Host, Port, Database, Username, Password, ExportEvents.
- Config loaded at startup (OnCreate); DB_LoadConfig() / DB_SaveConfig().
- Connection timeout set to 5 seconds so the app does not hang if the
  server is unreachable.
- libmysql.dll (MySQL Connector/C 32-bit) added to Gameleap/mw4/ so the
  deploy script copies it to MW4/ alongside mw4print.exe.

Database Settings dialog (File > Database Settings... / Ctrl+D):
- MFC dialog: enable checkbox, Host/Port/Database/Username/Password fields,
  Export Events checkbox, Test Connection button with live status label,
  OK/Cancel.  OK persists settings to mw4print.ini immediately.

Configurable banner text (File > Banner Setting...):
- The 'WWW.MECHJOCK.COM' URL string printed at the bottom of every score
  sheet is now configurable.  Stored as BannerText= in options.ini under
  [battle tech print] (same section/file as the other print layout params).
  File > Banner Setting... opens a dialog to edit it; OK saves to options.ini
  and takes effect on the next print job with no restart needed.
  Default value is the original MECHJOCK string if the key is absent.
2026-07-18 14:05:31 -05:00
dicion 92aaedfcbb Add Korean translation diff HTML for documentation
korean_diff.html: side-by-side GitHub-style diff of all 171 source files
changed in the Korean->English translation commit (af416960). Shows the
original EUC-KR/CP949 Korean text on the left and the English replacement
on the right, including the UTF-8 encoding cleanup changes.

Generated from: git diff HEAD~1..HEAD (af416960) for documentation/
review purposes. Not a build input.
2026-07-18 13:17:33 -05:00
dicion af416960fa Translate Korean comments/strings to English; fix UTF-8 encoding across source tree
Korean translation (84 source files, 876 lines):
- Translated all EUC-KR/CP949 Korean developer comments to English across
  84 source files in Gameleap/code/. Zero Korean bytes remain outside the
  intentional font-table headers (D3FFontEdit2/fontedit all.h etc.).
- Comment markers: //상훈 앞/뒤 -> //sanghoon begin/end (Sang-hun's code
  region markers); //상훈짱 begin/end, //상훈.. variants; // 鉉 -> // hyun
  (second developer's markers); // 鉉 - start/end patterns.
- Functional string translations in recscore.cpp (mw4 + mw4print copies):
  body-part return values (왼발/오른발/etc. -> Left Leg/Right Leg/etc.),
  kill-announcer format strings (~30 entries), and the nonmfc.h assert dialog.
- GosView profiler: 킪 -> us (microseconds) in timing display strings.
- Network/socket code (ctcl.cpp, mugsocs.h, ctime.cpp across Launcher/
  MW4Application/MW4GameEd2/AnimScript): state-machine comments, socket
  ID comments, login/session management comments.
- render.hpp CHSH_Device member comments; GUIRadarManager.cpp drawing
  routine comments; hudchat/hudcomp2/huddamage/hudmap/hudweapon/hudtarg
  HUD component comments.
- DXRasterizer.cpp: cleaned residual U+FFFD replacement characters left
  from a prior partial encoding conversion.

UTF-8 encoding cleanup (76+ files):
- Latin-1 single bytes converted to proper UTF-8 multi-byte sequences:
  © (0xA9) in 3dsmax4/Maxscrpt Autodesk/Wainwright copyright headers,
  ® (0xAE) in gosHelp/Remote.cpp, · (0xB7) bullet points in ai command.hpp,
  Û (0xDB) in SafeChain_Test.cpp tool header,
  ß (0xDF) in AnimationSuite version strings (8 files).
- Font lookup tables (D3FFontEdit2/, fontedit/ *.h) intentionally left
  as-is: raw byte values are C array data, not text.

Language DLL:
- Replaced Gameleap/mw4/Language.dll (original Korean binary) and
  MW4/Language.dll with freshly built English version from
  Language - Win32 English config (Language.dsp). Fixes Korean button
  labels in the GameOS exception/crash dialog (??? ??... / ?? / ???
  were showing instead of More Details.../Continue/Exit).
2026-07-18 13:10:31 -05:00
dicion eaa5fd3bbe fix mode 4: stagger right MFD BeginScene to step 1 to match flip timing
Root cause of the broken MFD2 display: CMFD_Device::BeginScene() cleared
the right device back buffer at old sh_step==0, but with the stagger the
right device flip also fired in that same frame (new sh_step==1 = old
sh_step==0 after increment). The flip presented a just-cleared buffer
with only the grid, no channel data.

Fix: split BeginScene for mode 4 - left device (step 0) vs right device
(step 1). Add BeginSceneRight() called from WinMain at old sh_step==1.
The right device flip at new sh_step==1 (= old sh_step==0) now shows
channels 3-4 rendered at steps 5-6 of the previous cycle - correct.

Cycle for mode 4 with stagger:
  old sh_step 0: radar+left BeginScene, no flip
  new sh_step 1: flip right MFD (shows prev cycle channels 3-4)
  old sh_step 1: right BeginScene (clear+grid)
  old sh_step 2-4: channels 0-2 -> left device
  old sh_step 5-6: channels 3-4 -> right device
  new sh_step 0: flip radar+left MFD (shows prev cycle channels 0-2)
2026-07-17 21:43:22 -05:00
dicion ab24aace11 stagger mfd device flips to try and prevent studder. 2026-07-17 21:43:21 -05:00
dicion ef82366132 remove dgvoodoo2 dlls from the deploy script. 2026-07-17 21:43:21 -05:00
dicion 6f630c777e fix errors 2026-07-17 21:43:20 -05:00
dicion 87e25677b5 dgvoodoo support files for building MW4 with dgvoodoo2 instead of DDrawCompat. This is a workaround for the fact that DDrawCompat is not compatible with Windows 11, and dgvoodoo2 is a more modern alternative. 2026-07-17 21:43:20 -05:00
dicion 7e6b457745 added ability to use dgvoodoo ddraw for VM resource builds. 2026-07-17 21:43:19 -05:00
dicion e5c4993436 Added tmdfs mode 4 for 2x 640x480 monitors 2026-07-17 21:43:18 -05:00
CydandClaude Fable 5 4cb3ab8ea8 Rebuild + deploy after PR #4 throttle-zero fix
Incremental Release + Profile builds (0 errors) picking up the
CRIOMAIN.CPP UpdateThrottle change; rel.bin\MW4.exe and MW4pro.exe
deployed to MW4\. Build logs in build-env\build_pr4_*.log.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 21:44:54 -05:00
CydandClaude Fable 5 4af0937661 Merge PR #4 from Dicion: fix throttle zero handling in CRIOMAIN
Removes the if(lT!=0) guard in UpdateThrottle that made an exact-zero
throttle reading keep the previous throttle value; adds a negative
clamp. Zero now falls into the deadzone branch -> ZERO_THROTTLE.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 21:41:17 -05:00
dicion e92e6adf2f Fix throttle zero handling: remove if(lT!=0) guard, add negative clamp 2026-07-15 20:37:42 -05:00
4195 changed files with 219091 additions and 169333 deletions
+10 -2
View File
@@ -8,10 +8,18 @@
# state you decide you never want mirrored.) # state you decide you never want mirrored.)
# run-firestorm skill screenshots (may capture the operator's desktop -- never mirror) # run-firestorm skill screenshots (may capture the operator's desktop -- never mirror)
.claude/skills/run-firestorm/shots/ .claude/skills/run-firestorm/shots/
# FS507D_20161015: later FireStorm release drop, kept local for analysis -- not part of the mirror # FS507D_20161015: later FireStorm release drop, kept local for analysis -- not part of the mirror
FS507D_20161015/ FS507D_20161015/
# player-created mechlab variants in the game deploy (local test/play data, per-machine) # player-created mechlab variants in the game deploy (local test/play data, per-machine)
MW4/Resource/Variants/ MW4/Resource/Variants/
# GameOS run-time reports: rewritten on every launch, per-machine
MW4/gos-displays.txt
MW4/gos-fps.txt
# Python bytecode cache: regenerated on import, and the filename is interpreter-specific
__pycache__/
*.pyc
+20
View File
@@ -0,0 +1,20 @@
{
// Legacy source files use CP949 (Korean Windows encoding, superset of EUC-KR).
// VS Code defaults to UTF-8, which silently corrupts the Korean comment bytes
// on save. Setting encoding to cp949 here preserves them exactly.
"files.encoding": "cp949",
// Let VS Code try to detect encoding per-file, falling back to cp949 above.
"files.autoGuessEncoding": true,
// Preserve the original Windows CRLF line endings (Git's * -text in
// .gitattributes also prevents conversion, but this keeps VS Code consistent).
"files.eol": "\r\n",
"python-envs.pythonProjects": [
{
"path": ".",
"envManager": "ms-python.python:venv",
"packageManager": "ms-python.python:pip"
}
]
}
Binary file not shown.
Binary file not shown.
+111
View File
@@ -0,0 +1,111 @@
#!/usr/bin/env python3
"""Generate special_weapon_locations.csv and rear_facing_weapons.csv from stock mech subsystems."""
import csv
from pathlib import Path
BASE = Path("Gameleap/mw4/Content")
MECH_TABLE = BASE / "Tables" / "MechTable.tbl"
OUT_DIR = Path("BTFrstrm")
def parse_ini(path):
sections, cur_sec, cur_dict = [], None, {}
with open(path, 'r', encoding='latin-1', errors='replace') as f:
for raw in f:
line = raw.strip()
if line.startswith('[') and line.endswith(']'):
if cur_sec is not None:
sections.append((cur_sec, cur_dict))
cur_sec, cur_dict = line[1:-1], {}
elif '=' in line and cur_sec is not None:
k, _, v = line.partition('=')
cur_dict[k.strip()] = v.strip()
if cur_sec is not None:
sections.append((cur_sec, cur_dict))
return sections
def find_dir_ci(parent, name):
"""Case-insensitive directory lookup for Linux."""
exact = parent / name
if exact.exists():
return exact
name_lower = name.lower()
for child in parent.iterdir():
if child.is_dir() and child.name.lower() == name_lower:
return child
return None
def weapon_model(model_path):
return Path(model_path.replace('\\', '/')).stem
# Read chassis list from MechTable.tbl
chassis_list = []
with open(MECH_TABLE, 'r', encoding='latin-1') as f:
for raw in f:
line = raw.strip()
if not line or line.startswith('//') or line.startswith('[') or '=' not in line:
continue
name, _, rel = line.partition('=')
# rel is like "Mechs\Atlas\Atlas.instance"
parts = rel.strip().replace('\\', '/').split('/')
chassis_list.append((name.strip(), parts)) # parts = ['Mechs', 'Atlas', 'Atlas.instance']
print(f"Found {len(chassis_list)} chassis in MechTable.tbl")
HDR = ['Chassis', 'InternalLocation', 'Model', 'Site', 'AmmoCount', 'GroupIndex', 'WeaponFacing']
special_rows, rear_rows, missing = [], [], []
mechs_dir = BASE / "Mechs"
for chassis_name, path_parts in chassis_list:
# path_parts[1] is the mech directory name (e.g. 'Atlas', 'MadCat_MkII', 'Urbanmech')
if len(path_parts) < 2:
missing.append(chassis_name)
continue
mech_dir = find_dir_ci(mechs_dir, path_parts[1])
if not mech_dir:
missing.append(f"{chassis_name} (dir not found: {path_parts[1]})")
continue
subs = [f for f in mech_dir.glob('*.subsystems') if f.suffix == '.subsystems']
if not subs:
missing.append(f"{chassis_name} (no .subsystems in {mech_dir.name})")
continue
subsys_file = subs[0]
sections = parse_ini(subsys_file)
for sec_name, fields in sections:
if 'WeaponSubsystem' not in fields.get('Model', ''):
continue
loc = fields.get('InternalLocation', '')
model = weapon_model(fields.get('Model', ''))
site = fields.get('Site', '')
ammo = fields.get('AmmoCount', '')
group = fields.get('GroupIndex', '')
facing = fields.get('WeaponFacing', '')
if loc in ('Special1', 'Special2'):
special_rows.append([chassis_name, loc, model, site, ammo, group, facing])
if facing and facing != '0':
rear_rows.append([chassis_name, loc, model, site, ammo, group, facing])
# Write CSVs
out1 = OUT_DIR / 'special_weapon_locations.csv'
with open(out1, 'w', newline='') as f:
w = csv.writer(f)
w.writerow(HDR)
w.writerows(special_rows)
out2 = OUT_DIR / 'rear_facing_weapons.csv'
with open(out2, 'w', newline='') as f:
w = csv.writer(f)
w.writerow(HDR)
w.writerows(rear_rows)
print(f"Spreadsheet 1 ? Special1/2 weapon locations: {len(special_rows)} rows -> {out1}")
print(f"Spreadsheet 2 ? Non-forward WeaponFacing: {len(rear_rows)} rows -> {out2}")
if missing:
print(f"\nSkipped: {missing}")
+88
View File
@@ -0,0 +1,88 @@
Mech,In_game_playable,Tech,Chassis_Tonnage,Max_Loadout_Tonnage,Armor_Type,Internal_Type,Heatsinks,Heatsink_Type,JumpJets_Installed,CanLoad_JJ,CanLoad_ECM,CanLoad_BAP,CanLoad_AMS,OmniSlots,Equipment,Armor_LeftArm,Armor_RightArm,Armor_LeftLeg,Armor_RightLeg,Armor_LeftFrontTorso,Armor_RightFrontTorso,Armor_CenterFrontTorso,Armor_CenterRearTorso,Armor_Head,Total_Armor_Multiplier,Weapon_Count,Weapons_Summary,Weapons_With_Locations,Default_Installed_Locations,Available_Slot_Capacity_By_Zone,Available_Hardpoints,notes
Annihilator,Yes,IS,23.000000,100.000000,Reflective,EndoSteel,17,Single,0,No,Yes,Yes,Yes,43,,1.8,1.8,1.8,1.8,1.85,1.85,2.0,0.8,0.3,14,10,"4x ClanERSmallLaser, 2x ClanMachineGun, 4x ClanUltraAC5",ClanERSmallLaser@LeftTorso(Front) G1 | ClanERSmallLaser@CenterTorso(Front) G1 | ClanERSmallLaser@CenterTorso(Front) G1 | ClanERSmallLaser@RightTorso(Front) G1 | ClanMachineGun@LeftTorso(Front) G1 | ClanMachineGun@RightTorso(Front) G1 | ClanUltraAC5@LeftArm(Front) G1 | ClanUltraAC5@LeftArm(Front) G1 | ClanUltraAC5@RightArm(Front) G1 | ClanUltraAC5@RightArm(Front) G1,LeftTorsox2 | CenterTorsox2 | RightTorsox2 | LeftArmx2 | RightArmx2,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Front) | Head(Unknown),
Archer,Yes,IS,17.000000,70.000000,Standard,Standard,20,Single,1,Yes,Yes,Yes,Yes,43,,1.1,1.1,1.2,1.2,1.4,1.4,1.5,0.5,0.3,9.7,9,"4x MediumPulseLaser, 2x SSRM2, 2x LRM15, NarcBeacon",MediumPulseLaser@LeftArm(Front) G1 | MediumPulseLaser@CenterTorso(Front) G1 | MediumPulseLaser@CenterTorso(Front) G1 | MediumPulseLaser@RightArm(Front) G1 | SSRM2@LeftArm(Front) G2 | SSRM2@RightArm(Front) G2 | LRM15@LeftTorso(Side) G2 | LRM15@RightTorso(Side) G2 | NarcBeacon@LeftTorso(Front) G3,LeftArmx2 | CenterTorsox2 | RightArmx2 | LeftTorsox2 | RightTorsox1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Front+Side) | RightTorso(Side) | CenterTorso(Front) | Head(Unknown),
ArcticWolf,Yes,Clan,9.500000,40.000000,FerroFiberus,EndoSteel,8,Single,1,Yes,Yes,Yes,Yes,43,,0.7,0.7,0.95,0.95,0.85,0.85,1.0,0.2,0.3,6.5,6,"2x ClanSmallPulseLaser, 4x ClanSSRM4",ClanSmallPulseLaser@RightTorso(Front) G1 | ClanSmallPulseLaser@RightTorso(Front) G1 | ClanSSRM4@leftarm(Front) G2 | ClanSSRM4@special1(Front) G2 | ClanSSRM4@special2(Front) G2 | ClanSSRM4@rightarm(Front) G2,RightTorsox2 | leftarmx1 | special1x1 | special2x1 | rightarmx1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O8) | RightTorso(M0/P0/B0/O8) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O4) | Special2(M0/P0/B0/O4),LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown) | Special1(Unknown) | Special2(Unknown),
Ares,Yes,Clan,15.500000,60.000000,FerroFiberus,Standard,15,Single,0,Yes,Yes,Yes,Yes,46,,0.9,0.9,1.15,1.15,1.1,1.1,1.45,0.9,0.25,8.9,9,"2x ClanERSmallLaser, 3x ClanERMediumLaser, ClanERLargeLaser, 3x ClanLRM10",ClanERSmallLaser@Special1(Front) G1 | ClanERSmallLaser@Special1(Front) G1 | ClanERMediumLaser@LeftArm(Front) G1 | ClanERMediumLaser@RightArm(Front) G1 | ClanERMediumLaser@RightArm(Front) G1 | ClanERLargeLaser@LeftArm(Front) G1 | ClanLRM10@Special2(Side) G2 | ClanLRM10@Special2(Side) G2 | ClanLRM10@Special2(Side) G2,Special1x2 | LeftArmx2 | RightArmx2 | Special2x3,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O2) | Special2(M0/P0/B0/O3),LeftArm(Front) | RightArm(Front) | LeftTorso(Unknown) | RightTorso(Unknown) | Head(Unknown) | Special1(Front) | Special2(Side),
Argus,Yes,IS,14.500000,60.000000,Standard,Standard,13,Single,0,No,Yes,Yes,Yes,43,,0.7,0.7,1.15,1.15,0.85,0.85,1.45,0.9,0.3,8.05,9,"3x MediumLaser, PPC, 4x MachineGun, LRM10",MediumLaser@Lefttorso(Front) G1 | MediumLaser@Righttorso(Front) G1 | MediumLaser@RightArm(Front) G1 | PPC@RightArm(Front) G1 | MachineGun@LeftTorso(Front) G1 | MachineGun@CenterTorso(Front) G1 | MachineGun@CenterTorso(Front) G1 | MachineGun@RightTorso(Front) G1 | LRM10@LeftArm(Front) G2,Lefttorsox1 | Righttorsox1 | RightArmx2 | LeftTorsox1 | CenterTorsox2 | RightTorsox1 | LeftArmx1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Front) | Head(Unknown),
Assassin2,Yes,IS,12.000000,45.000000,FerroFiberus,EndoSteel,6,Single,0,Yes,Yes,Yes,Yes,40,,0.85,0.85,0.85,0.85,0.9,0.9,1.05,0.45,0.25,6.95,7,"2x SmallLaser, 2x MediumLaser, 2x SRM4, LRM5",SmallLaser@LeftArm(Front) G1 | SmallLaser@RightArm(Front) G1 | MediumLaser@LeftArm(Front) G1 | MediumLaser@RightArm(Front) G1 | SRM4@LeftTorso(Front) G2 | SRM4@RightTorso(Front) G2 | LRM5@Special1(Side) G2,LeftArmx2 | RightArmx2 | LeftTorsox1 | RightTorsox1 | Special1x1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O10) | Special1(M0/P0/B0/O2),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | Special1(Side),
Atlas,Yes,IS,28.000000,100.000000,Standard,Standard,17,Single,0,No,Yes,Yes,Yes,43,,1.95,1.95,2.15,2.15,2.45,2.45,2.2,1.1,0.3,16.7,9,"4x MediumLaser, AC20, 2x SSRM2, SRM6, LRM20",MediumLaser@LeftArm(Front) G1 | MediumLaser@Head(Front) G1 | MediumLaser@CenterTorso(Front) G1 | MediumLaser@RightArm(Front) G1 | AC20@Special1(Front) G1 | SSRM2@LeftTorso(Front) G2 | SSRM2@RightTorso(Front) G2 | SRM6@Special2(Front) G2 | LRM20@LeftTorso(Side) G2,LeftArmx1 | Headx1 | CenterTorsox1 | RightArmx1 | Special1x1 | LeftTorsox2 | RightTorsox1 | Special2x1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O10) | RightTorso(M0/P0/B0/O8) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O4) | Special2(M0/P0/B0/O2),LeftArm(Front) | RightArm(Front) | LeftTorso(Front+Side) | RightTorso(Front) | CenterTorso(Front) | Head(Front) | Special1(Front) | Special2(Front),
Avatar,Yes,IS,17.000000,70.000000,Reactive,EndoSteel,12,Single,0,Yes,Yes,Yes,Yes,43,,1.3,1.3,1.5,1.5,1.6,1.6,1.75,0.5,0.3,11.35,10,"4x MachineGun, 2x LargeLaser, 2x LRM5, 2x LRM10",MachineGun@LeftArm(Front) G1 | MachineGun@Special1(Front) G1 | MachineGun@Special1(Front) G1 | MachineGun@RightArm(Front) G1 | LargeLaser@LeftArm(Front) G1 | LargeLaser@RightArm(Front) G1 | LRM5@LeftTorso(Side) G2 | LRM5@RightTorso(Side) G2 | LRM10@LeftTorso(Side) G2 | LRM10@RightTorso(Side) G2,LeftArmx2 | Special1x2 | RightArmx2 | LeftTorsox2 | RightTorsox2,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O2),LeftArm(Front) | RightArm(Front) | LeftTorso(Side) | RightTorso(Side) | Head(Unknown) | Special1(Front),
Awesome,Yes,IS,23.000000,80.000000,Standard,Standard,28,Single,0,No,Yes,Yes,Yes,43,,1.45,1.45,1.55,1.55,1.65,1.65,1.75,1.25,0.3,12.6,6,"SmallPulseLaser, 3x ERPPC, UltraAC5, LRM5",SmallPulseLaser@Head(Front) G1 | ERPPC@Lefttorso(Front) G1 | ERPPC@righttorso(Front) G1 | ERPPC@RightArm(Front) G1 | UltraAC5@LeftArm(Front) G1 | LRM5@CenterTorso(Side) G2,Headx1 | Lefttorsox1 | righttorsox1 | RightArmx1 | LeftArmx1 | CenterTorsox1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Unknown) | RightTorso(Unknown) | CenterTorso(Side) | Head(Front),
Battlemaster,Yes,IS,20.500000,85.000000,Standard,EndoSteel,25,Single,0,No,Yes,Yes,Yes,43,,1.45,1.45,1.6,1.6,1.8,1.8,2.05,0.95,0.3,13,10,"PPC, 6x MediumLaser, 2x MachineGun, SRM6",PPC@Special2(Front) G1 | MediumLaser@RightTorso(Front) G1 | MediumLaser@RightTorso(Front) G1 | MediumLaser@RightTorso(Rear) G1 | MediumLaser@LeftTorso(Front) G1 | MediumLaser@LeftTorso(Front) G1 | MediumLaser@LeftTorso(Rear) G1 | MachineGun@LeftArm(Front) G1 | MachineGun@LeftArm(Front) G1 | SRM6@Special1(Front) G2,Special2x1 | RightTorsox3 | LeftTorsox3 | LeftArmx2 | Special1x1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O4) | LeftTorso(M0/P0/B0/O9) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O3) | Special2(M0/P0/B0/O4),LeftArm(Front) | RightArm(Unknown) | LeftTorso(Front+Rear) | RightTorso(Front+Rear) | CenterTorso(Unknown) | Head(Unknown) | Special1(Front) | Special2(Front),
Battlemaster2c,Yes,Clan,15.500000,85.000000,Standard,EndoSteel,25,Single,0,No,Yes,Yes,Yes,39,,1.45,1.45,1.6,1.6,1.8,1.8,2.05,0.95,0.3,13,10,"ClanERPPC, 6x ClanERMediumLaser, 2x ClanGaussRifle, ClanSSRM6",ClanERPPC@RightArm(Front) G1 | ClanERMediumLaser@RightTorso(Front) G1 | ClanERMediumLaser@RightTorso(Front) G1 | ClanERMediumLaser@RightTorso(Rear) G1 | ClanERMediumLaser@LeftTorso(Front) G1 | ClanERMediumLaser@LeftTorso(Front) G1 | ClanERMediumLaser@LeftTorso(Rear) G1 | ClanGaussRifle@LeftArm(Front) G1 | ClanGaussRifle@LeftArm(Front) G1 | ClanSSRM6@Special1(Front) G2,RightArmx1 | RightTorsox3 | LeftTorsox3 | LeftArmx2 | Special1x1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O4) | LeftTorso(M0/P0/B0/O9) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O3),LeftArm(Front) | RightArm(Front) | LeftTorso(Front+Rear) | RightTorso(Front+Rear) | CenterTorso(Unknown) | Head(Unknown) | Special1(Front),
Behemoth,Yes,Clan,22.000000,100.000000,Standard,Standard,21,Single,1,Yes,Yes,Yes,Yes,43,,1.95,1.95,2.15,2.15,2.45,2.45,2.2,1.1,0.3,16.7,7,"4x ClanLargePulseLaser, 2x ClanGaussRifle, LargeLaser",ClanLargePulseLaser@LeftTorso(Front) G1 | ClanLargePulseLaser@LeftTorso(Front) G1 | ClanLargePulseLaser@RightTorso(Front) G1 | ClanLargePulseLaser@RightTorso(Front) G1 | ClanGaussRifle@LeftArm(Front) G1 | ClanGaussRifle@RightArm(Front) G1 | LargeLaser@Special1(Front) G1,LeftTorsox2 | RightTorsox2 | LeftArmx1 | RightArmx1 | Special1x1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O2),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | Head(Unknown) | Special1(Front),
Behemoth2,Yes,Clan,22.000000,100.000000,Standard,Standard,21,Single,1,Yes,Yes,Yes,Yes,43,,1.95,1.95,2.15,2.15,2.45,2.45,2.2,1.1,0.3,16.7,7,"4x ClanLargePulseLaser, 2x ClanGaussRifle, LargeLaser",ClanLargePulseLaser@LeftTorso(Front) G1 | ClanLargePulseLaser@LeftTorso(Front) G1 | ClanLargePulseLaser@RightTorso(Front) G1 | ClanLargePulseLaser@RightTorso(Front) G1 | ClanGaussRifle@LeftArm(Front) G1 | ClanGaussRifle@RightArm(Front) G1 | LargeLaser@Special1(Front) G1,LeftTorsox2 | RightTorsox2 | LeftArmx1 | RightArmx1 | Special1x1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O2),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | Head(Unknown) | Special1(Front),
Blackhawk,Yes,Clan,10.000000,50.000000,FerroFiberus,EndoSteel,12,Single,0,Yes,Yes,Yes,Yes,43,,0.9,0.9,0.95,0.95,1.05,1.05,1.2,0.55,0.25,7.8,4,"2x ClanMediumPulseLaser, 2x ClanERPPC",ClanMediumPulseLaser@LeftTorso(Front) G1 | ClanMediumPulseLaser@RightTorso(Front) G1 | ClanERPPC@LeftArm(Front) G1 | ClanERPPC@RightArm(Front) G1,LeftTorsox1 | RightTorsox1 | LeftArmx1 | RightArmx1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O10) | RightTorso(M0/P0/B0/O10) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O2) | Special2(M0/P0/B0/O2),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown) | Special1(Unknown) | Special2(Unknown),
Blackheart,No,Clan,14.000000,70.000000,FerroFiberus,Standard,0,,0,No,Yes,No,No,6,,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.9,0,,,,LeftArm(M0/P3/B0/O0) | RightArm(M0/P3/B0/O0) | LeftTorso(M0/P0/B1/O2) | RightTorso(M0/P0/B1/O2) | CenterTorso(M0/P0/B0/O2),LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown) | CenterTorso(Unknown),"not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud"
Blacklanner,Yes,Clan,12.500000,55.000000,FerroFiberus,EndoSteel,10,Single,0,No,Yes,Yes,Yes,40,ECM,0.9,0.9,1.0,1.0,1.05,1.05,1.15,0.75,0.25,8.05,5,"2x ClanERMediumLaser, ClanERLargeLaser, ClanSRM6, ClanLRM10",ClanERMediumLaser@LeftArm(Front) G1 | ClanERMediumLaser@LeftArm(Front) G1 | ClanERLargeLaser@RightArm(Front) G1 | ClanSRM6@Special2(Side) G2 | ClanLRM10@Special1(Side) G2,LeftArmx2 | RightArmx1 | Special2x1 | Special1x1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | Special1(M0/P0/B0/O12) | Special2(M0/P0/B0/O12),LeftArm(Front) | RightArm(Front) | Special1(Side) | Special2(Side),
Blacknight,Yes,IS,16.000000,75.000000,Standard,Standard,22,Single,0,Yes,Yes,Yes,Yes,43,,1.2,1.2,1.4,1.4,1.55,1.55,1.8,0.8,0.3,11.2,8,"SmallLaser, 4x MediumLaser, 2x LargeLaser, PPC",SmallLaser@Head(Front) G1 | MediumLaser@LeftArm(Front) G1 | MediumLaser@LeftTorso(Front) G1 | MediumLaser@RightTorso(Front) G1 | MediumLaser@RightArm(Front) G1 | LargeLaser@LeftTorso(Front) G1 | LargeLaser@RightTorso(Front) G1 | PPC@RightArm(Front) G1,Headx1 | LeftArmx1 | LeftTorsox2 | RightTorsox2 | RightArmx2,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Front),
Bowman,No,Clan,16.000000,70.000000,FerroFiberus,Standard,0,,0,No,Yes,Yes,No,5,,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.9,0,,,,LeftArm(M0/P2/B2/O2) | RightArm(M4/P0/B0/O0) | Special1(M4/P0/B0/O0) | Special2(M0/P0/B0/O3),LeftArm(Unknown) | RightArm(Unknown) | Special1(Unknown) | Special2(Unknown),"not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud"
Brigand,Yes,IS,7.000000,25.000000,FerroFiberus,Standard,10,Single,0,Yes,Yes,Yes,Yes,40,,0.45,0.45,0.45,0.45,0.55,0.55,0.55,0.4,0.25,4.1,4,"2x MediumPulseLaser, 2x MediumLaser",MediumPulseLaser@LeftArm(Front) G1 | MediumPulseLaser@RightArm(Front) G1 | MediumLaser@Special2(Front) G1 | MediumLaser@Special1(Front) G1,LeftArmx1 | RightArmx1 | Special2x1 | Special1x1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | Special1(M0/P0/B0/O12) | Special2(M0/P0/B0/O12),LeftArm(Front) | RightArm(Front) | Special1(Front) | Special2(Front),
Bushwacker,Yes,IS,15.500000,55.000000,FerroFiberus,Standard,11,Single,0,No,Yes,Yes,Yes,43,,0.9,0.9,1.1,1.1,1.15,1.15,1.35,0.5,0.25,8.4,8,"2x MediumLaser, ERLargeLaser, 2x MachineGun, AC10, 2x LRM5",MediumLaser@CenterTorso(Front) G1 | MediumLaser@CenterTorso(Front) G1 | ERLargeLaser@RightArm(Front) G1 | MachineGun@LeftTorso(Front) G1 | MachineGun@RightTorso(Front) G1 | AC10@LeftArm(Front) G1 | LRM5@Special1(Side) G2 | LRM5@Special1(Side) G2,CenterTorsox2 | RightArmx1 | LeftTorsox1 | RightTorsox1 | LeftArmx1 | Special1x2,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O8) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O4),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Front) | Head(Unknown) | Special1(Side),
Canis,No,Clan,15.000000,80.000000,FerroFiberus,EndoSteel,0,,0,Yes,No,No,No,,,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.9,0,,,,LeftArm(M0/P0/B5/O0) | RightArm(M0/P0/B5/O0) | Special1(M0/P3/B0/O0) | Special2(M0/P3/B0/O0),LeftArm(Unknown) | RightArm(Unknown) | Special1(Unknown) | Special2(Unknown),"not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud"
Catapult,Yes,IS,15.000000,65.000000,Reactive,Standard,10,Single,1,Yes,Yes,Yes,Yes,43,BAP,1.45,1.45,1.35,1.35,1.45,1.45,1.5,0.65,0.3,10.95,4,"2x LargeLaser, 2x LRM20",LargeLaser@LeftTorso(Front) G1 | LargeLaser@RightTorso(Front) G1 | LRM20@LeftArm(Side) G2 | LRM20@RightArm(Side) G2,LeftTorsox1 | RightTorsox1 | LeftArmx1 | RightArmx1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Side) | RightArm(Side) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown),
CauldronBorn,Yes,Clan,13.500000,65.000000,FerroFiberus,EndoSteel,13,Single,0,Yes,Yes,Yes,Yes,43,,0.85,0.85,0.95,0.95,1.15,1.15,1.55,0.85,0.25,8.55,6,"ClanERMediumLaser, ClanUltraAC5, ClanGaussRifle, ClanSSRM2, 2x ClanLRM10",ClanERMediumLaser@LeftTorso(Front) G1 | ClanUltraAC5@LeftArm(Front) G1 | ClanGaussRifle@RightArm(Front) G1 | ClanSSRM2@RightTorso(Front) G2 | ClanLRM10@Special1(Side) G2 | ClanLRM10@Special2(Side) G2,LeftTorsox1 | LeftArmx1 | RightArmx1 | RightTorsox1 | Special1x1 | Special2x1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O8) | RightTorso(M0/P0/B0/O8) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O4) | Special2(M0/P0/B0/O4),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown) | Special1(Side) | Special2(Side),
Chimera,Yes,IS,9.500000,40.000000,Standard,EndoSteel,14,Single,1,Yes,Yes,Yes,Yes,43,BAP,0.95,0.95,1.0,1.0,1.0,1.0,1.1,0.5,0.3,7.8,7,"2x MediumPulseLaser, ERLargeLaser, 4x LRM5",MediumPulseLaser@LeftArm(Front) G1 | MediumPulseLaser@LeftArm(Front) G1 | ERLargeLaser@RightArm(Front) G1 | LRM5@RightTorso(Side) G2 | LRM5@RightTorso(Side) G2 | LRM5@RightTorso(Side) G2 | LRM5@RightTorso(Side) G2,LeftArmx2 | RightArmx1 | RightTorsox4,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Unknown) | RightTorso(Side) | CenterTorso(Unknown) | Head(Unknown),
Commando,Yes,IS,7.000000,25.000000,Standard,Standard,8,Single,0,Yes,Yes,Yes,Yes,43,,0.6,0.6,0.8,0.8,0.75,0.75,0.8,0.45,0.3,5.85,3,"2x MediumLaser, SRM6",MediumLaser@LeftArm(Front) G1 | MediumLaser@RightArm(Front) G1 | SRM6@CenterTorso(Front) G2,LeftArmx1 | RightArmx1 | CenterTorsox1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Unknown) | RightTorso(Unknown) | CenterTorso(Front) | Head(Unknown),
Cougar,Yes,Clan,8.500000,35.000000,FerroFiberus,EndoSteel,10,Single,1,Yes,Yes,Yes,Yes,43,BAP,0.7,0.7,0.7,0.7,0.75,0.75,0.8,0.45,0.25,5.8,5,"2x ClanMediumPulseLaser, ClanMachineGun, 2x ClanLRM10",ClanMediumPulseLaser@LeftArm(Front) G1 | ClanMediumPulseLaser@RightArm(Front) G1 | ClanMachineGun@RightTorso(Front) G1 | ClanLRM10@LeftTorso(Side) G2 | ClanLRM10@RightTorso(Side) G2,LeftArmx1 | RightArmx1 | RightTorsox2 | LeftTorsox1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Side) | RightTorso(Front+Side) | CenterTorso(Unknown) | Head(Unknown),
Crab,No,IS,10.500000,50.000000,FerroFiberus,Standard,0,,0,No,No,Yes,No,1,,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.9,0,,,,LeftArm(M0/P0/B4/O0) | RightArm(M0/P0/B4/O0) | CenterTorso(M0/P0/B2/O0) | Special1(M0/P0/B1/O1),LeftArm(Unknown) | RightArm(Unknown) | CenterTorso(Unknown) | Special1(Unknown),"not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud"
Cyclops,Yes,IS,19.000000,90.000000,FerroFiberus,Standard,16,Single,0,No,Yes,Yes,Yes,45,BAP,1.4,1.4,1.4,1.4,1.55,1.55,1.45,0.8,0.25,11.2,8,"3x MediumPulseLaser, 2x MediumLaser, GaussRifle, SRM4, LRM10",MediumPulseLaser@LeftArm(Front) G1 | MediumPulseLaser@Special1(Front) G1 | MediumPulseLaser@RightArm(Front) G1 | MediumLaser@LeftArm(Front) G1 | MediumLaser@RightArm(Front) G1 | GaussRifle@RightTorso(Front) G1 | SRM4@CenterTorso(Front) G2 | LRM10@LeftTorso(Side) G2,LeftArmx2 | Special1x1 | RightArmx2 | RightTorsox1 | CenterTorsox1 | LeftTorsox1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O2),LeftArm(Front) | RightArm(Front) | LeftTorso(Side) | RightTorso(Front) | CenterTorso(Front) | Head(Unknown) | Special1(Front),
Daishi,Yes,Clan,25.000000,100.000000,Standard,Standard,21,Single,0,No,Yes,Yes,Yes,43,,1.75,1.75,1.9,1.9,2.0,2.0,2.2,1.5,0.3,15.3,6,"3x ClanLargePulseLaser, ClanGaussRifle, 2x ClanSSRM6",ClanLargePulseLaser@RightArm(Front) G1 | ClanLargePulseLaser@RightArm(Front) G1 | ClanLargePulseLaser@RightArm(Front) G1 | ClanGaussRifle@LeftArm(Front) G1 | ClanSSRM6@special1(Front) G2 | ClanSSRM6@special1(Front) G2,RightArmx3 | LeftArmx1 | special1x2,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O8) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O4),LeftArm(Front) | RightArm(Front) | LeftTorso(Unknown) | RightTorso(Unknown) | CenterTorso(Unknown) | Head(Unknown) | Special1(Unknown),
Deimos,Yes,Clan,18.000000,85.000000,Reactive,EndoSteel,17,Single,0,No,Yes,Yes,Yes,43,,1.55,1.55,1.65,1.65,1.75,1.75,2.05,0.7,0.3,12.95,10,"2x ClanERMediumLaser, 6x ClanUltraAC2, 2x ClanLRM15",ClanERMediumLaser@LeftTorso(Front) G1 | ClanERMediumLaser@RightTorso(Front) G1 | ClanUltraAC2@LeftArm(Front) G1 | ClanUltraAC2@LeftArm(Front) G1 | ClanUltraAC2@LeftArm(Front) G1 | ClanUltraAC2@RightArm(Front) G1 | ClanUltraAC2@RightArm(Front) G1 | ClanUltraAC2@RightArm(Front) G1 | ClanLRM15@Special1(Side) G2 | ClanLRM15@Special2(Side) G2,LeftTorsox1 | RightTorsox1 | LeftArmx3 | RightArmx3 | Special1x1 | Special2x1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O9) | RightTorso(M0/P0/B0/O9) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O3) | Special2(M0/P0/B0/O3),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown) | Special1(Side) | Special2(Side),
Dragon,Yes,Clan,17.000000,60.000000,Standard,Standard,13,Single,0,Yes,Yes,Yes,Yes,43,,1.15,1.15,1.4,1.4,1.25,1.25,1.55,0.9,0.3,10.35,5,"3x MediumLaser, ERPPC, LRM10",MediumLaser@LeftArm(Front) G1 | MediumLaser@LeftTorso(Front) G1 | MediumLaser@RightTorso(Front) G1 | ERPPC@RightArm(Front) G1 | LRM10@CenterTorso(Side) G2,LeftArmx1 | LeftTorsox1 | RightTorsox1 | RightArmx1 | CenterTorsox1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Side) | Head(Unknown),
Duangung,No,IS,7.000000,25.000000,FerroFiberus,Standard,0,,0,Yes,No,No,No,,,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.9,0,,,,LeftArm(M0/P0/B2/O0) | RightArm(M0/P0/B2/O0) | CenterTorso(M2/P0/B0/O0) | Special1(M0/P0/B1/O0) | Special2(M0/P0/B1/O0),LeftArm(Unknown) | RightArm(Unknown) | CenterTorso(Unknown) | Special1(Unknown) | Special2(Unknown),"not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud"
Fafnir,Yes,IS,21.000000,100.000000,FerroFiberus,Standard,7,Single,0,No,Yes,No,No,43,,1.8,1.8,1.9,1.9,2.0,2.0,2.2,1.0,0.3,14.9,7,"2x ClanGaussRifle, 2x LargeLaser, 3x MediumLaser",ClanGaussRifle@RightTorso(Front) G1 | ClanGaussRifle@LeftTorso(Front) G1 | LargeLaser@RightArm(Front) G1 | LargeLaser@LeftArm(Front) G1 | MediumLaser@RightTorso(Front) G1 | MediumLaser@CenterTorso(Front) G1 | MediumLaser@LeftTorso(Front) G1,RightTorsox2 | LeftTorsox2 | RightArmx1 | LeftArmx1 | CenterTorsox1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Front) | Head(Unknown),
Flea,Yes,IS,7.000000,20.000000,Standard,Standard,2,Single,0,No,Yes,Yes,Yes,43,,0.4,0.4,0.4,0.4,0.5,0.5,0.65,0.3,0.3,3.85,6,"2x SmallLaser, 2x MediumLaser, 2x MachineGun",SmallLaser@LeftTorso(Rear) G1 | SmallLaser@RightTorso(Rear) G1 | MediumLaser@LeftArm(Front) G1 | MediumLaser@RightArm(Front) G1 | MachineGun@LeftArm(Front) G1 | MachineGun@RightArm(Front) G1,LeftTorsox1 | RightTorsox1 | LeftArmx2 | RightArmx2,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Rear) | RightTorso(Rear) | CenterTorso(Unknown) | Head(Unknown),
Gargoyle,No,Clan,15.000000,80.000000,FerroFiberus,EndoSteel,0,,0,No,Yes,No,No,6,,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.9,0,,,,LeftArm(M1/P0/B0/O3) | RightArm(M1/P0/B0/O3) | LeftTorso(M0/P2/B2/O0) | RightTorso(M0/P2/B2/O0),LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown),"not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud"
Gesu,No,Clan,9.750000,45.000000,FerroFiberus,EndoSteel,0,,0,No,No,Yes,No,,,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.9,0,,,,LeftArm(M0/P2/B0/O0) | RightArm(M0/P2/B0/O0) | LeftTorso(M0/P0/B2/O0) | RightTorso(M0/P0/B2/O0) | Special1(M2/P0/B0/O0) | Special2(M2/P0/B0/O0),LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown) | Special1(Unknown) | Special2(Unknown),"not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud"
Gladiator,Yes,Clan,24.000000,95.000000,FerroFiberus,Standard,19,Single,1,Yes,Yes,Yes,Yes,43,,1.4,1.4,1.65,1.65,1.75,1.75,2.0,0.95,0.25,12.8,12,"5x ClanSmallPulseLaser, ClanERSmallLaser, 3x ClanMediumPulseLaser, ClanLargePulseLaser, 2x SRM6",ClanSmallPulseLaser@RightTorso(Front) G1 | ClanSmallPulseLaser@RightTorso(Front) G1 | ClanSmallPulseLaser@RightArm(Front) G1 | ClanSmallPulseLaser@RightArm(Front) G1 | ClanSmallPulseLaser@RightArm(Front) G1 | ClanERSmallLaser@LeftTorso(Front) G1 | ClanMediumPulseLaser@RightArm(Front) G1 | ClanMediumPulseLaser@RightArm(Front) G1 | ClanMediumPulseLaser@RightArm(Front) G1 | ClanLargePulseLaser@LeftArm(Front) G1 | SRM6@LeftArm(Front) G2 | SRM6@LeftArm(Front) G2,RightTorsox2 | RightArmx6 | LeftTorsox1 | LeftArmx3,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown),
Grizzly,Yes,Clan,16.000000,70.000000,FerroFiberus,Standard,11,Single,1,Yes,Yes,Yes,Yes,40,,1.2,1.2,1.1,1.1,1.4,1.4,1.5,0.5,0.25,9.65,6,"2x ClanSmallPulseLaser, ClanMediumPulseLaser, ClanLargePulseLaser, ClanGaussRifle, CLANLRM10",ClanSmallPulseLaser@LeftArm(Front) G1 | ClanSmallPulseLaser@LeftArm(Front) G1 | ClanMediumPulseLaser@RightTorso(Front) G1 | ClanLargePulseLaser@LeftArm(Front) G1 | ClanGaussRifle@RightArm(Front) G1 | CLANLRM10@LeftTorso(Side) G2,LeftArmx3 | RightTorsox1 | RightArmx1 | LeftTorsox1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12),LeftArm(Front) | RightArm(Front) | LeftTorso(Side) | RightTorso(Front),
Hauptmann,Yes,Clan,21.000000,95.000000,Standard,Standard,20,Single,0,Yes,Yes,Yes,Yes,43,ECM,1.7,1.7,1.8,1.8,1.9,1.9,2.1,1.1,0.3,14.3,8,"ClanERSmallLaser, 2x ClanMediumPulseLaser, 2x ClanLargePulseLaser, ClanUltraAC20, 2x ClanSSRM2",ClanERSmallLaser@Head(Front) G1 | ClanMediumPulseLaser@LeftArm(Front) G1 | ClanMediumPulseLaser@RightArm(Front) G1 | ClanLargePulseLaser@LeftArm(Front) G1 | ClanLargePulseLaser@RightArm(Front) G1 | ClanUltraAC20@Special1(Front) G1 | ClanSSRM2@LeftTorso(Front) G2 | ClanSSRM2@RightTorso(Front) G2,Headx1 | LeftArmx2 | RightArmx2 | Special1x1 | LeftTorsox1 | RightTorsox1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O8) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O4),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Front) | Special1(Front),
Hellhound,Yes,Clan,10.500000,50.000000,FerroFiberus,EndoSteel,12,Single,1,Yes,Yes,Yes,Yes,43,,0.9,0.9,0.95,0.95,1.05,1.05,1.2,0.55,0.25,7.8,6,"3x ClanMediumPulseLaser, ClanUltraAC2, ClanUltraAC5, ClanLRM10",ClanMediumPulseLaser@LeftArm(Front) G1 | ClanMediumPulseLaser@LeftArm(Front) G1 | ClanMediumPulseLaser@RightArm(Front) G1 | ClanUltraAC2@RightTorso(Front) G1 | ClanUltraAC5@RightTorso(Front) G1 | ClanLRM10@LeftTorso(Side) G2,LeftArmx2 | RightArmx1 | RightTorsox2 | LeftTorsox1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Side) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown),
Hellspawn,Yes,IS,10.000000,45.000000,Standard,Standard,9,Single,1,Yes,Yes,Yes,Yes,43,ECM,0.5,0.5,0.8,0.8,0.85,0.85,1.15,0.5,0.3,6.25,6,"2x MediumPulseLaser, LargePulseLaser, 2x SSRM2, LRM10",MediumPulseLaser@LeftArm(Front) G1 | MediumPulseLaser@LeftArm(Front) G1 | LargePulseLaser@LeftArm(Front) G1 | SSRM2@RightArm(Front) G2 | SSRM2@Special1(Front) G2 | LRM10@LeftTorso(Front) G2,LeftArmx3 | RightArmx1 | Special1x1 | LeftTorsox1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O8) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O4),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Unknown) | CenterTorso(Unknown) | Head(Unknown) | Special1(Front),
Highlander,Yes,IS,21.500000,90.000000,Standard,Standard,17,Single,1,Yes,Yes,Yes,Yes,43,,1.75,1.45,1.7,1.7,1.8,1.8,2.0,0.85,0.3,13.35,7,"4x MediumPulseLaser, GaussRifle, 2x SRM6",MediumPulseLaser@RightTorso(Front) G1 | MediumPulseLaser@RightTorso(Front) G1 | MediumPulseLaser@RightArm(Front) G1 | MediumPulseLaser@RightArm(Front) G1 | GaussRifle@LeftArm(Front) G1 | SRM6@LeftTorso(Front) G2 | SRM6@LeftTorso(Front) G2,RightTorsox2 | RightArmx2 | LeftArmx1 | LeftTorsox2,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown),
Hollander,Yes,IS,10.000000,45.000000,FerroFiberus,EndoSteel,12,Single,0,No,Yes,Yes,Yes,42,,0.85,0.85,0.85,0.85,0.9,0.9,1.05,0.45,0.25,6.95,6,"2x SmallPulseLaser, 3x MediumPulseLaser, GaussRifle",SmallPulseLaser@Special2(Front) G1 | SmallPulseLaser@Special2(Front) G1 | MediumPulseLaser@LeftArm(Front) G1 | MediumPulseLaser@LeftTorso(Front) G1 | MediumPulseLaser@RightArm(Front) G1 | GaussRifle@Special1(Front) G1,Special2x2 | LeftArmx1 | LeftTorsox1 | RightArmx1 | Special1x1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | Special1(M0/P0/B0/O12) | Special2(M0/P0/B0/O2),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | Special1(Front) | Special2(Front),
Hunchback,Yes,IS,13.500000,50.000000,Standard,Standard,10,Single,0,Yes,Yes,Yes,Yes,43,ECM,1.0,1.0,1.1,1.1,1.2,1.2,1.5,0.5,0.3,8.9,6,"SmallLaser, 4x MediumLaser, AC10",SmallLaser@Head(Front) G1 | MediumLaser@LeftArm(Front) G1 | MediumLaser@LeftArm(Front) G1 | MediumLaser@RightArm(Front) G1 | MediumLaser@RightArm(Front) G1 | AC10@Special1(Front) G1,Headx1 | LeftArmx2 | RightArmx2 | Special1x1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O8) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O4),LeftArm(Front) | RightArm(Front) | LeftTorso(Unknown) | RightTorso(Unknown) | CenterTorso(Unknown) | Head(Front) | Special1(Front),
Kodiak,Yes,IS,22.000000,100.000000,Standard,EndoSteel,20,Single,1,Yes,Yes,Yes,Yes,43,,1.5,1.5,1.9,1.9,1.5,1.5,2,1.1,0.3,13.2,7,"4x ClanERMediumLaser, ClanUltraAC20, 2x ClanSSRM6",ClanERMediumLaser@leftArm(Front) G1 | ClanERMediumLaser@leftArm(Front) G1 | ClanERMediumLaser@rightarm(Front) G1 | ClanERMediumLaser@rightarm(Front) G1 | ClanUltraAC20@RightTorso(Front) G1 | ClanSSRM6@LeftTorso(Front) G2 | ClanSSRM6@LeftTorso(Front) G2,leftArmx2 | rightarmx2 | RightTorsox1 | LeftTorsox2,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown),
Loki,Yes,Clan,17.500000,65.000000,Standard,Standard,13,Single,0,No,Yes,Yes,Yes,43,ECM,1.3,1.3,1.4,1.4,1.5,1.5,1.55,0.6,0.3,10.85,7,"2x ClanERMediumLaser, 2x ClanMachineGun, 2x ClanUltraAC5, ClanSSRM6",ClanERMediumLaser@LeftArm(Front) G1 | ClanERMediumLaser@RightArm(Front) G1 | ClanMachineGun@LeftTorso(Front) G1 | ClanMachineGun@RightTorso(Front) G1 | ClanUltraAC5@LeftArm(Front) G1 | ClanUltraAC5@RightArm(Front) G1 | ClanSSRM6@Special1(Front) G2,LeftArmx2 | RightArmx2 | LeftTorsox1 | RightTorsox1 | Special1x1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O10) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O2),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown) | Special1(Front),
Longbow,Yes,IS,20.500000,85.000000,Standard,Standard,11,Single,0,No,Yes,Yes,Yes,43,,1.6,1.6,1.65,1.65,1.2,1.2,1.85,1.0,0.3,12.05,6,"2x MediumLaser, 2x LRM5, 2x LRM20",MediumLaser@LeftTorso(Front) G1 | MediumLaser@RightTorso(Front) G1 | LRM5@LeftTorso(Front) G2 | LRM5@RightTorso(Front) G2 | LRM20@LeftArm(Front) G2 | LRM20@RightArm(Front) G2,LeftTorsox2 | RightTorsox2 | LeftArmx1 | RightArmx1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown),
MadCat_MkII,Yes,Clan,18.500000,90.000000,Reflective,EndoSteel,17,Single,1,Yes,Yes,Yes,Yes,43,,1.8,1.8,1.8,1.8,1.85,1.85,2.0,0.8,0.3,14,12,"4x ClanERSmallLaser, 2x ClanMachineGun, 4x ClanUltraAC5, 2x ClanLRM15",ClanERSmallLaser@LeftTorso(Front) G1 | ClanERSmallLaser@CenterTorso(Front) G1 | ClanERSmallLaser@CenterTorso(Front) G1 | ClanERSmallLaser@RightTorso(Front) G1 | ClanMachineGun@LeftTorso(Front) G1 | ClanMachineGun@RightTorso(Front) G1 | ClanUltraAC5@LeftArm(Front) G1 | ClanUltraAC5@LeftArm(Front) G1 | ClanUltraAC5@RightArm(Front) G1 | ClanUltraAC5@RightArm(Front) G1 | ClanLRM15@Special2(Side) G2 | ClanLRM15@Special1(Side) G2,LeftTorsox2 | CenterTorsox2 | RightTorsox2 | LeftArmx2 | RightArmx2 | Special2x1 | Special1x1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O8) | RightTorso(M0/P0/B0/O8) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O4) | Special2(M0/P0/B0/O4),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Front) | Head(Unknown) | Special1(Side) | Special2(Side),
Madcat,Yes,Clan,16.500000,75.000000,FerroFiberus,EndoSteel,17,Single,0,Yes,Yes,Yes,Yes,43,,1.1,1.1,1.2,1.2,1.4,1.4,1.5,0.5,0.25,9.65,8,"2x ClanMediumPulseLaser, 2x ClanERLargeLaser, 2x ClanMachineGun, 2x ClanLRM10",ClanMediumPulseLaser@LeftArm(Front) G1 | ClanMediumPulseLaser@RightArm(Front) G1 | ClanERLargeLaser@LeftArm(Front) G1 | ClanERLargeLaser@RightArm(Front) G1 | ClanMachineGun@LeftTorso(Front) G1 | ClanMachineGun@RightTorso(Front) G1 | ClanLRM10@Special1(Side) G2 | ClanLRM10@Special2(Side) G2,LeftArmx2 | RightArmx2 | LeftTorsox1 | RightTorsox1 | Special1x1 | Special2x1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O9) | RightTorso(M0/P0/B0/O9) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O3) | Special2(M0/P0/B0/O3),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown) | Special1(Side) | Special2(Side),
Masakari,Yes,Clan,18.000000,85.000000,FerroFiberus,Standard,15,Single,0,Yes,Yes,Yes,Yes,43,,1.25,1.25,1.4,1.4,1.45,1.45,1.8,0.85,0.25,11.1,7,"2x ClanERMediumLaser, 2x ClanUltraAC2, ClanGaussRifle, 2x ClanLRM10",ClanERMediumLaser@RightTorso(Front) G1 | ClanERMediumLaser@RightTorso(Front) G1 | ClanUltraAC2@RightArm(Front) G1 | ClanUltraAC2@RightArm(Front) G1 | ClanGaussRifle@LeftArm(Front) G1 | ClanLRM10@LeftTorso(Side) G2 | ClanLRM10@LeftTorso(Side) G2,RightTorsox2 | RightArmx2 | LeftArmx1 | LeftTorsox2,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Side) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown),
Mauler,Yes,IS,26.000000,90.000000,FerroFiberus,Standard,11,Single,0,No,Yes,Yes,Yes,43,,1.4,1.4,1.4,1.4,1.55,1.55,1.45,0.8,0.25,11.2,10,"2x GaussRifle, 2x SSRM4, 6x LRM5",GaussRifle@LeftArm(Front) G1 | GaussRifle@RightArm(Front) G1 | SSRM4@LeftTorso(Front) G2 | SSRM4@RightTorso(Front) G2 | LRM5@LeftTorso(Side) G2 | LRM5@LeftTorso(Side) G2 | LRM5@LeftTorso(Side) G2 | LRM5@RightTorso(Side) G2 | LRM5@RightTorso(Side) G2 | LRM5@RightTorso(Side) G2,LeftArmx1 | RightArmx1 | LeftTorsox4 | RightTorsox4,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Front+Side) | RightTorso(Front+Side) | CenterTorso(Unknown) | Head(Unknown),
Novacat,Yes,Clan,16.000000,70.000000,Reactive,EndoSteel,17,Single,1,Yes,Yes,Yes,Yes,43,ECM,1.55,1.55,1.45,1.45,1.5,1.5,1.7,0.75,0.3,11.75,7,"5x ClanMediumPulseLaser, 2x ClanERLargeLaser",ClanMediumPulseLaser@LeftArm(Front) G1 | ClanMediumPulseLaser@LeftArm(Front) G1 | ClanMediumPulseLaser@LeftArm(Front) G1 | ClanMediumPulseLaser@LeftTorso(Front) G1 | ClanMediumPulseLaser@RightTorso(Front) G1 | ClanERLargeLaser@RightArm(Front) G1 | ClanERLargeLaser@RightArm(Front) G1,LeftArmx3 | LeftTorsox1 | RightTorsox1 | RightArmx2,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown),
Osiris,Yes,IS,9.500000,30.000000,FerroFiberus,EndoSteel,11,Single,1,Yes,Yes,Yes,Yes,43,,0.6,0.6,0.6,0.6,0.65,0.65,0.75,0.3,0.25,5,5,"SmallPulseLaser, 2x MediumPulseLaser, SSRM2, NarcBeacon",SmallPulseLaser@special2(Front) G1 | MediumPulseLaser@special1(Front) G1 | MediumPulseLaser@RightArm(Front) G1 | SSRM2@LeftArm(Front) G2 | NarcBeacon@LeftArm(Front) G3,special2x1 | special1x1 | RightArmx1 | LeftArmx2,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O1) | Special2(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Unknown) | RightTorso(Unknown) | Head(Unknown) | Special1(Unknown) | Special2(Unknown),
Owens,Yes,IS,9.750000,35.000000,Standard,Standard,5,Single,0,No,Yes,Yes,Yes,39,ECM,0.9,0.9,0.8,0.8,0.8,0.8,1.05,0.35,0.3,6.7,5,"2x SmallLaser, MediumLaser, 2x SRM6",SmallLaser@CenterTorso(Front) G1 | SmallLaser@CenterTorso(Front) G1 | MediumLaser@Head(Front) G1 | SRM6@LeftArm(Front) G2 | SRM6@RightArm(Front) G2,CenterTorsox2 | Headx1 | LeftArmx1 | RightArmx1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O8) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Unknown) | RightTorso(Unknown) | CenterTorso(Front) | Head(Front),
Puma,Yes,Clan,7.500000,35.000000,FerroFiberus,EndoSteel,10,Single,1,Yes,Yes,Yes,Yes,43,,0.8,0.8,0.6,0.6,0.6,0.6,0.85,0.45,0.25,5.55,4,"2x ClanSmallPulseLaser, 2x ClanLRM20",ClanSmallPulseLaser@LeftTorso(Front) G1 | ClanSmallPulseLaser@RightTorso(Front) G1 | ClanLRM20@LeftArm(Front) G2 | ClanLRM20@RightArm(Front) G2,LeftTorsox1 | RightTorsox1 | LeftArmx1 | RightArmx1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown),
Raven,Yes,IS,10.250000,35.000000,FerroFiberus,Standard,7,Single,0,No,Yes,Yes,Yes,43,"BAP, ECM",0.6,0.65,0.65,0.65,0.7,0.7,0.75,0.25,0.25,5.2,5,"2x MediumLaser, SRM6, LRM5, NarcBeacon",MediumLaser@RightArm(Front) G1 | MediumLaser@RightArm(Front) G1 | SRM6@RightTorso(Front) G2 | LRM5@RightTorso(Side) G2 | NarcBeacon@LeftArm(Front) G3,RightArmx2 | RightTorsox2 | LeftArmx1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Unknown) | RightTorso(Front+Side) | CenterTorso(Unknown) | Head(Unknown),
Rifleman,Yes,IS,12.500000,60.000000,FerroFiberus,EndoSteel,17,Single,0,Yes,Yes,Yes,Yes,43,,0.9,0.9,1.0,1.0,1.5,1.5,1.3,0.7,0.25,9.05,6,"2x MediumLaser, 2x LargeLaser, 2x PPC",MediumLaser@Special1(Front) G1 | MediumLaser@Special1(Front) G1 | LargeLaser@LeftArm(Front) G1 | LargeLaser@RightArm(Front) G1 | PPC@LeftArm(Front) G1 | PPC@RightArm(Front) G1,Special1x2 | LeftArmx2 | RightArmx2,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O2),LeftArm(Front) | RightArm(Front) | LeftTorso(Unknown) | RightTorso(Unknown) | Head(Unknown) | Special1(Front),
Ryoken,Yes,IS,12.500000,55.000000,FerroFiberus,EndoSteel,10,Single,1,Yes,Yes,Yes,Yes,43,ECM,0.9,0.9,1.0,1.0,1.05,1.05,1.15,0.75,0.25,8.05,6,"4x ClanMediumPulseLaser, 2x ClanSSRM6",ClanMediumPulseLaser@LeftArm(Front) G1 | ClanMediumPulseLaser@LeftArm(Front) G1 | ClanMediumPulseLaser@RightArm(Front) G1 | ClanMediumPulseLaser@RightArm(Front) G1 | ClanSSRM6@LeftTorso(Front) G2 | ClanSSRM6@RightTorso(Front) G2,LeftArmx2 | RightArmx2 | LeftTorsox1 | RightTorsox1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown),
Shadowcat,Yes,Clan,9.750000,45.000000,FerroFiberus,EndoSteel,10,Single,1,Yes,Yes,Yes,Yes,43,BAP,0.85,0.85,0.85,0.85,0.9,0.9,1.05,0.45,0.25,6.95,5,"2x ClanMediumPulseLaser, ClanGaussRifle, 2x ClanSSRM4",ClanMediumPulseLaser@RightTorso(Front) G1 | ClanMediumPulseLaser@RightArm(Front) G1 | ClanGaussRifle@LeftArm(Front) G1 | ClanSSRM4@LeftTorso(Front) G2 | ClanSSRM4@RightTorso(Front) G2,RightTorsox2 | RightArmx1 | LeftArmx1 | LeftTorsox1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown),
Solitaire,Yes,Clan,7.000000,25.000000,FerroFiberus,EndoSteel,10,Single,0,Yes,Yes,Yes,Yes,46,,0.6,0.6,0.6,0.6,0.55,0.55,0.8,0.4,0.25,4.95,4,"ClanERSmallLaser, 2x ClanERMediumLaser, ClanERLargeLaser",ClanERSmallLaser@LeftTorso(Front) G1 | ClanERMediumLaser@LeftTorso(Front) G1 | ClanERMediumLaser@CenterTorso(Front) G1 | ClanERLargeLaser@Special1(Front) G1,LeftTorsox2 | CenterTorsox1 | Special1x1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O3),LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Front) | RightTorso(Unknown) | CenterTorso(Front) | Head(Unknown) | Special1(Front),
Sunder,Yes,IS,17.500000,90.000000,Standard,Standard,17,Single,0,Yes,Yes,Yes,Yes,43,,1.35,1.35,1.55,1.55,1.65,1.65,2.0,1.0,0.3,12.4,10,"6x MediumLaser, PPC, 2x MachineGun, SRM6",MediumLaser@LeftTorso(Front) G1 | MediumLaser@LeftTorso(Front) G1 | MediumLaser@LeftTorso(Rear) G1 | MediumLaser@RightTorso(Rear) G1 | MediumLaser@RightTorso(Front) G1 | MediumLaser@RightTorso(Front) G1 | PPC@LeftArm(Front) G1 | MachineGun@RightArm(Front) G1 | MachineGun@RightArm(Front) G1 | SRM6@CenterTorso(Front) G2,LeftTorsox3 | RightTorsox3 | LeftArmx1 | RightArmx2 | CenterTorsox1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Front+Rear) | RightTorso(Front+Rear) | CenterTorso(Front) | Head(Unknown),
Templar,Yes,IS,20.500000,85.000000,Standard,EndoSteel,25,Single,1,Yes,Yes,Yes,Yes,43,,1.45,1.45,1.6,1.6,1.8,1.8,2.05,0.95,0.3,13,11,"6x MediumPulseLaser, PPC, 2x MachineGun, AC20, SRM4",MediumPulseLaser@LeftArm(Front) G1 | MediumPulseLaser@LeftTorso(Front) G1 | MediumPulseLaser@RightTorso(Front) G1 | MediumPulseLaser@RightArm(Front) G1 | MediumPulseLaser@LeftTorso(Rear) G1 | MediumPulseLaser@RightTorso(Rear) G1 | PPC@LeftArm(Front) G1 | MachineGun@LeftTorso(Front) G1 | MachineGun@RightTorso(Front) G1 | AC20@RightArm(Front) G1 | SRM4@RightTorso(Front) G2,LeftArmx2 | LeftTorsox3 | RightTorsox4 | RightArmx2,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Front+Rear) | RightTorso(Front+Rear) | CenterTorso(Unknown) | Head(Unknown),
Thanatos,Yes,IS,18.000000,75.000000,FerroFiberus,EndoSteel,20,Single,1,Yes,Yes,Yes,Yes,43,,0.9,0.9,1.3,1.3,1.2,1.2,1.6,0.85,0.25,9.5,7,"MediumPulseLaser, 2x MediumLaser, 2x LargePulseLaser, 2x LRM10",MediumPulseLaser@LeftTorso(Front) G1 | MediumLaser@LeftArm(Front) G1 | MediumLaser@RightTorso(Front) G1 | LargePulseLaser@LeftArm(Front) G1 | LargePulseLaser@RightTorso(Front) G1 | LRM10@RightArm(Front) G2 | LRM10@RightArm(Front) G2,LeftTorsox1 | LeftArmx2 | RightTorsox2 | RightArmx2,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown),
Thor,Yes,Clan,19.000000,70.000000,FerroFiberus,Standard,20,Single,1,Yes,Yes,Yes,Yes,43,,1.1,1.1,1.2,1.2,1.4,1.4,1.5,0.5,0.25,9.65,7,"2x ClanMediumPulseLaser, ClanERPPC, 2x ClanMachineGun, ClanUltraAC10, CLANLRM10",ClanMediumPulseLaser@LeftTorso(Front) G1 | ClanMediumPulseLaser@RightTorso(Front) G1 | ClanERPPC@RightArm(Front) G1 | ClanMachineGun@LeftTorso(Front) G1 | ClanMachineGun@RightTorso(Front) G1 | ClanUltraAC10@LeftArm(Front) G1 | CLANLRM10@Special1(Side) G2,LeftTorsox2 | RightTorsox2 | RightArmx1 | LeftArmx1 | Special1x1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O10) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O2),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown) | Special1(Side),
Uller,Yes,Clan,8.000000,30.000000,FerroFiberus,EndoSteel,8,Single,0,Yes,Yes,Yes,Yes,43,ECM,0.45,0.45,0.5,0.5,0.6,0.6,0.65,0.45,0.25,4.45,4,"ClanERSmallLaser, ClanERMediumLaser, ClanUltraAC5, ClanSRM6",ClanERSmallLaser@LeftArm(Front) G1 | ClanERMediumLaser@RightArm(Front) G1 | ClanUltraAC5@RightArm(Front) G1 | ClanSRM6@LeftArm(Front) G2,LeftArmx2 | RightArmx2,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Unknown) | RightTorso(Unknown) | CenterTorso(Unknown) | Head(Unknown),
Uziel,Yes,IS,11.500000,50.000000,Standard,EndoSteel,10,Single,1,Yes,Yes,Yes,Yes,45,,1.2,1.2,1.05,1.05,1.15,1.15,1.0,0.45,0.3,8.55,7,"2x SmallPulseLaser, PPC, 2x MachineGun, UltraAC5, LRM10",SmallPulseLaser@LeftTorso(Front) G1 | SmallPulseLaser@RightTorso(Front) G1 | PPC@LeftArm(Front) G1 | MachineGun@LeftTorso(Front) G1 | MachineGun@RightTorso(Front) G1 | UltraAC5@RightArm(Front) G1 | LRM10@Special1(Side) G2,LeftTorsox2 | RightTorsox2 | LeftArmx1 | RightArmx1 | Special1x1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O4),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | Head(Unknown) | Special1(Side),
Victor,Yes,IS,17.000000,80.000000,Standard,EndoSteel,10,Single,1,Yes,Yes,Yes,Yes,43,ECM,1.3,1.3,1.3,1.3,1.25,1.25,1.65,1.1,0.3,10.75,7,"2x MediumLaser, 2x MachineGun, AC20, 2x SRM4",MediumLaser@LeftArm(Front) G1 | MediumLaser@LeftArm(Front) G1 | MachineGun@LeftTorso(Front) G1 | MachineGun@RightTorso(Front) G1 | AC20@RightArm(Front) G1 | SRM4@LeftTorso(Front) G2 | SRM4@RightTorso(Front) G2,LeftArmx2 | LeftTorsox2 | RightTorsox2 | RightArmx1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown),
Vulture,Yes,Clan,16.000000,60.000000,Reactive,EndoSteel,12,Single,0,No,Yes,Yes,Yes,43,BAP,1.3,1.3,1.5,1.5,1.6,1.6,1.75,0.5,0.3,11.35,10,"4x ClanMachineGun, 2x ClanLargePulseLaser, 2x ClanLRM5, 2x ClanLRM10",ClanMachineGun@LeftArm(Front) G1 | ClanMachineGun@Special1(Front) G1 | ClanMachineGun@Special1(Front) G1 | ClanMachineGun@RightArm(Front) G1 | ClanLargePulseLaser@LeftArm(Front) G1 | ClanLargePulseLaser@RightArm(Front) G1 | ClanLRM5@LeftTorso(Side) G2 | ClanLRM5@RightTorso(Side) G2 | ClanLRM10@LeftTorso(Side) G2 | ClanLRM10@RightTorso(Side) G2,LeftArmx2 | Special1x2 | RightArmx2 | LeftTorsox2 | RightTorsox2,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O2),LeftArm(Front) | RightArm(Front) | LeftTorso(Side) | RightTorso(Side) | Head(Unknown) | Special1(Front),
Warhammer,Yes,IS,15.000000,70.000000,Standard,Standard,17,Single,0,Yes,Yes,Yes,Yes,43,,1.55,1.55,1.45,1.45,1.5,1.5,1.7,0.75,0.3,11.75,5,"2x MediumLaser, 2x ERPPC, SRM6",MediumLaser@RightTorso(Front) G1 | MediumLaser@LeftTorso(Front) G1 | ERPPC@RightArm(Front) G1 | ERPPC@LeftArm(Front) G1 | SRM6@special2(Front) G2,RightTorsox1 | LeftTorsox1 | RightArmx1 | LeftArmx1 | special2x1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | Special2(M0/P0/B0/O2),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | Special2(Unknown),
Wolfhound,Yes,Clan,10.250000,35.000000,FerroFiberus,EndoSteel,14,Single,0,Yes,Yes,Yes,Yes,43,ECM,0.5,0.5,0.7,0.7,0.75,0.75,0.95,0.5,0.25,5.6,5,"3x ClanMediumPulseLaser, ClanERMediumLaser, ClanERLargeLaser",ClanMediumPulseLaser@LeftTorso(Front) G1 | ClanMediumPulseLaser@CenterTorso(Front) G1 | ClanMediumPulseLaser@RightTorso(Front) G1 | ClanERMediumLaser@CenterTorso(Rear) G1 | ClanERLargeLaser@RightArm(Front) G1,LeftTorsox1 | CenterTorsox2 | RightTorsox1 | RightArmx1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Unknown) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Front+Rear) | Head(Unknown),
Zeus,Yes,IS,22.000000,80.000000,FerroFiberus,Standard,22,Single,0,Yes,Yes,Yes,Yes,43,,1.1,1.1,1.25,1.25,1.45,1.45,1.55,0.8,0.25,10.2,7,"4x MediumPulseLaser, ERLargeLaser, ERPPC, LRM15",MediumPulseLaser@LeftTorso(Front) G1 | MediumPulseLaser@RightTorso(Front) G1 | MediumPulseLaser@RightArm(Front) G1 | MediumPulseLaser@RightTorso(Rear) G1 | ERLargeLaser@CenterTorso(Front) G1 | ERPPC@LeftArm(Front) G1 | LRM15@RightArm(Front) G2,LeftTorsox1 | RightTorsox2 | RightArmx2 | CenterTorsox1 | LeftArmx1,LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1),LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front+Rear) | CenterTorso(Front) | Head(Unknown),
jenner2c,No,Clan,10.000000,35.000000,FerroFiberus,Standard,0,,0,Yes,Yes,No,No,,,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.9,0,,,,LeftArm(M2/P0/B2/O0) | RightArm(M2/P0/B2/O0) | Special1(M3/P0/B0/O0),LeftArm(Unknown) | RightArm(Unknown) | Special1(Unknown),"not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud"
koto,No,IS,7.000000,25.000000,FerroFiberus,EndoSteel,0,,0,No,Yes,No,No,,,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.9,0,,,,LeftArm(M0/P1/B1/O0) | RightArm(M0/P1/B1/O0) | CenterTorso(M0/P0/B2/O0) | Special1(M0/P0/B3/O0),LeftArm(Unknown) | RightArm(Unknown) | CenterTorso(Unknown) | Special1(Unknown),"not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud"
locust2c,No,Clan,7.000000,25.000000,FerroFiberus,EndoSteel,0,,0,No,Yes,No,No,4,,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.9,0,,,,LeftArm(M0/P0/B1/O2) | RightArm(M0/P0/B1/O2) | Special1(M0/P0/B3/O0),LeftArm(Unknown) | RightArm(Unknown) | Special1(Unknown),"not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud"
marauder2,No,IS,22.000000,100.000000,FerroFiberus,EndoSteel,0,,0,Yes,No,Yes,No,6,,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.9,0,,,,LeftArm(M0/P0/B4/O1) | RightArm(M0/P0/B4/O1) | LeftTorso(M0/P0/B1/O1) | RightTorso(M0/P0/B1/O1) | Special1(M0/P7/B0/O0) | Special2(M0/P0/B0/O2),LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown) | Special1(Unknown) | Special2(Unknown),"not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud"
pitbull,No,Clan,15.000000,70.000000,FerroFiberus,EndoSteel,0,,0,Yes,No,No,No,,,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.9,0,,,,LeftArm(M0/P2/B2/O0) | RightArm(M0/P2/B2/O0) | LeftTorso(M0/P0/B2/O0) | RightTorso(M0/P0/B2/O0) | Special1(M0/P4/B0/O0),LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown) | Special1(Unknown),"not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud"
privateer,No,IS,12.500000,55.000000,FerroFiberus,Standard,0,,0,No,No,Yes,No,2,,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.9,0,,,,LeftArm(M0/P1/B0/O1) | RightArm(M0/P1/B0/O1) | LeftTorso(M0/P2/B2/O0) | RightTorso(M0/P0/B1/O0) | Special1(M4/P0/B0/O0),LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown) | Special1(Unknown),"not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud"
razorback,No,IS,9.000000,30.000000,FerroFiberus,EndoSteel,0,,0,No,Yes,No,No,,,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.9,0,,,,LeftArm(M2/P0/B2/O0) | RightArm(M0/P0/B3/O0) | Special1(M0/P2/B0/O0) | Special2(M0/P2/B0/O0),LeftArm(Unknown) | RightArm(Unknown) | Special1(Unknown) | Special2(Unknown),"not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud"
reaper,No,Clan,16.500000,75.000000,FerroFiberus,EndoSteel,0,,0,Yes,Yes,No,No,,,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.9,0,,,,LeftArm(M0/P0/B3/O0) | RightArm(M0/P0/B3/O0) | LeftTorso(M0/P0/B2/O0) | RightTorso(M0/P0/B2/O0) | CenterTorso(M0/P6/B0/O0),LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown) | CenterTorso(Unknown),"not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud"
reaver,No,Clan,10.000000,40.000000,FerroFiberus,EndoSteel,0,,0,Yes,No,Yes,No,,,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.9,0,,,,LeftArm(M0/P0/B3/O0) | RightArm(M0/P0/B3/O0) | LeftTorso(M0/P0/B1/O0) | RightTorso(M0/P0/B1/O0),LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown),"not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud"
shadowhawk,No,IS,12.500000,55.000000,FerroFiberus,Standard,0,,0,Yes,Yes,No,No,,,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.9,0,,,,LeftArm(M0/P1/B2/O0) | RightArm(M0/P0/B3/O0) | LeftTorso(M0/P0/B1/O0) | RightTorso(M0/P0/B1/O0) | Special1(M0/P2/B0/O0) | Special2(M2/P0/B0/O0),LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown) | Special1(Unknown) | Special2(Unknown),"not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud"
strider,No,IS,11.000000,40.000000,FerroFiberus,Standard,0,,0,No,No,Yes,No,4,,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.9,0,,,,LeftArm(M2/P0/B0/O0) | RightArm(M2/P0/B0/O0) | LeftTorso(M0/P0/B0/O2) | RightTorso(M0/P0/B0/O2) | CenterTorso(M0/P0/B2/O0),LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown) | CenterTorso(Unknown),"not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud"
urbanmech,Yes,IS,5.000000,30.000000,Standard,Standard,8,Single,0,Yes,Yes,No,No,50,,0.45,0.45,0.5,0.5,0.6,0.6,0.65,0.45,0.25,4.45,2,"SmallLaser, AC20",SmallLaser@LeftArm(Front) G1 | AC20@RightArm(Front) G1,LeftArmx1 | RightArmx1,LeftArm(M0/P0/B0/O12) | RightArm(M0/P0/B0/O12) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2),LeftArm(Front) | RightArm(Front) | LeftTorso(Unknown) | RightTorso(Unknown) | CenterTorso(Unknown),
urbanmech_iic,No,Clan,5.000000,30.000000,FerroFiberus,Standard,0,,0,Yes,Yes,No,No,,,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.9,0,,,,LeftArm(M0/P2/B2/O0) | RightArm(M0/P0/B4/O0) | LeftTorso(M0/P1/B0/O0) | RightTorso(M0/P1/B0/O0),LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown),"not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud"
ursus,No,Clan,10.500000,50.000000,FerroFiberus,Standard,0,,0,No,Yes,No,No,3,,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.9,0,,,,LeftArm(M0/P0/B0/O3) | RightArm(M0/P0/B4/O0) | CenterTorso(M2/P0/B0/O0) | Special1(M0/P0/B1/O0) | Special2(M0/P0/B1/O0),LeftArm(Unknown) | RightArm(Unknown) | CenterTorso(Unknown) | Special1(Unknown) | Special2(Unknown),"not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud"
vulture2,No,Clan,16.500000,75.000000,FerroFiberus,EndoSteel,0,,0,No,Yes,No,No,5,,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.9,0,,,,LeftArm(M0/P3/B0/O0) | RightArm(M0/P0/B0/O3) | LeftTorso(M2/P0/B2/O0) | RightTorso(M2/P0/B2/O0) | Special1(M0/P0/B0/O2) | Special2(M0/P0/B2/O0),LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown) | Special1(Unknown) | Special2(Unknown),"not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud"
vulturec,No,Clan,14.000000,60.000000,FerroFiberus,Standard,0,,0,No,Yes,No,No,3,,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.9,0,,,,LeftArm(M0/P3/B0/O0) | RightArm(M0/P3/B0/O0) | LeftTorso(M2/P0/B0/O0) | RightTorso(M2/P0/B0/O0) | CenterTorso(M0/P0/B0/O1) | Special1(M0/P0/B0/O1) | Special2(M0/P0/B0/O1),LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown) | CenterTorso(Unknown) | Special1(Unknown) | Special2(Unknown),"not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud"
1 Mech In_game_playable Tech Chassis_Tonnage Max_Loadout_Tonnage Armor_Type Internal_Type Heatsinks Heatsink_Type JumpJets_Installed CanLoad_JJ CanLoad_ECM CanLoad_BAP CanLoad_AMS OmniSlots Equipment Armor_LeftArm Armor_RightArm Armor_LeftLeg Armor_RightLeg Armor_LeftFrontTorso Armor_RightFrontTorso Armor_CenterFrontTorso Armor_CenterRearTorso Armor_Head Total_Armor_Multiplier Weapon_Count Weapons_Summary Weapons_With_Locations Default_Installed_Locations Available_Slot_Capacity_By_Zone Available_Hardpoints notes
2 Annihilator Yes IS 23.000000 100.000000 Reflective EndoSteel 17 Single 0 No Yes Yes Yes 43 1.8 1.8 1.8 1.8 1.85 1.85 2.0 0.8 0.3 14 10 4x ClanERSmallLaser, 2x ClanMachineGun, 4x ClanUltraAC5 ClanERSmallLaser@LeftTorso(Front) G1 | ClanERSmallLaser@CenterTorso(Front) G1 | ClanERSmallLaser@CenterTorso(Front) G1 | ClanERSmallLaser@RightTorso(Front) G1 | ClanMachineGun@LeftTorso(Front) G1 | ClanMachineGun@RightTorso(Front) G1 | ClanUltraAC5@LeftArm(Front) G1 | ClanUltraAC5@LeftArm(Front) G1 | ClanUltraAC5@RightArm(Front) G1 | ClanUltraAC5@RightArm(Front) G1 LeftTorsox2 | CenterTorsox2 | RightTorsox2 | LeftArmx2 | RightArmx2 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Front) | Head(Unknown)
3 Archer Yes IS 17.000000 70.000000 Standard Standard 20 Single 1 Yes Yes Yes Yes 43 1.1 1.1 1.2 1.2 1.4 1.4 1.5 0.5 0.3 9.7 9 4x MediumPulseLaser, 2x SSRM2, 2x LRM15, NarcBeacon MediumPulseLaser@LeftArm(Front) G1 | MediumPulseLaser@CenterTorso(Front) G1 | MediumPulseLaser@CenterTorso(Front) G1 | MediumPulseLaser@RightArm(Front) G1 | SSRM2@LeftArm(Front) G2 | SSRM2@RightArm(Front) G2 | LRM15@LeftTorso(Side) G2 | LRM15@RightTorso(Side) G2 | NarcBeacon@LeftTorso(Front) G3 LeftArmx2 | CenterTorsox2 | RightArmx2 | LeftTorsox2 | RightTorsox1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Front+Side) | RightTorso(Side) | CenterTorso(Front) | Head(Unknown)
4 ArcticWolf Yes Clan 9.500000 40.000000 FerroFiberus EndoSteel 8 Single 1 Yes Yes Yes Yes 43 0.7 0.7 0.95 0.95 0.85 0.85 1.0 0.2 0.3 6.5 6 2x ClanSmallPulseLaser, 4x ClanSSRM4 ClanSmallPulseLaser@RightTorso(Front) G1 | ClanSmallPulseLaser@RightTorso(Front) G1 | ClanSSRM4@leftarm(Front) G2 | ClanSSRM4@special1(Front) G2 | ClanSSRM4@special2(Front) G2 | ClanSSRM4@rightarm(Front) G2 RightTorsox2 | leftarmx1 | special1x1 | special2x1 | rightarmx1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O8) | RightTorso(M0/P0/B0/O8) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O4) | Special2(M0/P0/B0/O4) LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown) | Special1(Unknown) | Special2(Unknown)
5 Ares Yes Clan 15.500000 60.000000 FerroFiberus Standard 15 Single 0 Yes Yes Yes Yes 46 0.9 0.9 1.15 1.15 1.1 1.1 1.45 0.9 0.25 8.9 9 2x ClanERSmallLaser, 3x ClanERMediumLaser, ClanERLargeLaser, 3x ClanLRM10 ClanERSmallLaser@Special1(Front) G1 | ClanERSmallLaser@Special1(Front) G1 | ClanERMediumLaser@LeftArm(Front) G1 | ClanERMediumLaser@RightArm(Front) G1 | ClanERMediumLaser@RightArm(Front) G1 | ClanERLargeLaser@LeftArm(Front) G1 | ClanLRM10@Special2(Side) G2 | ClanLRM10@Special2(Side) G2 | ClanLRM10@Special2(Side) G2 Special1x2 | LeftArmx2 | RightArmx2 | Special2x3 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O2) | Special2(M0/P0/B0/O3) LeftArm(Front) | RightArm(Front) | LeftTorso(Unknown) | RightTorso(Unknown) | Head(Unknown) | Special1(Front) | Special2(Side)
6 Argus Yes IS 14.500000 60.000000 Standard Standard 13 Single 0 No Yes Yes Yes 43 0.7 0.7 1.15 1.15 0.85 0.85 1.45 0.9 0.3 8.05 9 3x MediumLaser, PPC, 4x MachineGun, LRM10 MediumLaser@Lefttorso(Front) G1 | MediumLaser@Righttorso(Front) G1 | MediumLaser@RightArm(Front) G1 | PPC@RightArm(Front) G1 | MachineGun@LeftTorso(Front) G1 | MachineGun@CenterTorso(Front) G1 | MachineGun@CenterTorso(Front) G1 | MachineGun@RightTorso(Front) G1 | LRM10@LeftArm(Front) G2 Lefttorsox1 | Righttorsox1 | RightArmx2 | LeftTorsox1 | CenterTorsox2 | RightTorsox1 | LeftArmx1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Front) | Head(Unknown)
7 Assassin2 Yes IS 12.000000 45.000000 FerroFiberus EndoSteel 6 Single 0 Yes Yes Yes Yes 40 0.85 0.85 0.85 0.85 0.9 0.9 1.05 0.45 0.25 6.95 7 2x SmallLaser, 2x MediumLaser, 2x SRM4, LRM5 SmallLaser@LeftArm(Front) G1 | SmallLaser@RightArm(Front) G1 | MediumLaser@LeftArm(Front) G1 | MediumLaser@RightArm(Front) G1 | SRM4@LeftTorso(Front) G2 | SRM4@RightTorso(Front) G2 | LRM5@Special1(Side) G2 LeftArmx2 | RightArmx2 | LeftTorsox1 | RightTorsox1 | Special1x1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O10) | Special1(M0/P0/B0/O2) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | Special1(Side)
8 Atlas Yes IS 28.000000 100.000000 Standard Standard 17 Single 0 No Yes Yes Yes 43 1.95 1.95 2.15 2.15 2.45 2.45 2.2 1.1 0.3 16.7 9 4x MediumLaser, AC20, 2x SSRM2, SRM6, LRM20 MediumLaser@LeftArm(Front) G1 | MediumLaser@Head(Front) G1 | MediumLaser@CenterTorso(Front) G1 | MediumLaser@RightArm(Front) G1 | AC20@Special1(Front) G1 | SSRM2@LeftTorso(Front) G2 | SSRM2@RightTorso(Front) G2 | SRM6@Special2(Front) G2 | LRM20@LeftTorso(Side) G2 LeftArmx1 | Headx1 | CenterTorsox1 | RightArmx1 | Special1x1 | LeftTorsox2 | RightTorsox1 | Special2x1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O10) | RightTorso(M0/P0/B0/O8) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O4) | Special2(M0/P0/B0/O2) LeftArm(Front) | RightArm(Front) | LeftTorso(Front+Side) | RightTorso(Front) | CenterTorso(Front) | Head(Front) | Special1(Front) | Special2(Front)
9 Avatar Yes IS 17.000000 70.000000 Reactive EndoSteel 12 Single 0 Yes Yes Yes Yes 43 1.3 1.3 1.5 1.5 1.6 1.6 1.75 0.5 0.3 11.35 10 4x MachineGun, 2x LargeLaser, 2x LRM5, 2x LRM10 MachineGun@LeftArm(Front) G1 | MachineGun@Special1(Front) G1 | MachineGun@Special1(Front) G1 | MachineGun@RightArm(Front) G1 | LargeLaser@LeftArm(Front) G1 | LargeLaser@RightArm(Front) G1 | LRM5@LeftTorso(Side) G2 | LRM5@RightTorso(Side) G2 | LRM10@LeftTorso(Side) G2 | LRM10@RightTorso(Side) G2 LeftArmx2 | Special1x2 | RightArmx2 | LeftTorsox2 | RightTorsox2 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O2) LeftArm(Front) | RightArm(Front) | LeftTorso(Side) | RightTorso(Side) | Head(Unknown) | Special1(Front)
10 Awesome Yes IS 23.000000 80.000000 Standard Standard 28 Single 0 No Yes Yes Yes 43 1.45 1.45 1.55 1.55 1.65 1.65 1.75 1.25 0.3 12.6 6 SmallPulseLaser, 3x ERPPC, UltraAC5, LRM5 SmallPulseLaser@Head(Front) G1 | ERPPC@Lefttorso(Front) G1 | ERPPC@righttorso(Front) G1 | ERPPC@RightArm(Front) G1 | UltraAC5@LeftArm(Front) G1 | LRM5@CenterTorso(Side) G2 Headx1 | Lefttorsox1 | righttorsox1 | RightArmx1 | LeftArmx1 | CenterTorsox1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Unknown) | RightTorso(Unknown) | CenterTorso(Side) | Head(Front)
11 Battlemaster Yes IS 20.500000 85.000000 Standard EndoSteel 25 Single 0 No Yes Yes Yes 43 1.45 1.45 1.6 1.6 1.8 1.8 2.05 0.95 0.3 13 10 PPC, 6x MediumLaser, 2x MachineGun, SRM6 PPC@Special2(Front) G1 | MediumLaser@RightTorso(Front) G1 | MediumLaser@RightTorso(Front) G1 | MediumLaser@RightTorso(Rear) G1 | MediumLaser@LeftTorso(Front) G1 | MediumLaser@LeftTorso(Front) G1 | MediumLaser@LeftTorso(Rear) G1 | MachineGun@LeftArm(Front) G1 | MachineGun@LeftArm(Front) G1 | SRM6@Special1(Front) G2 Special2x1 | RightTorsox3 | LeftTorsox3 | LeftArmx2 | Special1x1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O4) | LeftTorso(M0/P0/B0/O9) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O3) | Special2(M0/P0/B0/O4) LeftArm(Front) | RightArm(Unknown) | LeftTorso(Front+Rear) | RightTorso(Front+Rear) | CenterTorso(Unknown) | Head(Unknown) | Special1(Front) | Special2(Front)
12 Battlemaster2c Yes Clan 15.500000 85.000000 Standard EndoSteel 25 Single 0 No Yes Yes Yes 39 1.45 1.45 1.6 1.6 1.8 1.8 2.05 0.95 0.3 13 10 ClanERPPC, 6x ClanERMediumLaser, 2x ClanGaussRifle, ClanSSRM6 ClanERPPC@RightArm(Front) G1 | ClanERMediumLaser@RightTorso(Front) G1 | ClanERMediumLaser@RightTorso(Front) G1 | ClanERMediumLaser@RightTorso(Rear) G1 | ClanERMediumLaser@LeftTorso(Front) G1 | ClanERMediumLaser@LeftTorso(Front) G1 | ClanERMediumLaser@LeftTorso(Rear) G1 | ClanGaussRifle@LeftArm(Front) G1 | ClanGaussRifle@LeftArm(Front) G1 | ClanSSRM6@Special1(Front) G2 RightArmx1 | RightTorsox3 | LeftTorsox3 | LeftArmx2 | Special1x1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O4) | LeftTorso(M0/P0/B0/O9) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O3) LeftArm(Front) | RightArm(Front) | LeftTorso(Front+Rear) | RightTorso(Front+Rear) | CenterTorso(Unknown) | Head(Unknown) | Special1(Front)
13 Behemoth Yes Clan 22.000000 100.000000 Standard Standard 21 Single 1 Yes Yes Yes Yes 43 1.95 1.95 2.15 2.15 2.45 2.45 2.2 1.1 0.3 16.7 7 4x ClanLargePulseLaser, 2x ClanGaussRifle, LargeLaser ClanLargePulseLaser@LeftTorso(Front) G1 | ClanLargePulseLaser@LeftTorso(Front) G1 | ClanLargePulseLaser@RightTorso(Front) G1 | ClanLargePulseLaser@RightTorso(Front) G1 | ClanGaussRifle@LeftArm(Front) G1 | ClanGaussRifle@RightArm(Front) G1 | LargeLaser@Special1(Front) G1 LeftTorsox2 | RightTorsox2 | LeftArmx1 | RightArmx1 | Special1x1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O2) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | Head(Unknown) | Special1(Front)
14 Behemoth2 Yes Clan 22.000000 100.000000 Standard Standard 21 Single 1 Yes Yes Yes Yes 43 1.95 1.95 2.15 2.15 2.45 2.45 2.2 1.1 0.3 16.7 7 4x ClanLargePulseLaser, 2x ClanGaussRifle, LargeLaser ClanLargePulseLaser@LeftTorso(Front) G1 | ClanLargePulseLaser@LeftTorso(Front) G1 | ClanLargePulseLaser@RightTorso(Front) G1 | ClanLargePulseLaser@RightTorso(Front) G1 | ClanGaussRifle@LeftArm(Front) G1 | ClanGaussRifle@RightArm(Front) G1 | LargeLaser@Special1(Front) G1 LeftTorsox2 | RightTorsox2 | LeftArmx1 | RightArmx1 | Special1x1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O2) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | Head(Unknown) | Special1(Front)
15 Blackhawk Yes Clan 10.000000 50.000000 FerroFiberus EndoSteel 12 Single 0 Yes Yes Yes Yes 43 0.9 0.9 0.95 0.95 1.05 1.05 1.2 0.55 0.25 7.8 4 2x ClanMediumPulseLaser, 2x ClanERPPC ClanMediumPulseLaser@LeftTorso(Front) G1 | ClanMediumPulseLaser@RightTorso(Front) G1 | ClanERPPC@LeftArm(Front) G1 | ClanERPPC@RightArm(Front) G1 LeftTorsox1 | RightTorsox1 | LeftArmx1 | RightArmx1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O10) | RightTorso(M0/P0/B0/O10) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O2) | Special2(M0/P0/B0/O2) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown) | Special1(Unknown) | Special2(Unknown)
16 Blackheart No Clan 14.000000 70.000000 FerroFiberus Standard 0 0 No Yes No No 6 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.9 0 LeftArm(M0/P3/B0/O0) | RightArm(M0/P3/B0/O0) | LeftTorso(M0/P0/B1/O2) | RightTorso(M0/P0/B1/O2) | CenterTorso(M0/P0/B0/O2) LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown) | CenterTorso(Unknown) not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud
17 Blacklanner Yes Clan 12.500000 55.000000 FerroFiberus EndoSteel 10 Single 0 No Yes Yes Yes 40 ECM 0.9 0.9 1.0 1.0 1.05 1.05 1.15 0.75 0.25 8.05 5 2x ClanERMediumLaser, ClanERLargeLaser, ClanSRM6, ClanLRM10 ClanERMediumLaser@LeftArm(Front) G1 | ClanERMediumLaser@LeftArm(Front) G1 | ClanERLargeLaser@RightArm(Front) G1 | ClanSRM6@Special2(Side) G2 | ClanLRM10@Special1(Side) G2 LeftArmx2 | RightArmx1 | Special2x1 | Special1x1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | Special1(M0/P0/B0/O12) | Special2(M0/P0/B0/O12) LeftArm(Front) | RightArm(Front) | Special1(Side) | Special2(Side)
18 Blacknight Yes IS 16.000000 75.000000 Standard Standard 22 Single 0 Yes Yes Yes Yes 43 1.2 1.2 1.4 1.4 1.55 1.55 1.8 0.8 0.3 11.2 8 SmallLaser, 4x MediumLaser, 2x LargeLaser, PPC SmallLaser@Head(Front) G1 | MediumLaser@LeftArm(Front) G1 | MediumLaser@LeftTorso(Front) G1 | MediumLaser@RightTorso(Front) G1 | MediumLaser@RightArm(Front) G1 | LargeLaser@LeftTorso(Front) G1 | LargeLaser@RightTorso(Front) G1 | PPC@RightArm(Front) G1 Headx1 | LeftArmx1 | LeftTorsox2 | RightTorsox2 | RightArmx2 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Front)
19 Bowman No Clan 16.000000 70.000000 FerroFiberus Standard 0 0 No Yes Yes No 5 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.9 0 LeftArm(M0/P2/B2/O2) | RightArm(M4/P0/B0/O0) | Special1(M4/P0/B0/O0) | Special2(M0/P0/B0/O3) LeftArm(Unknown) | RightArm(Unknown) | Special1(Unknown) | Special2(Unknown) not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud
20 Brigand Yes IS 7.000000 25.000000 FerroFiberus Standard 10 Single 0 Yes Yes Yes Yes 40 0.45 0.45 0.45 0.45 0.55 0.55 0.55 0.4 0.25 4.1 4 2x MediumPulseLaser, 2x MediumLaser MediumPulseLaser@LeftArm(Front) G1 | MediumPulseLaser@RightArm(Front) G1 | MediumLaser@Special2(Front) G1 | MediumLaser@Special1(Front) G1 LeftArmx1 | RightArmx1 | Special2x1 | Special1x1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | Special1(M0/P0/B0/O12) | Special2(M0/P0/B0/O12) LeftArm(Front) | RightArm(Front) | Special1(Front) | Special2(Front)
21 Bushwacker Yes IS 15.500000 55.000000 FerroFiberus Standard 11 Single 0 No Yes Yes Yes 43 0.9 0.9 1.1 1.1 1.15 1.15 1.35 0.5 0.25 8.4 8 2x MediumLaser, ERLargeLaser, 2x MachineGun, AC10, 2x LRM5 MediumLaser@CenterTorso(Front) G1 | MediumLaser@CenterTorso(Front) G1 | ERLargeLaser@RightArm(Front) G1 | MachineGun@LeftTorso(Front) G1 | MachineGun@RightTorso(Front) G1 | AC10@LeftArm(Front) G1 | LRM5@Special1(Side) G2 | LRM5@Special1(Side) G2 CenterTorsox2 | RightArmx1 | LeftTorsox1 | RightTorsox1 | LeftArmx1 | Special1x2 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O8) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O4) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Front) | Head(Unknown) | Special1(Side)
22 Canis No Clan 15.000000 80.000000 FerroFiberus EndoSteel 0 0 Yes No No No 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.9 0 LeftArm(M0/P0/B5/O0) | RightArm(M0/P0/B5/O0) | Special1(M0/P3/B0/O0) | Special2(M0/P3/B0/O0) LeftArm(Unknown) | RightArm(Unknown) | Special1(Unknown) | Special2(Unknown) not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud
23 Catapult Yes IS 15.000000 65.000000 Reactive Standard 10 Single 1 Yes Yes Yes Yes 43 BAP 1.45 1.45 1.35 1.35 1.45 1.45 1.5 0.65 0.3 10.95 4 2x LargeLaser, 2x LRM20 LargeLaser@LeftTorso(Front) G1 | LargeLaser@RightTorso(Front) G1 | LRM20@LeftArm(Side) G2 | LRM20@RightArm(Side) G2 LeftTorsox1 | RightTorsox1 | LeftArmx1 | RightArmx1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Side) | RightArm(Side) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown)
24 CauldronBorn Yes Clan 13.500000 65.000000 FerroFiberus EndoSteel 13 Single 0 Yes Yes Yes Yes 43 0.85 0.85 0.95 0.95 1.15 1.15 1.55 0.85 0.25 8.55 6 ClanERMediumLaser, ClanUltraAC5, ClanGaussRifle, ClanSSRM2, 2x ClanLRM10 ClanERMediumLaser@LeftTorso(Front) G1 | ClanUltraAC5@LeftArm(Front) G1 | ClanGaussRifle@RightArm(Front) G1 | ClanSSRM2@RightTorso(Front) G2 | ClanLRM10@Special1(Side) G2 | ClanLRM10@Special2(Side) G2 LeftTorsox1 | LeftArmx1 | RightArmx1 | RightTorsox1 | Special1x1 | Special2x1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O8) | RightTorso(M0/P0/B0/O8) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O4) | Special2(M0/P0/B0/O4) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown) | Special1(Side) | Special2(Side)
25 Chimera Yes IS 9.500000 40.000000 Standard EndoSteel 14 Single 1 Yes Yes Yes Yes 43 BAP 0.95 0.95 1.0 1.0 1.0 1.0 1.1 0.5 0.3 7.8 7 2x MediumPulseLaser, ERLargeLaser, 4x LRM5 MediumPulseLaser@LeftArm(Front) G1 | MediumPulseLaser@LeftArm(Front) G1 | ERLargeLaser@RightArm(Front) G1 | LRM5@RightTorso(Side) G2 | LRM5@RightTorso(Side) G2 | LRM5@RightTorso(Side) G2 | LRM5@RightTorso(Side) G2 LeftArmx2 | RightArmx1 | RightTorsox4 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Unknown) | RightTorso(Side) | CenterTorso(Unknown) | Head(Unknown)
26 Commando Yes IS 7.000000 25.000000 Standard Standard 8 Single 0 Yes Yes Yes Yes 43 0.6 0.6 0.8 0.8 0.75 0.75 0.8 0.45 0.3 5.85 3 2x MediumLaser, SRM6 MediumLaser@LeftArm(Front) G1 | MediumLaser@RightArm(Front) G1 | SRM6@CenterTorso(Front) G2 LeftArmx1 | RightArmx1 | CenterTorsox1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Unknown) | RightTorso(Unknown) | CenterTorso(Front) | Head(Unknown)
27 Cougar Yes Clan 8.500000 35.000000 FerroFiberus EndoSteel 10 Single 1 Yes Yes Yes Yes 43 BAP 0.7 0.7 0.7 0.7 0.75 0.75 0.8 0.45 0.25 5.8 5 2x ClanMediumPulseLaser, ClanMachineGun, 2x ClanLRM10 ClanMediumPulseLaser@LeftArm(Front) G1 | ClanMediumPulseLaser@RightArm(Front) G1 | ClanMachineGun@RightTorso(Front) G1 | ClanLRM10@LeftTorso(Side) G2 | ClanLRM10@RightTorso(Side) G2 LeftArmx1 | RightArmx1 | RightTorsox2 | LeftTorsox1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Side) | RightTorso(Front+Side) | CenterTorso(Unknown) | Head(Unknown)
28 Crab No IS 10.500000 50.000000 FerroFiberus Standard 0 0 No No Yes No 1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.9 0 LeftArm(M0/P0/B4/O0) | RightArm(M0/P0/B4/O0) | CenterTorso(M0/P0/B2/O0) | Special1(M0/P0/B1/O1) LeftArm(Unknown) | RightArm(Unknown) | CenterTorso(Unknown) | Special1(Unknown) not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud
29 Cyclops Yes IS 19.000000 90.000000 FerroFiberus Standard 16 Single 0 No Yes Yes Yes 45 BAP 1.4 1.4 1.4 1.4 1.55 1.55 1.45 0.8 0.25 11.2 8 3x MediumPulseLaser, 2x MediumLaser, GaussRifle, SRM4, LRM10 MediumPulseLaser@LeftArm(Front) G1 | MediumPulseLaser@Special1(Front) G1 | MediumPulseLaser@RightArm(Front) G1 | MediumLaser@LeftArm(Front) G1 | MediumLaser@RightArm(Front) G1 | GaussRifle@RightTorso(Front) G1 | SRM4@CenterTorso(Front) G2 | LRM10@LeftTorso(Side) G2 LeftArmx2 | Special1x1 | RightArmx2 | RightTorsox1 | CenterTorsox1 | LeftTorsox1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O2) LeftArm(Front) | RightArm(Front) | LeftTorso(Side) | RightTorso(Front) | CenterTorso(Front) | Head(Unknown) | Special1(Front)
30 Daishi Yes Clan 25.000000 100.000000 Standard Standard 21 Single 0 No Yes Yes Yes 43 1.75 1.75 1.9 1.9 2.0 2.0 2.2 1.5 0.3 15.3 6 3x ClanLargePulseLaser, ClanGaussRifle, 2x ClanSSRM6 ClanLargePulseLaser@RightArm(Front) G1 | ClanLargePulseLaser@RightArm(Front) G1 | ClanLargePulseLaser@RightArm(Front) G1 | ClanGaussRifle@LeftArm(Front) G1 | ClanSSRM6@special1(Front) G2 | ClanSSRM6@special1(Front) G2 RightArmx3 | LeftArmx1 | special1x2 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O8) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O4) LeftArm(Front) | RightArm(Front) | LeftTorso(Unknown) | RightTorso(Unknown) | CenterTorso(Unknown) | Head(Unknown) | Special1(Unknown)
31 Deimos Yes Clan 18.000000 85.000000 Reactive EndoSteel 17 Single 0 No Yes Yes Yes 43 1.55 1.55 1.65 1.65 1.75 1.75 2.05 0.7 0.3 12.95 10 2x ClanERMediumLaser, 6x ClanUltraAC2, 2x ClanLRM15 ClanERMediumLaser@LeftTorso(Front) G1 | ClanERMediumLaser@RightTorso(Front) G1 | ClanUltraAC2@LeftArm(Front) G1 | ClanUltraAC2@LeftArm(Front) G1 | ClanUltraAC2@LeftArm(Front) G1 | ClanUltraAC2@RightArm(Front) G1 | ClanUltraAC2@RightArm(Front) G1 | ClanUltraAC2@RightArm(Front) G1 | ClanLRM15@Special1(Side) G2 | ClanLRM15@Special2(Side) G2 LeftTorsox1 | RightTorsox1 | LeftArmx3 | RightArmx3 | Special1x1 | Special2x1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O9) | RightTorso(M0/P0/B0/O9) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O3) | Special2(M0/P0/B0/O3) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown) | Special1(Side) | Special2(Side)
32 Dragon Yes Clan 17.000000 60.000000 Standard Standard 13 Single 0 Yes Yes Yes Yes 43 1.15 1.15 1.4 1.4 1.25 1.25 1.55 0.9 0.3 10.35 5 3x MediumLaser, ERPPC, LRM10 MediumLaser@LeftArm(Front) G1 | MediumLaser@LeftTorso(Front) G1 | MediumLaser@RightTorso(Front) G1 | ERPPC@RightArm(Front) G1 | LRM10@CenterTorso(Side) G2 LeftArmx1 | LeftTorsox1 | RightTorsox1 | RightArmx1 | CenterTorsox1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Side) | Head(Unknown)
33 Duangung No IS 7.000000 25.000000 FerroFiberus Standard 0 0 Yes No No No 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.9 0 LeftArm(M0/P0/B2/O0) | RightArm(M0/P0/B2/O0) | CenterTorso(M2/P0/B0/O0) | Special1(M0/P0/B1/O0) | Special2(M0/P0/B1/O0) LeftArm(Unknown) | RightArm(Unknown) | CenterTorso(Unknown) | Special1(Unknown) | Special2(Unknown) not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud
34 Fafnir Yes IS 21.000000 100.000000 FerroFiberus Standard 7 Single 0 No Yes No No 43 1.8 1.8 1.9 1.9 2.0 2.0 2.2 1.0 0.3 14.9 7 2x ClanGaussRifle, 2x LargeLaser, 3x MediumLaser ClanGaussRifle@RightTorso(Front) G1 | ClanGaussRifle@LeftTorso(Front) G1 | LargeLaser@RightArm(Front) G1 | LargeLaser@LeftArm(Front) G1 | MediumLaser@RightTorso(Front) G1 | MediumLaser@CenterTorso(Front) G1 | MediumLaser@LeftTorso(Front) G1 RightTorsox2 | LeftTorsox2 | RightArmx1 | LeftArmx1 | CenterTorsox1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Front) | Head(Unknown)
35 Flea Yes IS 7.000000 20.000000 Standard Standard 2 Single 0 No Yes Yes Yes 43 0.4 0.4 0.4 0.4 0.5 0.5 0.65 0.3 0.3 3.85 6 2x SmallLaser, 2x MediumLaser, 2x MachineGun SmallLaser@LeftTorso(Rear) G1 | SmallLaser@RightTorso(Rear) G1 | MediumLaser@LeftArm(Front) G1 | MediumLaser@RightArm(Front) G1 | MachineGun@LeftArm(Front) G1 | MachineGun@RightArm(Front) G1 LeftTorsox1 | RightTorsox1 | LeftArmx2 | RightArmx2 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Rear) | RightTorso(Rear) | CenterTorso(Unknown) | Head(Unknown)
36 Gargoyle No Clan 15.000000 80.000000 FerroFiberus EndoSteel 0 0 No Yes No No 6 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.9 0 LeftArm(M1/P0/B0/O3) | RightArm(M1/P0/B0/O3) | LeftTorso(M0/P2/B2/O0) | RightTorso(M0/P2/B2/O0) LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown) not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud
37 Gesu No Clan 9.750000 45.000000 FerroFiberus EndoSteel 0 0 No No Yes No 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.9 0 LeftArm(M0/P2/B0/O0) | RightArm(M0/P2/B0/O0) | LeftTorso(M0/P0/B2/O0) | RightTorso(M0/P0/B2/O0) | Special1(M2/P0/B0/O0) | Special2(M2/P0/B0/O0) LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown) | Special1(Unknown) | Special2(Unknown) not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud
38 Gladiator Yes Clan 24.000000 95.000000 FerroFiberus Standard 19 Single 1 Yes Yes Yes Yes 43 1.4 1.4 1.65 1.65 1.75 1.75 2.0 0.95 0.25 12.8 12 5x ClanSmallPulseLaser, ClanERSmallLaser, 3x ClanMediumPulseLaser, ClanLargePulseLaser, 2x SRM6 ClanSmallPulseLaser@RightTorso(Front) G1 | ClanSmallPulseLaser@RightTorso(Front) G1 | ClanSmallPulseLaser@RightArm(Front) G1 | ClanSmallPulseLaser@RightArm(Front) G1 | ClanSmallPulseLaser@RightArm(Front) G1 | ClanERSmallLaser@LeftTorso(Front) G1 | ClanMediumPulseLaser@RightArm(Front) G1 | ClanMediumPulseLaser@RightArm(Front) G1 | ClanMediumPulseLaser@RightArm(Front) G1 | ClanLargePulseLaser@LeftArm(Front) G1 | SRM6@LeftArm(Front) G2 | SRM6@LeftArm(Front) G2 RightTorsox2 | RightArmx6 | LeftTorsox1 | LeftArmx3 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown)
39 Grizzly Yes Clan 16.000000 70.000000 FerroFiberus Standard 11 Single 1 Yes Yes Yes Yes 40 1.2 1.2 1.1 1.1 1.4 1.4 1.5 0.5 0.25 9.65 6 2x ClanSmallPulseLaser, ClanMediumPulseLaser, ClanLargePulseLaser, ClanGaussRifle, CLANLRM10 ClanSmallPulseLaser@LeftArm(Front) G1 | ClanSmallPulseLaser@LeftArm(Front) G1 | ClanMediumPulseLaser@RightTorso(Front) G1 | ClanLargePulseLaser@LeftArm(Front) G1 | ClanGaussRifle@RightArm(Front) G1 | CLANLRM10@LeftTorso(Side) G2 LeftArmx3 | RightTorsox1 | RightArmx1 | LeftTorsox1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) LeftArm(Front) | RightArm(Front) | LeftTorso(Side) | RightTorso(Front)
40 Hauptmann Yes Clan 21.000000 95.000000 Standard Standard 20 Single 0 Yes Yes Yes Yes 43 ECM 1.7 1.7 1.8 1.8 1.9 1.9 2.1 1.1 0.3 14.3 8 ClanERSmallLaser, 2x ClanMediumPulseLaser, 2x ClanLargePulseLaser, ClanUltraAC20, 2x ClanSSRM2 ClanERSmallLaser@Head(Front) G1 | ClanMediumPulseLaser@LeftArm(Front) G1 | ClanMediumPulseLaser@RightArm(Front) G1 | ClanLargePulseLaser@LeftArm(Front) G1 | ClanLargePulseLaser@RightArm(Front) G1 | ClanUltraAC20@Special1(Front) G1 | ClanSSRM2@LeftTorso(Front) G2 | ClanSSRM2@RightTorso(Front) G2 Headx1 | LeftArmx2 | RightArmx2 | Special1x1 | LeftTorsox1 | RightTorsox1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O8) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O4) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Front) | Special1(Front)
41 Hellhound Yes Clan 10.500000 50.000000 FerroFiberus EndoSteel 12 Single 1 Yes Yes Yes Yes 43 0.9 0.9 0.95 0.95 1.05 1.05 1.2 0.55 0.25 7.8 6 3x ClanMediumPulseLaser, ClanUltraAC2, ClanUltraAC5, ClanLRM10 ClanMediumPulseLaser@LeftArm(Front) G1 | ClanMediumPulseLaser@LeftArm(Front) G1 | ClanMediumPulseLaser@RightArm(Front) G1 | ClanUltraAC2@RightTorso(Front) G1 | ClanUltraAC5@RightTorso(Front) G1 | ClanLRM10@LeftTorso(Side) G2 LeftArmx2 | RightArmx1 | RightTorsox2 | LeftTorsox1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Side) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown)
42 Hellspawn Yes IS 10.000000 45.000000 Standard Standard 9 Single 1 Yes Yes Yes Yes 43 ECM 0.5 0.5 0.8 0.8 0.85 0.85 1.15 0.5 0.3 6.25 6 2x MediumPulseLaser, LargePulseLaser, 2x SSRM2, LRM10 MediumPulseLaser@LeftArm(Front) G1 | MediumPulseLaser@LeftArm(Front) G1 | LargePulseLaser@LeftArm(Front) G1 | SSRM2@RightArm(Front) G2 | SSRM2@Special1(Front) G2 | LRM10@LeftTorso(Front) G2 LeftArmx3 | RightArmx1 | Special1x1 | LeftTorsox1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O8) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O4) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Unknown) | CenterTorso(Unknown) | Head(Unknown) | Special1(Front)
43 Highlander Yes IS 21.500000 90.000000 Standard Standard 17 Single 1 Yes Yes Yes Yes 43 1.75 1.45 1.7 1.7 1.8 1.8 2.0 0.85 0.3 13.35 7 4x MediumPulseLaser, GaussRifle, 2x SRM6 MediumPulseLaser@RightTorso(Front) G1 | MediumPulseLaser@RightTorso(Front) G1 | MediumPulseLaser@RightArm(Front) G1 | MediumPulseLaser@RightArm(Front) G1 | GaussRifle@LeftArm(Front) G1 | SRM6@LeftTorso(Front) G2 | SRM6@LeftTorso(Front) G2 RightTorsox2 | RightArmx2 | LeftArmx1 | LeftTorsox2 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown)
44 Hollander Yes IS 10.000000 45.000000 FerroFiberus EndoSteel 12 Single 0 No Yes Yes Yes 42 0.85 0.85 0.85 0.85 0.9 0.9 1.05 0.45 0.25 6.95 6 2x SmallPulseLaser, 3x MediumPulseLaser, GaussRifle SmallPulseLaser@Special2(Front) G1 | SmallPulseLaser@Special2(Front) G1 | MediumPulseLaser@LeftArm(Front) G1 | MediumPulseLaser@LeftTorso(Front) G1 | MediumPulseLaser@RightArm(Front) G1 | GaussRifle@Special1(Front) G1 Special2x2 | LeftArmx1 | LeftTorsox1 | RightArmx1 | Special1x1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | Special1(M0/P0/B0/O12) | Special2(M0/P0/B0/O2) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | Special1(Front) | Special2(Front)
45 Hunchback Yes IS 13.500000 50.000000 Standard Standard 10 Single 0 Yes Yes Yes Yes 43 ECM 1.0 1.0 1.1 1.1 1.2 1.2 1.5 0.5 0.3 8.9 6 SmallLaser, 4x MediumLaser, AC10 SmallLaser@Head(Front) G1 | MediumLaser@LeftArm(Front) G1 | MediumLaser@LeftArm(Front) G1 | MediumLaser@RightArm(Front) G1 | MediumLaser@RightArm(Front) G1 | AC10@Special1(Front) G1 Headx1 | LeftArmx2 | RightArmx2 | Special1x1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O8) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O4) LeftArm(Front) | RightArm(Front) | LeftTorso(Unknown) | RightTorso(Unknown) | CenterTorso(Unknown) | Head(Front) | Special1(Front)
46 Kodiak Yes IS 22.000000 100.000000 Standard EndoSteel 20 Single 1 Yes Yes Yes Yes 43 1.5 1.5 1.9 1.9 1.5 1.5 2 1.1 0.3 13.2 7 4x ClanERMediumLaser, ClanUltraAC20, 2x ClanSSRM6 ClanERMediumLaser@leftArm(Front) G1 | ClanERMediumLaser@leftArm(Front) G1 | ClanERMediumLaser@rightarm(Front) G1 | ClanERMediumLaser@rightarm(Front) G1 | ClanUltraAC20@RightTorso(Front) G1 | ClanSSRM6@LeftTorso(Front) G2 | ClanSSRM6@LeftTorso(Front) G2 leftArmx2 | rightarmx2 | RightTorsox1 | LeftTorsox2 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown)
47 Loki Yes Clan 17.500000 65.000000 Standard Standard 13 Single 0 No Yes Yes Yes 43 ECM 1.3 1.3 1.4 1.4 1.5 1.5 1.55 0.6 0.3 10.85 7 2x ClanERMediumLaser, 2x ClanMachineGun, 2x ClanUltraAC5, ClanSSRM6 ClanERMediumLaser@LeftArm(Front) G1 | ClanERMediumLaser@RightArm(Front) G1 | ClanMachineGun@LeftTorso(Front) G1 | ClanMachineGun@RightTorso(Front) G1 | ClanUltraAC5@LeftArm(Front) G1 | ClanUltraAC5@RightArm(Front) G1 | ClanSSRM6@Special1(Front) G2 LeftArmx2 | RightArmx2 | LeftTorsox1 | RightTorsox1 | Special1x1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O10) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O2) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown) | Special1(Front)
48 Longbow Yes IS 20.500000 85.000000 Standard Standard 11 Single 0 No Yes Yes Yes 43 1.6 1.6 1.65 1.65 1.2 1.2 1.85 1.0 0.3 12.05 6 2x MediumLaser, 2x LRM5, 2x LRM20 MediumLaser@LeftTorso(Front) G1 | MediumLaser@RightTorso(Front) G1 | LRM5@LeftTorso(Front) G2 | LRM5@RightTorso(Front) G2 | LRM20@LeftArm(Front) G2 | LRM20@RightArm(Front) G2 LeftTorsox2 | RightTorsox2 | LeftArmx1 | RightArmx1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown)
49 MadCat_MkII Yes Clan 18.500000 90.000000 Reflective EndoSteel 17 Single 1 Yes Yes Yes Yes 43 1.8 1.8 1.8 1.8 1.85 1.85 2.0 0.8 0.3 14 12 4x ClanERSmallLaser, 2x ClanMachineGun, 4x ClanUltraAC5, 2x ClanLRM15 ClanERSmallLaser@LeftTorso(Front) G1 | ClanERSmallLaser@CenterTorso(Front) G1 | ClanERSmallLaser@CenterTorso(Front) G1 | ClanERSmallLaser@RightTorso(Front) G1 | ClanMachineGun@LeftTorso(Front) G1 | ClanMachineGun@RightTorso(Front) G1 | ClanUltraAC5@LeftArm(Front) G1 | ClanUltraAC5@LeftArm(Front) G1 | ClanUltraAC5@RightArm(Front) G1 | ClanUltraAC5@RightArm(Front) G1 | ClanLRM15@Special2(Side) G2 | ClanLRM15@Special1(Side) G2 LeftTorsox2 | CenterTorsox2 | RightTorsox2 | LeftArmx2 | RightArmx2 | Special2x1 | Special1x1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O8) | RightTorso(M0/P0/B0/O8) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O4) | Special2(M0/P0/B0/O4) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Front) | Head(Unknown) | Special1(Side) | Special2(Side)
50 Madcat Yes Clan 16.500000 75.000000 FerroFiberus EndoSteel 17 Single 0 Yes Yes Yes Yes 43 1.1 1.1 1.2 1.2 1.4 1.4 1.5 0.5 0.25 9.65 8 2x ClanMediumPulseLaser, 2x ClanERLargeLaser, 2x ClanMachineGun, 2x ClanLRM10 ClanMediumPulseLaser@LeftArm(Front) G1 | ClanMediumPulseLaser@RightArm(Front) G1 | ClanERLargeLaser@LeftArm(Front) G1 | ClanERLargeLaser@RightArm(Front) G1 | ClanMachineGun@LeftTorso(Front) G1 | ClanMachineGun@RightTorso(Front) G1 | ClanLRM10@Special1(Side) G2 | ClanLRM10@Special2(Side) G2 LeftArmx2 | RightArmx2 | LeftTorsox1 | RightTorsox1 | Special1x1 | Special2x1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O9) | RightTorso(M0/P0/B0/O9) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O3) | Special2(M0/P0/B0/O3) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown) | Special1(Side) | Special2(Side)
51 Masakari Yes Clan 18.000000 85.000000 FerroFiberus Standard 15 Single 0 Yes Yes Yes Yes 43 1.25 1.25 1.4 1.4 1.45 1.45 1.8 0.85 0.25 11.1 7 2x ClanERMediumLaser, 2x ClanUltraAC2, ClanGaussRifle, 2x ClanLRM10 ClanERMediumLaser@RightTorso(Front) G1 | ClanERMediumLaser@RightTorso(Front) G1 | ClanUltraAC2@RightArm(Front) G1 | ClanUltraAC2@RightArm(Front) G1 | ClanGaussRifle@LeftArm(Front) G1 | ClanLRM10@LeftTorso(Side) G2 | ClanLRM10@LeftTorso(Side) G2 RightTorsox2 | RightArmx2 | LeftArmx1 | LeftTorsox2 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Side) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown)
52 Mauler Yes IS 26.000000 90.000000 FerroFiberus Standard 11 Single 0 No Yes Yes Yes 43 1.4 1.4 1.4 1.4 1.55 1.55 1.45 0.8 0.25 11.2 10 2x GaussRifle, 2x SSRM4, 6x LRM5 GaussRifle@LeftArm(Front) G1 | GaussRifle@RightArm(Front) G1 | SSRM4@LeftTorso(Front) G2 | SSRM4@RightTorso(Front) G2 | LRM5@LeftTorso(Side) G2 | LRM5@LeftTorso(Side) G2 | LRM5@LeftTorso(Side) G2 | LRM5@RightTorso(Side) G2 | LRM5@RightTorso(Side) G2 | LRM5@RightTorso(Side) G2 LeftArmx1 | RightArmx1 | LeftTorsox4 | RightTorsox4 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Front+Side) | RightTorso(Front+Side) | CenterTorso(Unknown) | Head(Unknown)
53 Novacat Yes Clan 16.000000 70.000000 Reactive EndoSteel 17 Single 1 Yes Yes Yes Yes 43 ECM 1.55 1.55 1.45 1.45 1.5 1.5 1.7 0.75 0.3 11.75 7 5x ClanMediumPulseLaser, 2x ClanERLargeLaser ClanMediumPulseLaser@LeftArm(Front) G1 | ClanMediumPulseLaser@LeftArm(Front) G1 | ClanMediumPulseLaser@LeftArm(Front) G1 | ClanMediumPulseLaser@LeftTorso(Front) G1 | ClanMediumPulseLaser@RightTorso(Front) G1 | ClanERLargeLaser@RightArm(Front) G1 | ClanERLargeLaser@RightArm(Front) G1 LeftArmx3 | LeftTorsox1 | RightTorsox1 | RightArmx2 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown)
54 Osiris Yes IS 9.500000 30.000000 FerroFiberus EndoSteel 11 Single 1 Yes Yes Yes Yes 43 0.6 0.6 0.6 0.6 0.65 0.65 0.75 0.3 0.25 5 5 SmallPulseLaser, 2x MediumPulseLaser, SSRM2, NarcBeacon SmallPulseLaser@special2(Front) G1 | MediumPulseLaser@special1(Front) G1 | MediumPulseLaser@RightArm(Front) G1 | SSRM2@LeftArm(Front) G2 | NarcBeacon@LeftArm(Front) G3 special2x1 | special1x1 | RightArmx1 | LeftArmx2 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O1) | Special2(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Unknown) | RightTorso(Unknown) | Head(Unknown) | Special1(Unknown) | Special2(Unknown)
55 Owens Yes IS 9.750000 35.000000 Standard Standard 5 Single 0 No Yes Yes Yes 39 ECM 0.9 0.9 0.8 0.8 0.8 0.8 1.05 0.35 0.3 6.7 5 2x SmallLaser, MediumLaser, 2x SRM6 SmallLaser@CenterTorso(Front) G1 | SmallLaser@CenterTorso(Front) G1 | MediumLaser@Head(Front) G1 | SRM6@LeftArm(Front) G2 | SRM6@RightArm(Front) G2 CenterTorsox2 | Headx1 | LeftArmx1 | RightArmx1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O8) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Unknown) | RightTorso(Unknown) | CenterTorso(Front) | Head(Front)
56 Puma Yes Clan 7.500000 35.000000 FerroFiberus EndoSteel 10 Single 1 Yes Yes Yes Yes 43 0.8 0.8 0.6 0.6 0.6 0.6 0.85 0.45 0.25 5.55 4 2x ClanSmallPulseLaser, 2x ClanLRM20 ClanSmallPulseLaser@LeftTorso(Front) G1 | ClanSmallPulseLaser@RightTorso(Front) G1 | ClanLRM20@LeftArm(Front) G2 | ClanLRM20@RightArm(Front) G2 LeftTorsox1 | RightTorsox1 | LeftArmx1 | RightArmx1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown)
57 Raven Yes IS 10.250000 35.000000 FerroFiberus Standard 7 Single 0 No Yes Yes Yes 43 BAP, ECM 0.6 0.65 0.65 0.65 0.7 0.7 0.75 0.25 0.25 5.2 5 2x MediumLaser, SRM6, LRM5, NarcBeacon MediumLaser@RightArm(Front) G1 | MediumLaser@RightArm(Front) G1 | SRM6@RightTorso(Front) G2 | LRM5@RightTorso(Side) G2 | NarcBeacon@LeftArm(Front) G3 RightArmx2 | RightTorsox2 | LeftArmx1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Unknown) | RightTorso(Front+Side) | CenterTorso(Unknown) | Head(Unknown)
58 Rifleman Yes IS 12.500000 60.000000 FerroFiberus EndoSteel 17 Single 0 Yes Yes Yes Yes 43 0.9 0.9 1.0 1.0 1.5 1.5 1.3 0.7 0.25 9.05 6 2x MediumLaser, 2x LargeLaser, 2x PPC MediumLaser@Special1(Front) G1 | MediumLaser@Special1(Front) G1 | LargeLaser@LeftArm(Front) G1 | LargeLaser@RightArm(Front) G1 | PPC@LeftArm(Front) G1 | PPC@RightArm(Front) G1 Special1x2 | LeftArmx2 | RightArmx2 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O2) LeftArm(Front) | RightArm(Front) | LeftTorso(Unknown) | RightTorso(Unknown) | Head(Unknown) | Special1(Front)
59 Ryoken Yes IS 12.500000 55.000000 FerroFiberus EndoSteel 10 Single 1 Yes Yes Yes Yes 43 ECM 0.9 0.9 1.0 1.0 1.05 1.05 1.15 0.75 0.25 8.05 6 4x ClanMediumPulseLaser, 2x ClanSSRM6 ClanMediumPulseLaser@LeftArm(Front) G1 | ClanMediumPulseLaser@LeftArm(Front) G1 | ClanMediumPulseLaser@RightArm(Front) G1 | ClanMediumPulseLaser@RightArm(Front) G1 | ClanSSRM6@LeftTorso(Front) G2 | ClanSSRM6@RightTorso(Front) G2 LeftArmx2 | RightArmx2 | LeftTorsox1 | RightTorsox1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown)
60 Shadowcat Yes Clan 9.750000 45.000000 FerroFiberus EndoSteel 10 Single 1 Yes Yes Yes Yes 43 BAP 0.85 0.85 0.85 0.85 0.9 0.9 1.05 0.45 0.25 6.95 5 2x ClanMediumPulseLaser, ClanGaussRifle, 2x ClanSSRM4 ClanMediumPulseLaser@RightTorso(Front) G1 | ClanMediumPulseLaser@RightArm(Front) G1 | ClanGaussRifle@LeftArm(Front) G1 | ClanSSRM4@LeftTorso(Front) G2 | ClanSSRM4@RightTorso(Front) G2 RightTorsox2 | RightArmx1 | LeftArmx1 | LeftTorsox1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown)
61 Solitaire Yes Clan 7.000000 25.000000 FerroFiberus EndoSteel 10 Single 0 Yes Yes Yes Yes 46 0.6 0.6 0.6 0.6 0.55 0.55 0.8 0.4 0.25 4.95 4 ClanERSmallLaser, 2x ClanERMediumLaser, ClanERLargeLaser ClanERSmallLaser@LeftTorso(Front) G1 | ClanERMediumLaser@LeftTorso(Front) G1 | ClanERMediumLaser@CenterTorso(Front) G1 | ClanERLargeLaser@Special1(Front) G1 LeftTorsox2 | CenterTorsox1 | Special1x1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O3) LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Front) | RightTorso(Unknown) | CenterTorso(Front) | Head(Unknown) | Special1(Front)
62 Sunder Yes IS 17.500000 90.000000 Standard Standard 17 Single 0 Yes Yes Yes Yes 43 1.35 1.35 1.55 1.55 1.65 1.65 2.0 1.0 0.3 12.4 10 6x MediumLaser, PPC, 2x MachineGun, SRM6 MediumLaser@LeftTorso(Front) G1 | MediumLaser@LeftTorso(Front) G1 | MediumLaser@LeftTorso(Rear) G1 | MediumLaser@RightTorso(Rear) G1 | MediumLaser@RightTorso(Front) G1 | MediumLaser@RightTorso(Front) G1 | PPC@LeftArm(Front) G1 | MachineGun@RightArm(Front) G1 | MachineGun@RightArm(Front) G1 | SRM6@CenterTorso(Front) G2 LeftTorsox3 | RightTorsox3 | LeftArmx1 | RightArmx2 | CenterTorsox1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Front+Rear) | RightTorso(Front+Rear) | CenterTorso(Front) | Head(Unknown)
63 Templar Yes IS 20.500000 85.000000 Standard EndoSteel 25 Single 1 Yes Yes Yes Yes 43 1.45 1.45 1.6 1.6 1.8 1.8 2.05 0.95 0.3 13 11 6x MediumPulseLaser, PPC, 2x MachineGun, AC20, SRM4 MediumPulseLaser@LeftArm(Front) G1 | MediumPulseLaser@LeftTorso(Front) G1 | MediumPulseLaser@RightTorso(Front) G1 | MediumPulseLaser@RightArm(Front) G1 | MediumPulseLaser@LeftTorso(Rear) G1 | MediumPulseLaser@RightTorso(Rear) G1 | PPC@LeftArm(Front) G1 | MachineGun@LeftTorso(Front) G1 | MachineGun@RightTorso(Front) G1 | AC20@RightArm(Front) G1 | SRM4@RightTorso(Front) G2 LeftArmx2 | LeftTorsox3 | RightTorsox4 | RightArmx2 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Front+Rear) | RightTorso(Front+Rear) | CenterTorso(Unknown) | Head(Unknown)
64 Thanatos Yes IS 18.000000 75.000000 FerroFiberus EndoSteel 20 Single 1 Yes Yes Yes Yes 43 0.9 0.9 1.3 1.3 1.2 1.2 1.6 0.85 0.25 9.5 7 MediumPulseLaser, 2x MediumLaser, 2x LargePulseLaser, 2x LRM10 MediumPulseLaser@LeftTorso(Front) G1 | MediumLaser@LeftArm(Front) G1 | MediumLaser@RightTorso(Front) G1 | LargePulseLaser@LeftArm(Front) G1 | LargePulseLaser@RightTorso(Front) G1 | LRM10@RightArm(Front) G2 | LRM10@RightArm(Front) G2 LeftTorsox1 | LeftArmx2 | RightTorsox2 | RightArmx2 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown)
65 Thor Yes Clan 19.000000 70.000000 FerroFiberus Standard 20 Single 1 Yes Yes Yes Yes 43 1.1 1.1 1.2 1.2 1.4 1.4 1.5 0.5 0.25 9.65 7 2x ClanMediumPulseLaser, ClanERPPC, 2x ClanMachineGun, ClanUltraAC10, CLANLRM10 ClanMediumPulseLaser@LeftTorso(Front) G1 | ClanMediumPulseLaser@RightTorso(Front) G1 | ClanERPPC@RightArm(Front) G1 | ClanMachineGun@LeftTorso(Front) G1 | ClanMachineGun@RightTorso(Front) G1 | ClanUltraAC10@LeftArm(Front) G1 | CLANLRM10@Special1(Side) G2 LeftTorsox2 | RightTorsox2 | RightArmx1 | LeftArmx1 | Special1x1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O10) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O2) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown) | Special1(Side)
66 Uller Yes Clan 8.000000 30.000000 FerroFiberus EndoSteel 8 Single 0 Yes Yes Yes Yes 43 ECM 0.45 0.45 0.5 0.5 0.6 0.6 0.65 0.45 0.25 4.45 4 ClanERSmallLaser, ClanERMediumLaser, ClanUltraAC5, ClanSRM6 ClanERSmallLaser@LeftArm(Front) G1 | ClanERMediumLaser@RightArm(Front) G1 | ClanUltraAC5@RightArm(Front) G1 | ClanSRM6@LeftArm(Front) G2 LeftArmx2 | RightArmx2 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Unknown) | RightTorso(Unknown) | CenterTorso(Unknown) | Head(Unknown)
67 Uziel Yes IS 11.500000 50.000000 Standard EndoSteel 10 Single 1 Yes Yes Yes Yes 45 1.2 1.2 1.05 1.05 1.15 1.15 1.0 0.45 0.3 8.55 7 2x SmallPulseLaser, PPC, 2x MachineGun, UltraAC5, LRM10 SmallPulseLaser@LeftTorso(Front) G1 | SmallPulseLaser@RightTorso(Front) G1 | PPC@LeftArm(Front) G1 | MachineGun@LeftTorso(Front) G1 | MachineGun@RightTorso(Front) G1 | UltraAC5@RightArm(Front) G1 | LRM10@Special1(Side) G2 LeftTorsox2 | RightTorsox2 | LeftArmx1 | RightArmx1 | Special1x1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O4) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | Head(Unknown) | Special1(Side)
68 Victor Yes IS 17.000000 80.000000 Standard EndoSteel 10 Single 1 Yes Yes Yes Yes 43 ECM 1.3 1.3 1.3 1.3 1.25 1.25 1.65 1.1 0.3 10.75 7 2x MediumLaser, 2x MachineGun, AC20, 2x SRM4 MediumLaser@LeftArm(Front) G1 | MediumLaser@LeftArm(Front) G1 | MachineGun@LeftTorso(Front) G1 | MachineGun@RightTorso(Front) G1 | AC20@RightArm(Front) G1 | SRM4@LeftTorso(Front) G2 | SRM4@RightTorso(Front) G2 LeftArmx2 | LeftTorsox2 | RightTorsox2 | RightArmx1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Unknown) | Head(Unknown)
69 Vulture Yes Clan 16.000000 60.000000 Reactive EndoSteel 12 Single 0 No Yes Yes Yes 43 BAP 1.3 1.3 1.5 1.5 1.6 1.6 1.75 0.5 0.3 11.35 10 4x ClanMachineGun, 2x ClanLargePulseLaser, 2x ClanLRM5, 2x ClanLRM10 ClanMachineGun@LeftArm(Front) G1 | ClanMachineGun@Special1(Front) G1 | ClanMachineGun@Special1(Front) G1 | ClanMachineGun@RightArm(Front) G1 | ClanLargePulseLaser@LeftArm(Front) G1 | ClanLargePulseLaser@RightArm(Front) G1 | ClanLRM5@LeftTorso(Side) G2 | ClanLRM5@RightTorso(Side) G2 | ClanLRM10@LeftTorso(Side) G2 | ClanLRM10@RightTorso(Side) G2 LeftArmx2 | Special1x2 | RightArmx2 | LeftTorsox2 | RightTorsox2 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | Head(M0/P0/B0/O1) | Special1(M0/P0/B0/O2) LeftArm(Front) | RightArm(Front) | LeftTorso(Side) | RightTorso(Side) | Head(Unknown) | Special1(Front)
70 Warhammer Yes IS 15.000000 70.000000 Standard Standard 17 Single 0 Yes Yes Yes Yes 43 1.55 1.55 1.45 1.45 1.5 1.5 1.7 0.75 0.3 11.75 5 2x MediumLaser, 2x ERPPC, SRM6 MediumLaser@RightTorso(Front) G1 | MediumLaser@LeftTorso(Front) G1 | ERPPC@RightArm(Front) G1 | ERPPC@LeftArm(Front) G1 | SRM6@special2(Front) G2 RightTorsox1 | LeftTorsox1 | RightArmx1 | LeftArmx1 | special2x1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | Special2(M0/P0/B0/O2) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | Special2(Unknown)
71 Wolfhound Yes Clan 10.250000 35.000000 FerroFiberus EndoSteel 14 Single 0 Yes Yes Yes Yes 43 ECM 0.5 0.5 0.7 0.7 0.75 0.75 0.95 0.5 0.25 5.6 5 3x ClanMediumPulseLaser, ClanERMediumLaser, ClanERLargeLaser ClanMediumPulseLaser@LeftTorso(Front) G1 | ClanMediumPulseLaser@CenterTorso(Front) G1 | ClanMediumPulseLaser@RightTorso(Front) G1 | ClanERMediumLaser@CenterTorso(Rear) G1 | ClanERLargeLaser@RightArm(Front) G1 LeftTorsox1 | CenterTorsox2 | RightTorsox1 | RightArmx1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Unknown) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front) | CenterTorso(Front+Rear) | Head(Unknown)
72 Zeus Yes IS 22.000000 80.000000 FerroFiberus Standard 22 Single 0 Yes Yes Yes Yes 43 1.1 1.1 1.25 1.25 1.45 1.45 1.55 0.8 0.25 10.2 7 4x MediumPulseLaser, ERLargeLaser, ERPPC, LRM15 MediumPulseLaser@LeftTorso(Front) G1 | MediumPulseLaser@RightTorso(Front) G1 | MediumPulseLaser@RightArm(Front) G1 | MediumPulseLaser@RightTorso(Rear) G1 | ERLargeLaser@CenterTorso(Front) G1 | ERPPC@LeftArm(Front) G1 | LRM15@RightArm(Front) G2 LeftTorsox1 | RightTorsox2 | RightArmx2 | CenterTorsox1 | LeftArmx1 LeftArm(M0/P0/B0/O8) | RightArm(M0/P0/B0/O8) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) | Head(M0/P0/B0/O1) LeftArm(Front) | RightArm(Front) | LeftTorso(Front) | RightTorso(Front+Rear) | CenterTorso(Front) | Head(Unknown)
73 jenner2c No Clan 10.000000 35.000000 FerroFiberus Standard 0 0 Yes Yes No No 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.9 0 LeftArm(M2/P0/B2/O0) | RightArm(M2/P0/B2/O0) | Special1(M3/P0/B0/O0) LeftArm(Unknown) | RightArm(Unknown) | Special1(Unknown) not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud
74 koto No IS 7.000000 25.000000 FerroFiberus EndoSteel 0 0 No Yes No No 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.9 0 LeftArm(M0/P1/B1/O0) | RightArm(M0/P1/B1/O0) | CenterTorso(M0/P0/B2/O0) | Special1(M0/P0/B3/O0) LeftArm(Unknown) | RightArm(Unknown) | CenterTorso(Unknown) | Special1(Unknown) not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud
75 locust2c No Clan 7.000000 25.000000 FerroFiberus EndoSteel 0 0 No Yes No No 4 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.9 0 LeftArm(M0/P0/B1/O2) | RightArm(M0/P0/B1/O2) | Special1(M0/P0/B3/O0) LeftArm(Unknown) | RightArm(Unknown) | Special1(Unknown) not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud
76 marauder2 No IS 22.000000 100.000000 FerroFiberus EndoSteel 0 0 Yes No Yes No 6 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.9 0 LeftArm(M0/P0/B4/O1) | RightArm(M0/P0/B4/O1) | LeftTorso(M0/P0/B1/O1) | RightTorso(M0/P0/B1/O1) | Special1(M0/P7/B0/O0) | Special2(M0/P0/B0/O2) LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown) | Special1(Unknown) | Special2(Unknown) not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud
77 pitbull No Clan 15.000000 70.000000 FerroFiberus EndoSteel 0 0 Yes No No No 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.9 0 LeftArm(M0/P2/B2/O0) | RightArm(M0/P2/B2/O0) | LeftTorso(M0/P0/B2/O0) | RightTorso(M0/P0/B2/O0) | Special1(M0/P4/B0/O0) LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown) | Special1(Unknown) not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud
78 privateer No IS 12.500000 55.000000 FerroFiberus Standard 0 0 No No Yes No 2 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.9 0 LeftArm(M0/P1/B0/O1) | RightArm(M0/P1/B0/O1) | LeftTorso(M0/P2/B2/O0) | RightTorso(M0/P0/B1/O0) | Special1(M4/P0/B0/O0) LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown) | Special1(Unknown) not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud
79 razorback No IS 9.000000 30.000000 FerroFiberus EndoSteel 0 0 No Yes No No 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.9 0 LeftArm(M2/P0/B2/O0) | RightArm(M0/P0/B3/O0) | Special1(M0/P2/B0/O0) | Special2(M0/P2/B0/O0) LeftArm(Unknown) | RightArm(Unknown) | Special1(Unknown) | Special2(Unknown) not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud
80 reaper No Clan 16.500000 75.000000 FerroFiberus EndoSteel 0 0 Yes Yes No No 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.9 0 LeftArm(M0/P0/B3/O0) | RightArm(M0/P0/B3/O0) | LeftTorso(M0/P0/B2/O0) | RightTorso(M0/P0/B2/O0) | CenterTorso(M0/P6/B0/O0) LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown) | CenterTorso(Unknown) not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud
81 reaver No Clan 10.000000 40.000000 FerroFiberus EndoSteel 0 0 Yes No Yes No 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.9 0 LeftArm(M0/P0/B3/O0) | RightArm(M0/P0/B3/O0) | LeftTorso(M0/P0/B1/O0) | RightTorso(M0/P0/B1/O0) LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown) not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud
82 shadowhawk No IS 12.500000 55.000000 FerroFiberus Standard 0 0 Yes Yes No No 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.9 0 LeftArm(M0/P1/B2/O0) | RightArm(M0/P0/B3/O0) | LeftTorso(M0/P0/B1/O0) | RightTorso(M0/P0/B1/O0) | Special1(M0/P2/B0/O0) | Special2(M2/P0/B0/O0) LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown) | Special1(Unknown) | Special2(Unknown) not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud
83 strider No IS 11.000000 40.000000 FerroFiberus Standard 0 0 No No Yes No 4 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.9 0 LeftArm(M2/P0/B0/O0) | RightArm(M2/P0/B0/O0) | LeftTorso(M0/P0/B0/O2) | RightTorso(M0/P0/B0/O2) | CenterTorso(M0/P0/B2/O0) LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown) | CenterTorso(Unknown) not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud
84 urbanmech Yes IS 5.000000 30.000000 Standard Standard 8 Single 0 Yes Yes No No 50 0.45 0.45 0.5 0.5 0.6 0.6 0.65 0.45 0.25 4.45 2 SmallLaser, AC20 SmallLaser@LeftArm(Front) G1 | AC20@RightArm(Front) G1 LeftArmx1 | RightArmx1 LeftArm(M0/P0/B0/O12) | RightArm(M0/P0/B0/O12) | LeftTorso(M0/P0/B0/O12) | RightTorso(M0/P0/B0/O12) | CenterTorso(M0/P0/B0/O2) LeftArm(Front) | RightArm(Front) | LeftTorso(Unknown) | RightTorso(Unknown) | CenterTorso(Unknown)
85 urbanmech_iic No Clan 5.000000 30.000000 FerroFiberus Standard 0 0 Yes Yes No No 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.9 0 LeftArm(M0/P2/B2/O0) | RightArm(M0/P0/B4/O0) | LeftTorso(M0/P1/B0/O0) | RightTorso(M0/P1/B0/O0) LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown) not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud
86 ursus No Clan 10.500000 50.000000 FerroFiberus Standard 0 0 No Yes No No 3 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.9 0 LeftArm(M0/P0/B0/O3) | RightArm(M0/P0/B4/O0) | CenterTorso(M2/P0/B0/O0) | Special1(M0/P0/B1/O0) | Special2(M0/P0/B1/O0) LeftArm(Unknown) | RightArm(Unknown) | CenterTorso(Unknown) | Special1(Unknown) | Special2(Unknown) not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud
87 vulture2 No Clan 16.500000 75.000000 FerroFiberus EndoSteel 0 0 No Yes No No 5 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.9 0 LeftArm(M0/P3/B0/O0) | RightArm(M0/P0/B0/O3) | LeftTorso(M2/P0/B2/O0) | RightTorso(M2/P0/B2/O0) | Special1(M0/P0/B0/O2) | Special2(M0/P0/B2/O0) LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown) | Special1(Unknown) | Special2(Unknown) not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud
88 vulturec No Clan 14.000000 60.000000 FerroFiberus Standard 0 0 No Yes No No 3 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.9 0 LeftArm(M0/P3/B0/O0) | RightArm(M0/P3/B0/O0) | LeftTorso(M2/P0/B0/O0) | RightTorso(M2/P0/B0/O0) | CenterTorso(M0/P0/B0/O1) | Special1(M0/P0/B0/O1) | Special2(M0/P0/B0/O1) LeftArm(Unknown) | RightArm(Unknown) | LeftTorso(Unknown) | RightTorso(Unknown) | CenterTorso(Unknown) | Special1(Unknown) | Special2(Unknown) not registered in active mech tables; missing M_* mech ID defines; core mech files exist; not fully packaged (core.build, props.build, textures.build); missing display/name string IDs (IDS/DNL); no loose hsh bmp in Mechs/MFD/hud
+654
View File
@@ -0,0 +1,654 @@
# mech_loadouts.csv Catch-Up Notes
This document is for future AI agents or maintainers who need to understand how [mech_loadouts.csv](mech_loadouts.csv) was built, where each column comes from, and what files must be updated together when mech data changes.
## Purpose
`mech_loadouts.csv` is a flattened mech audit sheet built from the FireStorm content tree. It combines:
- mech identity and stat data
- current in-game playability status
- installed/default weapon loadouts
- weapon facing and grouping metadata
- zone hardpoint capacity
- notes about missing registration, packaging, or asset support
The CSV is not a primary source of truth. It is a synthesized report built from the mech content files and several registration tables.
## Current CSV Column Layout
The important derived columns are:
- `In_game_playable`
- `Weapons_With_Locations`
- `Default_Installed_Locations`
- `Available_Slot_Capacity_By_Zone`
- `Available_Hardpoints`
- `notes`
The existing stat columns before those are mostly direct mech data fields and were not re-derived during the audit.
## What Each Derived Column Means
### `In_game_playable`
`Yes` if the mech is currently in the active playable roster, `No` otherwise.
This was determined by comparing the mech folder set against the active registration chain:
- `Gameleap/code/mw4/Code/MW4/MechLabHeaders.h`
- `Gameleap/mw4/Content/ShellScripts/MechLabHeaders.h`
- `Gameleap/mw4/Content/Tables/MechChassisTable.tbl`
- `Gameleap/mw4/Content/Tables/MechTable.tbl`
- `Gameleap/mw4/Content/core.build`
- plus supporting string and packaging manifests
Practical rule:
- folder presence alone does not make a mech playable
- playable mechs must have the ID/table/build registrations aligned
### `Weapons_With_Locations`
This column lists each installed weapon in the mech¡¯s default subsystem file, with:
- the weapon name
- the `InternalLocation`
- the `WeaponFacing`
- the weapon group
Current format:
- `WeaponName@Location(Facing) G#`
Examples:
- `MediumPulseLaser@LeftTorso(Rear) G1`
- `SRM4@RightTorso(Front) G2`
- `ClanLRM10@LeftTorso(Side) G2`
Derived from the mech¡¯s `.subsystems` file by reading weapon subsystem blocks.
Important notes:
- `WeaponFacing=0` => `Front`
- `WeaponFacing=1` => `Rear`
- `WeaponFacing=2` => `Side`
- if `WeaponFacing` is absent, it defaults to `Front`
### `Default_Installed_Locations`
A compact count summary of where the mech¡¯s default weapons are installed, based on `InternalLocation` in the `.subsystems` file.
Example:
- `LeftArmx2 | LeftTorsox3 | RightTorsox4 | RightArmx2`
This is about installed weapons, not slot capacity.
### `Available_Slot_Capacity_By_Zone`
A compact summary of zone capacities from the mech¡¯s `.damage` file.
Current format:
- `Zone(M# / P# / B# / O#)`
Where:
- `M` = MissileSlots
- `P` = ProjectileSlots
- `B` = BeamSlots
- `O` = OmniSlots
Example:
- `LeftTorso(M0/P2/B2/O0)`
This reflects what the mech can support in that zone, not what is currently installed.
### `Available_Hardpoints`
A list of zones that have at least one positive slot count in the `.damage` file.
Current format:
- `LeftArm(Front)`
- `LeftTorso(Front+Rear)`
- `RightTorso(Side)`
This column is a directional summary of where weapons can be mounted.
Important:
- this is derived from slot capacity plus facing information from weapon loadouts
- it is not a literal mechanical port map
- if a zone has multiple facing types in the mech¡¯s default loadout, it can show a combined label such as `Front+Rear` or `Front+Side`
## Core Data Sources
### 1. Mech registration and playability
Primary files:
- `Gameleap/code/mw4/Code/MW4/MechLabHeaders.h`
- `Gameleap/mw4/Content/ShellScripts/MechLabHeaders.h`
- `Gameleap/mw4/Content/Tables/MechChassisTable.tbl`
- `Gameleap/mw4/Content/Tables/MechTable.tbl`
- `Gameleap/mw4/Content/core.build`
What they control:
- which mechs are actually in the active roster
- which IDs exist in code and script space
- which mech resources are packed into the runtime build
If a mech folder exists but it is not listed in these files, it is usually not playable.
### 2. Default weapon loadout and weapon-facing metadata
Primary file:
- `Gameleap/mw4/Content/Mechs/<MechName>/<mech>.subsystems`
Each weapon block typically contains:
- `Model=` weapon subsystem resource
- `InternalLocation=` where the weapon is mounted
- `Site=` mount port name
- `GroupIndex=` weapon group number
- `WeaponFacing=` optional facing metadata
- `AmmoCount=` for ammo-using weapons
The `.subsystems` file is the source for:
- `Weapons_With_Locations`
- `Default_Installed_Locations`
- facing annotations in the hardpoint summaries
### 3. Slot capacity / hardpoint support
Primary file:
- `Gameleap/mw4/Content/Mechs/<MechName>/<mech>.damage`
Each section such as `[LeftArmInternal]` or `[RightTorsoInternal]` may contain:
- `MissileSlots=`
- `ProjectileSlots=`
- `BeamSlots=`
- `OmniSlots=`
These are used to derive:
- `Available_Slot_Capacity_By_Zone`
- `Available_Hardpoints`
### 4. Weapon slot type rules in code
Relevant code:
- `Gameleap/code/mw4/Code/MW4/MWDamageObject.cpp`
- `Gameleap/code/mw4/Code/MW4/MWDamageObject.hpp`
- `Gameleap/code/mw4/Code/MW4/MechLab.cpp`
- `Gameleap/code/mw4/Code/MW4/Subsystem_Tool.cpp`
- `Gameleap/code/mw4/Code/MW4/Weapon_Tool.cpp`
- `Gameleap/code/mw4/Code/MW4/Weapon.hpp`
- `Gameleap/code/mw4/Code/MW4/Weapon.cpp`
- `Gameleap/code/mw4/Code/MW4/hudweapon.cpp`
What the code does:
- reads slot counts from `.damage`
- reads `InternalLocation` and `WeaponFacing` from `.subsystems`
- maps weapon slot type and size from the weapon model
- assigns weapons to valid locations during mechlab editing
## Facing Semantics
Weapon facing is stored per weapon instance, not as a separate hardpoint label.
Values seen in the data/code:
- `0` = front
- `1` = rear
- `2` = side
Code references:
- `Gameleap/code/mw4/Code/MW4/Weapon_Tool.cpp`
- `Gameleap/code/mw4/Code/MW4/Weapon.hpp`
- `Gameleap/code/mw4/Code/MW4/Weapon.cpp`
- `Gameleap/code/mw4/Code/MW4/hudweapon.cpp`
- `Gameleap/code/mw4/Code/MW4/MechLab.cpp`
Important nuance:
- a zone name like `LeftTorso` does not tell you the facing by itself
- the facing comes from the weapon block¡¯s `WeaponFacing`
- the same zone can contain multiple weapons with different facings
## Group Index Meaning
The `G1`, `G2`, etc. suffix in `Weapons_With_Locations` is the weapon group number from `GroupIndex` in the subsystem file.
This is not a location or facing field.
Meaning:
- `G1` = weapon group 1
- `G2` = weapon group 2
- and so on
## How The Spreadsheet Was Built
The CSV was assembled by cross-referencing:
- mech folder contents under `Gameleap/mw4/Content/Mechs/`
- `.subsystems` weapon blocks
- `.damage` internal zone capacities
- active roster tables and IDs
- string registration files
- build manifests
- loose UI art assets under `Gameleap/mw4/hsh/`
The output is a derived audit sheet, not a direct export from a single source file.
## Known Pitfalls
### 1. Folder presence does not mean playable
Some mech folders exist but are not in the active roster tables or headers. These need ID/table/build integration before they are usable in-game.
### 2. File names are not always canonical
Some mechs use alternate base filenames inside their folder. Do not assume `FolderName/FolderName.damage` or `FolderName/FolderName.subsystems` always exist.
Prefer:
- parse the `.instance` file when present
- otherwise detect the first matching `.subsystems` / `.damage` file by extension and content
### 3. `Available_Hardpoints` is a summary, not a literal port map
It summarizes zones with capacity and facing hints, but it does not enumerate every exact port or site name.
### 4. Rear-facing weapons are per-instance
If a mech has one rear-mounted weapon in a torso zone, that does not mean all weapons in that zone are rear-facing.
## Update Checklist For Future Changes
If a mech is added or changed, check these in order:
1. update or verify the mech `.data`, `.instance`, `.subsystems`, and `.damage` files
2. ensure the mech is added to the active ID header(s)
3. update `MechChassisTable.tbl` and `MechTable.tbl`
4. update `core.build` and any related build manifests
5. add any needed string IDs or script references
6. confirm the mech has the expected loose or packed art assets
7. rebuild the CSV derived columns if the data changed
## Useful File Map
### Mech data
- `Gameleap/mw4/Content/Mechs/<MechName>/`
### Roster and IDs
- `Gameleap/code/mw4/Code/MW4/MechLabHeaders.h`
- `Gameleap/mw4/Content/ShellScripts/MechLabHeaders.h`
- `Gameleap/mw4/Content/Tables/MechChassisTable.tbl`
- `Gameleap/mw4/Content/Tables/MechTable.tbl`
### Slot/hardpoint logic
- `Gameleap/code/mw4/Code/MW4/MWDamageObject.cpp`
- `Gameleap/code/mw4/Code/MW4/MechLab.cpp`
- `Gameleap/code/mw4/Code/MW4/Subsystem_Tool.cpp`
### Facing logic
- `Gameleap/code/mw4/Code/MW4/Weapon.cpp`
- `Gameleap/code/mw4/Code/MW4/Weapon.hpp`
- `Gameleap/code/mw4/Code/MW4/Weapon_Tool.cpp`
- `Gameleap/code/mw4/Code/MW4/hudweapon.cpp`
### Build manifests
- `Gameleap/mw4/Content/core.build`
- `Gameleap/mw4/Content/props.build`
- `Gameleap/mw4/Content/textures.build`
## Current State Summary
At the time this doc was written:
- `mech_loadouts.csv` includes playability, default loadout, facing, hardpoint, and notes columns
- non-playable mechs are explicitly marked `No`
- Templar and other mechs with rear or side mounts have facing visible in the CSV
- the file is intended to be a living audit artifact for future data cleanup and enablement work
---
## MechEditor Web App — Data Sources, Fields, and Conversions (documented 2026-07-23)
The MechEditor is a single-file Python HTTP server at `/home/rich/Repositories/MechEditor/mech_editor.py`.
It runs at `localhost:8765`, parses the firestorm content tree, and presents a full mech configuration editor in the browser.
This section documents every data field it reads, every conversion it performs, and every naming rule it enforces.
---
### Data Files Parsed Per Mech
All files live under `Gameleap/mw4/Content/Mechs/<MechDir>/`.
#### `.data` file (`parse_data()`)
| Field in file | Editor key | Notes |
|---|---|---|
| `VehicleTonnage` | `tonnage` | chassis base tonnage (float) |
| `MaxVehicleTonnage` | `max_tonnage` | max loadout tonnage (float) |
| `TechType` | `tech` | `$(Tech_IS)` ? `"IS"`, `$(Tech_Clan)` ? `"Clan"` |
| `MaxHeat` | `max_heat` | heat capacity (int) |
| `VehicleTradeValue` | `trade_value` | C-bills |
| `DragoonValue` | `dragoon` | used in Power Rating bar scaling |
| `MinMaxSpeed` | `min_max_speed` | base speed ceiling in **m/s** |
| `MaxSpeed` | `max_speed` | absolute speed ceiling in **m/s** (engine upgrades may not exceed this) |
| `FullStopTurnRate` | `full_stop_turn` | turn rate at zero speed, in **degrees/sec** |
| `TopSpeedTurnRate` | `top_speed_turn` | turn rate at top speed, in **degrees/sec** |
| `Acceleration` | `acceleration` | forward acceleration in **m/s²** |
| `Decceleration` | `decceleration` | forward braking in **m/s²****double-c spelling is canonical in the engine source** |
| `ReverseAccelerationMultiplier` | `rev_accel_mult` | multiplier applied to Acceleration for reverse |
| `ReverseDeccelerationMultiplier` | `rev_decel_mult` | multiplier applied to Decceleration for reverse — double-c canonical |
| `MinStandTransitionSpeed` | not surfaced | animation threshold only — see note below |
| `CanLoadJumpJets` | `can_jj` | Yes/No |
| `CanLoadECM` | `can_ecm` | Yes/No |
| `CanLoadBeagle` | `can_bap` | Yes/No |
| `CanLoadLightAmp` | `can_lightamp` | Yes/No |
| `CanLoadAMS` | `can_ams` | Yes/No |
| `CanLoadLAMS` | `can_lams` | Yes/No |
| `CanLoadIFF_Jammer` | `can_iff` | Yes/No |
**MinStandTransitionSpeed** is the speed (m/s) below which the mech switches from its walking animation to its idle/standing animation.
It is NOT a hard movement limit. It is purely an animation state machine threshold in `Mech.cpp`.
Code path: `animStateEngine?RequestState(StandState)` when `currentSpeedMPS <= minStandTransitionSpeed`.
Must be > 0 (validated in `Vehicle_Tool.cpp`). Argus = 12.631 m/s = 45.5 kph.
#### `.instance` file (`parse_instance()`)
| Field | Editor key | Notes |
|---|---|---|
| `PowerRating` | `PowerRating` | mechlab bar value, 0100 |
| `ArmorRating` | `ArmorRating` | mechlab bar value, 0100 |
| `SpeedRating` | `SpeedRating` | mechlab bar value, 0100 |
| `HeatRating` | `HeatRating` | mechlab bar value, 0100 |
| `DoesHaveLightAmp` | `has_lightamp` | 0 or 1, default 1 — whether LightAmp is currently installed |
#### `.subsystems` file (`parse_subsystems()`)
Provides: armor type + per-zone multipliers, installed heatsinks, jump jets, engine upgrades, weapons, electronics.
**Armor block:**
- `ArmorType=` ? armor type string (`Standard`, `FerroFiberus`, `Reactive`, `Reflective`, `Solarian`)
- Per-zone entries: `LeftArm=1.0`, `RightTorso=2.5`, etc. — multiplier for that zone
**Engine:**
- `EngineUpgrade` blocks counted ? `engine_upgrades` (05)
**Weapons** — each weapon block contains:
- `Model=` ? weapon subsystem resource path (name extracted)
- `InternalLocation=` ? zone name
- `Site=` ? mount port name (from armature)
- `GroupIndex=` ? weapon group number
- `WeaponFacing=` ? 0=Front, 1=Rear, 2=Side (absent = Front)
- `AmmoCount=` ? rounds for ammo-using weapons
- `EjectSite=` ? optional ejection site for ammo
**Electronics:** ECM, Beagle (BAP), AMS, LAMS, IFF_Jammer detected by subsystem model path.
#### `.damage` file (`parse_damage()`)
Per zone section `[ZoneInternal]`:
- `BaseArmorValue` ? starting armor (float)
- `MaxArmorValue` ? hard cap on armor pts for that zone
- `InternalHPValue` ? internal structure HP
- `OmniSlots`, `BeamSlots`, `MissileSlots`, `ProjectileSlots` ? weapon slot counts
Special zones:
- `SpecialAttachedToZone=` ? which body section this special zone is attached to
- `DamagePropagationZone=` ? where overflow damage propagates
#### `.engine` file (`parse_engine()`)
| Field | Editor key | Notes |
|---|---|---|
| `NumHeatSinks` | `NumHeatSinks` | free heatsinks from engine (not in subsystems) |
| `TonsPerUpgrade` | `TonsPerUpgrade` | tonnage cost per engine upgrade tier |
| `MPSPerUpgrade` | `MPSPerUpgrade` | m/s speed gain per upgrade tier |
#### `.torso` file (`parse_torso()`)
| Field | Notes |
|---|---|
| `TwistSpeed` | torso horizontal rotation speed — may be a macro reference |
| `PitchSpeed` | torso vertical rotation speed — may be a macro reference |
| `TwistRadius` | max horizontal twist angle — may be a macro reference |
| `PitchRadius` | max vertical pitch angle — may be a macro reference |
| `ArmRatioAngle` | ratio of arm tracking vs torso rotation — may be a macro reference |
All torso fields may reference macros from `Content/Defines/MechTorso.defines`.
The editor resolves them using a preloaded `TORSO_DEFINES` dict. Notable values:
```
NORMAL_RATIO = 40
SNAIL_TSPEED = 40
NORMAL_TSPEED = 60
FAST_TSPEED = 80
WIDE_TRADIUS = 160
NORMAL_PRADIUS = 40
```
---
### Calculated Ratings and Conversions
#### Speed (kph)
```
top_speed_kph = min(MinMaxSpeed + MPSPerUpgrade × engine_upgrades, MaxSpeed) × 3.6
```
- `MinMaxSpeed` and `MaxSpeed` from `.data` (m/s)
- `MPSPerUpgrade` from `.engine` (m/s per tier)
- `engine_upgrades` from `.subsystems` (05)
- Multiply by 3.6 to convert m/s ? kph
- Argus example: (20.28 + 1.11 × 10) × 3.6 = 113.5 kph
#### Speed Rating (bar)
```
speed_rating = (top_speed_mps / MaxSpeed) × 100 [capped at 100]
```
#### Turn Rate — degrees to radians
The `.data` file stores turn rates in **degrees/sec**. The mechlab UI label was changed to show rad/sec:
- `StringResource.rc`: `IDS_ML_CH_TURNRATE` ? `"Turn Rate (Top Speed Rad/Sec):"`
- Conversion: `radians = degrees × ?/180` where `?/180 ? 0.017453`
- Argus: FullStopTurn = 75° = **1.309 rad/sec**, TopSpeedTurn = 45° = **0.785 rad/sec**
#### Acceleration / Deceleration (m/s²)
Stored directly in `.data`. Reverse values are derived:
```
reverse_accel = Acceleration × ReverseAccelerationMultiplier
reverse_decel = Decceleration × ReverseDeccelerationMultiplier
```
**The double-c spelling (`Decceleration`, `ReverseDeccelerationMultiplier`) is canonical — it matches the engine source. Do not "fix" the spelling.**
#### Heat Rating (bar)
```
total_hs = NumHeatSinks (engine) + installed_heatsinks (subsystems)
effective_hs = total_hs × (2 if Double else 1)
heat_rating = (effective_hs / MaxHeat) × 100 [capped at 100]
```
#### Power Rating (bar)
```
total_damage = ? (DamageAmount × NumFire) for each installed weapon
power_rating = (total_damage / 80) × 100 [capped at 100]
```
- `DamageAmount` and `NumFire` come from `WeaponSubsystems/<weapon>.data` following `!include` chains
- Parsed by `load_weapon_damages()` at server startup, cached in `Handler.weapon_damages`
- Argus example with default load: ~36.2 total damage ? 45 rating (stored = 42)
#### Armor Rating (bar)
```
for each zone:
pts = multiplier × ARMOR_PTS_PER_TON[armor_type]
effective = min(pts, MaxArmorValue[zone])
armor_rating = (? effective / ? MaxArmorValue) × 100
```
Armor pts per ton by type (from `Adept/ResourceImagePool.cpp` and game design):
| Type | Pts/ton |
|---|---|
| Standard | 32 |
| FerroFiberus | 38 |
| Reactive | 30 |
| Reflective | 30 |
| Solarian | 60 |
Note: `FerroFiberus` is the canonical internal token (not player-visible). The player sees `DNL_FERROFIB = "Ferro Fibrous"` via string lookup.
---
### Active-in-Game Detection
Source: `Gameleap/mw4/Content/Tables/MechChassisTable.tbl`
Format:
```
DisplayKey=Mechs\DirName\FileName.data
//CommentedKey=Mechs\DirName\FileName.data <- inactive
```
- Active = entry exists AND is not prefixed with `//`
- Currently the only inactive mech: **Dasher** (commented out)
- The editor shows a green **ACTIVE IN GAME** or red **NOT IN GAME** banner at top of Stats tab
---
### hsh/ Image Naming Conventions
The `hsh/` directory under `Gameleap/mw4/` holds loose BMP files loaded at runtime (not packed into `.mw4`).
There are four relevant subdirectories, each with a different naming authority.
#### `hsh/hud/` — in-game HUD damage silhouette (own mech)
#### `hsh/MFD/` — MFD target display silhouette (target mech)
#### `hsh/radar/hud/` — radar damage overlay
**All three use identical stems** sourced from `huddamage.cpp` `texturename[]` array.
Load path:
- hud/MFD: `hsh\<texturename>.bmp` where texturename = `hud\<stem>` ? file = `hsh/hud/<stem>.bmp`
- radar: `hsh\radar\<texturename>.bmp` ? file = `hsh/radar/hud/<stem>.bmp`
Code: `render.cpp` `CRadar_Device::LoadRadarDamageTexture()` and `huddamage.cpp` `HUDDamage`.
#### `hsh/Mechs/` — mw4print scorecard portrait
Load path: `recscore.cpp` ? `GetLocString(model->m_nameIndex)` ? `DNL_*` string from `StringResource.rc` ? lowercased filename.
Different naming authority from the other three.
---
### Complete Canonical Name Table
Key: mech directory name (case-insensitive) ? canonical stem for `hsh/hud/`, `hsh/MFD/`, `hsh/radar/hud/`.
Entries in **bold** differ from the directory name.
| Directory | hud/MFD/radar stem | hsh/Mechs/ portrait filename |
|---|---|---|
| Annihilator | annihilator | annihilator.bmp |
| Archer | archer | archer.bmp |
| ArcticWolf | arcticwolf | arctic wolf.bmp |
| Ares | ares | ares.bmp |
| Argus | argus | argus.bmp |
| Assassin2 | assassin2 | **assassin ii.bmp** |
| Atlas | atlas | atlas.bmp |
| Avatar | avatar | avatar.bmp |
| Awesome | awesome | awesome.bmp |
| Battlemaster | battlemaster | battlemaster.bmp |
| Battlemaster2c | **battlemasteriic** | **battlemaster iic.bmp** |
| Behemoth | behemoth | behemoth.bmp |
| Behemoth2 | **behemothii** | **behemoth ii.bmp** |
| Blackhawk | blackhawk | black hawk.bmp |
| Blacknight | **blackknight** | black knight.bmp |
| Blacklanner | blacklanner | black lanner.bmp |
| Brigand | brigand | brigand.bmp |
| Bushwacker | bushwacker | bushwacker.bmp |
| Catapult | catapult | catapult.bmp |
| CauldronBorn | cauldronborn | **cauldronborn.bmp** (table key has hyphen; DNL does not) |
| Chimera | chimera | chimera.bmp |
| Commando | commando | commando.bmp |
| Cougar | cougar | cougar.bmp |
| Cyclops | cyclops | cyclops.bmp |
| Daishi | daishi | daishi.bmp |
| Deimos | deimos | deimos.bmp |
| Dragon | dragon | dragon.bmp |
| Fafnir | fafnir | fafnir.bmp |
| Flea | flea | flea.bmp |
| Gladiator | gladiator | gladiator.bmp |
| Grizzly | grizzly | grizzly.bmp |
| Hauptmann | hauptmann | hauptmann.bmp |
| Hellhound | hellhound | hellhound.bmp |
| Hellspawn | hellspawn | hellspawn.bmp |
| Highlander | highlander | highlander.bmp |
| Hollander | **hollanderii** | **hollander ii.bmp** |
| Hunchback | hunchback | hunchback.bmp |
| Kodiak | kodiak | kodiak.bmp |
| Loki | loki | loki.bmp |
| Longbow | longbow | longbow.bmp |
| Madcat | madcat | mad cat.bmp |
| Madcat_MKII | **madcat2** | mad cat mkii.bmp |
| Masakari | masakari | masakari.bmp |
| Mauler | mauler | mauler.bmp |
| Novacat | novacat | nova cat.bmp |
| Osiris | osiris | osiris.bmp |
| Owens | owens | owens.bmp |
| Puma | puma | puma.bmp |
| Raven | raven | raven.bmp |
| Rifleman | rifleman | rifleman.bmp |
| Ryoken | ryoken | ryoken.bmp |
| Shadowcat | shadowcat | shadow cat.bmp |
| Solitaire | solitaire | solitaire.bmp |
| Sunder | sunder | sunder.bmp |
| Templar | templar | templar.bmp |
| Thanatos | thanatos | thanatos.bmp |
| Thor | thor | thor.bmp |
| Uller | uller | uller.bmp |
| Urbanmech | urbanmech | urbanmech.bmp |
| Uziel | uziel | uziel.bmp |
| Victor | victor | victor.bmp |
| Vulture | vulture | vulture.bmp |
| Warhammer | warhammer | warhammer.bmp |
| Wolfhound | wolfhound | wolfhound.bmp |
| Zeus | zeus | zeus.bmp |
**Critical mismatches where directory name ? hud/MFD stem (files must use the stem, not the dir name):**
| Directory | Wrong name (dir-based) | Correct name (stem) |
|---|---|---|
| Battlemaster2c | battlemaster2c.bmp | **battlemasteriic.bmp** |
| Behemoth2 | behemoth2.bmp | **behemothii.bmp** |
| Blacknight | blacknight.bmp | **blackknight.bmp** |
| Hollander | hollander.bmp | **hollanderii.bmp** |
| Madcat_MKII | madcat_mkii.bmp | **madcat2.bmp** |
**Portrait mismatches for hsh/Mechs/ (table key ? DNL string):**
The `MechChassisTable.tbl` display key and `GetLocString()` DNL string differ for these mechs.
mw4print uses the DNL string. The table key is NOT the correct portrait filename for these 5 mechs.
| Directory | Table key (wrong for mw4print) | DNL string (correct portrait stem) |
|---|---|---|
| Assassin2 | AssassinII ? assassinii | `DNL_ASSASSIN2 "Assassin II"` ? **assassin ii.bmp** |
| Battlemaster2c | BattlemasterIIC ? battlemasteriic | `DNL_BATTLEMASTERIIC "Battlemaster IIc"` ? **battlemaster iic.bmp** |
| Behemoth2 | BehemothII ? behemothii | `DNL_BEHEMOTHII "Behemoth II"` ? **behemoth ii.bmp** |
| CauldronBorn | Cauldron-Born ? cauldron-born | `DNL_CAULDRONBORN "Cauldronborn"` ? **cauldronborn.bmp** |
| Hollander | HollanderII ? hollanderii | `DNL_HOLLANDERII "Hollander II"` ? **hollander ii.bmp** |
---
### MechEditor Implementation Notes
The editor encodes all of the above knowledge in two Python dicts:
**`MECH_HSH_STEMS`** (in `mech_editor.py`)
Maps lowercase dir name ? canonical stem for `hsh/hud/`, `hsh/MFD/`, `hsh/radar/hud/`.
Source: `huddamage.cpp` `texturename[]` array.
**`MECH_PORTRAIT_OVERRIDES`** (in `mech_editor.py`)
Maps lowercase dir name ? portrait stem for `hsh/Mechs/` where the DNL string differs from the chassis table key.
Source: `DNL_*` entries in `Gameleap/code/mw4/Code/scriptstrings/StringResource.rc`.
For all other mechs, the portrait stem is derived dynamically from `MechChassisTable.tbl` (display key, lowercased).
The Assets tab in the editor shows all four image types. When an image is missing, it displays:
```
Wants: hsh/<subdir>/<expected_filename>.bmp
```
so the user knows exactly what to rename or create.
+64
View File
@@ -0,0 +1,64 @@
Chassis,InternalLocation,Model,Site,AmmoCount,GroupIndex,WeaponFacing
Archer,LeftTorso,LRM15,site_ldmissile,24,2,2
Archer,RightTorso,LRM15,site_rdmissile,24,2,2
Ares,Special2,ClanLRM10,site_lmissileport,24,2,2
Ares,Special2,ClanLRM10,site_missileport,24,2,2
Ares,Special2,ClanLRM10,site_rmissileport,24,2,2
AssassinII,Special1,LRM5,site_missileport,24,2,2
Atlas,LeftTorso,LRM20,site_lmissileport,12,2,2
Avatar,LeftTorso,LRM5,site_lmissileport,24,2,2
Avatar,RightTorso,LRM5,site_rmissileport,24,2,2
Avatar,LeftTorso,LRM10,site_lmissileport,24,2,2
Avatar,RightTorso,LRM10,site_rmissileport,24,2,2
Awesome,CenterTorso,LRM5,site_ctorsoport,24,2,2
Battlemaster,RightTorso,MediumLaser,site_rdtorsoport,,1,1
Battlemaster,LeftTorso,MediumLaser,site_ldtorsoport,,1,1
BattlemasterIIC,RightTorso,ClanERMediumLaser,site_rdtorsoport,,1,1
BattlemasterIIC,LeftTorso,ClanERMediumLaser,site_ldtorsoport,,1,1
Black Lanner,Special2,ClanSRM6,site_rmissileport,15,2,2
Black Lanner,Special1,ClanLRM10,site_lmissileport,24,2,2
Bushwacker,Special1,LRM5,site_missileport,24,2,2
Bushwacker,Special1,LRM5,site_missileport,24,2,2
Catapult,LeftArm,LRM20,site_lmissileport,12,2,2
Catapult,RightArm,LRM20,site_rmissileport,12,2,2
Cauldron-Born,Special1,ClanLRM10,site_rmissileport,24,2,2
Cauldron-Born,Special2,ClanLRM10,site_lmissileport,24,2,2
Chimera,RightTorso,LRM5,site_missileport,24,2,2
Chimera,RightTorso,LRM5,site_missileport,24,2,2
Chimera,RightTorso,LRM5,site_missileport,24,2,2
Chimera,RightTorso,LRM5,site_missileport,24,2,2
Cyclops,LeftTorso,LRM10,site_lmissile,24,2,2
Cougar,LeftTorso,ClanLRM10,site_lmissileport,12,2,2
Cougar,RightTorso,ClanLRM10,site_rmissileport,12,2,2
Deimos,Special1,ClanLRM15,site_lmissileport,16,2,2
Deimos,Special2,ClanLRM15,site_rmissileport,16,2,2
Dragon,CenterTorso,LRM10,site_missleport,24,2,2
Flea,LeftTorso,SmallLaser,site_ltorsoport,,1,1
Flea,RightTorso,SmallLaser,site_rtorsoport,,1,1
Grizzly,LeftTorso,CLANLRM10,site_missleport,24,2,2
Hellhound,LeftTorso,ClanLRM10,site_missileport,12,2,2
Mad Cat,Special1,ClanLRM10,site_lmissileport,24,2,2
Mad Cat,Special2,ClanLRM10,site_rmissileport,24,2,2
Mad Cat MKII,Special2,ClanLRM15,site_lmissileport,16,2,2
Mad Cat MKII,Special1,ClanLRM15,site_rmissileport,16,2,2
Masakari,LeftTorso,ClanLRM10,site_lmissleport,24,2,2
Masakari,LeftTorso,ClanLRM10,site_ltorsoport,24,2,2
Mauler,LeftTorso,LRM5,site_lmissileport,24,2,2
Mauler,LeftTorso,LRM5,site_lmissileport,24,2,2
Mauler,LeftTorso,LRM5,site_lmissileport,24,2,2
Mauler,RightTorso,LRM5,site_rmissileport,24,2,2
Mauler,RightTorso,LRM5,site_rmissileport,24,2,2
Mauler,RightTorso,LRM5,site_rmissileport,24,2,2
Raven,RightTorso,LRM5,site_rtorsoport,24,2,2
Sunder,LeftTorso,MediumLaser,site_ldtorsoport,,1,1
Sunder,RightTorso,MediumLaser,site_rdtorsoport,,1,1
Templar,LeftTorso,MediumPulseLaser,site_ldrtorsoport,,1,1
Templar,RightTorso,MediumPulseLaser,site_rdrtorsoport,,1,1
Thor,Special1,CLANLRM10,site_missileport,24,2,2
Uziel,Special1,LRM10,site_missileport,24,2,2
Vulture,LeftTorso,ClanLRM5,site_lmissileport,24,2,2
Vulture,RightTorso,ClanLRM5,site_rmissileport,24,2,2
Vulture,LeftTorso,ClanLRM10,site_lmissileport,24,2,2
Vulture,RightTorso,ClanLRM10,site_rmissileport,24,2,2
Wolfhound,CenterTorso,ClanERMediumLaser,site_reartorsoport,,1,1
Zeus,RightTorso,MediumPulseLaser,site_reartorsoport,,1,1
1 Chassis InternalLocation Model Site AmmoCount GroupIndex WeaponFacing
2 Archer LeftTorso LRM15 site_ldmissile 24 2 2
3 Archer RightTorso LRM15 site_rdmissile 24 2 2
4 Ares Special2 ClanLRM10 site_lmissileport 24 2 2
5 Ares Special2 ClanLRM10 site_missileport 24 2 2
6 Ares Special2 ClanLRM10 site_rmissileport 24 2 2
7 AssassinII Special1 LRM5 site_missileport 24 2 2
8 Atlas LeftTorso LRM20 site_lmissileport 12 2 2
9 Avatar LeftTorso LRM5 site_lmissileport 24 2 2
10 Avatar RightTorso LRM5 site_rmissileport 24 2 2
11 Avatar LeftTorso LRM10 site_lmissileport 24 2 2
12 Avatar RightTorso LRM10 site_rmissileport 24 2 2
13 Awesome CenterTorso LRM5 site_ctorsoport 24 2 2
14 Battlemaster RightTorso MediumLaser site_rdtorsoport 1 1
15 Battlemaster LeftTorso MediumLaser site_ldtorsoport 1 1
16 BattlemasterIIC RightTorso ClanERMediumLaser site_rdtorsoport 1 1
17 BattlemasterIIC LeftTorso ClanERMediumLaser site_ldtorsoport 1 1
18 Black Lanner Special2 ClanSRM6 site_rmissileport 15 2 2
19 Black Lanner Special1 ClanLRM10 site_lmissileport 24 2 2
20 Bushwacker Special1 LRM5 site_missileport 24 2 2
21 Bushwacker Special1 LRM5 site_missileport 24 2 2
22 Catapult LeftArm LRM20 site_lmissileport 12 2 2
23 Catapult RightArm LRM20 site_rmissileport 12 2 2
24 Cauldron-Born Special1 ClanLRM10 site_rmissileport 24 2 2
25 Cauldron-Born Special2 ClanLRM10 site_lmissileport 24 2 2
26 Chimera RightTorso LRM5 site_missileport 24 2 2
27 Chimera RightTorso LRM5 site_missileport 24 2 2
28 Chimera RightTorso LRM5 site_missileport 24 2 2
29 Chimera RightTorso LRM5 site_missileport 24 2 2
30 Cyclops LeftTorso LRM10 site_lmissile 24 2 2
31 Cougar LeftTorso ClanLRM10 site_lmissileport 12 2 2
32 Cougar RightTorso ClanLRM10 site_rmissileport 12 2 2
33 Deimos Special1 ClanLRM15 site_lmissileport 16 2 2
34 Deimos Special2 ClanLRM15 site_rmissileport 16 2 2
35 Dragon CenterTorso LRM10 site_missleport 24 2 2
36 Flea LeftTorso SmallLaser site_ltorsoport 1 1
37 Flea RightTorso SmallLaser site_rtorsoport 1 1
38 Grizzly LeftTorso CLANLRM10 site_missleport 24 2 2
39 Hellhound LeftTorso ClanLRM10 site_missileport 12 2 2
40 Mad Cat Special1 ClanLRM10 site_lmissileport 24 2 2
41 Mad Cat Special2 ClanLRM10 site_rmissileport 24 2 2
42 Mad Cat MKII Special2 ClanLRM15 site_lmissileport 16 2 2
43 Mad Cat MKII Special1 ClanLRM15 site_rmissileport 16 2 2
44 Masakari LeftTorso ClanLRM10 site_lmissleport 24 2 2
45 Masakari LeftTorso ClanLRM10 site_ltorsoport 24 2 2
46 Mauler LeftTorso LRM5 site_lmissileport 24 2 2
47 Mauler LeftTorso LRM5 site_lmissileport 24 2 2
48 Mauler LeftTorso LRM5 site_lmissileport 24 2 2
49 Mauler RightTorso LRM5 site_rmissileport 24 2 2
50 Mauler RightTorso LRM5 site_rmissileport 24 2 2
51 Mauler RightTorso LRM5 site_rmissileport 24 2 2
52 Raven RightTorso LRM5 site_rtorsoport 24 2 2
53 Sunder LeftTorso MediumLaser site_ldtorsoport 1 1
54 Sunder RightTorso MediumLaser site_rdtorsoport 1 1
55 Templar LeftTorso MediumPulseLaser site_ldrtorsoport 1 1
56 Templar RightTorso MediumPulseLaser site_rdrtorsoport 1 1
57 Thor Special1 CLANLRM10 site_missileport 24 2 2
58 Uziel Special1 LRM10 site_missileport 24 2 2
59 Vulture LeftTorso ClanLRM5 site_lmissileport 24 2 2
60 Vulture RightTorso ClanLRM5 site_rmissileport 24 2 2
61 Vulture LeftTorso ClanLRM10 site_lmissileport 24 2 2
62 Vulture RightTorso ClanLRM10 site_rmissileport 24 2 2
63 Wolfhound CenterTorso ClanERMediumLaser site_reartorsoport 1 1
64 Zeus RightTorso MediumPulseLaser site_reartorsoport 1 1
+45
View File
@@ -0,0 +1,45 @@
Chassis,InternalLocation,Model,Site,AmmoCount,GroupIndex,WeaponFacing
Ares,Special1,ClanERSmallLaser,site_lgunport,,1,
Ares,Special1,ClanERSmallLaser,site_rgunport,,1,
Ares,Special2,ClanLRM10,site_lmissileport,24,2,2
Ares,Special2,ClanLRM10,site_missileport,24,2,2
Ares,Special2,ClanLRM10,site_rmissileport,24,2,2
AssassinII,Special1,LRM5,site_missileport,24,2,2
Atlas,Special1,AC20,site_rtorsoport,10,1,
Atlas,Special2,SRM6,site_llmissile,15,2,
Avatar,Special1,MachineGun,site_ltorsoport,200,1,
Avatar,Special1,MachineGun,site_rtorsoport,200,1,
Battlemaster,Special2,PPC,site_rdgunport,,1,
Battlemaster,Special1,SRM6,site_missile,15,2,
BattlemasterIIC,Special1,ClanSSRM6,site_missile,15,2,
Behemoth,Special1,LargeLaser,site_ctorsoport,,1,
BehemothII,Special1,LargeLaser,site_ctorsoport,,1,
Black Lanner,Special2,ClanSRM6,site_rmissileport,15,2,2
Black Lanner,Special1,ClanLRM10,site_lmissileport,24,2,2
Brigand,Special2,MediumLaser,site_ltorsoport,,1,
Brigand,Special1,MediumLaser,site_rtorsoport,,1,
Bushwacker,Special1,LRM5,site_missileport,24,2,2
Bushwacker,Special1,LRM5,site_missileport,24,2,2
Cauldron-Born,Special1,ClanLRM10,site_rmissileport,24,2,2
Cauldron-Born,Special2,ClanLRM10,site_lmissileport,24,2,2
Cyclops,Special1,MediumPulseLaser,site_gunport,,1,
Deimos,Special1,ClanLRM15,site_lmissileport,16,2,2
Deimos,Special2,ClanLRM15,site_rmissileport,16,2,2
Hauptmann,Special1,ClanUltraAC20,site_rutorsoport,16,1,
Hellspawn,Special1,SSRM2,site_rmissileport,50,2,
HollanderII,Special2,SmallPulseLaser,site_cltorsoport,,1,
HollanderII,Special2,SmallPulseLaser,site_crtorsoport,,1,
HollanderII,Special1,GaussRifle,site_rtorsoport,16,1,
Hunchback,Special1,AC10,site_rutorsoport,20,1,
Loki,Special1,ClanSSRM6,site_missileport,15,2,
Mad Cat,Special1,ClanLRM10,site_lmissileport,24,2,2
Mad Cat,Special2,ClanLRM10,site_rmissileport,24,2,2
Mad Cat MKII,Special2,ClanLRM15,site_lmissileport,16,2,2
Mad Cat MKII,Special1,ClanLRM15,site_rmissileport,16,2,2
Rifleman,Special1,MediumLaser,site_ltorsoport,,1,
Rifleman,Special1,MediumLaser,site_rtorsoport,,1,
Solitaire,Special1,ClanERLargeLaser,site_rtorsoport,,1,
Thor,Special1,CLANLRM10,site_missileport,24,2,2
Uziel,Special1,LRM10,site_missileport,24,2,2
Vulture,Special1,ClanMachineGun,site_ltorsoport,200,1,
Vulture,Special1,ClanMachineGun,site_rtorsoport,200,1,
1 Chassis InternalLocation Model Site AmmoCount GroupIndex WeaponFacing
2 Ares Special1 ClanERSmallLaser site_lgunport 1
3 Ares Special1 ClanERSmallLaser site_rgunport 1
4 Ares Special2 ClanLRM10 site_lmissileport 24 2 2
5 Ares Special2 ClanLRM10 site_missileport 24 2 2
6 Ares Special2 ClanLRM10 site_rmissileport 24 2 2
7 AssassinII Special1 LRM5 site_missileport 24 2 2
8 Atlas Special1 AC20 site_rtorsoport 10 1
9 Atlas Special2 SRM6 site_llmissile 15 2
10 Avatar Special1 MachineGun site_ltorsoport 200 1
11 Avatar Special1 MachineGun site_rtorsoport 200 1
12 Battlemaster Special2 PPC site_rdgunport 1
13 Battlemaster Special1 SRM6 site_missile 15 2
14 BattlemasterIIC Special1 ClanSSRM6 site_missile 15 2
15 Behemoth Special1 LargeLaser site_ctorsoport 1
16 BehemothII Special1 LargeLaser site_ctorsoport 1
17 Black Lanner Special2 ClanSRM6 site_rmissileport 15 2 2
18 Black Lanner Special1 ClanLRM10 site_lmissileport 24 2 2
19 Brigand Special2 MediumLaser site_ltorsoport 1
20 Brigand Special1 MediumLaser site_rtorsoport 1
21 Bushwacker Special1 LRM5 site_missileport 24 2 2
22 Bushwacker Special1 LRM5 site_missileport 24 2 2
23 Cauldron-Born Special1 ClanLRM10 site_rmissileport 24 2 2
24 Cauldron-Born Special2 ClanLRM10 site_lmissileport 24 2 2
25 Cyclops Special1 MediumPulseLaser site_gunport 1
26 Deimos Special1 ClanLRM15 site_lmissileport 16 2 2
27 Deimos Special2 ClanLRM15 site_rmissileport 16 2 2
28 Hauptmann Special1 ClanUltraAC20 site_rutorsoport 16 1
29 Hellspawn Special1 SSRM2 site_rmissileport 50 2
30 HollanderII Special2 SmallPulseLaser site_cltorsoport 1
31 HollanderII Special2 SmallPulseLaser site_crtorsoport 1
32 HollanderII Special1 GaussRifle site_rtorsoport 16 1
33 Hunchback Special1 AC10 site_rutorsoport 20 1
34 Loki Special1 ClanSSRM6 site_missileport 15 2
35 Mad Cat Special1 ClanLRM10 site_lmissileport 24 2 2
36 Mad Cat Special2 ClanLRM10 site_rmissileport 24 2 2
37 Mad Cat MKII Special2 ClanLRM15 site_lmissileport 16 2 2
38 Mad Cat MKII Special1 ClanLRM15 site_rmissileport 16 2 2
39 Rifleman Special1 MediumLaser site_ltorsoport 1
40 Rifleman Special1 MediumLaser site_rtorsoport 1
41 Solitaire Special1 ClanERLargeLaser site_rtorsoport 1
42 Thor Special1 CLANLRM10 site_missileport 24 2 2
43 Uziel Special1 LRM10 site_missileport 24 2 2
44 Vulture Special1 ClanMachineGun site_ltorsoport 200 1
45 Vulture Special1 ClanMachineGun site_rtorsoport 200 1
+731 -1
View File
@@ -52,7 +52,9 @@ Toolchain is late-90s/early-2000s **Visual C++ 6.0** — no git, no modern build
- **`Gameleap/`** — only **`mw4/`** is used (game-data source for resource packing). Former siblings - **`Gameleap/`** — only **`mw4/`** is used (game-data source for resource packing). Former siblings
(editor docs, drivers, batch tools, runtime env) were historical/utility → moved to `_UNUSED\`. (editor docs, drivers, batch tools, runtime env) were historical/utility → moved to `_UNUSED\`.
- **`BTFrstrm/`** — FireStorm design data: mech stat workbooks (`MechInfo_5.04.xls`, `scriptaddmech.xls`). - **`BTFrstrm/`** — FireStorm design data: mech stat workbooks (`MechInfo_5.04.xls`, `scriptaddmech.xls`).
- **`Finished HUDS from J&J/`** — per-mech HUD art (MFD + Radar) for ~15 'Mechs. - **`Finished HUDS from J&J/`** — J&J external MFD/Radar source art, measurements, generated
comparison maps, and audit summary for 13 chassis. The usable mappings were installed on
2026-08-07; see `MFD-RADAR-MAPPINGS.md` and the STEP 13 note below.
### Inside `Gameleap\code/` ### Inside `Gameleap\code/`
- `CoreTech/` — reusable engine layer (GameOS, gosFX, GOSScript, MLR renderer, Network, Stuff, Language, blade) + Tools. - `CoreTech/` — reusable engine layer (GameOS, gosFX, GOSScript, MLR renderer, Network, Stuff, Language, blade) + Tools.
@@ -734,7 +736,735 @@ keeping old-RIO (type 0) protocol behavior byte-identical:
call it via `-Command "& '...driver.ps1' game-start '-tbaud 115200'"``-File` binding call it via `-Command "& '...driver.ps1' game-start '-tbaud 115200'"``-File` binding
rejects dash-leading positional args, and `Start-Process -ArgumentList` doesn't re-quote. rejects dash-leading positional args, and `Start-Process -ArgumentList` doesn't re-quote.
## 📋 Branch `mfdsplit`: mech loadouts, time list, build fixes (2026-07-18)
### Mech loadout changes
- **Battlemaster IS** (`Content\Mechs\Battlemaster\battlemaster.subsystems`): replaced lone
MediumPulseLaser default with full stock IS loadout — PPC (Special2), 6×ML (3 RT, 3 LT),
2×MG (LA, 200 rds), SRM6 (Special1, 15 rds), all GroupIndex=1 except SRM6 (group 2).
- **Battlemaster Clan 2C** (`battlemaster2c.subsystems`): ER PPC (RA), 6×ER ML (3 RT, 3 LT),
2×Clan Gauss (LA, 16 rds each), Clan SSRM6 (Special1, 15 rds).
- **Behemoth / Behemoth2** (`.subsystems`): moved Gauss rifles from weapon group 3 → group 1
(3 occurrences each; gauss rifles now in group 1 alongside other main weapons).
### MP time-limit list fix (ConLobbyMission.script)
- Expanded from 9 → 18 entries (115, 20, 25, 30 min); `max_displayed=10` so dropdown scrolls.
- Fixed bare `else if` (missing braces) that caused a null-reference crash in the console lobby
when the commit was merged from `main`. This was the root cause of the `aa500be7` regression.
- Fixed `i==5` vs `i==6` max_displayed assignment for time vs radar dropdowns.
### Resource builder fix (build-resources.ps1) — VM / generic adapter support
- Builder now always runs with `-window` (windowed mode + DDrawCompat). Fullscreen native DDraw
fails on VMs with generic Microsoft display adapters (no hardware DDraw); windowed DDrawCompat
works on all tested configs including VMs.
- dgVoodoo2 D3D DLL interceptors (`D3D8.dll`, `D3D9.dll`, `D3DImm.dll`) in the working dir
silently break the builder (it exits 0 without building anything). These files were left over
from the abandoned dgVoodoo2 experiment (commit `87e25677`) — removed from repo (`git rm`).
The build script also moves any such files aside defensively via the `$dgvMoved` block.
- Removed all dead code for fullscreen fallback, bitdepth patching, ddraw move-aside logic.
- `dgVoodoo.conf` / `dgVoodooCpl.exe` also removed from `Gameleap\mw4\` (no longer in use).
- Expected behaviour on VM: a "Hardware Error: not compatible with MechWarrior 4" dialog appears
at the end of the build — this is a GameOS hardware-capability warning, NOT a fatal error.
Click OK; the .mw4 packages are built correctly regardless.
### Source tree Korean → English translation + UTF-8 cleanup (2026-07-18)
- Translated all EUC-KR/CP949 Korean developer comments to English across **84 source files**
(~876 lines). Zero Korean bytes remain outside intentional font-table headers (D3FFontEdit2/
fontedit `all.h` etc.). Files are now clean UTF-8 (no BOM), safe for any modern editor.
- Notable functional translations: `recscore.cpp` body-part return values and kill-announcer
format strings (gameplay-visible on Korean Windows; now English on all systems); `nonmfc.h`
assert dialog; GosView profiler `킪``us` (microseconds).
- Latin-1 chars converted to proper UTF-8: © in 3dsmax4/Maxscrpt headers (82 files), ® in
`gosHelp/Remote.cpp`, · bullet points in `ai command.hpp`, Û/ß in AnimationSuite headers.
- Font table `*.h` files (`D3FFontEdit2/`, `fontedit/`) intentionally left as-is (raw byte
values in C array data, not text).
- `korean_diff.html` (side-by-side GitHub-style diff, ~873 KB) committed as documentation.
### Language DLL: English version from source
- `Gameleap\mw4\Language.dll` and `MW4\Language.dll` replaced with the freshly compiled English
build from `Language - Win32 English` config (`Language.dsp`). Fixes Korean button labels in
the GameOS exception/crash dialog (`??? ??...` / `??` / `???` → More Details / Continue / Exit).
The old binary was the original Korean build from the 2009 dev machine.
### mw4print v2.0 additions
- **MySQL export** (`dbexport.h`/`dbexport.cpp`): late-bound runtime load of `libmysql.dll`
(no compile-time MySQL SDK needed). Exports match data (match, player_result, pvp, optional
event tables) to an external MySQL server after each print job. Schema in `db_schema.sql`.
Config via `mw4print.ini [MySQLExport]`; UI via File → Database Settings (Ctrl+D).
- **Configurable banner text**: File → Banner Setting... edits the bottom-of-sheet URL string
(was hardcoded `WWW.MECHJOCK.COM`). Persisted to `options.ini [battle tech print] BannerText=`.
- **libmysql.dll** (MySQL Connector/C 32-bit) added to `Gameleap\mw4\` so deploy copies it
alongside `mw4print.exe`.
- Version bumped to **2.0**, copyright year updated to **2026**.
## 📋 MFD mode 4: right device stagger fix (2026-07-18, eaa5fd3)
Root cause: `CMFD_Device::BeginScene()` cleared BOTH MFD device back-buffers at `sh_step==0`.
But in mode 4 the right MFD flip also fires at `sh_step==1` (= old `sh_step==0` after the
stagger increment), so it presented a just-cleared buffer — only the grid, no channel data.
Fix: `BeginScene()` now only clears/grids the LEFT device at `sh_step==0`. New `BeginSceneRight()`
(added to `render.cpp` / `render.hpp`, called from `WinMain.cpp`) clears/grids the RIGHT device at
`sh_step==1` — one frame AFTER the right flip — so channels 34 render into a fresh buffer before
the next flip. Mode 1 is unchanged.
Mode 4 cycle (with stagger):
- `sh_step 0`: radar + left `BeginScene` (clear + grid); no flip
- `sh_step 1`: flip right MFD (shows channels 34 from previous cycle) + right `BeginScene` (clear + grid)
- `sh_step 24`: channels 02 → left device
- `sh_step 56`: channels 34 → right device
- `sh_step 0` (next): flip radar + left MFD (shows channels 02 from previous cycle)
Files: `CoreTech/Libraries/GameOS/WinMain.cpp`, `render.cpp`, `render.hpp`.
## 📋 Linux→Windows development workflow (2026-07-19, 55b9bfc5)
`build-env/sync-to-windows.sh` — rsync script that pushes all source, content, toolchain, and
assets to the Windows build machine at `/vwe/firestorm`. Excludes generated build outputs
(`rel.bin/`, `dbg.bin/`, `*.mw4`, `*.dep`), `.git`/LFS objects, `_UNUSED/`, and the game deploy
dir (`MW4/`). Run from Linux before triggering a Windows build.
## 📋 ddraw.dll removed from repo; build script moves it aside (2026-07-19, 0ceba9c7, 24825ff3)
`Gameleap/mw4/ddraw.dll` (DDrawCompat) removed from git (was LFS-tracked); now lives as
`ddraw.dll.old` in the same dir for reference. `deploy-editor.ps1` installs DDrawCompat as
`ddraw.dll` at deploy time (the editor needs it for its windowed D3D7 viewport).
`build-resources.ps1` moves `ddraw.dll` aside (`.buildaside`) before running `MW4pro.exe`
because **DDrawCompat is fatal to MW4pro.exe in BOTH windowed and fullscreen modes**, then
restores it in `finally`. The same block also defensively covers dgVoodoo2 interceptors
(`D3D8/D3D9/D3DImm.dll`) in case they reappear.
## 📋 Branch `5.1.0b-in-progress`: multiplayer + RIO + source cleanup (2026-07-19)
### ConLobby V5.1.0b1 — Super6 mech rotation (c768f7c4)
Changes from Buddy 'Highlight' Taylor (MCHL), merged manually:
- Console version string bumped to **V5.1.0b1**.
- `ROOKIEMECH` defines expanded from 4 → **6 mechs**: added Archer (ID=1) and Warhammer (ID=62).
- 16-slot default assignments cycle through all 6 Super6 mechs.
- Right-click randomizer expanded from `random(0,3)``random(0,5)`.
### CRIOMAIN.CPP — Korean translation + RIO poll timeout scaling (16fca6c4, a712002f)
- Translated all EUC-KR/CP949 Korean developer comments (~35 lines) to English. File re-saved
as clean UTF-8. CRLF line endings preserved (`* -text` in `.gitattributes`).
⚠️ **Encoding hazard:** always read/write CRIOMAIN.CPP as binary (or with an explicit encoding
codec). Python text-mode `readlines()` silently strips `\r`, causing a 3000+-line git diff when
every line's `\r\n` becomes `\n`. If that happens, restore CRLF with `re.sub(b'(?<!\r)\n', b'\r\n', data)` in binary mode, then amend the commit.
- Added **`g_dwRIOPollTimeout`** global (default 50 ms). Computed in `SetupConnection` before the
receive loop using: `clamp(⌈480000/baud⌉, 5, 50)`. Preserves 50 ms at 9600 baud; floors at
5 ms for 115200 baud. Implemented with explicit ternary — **`min`/`max` are undeclared in this
translation unit under VC6** (not pulled in by CRIOMAIN's includes); use ternary or `__min`/`__max`.
### 16 pilots + 1 cameraship in multiplayer (f76dc05f)
`MW4Shell.cpp`:
- `CTCL_DefaultHostSetup` (non-coop): `Environment.NetworkMaxPlayers` set to
`params->m_maxPlayers + (CTCL_GetTeslaCountAll() - CTCL_GetTeslaCount())`.
The delta = camera-only seats (Tesla seats not assigned to pilots), reserving one extra
DirectPlay slot per cameraship so the 17th connection isn't rejected.
- `SetNetworkMissionParamater / PLAYER_LIMIT_PARAMETER`: same formula applied at runtime when
host changes the player limit. `gos_NetServerCommands(gos_Commend_UpdateMaxPlayers)` still called;
`break` must be present in this case (was accidentally dropped once — fall-through to
`JOIN_IN_PROGRESS_PARAMETER` corrupts `m_joinInProgress`).
- COOP branch unchanged (capped at 9+bots; no camera seat needed there).
`ConLobby.script`: launch guard `nTempPlayerCount > 16` raised to `> 17` so the cameraship
connection doesn't trigger "Too many player/bots".
### hsh/ BMP canonical renames (5813aeb6, 2026-07-23)
All `hsh/MFD/*.bmp` and `hsh/Mechs/*.bmp` filenames reconciled against the canonical stems
expected by game code. The engine loads MFD images via `huddamage.cpp` `texturename[]` array
(lowercase, no spaces) and Mechs portraits via `GetLocString` DNL strings (mixed-case with
spaces). Any mismatch = silently missing image at runtime.
Key renames:
- `hsh/MFD/assassinii.bmp``assassin2.bmp` (matches `texturename[]` canonical)
- `hsh/Mechs/battlemasteriic.bmp``battlemaster iic.bmp`
- `hsh/Mechs/mad cat mk.ii.bmp``mad cat mkii.bmp`
- `hsh/Mechs/behemoth ii.bmp` added (was absent)
Most other files in both directories are LFS pointer updates only (content unchanged).
### RookieMission configurable defaults via options.ini (5813aeb6, 2026-07-23)
CTCL (console) arcade mode has a "Rookie Mission" quick-launch that previously hardcoded all
game params. Now all 13 params are overridable from an `[RookieMission]` section in
`options.ini` (read by `CTCL_SetCDSP` at startup):
- `MW4Shell.cpp`: added 14 `g_` globals (`g_szRookieMission`, `g_nRookieGameType`, and 12
numeric params); registered as `gosScript_RegisterVariable` in `StartUp`/`ShutDown`;
`CTCL_SetCDSP` reads the `[RookieMission]` page via `NotationFile` and populates them.
Defaults: `"ScarabStronghold - Attrition"`, GameType=2 (Attrition), UnlimitedAmmo=1, all
others zero. `g_nRookieTimeLimit=-1` means "use the server's current time setting".
- `ConLobbyMission.script`: all hardcoded values in `MAIL_SET_ROOKIE_MISSION` handler replaced
with `$$g_szRookieMission$$` / `$$g_nRookieXxx$$` references.
- Requires rebuild: `MW4.exe` (Release + Profile).
### Mechlab turn rate label (5813aeb6, 2026-07-23)
`StringResource.rc` `IDS_ML_CH_TURNRATE`: "Turn Rate (Degrees/Sec.):" →
"Turn Rate (Top Speed Rad/Sec):" to match the actual `.data` field semantics
(`TopSpeedTurnRate` is in rad/s at top speed, not deg/s).
Requires rebuild: `ScriptStrings.dll`.
### BTFrstrm design documentation (840bc96c, 2026-07-23)
Two Word documents added to `BTFrstrm/`:
- `MechDependencyTree.docx` — dependency relationships between mech chassis/variants.
- `Special_Zones.docx` — documentation of special zone types used in maps/missions.
### mech_loadouts.md: MechEditor data model (0344418a, 2026-07-23)
`BTFrstrm/mech_loadouts.md` extended with a full reference section documenting the
MechEditor web app (`/home/rich/Repositories/MechEditor/mech_editor.py`, localhost:8765):
every `.data`/`.instance`/`.subsystems` field parsed, conversions performed (m/s ↔ kph,
rad/s ↔ deg/s), and `hsh/` naming rules for MFD/Mechs/HUD/Radar images. Canonical
reference for future mech data work.
### Load File autoconfig stabilization + docs update (2026-07-24)
End-to-end Load File flow for the console lobby was stabilized and verified with a
full-delta regression INI (all mission options + all 16 slots changed away from rookie
defaults), then round-tripped back via the Default button.
Key fixes in `ConLobby.script` / `ConLobbyMission.script`:
- Eliminated first-click vs second-click drift by moving auto-load to a dedicated
deterministic mission path and preventing redraw-time decal mutation.
- Corrected decal handling: map INI decal IDs to lobby dropdown indices, clamp invalid
indices, and show actual decal IDs in labels.
- Corrected mission+map sequencing: game type now rebuilds map list first; mission name
then resolves against that list (with fallback to map index 0).
- Fixed option-state application so first click applies all options (visibility/weather/
time/radar/heat/friendly fire/splash/unlimited/jam/advance/armor) without needing a
second click.
- Fixed Weapon Jam checkbox visibility refresh when set via Load File (UI now re-inits
correctly when heat/advanced states are applied).
New supported autoconfig key:
- Added `NoReturn=0|1` under `[mission]`:
- parser + script variable in `MW4Shell.cpp` (`g_nAutoNoReturn`),
- application in `ConLobbyMission.script` (`RESPAWN_LIMIT_PARAMETER`),
- docs + sample INIs updated.
Docs updates:
- `BTFrstrm/autoconfig-file-spec.html` corrected for real behavior:
- missing mission keys/section apply defaults (not current UI values),
- missing `MissionName` now falls back to first map for that game type,
- added `NoReturn` semantics and examples.
## 📋 STEP 10: Four-monitor MFD bring-up, display diagnostics, and the native-DirectDraw
## exclusive-mode wall (2026-07-25)
A long, dense session on the new 4-monitor bench (`MR_new`: AMD FirePro W4100, 4 outputs,
Win10). Everything below is empirical — measured on real hardware, not inferred.
### The root problem that made all of this hard
`CHSH_Device::InitFirst` / `InitSecond` (`CoreTech\Libraries\GameOS\render.cpp`) **discarded
every single `HRESULT`** (`SetCooperativeLevel`, `SetDisplayMode`, `CreateSurface`,
`GetAttachedSurface`, `QueryInterface`, `CreateDevice`) and **returned `true`
unconditionally**. A panel that failed to open produced no error, no crash and no log entry —
the monitor simply stayed on the desktop. SPEW is compiled out of shipping builds, so there
was no way to see any of it. **Fixing that visibility was what unblocked the whole session.**
### ✅ Diagnostics added (KEEP THESE — they are why everything below was findable)
- **`gos-displays.txt`** (written next to the exe by `VideoCard.cpp` `LogDisplayDevices()`):
`NumDevices`/`NumHWDevices`/`NumMonitors`, every DirectDraw device + `hw_rasterization`,
the role assignment (`FullScreenDevice`/`g_nNonDualHead`/`g_nDualHead`/`g_nDualHead2`/
`g_nMFD1`/`g_nMFD2`), `-tmon` APPLIED/REJECTED per slot, and the decisive
"mode 4 requires BOTH mfd1 and mfd2" line.
- **Per-call HRESULT logging** in `InitFirst`/`InitSecond` via `HSH_LogInit()` / `HSH_CheckHR()`
/ `HSH_HRName()` (27 `DDERR_*` codes decoded by name). Appends to the same file; each line is
written and flushed individually so **the log survives a crash**.
- **`gos-fps.txt`** — new **`-fps`** switch (`WinMain.cpp` `GOS_LogFrameRate`, hooked onto the
existing `frameRate` global). Per-second: frames, avg fps, **5% low**, worst frame in ms,
and a count of frames over 2x average ("hitches"), plus a **session summary** at exit with
true whole-session 1% / 0.1% lows (computed from a 0.5 ms-bucket histogram). Works in
**Release** — the engine's own `AddDebugData("FrameRate")` is `#ifdef LAB_ONLY`
(`MWMission.cpp`) so it only exists in `MW4pro.exe`. Off unless `-fps` is given (a global
read + branch when absent, no file created).
- ⚠️ **Lesson from the first real run: the instrument was measuring itself.** The original
version wrote one line per second straight to the file. The frame time is recorded
*before* the line is emitted, so the `WriteFile` cost landed in the **next** frame —
producing exactly one inflated frame every second (median worst-frame 24.4 ms against a
16.7 ms vsync interval, in 82% of seconds). Output is now buffered in memory and flushed
only when full or at exit (`atexit`), so a normal session performs no writes while
running. Trade-off: a hard crash loses the un-flushed tail — `gos-displays.txt` is the
crash-survivable log, this one is a measurement instrument.
- Also fixed: a "1% low" over 60 samples/sec degenerates to `nFrames/100 = 0` → clamped to
1 → literally the worst frame restated, so the column was redundant. Per-second is now a
5% low (worst 3-of-60); true 1% / 0.1% lows are in the session summary. And a single
frame longer than a second (a level load) no longer spills into following buckets and
emits a run of bogus one-frame rows.
### ✅ Crash-safety fix (independent of everything else, worth keeping)
`hsh_initialized` was set **unconditionally** after panel init. A failed panel therefore left
null surfaces and a null `IDirect3DDevice7` behind, and the per-frame path called straight
through them. Now: all four `InitSecond` overrides (`CMR`/`CRadar`/`CMFD`/`CMFDRight`) bail on
base failure, `CMFD_Device::InitFirst` reports the real result instead of always `true`, and
`hsh_initialized` is only set when the panels genuinely came up → **the game runs without MFDs
instead of dying**.
- **Crash signature to recognise:** `call [ecx+0x44]` with `ECX=0` = `IDirectDrawSurface7::GetDC`
on a never-created surface (verified against `build-env\dx7asdk\include\ddraw.h` vtable order).
Reported as `EXCEPTION : Attempt to read from address 0x00000044`.
### ✅ AppCompat shim — the single most important operational fact
**`DWM8And16BitMitigation` is keyed on the executable's FULL PATH.** A copy of the game at a
new path silently loses it. MW4 renders at `bitdepth=16` and **modern GPUs expose ZERO 16-bit
display modes** — the shim *synthesises* them (proved directly: crash dump shows
`16 bit modes :` EMPTY without the shim, populated with it).
- **Without the shim the error message actively misleads.** GameOS raises
`GOS_DXRASTERIZER_NOFULLSCREEN` — *"Another application is preventing use of full screen
mode"* (`DXRasterizer.cpp:~1125`). That is a **catch-all** fired after every `SetDisplayMode`
attempt fails; it even scans for NetMeeting. It sends you hunting for a conflicting program
that does not exist. The real cause is the missing shim / absent 16-bit modes.
- **NEW: `build-env\set-appcompat.ps1` + `set-appcompat.bat`** — self-locating (`$PSScriptRoot`)
one-click installer. Applies the layer to `MW4.exe`/`MW4pro.exe`/`MW4Ed2.exe` sitting next to
it, wherever that install lives; HKCU always, HKLM too if elevated (note the HKLM value format
differs — it carries a leading `$` marker). Verifies by reading back; detects the
`HIGHDPIAWARE`-only entry that *suppresses* the auto-shim (the original STEP 5 bug).
`-Remove` / `-WhatIfOnly` supported. **`deploy-mw4.ps1` now ships both into every deployment.**
### ✅ Exclusive fullscreen DOES work on Win10 without dgVoodoo2
Confirmed on the W4100 with system `ddraw.dll` and dgVoodoo2 physically removed: `MW4.exe`
alt-enters to exclusive fullscreen correctly **once the shim is applied to that exe path**.
Windowed mode also works natively with no shim at all (windowed sets
`Environment.bitDepth = DesktopBpp` = 32, so there is no mode switch). Console/shell confirmed
windowed; a **full mission windowed is still untested**.
### ❌ Native multi-monitor MFD is BLOCKED — and no flag combination fixes it
The panels' cooperative-level call was genuinely wrong (a latent 2002 bug): every panel asked to
be **both** the process focus window **and** its own device window, on the one shared `hWindow`,
*after* the main device had already taken exclusive mode on it. The main device
(`DXRasterizer.cpp:~1027`) already uses the correct two-call idiom
(`SETFOCUSWINDOW` alone, then `EXCLUSIVE|FULLSCREEN`) — tagged `//sanghoon`, same author. The
panels never were.
A new **`-tcoop <0-5>`** switch was added to test every plausible form on real hardware without a
rebuild between attempts. Results (no dgVoodoo2, shim applied, `-tmfds 4`):
| `-tcoop` | Flags | Result |
|---|---|---|
| 0 | `SETFOCUSWINDOW\|CREATEDEVICEWINDOW\|ALLOWREBOOT\|EXCLUSIVE\|FULLSCREEN` (legacy) | `DDERR_EXCLUSIVEMODEALREADYSET` |
| 1 | `CREATEDEVICEWINDOW\|EXCLUSIVE\|FULLSCREEN` (no focus claim) | `DDERR_INVALIDPARAMS` |
| 2 | `SETFOCUSWINDOW`, then `CREATEDEVICEWINDOW\|…` | `DDERR_INVALIDPARAMS` |
| 3 | `SETFOCUSWINDOW`, then `EXCLUSIVE\|FULLSCREEN` | 1st panel collides; **that collision steals exclusive from the main display**, after which panels 2+3 fully init (radar reached `CreateDevice(HAL) = DD_OK`). Side effect: desktop left at 1920x1080 **16bpp**. Not viable. |
| 4 | `EXCLUSIVE\|FULLSCREEN` only | `DDERR_EXCLUSIVEMODEALREADYSET` on **all** panels |
| 5 | `ALLOWREBOOT\|EXCLUSIVE\|FULLSCREEN` | `DDERR_EXCLUSIVEMODEALREADYSET` on **all** panels |
**CONCLUSION: on modern Windows only ONE DirectDraw object per process may hold exclusive
fullscreen.** The main display takes it; every secondary panel is refused. XP allowed multiple;
**dgVoodoo2 allows it because it is a full reimplementation of ddraw, not bound by that rule.**
=> **dgVoodoo2 cannot be removed by fixing flags.** The limitation is per *secondary display*,
not per feature, so it applies to **every mode that opens a second monitor** — all MFD modes
(`-tmfds 1/3/4`) **and cameraship mode**, which drives two displays with no MFDs at all via the
single-secondary `mr_device` path (`IsMultimonitorAvaliable()` returns false for
`_ECTCL_CameraShip`, so it falls to `IsSecondaryMonitorAvaliable()` — still a second exclusive
IDirectDraw7). **Console mode is the only configuration that needs no dgVoodoo2**, because it is
single-display. Default stays `-tcoop 0`.
(`-tcoop` is retained: it is how this was settled and will re-settle it on different hardware.)
### ✅ Working 4-monitor config (WITH dgVoodoo2)
- **dgVoodoo2 Scaling mode MUST be "Stretched, Keep Aspect Ratio".** Plain "Stretched" fails
*silently*: main + radar go fullscreen black, both MFD monitors keep showing the desktop, and
**every DirectDraw call still returns `DD_OK`** — the devices are alive but dgVoodoo2 never
drives those outputs. Diagnosed with a temporary per-panel colour-flash test (since removed).
- Device index → physical monitor is **1:1** on this bench (colour test confirmed): main=0,
radar=1, mfd-left=2, mfd-right=3, so `-tmon 1,2,3,4` is identical to auto-detection.
- **Confirmed working end-to-end: all three secondary panels present, full mission played.**
- ⚠️ **dgVoodoo2 is deliberately NOT in the repo and must NOT be — it breaks Windows XP
deployments.** The pod fleet is mixed: XP pods need native DirectDraw and nothing else, while
Win10/11 pods need dgVoodoo2 for any MFD mode. Shipping it in the build (or having
`deploy-mw4.ps1` place it) would push a Win10-only dependency onto XP machines and break them.
It stays an **installed-per-machine, OS-dependent prerequisite** set up by the pod owner on
Win10/11 only. `dgVoodoo.conf`/`dgVoodooCpl.exe` were removed in `0ceba9c7` for this reason —
do not "fix" that by re-adding them. The required setting (Scaling mode
`Stretched, Keep Aspect Ratio`, on the **General** tab) is documented in the pod-owner release
notes instead.
### 📐 Engine is 4:3 ONLY (relevant to every display decision)
`ImageHlp.cpp:~464` asserts the complete supported resolution set: **640x480, 512x384, 800x600,
960x720, 1024x768, 1280x1024 (5:4), 1600x1200**. There is no 16:9 mode and **no aspect
correction anywhere** in the codebase (the only `aspect` hits are texture-dimension caps and
CameraShip). On a 16:9 monitor a 4:3 image must be adapted by the scaler: plain stretch =
distorted (circles → ovals, reticle wrong); keep-aspect = correct geometry + pillarbox bars.
Native widescreen would require re-authoring every 2D/shell layout (all fixed 640x480 pixel
coordinates) — a content project, not a code tweak.
### 📐 Assessment: moving to borderless windowed (discussed, NOT started)
The only native path to multi-monitor, since it removes exclusive mode from the picture entirely.
Smaller than it sounds because the mechanisms already exist:
- The windowed present is already `wBlt(FrontBufferSurface, &Window, BackBufferSurface, …)`
a blit to an arbitrary screen rect. `Blt` stretches when the dest rect differs in size, so
aspect-preserving borderless output is **rect maths, not new machinery**.
- Clipper wrappers (`wCreateClipper`/`wSetClipper`/`wSetHWnd`) already exist in `DirectDraw.cpp`,
unused by the fullscreen path.
- **None of the drawing code changes** — panels already render into `pDDSTarget` and composite;
`DrawQuad`/`DrawTexture`/fonts/`SwapRightState` don't care whether the present is Flip or Blt.
- Work is confined to `render.cpp`, `DXRasterizer.cpp`, `WinMain.cpp` (+ a little `Windows.cpp`):
panel init → `DDSCL_NORMAL` + clipper + offscreen render surface (~150 lines); ~4 panel `Flip`
sites → `Blt`; break the `HSH_EnterFullScreen2()``EnterFullScreenMode()` coupling
(`DXRasterizer.cpp:~1132` is its ONLY call site, which is also why windowed mode currently
creates no panels at all); **new: explicit frame pacing** (windowed Blt has no vsync — this is
the same root cause as the known mechlab fast-spin bug).
- **Gains:** no dgVoodoo2, no shim, no 16-bit dependency, no exclusive-mode contention, aspect
under our control, and panels stay at their monitor's native mode so "this panel won't accept
640x480" becomes impossible.
- **Risk that decides it:** windowed D3D7 device creation is per-GPU (the *editor* hit
`DDERR_INVALIDOBJECT` on Win11 and needed DDrawCompat; the *game* succeeded natively on the
W4100). Version lockstep means one build must serve every pod.
- **Recommended staging:** (1) `-borderless` for the MAIN display only, default off; (2) convert
**one** panel (radar) — the small, make-or-break test of `DDSCL_NORMAL` + clipper +
D3D-on-offscreen on the target GPU; (3) all three panels; (4) retire dgVoodoo2 + shim. Keep
exclusive fullscreen switch-selectable indefinitely.
### Incidental findings
- **`-2dt` is not a recognised switch anywhere in the codebase**, despite appearing in production
`ctcl.ini` launch lines. Completely inert. (2D targets are already the default; `-3dt` is what
switches to the 3D model.)
- `NumHWDevices` (5) can exceed `NumDevices` (4) — it counts D3D device-enumeration callbacks, and
an adapter exposing both a HAL and a T&L HAL yields two. Benign; `InitSecond` hardcodes
`IID_IDirect3DHALDevice`.
- New switches are documented in `-help` (`-fps` under LOGGING AND DIAGNOSTICS, `-tcoop` under
DISPLAY AND VIDEO).
## STEP 11: Cameraship Map/Armor screen shows background but no overlays — SOLVED
## (2026-07-26): it is the CTCL role, NOT the video card
Reported as *"the secondary armor/score/map screen in cameraship mode shows the background BMP
fine but none of the overlay graphics, with certain video cards but not others"*. **The video
card is a red herring. The machine was not being told it was a cameraship.**
### Root cause
`-ctcltype <n>` maps **straight** to the role enum — no remapping
(`MW4Application.cpp:~1534`, `g_nCTCL = token[0]-'0'`; `ctcl_params.h`):
`1 = console`, **`2 = game pod`**, **`3 = cameraship`**, `4 = none`.
The failing launch line used `-ctcltype 2` (game pod). The overlay drawing in
`mw4\Code\MW4\hudchat.cpp:~596` is gated on
`bool draw_mr = CTCL_GetType()==_ECTCL_CameraShip;` (`#ifdef _DEBUG` -> unconditional `true`),
so with role 2 the overlay block simply never executes. Fixed by launching `-ctcltype 3`
(user-confirmed working).
### ⚠️ Why this is so hard to spot — the two gates are INDEPENDENT
**The Map/Armor screen opens and paints its background regardless of the CTCL role.**
`HSH_EnterFullScreen2` / `CMR_Device` decide purely on *"is a spare secondary monitor
available?"* and never consult `CTCL_GetType()`; only `hudchat.cpp` checks the role. So a wrong
`-ctcltype` produces a screen that lights up, shows the correct artwork, survives mode changes
and renders nothing — **visually identical to a graphics-card/DirectDraw fault**, which is
exactly why it got mis-attributed to specific GPUs. Diagnose it from `gos-displays.txt`
(`CTCL type = ...`), never by eye.
- Corollary: `mr_device` also opens in **non-cameraship** configs. Observed here with
`-tmfds 1` when `g_nDualHead = -1` (no spanned pair found): MFD mode 1 fell through to the
single-secondary `mr_device` path on a *game pod*, giving the same background-only screen.
- **Field triage:** on a failing machine read that pod's `ctcl.ini` launch line and its
`gos-displays.txt`. `CTCL type = 2` means it is misconfigured, not broken.
### Diagnostics added (kept)
- **`CTCL type = <n>` line in `gos-displays.txt`** (`render.cpp` `HSH_EnterFullScreen2`, via the
already-present `g_pfnCTCL_GetType` hook — no game-code dependency). Always logged, states in
words whether overlays will be drawn. One look settles the question.
- **`-tmr <0-3>`** Map/Armor diagnostic ladder (`g_nMRDiag`, `CMR_Device::BeginScene`):
0 = normal, 1 = background blit with `DDBLTFAST_WAIT|DDBLTFAST_NOCOLORKEY`,
2 = `Clear` instead of the background blit, 3 = additionally alpha-blend off + untextured
magenta `DrawQuad`. Results that cracked this: **3 drew magenta** (device/3D/flip all fine),
**2 was pure black** (the background blit was never erasing the overlays), 1 unchanged.
- **Draw-call counter**`CHSH_Device::m_nDrawCalls`, bumped in `DrawQuad` / `DrawThickFrame` /
`DrawTexture`, reset in `CMR_Device::BeginScene`, reported for the first 5 frames from
`CMR_Device::EndScene` when `g_nMRDiag` is set. **This was the decisive instrument:**
`0 draw call(s)` proves the overlay code never ran (a gating problem), whereas non-zero with a
blank screen would mean it ran and drew invisibly (texture/alpha). Keep it — it separates
"not running" from "not visible" in one run.
- Also logged: `CMR_Device` surface creation (`pDDSBackground`/`pDDSTexture`/`pDDSMapTexture`)
plus the overlay texture's size/bpp and channel masks. Healthy reference reading on the W4100:
`256x256 16bpp a=0000F000 r=00000F00 g=000000F0 b=0000000F` (normal 4-bit alpha, not zero).
### Method note
Every hypothesis reasoned forward from the code was wrong again (blit erasing overlays; alpha
channel; per-GPU rendering). The `-tmr` ladder plus the draw-call counter settled it in two runs.
Consistent with the rest of STEP 10: **instrument, don't infer.**
## STEP 12: Intel iGPU + USB display MFD failure — was `-tmon` misread + a real
## panel re-entry leak (2026-08-05)
Reported as *"MFD modes work on a W4100 and a Quadro, and with the radar on a USB adapter, but
fail on a machine with CPU-integrated Intel graphics"* — 3 displays (main + 2 MFDs) on an
Intel HD 630, radar on a Trigger 6 USB adapter, Win10 + dgVoodoo2. Second MFD panel returned
`DDERR_EXCLUSIVEMODEALREADYSET`. **Neither the Intel iGPU nor the mixed-adapter setup was at
fault.** Two independent causes, both now fixed/instrumented.
### Cause 1 — Windows Display Settings numbers are NOT the DirectDraw device order
The operator set `-tmon 3,4,2,1` by reading the numbers off the Windows Display Settings
arrangement diagram. Those numbers are an **undocumented UI ordinal that no API exposes** and
that need not match `\\.\DISPLAYn` *or* the DirectDraw index. On this box all three differed:
| Settings | `\\.\DISPLAYn` | DDraw device | desktop X | role wanted |
|---|---|---|---|---|
| 3 (primary, 800x600) | DISPLAY1 | 0 | 0 | main |
| 4 | DISPLAY4 | 3 | 800 | radar |
| 2 | DISPLAY2 | 1 | 1440 | mfd1 |
| 1 | DISPLAY3 | 2 | 2080 | mfd2 |
A permutation with two accidental fixed points — no derivable rule, so it cannot be corrected
in code. `-tmon 1,4,2,3` (correct DirectDraw indices) worked first try, user-confirmed.
**This is why Windows itself ships an *Identify* button rather than publishing the mapping.**
### Cause 2 — panels were re-opened without being released (REAL BUG, all machines)
`EnterFullScreenMode()` (`DXRasterizer.cpp:1132`) calls `HSH_EnterFullScreen2()` on **every**
mode change and every lost-front-buffer recovery, but the teardown `HSH_DirectDrawRelease2()`
was only wired to `DirectDrawRelease()` — i.e. full shutdown. So `CHSH_Device::InitFirst()`
overwrote `pDD` with a fresh `IDirectDraw7` while the previous one still held exclusive
fullscreen, **leaking it and its exclusive claim for the life of the process**.
- The log shows it plainly: `HSH_EnterFullScreen2` runs **twice**; the first entry initialises
all panels `DD_OK`, the second fails on the same device.
- Only bites when a panel sits on the **Windows primary** — secondary outputs grant exclusive
mode again, the primary does not. Every working pod happens to have the *main display* on the
primary, so no panel is ever there. That is the whole reason it looked hardware-specific.
- **FIX:** `render.cpp` `HSH_EnterFullScreen2()` now calls `HSH_DirectDrawRelease2()` first when
`hsh_initialized || hsh_mrdev_initialized`. Tagged `[panelreinit]`.
### ✅ `gos-displays.txt` rewritten as a full startup trace (`VideoCard.cpp`)
Was: final device list + role numbers. Now: every stage in the order the game does it —
command line (with `-tmon` shown already converted 1-based→0-based), **Windows desktop topology**
(`EnumDisplayDevicesA`/`EnumDisplaySettingsA`), each enumeration callback with its GUID and
`HMONITOR` resolved to `\\.\DISPLAYn` + rect + primary flag, per-device ACCEPTED/REJECTED, the
device table **before and after** the NULL-device merge with `DeviceGUID` **and**
`guidDeviceIdentifier` and an **adapter grouping**, every index shift in the merge, every
role-selection decision *with the reason each device was skipped*, each `-tmon` override
(applied/rejected + what auto-detect had chosen), a **desktop left-to-right order** block, and a
**consistency check that names two roles landing on one device as a CLASH**.
- **Fixed a real desync while doing it:** the merge shifted `DeviceArray` but nothing tracked
which monitor each slot drove → `g_ahDevMonitor[]` is now shifted in lockstep. `HMONITOR` was
previously discarded entirely (`videoDevices` gained `hMonitor`; `BufferDevice` takes it).
- Startup-only, ~255 open-append-close writes (~25130 ms), nothing on the frame path. Kept
unbuffered deliberately — this is the crash-survivable log (`gos-fps.txt` is the buffered one).
- APIs resolved via `GetProcAddress` with locally-declared `MONITORINFOEXA`/`DISPLAY_DEVICEA`
layouts, so nothing depends on the 1998 SDK having multimon headers.
### ✅ NEW: `-tident` — the game's own Identify
`MW4.exe -tident [3..120, default 20]` fills **every** display with a distinct colour and prints,
huge, the number to type into `-tmon`, plus its DirectDraw device index and the role currently
assigned to it. Then exits. This is the only reliable way to map monitors, precisely because the
Settings ordinal is unreadable.
- **Uses `DDSCL_NORMAL` + GDI on the primary surface — no exclusive mode, no mode change.**
Taking exclusive fullscreen on several devices at once is the very failure being diagnosed; a
diagnostic that trips over it is worthless. It paints onto each monitor's existing desktop, so
it works even on a pod where the MFD modes are broken.
- Opens the **real DirectDraw devices** rather than positioning GDI windows by `HMONITOR` — that
proves the device-index→physical-output association through the same path the panels use.
(Positioning by DirectDraw's own reported `HMONITOR` would be circular.)
- Implemented in `VideoCard.cpp` `IdentifyDisplays()`, called at the end of `FindVideoCards()`
then `ExitProcess(0)`. Everything it does is also written to `gos-displays.txt`.
### Docs
`-help` for `-tmon` now states outright that these are **not** Windows Display Settings numbers
and points at `-tident`/`gos-displays.txt`; the old `-tmon 1,2,3,4` example was actively inviting
the mistake. Release notes gained a `-tident` section, the clash/re-entry fixes, and an upgrade
checklist step.
### Method note
Same lesson as STEPs 10 and 11, and it caught me out again: my forward-reasoned hypothesis
(duplicate `-tmon` assignment) was **wrong**, and the new logging disproved it in one run — the
consistency check explicitly printed *"No duplicate device assignments"*. The double
`HSH_EnterFullScreen2` was only visible because the log covers the whole start-up rather than a
summary. **Instrument, don't infer.**
## ✅ STEP 13: External MFD/Radar damage mappings installed + art audited (2026-08-07)
The coordinate and art workflow for the external MFD and Radar damage paper dolls is now fully
documented in **`MFD-RADAR-MAPPINGS.md`**. Start there; do not rediscover the image transforms,
tuple meanings, runtime offsets, legacy J&J formats, or BMP comparison rules from scratch.
### Controlling source and semantics
- `Gameleap\code\CoreTech\Libraries\GameOS\coord.cpp` owns four positional 65x11 arrays:
`texuv2`/`offset2` = external MFD source rectangles/exploded origins;
`texuv3`/`offset3` = Radar source rectangles/exploded origins. Numeric Mech ID (array row) is
authoritative; comments are labels. Zone order is `LL,RL,LA,RA,RT,LT,CT,CTR,HD,S1,S2`.
- These are NOT the normal cockpit HUD's `texuv`/`offset` arrays in `huddamage.cpp`, and NOT the
small atlas images under `hsh\MFD`.
- Runtime loose art is `Gameleap\mw4\hsh\hud\<stem>.bmp` for MFD and
`Gameleap\mw4\hsh\radar\hud\<stem>.bmp` for Radar. Coordinate changes require an MW4.exe
rebuild because `DXRasterizer.cpp` includes `coord.cpp`; loose art changes do not require a
`.mw4` resource repack.
### Installed mappings and reproducible audit (`97558039`)
- Added `Finished HUDS from J&J/generate_comparison_maps.py` (Pillow) plus 38 LFS-tracked review
PNGs and `COMPARISON-SUMMARY.md`. Red = current `coord.cpp`; green = supplied J&J geometry.
- Installed every complete supplied set: **19 displays across 13 chassis** (12 MFD + 7 Radar).
Final generator result: **19 exact, 0 different, 0 warnings, 7 missing opposite-display
inputs**. Missing inputs are Argus/Fafnir/Flea/Gladiator/Kodiak/Longbow Radar and Hellspawn MFD;
they were not guessed or changed.
- J&J data has two formats. Modern = 4-value unexploded source rectangle + 2-value exploded
origin. Legacy = 2-value source upper-left + 4-value exploded bounding box; derive source width/
height from that exploded box. Combined files use section headings. All-zero tuples may appear
at either width and mean "absent zone."
- Normalized four unambiguous input typos without changing reviewed geometry: Behemoth MFD LT
`1742→174`, Behemoth Radar CT `202.242→202,242`, blank Behemoth Radar S2 `→0,0`, and Fafnir MFD
LL `162.326→162,326`.
- Preserve validated odd coordinates rather than rounding them. Radar divides full-size authored
values by two using integer arithmetic, so rounding changes final placement.
### Behemoth II inheritance (`deafc2b0`)
- Behemoth II is Mech ID 12 and intentionally shares Behemoth ID 11's art and geometry. All four
ID 12 rows now exactly inherit ID 11. The generator asserts this relationship and fails on any
future divergence. There is **no Annihilator II** in the 65-ID roster; do not invent a row or
runtime stem for it.
### Runtime-art result: no copies needed
- Decoded every supplied exploded 512x512 BMP and compared pixels against its canonical `hsh`
destination. All 19 supplied display assets were already pixel-identical. File hashes differed
because BMP mode/palette/header encoding differed (`P`/`L`/`RGB`), which is not an art change;
no `hsh` file was replaced.
- Separately compared `hud\behemothii.bmp` and `radar\hud\behemothii.bmp` to Behemoth's runtime
files; both existing Behemoth II files were already pixel-identical 512x512 grayscale images.
- Compare decoded pixels before copying. The exploded BMP is the runtime asset; unexploded BMPs
and full-color images are authoring references. Resolve canonical stems rather than trusting
supplied names (`assian2_*` is a typo; runtime is `assassin2.bmp`; preserve existing `Fafnir.bmp`
case on Linux).
### Validation and remaining work
- Run: `python3 "Finished HUDS from J&J/generate_comparison_maps.py"`. Expected current output:
`Generated 38 maps for 19 display sets: 19 exact, 0 different` and
`Warnings: 0; missing display sets: 7`, followed by the Behemoth II inheritance assertion in
`COMPARISON-SUMMARY.md`.
- `coord.cpp` must remain CRLF-only and retain four 65-row arrays with 11 zones per row.
- **Still required:** rebuild Release/Profile on the Windows VC6 machine, deploy MW4.exe, and test
intact/damaged states on physical MFD and Radar displays. Linux validation proves source/data
equality and image alignment, not runtime hardware behavior.
## ✅ STEP 14: High Explosive MechLab firing repaired (2026-08-08)
High Explosives (weapon ID 87) could be installed in MechLab but never fired. They are ordinary
one-shot weapons activated through their assigned weapon group (group 3 by default), **not** by
ejecting. Put multiple charges in the same group and use Group Fire to detonate all of them in one
trigger cycle; Chain Fire activates only one at a time. Each fired charge independently creates a
100-damage blast in a 30 m radius and applies 200 center-torso damage to its own mech.
Three legacy defects made the feature unusable:
- `HighExplosive.data` used the unregistered key `MaxAmmoCount=1`; the weapon model registers
`MaxAmmo`, so the charge had no valid conventional ammo capacity.
- MechLab computes initial ammo as `MaxAmmo / (3 * TotalSlotsTaken)`. Even with the corrected key,
`1 / (3 * 2)` truncated to zero. `Weapon.cpp` now clamps starting ammo and ammo-per-pack to one
only when the weapon declares a positive maximum; zero-capacity weapons remain unchanged.
- `MechBay/weapons.script` applied High Explosive UI handling to stale ID 84 (NARC) instead of 87.
Side effect: NARC Beacon (`MaxAmmo=18`, 1 slot → 6 rounds) was wrongly inheriting the
`highexplosive` flag, which suppresses the ammo-round count, so NARC now displays its rounds
in MechLab again. Intended; the flag exists only to hide the count on a one-shot charge.
The two edited content files live in **different packages**`HighExplosive.data` is packed by
`core.build`**`core.mw4`** (`props.build` contains no `WeaponSubsystems` entries at all), and
`weapons.script` by `props.build`**`props.mw4`**. `build-resources.ps1` repacks any package with
newer sources, so both are picked up automatically; the distinction only matters if a package is
ever repacked selectively.
Residual gap (not hit today): the MechLab path fixed here is `Weapon::CreateStream`. Authored
content takes a different path — `Weapon_Tool.cpp:66` defaults `ammoCount` to `-1` when the
instance page omits `AmmoCount`. No mech `.subsystems` currently mounts a High Explosive, but
adding one to a stock loadout requires an explicit `AmmoCount=1` or it will be unfireable for the
same reason. (`Content\ShellScriptsDev\MechBay\weapons.script` still has the old `84`; no `.build`
packages that tree, so it never ships.)
Linux validation confirmed High Explosive now constructs with one round, zero-capacity weapons
still produce zero ammo packs, a sweep of every weapon subsystem found High Explosive to be the
**only** one whose starting-ammo formula truncated to zero (so no other weapon's ammo changed),
edited files retain their original CRLF/legacy encodings, and the focused diagnostics/diff checks
are clean. Still required: rebuild `MW4.exe` on the Windows VC6 machine, repack resources, deploy,
then remove/reinstall the charge in existing saved variants that may retain serialized zero ammo.
## ✅ STEP 15: `options.ini` silently ignored -- Windows file virtualization (2026-08-14)
Reported after the RC3 test night as *"options.ini isn't being read when the game is in
`c:\mw4510b3`, but works in `c:\mw4`"*, along with mw4print misbehaving in the same folder.
**Neither the folder name nor any hardcoded path was involved.**
### The path logic is exe-relative and was never the problem
- `options.ini` is opened by bare relative name (`MW4Shell.cpp:818`,
`NotationFile opts("options.ini", ...)`), and GameOS sets the working directory to the running
exe's own folder before the shell starts: `ImageHlp.cpp:773` `strcpy(AssetsDirectory2, _pgmptr)`
-> chop the exe name -> `:797 SetCurrentDirectory(AssetsDirectory2)`, inside
`InitExceptionHandler`, called from `WinMain.cpp:436`. No install path is compiled in anywhere.
- **The decisive deduction:** the game RAN from the new folder. `Resource\*.mw4` resolve through
the same relative mechanism, so the working directory was demonstrably correct and the file WAS
being found. That reframed the search away from paths entirely.
- `c:\ctcl.ini` IS a hardcoded path, and `RunExec` (`ctcl.cpp:398`) derives a working directory
from the exe token of its command line and passes it to `CreateProcess` -- but only when the
arcade console launches the game. Irrelevant when the exe is started by hand.
### ROOT CAUSE: UAC file virtualization (VirtualStore)
Windows virtualizes a process when ALL of: 32-bit, **no UAC manifest**, non-elevated, and writing
somewhere the user cannot. Verified against the shipped binaries -- neither `MW4.exe` nor
`mw4print.exe` carried any `requestedExecutionLevel` (VC6 predates manifests). Writes to
`options.ini` were redirected to `%LOCALAPPDATA%\VirtualStore\<folder>`, **and once that shadow
copy exists reads are served from it too** -- so the game read a stale file while the owner edited
the real one. Nothing reports an error at any point.
- Explains every symptom at once: settings ignored regardless of where in the file they were put,
the game never rewriting `options.ini` (it normally does, from ~12 call sites), and mw4print
failing in the same folder (it writes `mw4print.ini` / `banner.txt` beside itself).
- Why `c:\mw4` worked: Windows 10's default ACL on `C:\` gives `Users` only Read & Execute but
grants CREATOR OWNER full control over new subfolders. A folder you create yourself is writable;
one created by an elevated process (installer, admin unzip, elevated copy) is not.
- Field check: `dir "%LOCALAPPDATA%\VirtualStore\<game folder>"`. Repair with
`icacls "<folder>" /grant *S-1-5-32-545:(OI)(CI)M /T` and THEN delete the shadow copy -- in that
order, or the stale copy keeps winning.
### Fixes (RC4)
- **Manifests added** to `MW4Application` (MW4.exe / MW4pro.exe), `mw4print`, `Launcher` and
`autoconfig`: a `<project>.manifest` plus `1 24 "<project>.manifest"` in the matching `.rc`.
Deliberately minimal -- `requestedExecutionLevel asInvoker` and nothing else. NO `supportedOS`
(would change OS version reporting and compatibility shimming), NO `dpiAware` (the
`DWM8And16BitMitigation` layer owns that), NO comctl32 v6 dependency (would restyle the UI and
is the classic XP side-by-side breakage).
- **`MW4_WarnIfFolderNotWritable()`** (`MW4Application.cpp`, called from `GetGameOSEnvironment`)
probes with a delete-on-close temp file and, on failure, names the folder and prints the
`icacls` fix. Placed in `GetGameOSEnvironment` deliberately: it runs BEFORE
`EnterFullScreenMode` (`WinMain.cpp:545` vs `:552`), so it cannot reproduce the STEP 7
modal-dialog-over-DirectDraw hang. Without it the manifest would merely convert silent success
into silent failure.
- **Windows XP is unaffected.** Virtualization is Vista+, and XP ignores the `asm.v3` `trustInfo`
element (this was Microsoft's documented single-binary approach). The residual risk is VC6's
`rc.exe` emitting a malformed manifest resource, which XP's stricter side-by-side loader would
reject at launch -- hence the explicit XP items in checklist section 23.
### Also found while looking
- **The notation reader strips `//` and `/* */` at parse time** (`NotationFile.cpp:397-455`), so
comments never reach memory and `Save()` (`:629`) writes them away permanently. The fully
commented `[RookieMission]` template in the release notes therefore disappears the first time
the game rewrites the file. Entries survive a rewrite; comments never do.
- **mw4print's "libmysql.dll not found" was three distinct failures sharing one message**, with
`GetLastError` never called (`dbexport.cpp`). `libmysql.dll` imports `VCRUNTIME140.dll` -- the
ONLY one of its 16 imports absent from a stock Windows 10 (the 11 `api-ms-win-crt-*` are the OS
UCRT). Windows returns `ERROR_MOD_NOT_FOUND` for a missing *dependency*, the same code as a
missing file, hence "not found" for a file plainly sitting there. Now reports four cases
separately and names the redistributable. **Virtualization is NOT involved here: it never
applies to `.exe` / `.dll`.** RC4 also ships `vcruntime140.dll` (14.44.35211.0, x86, from the
official MS redistributable) in `Gameleap\mw4\`, deployed as a root `*.dll`, so no runtime
install is needed on a pod.
### Method note
Two hypotheses were reasoned out and both were wrong -- a duplicate exe on `PATH` (killed when the
owner confirmed only one copy existed on that machine) and a hardcoded launch path in `ctcl.ini`
(killed when he confirmed he ran the exe by hand). What settled it was reading the binaries:
`strings` showing no `requestedExecutionLevel`, and `objdump` on the import tables. Same lesson as
STEPs 10-12: **instrument, don't infer.** Also worth recording that the owner's own observation --
*"during none of the testing did it write the file at all"* -- was the single most valuable datum,
worth more than any amount of code reading.
## Next steps (proposed) ## Next steps (proposed)
- [ ] (Parked, diagnostics-only, cannot affect a real pod) Two gaps found while testing `-tident`
on a **single-monitor VM** (2026-08-05). Both only occur when one physical monitor is exposed as
two DirectDraw devices, which happens because the NULL-device merge is gated on
`NumMonitors>=2` (`VideoCard.cpp`) — by design, it is a multi-monitor fixup, so a single-monitor
box keeps the alias in slot 0.
1. **`-tident` overpaints itself.** With device 0 = NULL alias and device 1 = the real output,
both resolve to the same screen, so it paints red/"1" then immediately green/"2" over the top.
Looks like a glitch; is actually correct. Should detect devices sharing an `HMONITOR` and
label them together (`1 & 2 - same monitor`) instead of painting sequentially.
2. **The clash check compares device INDICES, not resolved monitors.** On that VM it reported
*"No duplicate device assignments"* while `main`→device 0 (primary alias) and `radar`→device 1
(`\\.\DISPLAY49`) were the **same physical monitor** — i.e. exactly the collision the check
exists to predict, reported as all-clear. Fix: treat a device with no `HMONITOR` as the
Windows primary, then compare resolved `HMONITOR`s rather than indices.
Neither can occur on a multi-monitor pod. Worth doing only if bench/VM testing starts relying on
the check being trustworthy.
- [ ] (Researched, NOT started) Make `-window` work for **all** modes — full findings in
**`WINDOWED-MODE.md`** (repo root, 2026-08-05). Headline: `-window` does not fail for MFD/
cameraship modes, it **silently disables** them, because `use_shgui` is derived from
`Environment.fullScreen` (`MW4Application.cpp:1373`). Beyond that one line the work is a windowed
variant of `CHSH_Device::InitFirst`/`InitSecond` (DDSCL_NORMAL + clipper + offscreen backbuffer,
no SetDisplayMode), one HWND per panel, and 8 `Flip``Blt` sites in `WinMain.cpp`. The deciding
risk is whether **four** windowed D3D7 devices can coexist in one process — the editor failed
this on Win11 and needed DDrawCompat (STEP 8), while the game's own windowed main display
succeeds on the W4100. Overlaps heavily with the borderless-windowed assessment below.
- [ ] (Decision pending) Borderless-windowed migration — see the staged assessment in STEP 10.
Step 2 (one panel) is the cheap, decisive experiment. Note this is the only route that would
remove the dgVoodoo2 prerequisite **without** breaking the XP pods, since it needs no external
DLL on either OS.
- [ ] Test a **full mission windowed** (only the console/shell has been verified windowed).
- [x] ~~Windowed 3D viewport on Win11~~ — DONE via DDrawCompat (see STEP 8 viewport section). - [x] ~~Windowed 3D viewport on Win11~~ — DONE via DDrawCompat (see STEP 8 viewport section).
- [ ] (Optional) Curate remaining WIP content as features are exercised (editor loads dev `Content\`). - [ ] (Optional) Curate remaining WIP content as features are exercised (editor loads dev `Content\`).
- [ ] (Optional) Build Debug/Armor configs (need `dbg.bin`/`arm.bin` output dirs). - [ ] (Optional) Build Debug/Armor configs (need `dbg.bin`/`arm.bin` output dirs).
@@ -3,7 +3,7 @@ RL 184,170,262,302
LA 12,106,126,174 LA 12,106,126,174
RA 212,106,326,174 RA 212,106,326,174
RT 176,86,226,174 RT 176,86,226,174
LT 110,82,160,1742 LT 110,82,160,174
CT 140,96,198,224 CT 140,96,198,224
CTR 0,0,0,0 CTR 0,0,0,0
HD 164,122,174,134 HD 164,122,174,134
@@ -4,8 +4,8 @@ LA 8,26
RA 302,26 RA 302,26
RT 294,130 RT 294,130
LT 108,130 LT 108,130
CT 202.242 CT 202,242
CTR 0,0 CTR 0,0
HD 230,174 HD 230,174
S1 202,28 S1 202,28
S2 S2 0,0
@@ -0,0 +1,86 @@
# J&J Coordinate Comparison Summary
Generated by `generate_comparison_maps.py`. Red is the current `coord.cpp` mapping; green is the provided J&J mapping.
`EXACT` means all eleven source rectangles and all eleven exploded offsets match numerically. `DIFFERENT` means at least one value differs; inspect both generated images.
| Mech | Display | Status | Source rectangles different | Exploded offsets different |
|---|---:|---:|---:|---:|
| Annihilator | MFD | **EXACT** | 0/11 | 0/11 |
| Annihilator | RADAR | **EXACT** | 0/11 | 0/11 |
| Argus | MFD | **EXACT** | 0/11 | 0/11 |
| AssassinII | MFD | **EXACT** | 0/11 | 0/11 |
| AssassinII | RADAR | **EXACT** | 0/11 | 0/11 |
| Avatar | MFD | **EXACT** | 0/11 | 0/11 |
| Avatar | RADAR | **EXACT** | 0/11 | 0/11 |
| Behemoth | MFD | **EXACT** | 0/11 | 0/11 |
| Behemoth | RADAR | **EXACT** | 0/11 | 0/11 |
| BlackHawk | MFD | **EXACT** | 0/11 | 0/11 |
| BlackHawk | RADAR | **EXACT** | 0/11 | 0/11 |
| Fafnir | MFD | **EXACT** | 0/11 | 0/11 |
| Flea | MFD | **EXACT** | 0/11 | 0/11 |
| Gladiator | MFD | **EXACT** | 0/11 | 0/11 |
| hellspawn | RADAR | **EXACT** | 0/11 | 0/11 |
| Kodiak | MFD | **EXACT** | 0/11 | 0/11 |
| Longbow | MFD | **EXACT** | 0/11 | 0/11 |
| Warhammer | MFD | **EXACT** | 0/11 | 0/11 |
| Warhammer | RADAR | **EXACT** | 0/11 | 0/11 |
## Inherited mappings
- `Behemoth II` (Mech ID 12) inherits all four coordinate rows from `Behemoth` (Mech ID 11); generator assertion passed
## Missing comparison inputs
- `Argus` RADAR: no complete provided mapping/image set configured
- `Fafnir` RADAR: no complete provided mapping/image set configured
- `Flea` RADAR: no complete provided mapping/image set configured
- `Gladiator` RADAR: no complete provided mapping/image set configured
- `Kodiak` RADAR: no complete provided mapping/image set configured
- `Longbow` RADAR: no complete provided mapping/image set configured
- `hellspawn` MFD: no complete provided mapping/image set configured
## Input warnings
- None
## Generated files
- `Annihilator/MFD/annihilator_mfd_unexploded_coords_comparison.png`
- `Annihilator/MFD/annihilator_mfd_exploded_coords_comparison.png`
- `Annihilator/Radar/annihilator_radar_unexploded_coords_comparison.png`
- `Annihilator/Radar/annihilator_radar_exploded_coords_comparison.png`
- `Argus/MFD/argus_mfd_unexploded_coords_comparison.png`
- `Argus/MFD/argus_mfd_exploded_coords_comparison.png`
- `AssassinII/HUD/assian2_mfd_unexploded_coords_comparison.png`
- `AssassinII/HUD/assian2_mfd_exploded_coords_comparison.png`
- `AssassinII/Radar/assian2_radar_unexploded_coords_comparison.png`
- `AssassinII/Radar/assian2_radar_exploded_coords_comparison.png`
- `Avatar/MFD/avatar_mfd_unexploded_coords_comparison.png`
- `Avatar/MFD/avatar_mfd_exploded_coords_comparison.png`
- `Avatar/Radar/avatar_radar_unexploded_coords_comparison.png`
- `Avatar/Radar/avatar_radar_exploded_coords_comparison.png`
- `Behemoth/HUD/behemoth_mfd_unexploded_coords_comparison.png`
- `Behemoth/HUD/behemoth_mfd_exploded_coords_comparison.png`
- `Behemoth/Radar/behemoth_radar_unexploded_coords_comparison.png`
- `Behemoth/Radar/behemoth_radar_exploded_coords_comparison.png`
- `BlackHawk/MFD/blackhawk_mfd_unexploded_coords_comparison.png`
- `BlackHawk/MFD/blackhawk_mfd_exploded_coords_comparison.png`
- `BlackHawk/Radar/blackhawk_radar_unexploded_coords_comparison.png`
- `BlackHawk/Radar/blackhawk_radar_exploded_coords_comparison.png`
- `Fafnir/HUD/fafnir_mfd_unexploded_coords_comparison.png`
- `Fafnir/HUD/fafnir_mfd_exploded_coords_comparison.png`
- `Flea/MFD/flea_mfd_unexploded_coords_comparison.png`
- `Flea/MFD/flea_mfd_exploded_coords_comparison.png`
- `Gladiator/MFD/gladiator_mfd_unexploded_coords_comparison.png`
- `Gladiator/MFD/gladiator_mfd_exploded_coords_comparison.png`
- `hellspawn/radar/hellspawn_radar_unexploded_coords_comparison.png`
- `hellspawn/radar/hellspawn_radar_exploded_coords_comparison.png`
- `Kodiak/MFD/kodiak_mfd_unexploded_coords_comparison.png`
- `Kodiak/MFD/kodiak_mfd_exploded_coords_comparison.png`
- `Longbow/MFD/longbow_mfd_unexploded_coords_comparison.png`
- `Longbow/MFD/longbow_mfd_exploded_coords_comparison.png`
- `Warhammer/MFD/warhammer_mfd_unexploded_coords_comparison.png`
- `Warhammer/MFD/warhammer_mfd_exploded_coords_comparison.png`
- `Warhammer/Radar/warhammer_radar_unexploded_coords_comparison.png`
- `Warhammer/Radar/warhammer_radar_exploded_coords_comparison.png`
@@ -1,4 +1,4 @@
LL 86,142,162.326 LL 86,142,162,326
RL 178,142,254,326 RL 178,142,254,326
LA 24,22,68,134 LA 24,22,68,134
RA 272,22,316,134 RA 272,22,316,134
@@ -0,0 +1,339 @@
#!/usr/bin/env python3
import re
from dataclasses import dataclass
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
ROOT = Path(__file__).resolve().parent
REPO = ROOT.parent
COORD = REPO / "Gameleap/code/CoreTech/Libraries/GameOS/coord.cpp"
ZONES = ("LL", "RL", "LA", "RA", "RT", "LT", "CT", "CTR", "HD", "S1", "S2")
COLORS = {"current": "#ff3030", "provided": "#20e060"}
@dataclass(frozen=True)
class Display:
folder: str
mech_id: int
kind: str
output_dir: str
slug: str
unexploded_text: str
exploded_text: str
unexploded_image: str | None
exploded_image: str
source_image: str | None = None
runtime_stem: str | None = None
DISPLAYS = (
Display("Annihilator", 0, "mfd", "MFD", "annihilator", "MFD/Annihilator Unexploded.txt", "MFD/Annihilator Exploded.txt", "MFD/annihilator_unexploded.bmp", "MFD/annihilator_Exploded.bmp"),
Display("Annihilator", 0, "radar", "Radar", "annihilator", "Radar/Annihilator Unexploded.txt", "Radar/Annihilator Exploded.txt", "Radar/annihilator_unexploded.bmp", "Radar/annihilator_exploded.bmp"),
Display("Argus", 4, "mfd", "MFD", "argus", "MFD/Argus Cords.txt", "MFD/Argus Cords.txt", "MFD/argus_unexpanded.bmp", "MFD/argus_explode.bmp"),
Display("AssassinII", 5, "mfd", "HUD", "assian2", "HUD/assassinII_HUD_unexploded.txt", "HUD/assassinII_hud_exploded.txt", None, "HUD/assian2_hud_exploded.bmp", "assian2_color.png", "assassin2"),
Display("AssassinII", 5, "radar", "Radar", "assian2", "Radar/assassinII_radar_unexploded.txt", "Radar/assassinII_radar_exploded.txt", "Radar/assian2_radar_unexploded.bmp", "Radar/assian2_radar_exploded.bmp", None, "assassin2"),
Display("Avatar", 7, "mfd", "MFD", "avatar", "MFD/Avatar Coords.txt", "MFD/Avatar Coords.txt", "MFD/avatar_unexploded.bmp", "MFD/avatar_exploded.bmp"),
Display("Avatar", 7, "radar", "Radar", "avatar", "Radar/Avatar Coords.txt", "Radar/Avatar Coords.txt", "Radar/avatar_unexploded.bmp", "Radar/avatar_exploded.bmp"),
Display("Behemoth", 11, "mfd", "HUD", "behemoth", "HUD/Behemoth_HUD_unexploded.txt", "HUD/Behemoth_HUD_exploded.txt", "HUD/Behemoth_HUD_unexploded.bmp", "HUD/Behemoth_HUD_exploded.bmp"),
Display("Behemoth", 11, "radar", "Radar", "behemoth", "Radar/Behemoth_unexploded.txt", "Radar/Behemoth_exploded.txt", "Radar/Behemoth_unexploded_radar.bmp", "Radar/Behemoth_radar_exploded.bmp"),
Display("BlackHawk", 13, "mfd", "MFD", "blackhawk", "MFD/Black Hawk.txt", "MFD/Black Hawk.txt", "MFD/blackhawk_unexploded.bmp", "MFD/blackhawk_exploded.bmp"),
Display("BlackHawk", 13, "radar", "Radar", "blackhawk", "Radar/Black Hawk.txt", "Radar/Black Hawk.txt", "Radar/blackhawk_unexploded.bmp", "Radar/blackhawk_exploded.bmp"),
Display("Fafnir", 27, "mfd", "HUD", "fafnir", "HUD/fafnir_unexploded.txt", "HUD/fafnir_exploded.txt", "HUD/fafnir_unexploded.bmp", "HUD/fafnir_exploded.bmp"),
Display("Flea", 28, "mfd", "MFD", "flea", "MFD/Flea unexploded.txt", "MFD/Flea exploded.txt", "MFD/flea_unexploded copy.bmp", "MFD/flea_exploded copy.bmp"),
Display("Gladiator", 29, "mfd", "MFD", "gladiator", "MFD/Gladiator unexploded.txt", "MFD/Gladiator Exploded.txt", "MFD/gladiator_unexploded copy.bmp", "MFD/gladiator_exploded copy.bmp"),
Display("hellspawn", 33, "radar", "radar", "hellspawn", "radar/hellspawn_radar_unexploded.txt", "radar/hellspawn_radar_exploded.txt", "radar/hellspawn_radar_unexploded.bmp", "radar/hellspawn_radar_exploded.bmp"),
Display("Kodiak", 37, "mfd", "MFD", "kodiak", "MFD/Kodiak Cords.txt", "MFD/Kodiak Cords.txt", "MFD/kodiak_unexpanded.bmp", "MFD/kodiak_Explode.bmp"),
Display("Longbow", 39, "mfd", "MFD", "longbow", "MFD/Lomgbow unexploded.txt", "MFD/Longbow exploded.txt", "MFD/longbow_unexploded copy.bmp", "MFD/longbow_exploded copy.bmp"),
Display("Warhammer", 62, "mfd", "MFD", "warhammer", "MFD/Warhammer unexploded.txt", "MFD/Warhammer exploded.txt", "MFD/warhammer_unexploded copy.bmp", "MFD/warhammer_exploded.bmp"),
Display("Warhammer", 62, "radar", "Radar", "warhammer", "Radar/Warhammer unexploded.txt", "Radar/Warhammer exploded.txt", "Radar/warhammer_unexploded copy.bmp", "Radar/warhammer_exploded.bmp"),
)
INHERITED_MAPPINGS = (
("Behemoth II", 12, "Behemoth", 11),
)
def parse_coord_arrays() -> dict[str, list[list[tuple[int, ...]]]]:
text = COORD.read_bytes().decode("ascii")
arrays = {}
for name in ("texuv2", "offset2", "texuv3", "offset3"):
match = re.search(rf"\b{name}\[.*?\]=\{{\r?\n(.*?)\r?\n\}};", text, re.S)
if not match:
raise RuntimeError(f"Could not find {name} in {COORD}")
rows = []
for line in match.group(1).splitlines():
if not line.lstrip().startswith("{{"):
continue
values = [
tuple(map(int, group.split(",")))
for group in re.findall(r"\{\s*([0-9 ]+(?:,\s*[0-9 ]+){1,3})\s*\}", line)
]
if len(values) != len(ZONES):
raise RuntimeError(f"Expected 11 zones in {name} row: {line}")
rows.append(values)
if len(rows) != 65:
raise RuntimeError(f"Expected 65 active rows in {name}, found {len(rows)}")
arrays[name] = rows
return arrays
def parse_measurements(path: Path, wanted_section: str, warnings: list[str]) -> dict[str, tuple[int, ...]]:
rows = {}
section = None
for line_number, raw in enumerate(path.read_text(errors="replace").splitlines(), 1):
stripped = raw.strip()
lower = stripped.lower()
zone_match = re.match(r"^(LL|RL|LA|RA|RT|LT|CT|CTR|HD|S1|S2)\b(.*)$", stripped, re.I)
if not zone_match:
if "unexploded" in lower or "unexpanded" in lower:
section = "unexploded"
elif "exploded" in lower or "explode" in lower:
section = "exploded"
continue
if section and section != wanted_section:
continue
zone = zone_match.group(1).upper()
remainder = zone_match.group(2)
numbers = tuple(map(int, re.findall(r"\d+", remainder)))
if "." in remainder:
warnings.append(f"{path.relative_to(ROOT)}:{line_number}: treated '.' as a coordinate separator")
if not numbers:
warnings.append(f"{path.relative_to(ROOT)}:{line_number}: blank {zone} value treated as zero")
rows[zone] = numbers
return rows
def normalize_measurements(display: Display, warnings: list[str]) -> tuple[list[tuple[int, ...]], list[tuple[int, ...]]]:
base = ROOT / display.folder
unexploded_path = base / display.unexploded_text
exploded_path = base / display.exploded_text
unexploded = parse_measurements(unexploded_path, "unexploded", warnings)
exploded = parse_measurements(exploded_path, "exploded", warnings)
unexploded_lengths = {len(value) for value in unexploded.values() if value and any(value)}
exploded_lengths = {len(value) for value in exploded.values() if value and any(value)}
if unexploded_lengths == {4} and exploded_lengths == {2}:
mode = "modern"
elif unexploded_lengths == {2} and exploded_lengths == {4}:
mode = "legacy"
else:
raise RuntimeError(
f"Cannot determine format for {display.folder} {display.kind}: "
f"unexploded lengths={unexploded_lengths}, exploded lengths={exploded_lengths}"
)
rectangles = []
offsets = []
for zone in ZONES:
un = unexploded.get(zone, ())
ex = exploded.get(zone, ())
if not un:
un = (0, 0, 0, 0) if mode == "modern" else (0, 0)
if not ex:
ex = (0, 0) if mode == "modern" else (0, 0, 0, 0)
expected_un = 4 if mode == "modern" else 2
expected_ex = 2 if mode == "modern" else 4
if un and not any(un):
un = (0,) * expected_un
if ex and not any(ex):
ex = (0,) * expected_ex
if len(un) != expected_un or len(ex) != expected_ex:
raise RuntimeError(f"Invalid {zone} values for {display.folder} {display.kind}: {un}, {ex}")
if mode == "modern":
rectangle = un
offset = ex
else:
width = ex[2] - ex[0]
height = ex[3] - ex[1]
rectangle = (un[0], un[1], un[0] + width, un[1] + height) if any(ex) else (0, 0, 0, 0)
offset = ex[:2]
rectangles.append(rectangle)
offsets.append(offset)
return rectangles, offsets
def load_unexploded(display: Display) -> Image.Image:
base = ROOT / display.folder
if display.source_image:
source = Image.open(base / display.source_image).convert("RGB")
scaled = source.resize((320, 320), Image.Resampling.LANCZOS)
prepared = Image.new("RGB", (340, 340), "black")
prepared.paste(scaled, (10, 10))
else:
prepared = Image.open(base / display.unexploded_image).convert("RGB")
if prepared.size == (512, 512):
return prepared
if prepared.width > 512 or prepared.height > 512:
raise RuntimeError(f"Unexpected unexploded size {prepared.size} for {display.folder} {display.kind}")
canvas = Image.new("RGB", (512, 512), "black")
canvas.paste(prepared, (0, 0))
return canvas
def component_boxes(rectangles: list[tuple[int, ...]], offsets: list[tuple[int, ...]]) -> list[tuple[int, ...]]:
boxes = []
for rectangle, offset in zip(rectangles, offsets):
if not any(rectangle) or not any(offset):
boxes.append((0, 0, 0, 0))
continue
width = rectangle[2] - rectangle[0]
height = rectangle[3] - rectangle[1]
boxes.append((offset[0], offset[1], offset[0] + width, offset[1] + height))
return boxes
def draw_legend(draw: ImageDraw.ImageDraw, font: ImageFont.ImageFont) -> None:
draw.rectangle((365, 12, 505, 67), fill="black", outline="white", width=1)
draw.line((377, 30, 402, 30), fill=COLORS["current"], width=3)
draw.text((410, 22), "current", fill=COLORS["current"], font=font)
draw.line((377, 51, 402, 51), fill=COLORS["provided"], width=3)
draw.text((410, 43), "provided", fill=COLORS["provided"], font=font)
def draw_comparison(
image: Image.Image,
current: list[tuple[int, ...]],
provided: list[tuple[int, ...]],
output: Path,
mark_origins: bool,
) -> None:
draw = ImageDraw.Draw(image)
font = ImageFont.load_default(size=14)
for label, boxes in (("current", current), ("provided", provided)):
color = COLORS[label]
for zone, box in zip(ZONES, boxes):
if not any(box):
continue
draw.rectangle(box, outline=color, width=3)
if mark_origins:
x, y = box[:2]
draw.line((x - 4, y, x + 5, y), fill=color, width=2)
draw.line((x, y - 4, x, y + 5), fill=color, width=2)
draw.text((box[0] + 3, box[1] + 2), zone, fill=color, font=font, stroke_width=2, stroke_fill="black")
draw_legend(draw, font)
output.parent.mkdir(parents=True, exist_ok=True)
image.save(output)
def validate_provided(
display: Display,
rectangles: list[tuple[int, ...]],
offsets: list[tuple[int, ...]],
warnings: list[str],
) -> None:
source_limit = 340 if display.kind == "mfd" else 512
for zone, rectangle, offset in zip(ZONES, rectangles, offsets):
if any(rectangle):
if rectangle[2] <= rectangle[0] or rectangle[3] <= rectangle[1]:
warnings.append(f"{display.folder} {display.kind} {zone}: non-positive source rectangle {rectangle}")
if min(rectangle) < 0 or max(rectangle) > source_limit:
warnings.append(f"{display.folder} {display.kind} {zone}: source rectangle outside 0..{source_limit}: {rectangle}")
if any(offset) and (offset[0] < 0 or offset[1] < 0 or offset[0] > 512 or offset[1] > 512):
warnings.append(f"{display.folder} {display.kind} {zone}: offset outside 512 canvas: {offset}")
def difference_count(left: list[tuple[int, ...]], right: list[tuple[int, ...]]) -> int:
return sum(a != b for a, b in zip(left, right))
def main() -> None:
arrays = parse_coord_arrays()
results = []
all_warnings = []
generated = []
for variant_name, variant_id, base_name, base_id in INHERITED_MAPPINGS:
for array_name in ("texuv2", "offset2", "texuv3", "offset3"):
if arrays[array_name][variant_id] != arrays[array_name][base_id]:
raise RuntimeError(
f"{variant_name} (ID {variant_id}) must inherit {array_name} "
f"from {base_name} (ID {base_id})"
)
for display in DISPLAYS:
warnings = []
provided_rectangles, provided_offsets = normalize_measurements(display, warnings)
validate_provided(display, provided_rectangles, provided_offsets, warnings)
current_rectangles = arrays["texuv2" if display.kind == "mfd" else "texuv3"][display.mech_id]
current_offsets = arrays["offset2" if display.kind == "mfd" else "offset3"][display.mech_id]
output_dir = ROOT / display.folder / display.output_dir
unexploded_output = output_dir / f"{display.slug}_{display.kind}_unexploded_coords_comparison.png"
exploded_output = output_dir / f"{display.slug}_{display.kind}_exploded_coords_comparison.png"
draw_comparison(
load_unexploded(display),
current_rectangles,
provided_rectangles,
unexploded_output,
mark_origins=False,
)
exploded_image = Image.open(ROOT / display.folder / display.exploded_image).convert("RGB")
draw_comparison(
exploded_image,
component_boxes(current_rectangles, current_offsets),
component_boxes(provided_rectangles, provided_offsets),
exploded_output,
mark_origins=True,
)
rectangle_differences = difference_count(current_rectangles, provided_rectangles)
offset_differences = difference_count(current_offsets, provided_offsets)
status = "EXACT" if rectangle_differences == 0 and offset_differences == 0 else "DIFFERENT"
results.append((display, status, rectangle_differences, offset_differences, warnings))
generated.extend((unexploded_output, exploded_output))
all_warnings.extend(warnings)
configured = {(display.folder.lower(), display.kind) for display in DISPLAYS}
missing = []
for folder in sorted(path.name for path in ROOT.iterdir() if path.is_dir()):
for kind in ("mfd", "radar"):
if (folder.lower(), kind) not in configured:
missing.append((folder, kind))
lines = [
"# J&J Coordinate Comparison Summary",
"",
"Generated by `generate_comparison_maps.py`. Red is the current `coord.cpp` mapping; green is the provided J&J mapping.",
"",
"`EXACT` means all eleven source rectangles and all eleven exploded offsets match numerically. `DIFFERENT` means at least one value differs; inspect both generated images.",
"",
"| Mech | Display | Status | Source rectangles different | Exploded offsets different |",
"|---|---:|---:|---:|---:|",
]
for display, status, rectangle_differences, offset_differences, _ in results:
lines.append(f"| {display.folder} | {display.kind.upper()} | **{status}** | {rectangle_differences}/11 | {offset_differences}/11 |")
lines.extend(("", "## Inherited mappings", ""))
for variant_name, variant_id, base_name, base_id in INHERITED_MAPPINGS:
lines.append(
f"- `{variant_name}` (Mech ID {variant_id}) inherits all four coordinate rows "
f"from `{base_name}` (Mech ID {base_id}); generator assertion passed"
)
lines.extend(("", "## Missing comparison inputs", ""))
for folder, kind in missing:
lines.append(f"- `{folder}` {kind.upper()}: no complete provided mapping/image set configured")
lines.extend(("", "## Input warnings", ""))
if all_warnings:
for warning in sorted(set(all_warnings)):
lines.append(f"- {warning}")
else:
lines.append("- None")
lines.extend(("", "## Generated files", ""))
for output in generated:
lines.append(f"- `{output.relative_to(ROOT)}`")
lines.append("")
(ROOT / "COMPARISON-SUMMARY.md").write_text("\n".join(lines))
exact = sum(status == "EXACT" for _, status, _, _, _ in results)
print(f"Generated {len(generated)} maps for {len(results)} display sets: {exact} exact, {len(results) - exact} different")
print(f"Warnings: {len(set(all_warnings))}; missing display sets: {len(missing)}")
if __name__ == "__main__":
main()
@@ -104,7 +104,7 @@ char *TimeToString( double secs )
else else
if( secs*1000000.f >= 1.0f ) if( secs*1000000.f >= 1.0f )
{ {
sprintf(TimeStr,"%.2f µS", secs*1000000.f); sprintf(TimeStr,"%.2f us", secs*1000000.f);
} }
else else
{ {
@@ -24,9 +24,9 @@ const char *DivStrings[] =
"1nS / div", // 10^-9 "1nS / div", // 10^-9
"10nS / div", // 10^-8 "10nS / div", // 10^-8
"100nS / div", "100nS / div",
"1µS / div", "1us / div",
"10µS / div", "10us / div",
"100µS / div", "100us / div",
"1mS / div", "1mS / div",
"10mS / div", "10mS / div",
"100mS / div", "100mS / div",
@@ -104,7 +104,7 @@ char *TimeToString( double secs )
else else
if( secs*1000000.f >= 1.0f ) if( secs*1000000.f >= 1.0f )
{ {
sprintf(TimeStr,"%.2f µS", secs*1000000.f); sprintf(TimeStr,"%.2f us", secs*1000000.f);
} }
else else
{ {
@@ -24,9 +24,9 @@ const char *DivStrings[] =
"1nS / div", // 10^-9 "1nS / div", // 10^-9
"10nS / div", // 10^-8 "10nS / div", // 10^-8
"100nS / div", "100nS / div",
"1µS / div", "1us / div",
"10µS / div", "10us / div",
"100µS / div", "100us / div",
"1mS / div", "1mS / div",
"10mS / div", "10mS / div",
"100mS / div", "100mS / div",
@@ -1032,19 +1032,19 @@ void __stdcall gos_SetViewport( DWORD LeftX, DWORD TopY, DWORD Width, DWORD Heig
// You can only clear the backbuffer or Z buffer on the FIRST setup viewport (outside the Begin/End Scene // You can only clear the backbuffer or Z buffer on the FIRST setup viewport (outside the Begin/End Scene
// //
//상훈 앞 //sanghoon begin
void __cdecl ClearTargetCameraBackBuffer() void __cdecl ClearTargetCameraBackBuffer()
{ {
D3DRECT rc={0,10,120,112}; D3DRECT rc={0,10,120,112};
wClear(d3dDevice7,1,&rc,D3DCLEAR_TARGET,0xFF00FFFF,0,0); wClear(d3dDevice7,1,&rc,D3DCLEAR_TARGET,0xFF00FFFF,0,0);
return ; return ;
} }
//상훈 뒤 //sanghoon end
void __stdcall gos_SetupViewport( bool FillZ, float ZBuffer, bool FillBG, DWORD BGColor, float top, float left, float bottom, float right, bool ClearStencil, DWORD StencilValue ) void __stdcall gos_SetupViewport( bool FillZ, float ZBuffer, bool FillBG, DWORD BGColor, float top, float left, float bottom, float right, bool ClearStencil, DWORD StencilValue )
{ {
//상훈짱... //sanghoon
//원래의 법칙에 어긋나는 행동이기 때문에 ASSERT를 무시한다. //This violates the normal rules, so we suppress the ASSERT.
//gosASSERT( !InsideBeginScene || !(FillZ || FillBG) ); //gosASSERT( !InsideBeginScene || !(FillZ || FillBG) );
// //
// Work out size of viewport // Work out size of viewport
@@ -47,11 +47,11 @@ extern DWORD gDisableJoystick;
bool DisablePolling=0; bool DisablePolling=0;
void CMRestoreEffects( int stick ); void CMRestoreEffects( int stick );
// 鉉 - start // hyun begin
int g_bUseOrgJoy = TRUE; int g_bUseOrgJoy = TRUE;
void (__stdcall *g_pfnRIO_Joy)(DIJOYSTATE& js) = NULL; // Check out Test .. void (__stdcall *g_pfnRIO_Joy)(DIJOYSTATE& js) = NULL; // Check out Test ..
bool g_bNoWeaponRangeCheck = false; bool g_bNoWeaponRangeCheck = false;
// 鉉 - end // hyun end
// //
// //
// Initialize the DirectInput devices // Initialize the DirectInput devices
@@ -479,7 +479,7 @@ extern DWORD EnableDisplayInfo;
if( EnableDisplayInfo==0 ) { if( EnableDisplayInfo==0 ) {
disp: disp:
sprintf(DisplayInfoText, "COIN: %d 코인이래요", g_nCoinCount); sprintf(DisplayInfoText, "COIN: %d coins", g_nCoinCount);
EnableDisplayInfo = 1; EnableDisplayInfo = 1;
} else if (EnableDisplayInfo==2) { } else if (EnableDisplayInfo==2) {
if( timeGetTime() < EndDisplayInfoTime+50 ) { if( timeGetTime() < EndDisplayInfoTime+50 ) {
@@ -100,11 +100,11 @@ IDirectDraw7* DDobject = NULL; // Primiary DirectDraw object (for persistant
IDirectDraw7* CurrentDDobject = NULL; // DirectDraw object for rendering (can be the same as DDObject) IDirectDraw7* CurrentDDobject = NULL; // DirectDraw object for rendering (can be the same as DDObject)
IDirectDrawSurface7* ZBufferSurface = NULL; // ZBuffer surface IDirectDrawSurface7* ZBufferSurface = NULL; // ZBuffer surface
IDirectDrawSurface7* RefZBufferSurface = NULL; // Referemce rasterizer ZBuffer surface IDirectDrawSurface7* RefZBufferSurface = NULL; // Referemce rasterizer ZBuffer surface
// //sanghoon marker
IDirectDrawSurface7* SH_TargetBufferSurface = NULL; IDirectDrawSurface7* SH_TargetBufferSurface = NULL;
IDirectDrawSurface7* SH_SwirlTexture= NULL; IDirectDrawSurface7* SH_SwirlTexture= NULL;
IDirectDrawSurface7* SH_GameEndTexture= NULL; IDirectDrawSurface7* SH_GameEndTexture= NULL;
// //sanghoon marker
DDSURFACEDESC2 BackBufferddsd; DDSURFACEDESC2 BackBufferddsd;
// //
@@ -167,7 +167,7 @@ float GammaSetting=0.0;
bool UseGammaCorrection=0; bool UseGammaCorrection=0;
float UserGamma=1.0f; float UserGamma=1.0f;
//¯-begin //sanghoon begin
bool use_shgui = false; bool use_shgui = false;
bool hsh_initialized=false; bool hsh_initialized=false;
bool hsh_mrdev_initialized=false; bool hsh_mrdev_initialized=false;
@@ -176,7 +176,7 @@ bool hsh_mrdev_initialized=false;
#include "coord.cpp" #include "coord.cpp"
//#include "hsh_dxras.cpp" //#include "hsh_dxras.cpp"
#include "render.hpp" #include "render.hpp"
//¯-end //sanghoon end
// //
// Value range 0-10,000, default 750 (See DirectX docs) // Value range 0-10,000, default 750 (See DirectX docs)
@@ -1021,11 +1021,11 @@ void EnterFullScreenMode()
// //
#ifdef LAB_ONLY #ifdef LAB_ONLY
//¯ //sanghoon
wSetCooperativeLevel( CurrentDDobject, hWindow, DDSCL_SETFOCUSWINDOW ); wSetCooperativeLevel( CurrentDDobject, hWindow, DDSCL_SETFOCUSWINDOW );
wSetCooperativeLevel( CurrentDDobject, hWindow, DDSCL_ALLOWREBOOT | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN ); wSetCooperativeLevel( CurrentDDobject, hWindow, DDSCL_ALLOWREBOOT | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN );
#else #else
//¯ //sanghoon
wSetCooperativeLevel( CurrentDDobject, hWindow, DDSCL_SETFOCUSWINDOW ); wSetCooperativeLevel( CurrentDDobject, hWindow, DDSCL_SETFOCUSWINDOW );
wSetCooperativeLevel( CurrentDDobject, hWindow, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN ); wSetCooperativeLevel( CurrentDDobject, hWindow, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN );
#endif #endif
@@ -1127,11 +1127,11 @@ void EnterFullScreenMode()
} }
} }
//¯-begin //sanghoon begin
if(use_shgui){ if(use_shgui){
HSH_EnterFullScreen2(); HSH_EnterFullScreen2();
} }
//¯-end //sanghoon end
// //
// Changed modes, create all the surfaces // Changed modes, create all the surfaces
@@ -1164,7 +1164,7 @@ void EnterFullScreenMode()
// Debugging information // Debugging information
// //
SPEW(( GROUP_DIRECTDRAW, "EnterFullScreenMode() Finished" )); SPEW(( GROUP_DIRECTDRAW, "EnterFullScreenMode() Finished" ));
// //sanghoon
} }
@@ -1289,7 +1289,7 @@ void DisplayBackBuffer()
else else
{ {
#if 0 #if 0
// //sanghoon marker
extern bool sh_game_started; extern bool sh_game_started;
static FILE* fp=0; static FILE* fp=0;
static recorded_count=0; static recorded_count=0;
@@ -1352,11 +1352,11 @@ extern bool sh_game_started;
pp=(double*)malloc(800*600*2); pp=(double*)malloc(800*600*2);
} }
} }
// //sanghoon marker
#endif #endif
wFlip( FrontBufferSurface,NULL,DDFLIP_DONOTWAIT );//|DDFLIP_INTERVAL2 ); wFlip( FrontBufferSurface,NULL,DDFLIP_DONOTWAIT );//|DDFLIP_INTERVAL2 );
// ħ.. //sanghoon marker..
// //sanghoon
//wFlip( FrontBufferSurface,NULL,DDFLIP_WAIT ); //wFlip( FrontBufferSurface,NULL,DDFLIP_WAIT );
} }
} }
@@ -1403,11 +1403,11 @@ extern bool sh_game_started;
// //
void DirectDrawRelease() void DirectDrawRelease()
{ {
//¯-begin //sanghoon begin
if(use_shgui){ if(use_shgui){
HSH_DirectDrawRelease2(); HSH_DirectDrawRelease2();
} }
//¯-end //sanghoon end
SPEW(( GROUP_DIRECTDRAW, "DirectDrawRelease()" )); SPEW(( GROUP_DIRECTDRAW, "DirectDrawRelease()" ));
SafeFPU(); SafeFPU();
@@ -1496,7 +1496,7 @@ void DirectDrawRelease()
wRelease( ZBufferSurface ); wRelease( ZBufferSurface );
ZBufferSurface=0; ZBufferSurface=0;
} }
// //sanghoon marker
//DirectDrawRelease //DirectDrawRelease
if( SH_TargetBufferSurface) if( SH_TargetBufferSurface)
{ {
@@ -1513,7 +1513,7 @@ void DirectDrawRelease()
wRelease( SH_GameEndTexture ); wRelease( SH_GameEndTexture );
SH_GameEndTexture=0; SH_GameEndTexture=0;
} }
// //sanghoon marker
if( GammaControlInterface ) if( GammaControlInterface )
{ {
wRelease( GammaControlInterface ); wRelease( GammaControlInterface );
@@ -1593,20 +1593,20 @@ void DirectDrawCreateDDObject()
// //
// Create the NULL (primary) DirectDraw object // Create the NULL (primary) DirectDraw object
// //
// //sanghoon marker
/* /*
wDirectDrawCreateEx( &DeviceArray[0].DeviceGUID, (void**)&DDobject, IID_IDirectDraw7, NULL ); wDirectDrawCreateEx( &DeviceArray[0].DeviceGUID, (void**)&DDobject, IID_IDirectDraw7, NULL );
// wDirectDrawCreateEx( &DeviceArray[Environment.FullScreenDevice].DeviceGUID, (void**)&DDobject, IID_IDirectDraw7, NULL ); // wDirectDrawCreateEx( &DeviceArray[Environment.FullScreenDevice].DeviceGUID, (void**)&DDobject, IID_IDirectDraw7, NULL );
wSetCooperativeLevel( DDobject, hWindow, DDSCL_NORMAL ); wSetCooperativeLevel( DDobject, hWindow, DDSCL_NORMAL );
CurrentDDobject=DDobject; CurrentDDobject=DDobject;
*/ */
// ҽ.. //sanghoon ..
//wDirectDrawCreateEx(&DeviceArray[0].DeviceGUID, (void**)&DDobject, IID_IDirectDraw7, NULL ); //wDirectDrawCreateEx(&DeviceArray[0].DeviceGUID, (void**)&DDobject, IID_IDirectDraw7, NULL );
wDirectDrawCreateEx(NULL, (void**)&DDobject, IID_IDirectDraw7, NULL ); wDirectDrawCreateEx(NULL, (void**)&DDobject, IID_IDirectDraw7, NULL );
wSetCooperativeLevel( DDobject, hWindow, DDSCL_NORMAL ); wSetCooperativeLevel( DDobject, hWindow, DDSCL_NORMAL );
CurrentDDobject=DDobject; CurrentDDobject=DDobject;
// //sanghoon marker
// //
// //
// //
@@ -1630,11 +1630,11 @@ bool SetupMode( bool FullScreen, DWORD Renderer )
BackBufferSurface=0; BackBufferSurface=0;
ClipperObject=0; ClipperObject=0;
ZBufferSurface=0; ZBufferSurface=0;
// //sanghoon marker
SH_TargetBufferSurface = 0; SH_TargetBufferSurface = 0;
SH_SwirlTexture=0; SH_SwirlTexture=0;
SH_GameEndTexture=0; SH_GameEndTexture=0;
// //sanghoon marker
Maind3dDevice7=0; Maind3dDevice7=0;
d3dDevice7=0; d3dDevice7=0;
DDSURFACEDESC2 ddsd; DDSURFACEDESC2 ddsd;
@@ -1993,7 +1993,7 @@ bool SetupMode( bool FullScreen, DWORD Renderer )
} }
wAddAttachedSurface( BackBufferSurface, ZBufferSurface ); wAddAttachedSurface( BackBufferSurface, ZBufferSurface );
// //sanghoon marker
{ {
DDSURFACEDESC2 rdesc; DDSURFACEDESC2 rdesc;
memset(&rdesc,0,sizeof(ddsd)); memset(&rdesc,0,sizeof(ddsd));
@@ -2019,7 +2019,7 @@ bool SetupMode( bool FullScreen, DWORD Renderer )
if(SUCCEEDED(wCreateSurface( CurrentDDobject, &rdesc, &SH_SwirlTexture, NULL ))){ if(SUCCEEDED(wCreateSurface( CurrentDDobject, &rdesc, &SH_SwirlTexture, NULL ))){
; ;
//ȭؽ . //Draw texture content to screen. .
} }
rdesc.dwFlags = DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH|DDSD_PIXELFORMAT|DDSD_TEXTURESTAGE; rdesc.dwFlags = DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH|DDSD_PIXELFORMAT|DDSD_TEXTURESTAGE;
@@ -2031,11 +2031,11 @@ bool SetupMode( bool FullScreen, DWORD Renderer )
if(SUCCEEDED(wCreateSurface( CurrentDDobject, &rdesc, &SH_GameEndTexture, NULL ))){ if(SUCCEEDED(wCreateSurface( CurrentDDobject, &rdesc, &SH_GameEndTexture, NULL ))){
; ;
//ȭؽ . //Draw texture content to screen. .
} }
} }
// //sanghoon marker
// //
// Get the Z buffer pixel format (nVidia may always match front bit depth) // Get the Z buffer pixel format (nVidia may always match front bit depth)
// //
@@ -2258,7 +2258,7 @@ Failed:
wRelease( ZBufferSurface ); wRelease( ZBufferSurface );
ZBufferSurface=0; ZBufferSurface=0;
} }
// //sanghoon
//SetupMode //SetupMode
if( SH_TargetBufferSurface) if( SH_TargetBufferSurface)
{ {
@@ -2275,7 +2275,7 @@ Failed:
wRelease( SH_SwirlTexture ); wRelease( SH_SwirlTexture );
SH_SwirlTexture=0; SH_SwirlTexture=0;
} }
// //sanghoon
if( ClipperObject ) if( ClipperObject )
{ {
if( FrontBufferSurface ) if( FrontBufferSurface )
@@ -2578,7 +2578,7 @@ void DirectDrawCreateAllBuffers()
wRelease( ZBufferSurface ); wRelease( ZBufferSurface );
ZBufferSurface=0; ZBufferSurface=0;
} }
// //sanghoon marker
//DirectDrawCreateAllBuffers //DirectDrawCreateAllBuffers
if( SH_TargetBufferSurface) if( SH_TargetBufferSurface)
{ {
@@ -2595,7 +2595,7 @@ void DirectDrawCreateAllBuffers()
wRelease( SH_SwirlTexture ); wRelease( SH_SwirlTexture );
SH_SwirlTexture=0; SH_SwirlTexture=0;
} }
// //sanghoon marker
if( ClipperObject ) if( ClipperObject )
{ {
if( FrontBufferSurface ) if( FrontBufferSurface )
@@ -161,7 +161,7 @@ HRESULT wEndScene( IDirect3DDevice7* d3dDevice7 )
//Original //Original
//if( FAILED(result) ) //if( FAILED(result) )
//상훈 //sanghoon
if( FAILED(result) && result!=DDERR_SURFACELOST) if( FAILED(result) && result!=DDERR_SURFACELOST)
PAUSE(( "FAILED (0x%x - %s) - EndScene()",result,ErrorNumberToMessage(result))); PAUSE(( "FAILED (0x%x - %s) - EndScene()",result,ErrorNumberToMessage(result)));
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More