An HTML roster of all 18 selectable mechs in the VTV-PRESETS layout style:
per-mech portrait, loadout chips, manual stat sheet, and the authored
weapons/ammo table, with filter (all/base/variant), search and live count.
Nothing is transcribed by hand where the game can be read directly
(tools/build_mechs.py + tools/mechs.tpl -> docs/MECHS.html):
- ROSTER: kVehicles[] parsed from game/glass/btl4fe.cpp -- the exact
18-entry catalog the menu offers, in menu order (8 base chassis + 10
variants).
- WEAPONS + AMMO BINS: each mech's cmCrit(...) lines in
content/GAUGE/L4GAUGE.CFG (the critical-schematic's authored subsystem
roster; independently cross-checked against the rtype-17 subsystem
records in BTL4.RES, 2026-08-12 -- all 18 agree).
- PORTRAITS: the game's own mission-review images (GAUGE/<stem>_MR.PCC,
PCC=PCX) resolved through mrpal.pcc with the zone slots (32..63) held
at adpal entry 0 -- the review screen's undamaged green. Variants
without their own portrait share the base's, as the RES does.
- MECH STATS: the original player's manual stat sheets, transcribed [T1]
from page renders (pp. 25 Loki/Thor, 26 Vulture/MadCat, 29
Owens/Blackhawk). The manual covers the six 4.0 hulls only; Avatar,
Sunder and the variants show a dash, never an invented number.
- FIXED vs TWISTING torso: derived from the shipped RES joint tables
(jointtorso/jointtshadow presence; torso.cpp:270 gate) -- blkhawk,
bhk1, owens, own1 are fixed, matching the manual's Torso 0/0 rows.
Rendered + verified headless (top and bottom, all 18 cards, filters wired).
Not yet shipped by mkdist (MAPS.html is not either); add a block modelled
on the CONTROLS.html one if testers should get it in the zip.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
261 lines
11 KiB
Smarty
261 lines
11 KiB
Smarty
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>BattleTech 4.11 — the mech roster</title>
|
|
<style>
|
|
*, *::before, *::after {{ box-sizing: border-box; }}
|
|
:root {{
|
|
--ground: #0e0f11; --panel: #16181b; --rule: #2a2e33;
|
|
--ink: #e8e6e2; --ink-mid: #a3a8ae; --ink-low: #71777e;
|
|
--hot: #f39313; /* palette 6, the display's own amber */
|
|
--nrg: #ff5340; /* energy weapons */
|
|
--bal: #f39313; /* ballistic */
|
|
--msl: #58c470; /* missiles -- the armor doll's green family */
|
|
--mono: ui-monospace, "Cascadia Mono", Consolas, Menlo, monospace;
|
|
--sans: ui-sans-serif, system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
}}
|
|
@media (prefers-color-scheme: light) {{
|
|
:root {{
|
|
--ground: #f4f2ee; --panel: #fbfaf8; --rule: #ddd8d0;
|
|
--ink: #14161a; --ink-mid: #565c63; --ink-low: #7d838a;
|
|
--hot: #a35f00; --nrg: #b3260f; --bal: #a35f00; --msl: #1c7a3f;
|
|
}}
|
|
}}
|
|
body {{
|
|
margin: 0; background: var(--ground); color: var(--ink);
|
|
font: 400 15px/1.6 var(--sans);
|
|
}}
|
|
.wrap {{ max-width: 78rem; margin: 0 auto; padding: 0 clamp(1rem, 4vw, 2.5rem); }}
|
|
.sr-only {{
|
|
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
|
|
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
|
|
}}
|
|
:focus-visible {{ outline: 2px solid var(--hot); outline-offset: 2px; }}
|
|
|
|
header.top {{ border-bottom: 1px solid var(--rule); padding: clamp(2.5rem,6vw,4rem) 0 1.75rem; }}
|
|
.eyebrow {{
|
|
font: 500 12px/1 var(--mono); letter-spacing: .2em; text-transform: uppercase;
|
|
color: var(--ink-low); margin: 0 0 1rem;
|
|
}}
|
|
h1 {{
|
|
font: 700 clamp(1.9rem,1.4rem+2.2vw,3rem)/1.05 var(--mono);
|
|
letter-spacing: -.02em; text-transform: uppercase; margin: 0 0 .9rem;
|
|
}}
|
|
h1 em {{ font-style: normal; color: var(--hot); }}
|
|
.lede {{ max-width: 62ch; color: var(--ink-mid); margin: 0 0 1.4rem; font-size: 1.05rem; }}
|
|
.lede strong {{ color: var(--ink); font-weight: 600; }}
|
|
.rules {{ display: flex; flex-wrap: wrap; gap: .6rem 2rem; margin: 0; padding: 0; list-style: none; }}
|
|
.rules li {{
|
|
font: 500 12px/1.5 var(--mono); color: var(--ink-mid);
|
|
padding-left: 1.1rem; position: relative;
|
|
}}
|
|
.rules li::before {{
|
|
content: ""; position: absolute; left: 0; top: .45em;
|
|
width: .45rem; height: .45rem; background: var(--hot);
|
|
}}
|
|
.rules b {{ color: var(--ink); font-weight: 700; }}
|
|
|
|
.bar {{
|
|
position: sticky; top: 0; z-index: 5;
|
|
background: var(--ground);
|
|
border-bottom: 1px solid var(--rule); padding: .75rem 0;
|
|
}}
|
|
.bar__in {{ display: flex; flex-wrap: wrap; gap: .75rem 1.25rem; align-items: center; }}
|
|
.seg {{ display: inline-flex; border: 1px solid var(--rule); }}
|
|
.seg button {{
|
|
font: 500 12px/1 var(--mono); letter-spacing: .12em; text-transform: uppercase;
|
|
background: none; border: 0; color: var(--ink-mid); padding: .55rem .9rem; cursor: pointer;
|
|
}}
|
|
.seg button[aria-pressed="true"] {{ background: var(--hot); color: var(--ground); }}
|
|
.search {{
|
|
flex: 1 1 12rem; min-width: 9rem;
|
|
font: 400 12px var(--mono);
|
|
background: var(--panel); color: var(--ink);
|
|
border: 1px solid var(--rule); padding: .55rem .7rem;
|
|
}}
|
|
.search::placeholder {{ color: var(--ink-low); }}
|
|
.count {{ font: 500 12px var(--mono); color: var(--ink-low); letter-spacing: .1em; }}
|
|
|
|
.key {{ display: flex; flex-wrap: wrap; gap: .4rem .9rem; padding: 1.25rem 0 0; }}
|
|
.key span {{ display: inline-flex; align-items: center; gap: .4rem;
|
|
font: 500 12px var(--mono); letter-spacing: .08em; color: var(--ink-low); }}
|
|
.key i {{ width: .7rem; height: .7rem; display: inline-block; }}
|
|
|
|
.roster {{ display: grid; gap: 1px; background: var(--rule); border: 1px solid var(--rule);
|
|
margin: 1.75rem 0 3rem; }}
|
|
.mech {{ background: var(--panel); display: grid; gap: 0;
|
|
grid-template-columns: minmax(15rem, 19rem) 1fr; }}
|
|
@media (max-width: 46rem) {{ .mech {{ grid-template-columns: 1fr; }} }}
|
|
.mech[hidden] {{ display: none; }}
|
|
|
|
.mech__id {{ padding: 1.1rem 1.25rem 1.35rem; border-right: 1px solid var(--rule); }}
|
|
@media (max-width: 46rem) {{ .mech__id {{ border-right: 0; border-bottom: 1px solid var(--rule); }} }}
|
|
.hull {{ margin: 0 0 .9rem; display: flex; flex-direction: column; gap: .45rem;
|
|
align-items: flex-start; }}
|
|
/* The game's own review image on the display's own black, either theme. */
|
|
.hull__img {{
|
|
display: block; width: 7rem; height: auto;
|
|
background: #000; border: 1px solid var(--rule); image-rendering: pixelated;
|
|
}}
|
|
.hull figcaption {{
|
|
font: 500 11px/1.35 var(--mono); letter-spacing: .1em;
|
|
text-transform: uppercase; color: var(--ink-low);
|
|
}}
|
|
.mech__id h3 {{
|
|
font: 700 1.5rem/1 var(--mono); letter-spacing: .01em;
|
|
margin: 0 0 .55rem; text-transform: uppercase;
|
|
}}
|
|
.mech__key {{
|
|
font: 500 12px/1 var(--mono); letter-spacing: .12em;
|
|
color: var(--ink-low); margin: -.3rem 0 .7rem;
|
|
}}
|
|
.mech__load {{ display: flex; flex-wrap: wrap; gap: .3rem; margin: 0 0 .7rem; }}
|
|
.chip {{
|
|
font: 500 11px/1 var(--mono); letter-spacing: .08em;
|
|
padding: .3rem .45rem; border: 1px solid currentColor;
|
|
}}
|
|
.chip--e {{ color: var(--nrg); }}
|
|
.chip--b {{ color: var(--bal); }}
|
|
.chip--m {{ color: var(--msl); }}
|
|
.mech__meta {{ display: flex; flex-wrap: wrap; gap: .8rem; margin: 0 0 .2rem;
|
|
font: 400 12px var(--mono); color: var(--ink-low); font-variant-numeric: tabular-nums; }}
|
|
.spec {{
|
|
margin: .75rem 0 0; display: grid; grid-template-columns: 1fr auto;
|
|
gap: .2rem .8rem; font: 500 12px var(--mono);
|
|
}}
|
|
.spec div {{ display: contents; }}
|
|
.spec dt {{ color: var(--ink-low); letter-spacing: .06em; }}
|
|
.spec dd {{ margin: 0; text-align: right; color: var(--ink); font-variant-numeric: tabular-nums; }}
|
|
.mech__sheet {{ margin: .6rem 0 0; font: 400 11px/1.5 var(--mono); color: var(--ink-low); }}
|
|
|
|
.mech__tables {{ min-width: 0; }}
|
|
.tablewrap {{ overflow-x: auto; }}
|
|
table {{ border-collapse: collapse; width: 100%; font-variant-numeric: tabular-nums; }}
|
|
thead th {{
|
|
font: 500 12px/1 var(--mono); letter-spacing: .14em;
|
|
color: var(--ink-low); text-align: left; font-weight: 500;
|
|
padding: .9rem .9rem .55rem; border-bottom: 1px solid var(--rule); white-space: nowrap;
|
|
}}
|
|
tbody th {{
|
|
font: 500 13px/1.3 var(--mono); text-align: left; color: var(--ink);
|
|
padding: .5rem .9rem; border-bottom: 1px solid var(--rule); white-space: nowrap;
|
|
}}
|
|
tbody td {{
|
|
font: 400 13px/1.3 var(--mono); color: var(--ink-mid);
|
|
padding: .5rem .9rem; border-bottom: 1px solid var(--rule); white-space: nowrap;
|
|
}}
|
|
tbody tr:last-child th, tbody tr:last-child td {{ border-bottom: 0; }}
|
|
th.num, td.num {{ text-align: right; }}
|
|
|
|
.empty-state {{
|
|
margin: 2rem 0; font: 500 13px var(--mono); color: var(--ink-low);
|
|
}}
|
|
.foot {{
|
|
border-top: 1px solid var(--rule); padding: 1.5rem 0 0; max-width: 76ch;
|
|
color: var(--ink-low); font-size: 13px;
|
|
}}
|
|
.foot:last-child {{ padding-bottom: 4rem; }}
|
|
.foot code {{ font-family: var(--mono); color: var(--ink-mid); }}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<header class="top">
|
|
<div class="wrap">
|
|
<p class="eyebrow">BattleTech 4.11 · from btl4fe.cpp, L4GAUGE.CFG and the player's manual</p>
|
|
<h1>Every mech,<br><em>and what it carries</em></h1>
|
|
<p class="lede">All <strong>{n} mechs the menu offers</strong> — {nbase} base chassis
|
|
and {nvar} variants — each with its authored weapons roster, ammo bins and the
|
|
stat sheet from the original player's manual. The portraits are the game's own
|
|
mission-review images, shown in their undamaged state.</p>
|
|
<ul class="rules">
|
|
<li><b>Variants share the chassis</b>, not the loadout</li>
|
|
<li><b>Blackhawk, Nova, Owens, Kotori</b> have a fixed torso</li>
|
|
<li>The manual sheets cover the <b>six 4.0 hulls</b> only</li>
|
|
</ul>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="bar">
|
|
<div class="wrap bar__in">
|
|
<div class="seg" role="group" aria-label="Filter">
|
|
<button type="button" data-filter="all" aria-pressed="true">All {n}</button>
|
|
<button type="button" data-filter="base" aria-pressed="false">Base {nbase}</button>
|
|
<button type="button" data-filter="variant" aria-pressed="false">Variants {nvar}</button>
|
|
</div>
|
|
<input id="search" class="search" type="search" placeholder="Search mech or weapon…"
|
|
aria-label="Search mech or weapon">
|
|
<span class="count" id="count">{n} / {n} mechs</span>
|
|
</div>
|
|
</div>
|
|
|
|
<main class="wrap">
|
|
<div class="key">
|
|
<span><i style="background:var(--nrg)"></i>Energy</span>
|
|
<span><i style="background:var(--bal)"></i>Ballistic</span>
|
|
<span><i style="background:var(--msl)"></i>Missile</span>
|
|
</div>
|
|
|
|
<div class="roster">{cards}</div>
|
|
<p class="empty-state" id="nomatch" hidden>No mech matches that.</p>
|
|
|
|
<p class="foot">The roster is <code>kVehicles[]</code> in <code>game/glass/btl4fe.cpp</code>
|
|
— the exact catalog the in-game menu offers, in menu order. Each mech's weapons and
|
|
ammo bins are its <code>cmCrit(...)</code> lines in <code>content/GAUGE/L4GAUGE.CFG</code>,
|
|
the critical-damage schematic's authored subsystem roster (independently confirmed against
|
|
the subsystem records in <code>BTL4.RES</code>). Nothing is transcribed by hand.</p>
|
|
<p class="foot">The portraits are each mech's mission-review image
|
|
(<code>GAUGE/<stem>_MR.PCC</code>) resolved through the review screen's own palette.
|
|
In play, its zone indexes are live damage slots painted green→orange→red by the
|
|
ColorMapper gauges; this page shows every zone at the undamaged ramp entry — the
|
|
same green the armor doll idles at. Variants without their own portrait share the base's,
|
|
exactly as the resource file does.</p>
|
|
<p class="foot">Mech stats are the original player's manual stat sheets (pp. 25, 26, 29).
|
|
The manual documents the six 4.0 hulls; Avatar and Sunder arrived with 4.10 and have no
|
|
sheet, so their rows show a dash rather than an invented number. FIXED vs twisting torso
|
|
is read from the shipped resource file: a mech twists only if its subsystem record carries
|
|
the <code>jointtorso</code>/<code>jointtshadow</code> joints — the four fixed mechs
|
|
match the manual's Torso 0/0 rows exactly.</p>
|
|
<p class="foot">Weapon notes: ER lasers fire red beams, PPCs blue, standard lasers yellow.
|
|
Authored ranges where the game states them: PPCs 900 m, missiles 800 m, the
|
|
Blackhawk's lasers 500 m, the AFC 50 750 m. The Blackhawk's SRM 6
|
|
racks fire Streak ammunition. LRM tube counts are the salvo size; each launcher's ammo
|
|
bin is listed beside it.</p>
|
|
</main>
|
|
|
|
<script>
|
|
const cards = [...document.querySelectorAll('.mech')];
|
|
const count = document.getElementById('count');
|
|
const search = document.getElementById('search');
|
|
let filter = 'all';
|
|
|
|
function apply() {{
|
|
const q = search.value.trim().toLowerCase();
|
|
let shown = 0;
|
|
for (const c of cards) {{
|
|
const okTag = filter === 'all' || c.dataset.tags.split(' ').includes(filter);
|
|
const okQ = !q || c.dataset.search.includes(q);
|
|
const on = okTag && okQ;
|
|
c.hidden = !on;
|
|
if (on) shown++;
|
|
}}
|
|
count.textContent = shown + ' / ' + cards.length + ' mechs';
|
|
document.getElementById('nomatch').hidden = shown > 0;
|
|
}}
|
|
|
|
for (const b of document.querySelectorAll('[data-filter]')) {{
|
|
b.addEventListener('click', () => {{
|
|
filter = b.dataset.filter;
|
|
for (const o of document.querySelectorAll('[data-filter]'))
|
|
o.setAttribute('aria-pressed', String(o === b));
|
|
apply();
|
|
}});
|
|
}}
|
|
search.addEventListener('input', apply);
|
|
apply();
|
|
</script>
|
|
</body>
|
|
</html>
|