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>
1062 lines
45 KiB
HTML
1062 lines
45 KiB
HTML
<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 & 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 A–D (F9–F12); 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 A–D, bus mode, coolant toggle, trigger configure, and <b>eject</b>.</td></tr>
|
||
<tr><th>Jams & 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 & torso centre" data-addr="0x41–0x44" 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)">25–200, 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> <KEY> <b>button</b> <addr>
|
||
<b>key</b> <KEY> <b>axis</b> <chan> deflect <+|-> <rate>
|
||
<b>key</b> <KEY> <b>axis</b> <chan> slew <+|-> <rate>
|
||
<b>key</b> <KEY> <b>axis</b> <chan> set <value>
|
||
<b>pad</b> <BTN> <b>button</b> <addr>
|
||
<b>padaxis</b> <SRC> <b>axis</b> <chan> [invert] [slew <r>]
|
||
|
||
<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 0x38–0x3E <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">A–Z · 0–9 · F1–F12 · NUMPAD0–9 · 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>
|