CONTROLS.md gained this in the port; the page that ships beside it did not, and the page is the one people actually look at. A "Bring your own stick" section before Rebinding: what DirectInput is and why it needs telling what its axes are for, joyconfig.bat as the answer, and the four beats of running it. The callout carries the reason the wizard reads direction rather than asking you to know it, since that is the part that looks like a quirk until it is explained. Then what it writes beside the grammar that produced it, and a table for the two rules the pod's shape asks for - the signed Pedals axis working the pedal pair, and a real lever owning the throttle channel. The reference tables were left incomplete by the port and are now whole: Pedals joins the axis list, the joy rows join the grammar block, and DirectInput's own axis names get a row of their own. Written in the page's existing components - glance, callout, two-col, tbl-scroll - rather than new ones. The two <kbd> elements I reached for first are not styled anywhere on this page and would have rendered as browser defaults, so they are <code> like every other inline literal here. No version change: 4.12.7 is republished with the page in it. Verified by rendering the packed CONTROLS.html headless at 1280 wide and reading the section back - heading, lede, the four-panel strip, callout, both code blocks, both tables, and the reference rows all sit in the page's own idiom, and the tag balance is unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1641 lines
88 KiB
HTML
1641 lines
88 KiB
HTML
<title>Red Planet 4.12.7 — 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;
|
||
--rust: #8a3a1e;
|
||
--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; /* keycap unit */
|
||
--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;
|
||
}
|
||
|
||
/* faint CRT vignette, no animation */
|
||
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 ---------- */
|
||
|
||
.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: 62ch; 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);
|
||
}
|
||
|
||
/* ---------- at-a-glance ---------- */
|
||
|
||
.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; }
|
||
|
||
/* ---------- sections ---------- */
|
||
|
||
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);
|
||
}
|
||
|
||
.sub { margin: 0 0 26px; max-width: 60ch; color: var(--ink-quiet); font-size: .93rem; }
|
||
|
||
/* ---------- panel frame ---------- */
|
||
|
||
.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 ---------- */
|
||
|
||
.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); }
|
||
|
||
/* ---------- keyboard ---------- */
|
||
|
||
.kb { display: flex; flex-direction: column; gap: 14px; min-width: 940px; position: relative; padding-top: 22px; }
|
||
|
||
/* cluster outlines drawn over the keys after layout */
|
||
.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 path.g-fly { stroke: var(--phosphor); }
|
||
|
||
.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-groups .g-label.g-fly { color: var(--phosphor); }
|
||
.kb-bank { display: flex; gap: 22px; }
|
||
.kb-main { display: flex; flex-direction: column; gap: var(--gap); }
|
||
.kb-row { display: flex; gap: var(--gap); }
|
||
/* room above the rows that start an MFD zone, for its label */
|
||
.kb-row.zone { margin-top: 18px; }
|
||
.kb-side { display: flex; flex-direction: column; gap: var(--gap); justify-content: flex-end; }
|
||
/* nav column: block sits with the number rows, arrows drop to the bottom */
|
||
.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="abort"] {
|
||
color: #fff;
|
||
border-color: #8a2f2a;
|
||
background: linear-gradient(180deg, #3a1210, #220a09);
|
||
}
|
||
|
||
.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); }
|
||
|
||
/* numpad: the tall +/Enter keys own two rows, 0 owns two columns */
|
||
.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 ---------- */
|
||
|
||
.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 ---------- */
|
||
|
||
.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; }
|
||
|
||
/* ---------- pod stick ---------- */
|
||
|
||
.stick-wrap {
|
||
display: grid;
|
||
grid-template-columns: minmax(230px, 300px) 1fr;
|
||
gap: 22px;
|
||
align-items: start;
|
||
}
|
||
|
||
.mfd-tile {
|
||
margin: 0;
|
||
border: 1px solid var(--glass-edge);
|
||
background: #040806;
|
||
padding: 0 0 14px;
|
||
}
|
||
|
||
.mfd-tile figcaption {
|
||
font-family: var(--mono);
|
||
font-size: .78rem;
|
||
letter-spacing: .22em;
|
||
text-transform: uppercase;
|
||
color: var(--phosphor);
|
||
text-align: center;
|
||
padding: 14px 10px 6px;
|
||
text-shadow: 0 0 14px rgba(64,255,64,.35);
|
||
}
|
||
|
||
svg.stick { width: 100%; height: auto; display: block; }
|
||
.stick-hidden { display: none; }
|
||
|
||
/* the cockpit's own trigger-status plate, with hotspots on its circles */
|
||
.stick-art { position: relative; padding: 4px 10px 10px; }
|
||
.stick-art img {
|
||
width: 100%;
|
||
height: auto;
|
||
display: block;
|
||
filter: drop-shadow(0 0 10px rgba(64,255,64,.25));
|
||
}
|
||
|
||
.stick-art .hot {
|
||
position: absolute;
|
||
left: var(--cx);
|
||
top: var(--cy);
|
||
width: 21%;
|
||
aspect-ratio: 1;
|
||
transform: translate(-50%, -50%);
|
||
border-radius: 50%;
|
||
border: 1px solid transparent;
|
||
background: transparent;
|
||
padding: 0;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: background .12s ease, border-color .12s ease;
|
||
}
|
||
|
||
.stick-art .hot span {
|
||
font-family: var(--mono);
|
||
font-size: .58rem;
|
||
letter-spacing: .06em;
|
||
text-transform: uppercase;
|
||
color: transparent;
|
||
transition: color .12s ease;
|
||
}
|
||
|
||
.stick-art .hot:hover,
|
||
.stick-art .hot:focus-visible,
|
||
.stick-art .hot.is-active {
|
||
background: rgba(64,255,64,.22);
|
||
border-color: var(--phosphor);
|
||
outline: none;
|
||
}
|
||
|
||
.stick-art .hot:hover span,
|
||
.stick-art .hot:focus-visible span,
|
||
.stick-art .hot.is-active span { color: #eafff0; }
|
||
|
||
/* the pod's own instruction plates */
|
||
.axis-art {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||
gap: 18px;
|
||
margin-top: 22px;
|
||
}
|
||
|
||
.art-tile {
|
||
margin: 0;
|
||
border: 1px solid var(--glass-edge);
|
||
background:
|
||
radial-gradient(120% 90% at 50% 0%, rgba(64,255,64,.05), transparent 60%),
|
||
#040806;
|
||
padding: 18px 18px 16px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
|
||
.art-tile img {
|
||
width: 100%;
|
||
height: auto;
|
||
display: block;
|
||
filter: drop-shadow(0 0 12px rgba(64,255,64,.25));
|
||
}
|
||
|
||
.art-tile figcaption { font-size: .87rem; color: var(--ink-quiet); }
|
||
.art-tile figcaption em {
|
||
display: block;
|
||
font-style: normal;
|
||
font-family: var(--mono);
|
||
font-size: .74rem;
|
||
letter-spacing: .2em;
|
||
text-transform: uppercase;
|
||
color: var(--phosphor);
|
||
margin-bottom: 5px;
|
||
}
|
||
.art-tile figcaption b { font-family: var(--mono); font-weight: 400; color: var(--phosphor-2); }
|
||
|
||
.art-credit { margin: 12px 0 0; font-size: .78rem; color: var(--ink-quiet); font-style: italic; }
|
||
|
||
.stick-key table { min-width: 0; }
|
||
.stick-key th { color: var(--phosphor); font-family: var(--mono); font-size: .8rem; letter-spacing: .06em; text-transform: none; }
|
||
.stick-key table { border: 1px solid var(--glass-edge); }
|
||
.stick-note { margin: 14px 0 0; font-size: .86rem; color: var(--ink-quiet); max-width: 46ch; }
|
||
|
||
/* ---------- cockpit map ---------- */
|
||
|
||
.cockpit {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 16px;
|
||
min-width: 720px;
|
||
}
|
||
|
||
.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-strip.amber b { background: linear-gradient(180deg, #c8a32e, #8d711b); color: #2a2205; }
|
||
|
||
.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; }
|
||
|
||
.mfd.wide { grid-column: span 3; }
|
||
.cockpit-note { grid-column: span 3; margin: 2px 0 0; font-size: .82rem; color: var(--ink-quiet); }
|
||
|
||
/* the map is portrait, flanked by six amber buttons a side */
|
||
.cockpit { align-items: start; }
|
||
.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; }
|
||
|
||
/* ---------- tables ---------- */
|
||
|
||
.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); }
|
||
|
||
/* ---------- rebind ---------- */
|
||
|
||
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; }
|
||
|
||
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;
|
||
}
|
||
|
||
/* ---------- the mouse: button reach + display layout ---------- */
|
||
|
||
h3 {
|
||
font-family: var(--mono);
|
||
font-size: .78rem;
|
||
font-weight: 400;
|
||
letter-spacing: .2em;
|
||
text-transform: uppercase;
|
||
color: var(--phosphor);
|
||
margin: 46px 0 8px;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
/* the press region: half the glass, top and bottom */
|
||
.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; }
|
||
|
||
/* the lamp strip, all that shows outside the glass */
|
||
.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);
|
||
}
|
||
|
||
/* ---------- radar placement thumbnails ---------- */
|
||
|
||
.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);
|
||
}
|
||
|
||
/* sizes are the real canvas proportions: MFD 320x240, radar 324x432
|
||
of a 1920x1080 cockpit */
|
||
.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; }
|
||
|
||
/* centred on the bottom edge, as the pod had it */
|
||
.pos-box u { left: 41.55%; bottom: 0; }
|
||
|
||
/* in a bottom corner the lower MFD beside it slides inboard */
|
||
.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%; }
|
||
|
||
/* halfway up a side it leaves the bottom row alone */
|
||
.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;
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
* { transition: none !important; }
|
||
}
|
||
</style>
|
||
|
||
<div class="wrap">
|
||
|
||
<header class="masthead">
|
||
<p class="eyebrow">Virtual World Entertainment · Tesla pod · RP 4.12.7</p>
|
||
<h1>Red Planet<br>Controls Map</h1>
|
||
<p class="lede">
|
||
Every input the pod answers to, on the gamepad and the keyboard. The
|
||
keyboard is the pod's button board: the letter and number rows are the
|
||
MFD banks laid out as they are printed on the panel, and flight moved to
|
||
the number pad so the board stays free.
|
||
</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</small></dd>
|
||
</div>
|
||
<div>
|
||
<dt>Throttle</dt>
|
||
<dd>Shift / Ctrl<small>walks up or down, holds where you leave it</small></dd>
|
||
</div>
|
||
<div>
|
||
<dt>Reverse</dt>
|
||
<dd>Alt<small>the throttle button on the pod stick</small></dd>
|
||
</div>
|
||
<div>
|
||
<dt>Abort</dt>
|
||
<dd>Alt + Q<small>ends your mission, score banked</small></dd>
|
||
</div>
|
||
</dl>
|
||
|
||
|
||
|
||
<section>
|
||
<h2>Gamepad</h2>
|
||
<p class="sub">An Xbox-compatible pad drives all five axes and the grip buttons. Hover or tap 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 diagram with Red Planet bindings">
|
||
<!-- shoulders -->
|
||
<rect class="ctl" data-name="LB — Panic" data-addr="0x3D" data-desc="Panic button" x="112" y="60" width="86" height="26" rx="13"></rect>
|
||
<rect class="ctl" data-name="RB — Reverse thrust" data-addr="0x3F" data-desc="Throttle button on the pod stick" x="502" y="60" width="86" height="26" rx="13"></rect>
|
||
<rect class="ctl" data-name="LT — Left pedal" data-addr="axis" data-desc="Left pedal, deadzone 0.12" x="132" y="26" width="66" height="24" rx="12"></rect>
|
||
<rect class="ctl" data-name="RT — Right pedal" data-addr="axis" data-desc="Right pedal, deadzone 0.12" 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 · panic</text>
|
||
<text class="lbl-q" x="486" y="102">RB · reverse thrust</text>
|
||
|
||
<!-- body -->
|
||
<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>
|
||
|
||
<!-- left stick -->
|
||
<circle class="ctl" data-name="Left stick — Joystick X / Y" data-addr="axes" data-desc="Inverted, deadzone 0.24 (pod convention)" 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="66" y="180">joystick X/Y</text>
|
||
|
||
<!-- dpad -->
|
||
<g class="ctl" data-name="D-pad — Hat" data-addr="0x41–0x44" data-desc="Look up, back, left, right">
|
||
<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="96" y="303">hat · look</text>
|
||
|
||
<!-- right stick -->
|
||
<circle class="ctl" data-name="Right stick Y — Throttle" data-addr="axis" data-desc="Winds the throttle up or down; position holds" 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 (Y)</text>
|
||
|
||
<!-- face buttons -->
|
||
<circle class="ctl" data-name="Y — Upper" data-addr="0x47" data-desc="Upper stick button" cx="470" cy="146" r="20"></circle>
|
||
<circle class="ctl" data-name="X — Pinky" data-addr="0x45" data-desc="Pinky stick button" cx="428" cy="180" r="20"></circle>
|
||
<circle class="ctl" data-name="B — Middle" data-addr="0x46" data-desc="Middle stick button" cx="512" cy="180" r="20"></circle>
|
||
<circle class="ctl" data-name="A — Main trigger" data-addr="0x40" data-desc="Fires the selected tool" 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">Y upper · X pinky · B middle</text>
|
||
<path class="lead" d="M532 180 H556"></path>
|
||
|
||
<!-- start / back: unbound by default, free to map -->
|
||
<circle class="ctl" data-name="Back" data-addr="unbound" data-desc="Free — map it in bindings.txt" cx="316" cy="176" r="13"></circle>
|
||
<circle class="ctl" data-name="Start" data-addr="unbound" data-desc="Free — map it in bindings.txt" cx="384" cy="176" r="13"></circle>
|
||
<text class="lbl-q" x="292" y="146">unbound</text>
|
||
</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>
|
||
</section>
|
||
|
||
<section>
|
||
<h2>Keyboard</h2>
|
||
<p class="sub">
|
||
Colour is the bank each key drives: green flies the pod, red is an MFD
|
||
button bank, amber is a column beside the map. Dark keys are unbound and
|
||
free to take.
|
||
</p>
|
||
|
||
<div class="panel">
|
||
<div class="kb" id="kb"></div>
|
||
<div class="legend">
|
||
<span><i class="lg-flight"></i> flight & stick</span>
|
||
<span><i class="lg-mfd"></i> MFD button banks</span>
|
||
<span><i class="lg-col"></i> secondary / screen 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>ALT + Q</strong> aborts the mission on the spot — score banked, back to the menu or lobby. It is the only chord the engine listens for; the arrow keys are safe.</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 MFDs,
|
||
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>Upper Left</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>Upper Middle</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>Upper Right</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>Lower Left</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>
|
||
Secondary 0x10 – 0x15<br>
|
||
Screen 0x18 – 0x1D
|
||
</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>Lower Right</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. Every button here is also a mouse target on screen —
|
||
see <em style="font-style:normal;color:var(--phosphor)">The mouse</em>
|
||
below — and lights when the game commands its lamp.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section>
|
||
<h2>The pod stick</h2>
|
||
<p class="sub">
|
||
What the gamepad stands in for. This is the cockpit's own TRIGGER STATUS
|
||
readout — four buttons on the grip, drawn the way the upper-right MFD
|
||
draws them. Hover one for its address and its stand-ins.
|
||
</p>
|
||
|
||
<div class="stick-wrap">
|
||
<figure class="mfd-tile">
|
||
<figcaption>Trigger Status</figcaption>
|
||
<div class="stick-art">
|
||
<img src="data:image/png;base64,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" alt="The pod joystick as the cockpit draws it, with its four grip buttons circled">
|
||
<button class="hot" type="button" style="--cx:37.7%; --cy:13.9%"
|
||
data-name="Upper" data-addr="0x47" data-desc="Thumb button — pad Y"><span>Upper</span></button>
|
||
<button class="hot" type="button" style="--cx:37.7%; --cy:36.5%"
|
||
data-name="Main trigger" data-addr="0x40" data-desc="Index trigger — pad A, Space, NumPad 0"><span>Main</span></button>
|
||
<button class="hot" type="button" style="--cx:37.7%; --cy:59.0%"
|
||
data-name="Middle" data-addr="0x46" data-desc="Middle finger — pad B"><span>Middle</span></button>
|
||
<button class="hot" type="button" style="--cx:37.7%; --cy:81.6%"
|
||
data-name="Pinky" data-addr="0x45" data-desc="Pinky finger — pad X"><span>Pinky</span></button>
|
||
</div>
|
||
<svg class="stick-hidden" viewBox="0 0 250 360" aria-hidden="true" hidden>
|
||
<!-- grip: tapered column, leaning back like the pod stick -->
|
||
<path class="stk" d="M156 62 c30 -6 50 10 52 42 l6 156 c1 26 -4 40 -16 48 l-72 6 c-15 1 -23 -9 -21 -24 l24 -196 c3 -22 13 -30 27 -32 z"></path>
|
||
<!-- thumb cap, top right -->
|
||
<circle class="stk" cx="196" cy="86" r="22"></circle>
|
||
<!-- trigger hook and lower nub, left face -->
|
||
<path class="stk" d="M124 150 c-13 1 -21 9 -21 20 c0 10 8 17 19 17"></path>
|
||
<path class="stk" d="M116 222 c-11 1 -18 8 -18 17 c0 9 7 15 16 15"></path>
|
||
<!-- hat / thumb cylinder -->
|
||
<rect class="stk" x="150" y="238" width="28" height="46" rx="14"></rect>
|
||
<!-- base -->
|
||
<path class="stk" d="M104 300 l100 -10 c15 -2 24 8 24 21 l0 20 c0 13 -9 21 -22 22 l-88 8 c-15 1 -24 -8 -24 -21 l0 -20 c0 -11 4 -18 10 -20 z"></path>
|
||
|
||
<!-- callouts: top to bottom as the hand finds them -->
|
||
<g class="ctl stick-btn" data-name="Upper" data-addr="0x47" data-desc="Thumb button — pad Y">
|
||
<line class="lead2" x1="86" y1="96" x2="146" y2="96"></line>
|
||
<circle cx="60" cy="96" r="25"></circle>
|
||
</g>
|
||
<g class="ctl stick-btn" data-name="Middle" data-addr="0x46" data-desc="Middle finger — pad B">
|
||
<line class="lead2" x1="86" y1="164" x2="120" y2="164"></line>
|
||
<circle cx="60" cy="164" r="25"></circle>
|
||
</g>
|
||
<g class="ctl stick-btn" data-name="Pinky" data-addr="0x45" data-desc="Pinky finger — pad X">
|
||
<line class="lead2" x1="86" y1="232" x2="112" y2="232"></line>
|
||
<circle cx="60" cy="232" r="25"></circle>
|
||
</g>
|
||
<g class="ctl stick-btn" data-name="Main trigger" data-addr="0x40" data-desc="Index trigger — pad A, Space, NumPad 0">
|
||
<line class="lead2" x1="86" y1="300" x2="112" y2="300"></line>
|
||
<circle cx="60" cy="300" r="25"></circle>
|
||
</g>
|
||
</svg>
|
||
</figure>
|
||
|
||
<div class="stick-key">
|
||
<table>
|
||
<caption>Grip buttons</caption>
|
||
<tbody>
|
||
<tr><th>Upper</th><td class="addr">0x47</td><td class="mono">Y</td></tr>
|
||
<tr><th>Main</th><td class="addr">0x40</td><td class="mono">A · Space · Num 0</td></tr>
|
||
<tr><th>Middle</th><td class="addr">0x46</td><td class="mono">B</td></tr>
|
||
<tr><th>Pinky</th><td class="addr">0x45</td><td class="mono">X</td></tr>
|
||
<tr><th>Throttle</th><td class="addr">0x3F</td><td class="mono">RB · Alt</td></tr>
|
||
<tr><th>Panic</th><td class="addr">0x3D</td><td class="mono">LB</td></tr>
|
||
<tr><th>Hat</th><td class="addr">0x41–44</td><td class="mono">D-pad · arrows</td></tr>
|
||
<tr><th>Config 1 · 2</th><td class="addr">0x37 · 0x36</td><td class="mono">unbound — yours to map</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p class="stick-note">
|
||
The throttle button is reverse thrust; the hat looks around. Both live
|
||
off the grip on the real stick.
|
||
</p>
|
||
<div class="readout" id="rd-stick" data-idle="true">
|
||
<span class="rd-name">Select a button</span>
|
||
<span class="rd-desc">Hover a circle on the grip.</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="axis-art">
|
||
<figure class="art-tile">
|
||
<img src="data:image/png;base64,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" alt="Pod diagram: the stick pitches, rolls and yaws the VTV">
|
||
<figcaption>
|
||
<em>Stick — attitude</em>
|
||
Pitch, roll and yaw. <b>Left stick</b> on the pad, <b>Num 8 2 4 6</b> on
|
||
the keyboard.
|
||
</figcaption>
|
||
</figure>
|
||
<figure class="art-tile">
|
||
<img src="data:image/png;base64,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" alt="Pod diagram: the throttle drives the VTV forward and back">
|
||
<figcaption>
|
||
<em>Throttle — travel</em>
|
||
Forward and reverse. <b>Right stick Y</b> on the pad,
|
||
<b>Shift</b> / <b>Ctrl</b> on the keyboard; it holds where you leave it.
|
||
</figcaption>
|
||
</figure>
|
||
</div>
|
||
<p class="art-credit">Both plates are the pod's own instruction art, lifted from the shipped gauge assets.</p>
|
||
</section>
|
||
|
||
<section>
|
||
<h2>The mouse</h2>
|
||
<p class="sub">
|
||
The third input. Every button on every display is clickable and lights
|
||
when the game commands its lamp — and each one reaches in under the
|
||
glass beside it, so the picture is the press target. The lit strip at
|
||
the edge is only the lamp.
|
||
</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
|
||
<b>240px</b> deep against a <b>480px</b> pane — so the two banks
|
||
meet in the middle and the whole face answers the mouse. Only
|
||
<b>10px</b> of each 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 radar to place. Set these in <code>environ.ini</code>
|
||
beside the exe; they are read once at startup.
|
||
</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>L4MFDSCALE</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>L4MFDSCALE_<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>L4RADARSCALE</th><td>The radar alone. It already sits at 1.35× the MFDs.</td></tr>
|
||
<tr><th>L4RADARPOS</th><td class="mono">CENTER · LEFT · RIGHT · MIDLEFT · MIDRIGHT</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<div class="callout">
|
||
<p>
|
||
<strong>Ask for too much and the layout wins.</strong> Panes are
|
||
clamped against their actual neighbours and shrink evenly, so nothing
|
||
overlaps and nothing comes out stretched. Scale the MFDs beside the
|
||
radar and it is the radar that gives way — halfway up a side it has
|
||
one above and one below, and it grows from the middle both ways.
|
||
</p>
|
||
</div>
|
||
|
||
<h3>Filling the screen</h3>
|
||
<p class="sub" style="margin-bottom:0">
|
||
The cockpit fits itself to whatever window it gets and keeps 16:9, so a
|
||
wide panel letterboxes rather than stretching. Launch with
|
||
<code>-fit</code> — <code>start-fullscreen.bat</code> — and it goes
|
||
borderless over the whole monitor and picks the render size to land on
|
||
the viewscreen 1:1, instead of you guessing at <code>-res</code>.
|
||
</p>
|
||
</section>
|
||
|
||
<section>
|
||
<h2>Bring your own stick</h2>
|
||
<p class="sub">
|
||
A gamepad speaks XInput, and the game reads it without being asked.
|
||
Everything else — a flight stick, a HOTAS throttle, a twist grip,
|
||
rudder pedals, a wheel — speaks <b>DirectInput</b>, which names its axes
|
||
<span class="mono">X Y Z RX RY RZ SL0 SL1</span> and says nothing about
|
||
what they are <i>for</i>. Somebody has to decide that your twist grip is
|
||
the rudder and that pushing it right means right. That is what
|
||
<code>joyconfig.bat</code> is.
|
||
</p>
|
||
|
||
<div class="panel">
|
||
<dl class="glance">
|
||
<div>
|
||
<dt>Run it once</dt>
|
||
<dd>joyconfig.bat<small>beside the exe; re-run any time</small></dd>
|
||
</div>
|
||
<div>
|
||
<dt>It asks</dt>
|
||
<dd>Move that one<small>steer · pitch · pedals · throttle · buttons</small></dd>
|
||
</div>
|
||
<div>
|
||
<dt>It watches</dt>
|
||
<dd>Which way<small>the direction you moved sets the sign</small></dd>
|
||
</div>
|
||
<div>
|
||
<dt>Then</dt>
|
||
<dd>Straight in<small>the game carries on to the setup screen</small></dd>
|
||
</div>
|
||
</dl>
|
||
</div>
|
||
|
||
<div class="callout">
|
||
<p>
|
||
<strong>The direction is the whole point.</strong> A stick that reads
|
||
positive pushed right and one that reads negative are equally common,
|
||
and nothing printed on the box tells you which you own. So the wizard
|
||
does not ask you to know — it asks you to <i>move</i>, and reads the
|
||
answer off the movement. Skip a prompt with <code>SPACE</code> if you
|
||
have no such control, <code>ESC</code> to abort without writing.
|
||
</p>
|
||
</div>
|
||
|
||
<p class="sub">
|
||
It writes only its own section of <code>bindings.txt</code>, between two
|
||
marker lines, so anything you have written yourself is kept — including a
|
||
deadzone you tuned by hand after trying it. Before it starts it prints
|
||
every axis <i>at rest</i>: a stick whose driver reports an odd range
|
||
shows up there as <span class="mono">X +1.00</span> on an untouched
|
||
stick, rather than as a mystery later. Xbox-class pads are excluded from
|
||
all of this, so nothing ever counts twice.
|
||
</p>
|
||
|
||
<div class="two-col">
|
||
<pre><i># what it writes</i>
|
||
<b>joydev</b> 0 Logitech Extreme 3D
|
||
<b>joyaxis</b> X <b>axis</b> JoystickX invert deadzone 0.08
|
||
<b>joyaxis</b> Y <b>axis</b> JoystickY deadzone 0.08
|
||
<b>joyaxis</b> RZ <b>axis</b> Pedals deadzone 0.08
|
||
<b>joyaxis</b> SL0 <b>axis</b> Throttle invert deadzone 0
|
||
<b>joybutton</b> 0 <b>button</b> 0x40
|
||
<b>joyhat</b> 0 up <b>button</b> 0x42</pre>
|
||
|
||
<pre><i># grammar</i>
|
||
<b>joydev</b> <slot> [product-name substring]
|
||
<b>joyaxis</b> <src> <b>axis</b> <axis> [invert]
|
||
[deadzone <d>] [rate <n>]
|
||
<b>joybutton</b> <n> <b>button</b> <addr> [toggle]
|
||
<b>joyhat</b> <n> <up|down|left|right>
|
||
<b>button</b> <addr>
|
||
|
||
<i># a named slot binds that product; a bare
|
||
# one binds the Nth stick Windows lists</i></pre>
|
||
</div>
|
||
|
||
<div class="tbl-scroll" style="margin-top:22px">
|
||
<table>
|
||
<caption>Two rules the pod's shape asks for</caption>
|
||
<tbody>
|
||
<tr>
|
||
<th class="mono">Pedals</th>
|
||
<td>
|
||
A <b>signed</b> axis that works the pedal <b>pair</b> — positive
|
||
for the right pedal, negative for the left. The pod has one
|
||
under each foot; a twist grip or rudder bar is a single control
|
||
that presses one or the other and never both, which is exactly
|
||
what this says. It is a channel name like any other, so a gamepad
|
||
stick can drive the turn with it too.
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th class="mono">Throttle</th>
|
||
<td>
|
||
A <span class="mono">joyaxis</span> on the throttle with no
|
||
<span class="mono">rate</span> is treated as a <b>real lever</b>
|
||
and owns the channel: its full travel <i>is</i> the throttle
|
||
range. Spring-centred sticks have to nudge a position instead,
|
||
which is what <span class="mono">rate</span> does — a lever that
|
||
stays where you put it needs none of that.
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<p class="sub" style="margin-bottom:0">
|
||
A twist grip is usually <span class="mono">RZ</span>; a HOTAS throttle is
|
||
usually <span class="mono">Z</span> or <span class="mono">SL0</span>. Set
|
||
<code>RP412JOYLOG=1</code> to log devices as they attach and drop away.
|
||
</p>
|
||
</section>
|
||
|
||
<section>
|
||
<h2>Rebinding</h2>
|
||
<p class="sub">
|
||
<code>bindings.txt</code> sits beside the exe, 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> <name> <b>button</b> <addr> [toggle]
|
||
<b>key</b> <name> <b>axis</b> <axis> deflect <n>
|
||
<b>key</b> <name> <b>axis</b> <axis> rate <n>
|
||
<b>pad</b> <button> <b>button</b> <addr> [toggle]
|
||
<b>padaxis</b> <src> <b>axis</b> <axis> [invert]
|
||
[deadzone <d>] [rate <n>]
|
||
<b>joydev joyaxis joybutton joyhat</b>
|
||
<i>see above</i>
|
||
|
||
<i># deflect springs back, rate sticks where
|
||
# you leave it (that is the throttle)</i></pre>
|
||
|
||
<pre><i># examples</i>
|
||
<b>key</b> J <b>button</b> 0x40 <i># trigger on J too</i>
|
||
<b>pad</b> X <b>button</b> 0x40 <i># trigger on X</i>
|
||
|
||
<b>key</b> NumPadAdd <b>axis</b> Throttle rate 0.75
|
||
<b>key</b> NumPadSubtract <b>axis</b> Throttle rate -0.75
|
||
|
||
<i># drop 'invert' to flip the stick</i>
|
||
<b>padaxis</b> LeftStickX <b>axis</b> JoystickX deadzone 0.2</pre>
|
||
</div>
|
||
|
||
<div class="tbl-scroll" style="margin-top:22px">
|
||
<table>
|
||
<caption>Reference — axes, address space, key names</caption>
|
||
<tbody>
|
||
<tr><th>Axes</th><td class="mono">Throttle · LeftPedal · RightPedal · JoystickX · JoystickY · Pedals</td></tr>
|
||
<tr><th>Buttons</th><td class="addr">0x00 – 0x47</td></tr>
|
||
<tr><th>Keypads</th><td class="addr">0x50 – 0x6F <span style="color:var(--ink-quiet);font-family:var(--sans)">— unbound by default; the game never reads them</span></td></tr>
|
||
<tr><th>Key names</th><td class="mono">A–Z · D0–D9 · F1–F12 · NumPad0–9 · Up Down Left Right · Space · Return · Shift · Ctrl · Alt · OemMinus · Oemplus · Oemcomma · OemPeriod</td></tr>
|
||
<tr><th>Pad</th><td class="mono">A B X Y · DPadUp/Down/Left/Right · Start · Back · LeftShoulder · RightShoulder · LeftThumb · RightThumb</td></tr>
|
||
<tr><th>Pad axes</th><td class="mono">LeftStickX/Y · RightStickX/Y · LeftTrigger · RightTrigger</td></tr>
|
||
<tr><th>Joystick axes</th><td class="mono">X · Y · Z · RX · RY · RZ · SL0 · SL1 <span style="color:var(--ink-quiet);font-family:var(--sans)">— DirectInput's own names</span></td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</section>
|
||
|
||
<footer>
|
||
<span>Red Planet 4.12.7</span>
|
||
<span>gitea.mysticmachines.com/VWE/RP412</span>
|
||
<span>docs/CONTROLS.md · CONTROLS.txt ships with the game</span>
|
||
<span>RGB keyboards mirror the pod lamps · RP412KEYLIGHT=0 to disable</span>
|
||
</footer>
|
||
</div>
|
||
|
||
<script>
|
||
(function () {
|
||
"use strict";
|
||
|
||
// Key rows, straight from the default bindings profile.
|
||
// [cap, sub-label, role, address, description, widthClass]
|
||
var F = "flight", M = "mfd", C = "column", X = null;
|
||
|
||
var SP = "spacer";
|
||
|
||
var rows = [
|
||
[
|
||
["Esc", "", X], [SP],
|
||
["F1", "SEC", C, "0x10", "Secondary column 1"],
|
||
["F2", "SEC", C, "0x11", "Secondary column 2"], ["F3", "SEC", C, "0x12", "Secondary column 3"],
|
||
["F4", "SEC", C, "0x13", "Secondary column 4"], [SP],
|
||
["F5", "SEC", C, "0x14", "Secondary column 5"],
|
||
["F6", "SEC", C, "0x15", "Secondary column 6"], ["F7", "SCR", C, "0x18", "Screen column 1"],
|
||
["F8", "SCR", C, "0x19", "Screen column 2"], [SP],
|
||
["F9", "SCR", C, "0x1A", "Screen column 3"],
|
||
["F10", "SCR", C, "0x1B", "Screen column 4"], ["F11", "SCR", C, "0x1C", "Screen column 5"],
|
||
["F12", "SCR", C, "0x1D", "Screen column 6"]
|
||
],
|
||
[
|
||
["`", "", X],
|
||
["1", "", M, "0x2F", "Upper Left MFD · top 1"], ["2", "", M, "0x2E", "Upper Left MFD · top 2"],
|
||
["3", "", M, "0x2D", "Upper Left MFD · top 3"], ["4", "", M, "0x2C", "Upper Left MFD · top 4"],
|
||
["5", "", M, "0x27", "Upper Middle MFD · top 1"], ["6", "", M, "0x26", "Upper Middle MFD · top 2"],
|
||
["7", "", M, "0x25", "Upper Middle MFD · top 3"], ["8", "", M, "0x24", "Upper Middle MFD · top 4"],
|
||
["9", "", M, "0x37", "Upper Right MFD · top 1"],
|
||
["0", "", M, "0x36", "Upper Right MFD · top 2"],
|
||
["-", "", M, "0x35", "Upper Right MFD · top 3"], ["=", "", M, "0x34", "Upper Right MFD · top 4"],
|
||
["Bksp", "", X, "", "", "w2"]
|
||
],
|
||
[
|
||
["Tab", "", X, "", "", "w15"],
|
||
["Q", "ABORT", M, "0x2B", "Upper Left MFD · lower 1 — with Alt, aborts the mission"],
|
||
["W", "", M, "0x2A", "Upper Left MFD · lower 2"], ["E", "", M, "0x29", "Upper Left MFD · lower 3"],
|
||
["R", "", M, "0x28", "Upper Left MFD · lower 4"], ["T", "", M, "0x23", "Upper Middle MFD · lower 1"],
|
||
["Y", "", M, "0x22", "Upper Middle MFD · lower 2"], ["U", "", M, "0x21", "Upper Middle MFD · lower 3"],
|
||
["I", "", M, "0x20", "Upper Middle MFD · lower 4"], ["O", "", M, "0x33", "Upper Right MFD · lower 1"],
|
||
["P", "", M, "0x32", "Upper Right MFD · lower 2"], ["[", "", M, "0x31", "Upper Right MFD · lower 3"],
|
||
["]", "", M, "0x30", "Upper Right MFD · lower 4"], ["\\", "", X, "", "", "w15"]
|
||
],
|
||
[
|
||
["Caps", "", X, "", "", "w175"],
|
||
["A", "", M, "0x0F", "Lower Left MFD · top 1"], ["S", "", M, "0x0E", "Lower Left MFD · top 2"],
|
||
["D", "", M, "0x0D", "Lower Left MFD · top 3"], ["F", "", M, "0x0C", "Lower Left MFD · top 4"],
|
||
["G", "GAP", X, "", "Panel gap between the lower clusters — free"],
|
||
["H", "", M, "0x07", "Lower Right MFD · top 1"], ["J", "", M, "0x06", "Lower Right MFD · top 2"],
|
||
["K", "", M, "0x05", "Lower Right MFD · top 3"], ["L", "", M, "0x04", "Lower Right MFD · top 4"],
|
||
[";", "", X], ["'", "", X], ["Enter", "", X, "", "", "w225"]
|
||
],
|
||
[
|
||
["Shift", "THR +", F, "axis", "Throttle up — walks the axis, position holds", "w225"],
|
||
["Z", "", M, "0x0B", "Lower Left MFD · lower 1"], ["X", "", M, "0x0A", "Lower Left MFD · lower 2"],
|
||
["C", "", M, "0x09", "Lower Left MFD · lower 3"], ["V", "", M, "0x08", "Lower Left MFD · lower 4"],
|
||
["B", "GAP", X, "", "Panel gap between the lower clusters — free"],
|
||
["N", "", M, "0x03", "Lower Right MFD · lower 1"], ["M", "", M, "0x02", "Lower Right MFD · lower 2"],
|
||
[",", "", M, "0x01", "Lower Right MFD · lower 3"], [".", "", M, "0x00", "Lower Right MFD · lower 4"],
|
||
["/", "", X], ["Shift", "THR +", F, "axis", "Throttle up", "w275"]
|
||
],
|
||
[
|
||
["Ctrl", "THR −", F, "axis", "Throttle down — walks the axis, position holds", "w125", "ctrl-l"],
|
||
["Win", "", X, "", "", "w125"],
|
||
["Alt", "REV", F, "0x3F", "Reverse thrust — the pod stick's throttle 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",
|
||
[["↑", "HAT", F, "0x42", "Hat up — look"]],
|
||
[["←", "HAT", F, "0x44", "Hat left"], ["↓", "HAT", F, "0x41", "Hat back"], ["→", "HAT", F, "0x43", "Hat right"]]
|
||
];
|
||
|
||
// Physical numpad order. The tall + and Enter each own two rows and
|
||
// 0 owns two columns, so the rest lands where it does on the board.
|
||
var padKeys = [
|
||
["Num", "", X], ["/", "", X], ["*", "", X], ["−", "", X],
|
||
|
||
["7", "L PED", F, "axis", "Left pedal"],
|
||
["8", "FWD", F, "axis", "Stick forward"],
|
||
["9", "R PED", F, "axis", "Right pedal"],
|
||
["+", "", X, "", "", "tall"],
|
||
|
||
["4", "LEFT", F, "axis", "Stick left"],
|
||
["5", "", X],
|
||
["6", "RIGHT", F, "axis", "Stick right"],
|
||
|
||
["1", "", X],
|
||
["2", "BACK", F, "axis", "Stick back"],
|
||
["3", "", X],
|
||
["Ent", "", X, "", "", "tall"],
|
||
|
||
["0", "TRIGGER", F, "0x40", "Main trigger", "pad0"],
|
||
[".", "", X]
|
||
];
|
||
|
||
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], id = def[6];
|
||
var el = document.createElement(role ? "button" : "div");
|
||
el.className = "key" + (w ? " " + w : "");
|
||
if (id) el.setAttribute("data-id", id);
|
||
if (role) {
|
||
el.type = "button";
|
||
el.setAttribute("data-role", cap === "Q" && sub === "ABORT" ? "mfd" : 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.setAttribute("data-role", "");
|
||
el.removeAttribute("data-role");
|
||
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;
|
||
}
|
||
|
||
// main block
|
||
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");
|
||
// the number row and the home row each open a bank of MFDs
|
||
r.className = "kb-row" + (index === 1 || index === 3 ? " zone" : "");
|
||
row.forEach(function (def) { r.appendChild(makeKey(def)); });
|
||
main.appendChild(r);
|
||
});
|
||
bank.appendChild(main);
|
||
|
||
// nav cluster
|
||
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);
|
||
|
||
// numpad
|
||
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);
|
||
|
||
// ---- readouts ----
|
||
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) {
|
||
var active = null;
|
||
root.addEventListener("pointerover", function (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("focusin", function (e) {
|
||
var t = e.target.closest(selector);
|
||
if (!t || !t.getAttribute("data-name")) return;
|
||
show(readout, t.getAttribute("data-name"), t.getAttribute("data-addr"), t.getAttribute("data-desc"));
|
||
});
|
||
root.addEventListener("click", function (e) {
|
||
var t = e.target.closest(selector);
|
||
if (!t || !t.getAttribute("data-name")) return;
|
||
show(readout, t.getAttribute("data-name"), t.getAttribute("data-addr"), t.getAttribute("data-desc"));
|
||
});
|
||
}
|
||
|
||
// ---- cluster outlines -------------------------------------------
|
||
// Each MFD owns two runs of four keys on staggered rows, so the
|
||
// outline is a stepped polygon that hugs both runs exactly.
|
||
var byCap = {};
|
||
Array.prototype.forEach.call(main.querySelectorAll(".key"), function (el) {
|
||
var cap = el.firstChild ? el.firstChild.textContent : "";
|
||
var id = el.getAttribute("data-id");
|
||
if (id) byCap["#" + id] = el;
|
||
if (cap && !byCap[cap]) byCap[cap] = el;
|
||
});
|
||
|
||
var groups = [
|
||
{ label: "Radar left · secondary", kind: "g-col", top: ["F1", "F6"] },
|
||
{ label: "Radar right · screen", kind: "g-col", top: ["F7", "F12"] },
|
||
{ label: "Upper left MFD", kind: "g-mfd", top: ["1", "4"], bottom: ["Q", "R"] },
|
||
{ label: "Upper middle MFD", kind: "g-mfd", top: ["5", "8"], bottom: ["T", "I"] },
|
||
{ label: "Upper right MFD", kind: "g-mfd", top: ["9", "="], bottom: ["O", "]"] },
|
||
{ label: "Lower left MFD", kind: "g-mfd", top: ["A", "F"], bottom: ["Z", "V"] },
|
||
{ label: "Lower right MFD", 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 pad = 3;
|
||
|
||
function box(a, b) {
|
||
if (!a || !b) return null;
|
||
var ra = a.getBoundingClientRect(), rb = b.getBoundingClientRect();
|
||
return {
|
||
l: ra.left - base.left - pad,
|
||
r: rb.right - base.left + pad,
|
||
t: ra.top - base.top - pad,
|
||
b: ra.bottom - base.top + pad
|
||
};
|
||
}
|
||
|
||
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");
|
||
bind(document.querySelector(".stick-art"), document.getElementById("rd-stick"), ".hot");
|
||
})();
|
||
</script>
|