Files
BT411/docs/CONTROLS.html
T
CydandClaude Opus 5 5f88a4eeb2 The keyboard becomes the button board (RP412 bindings port) + a controls page
Ported RP412's bindings design and made it the DEFAULT: the letter and number
rows are the MFD button banks laid out WHERE THEY SIT ON THE PANEL, flight
moves to the numpad so the board stays free, F1-F12 are the map's two columns,
and G/B stay unbound as the physical gap between the lower clusters.  Expressed
in BT's OWN grammar (slew/deflect/set, the Turn channel) -- bindings.txt is a
documented compatibility surface and was not touched.

Coverage: 61 of 72 addresses on the keyboard, and the 11 absent ones are
exactly those the pod never wired (0x16/0x17/0x1E/0x1F column gaps, 0x38-0x3E
intercom/door).  All 72 stay clickable on the panel.

It lands on BT's addresses unreasonably well: 1-4 + QWER are the ENTIRE coolant
system (Condensers 1-6, flush, balance), F6/F7 the display and control-mode
cycles, F9-F12 Generators A-D, F4 the crouch button reconstructed earlier
today.

THE DELIBERATE COST.  A bound key is removed from the authentic 1995
typed-hotkey channel so it cannot double-dispatch, and this board binds nearly
everything -- so 5 (Quad page), z (Eng1), t/y/u/i/o (pilot select) and +/-
(target zoom) are given up.  Unbind a key to get its 1995 meaning back.
Documented in the file header, the markdown and the page.

NEW RULE: A BINDINGS ROW WINS OVER A BUILT-IN CONVENIENCE KEY
(PadRIO::KeyHasBinding).  V and J/K/L are board buttons now, and those polls
read GetAsyncKeyState directly -- without this they would have fired BOTH the
button and the built-in.  View toggle lives on BACKTICK alone.

CONTROLS.MAP rewritten to mirror the board so glass/pod/dev still feel
identical (the 2026-07-21 settlement): 90 bindings, 0 parse complaints.

HAT LABELS CORRECTED [T1].  INPUT_PATH_AUDIT flagged 0x41-0x44 and was right.
Settled from the streamed mapping (BT_CTRLMAP_LOG): elem 66 -> subsys 17
attrID 14, i.e. 0x42 is the TORSO subsystem, not a look; 0x44/0x43/0x41 are the
mapper's LookLeft/LookRight/LookBehind (attrID 10/11/12).  The .RES has no
"TORSO CENTER" string -- its names are LookBehind/Down/Forward/Left/Right -- so
the audit's wording was loose but its substance correct.  Swept L4GLASSWIN,
L4PADPANEL and L4VB16.

docs/CONTROLS.html: interactive keyboard (hover a key for its address and
meaning, MFD clusters outlined), pad diagram, panel map, the under-glass
press-target figure, radar placement thumbnails and the environ.ini table --
modelled on RP412's page, rewritten for BT's addresses and hazards.  mkdist
wraps the fragment in a doctype/charset shell and ships it beside CONTROLS.txt.

Verified: bindings.txt regenerates and loads (74 keys, 0 errors); CONTROLS.MAP
90 bindings, 0 errors; 72/72 panel addresses still dispatch; surround /
exploded / dock / pod / dev boot and simulate with zero faults.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 03:23:27 -05:00

1062 lines
45 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<title>BattleTech 4.11 — Controls</title>
<style>
:root {
--void: #05070a;
--void-2: #080d10;
--glass: #0a1410;
--glass-edge: #16261d;
--phosphor: #40ff40;
--phosphor-2: #8dffa0;
--dim: #189418;
--hazard: #b0302a;
--hazard-lit: #ff5a48;
--amber: #c8a32e;
--amber-lit: #ffd25e;
--ink: #b9cfc0;
--ink-quiet: #6d8478;
--mono: "Cascadia Mono", Consolas, "SF Mono", ui-monospace, Menlo, monospace;
--sans: system-ui, -apple-system, "Segoe UI Variable Text", "Segoe UI", Roboto, sans-serif;
--u: 42px;
--gap: 5px;
}
* { box-sizing: border-box; }
body {
margin: 0;
background:
radial-gradient(120% 90% at 50% -10%, #0d1a14 0%, transparent 60%),
var(--void);
color: var(--ink);
font-family: var(--sans);
line-height: 1.65;
-webkit-font-smoothing: antialiased;
}
body::after {
content: "";
position: fixed;
inset: 0;
pointer-events: none;
background: radial-gradient(130% 100% at 50% 45%, transparent 55%, rgba(0,0,0,.55) 100%);
z-index: 5;
}
.wrap {
max-width: 1180px;
margin: 0 auto;
padding: clamp(28px, 5vw, 64px) clamp(16px, 4vw, 40px) 80px;
position: relative;
z-index: 1;
}
.masthead { border-bottom: 1px solid var(--glass-edge); padding-bottom: 28px; }
.eyebrow {
font-family: var(--mono);
font-size: .72rem;
letter-spacing: .28em;
text-transform: uppercase;
color: var(--dim);
margin: 0 0 14px;
}
h1 {
font-family: var(--mono);
font-size: clamp(1.9rem, 5.2vw, 3.1rem);
line-height: 1.05;
letter-spacing: .04em;
text-transform: uppercase;
color: var(--phosphor);
text-shadow: 0 0 22px rgba(64,255,64,.28);
margin: 0 0 18px;
text-wrap: balance;
}
.lede { max-width: 64ch; margin: 0; color: var(--ink); }
.lede code { font-family: var(--mono); color: var(--phosphor-2); font-size: .92em; }
.provenance {
margin: 18px 0 0;
font-family: var(--mono);
font-size: .74rem;
letter-spacing: .08em;
color: var(--ink-quiet);
}
.glance {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
gap: 1px;
background: var(--glass-edge);
border: 1px solid var(--glass-edge);
margin: 34px 0 0;
}
.glance div { background: var(--void-2); padding: 16px 18px; }
.glance dt {
font-family: var(--mono);
font-size: .68rem;
letter-spacing: .2em;
text-transform: uppercase;
color: var(--ink-quiet);
margin: 0 0 6px;
}
.glance dd { margin: 0; font-family: var(--mono); font-size: 1.02rem; color: var(--phosphor); }
.glance dd small { display: block; font-family: var(--sans); font-size: .78rem; color: var(--ink-quiet); letter-spacing: 0; margin-top: 3px; }
section { margin-top: 64px; }
h2 {
font-family: var(--mono);
font-size: .82rem;
letter-spacing: .3em;
text-transform: uppercase;
color: var(--phosphor);
margin: 0 0 6px;
display: flex;
align-items: center;
gap: 14px;
}
h2::after { content: ""; flex: 1; height: 1px; background: linear-gradient(90deg, var(--glass-edge), transparent); }
h3 {
font-family: var(--mono);
font-size: .78rem;
font-weight: 400;
letter-spacing: .2em;
text-transform: uppercase;
color: var(--phosphor);
margin: 46px 0 8px;
}
.sub { margin: 0 0 26px; max-width: 64ch; color: var(--ink-quiet); font-size: .93rem; }
.panel {
background: linear-gradient(180deg, rgba(64,255,64,.03), transparent 40%), var(--glass);
border: 1px solid var(--glass-edge);
padding: clamp(16px, 3vw, 30px);
overflow-x: auto;
}
.readout {
display: flex;
flex-wrap: wrap;
align-items: baseline;
gap: 8px 22px;
border: 1px solid var(--glass-edge);
border-top: none;
background: var(--void-2);
padding: 13px clamp(16px, 3vw, 30px);
font-family: var(--mono);
min-height: 54px;
}
.readout .rd-name { color: var(--phosphor); font-size: 1rem; letter-spacing: .04em; }
.readout .rd-addr { color: var(--amber-lit); font-size: .82rem; border: 1px solid rgba(200,163,46,.35); padding: 1px 8px; }
.readout .rd-desc { color: var(--ink-quiet); font-size: .85rem; font-family: var(--sans); }
.readout[data-idle="true"] .rd-name { color: var(--ink-quiet); }
.kb { display: flex; flex-direction: column; gap: 14px; min-width: 940px; position: relative; padding-top: 22px; }
.kb-groups { position: absolute; inset: 0; pointer-events: none; }
.kb-groups svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.kb-groups path { fill: none; stroke-width: 1; opacity: .85; }
.kb-groups path.g-mfd { stroke: #ff9a8c; }
.kb-groups path.g-col { stroke: var(--amber-lit); }
.kb-groups .g-label {
position: absolute;
transform: translate(0, -100%);
font-family: var(--mono);
font-size: .56rem;
letter-spacing: .16em;
text-transform: uppercase;
white-space: nowrap;
padding-bottom: 3px;
}
.kb-groups .g-label.g-mfd { color: #ff9a8c; }
.kb-groups .g-label.g-col { color: var(--amber-lit); }
.kb-bank { display: flex; gap: 22px; }
.kb-main { display: flex; flex-direction: column; gap: var(--gap); }
.kb-row { display: flex; gap: var(--gap); }
.kb-row.zone { margin-top: 18px; }
.kb-side { display: flex; flex-direction: column; gap: var(--gap); justify-content: flex-end; }
.kb-nav { display: flex; flex-direction: column; gap: var(--gap); align-items: center; padding-top: calc(var(--u) + var(--gap)); height: 100%; }
.kb-nav .grow { flex: 1 1 auto; min-height: var(--gap); }
.kb-pad { display: grid; grid-template-columns: repeat(4, var(--u)); gap: var(--gap); }
.key {
width: var(--u);
height: var(--u);
flex: none;
border: 1px solid #1d2b24;
border-bottom-color: #0c1512;
background: linear-gradient(180deg, #101a16, #0b120f);
color: #4e6a5c;
font-family: var(--mono);
font-size: .72rem;
line-height: 1.1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 2px;
padding: 2px;
cursor: default;
transition: transform .08s ease, box-shadow .12s ease, border-color .12s ease;
}
.key span.k-sub { font-size: .53rem; letter-spacing: .04em; text-transform: uppercase; opacity: .85; }
.key[data-role] { cursor: pointer; }
.key:focus-visible { outline: 2px solid var(--phosphor); outline-offset: 2px; }
.key[data-role="flight"] { color: var(--phosphor); border-color: #2c6b38; background: linear-gradient(180deg, #10281a, #0a1a11); }
.key[data-role="mfd"] { color: #ff9a8c; border-color: #6b2723; background: linear-gradient(180deg, #2a100e, #1a0a09); }
.key[data-role="column"] { color: var(--amber-lit); border-color: #6d5a1c; background: linear-gradient(180deg, #251d08, #171205); }
.key[data-role]:hover, .key.is-active {
transform: translateY(-1px);
border-color: currentColor;
box-shadow: 0 0 0 1px currentColor, 0 0 18px -4px currentColor;
}
.key.w125 { width: calc(var(--u) * 1.25 + var(--gap) * .25); }
.key.w15 { width: calc(var(--u) * 1.5 + var(--gap) * .5); }
.key.w175 { width: calc(var(--u) * 1.75 + var(--gap) * .75); }
.key.w2 { width: calc(var(--u) * 2 + var(--gap)); }
.key.w225 { width: calc(var(--u) * 2.25 + var(--gap) * 1.25); }
.key.w275 { width: calc(var(--u) * 2.75 + var(--gap) * 1.75); }
.key.w6 { width: calc(var(--u) * 6.25 + var(--gap) * 5.25); }
.key.tall { grid-row: span 2; height: calc(var(--u) * 2 + var(--gap)); }
.key.pad0 { grid-column: span 2; width: calc(var(--u) * 2 + var(--gap)); }
.spacer { width: calc(var(--u) * .5); flex: none; }
.legend { display: flex; flex-wrap: wrap; gap: 10px 26px; margin-top: 20px; font-family: var(--mono); font-size: .74rem; letter-spacing: .06em; }
.legend span { display: flex; align-items: center; gap: 8px; color: var(--ink-quiet); }
.legend i { width: 13px; height: 13px; border: 1px solid; display: block; }
.lg-flight { color: var(--phosphor); border-color: currentColor; background: #10281a; }
.lg-mfd { color: #ff9a8c; border-color: currentColor; background: #2a100e; }
.lg-col { color: var(--amber-lit); border-color: currentColor; background: #251d08; }
.lg-off { color: #4e6a5c; border-color: #1d2b24; background: #101a16; }
.pad-shell { display: flex; justify-content: center; }
svg.pad { width: 100%; max-width: 720px; height: auto; }
svg.pad .body { fill: #0c1512; stroke: #1f3329; stroke-width: 2; }
svg.pad .ctl { fill: #12211a; stroke: #2c6b38; stroke-width: 1.6; cursor: pointer; transition: fill .12s ease, stroke .12s ease; }
svg.pad .ctl:hover, svg.pad .ctl.is-active { fill: #1a3b26; stroke: var(--phosphor); }
svg.pad .lbl { font-family: var(--mono); font-size: 11px; fill: var(--phosphor); letter-spacing: .06em; pointer-events: none; }
svg.pad .lbl-q { font-family: var(--mono); font-size: 10px; fill: var(--ink-quiet); letter-spacing: .08em; pointer-events: none; text-transform: uppercase; }
svg.pad .lead { stroke: #244a33; stroke-width: 1; fill: none; }
svg.pad .cap { font-family: var(--mono); font-size: 11px; fill: #cfe8d8; pointer-events: none; }
.cockpit { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; min-width: 720px; align-items: start; }
.mfd { border: 1px solid var(--glass-edge); background: #060c09; display: flex; flex-direction: column; }
.mfd-strip { display: flex; gap: 2px; padding: 3px; background: #150a09; }
.mfd-strip b {
flex: 1;
background: linear-gradient(180deg, #b0302a, #7d211d);
color: #ffd9d3;
font-family: var(--mono);
font-size: .62rem;
font-weight: 400;
text-align: center;
padding: 4px 0;
letter-spacing: .04em;
}
.mfd-face { padding: 14px 12px; text-align: center; font-family: var(--mono); }
.mfd-face em { display: block; font-style: normal; color: var(--phosphor); font-size: .8rem; letter-spacing: .12em; text-transform: uppercase; }
.mfd-face small { display: block; color: var(--ink-quiet); font-size: .68rem; margin-top: 5px; letter-spacing: .06em; }
.cockpit-note { grid-column: span 3; margin: 2px 0 0; font-size: .82rem; color: var(--ink-quiet); }
.mfd-map { min-height: 300px; }
.map-body { display: flex; align-items: stretch; flex: 1; }
.map-col { display: flex; flex-direction: column; gap: 2px; padding: 3px; background: #150a09; }
.map-col b {
flex: 1;
min-height: 26px;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(90deg, #c8a32e, #8d711b);
color: #2a2205;
font-family: var(--mono);
font-size: .62rem;
font-weight: 400;
letter-spacing: .04em;
padding: 0 7px;
}
.map-face {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 6px;
padding: 14px 8px;
font-family: var(--mono);
text-align: center;
background: repeating-linear-gradient(0deg, rgba(64,255,64,.05) 0 1px, transparent 1px 22px), #060c09;
}
.map-face em { font-style: normal; color: var(--phosphor); font-size: .8rem; letter-spacing: .12em; text-transform: uppercase; }
.map-face small { color: var(--ink-quiet); font-size: .66rem; letter-spacing: .06em; line-height: 1.5; }
.tbl-scroll { overflow-x: auto; border: 1px solid var(--glass-edge); }
table { width: 100%; border-collapse: collapse; font-size: .87rem; min-width: 480px; }
caption { text-align: left; font-family: var(--mono); font-size: .7rem; letter-spacing: .2em; text-transform: uppercase; color: var(--ink-quiet); padding: 12px 14px; background: var(--void-2); }
th, td { text-align: left; padding: 9px 14px; border-top: 1px solid var(--glass-edge); }
th { font-family: var(--mono); font-size: .68rem; letter-spacing: .16em; text-transform: uppercase; color: var(--ink-quiet); font-weight: 400; }
td.mono { font-family: var(--mono); color: var(--phosphor); white-space: nowrap; }
td.addr { font-family: var(--mono); color: var(--amber-lit); font-variant-numeric: tabular-nums; white-space: nowrap; }
tbody tr:hover td { background: rgba(64,255,64,.035); }
pre {
margin: 0;
padding: 18px clamp(14px, 3vw, 24px);
background: var(--void-2);
border: 1px solid var(--glass-edge);
overflow-x: auto;
font-family: var(--mono);
font-size: .82rem;
line-height: 1.7;
color: var(--ink);
}
pre b { color: var(--phosphor); font-weight: 400; }
pre i { color: var(--ink-quiet); font-style: normal; }
.two-col { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 22px; }
.callout {
border-left: 2px solid var(--hazard);
background: linear-gradient(90deg, rgba(176,48,42,.12), transparent 70%);
padding: 14px 18px;
margin-top: 26px;
}
.callout p { margin: 0; font-size: .9rem; }
.callout strong { font-family: var(--mono); color: var(--hazard-lit); letter-spacing: .04em; }
.reach { display: flex; justify-content: center; }
.reach-fig { margin: 0; width: min(430px, 100%); }
.reach-glass { position: relative; aspect-ratio: 4 / 3; background: var(--glass); border: 1px solid var(--glass-edge); overflow: hidden; }
.reach-band {
position: absolute;
left: 0; right: 0;
height: 50%;
background: repeating-linear-gradient(-45deg, rgba(176,48,42,.16) 0 5px, transparent 5px 11px);
display: flex; align-items: center; justify-content: center;
font-family: var(--mono); font-size: .66rem; letter-spacing: .14em; text-transform: uppercase; color: #e08a7e;
}
.reach-band.is-top { top: 0; border-bottom: 1px dashed rgba(176,48,42,.6); }
.reach-band.is-bot { bottom: 0; }
.reach-strip { position: absolute; left: 0; right: 0; height: 8px; display: flex; gap: 2px; z-index: 2; }
.reach-strip.is-top { top: 0; }
.reach-strip.is-bot { bottom: 0; }
.reach-strip i { flex: 1; background: var(--hazard); display: block; }
.reach-strip.is-lit i:nth-child(2) { background: var(--hazard-lit); }
.reach-fig figcaption { margin-top: 12px; font-size: .84rem; color: var(--ink-quiet); }
.reach-fig figcaption b { font-family: var(--mono); font-weight: 400; color: var(--phosphor-2); }
.pos-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(148px, 1fr)); gap: 18px; }
.pos { margin: 0; }
.pos-box { position: relative; aspect-ratio: 16 / 9; background: var(--void-2); border: 1px solid var(--glass-edge); }
.pos-box i, .pos-box u { position: absolute; display: block; }
.pos-box i { width: 16.7%; height: 22.2%; background: #2a100e; border: 1px solid #7d3029; }
.pos-box u { width: 16.9%; height: 40%; background: #251d08; border: 1px solid var(--amber); }
.pos-box i.ul { left: 0; top: 0; }
.pos-box i.uc { left: 41.65%; top: 0; }
.pos-box i.ur { right: 0; top: 0; }
.pos-box i.ll { left: 0; bottom: 0; }
.pos-box i.lr { right: 0; bottom: 0; }
.pos-box u { left: 41.55%; bottom: 0; }
.p-left u { left: 0; bottom: 0; }
.p-left i.ll { left: 16.9%; }
.p-right u { left: auto; right: 0; bottom: 0; }
.p-right i.lr { right: 16.9%; }
.p-midleft u { left: 0; bottom: auto; top: 30%; }
.p-midright u { left: auto; right: 0; bottom: auto; top: 30%; }
.pos figcaption { margin-top: 9px; font-family: var(--mono); font-size: .72rem; letter-spacing: .12em; color: var(--phosphor); }
.pos figcaption small { display: block; font-family: var(--sans); font-size: .76rem; letter-spacing: 0; color: var(--ink-quiet); margin-top: 2px; }
footer {
margin-top: 72px;
padding-top: 22px;
border-top: 1px solid var(--glass-edge);
font-family: var(--mono);
font-size: .74rem;
letter-spacing: .06em;
color: var(--ink-quiet);
display: flex;
flex-wrap: wrap;
gap: 8px 24px;
}
@media (prefers-reduced-motion: reduce) { * { transition: none !important; } }
</style>
<div class="wrap">
<header class="masthead">
<p class="eyebrow">Virtual World Entertainment · Tesla II pod · BT 4.11</p>
<h1>BattleTech<br>Controls Map</h1>
<p class="lede">
Every input the pod answers to. <strong>The keyboard is the button
board</strong>: the letter and number rows are the MFD banks laid out
where they sit on the panel, and flight moved to the number pad so the
board stays free. Every one of the 72 buttons is also a mouse target on
the cockpit itself.
</p>
<p class="provenance">Generated from the default <code>bindings.txt</code> · all of it rebindable</p>
</header>
<dl class="glance">
<div>
<dt>Fly</dt>
<dd>Num 8 2 4 6<small>stick · 7/9 pedals · 0 fires · 5 all-stop</small></dd>
</div>
<div>
<dt>Throttle</dt>
<dd>Shift / Ctrl<small>walks the lever; it sticks where you leave it</small></dd>
</div>
<div>
<dt>Reverse</dt>
<dd>Alt<small>the throttle handle's own button</small></dd>
</div>
<div>
<dt>Control mode</dt>
<dd>F7<small>BAS → MID → ADV. You spawn in BASIC</small></dd>
</div>
</dl>
<div class="callout">
<p>
<strong>YOU SPAWN IN BASIC.</strong> In BASIC the stick steers and the
pedals do nothing. Press <b>F7</b> once for MID, where the pedals steer
and the stick twists the torso — the full pod experience. If "turning
doesn't work", this is why, not a bug. (Control mode is not the menu's
experience setting; that one changes the simulation, not your controls.)
</p>
</div>
<section>
<h2>Keyboard</h2>
<p class="sub">
Colour is the bank each key drives: green flies the mech, red is an MFD
button bank, amber is a column beside the map. Dark keys are unbound and
free to take. Hover a key to read what it does.
</p>
<div class="panel">
<div class="kb" id="kb"></div>
<div class="legend">
<span><i class="lg-flight"></i> flight &amp; fire</span>
<span><i class="lg-mfd"></i> MFD button banks</span>
<span><i class="lg-col"></i> map columns</span>
<span><i class="lg-off"></i> unbound</span>
</div>
</div>
<div class="readout" id="rd-kb" data-idle="true">
<span class="rd-name">Select a key</span>
<span class="rd-desc">Hover a key to read what it drives.</span>
</div>
<div class="callout">
<p>
<strong>COOLANT VALVES BITE.</strong> The valve detents on
<b>1 2 3 Q W E</b> run 1 5 50 (max) <b>0 (CLOSED)</b> back to
1. One press past max shuts that loop off and everything on it
overheats. Boosting one loop starves the others — the supply is
shared. <b>4</b> is the flush (hold it), <b>R</b> re-balances every
valve.
</p>
</div>
</section>
<section>
<h2>Where the keys live on the panel</h2>
<p class="sub">
Each MFD carries eight buttons: a four-wide top row and a four-wide row
beneath it. The keyboard rows run left to right across the upper
displays, then the lower pair — the same order your hand finds them in
the pod.
</p>
<div class="panel">
<div class="cockpit">
<div class="mfd">
<div class="mfd-strip"><b>1</b><b>2</b><b>3</b><b>4</b></div>
<div class="mfd-face"><em>Heat / coolant</em><small>0x2F 0x2C<br>0x2B 0x28</small></div>
<div class="mfd-strip"><b>Q</b><b>W</b><b>E</b><b>R</b></div>
</div>
<div class="mfd">
<div class="mfd-strip"><b>5</b><b>6</b><b>7</b><b>8</b></div>
<div class="mfd-face"><em>Engineering</em><small>0x27 0x24<br>0x23 0x20</small></div>
<div class="mfd-strip"><b>T</b><b>Y</b><b>U</b><b>I</b></div>
</div>
<div class="mfd">
<div class="mfd-strip"><b>9</b><b>0</b><b>-</b><b>=</b></div>
<div class="mfd-face"><em>Comm / hotbox</em><small>0x37 0x34<br>0x33 0x30</small></div>
<div class="mfd-strip"><b>O</b><b>P</b><b>[</b><b>]</b></div>
</div>
<div class="mfd">
<div class="mfd-strip"><b>A</b><b>S</b><b>D</b><b>F</b></div>
<div class="mfd-face"><em>Left Weapons</em><small>0x0F 0x0C<br>0x0B 0x08</small></div>
<div class="mfd-strip"><b>Z</b><b>X</b><b>C</b><b>V</b></div>
</div>
<div class="mfd mfd-map">
<div class="map-body">
<div class="map-col"><b>F1</b><b>F2</b><b>F3</b><b>F4</b><b>F5</b><b>F6</b></div>
<div class="map-face">
<em>Map · radar</em>
<small>
zoom in / out<br>
thermal · CROUCH<br>
searchlight · display<br>
0x10 0x15
</small>
</div>
<div class="map-col"><b>F7</b><b>F8</b><b>F9</b><b>F10</b><b>F11</b><b>F12</b></div>
</div>
</div>
<div class="mfd">
<div class="mfd-strip"><b>H</b><b>J</b><b>K</b><b>L</b></div>
<div class="mfd-face"><em>Right Weapons</em><small>0x07 0x04<br>0x03 0x00</small></div>
<div class="mfd-strip"><b>N</b><b>M</b><b>,</b><b>.</b></div>
</div>
<p class="cockpit-note">
G and B stay unbound on purpose: they are the physical gap between
the lower clusters. The right-hand map column is control mode (F7)
and Generators AD (F9F12); F8 is a column slot the pod never
wired.
</p>
</div>
</div>
<div class="tbl-scroll" style="margin-top:26px">
<table>
<caption>The weapon and engineering displays are page-gated</caption>
<tbody>
<tr><th>Quad page</th><td>The outer buttons jump straight to a populated Eng page.</td></tr>
<tr><th>Eng page</th><td>Generator select AD, bus mode, coolant toggle, trigger configure, and <b>eject</b>.</td></tr>
<tr><th>Jams &amp; fires</th><td>A weapon gone quiet with ammo left has <b>jammed</b> — heat jams ballistics, authentically. Its Eng-page buttons <b>flash</b>. <b>Tap eject</b> to clear the round; <b>hold ~3 s</b> to jettison a burning bay before it detonates. An ammo bay fire gives you about ten seconds and it will kill you.</td></tr>
</tbody>
</table>
</div>
</section>
<section>
<h2>Gamepad</h2>
<p class="sub">An Xbox-compatible pad drives all five axes and the grip buttons. Hover a control for its RIO address.</p>
<div class="panel pad-shell">
<svg class="pad" viewBox="0 0 700 400" role="img" aria-label="Xbox controller with BattleTech bindings">
<rect class="ctl" data-name="LB — Look left" data-addr="0x44" data-desc="Hat left" x="112" y="60" width="86" height="26" rx="13"></rect>
<rect class="ctl" data-name="RB — Look right" data-addr="0x43" data-desc="Hat right" x="502" y="60" width="86" height="26" rx="13"></rect>
<rect class="ctl" data-name="LT — Left pedal" data-addr="axis" data-desc="Turn left (MID/ADV control mode)" x="132" y="26" width="66" height="24" rx="12"></rect>
<rect class="ctl" data-name="RT — Right pedal" data-addr="axis" data-desc="Turn right (MID/ADV control mode)" x="502" y="26" width="66" height="24" rx="12"></rect>
<text class="lbl-q" x="132" y="20">LT · left pedal</text>
<text class="lbl-q" x="502" y="20">RT · right pedal</text>
<text class="lbl-q" x="112" y="102">LB · look left</text>
<text class="lbl-q" x="486" y="102">RB · look right</text>
<path class="body" d="M196 112 h308 c46 0 78 26 92 74 l32 108 c14 48 -10 82 -52 82 c-36 0 -58 -22 -74 -54 l-22 -44 h-180 l-22 44 c-16 32 -38 54 -74 54 c-42 0 -66 -34 -52 -82 l32 -108 c14 -48 46 -74 92 -74 z"></path>
<circle class="ctl" data-name="Left stick — torso twist / aim" data-addr="axes" data-desc="Pod-faithful: X twists the torso, Y aims it (deadzone 0.24)" cx="248" cy="176" r="34"></circle>
<text class="cap" x="222" y="181">STICK</text>
<path class="lead" d="M214 176 H150"></path>
<text class="lbl-q" x="60" y="180">torso twist / aim</text>
<g class="ctl" data-name="D-pad — look &amp; torso centre" data-addr="0x410x44" data-desc="Up = torso centre, down = look behind, left/right = look">
<rect x="286" y="266" width="24" height="66" rx="4"></rect>
<rect x="265" y="287" width="66" height="24" rx="4"></rect>
</g>
<path class="lead" d="M265 299 H180"></path>
<text class="lbl-q" x="86" y="303">look · torso centre</text>
<circle class="ctl" data-name="Right stick Y — Throttle" data-addr="axis" data-desc="Walks the throttle lever; the position sticks" cx="404" cy="266" r="32"></circle>
<text class="cap" x="381" y="271">THRTL</text>
<path class="lead" d="M436 266 H520"></path>
<text class="lbl-q" x="524" y="270">throttle</text>
<circle class="ctl" data-name="Y — Torso centre" data-addr="0x42" data-desc="Re-centre the torso" cx="470" cy="146" r="20"></circle>
<circle class="ctl" data-name="X — Upper thumb" data-addr="0x47" data-desc="Fire group" cx="428" cy="180" r="20"></circle>
<circle class="ctl" data-name="B — Reverse thrust" data-addr="0x3F" data-desc="The throttle handle's button" cx="512" cy="180" r="20"></circle>
<circle class="ctl" data-name="A — Main trigger" data-addr="0x40" data-desc="Fires the selected weapon group" cx="470" cy="214" r="20"></circle>
<text class="lbl" x="465" y="151">Y</text>
<text class="lbl" x="423" y="185">X</text>
<text class="lbl" x="507" y="185">B</text>
<text class="lbl" x="465" y="219">A</text>
<path class="lead" d="M490 214 L556 236"></path>
<text class="lbl-q" x="560" y="240">A · main trigger</text>
<text class="lbl-q" x="560" y="196">X upper · B reverse · Y torso centre</text>
<path class="lead" d="M532 180 H556"></path>
</svg>
</div>
<div class="readout" id="rd-pad" data-idle="true">
<span class="rd-name">Select a control</span>
<span class="rd-desc">Hover the diagram to read its binding.</span>
</div>
<p class="sub" style="margin-top:22px">
<b>Flight sticks, HOTAS and pedals:</b> run <code>joyconfig.bat</code>
once. A wizard asks you to move each control in turn, writes the
bindings, and drops you into the solo menu. It only rewrites its own
section of <code>bindings.txt</code>, so hand edits elsewhere survive.
</p>
</section>
<section>
<h2>The mouse</h2>
<p class="sub">
Every button on every display is clickable and lights when the game
commands its lamp — and each reaches in <em style="font-style:normal;color:var(--phosphor-2)">under the glass</em>
beside it, so the picture is the press target. The lit strip at the edge
is only the lamp. Right-click latches a button held.
</p>
<div class="panel">
<div class="reach">
<figure class="reach-fig">
<div class="reach-glass">
<span class="reach-strip is-top is-lit" aria-hidden="true"><i></i><i></i><i></i><i></i></span>
<span class="reach-band is-top">press — top row</span>
<span class="reach-band is-bot">press — bottom row</span>
<span class="reach-strip is-bot" aria-hidden="true"><i></i><i></i><i></i><i></i></span>
</div>
<figcaption>
One MFD. Four buttons above the glass and four below, each reaching
<b>half the pane</b> deep — so the two banks meet in the middle and
the whole face answers the mouse. Only a thin strip clears the edge
to show its lamp.
</figcaption>
</figure>
</div>
</div>
<h3>Arranging the glass</h3>
<p class="sub">
The pod bolted its displays down at one size. On a desktop panel there is
room to trade viewscreen for instrument, so all six are yours to size —
and the map to place. Set these in <code>content\environ.ini</code>,
which the game writes on first run with every option documented.
</p>
<div class="pos-grid">
<figure class="pos">
<div class="pos-box p-center"><i class="ul"></i><i class="uc"></i><i class="ur"></i><i class="ll"></i><i class="lr"></i><u></u></div>
<figcaption>CENTER<small>as the pod had it</small></figcaption>
</figure>
<figure class="pos">
<div class="pos-box p-left"><i class="ul"></i><i class="uc"></i><i class="ur"></i><i class="ll"></i><i class="lr"></i><u></u></div>
<figcaption>LEFT<small>bottom corner; the MFD slides inboard</small></figcaption>
</figure>
<figure class="pos">
<div class="pos-box p-right"><i class="ul"></i><i class="uc"></i><i class="ur"></i><i class="ll"></i><i class="lr"></i><u></u></div>
<figcaption>RIGHT<small>the same, other side</small></figcaption>
</figure>
<figure class="pos">
<div class="pos-box p-midleft"><i class="ul"></i><i class="uc"></i><i class="ur"></i><i class="ll"></i><i class="lr"></i><u></u></div>
<figcaption>MIDLEFT<small>halfway up; leaves the bottom row alone</small></figcaption>
</figure>
<figure class="pos">
<div class="pos-box p-midright"><i class="ul"></i><i class="uc"></i><i class="ur"></i><i class="ll"></i><i class="lr"></i><u></u></div>
<figcaption>MIDRIGHT<small>halfway up, right</small></figcaption>
</figure>
</div>
<div class="tbl-scroll" style="margin-top:26px">
<table>
<caption>environ.ini — display layout</caption>
<tbody>
<tr><th>BT_MFD_SCALE</th><td>All five MFDs, as a percentage of pod size. <span style="color:var(--ink-quiet)">25200, default 100</span></td></tr>
<tr><th>BT_MFD_SCALE_<span style="color:var(--phosphor)">xx</span></th><td>Any one on its own — <span class="addr" style="font-family:var(--mono)">UL UC UR LL LR</span>. Unset inherits the line above.</td></tr>
<tr><th>BT_RADAR_SCALE</th><td>The map alone.</td></tr>
<tr><th>BT_RADAR_POS</th><td class="mono">CENTER · LEFT · RIGHT · MIDLEFT · MIDRIGHT</td></tr>
<tr><th>BT_GLASS_PANELS</th><td>1 = break every display into its own desktop window instead of the composited cockpit.</td></tr>
</tbody>
</table>
</div>
<div class="callout">
<p>
<strong>Ask for too much and the layout wins.</strong> The surround
bands grow to fit whatever you ask for, and a map in a bottom corner
pushes the lower MFD beside it inboard — nothing overlaps and nothing
comes out stretched.
</p>
</div>
<h3>Filling the screen</h3>
<p class="sub" style="margin-bottom:0">
The cockpit fits itself to whatever window it gets at one uniform scale,
so drag it to any shape and nothing distorts — a window that isn't the
cockpit's shape just gets black bars. Launch with <code>-fit</code> for
borderless over the whole monitor.
</p>
</section>
<section>
<h2>Rebinding</h2>
<p class="sub">
<code>content\bindings.txt</code> is written with the full default layout
on first run. Change a line, restart. Delete the file to restore these
defaults — worth doing after an update, since an existing file is never
overwritten.
</p>
<div class="two-col">
<pre><i># grammar</i>
<b>key</b> &lt;KEY&gt; <b>button</b> &lt;addr&gt;
<b>key</b> &lt;KEY&gt; <b>axis</b> &lt;chan&gt; deflect &lt;+|-&gt; &lt;rate&gt;
<b>key</b> &lt;KEY&gt; <b>axis</b> &lt;chan&gt; slew &lt;+|-&gt; &lt;rate&gt;
<b>key</b> &lt;KEY&gt; <b>axis</b> &lt;chan&gt; set &lt;value&gt;
<b>pad</b> &lt;BTN&gt; <b>button</b> &lt;addr&gt;
<b>padaxis</b> &lt;SRC&gt; <b>axis</b> &lt;chan&gt; [invert] [slew &lt;r&gt;]
<i># deflect springs back, slew walks and sticks
# (that is the throttle lever), set jumps</i></pre>
<pre><i># examples</i>
<b>key</b> G <b>button</b> 0x0E <i># regroup weapon 1 on G</i>
<b>key</b> B <b>button</b> 0x41 <i># look behind on B</i>
<i># put driving back on WASD if you prefer</i>
<b>key</b> W <b>axis</b> Throttle slew + 0.7
<b>key</b> S <b>axis</b> Throttle slew - 0.7
<b>key</b> A <b>axis</b> LeftPedal deflect + 2.5
<b>key</b> D <b>axis</b> RightPedal deflect + 2.5</pre>
</div>
<div class="tbl-scroll" style="margin-top:22px">
<table>
<caption>Reference — channels, addresses, key names</caption>
<tbody>
<tr><th>Channels</th><td class="mono">Throttle · LeftPedal · RightPedal · JoystickX · JoystickY · Turn</td></tr>
<tr><th>Buttons</th><td class="addr">0x00 0x47</td></tr>
<tr><th>No function</th><td class="addr">0x16 0x17 0x19 0x1E 0x1F 0x380x3E <span style="color:var(--ink-quiet);font-family:var(--sans)">— the pod never wired these</span></td></tr>
<tr><th>Key names</th><td class="mono">AZ · 09 · F1F12 · NUMPAD09 · NUMPADDOT · UP DOWN LEFT RIGHT · SPACE · LSHIFT LCTRL LALT · MINUS EQUALS COMMA PERIOD LBRACKET RBRACKET BACKTICK</td></tr>
<tr><th>Pad</th><td class="mono">A B X Y · DPAD_UP/DOWN/LEFT/RIGHT · LB RB · BACK START · LS RS</td></tr>
<tr><th>Pad axes</th><td class="mono">LX LY RX RY LT RT</td></tr>
</tbody>
</table>
</div>
<div class="callout">
<p>
<strong>A BOUND KEY LOSES ITS 1995 MEANING.</strong> Binding a key
removes it from the authentic typed-hotkey channel so it cannot
double-dispatch — and this board binds nearly everything, so the old
typed hotkeys (5 = Quad page, z = Eng1, t/y/u/i/o = pilot select, +/
= target zoom) are given up by design. Unbind a key to get its 1995
meaning back. Same rule for the built-ins: <b>`</b> is the view
toggle, and J/K/L cycle MFD preset pages only while unbound.
</p>
</div>
</section>
<footer>
<span>BattleTech 4.11</span>
<span>gitea.mysticmachines.com/VWE/BT411</span>
<span>docs/CONTROLS.md · CONTROLS.txt ships with the game</span>
<span>RGB keyboards mirror the pod lamps · BT_KEYLIGHT=0 to disable</span>
</footer>
</div>
<script>
(function () {
"use strict";
var F = "flight", M = "mfd", C = "column", X = null, SP = "spacer";
var rows = [
[
["Esc", "", X], [SP],
["F1", "MAP+", C, "0x10", "Map zoom in"],
["F2", "MAP-", C, "0x11", "Map zoom out"],
["F3", "IR", C, "0x12", "Thermal sight"],
["F4", "CROUCH", C, "0x13", "Duck — present a smaller target"], [SP],
["F5", "LAMP", C, "0x14", "Searchlight"],
["F6", "DISP", C, "0x15", "Cycle the secondary display"],
["F7", "MODE", C, "0x18", "Cycle control mode: BAS / MID / ADV"],
["F8", "", C, "0x19", "Column slot the pod never wired"], [SP],
["F9", "GEN A", C, "0x1A", "Generator A on/off"],
["F10", "GEN B", C, "0x1B", "Generator B on/off"],
["F11", "GEN C", C, "0x1C", "Generator C on/off"],
["F12", "GEN D", C, "0x1D", "Generator D on/off"]
],
[
["`", "VIEW", F, "", "Cockpit / chase camera toggle"],
["1", "VALVE", M, "0x2F", "Condenser 1 valve — detents 1-5-50-CLOSED"],
["2", "VALVE", M, "0x2E", "Condenser 2 valve"],
["3", "VALVE", M, "0x2D", "Condenser 3 valve"],
["4", "FLUSH", M, "0x2C", "Coolant flush — HOLD to dump the reservoir"],
["5", "", M, "0x27", "Engineering · top 1"], ["6", "", M, "0x26", "Engineering · top 2"],
["7", "", M, "0x25", "Engineering · top 3"], ["8", "", M, "0x24", "Engineering · top 4"],
["9", "", M, "0x37", "Comm / hotbox · top 1"], ["0", "", M, "0x36", "Comm / hotbox · top 2"],
["-", "", M, "0x35", "Comm / hotbox · top 3"], ["=", "", M, "0x34", "Comm / hotbox · top 4"],
["Bksp", "", X, "", "", "w2"]
],
[
["Tab", "", X, "", "", "w15"],
["Q", "VALVE", M, "0x2B", "Condenser 4 valve"], ["W", "VALVE", M, "0x2A", "Condenser 5 valve"],
["E", "VALVE", M, "0x29", "Condenser 6 valve"], ["R", "BALANCE", M, "0x28", "Balance every coolant valve"],
["T", "", M, "0x23", "Engineering · lower 1"], ["Y", "", M, "0x22", "Engineering · lower 2"],
["U", "", M, "0x21", "Engineering · lower 3"], ["I", "", M, "0x20", "Engineering · lower 4"],
["O", "", M, "0x33", "Comm / hotbox · lower 1"], ["P", "", M, "0x32", "Comm / hotbox · lower 2"],
["[", "", M, "0x31", "Comm / hotbox · lower 3"], ["]", "", M, "0x30", "Comm / hotbox · lower 4"],
["\\", "", X, "", "", "w15"]
],
[
["Caps", "", X, "", "", "w175"],
["A", "", M, "0x0F", "Left Weapons · top 1"], ["S", "", M, "0x0E", "Left Weapons · top 2"],
["D", "", M, "0x0D", "Left Weapons · top 3"], ["F", "", M, "0x0C", "Left Weapons · top 4"],
["G", "GAP", X, "", "Panel gap between the lower clusters — free"],
["H", "", M, "0x07", "Right Weapons · top 1"], ["J", "", M, "0x06", "Right Weapons · top 2"],
["K", "", M, "0x05", "Right Weapons · top 3"], ["L", "", M, "0x04", "Right Weapons · top 4"],
[";", "", X], ["'", "", X], ["Enter", "", X, "", "", "w225"]
],
[
["Shift", "THR +", F, "axis", "Throttle up — walks the lever, position holds", "w225"],
["Z", "", M, "0x0B", "Left Weapons · lower 1"], ["X", "", M, "0x0A", "Left Weapons · lower 2"],
["C", "", M, "0x09", "Left Weapons · lower 3"], ["V", "", M, "0x08", "Left Weapons · lower 4"],
["B", "GAP", X, "", "Panel gap between the lower clusters — free"],
["N", "", M, "0x03", "Right Weapons · lower 1"], ["M", "", M, "0x02", "Right Weapons · lower 2"],
[",", "", M, "0x01", "Right Weapons · lower 3"], [".", "", M, "0x00", "Right Weapons · lower 4"],
["/", "", X], ["Shift", "THR +", F, "axis", "Throttle up", "w275"]
],
[
["Ctrl", "THR ", F, "axis", "Throttle down — walks the lever, position holds", "w125"],
["Win", "", X, "", "", "w125"],
["Alt", "REV", F, "0x3F", "Reverse thrust — the throttle handle's button", "w125"],
["Space", "TRIGGER", F, "0x40", "Main trigger", "w6"],
["Alt", "REV", F, "0x3F", "Reverse thrust", "w125"],
["Fn", "", X, "", "", "w125"],
["Menu", "", X, "", "", "w125"],
["Ctrl", "THR ", F, "axis", "Throttle down", "w125"]
]
];
var navRows = [
[["Ins", "", X], ["Home", "", X], ["PgUp", "", X]],
[["Del", "", X], ["End", "", X], ["PgDn", "", X]],
"grow",
[["↑", "CENTRE", F, "0x42", "Torso centre"]],
[["←", "LOOK", F, "0x44", "Look left"], ["↓", "BEHIND", F, "0x41", "Look behind"], ["→", "LOOK", F, "0x43", "Look right"]]
];
var padKeys = [
["Num", "", X], ["/", "", X], ["*", "", X], ["", "", X],
["7", "L PED", F, "axis", "Left pedal — turns in MID/ADV"],
["8", "AIM UP", F, "axis", "Torso aim up"],
["9", "R PED", F, "axis", "Right pedal — turns in MID/ADV"],
["+", "", X, "", "", "tall"],
["4", "TWIST", F, "axis", "Torso twist left"],
["5", "ALL STOP", F, "axis", "Throttle to zero"],
["6", "TWIST", F, "axis", "Torso twist right"],
["1", "PINKY", F, "0x45", "Fire — pinky"],
["2", "AIM DN", F, "axis", "Torso aim down"],
["3", "MIDDLE", F, "0x46", "Fire — middle thumb"],
["Ent", "", X, "", "", "tall"],
["0", "TRIGGER", F, "0x40", "Main trigger", "pad0"],
[".", "UPPER", F, "0x47", "Fire — upper thumb"]
];
var kbEl = document.getElementById("kb");
var rdKb = document.getElementById("rd-kb");
var rdPad = document.getElementById("rd-pad");
function makeKey(def) {
if (def[0] === SP) {
var gap = document.createElement("div");
gap.className = "spacer";
gap.setAttribute("aria-hidden", "true");
return gap;
}
var cap = def[0], sub = def[1], role = def[2], addr = def[3] || "", desc = def[4] || "", w = def[5];
var el = document.createElement(role ? "button" : "div");
el.className = "key" + (w ? " " + w : "");
if (role) {
el.type = "button";
el.setAttribute("data-role", role);
el.setAttribute("data-name", cap);
el.setAttribute("data-addr", addr);
el.setAttribute("data-desc", desc);
} else if (desc) {
el.setAttribute("data-name", cap);
el.setAttribute("data-desc", desc);
el.title = desc;
}
var main = document.createElement("span");
main.textContent = cap;
el.appendChild(main);
if (sub) {
var s = document.createElement("span");
s.className = "k-sub";
s.textContent = sub;
el.appendChild(s);
}
return el;
}
var bank = document.createElement("div");
bank.className = "kb-bank";
var main = document.createElement("div");
main.className = "kb-main";
rows.forEach(function (row, index) {
var r = document.createElement("div");
r.className = "kb-row" + (index === 1 || index === 3 ? " zone" : "");
row.forEach(function (def) { r.appendChild(makeKey(def)); });
main.appendChild(r);
});
bank.appendChild(main);
var nav = document.createElement("div");
nav.className = "kb-side";
var navWrap = document.createElement("div");
navWrap.className = "kb-nav";
navRows.forEach(function (row) {
if (row === "grow") {
var g = document.createElement("div");
g.className = "grow";
navWrap.appendChild(g);
return;
}
var r = document.createElement("div");
r.className = "kb-row";
row.forEach(function (def) { r.appendChild(makeKey(def)); });
navWrap.appendChild(r);
});
nav.appendChild(navWrap);
bank.appendChild(nav);
var side = document.createElement("div");
side.className = "kb-side";
var pad = document.createElement("div");
pad.className = "kb-pad";
padKeys.forEach(function (def) { pad.appendChild(makeKey(def)); });
side.appendChild(pad);
bank.appendChild(side);
kbEl.appendChild(bank);
function show(readout, name, addr, desc) {
readout.setAttribute("data-idle", "false");
readout.innerHTML = "";
var n = document.createElement("span");
n.className = "rd-name";
n.textContent = name;
readout.appendChild(n);
if (addr) {
var a = document.createElement("span");
a.className = "rd-addr";
a.textContent = addr;
readout.appendChild(a);
}
if (desc) {
var d = document.createElement("span");
d.className = "rd-desc";
d.textContent = desc;
readout.appendChild(d);
}
}
function bind(root, readout, selector) {
if (!root) return;
var active = null;
function handle(e) {
var t = e.target.closest(selector);
if (!t || !t.getAttribute("data-name")) return;
if (active) active.classList.remove("is-active");
active = t;
t.classList.add("is-active");
show(readout, t.getAttribute("data-name"), t.getAttribute("data-addr"), t.getAttribute("data-desc"));
}
root.addEventListener("pointerover", handle);
root.addEventListener("focusin", handle);
root.addEventListener("click", handle);
}
var byCap = {};
Array.prototype.forEach.call(main.querySelectorAll(".key"), function (el) {
var cap = el.firstChild ? el.firstChild.textContent : "";
if (cap && !byCap[cap]) byCap[cap] = el;
});
var groups = [
{ label: "Map left column", kind: "g-col", top: ["F1", "F6"] },
{ label: "Map right column", kind: "g-col", top: ["F7", "F12"] },
{ label: "Heat / coolant", kind: "g-mfd", top: ["1", "4"], bottom: ["Q", "R"] },
{ label: "Engineering", kind: "g-mfd", top: ["5", "8"], bottom: ["T", "I"] },
{ label: "Comm / hotbox", kind: "g-mfd", top: ["9", "="], bottom: ["O", "]"] },
{ label: "Left Weapons", kind: "g-mfd", top: ["A", "F"], bottom: ["Z", "V"] },
{ label: "Right Weapons", kind: "g-mfd", top: ["H", "L"], bottom: ["N", "."] }
];
function drawGroups() {
var old = kbEl.querySelector(".kb-groups");
if (old) old.remove();
var host = document.createElement("div");
host.className = "kb-groups";
var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
host.appendChild(svg);
var base = kbEl.getBoundingClientRect();
var padPx = 3;
function box(a, b) {
if (!a || !b) return null;
var ra = a.getBoundingClientRect(), rb = b.getBoundingClientRect();
return {
l: ra.left - base.left - padPx,
r: rb.right - base.left + padPx,
t: ra.top - base.top - padPx,
b: ra.bottom - base.top + padPx
};
}
groups.forEach(function (g) {
var top = box(byCap[g.top[0]], byCap[g.top[1]]);
if (!top) return;
var bot = g.bottom ? box(byCap[g.bottom[0]], byCap[g.bottom[1]]) : null;
var d;
if (bot) {
d = "M" + top.l + " " + top.t + "H" + top.r + "V" + bot.t +
"H" + bot.r + "V" + bot.b + "H" + bot.l + "V" + top.b + "H" + top.l + "Z";
} else {
d = "M" + top.l + " " + top.t + "H" + top.r + "V" + top.b + "H" + top.l + "Z";
}
var path = document.createElementNS("http://www.w3.org/2000/svg", "path");
path.setAttribute("d", d);
path.setAttribute("class", g.kind);
svg.appendChild(path);
var label = document.createElement("span");
label.className = "g-label " + g.kind;
label.textContent = g.label;
label.style.left = top.l + "px";
label.style.top = top.t + "px";
host.appendChild(label);
});
kbEl.appendChild(host);
}
drawGroups();
window.addEventListener("resize", drawGroups);
if (document.fonts && document.fonts.ready) document.fonts.ready.then(drawGroups);
bind(kbEl, rdKb, ".key");
bind(document.querySelector("svg.pad"), rdPad, ".ctl");
})();
</script>