@rydr/game-sdk 8.0.0 → 8.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/PlatformClient.d.ts.map +1 -1
- package/dist/client/PlatformClient.js +3 -5
- package/dist/client/PlatformClient.js.map +1 -1
- package/dist/nav/README.md +13 -2
- package/dist/nav/motion.d.ts +10 -0
- package/dist/nav/motion.d.ts.map +1 -0
- package/dist/nav/motion.js +16 -0
- package/dist/nav/motion.js.map +1 -0
- package/dist/nav/scroll-nav.d.ts.map +1 -1
- package/dist/nav/scroll-nav.js +2 -6
- package/dist/nav/scroll-nav.js.map +1 -1
- package/dist/nav/spatial-nav.d.ts +12 -1
- package/dist/nav/spatial-nav.d.ts.map +1 -1
- package/dist/nav/spatial-nav.js +19 -5
- package/dist/nav/spatial-nav.js.map +1 -1
- package/dist/protocol/buttons.d.ts +17 -0
- package/dist/protocol/buttons.d.ts.map +1 -1
- package/dist/protocol/buttons.js +24 -0
- package/dist/protocol/buttons.js.map +1 -1
- package/dist/protocol/version.d.ts +1 -1
- package/dist/protocol/version.js +1 -1
- package/dist/ui/README.md +59 -4
- package/dist/ui/controller/combo-demo.d.ts +13 -4
- package/dist/ui/controller/combo-demo.d.ts.map +1 -1
- package/dist/ui/controller/combo-demo.js +55 -21
- package/dist/ui/controller/combo-demo.js.map +1 -1
- package/dist/ui/controller/control-spotlight.d.ts +188 -0
- package/dist/ui/controller/control-spotlight.d.ts.map +1 -0
- package/dist/ui/controller/control-spotlight.js +563 -0
- package/dist/ui/controller/control-spotlight.js.map +1 -0
- package/dist/ui/controller/control-trainer.d.ts.map +1 -1
- package/dist/ui/controller/control-trainer.js +6 -22
- package/dist/ui/controller/control-trainer.js.map +1 -1
- package/dist/ui/controller/controller-map.d.ts +20 -0
- package/dist/ui/controller/controller-map.d.ts.map +1 -1
- package/dist/ui/controller/controller-map.js +115 -56
- package/dist/ui/controller/controller-map.js.map +1 -1
- package/dist/ui/controller/controller-pad.d.ts +9 -3
- package/dist/ui/controller/controller-pad.d.ts.map +1 -1
- package/dist/ui/controller/controller-pad.js +7 -7
- package/dist/ui/controller/controller-pad.js.map +1 -1
- package/dist/ui/controller/index.d.ts +2 -0
- package/dist/ui/controller/index.d.ts.map +1 -1
- package/dist/ui/controller/index.js +2 -0
- package/dist/ui/controller/index.js.map +1 -1
- package/dist/ui/controller/live-presses.d.ts +68 -0
- package/dist/ui/controller/live-presses.d.ts.map +1 -0
- package/dist/ui/controller/live-presses.js +71 -0
- package/dist/ui/controller/live-presses.js.map +1 -0
- package/dist/ui/controller/pad-svg.d.ts +23 -3
- package/dist/ui/controller/pad-svg.d.ts.map +1 -1
- package/dist/ui/controller/pad-svg.js +28 -4
- package/dist/ui/controller/pad-svg.js.map +1 -1
- package/dist/ui/controller/parts-map.d.ts +51 -3
- package/dist/ui/controller/parts-map.d.ts.map +1 -1
- package/dist/ui/controller/parts-map.js +76 -4
- package/dist/ui/controller/parts-map.js.map +1 -1
- package/dist/ui/controller/styles.js +113 -8
- package/dist/ui/controller/styles.js.map +1 -1
- package/dist/ui/keycap.d.ts +7 -1
- package/dist/ui/keycap.d.ts.map +1 -1
- package/dist/ui/keycap.js.map +1 -1
- package/dist/ui/option-menu.d.ts +3 -2
- package/dist/ui/option-menu.d.ts.map +1 -1
- package/dist/ui/option-menu.js +5 -0
- package/dist/ui/option-menu.js.map +1 -1
- package/dist/ui/showcase/gallery.d.ts +12 -3
- package/dist/ui/showcase/gallery.d.ts.map +1 -1
- package/dist/ui/showcase/gallery.js +16 -7
- package/dist/ui/showcase/gallery.js.map +1 -1
- package/dist/ui/showcase/index.d.ts +4 -3
- package/dist/ui/showcase/index.d.ts.map +1 -1
- package/dist/ui/showcase/index.js +175 -13
- package/dist/ui/showcase/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CONTROL SPOTLIGHT — "this button, and here's what it does", written on the button itself.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* // A tour: each control in turn, captioned, on a clock.
|
|
6
|
+
* const spot = mountControlSpotlight(host, {
|
|
7
|
+
* press: shellInput,
|
|
8
|
+
* watch: true,
|
|
9
|
+
* steps: [
|
|
10
|
+
* { label: "Descendre de vitesse", combo: ["LB"] },
|
|
11
|
+
* { label: "Monter de vitesse", combo: ["RB"] },
|
|
12
|
+
* ],
|
|
13
|
+
* });
|
|
14
|
+
*
|
|
15
|
+
* // Or no clock at all: one control, lit and captioned, indefinitely.
|
|
16
|
+
* mountControlSpotlight(hud, { press: session, mode: "still", steps: [{ label: "Move", combo: ["LSTICK"] }] });
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* The caption sits **on the control**, not in a list beside the pad — which is the whole reason this
|
|
20
|
+
* view exists. A rider on a bike reading a trainer screen from a few feet away cannot be asked to trace
|
|
21
|
+
* a line from a legend back to a button, and the control genuinely cannot be named in text: the same
|
|
22
|
+
* button reads `LB` on an Xbox pad, `L1` on a DualSense and a bare `L` on a Switch. Pointing at it and
|
|
23
|
+
* saying what it does is the only honest way to teach it.
|
|
24
|
+
*
|
|
25
|
+
* ## Which of the four controller views is this
|
|
26
|
+
*
|
|
27
|
+
* {@link mountControllerPad} the drawing alone, reflecting real input
|
|
28
|
+
* {@link mountControllerMap} STATIC — every control at once, callouts in columns joined by lines
|
|
29
|
+
* {@link mountComboDemo} DYNAMIC — one combination, animated on a loop
|
|
30
|
+
* {@link mountControlSpotlight} N controls, ONE at a time, captioned ON the button, on its own clock
|
|
31
|
+
* {@link mountControlTrainer} a lesson that WATCHES and gates on the rider performing it
|
|
32
|
+
*
|
|
33
|
+
* The map is what you want for "here is the control scheme". This is what you want when the caption has
|
|
34
|
+
* to land on the button, when each control needs its own dwell, or when there is no clock at all and you
|
|
35
|
+
* simply want one control lit with a label on it.
|
|
36
|
+
*
|
|
37
|
+
* ## Two modes, one render
|
|
38
|
+
*
|
|
39
|
+
* `"tour"` walks the steps: each lights for its `holdMs`, then a dark `gapMs`, then the next. The gap is
|
|
40
|
+
* load-bearing — two steps shown back to back read as one flickering picture, and the dark beat between
|
|
41
|
+
* them is what makes them two statements rather than one.
|
|
42
|
+
*
|
|
43
|
+
* `"still"` has no clock: every step lights and every caption shows, and stays. With a single step that
|
|
44
|
+
* is "highlight this key, with this text, for as long as I'm on screen", which needs no timeline to
|
|
45
|
+
* express. `prefers-reduced-motion` simply forces this mode — so the reduced-motion still is not a
|
|
46
|
+
* second code path to keep in step with the first, it IS the first.
|
|
47
|
+
*
|
|
48
|
+
* ## The rider outranks the lesson (`watch`)
|
|
49
|
+
*
|
|
50
|
+
* With `watch`, a control the rider is actually holding paints **green** (`done`) instead of the accent,
|
|
51
|
+
* and the timeline **stands down**: it stops, every unheld control goes dark, and it restarts from the
|
|
52
|
+
* top `resumeMs` after they let go. A loop that keeps cycling underneath a rider trying it out is two
|
|
53
|
+
* hands on the same pad — they cannot tell which of the lights is theirs, which is the entire feedback
|
|
54
|
+
* the lesson exists to give.
|
|
55
|
+
*
|
|
56
|
+
* Green is not limited to the controls the steps name: with `watch` the drawing paints EVERY control the
|
|
57
|
+
* rider is holding, including ones no step teaches. A rider trying the pad out while the lesson runs is
|
|
58
|
+
* asking "which one is this?", and a drawing that answers only for the two buttons it happens to be
|
|
59
|
+
* teaching reads as a dead pad rather than as an answer. Only what the rider holds turns green, so the
|
|
60
|
+
* lesson's own controls stay the only BLUE thing on it.
|
|
61
|
+
*
|
|
62
|
+
* Blue says *this is the press*; green says *and that one was yours*. Neither is the whitish `hint` the
|
|
63
|
+
* trainer uses, which is built for "do this, I'll wait" — here the instruction is a moving picture of a
|
|
64
|
+
* press, and a press drawn in not-a-press colours doesn't look like the thing it's asking for.
|
|
65
|
+
*
|
|
66
|
+
* ## This component is the sole writer of the drawing
|
|
67
|
+
*
|
|
68
|
+
* The pad is mounted **glyph-only** (it gets the hardware store for its lettering, and an `onButton` that
|
|
69
|
+
* does nothing), exactly as {@link mountControlTrainer} does. Wiring the pad to input as well would give
|
|
70
|
+
* two writers for the same classes every frame, each undoing the other. We take the rider's presses
|
|
71
|
+
* ourselves and fold them into the paint, so there is one writer and no race.
|
|
72
|
+
*
|
|
73
|
+
* ⚠️ **A caption is ONE line: the action's name.** No `hint` subtitle — `ControlSpotlightStep` types it
|
|
74
|
+
* out — because this bubble floats over the rider's controller for about two seconds while they pedal,
|
|
75
|
+
* and a second quieter line under the first means neither gets read. Qualify the label if you must.
|
|
76
|
+
*
|
|
77
|
+
* ⚠️ **Captions are text-only for now.** A step whose combo names several controls lights all of them
|
|
78
|
+
* and pins its caption per the usual anchor rule, but the bubble does not draw chips — the multi-control
|
|
79
|
+
* case is getting its own treatment later. Don't grow chips in here by drift; `mountComboDemo` and
|
|
80
|
+
* `mountControllerMap` are where a combination is spelled out today.
|
|
81
|
+
*/
|
|
82
|
+
import { mountControllerPad } from "./controller-pad.js";
|
|
83
|
+
import { anchorPartOfToken, anchorTokenOf, channelsOf, litPartOfControl, partCentreOf, partGeomOf, triggerSideOfPart, } from "./parts-map.js";
|
|
84
|
+
import { createLivePresses } from "./live-presses.js";
|
|
85
|
+
import { injectPadCss } from "./styles.js";
|
|
86
|
+
import { isShellControl, parseToken } from "./tokens.js";
|
|
87
|
+
import { partCentre } from "./types.js";
|
|
88
|
+
/** How long a step holds lit, unless it says otherwise. */
|
|
89
|
+
export const SPOTLIGHT_HOLD_MS = 2000;
|
|
90
|
+
/**
|
|
91
|
+
* The dark beat after a step. Not decoration — see the note on the gap above; two lit controls with no
|
|
92
|
+
* dark between them read as one picture flickering rather than as two separate statements.
|
|
93
|
+
*/
|
|
94
|
+
export const SPOTLIGHT_GAP_MS = 1000;
|
|
95
|
+
/** How long after the rider lets go of everything before the timeline takes the pad back. */
|
|
96
|
+
export const SPOTLIGHT_RESUME_MS = 1000;
|
|
97
|
+
/** One full revolution of an undirected stick sweep, ms. Matches `combo-demo`'s feel. */
|
|
98
|
+
const SWEEP_MS = 1200;
|
|
99
|
+
/** Past this deflection an undirected sweep counts as engaged, for the caption's sake. */
|
|
100
|
+
const SWEEP_RAMP = 1;
|
|
101
|
+
export function mountControlSpotlight(host, opts) {
|
|
102
|
+
injectPadCss();
|
|
103
|
+
const reduceMotion = typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
104
|
+
const mode = reduceMotion ? "still" : (opts.mode ?? "tour");
|
|
105
|
+
const watch = opts.watch === true && opts.press != null;
|
|
106
|
+
const resumeMs = opts.resumeMs ?? SPOTLIGHT_RESUME_MS;
|
|
107
|
+
const speed = opts.speed ?? 1;
|
|
108
|
+
const loop = opts.loop !== false;
|
|
109
|
+
const root = document.createElement("div");
|
|
110
|
+
root.className = "rydr-padspot" + (opts.glow === false ? " no-glow" : "");
|
|
111
|
+
// The captions are absolutely positioned over the drawing, so they need a positioned box of their own.
|
|
112
|
+
// `host` belongs to the caller and may be laid out however that screen wants.
|
|
113
|
+
const stage = document.createElement("div");
|
|
114
|
+
stage.className = "rydr-padspot-stage";
|
|
115
|
+
root.appendChild(stage);
|
|
116
|
+
host.appendChild(root);
|
|
117
|
+
/**
|
|
118
|
+
* Does any step name a shell-owned control? Then the drawing must be allowed to light it — that IS the
|
|
119
|
+
* lesson. Derived rather than asked for, so a caller can't forget it and get a dead highlight. (Same
|
|
120
|
+
* reasoning, same shape, as `control-trainer`.)
|
|
121
|
+
*/
|
|
122
|
+
const teachesShellOwned = (steps) => steps.some((s) => s.combo.some((t) => {
|
|
123
|
+
const p = parseToken(t);
|
|
124
|
+
return p.kind === "button" && isShellControl(p.name);
|
|
125
|
+
}));
|
|
126
|
+
// Glyph-only: this component owns the drawing's state, so real presses must not write it too. We route
|
|
127
|
+
// them ourselves, below, and fold them into the same paint the timeline uses.
|
|
128
|
+
/**
|
|
129
|
+
* Whether the drawing may light the shell-owned shoulders. Held in a variable because the live-press
|
|
130
|
+
* paint below has to respect the same answer the pad was mounted with: lighting `LB` on a drawing that
|
|
131
|
+
* draws it faint (because nothing here teaches it) would be a highlight nobody can read.
|
|
132
|
+
*/
|
|
133
|
+
const shellOwnedLit = teachesShellOwned(opts.steps);
|
|
134
|
+
const pad = mountControllerPad(stage, {
|
|
135
|
+
press: opts.press && { onButton: () => () => { }, hardware: opts.press.hardware },
|
|
136
|
+
brand: opts.brand,
|
|
137
|
+
glyphSet: opts.glyphSet,
|
|
138
|
+
size: opts.size ?? "lg",
|
|
139
|
+
labels: opts.labels,
|
|
140
|
+
accent: opts.accent,
|
|
141
|
+
allowShellOwned: shellOwnedLit,
|
|
142
|
+
});
|
|
143
|
+
let spots = [];
|
|
144
|
+
let cycleMs = 0;
|
|
145
|
+
let done = [];
|
|
146
|
+
/** What we last painted per part — the dirty guard that keeps this a few DOM writes a second. */
|
|
147
|
+
const painted = new Map();
|
|
148
|
+
/** What tone each caption last had, for the same reason. */
|
|
149
|
+
const toned = new Map();
|
|
150
|
+
/** Controls the rider is holding right now. These paint green whatever the timeline is doing. */
|
|
151
|
+
const held = new Set();
|
|
152
|
+
// ── build ───────────────────────────────────────────────────────────────────────────────────────
|
|
153
|
+
function buildSpots(steps) {
|
|
154
|
+
for (const s of spots)
|
|
155
|
+
s.tag.remove();
|
|
156
|
+
painted.clear();
|
|
157
|
+
toned.clear();
|
|
158
|
+
let at = 0;
|
|
159
|
+
spots = steps.map((step) => {
|
|
160
|
+
const holdMs = step.holdMs ?? SPOTLIGHT_HOLD_MS;
|
|
161
|
+
const from = at;
|
|
162
|
+
at += holdMs + (step.gapMs ?? SPOTLIGHT_GAP_MS);
|
|
163
|
+
const anchorToken = anchorTokenOf(step);
|
|
164
|
+
const spot = {
|
|
165
|
+
step,
|
|
166
|
+
channels: channelsOf(step),
|
|
167
|
+
// Only BUTTONS can be observed as held: `onButton` is the only stream we have, and a stick's
|
|
168
|
+
// deflection isn't a press. A stick-only step therefore never counts as performed, which is
|
|
169
|
+
// honest — we'd need axes to know, and the caption is doing the teaching either way.
|
|
170
|
+
required: step.combo.map((t) => parseToken(t)).flatMap((p) => (p.kind === "button" ? [p.name] : [])),
|
|
171
|
+
anchor: anchorToken ? anchorPartOfToken(anchorToken) : undefined,
|
|
172
|
+
tag: buildTag(step.label),
|
|
173
|
+
from,
|
|
174
|
+
holdMs,
|
|
175
|
+
};
|
|
176
|
+
stage.appendChild(spot.tag);
|
|
177
|
+
return spot;
|
|
178
|
+
});
|
|
179
|
+
cycleMs = Math.max(1, at);
|
|
180
|
+
done = spots.map(() => false);
|
|
181
|
+
placeAll();
|
|
182
|
+
}
|
|
183
|
+
/** The bubble: one line, and the caret that ties it to its control. Nothing else — see `hint`. */
|
|
184
|
+
function buildTag(label) {
|
|
185
|
+
const tag = document.createElement("div");
|
|
186
|
+
tag.className = "rydr-padspot-tag";
|
|
187
|
+
const text = document.createElement("span");
|
|
188
|
+
text.className = "rydr-padspot-label";
|
|
189
|
+
text.textContent = label;
|
|
190
|
+
tag.appendChild(text);
|
|
191
|
+
// The caret ties the bubble to the control directly beneath (or above) it. A separate element rather
|
|
192
|
+
// than a border trick on the bubble, because it has to be recoloured along with it.
|
|
193
|
+
const caret = document.createElement("span");
|
|
194
|
+
caret.className = "rydr-padspot-caret";
|
|
195
|
+
tag.appendChild(caret);
|
|
196
|
+
return tag;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Which side of its control a caption sits on.
|
|
200
|
+
*
|
|
201
|
+
* Top half → above, bottom half → below, so the bubble lands in the free space beyond the drawing
|
|
202
|
+
* rather than across the middle of the control cluster. Derived from the layout, never hardcoded per
|
|
203
|
+
* brand, for the same reason `sideOfPart` is: it has to stay right across five very different control
|
|
204
|
+
* arrangements.
|
|
205
|
+
*
|
|
206
|
+
* ⚠️ For a control in the MIDDLE of the body there is no side that covers nothing — the controls
|
|
207
|
+
* occupy the middle, and a caption near one of them will cross another. A left-stick caption crosses
|
|
208
|
+
* the shoulder row; a d-pad caption crosses the grips. That is the same trade the shell's original
|
|
209
|
+
* shift demo made deliberately (a caption sitting ON its control is what makes it unambiguous, and a
|
|
210
|
+
* control hidden for two seconds costs less than a caption the rider has to trace back to its button).
|
|
211
|
+
* `captionSide` is the escape when a particular screen disagrees.
|
|
212
|
+
*/
|
|
213
|
+
function sideFor(part) {
|
|
214
|
+
if (opts.captionSide && opts.captionSide !== "auto")
|
|
215
|
+
return opts.captionSide;
|
|
216
|
+
const layout = pad.layout();
|
|
217
|
+
const centre = part ? partCentreOf(layout, part) : undefined;
|
|
218
|
+
const midY = layout.viewBox[1] + layout.viewBox[3] / 2;
|
|
219
|
+
return (centre?.y ?? midY) <= midY ? "above" : "below";
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Where a control sits, in page pixels relative to the stage.
|
|
223
|
+
*
|
|
224
|
+
* Projected from the layout's own GEOMETRY through the SVG's screen matrix, rather than measured off
|
|
225
|
+
* the part's `getBoundingClientRect()`. That distinction is load-bearing for the sticks: the node
|
|
226
|
+
* registered as `lstickPress` lives *inside* the translated group that deflects the cap, so measuring
|
|
227
|
+
* it pins the caption to the moving thumb-cap and the bubble drifts around as the stick sweeps. The
|
|
228
|
+
* geometry is the stick's RESTING centre — the well — which is what the caption should aim at and the
|
|
229
|
+
* one point on a stick that doesn't move.
|
|
230
|
+
*
|
|
231
|
+
* It also means nothing here depends on a transform anywhere in the drawing, so a caption can be
|
|
232
|
+
* placed once and stay correct however the control animates underneath it.
|
|
233
|
+
*/
|
|
234
|
+
function projectPart(part) {
|
|
235
|
+
const svgEl = pad.svg().svg;
|
|
236
|
+
const geom = partGeomOf(pad.layout(), part);
|
|
237
|
+
const ctm = svgEl.getScreenCTM();
|
|
238
|
+
if (!geom || !ctm)
|
|
239
|
+
return null;
|
|
240
|
+
let x;
|
|
241
|
+
let yTop;
|
|
242
|
+
let yBottom;
|
|
243
|
+
if (geom.kind === "circle") {
|
|
244
|
+
x = geom.cx;
|
|
245
|
+
yTop = geom.cy - geom.r;
|
|
246
|
+
yBottom = geom.cy + geom.r;
|
|
247
|
+
}
|
|
248
|
+
else if (geom.kind === "rect") {
|
|
249
|
+
x = geom.x + geom.w / 2;
|
|
250
|
+
yTop = geom.y;
|
|
251
|
+
yBottom = geom.y + geom.h;
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
const c = partCentre(geom);
|
|
255
|
+
x = c.x;
|
|
256
|
+
yTop = c.y;
|
|
257
|
+
yBottom = c.y;
|
|
258
|
+
}
|
|
259
|
+
const box = stage.getBoundingClientRect();
|
|
260
|
+
const pt = svgEl.createSVGPoint();
|
|
261
|
+
pt.x = x;
|
|
262
|
+
pt.y = yTop;
|
|
263
|
+
const a = pt.matrixTransform(ctm);
|
|
264
|
+
pt.y = yBottom;
|
|
265
|
+
const b = pt.matrixTransform(ctm);
|
|
266
|
+
return { cx: a.x - box.left, top: a.y - box.top, bottom: b.y - box.top };
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Park a caption over (or under) the control it names.
|
|
270
|
+
*
|
|
271
|
+
* Pinned in PAGE PIXELS, because the bubble is DOM and the control lives in an SVG viewBox — the same
|
|
272
|
+
* constraint the map view's connector layer has. Anything that changes that mapping has to re-run
|
|
273
|
+
* this: a pad swap rebuilds the SVG outright, and a resize rescales it underneath.
|
|
274
|
+
*/
|
|
275
|
+
function place(spot) {
|
|
276
|
+
const side = sideFor(spot.anchor);
|
|
277
|
+
spot.tag.classList.toggle("above", side === "above");
|
|
278
|
+
spot.tag.classList.toggle("below", side === "below");
|
|
279
|
+
const at = spot.anchor ? projectPart(spot.anchor) : null;
|
|
280
|
+
// A layout may genuinely lack the part (a Zwift pod has no sticks or triggers). Centre the caption
|
|
281
|
+
// on the drawing rather than dropping it: the words are still the lesson.
|
|
282
|
+
if (!at) {
|
|
283
|
+
spot.tag.style.left = "50%";
|
|
284
|
+
spot.tag.style.top = "50%";
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
spot.tag.style.left = `${at.cx}px`;
|
|
288
|
+
spot.tag.style.top = side === "above" ? `${at.top - 6}px` : `${at.bottom + 6}px`;
|
|
289
|
+
}
|
|
290
|
+
const placeAll = () => spots.forEach((spot) => place(spot));
|
|
291
|
+
// ── paint ───────────────────────────────────────────────────────────────────────────────────────
|
|
292
|
+
const paint = (part, state) => {
|
|
293
|
+
if (painted.get(part) === state)
|
|
294
|
+
return;
|
|
295
|
+
painted.set(part, state);
|
|
296
|
+
pad.driver().setPartState(part, state);
|
|
297
|
+
};
|
|
298
|
+
/**
|
|
299
|
+
* Paint one part, and move the meter if that part is a trigger — the pair that always travels together
|
|
300
|
+
* (see `triggerSideOfPart`). Both writers below go through here so neither can light a shoulder without
|
|
301
|
+
* pulling it.
|
|
302
|
+
*/
|
|
303
|
+
function paintPart(part, state, engaged) {
|
|
304
|
+
paint(part, state);
|
|
305
|
+
const meter = triggerSideOfPart(part);
|
|
306
|
+
if (meter)
|
|
307
|
+
pad.driver().setMeter(meter, engaged ? 1 : 0);
|
|
308
|
+
}
|
|
309
|
+
/** Is the rider performing this step right now? Every button it names has to be down. */
|
|
310
|
+
const riderDoing = (spot) => watch && spot.required.length > 0 && spot.required.every((n) => held.has(n));
|
|
311
|
+
/** Is the rider holding anything this spotlight teaches? That's what makes the timeline stand down. */
|
|
312
|
+
const lessonHeld = () => spots.some((s) => s.required.some((n) => held.has(n)));
|
|
313
|
+
function showTag(spot, index, on, mine) {
|
|
314
|
+
if (on && !spot.tag.classList.contains("show"))
|
|
315
|
+
place(spot);
|
|
316
|
+
spot.tag.classList.toggle("show", on);
|
|
317
|
+
// Only ever recoloured while it's UP: a caption that turned white on its way out would flash through
|
|
318
|
+
// the fade, which reads as a second, different caption leaving rather than the same one.
|
|
319
|
+
if (!on || toned.get(index) === mine)
|
|
320
|
+
return;
|
|
321
|
+
toned.set(index, mine);
|
|
322
|
+
spot.tag.classList.toggle("mine", mine);
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Apply one step's state to the drawing and its caption.
|
|
326
|
+
*
|
|
327
|
+
* `since` is ms into this step's own hold, which only an undirected stick needs — it sweeps its cap
|
|
328
|
+
* around the circle so the drawing says "any direction" rather than parking it somewhere arbitrary.
|
|
329
|
+
*/
|
|
330
|
+
function paintSpot(spot, index, lit, since) {
|
|
331
|
+
const mine = riderDoing(spot);
|
|
332
|
+
const driver = pad.driver();
|
|
333
|
+
const engaged = lit || mine;
|
|
334
|
+
const state = mine ? "done" : lit ? "on" : "off";
|
|
335
|
+
for (const ch of spot.channels) {
|
|
336
|
+
if (ch.stick) {
|
|
337
|
+
let x = 0;
|
|
338
|
+
let y = 0;
|
|
339
|
+
if (engaged) {
|
|
340
|
+
if (ch.stick.vec) {
|
|
341
|
+
x = ch.stick.vec[0];
|
|
342
|
+
y = ch.stick.vec[1];
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
const angle = (Math.max(0, since) / SWEEP_MS) * Math.PI * 2;
|
|
346
|
+
x = Math.cos(angle) * SWEEP_RAMP;
|
|
347
|
+
y = Math.sin(angle) * SWEEP_RAMP;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
driver.setStick(ch.stick.side, x, y, engaged ? "on" : "off");
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
if (!ch.part)
|
|
354
|
+
continue;
|
|
355
|
+
paintPart(ch.part, state, engaged);
|
|
356
|
+
}
|
|
357
|
+
showTag(spot, index, engaged, mine);
|
|
358
|
+
if (mine && !done[index]) {
|
|
359
|
+
done[index] = true;
|
|
360
|
+
opts.onPerformed?.(spot.step, index);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
/** Does any step draw this part? Then it, not the live pass, decides what the part looks like unheld. */
|
|
364
|
+
const ownedByAStep = (part) => spots.some((s) => s.channels.some((ch) => ch.part === part));
|
|
365
|
+
/**
|
|
366
|
+
* Paint every control the rider is holding GREEN, on top of whatever the timeline just drew — the shared
|
|
367
|
+
* live-press pass (`live-presses.ts`), which every view in this family runs.
|
|
368
|
+
*
|
|
369
|
+
* It goes LAST, so a held control wins the part it shares with a step: the rider's own hand is the one
|
|
370
|
+
* thing on the drawing they can verify.
|
|
371
|
+
*/
|
|
372
|
+
const paintLivePresses = createLivePresses({
|
|
373
|
+
state: "done",
|
|
374
|
+
axis: opts.press?.axis?.bind(opts.press),
|
|
375
|
+
shellOwned: shellOwnedLit,
|
|
376
|
+
light: (part, state) => paintPart(part, state, true),
|
|
377
|
+
// The rider's own stick outranks a step's demonstration on that side, for the same reason their press
|
|
378
|
+
// outranks a lit part: one of the two is a picture of their actual hand.
|
|
379
|
+
setStick: (side, x, y, state) => pad.driver().setStick(side, x, y, state === "done" ? "done" : "on"),
|
|
380
|
+
release: (part) => {
|
|
381
|
+
// A part a step draws is HANDED BACK, not blanked: dropping the cached value is enough, since that
|
|
382
|
+
// step repaints it (meter included) on the next frame. Blanking it here would flash one dark frame
|
|
383
|
+
// through a control the lesson is currently lighting.
|
|
384
|
+
if (ownedByAStep(part))
|
|
385
|
+
painted.delete(part);
|
|
386
|
+
else
|
|
387
|
+
paintPart(part, "off", false);
|
|
388
|
+
},
|
|
389
|
+
});
|
|
390
|
+
/**
|
|
391
|
+
* Every step engaged at once, captions up — the `"still"` render.
|
|
392
|
+
*
|
|
393
|
+
* `"still"` means there is no TIMELINE: nothing steps from one control to the next, nothing goes dark
|
|
394
|
+
* between them. It does **not** mean the artwork is a frozen frame. A control whose engaged state is
|
|
395
|
+
* inherently a motion still has to move, or the drawing stops saying what it is: an undirected stick
|
|
396
|
+
* parked at one arbitrary angle reads as "push it that way", when the whole point of a bare `LSTICK`
|
|
397
|
+
* is *any* direction. So the sweep keeps running and `t` is passed straight through.
|
|
398
|
+
*
|
|
399
|
+
* The exception is reduced motion, which asks for no animation at all — there we pass a fixed `t`.
|
|
400
|
+
*/
|
|
401
|
+
function renderStill(t) {
|
|
402
|
+
spots.forEach((spot, i) => paintSpot(spot, i, true, t));
|
|
403
|
+
}
|
|
404
|
+
// ── the loop ────────────────────────────────────────────────────────────────────────────────────
|
|
405
|
+
let raf = 0;
|
|
406
|
+
/** Timeline origin. Cleared whenever the rider takes over, so the tour RESTARTS rather than resumes. */
|
|
407
|
+
let start = 0;
|
|
408
|
+
/** Nothing may light before this. Pushed forward every frame the rider is holding something. */
|
|
409
|
+
let resumeAt = 0;
|
|
410
|
+
/** Which step was last reported to `onStepShown`, so it fires on the change and not every frame. */
|
|
411
|
+
let shown = -1;
|
|
412
|
+
/** Which cycle we were in last frame, so `onCycle` fires once per pass. */
|
|
413
|
+
let cycles = 0;
|
|
414
|
+
/**
|
|
415
|
+
* One frame, for both modes — so the rAF plumbing, the hidden-tab guard and "what does engaged look
|
|
416
|
+
* like" each exist exactly once. `"still"` simply has no timeline to advance.
|
|
417
|
+
*/
|
|
418
|
+
function frame(t) {
|
|
419
|
+
raf = requestAnimationFrame(frame);
|
|
420
|
+
if (document.hidden)
|
|
421
|
+
return;
|
|
422
|
+
if (mode === "still")
|
|
423
|
+
renderStill(reduceMotion ? 0 : t);
|
|
424
|
+
else
|
|
425
|
+
renderTour(t);
|
|
426
|
+
if (watch)
|
|
427
|
+
paintLivePresses(held);
|
|
428
|
+
}
|
|
429
|
+
function renderTour(t) {
|
|
430
|
+
// The rider driving outranks the lesson. `"still"` is exempt: there's no animation to stand down
|
|
431
|
+
// from, just a labelled diagram, and blanking it would take the lesson away mid-press.
|
|
432
|
+
if (lessonHeld()) {
|
|
433
|
+
start = 0;
|
|
434
|
+
resumeAt = t + resumeMs;
|
|
435
|
+
}
|
|
436
|
+
// Held pushes `resumeAt` forward every frame, so one test covers both the press and the quiet beat.
|
|
437
|
+
const waiting = resumeAt > t;
|
|
438
|
+
if (!waiting && !start)
|
|
439
|
+
start = t;
|
|
440
|
+
const elapsed = waiting ? -1 : (t - start) * speed;
|
|
441
|
+
if (!loop && elapsed >= cycleMs) {
|
|
442
|
+
spots.forEach((spot, i) => paintSpot(spot, i, false, 0));
|
|
443
|
+
stopFrames();
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
if (elapsed >= 0) {
|
|
447
|
+
const pass = Math.floor(elapsed / cycleMs);
|
|
448
|
+
if (pass > cycles) {
|
|
449
|
+
cycles = pass;
|
|
450
|
+
opts.onCycle?.();
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
// Out of every step's range, so a waiting frame paints the pad dark and leaves the rider's own press
|
|
454
|
+
// as the only thing on it.
|
|
455
|
+
const at = waiting ? -1 : elapsed % cycleMs;
|
|
456
|
+
let lighting = -1;
|
|
457
|
+
spots.forEach((spot, i) => {
|
|
458
|
+
const lit = at >= spot.from && at < spot.from + spot.holdMs;
|
|
459
|
+
if (lit)
|
|
460
|
+
lighting = i;
|
|
461
|
+
paintSpot(spot, i, lit, at - spot.from);
|
|
462
|
+
});
|
|
463
|
+
if (lighting !== shown) {
|
|
464
|
+
shown = lighting;
|
|
465
|
+
if (lighting >= 0)
|
|
466
|
+
opts.onStepShown?.(spots[lighting].step, lighting);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
function startFrames() {
|
|
470
|
+
if (!raf)
|
|
471
|
+
raf = requestAnimationFrame(frame);
|
|
472
|
+
}
|
|
473
|
+
function stopFrames() {
|
|
474
|
+
if (raf)
|
|
475
|
+
cancelAnimationFrame(raf);
|
|
476
|
+
raf = 0;
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Is anything in `"still"` mode inherently in motion? Only an UNDIRECTED stick: a bare `LSTICK` means
|
|
480
|
+
* "any direction", which is a sweep, not a position. A directed token (`LSTICK:left`) is a place the
|
|
481
|
+
* cap sits, and buttons are just lit — neither needs a frame after the first.
|
|
482
|
+
*/
|
|
483
|
+
const stillAnimates = () => !reduceMotion && spots.some((s) => s.channels.some((c) => c.stick && !c.stick.vec));
|
|
484
|
+
/**
|
|
485
|
+
* `"still"` only takes frames when it has a reason to: an animating control, or `watch` (where a press
|
|
486
|
+
* still has to turn green). A permanent spotlight on a plain button costs nothing per frame, which is
|
|
487
|
+
* part of why you'd ask for it.
|
|
488
|
+
*/
|
|
489
|
+
function begin() {
|
|
490
|
+
renderStill(0);
|
|
491
|
+
if (mode === "tour" || watch || stillAnimates())
|
|
492
|
+
startFrames();
|
|
493
|
+
}
|
|
494
|
+
// ── input ───────────────────────────────────────────────────────────────────────────────────────
|
|
495
|
+
const offButton = watch
|
|
496
|
+
? opts.press?.onButton(({ name, edge }) => {
|
|
497
|
+
if (edge === "down")
|
|
498
|
+
held.add(name);
|
|
499
|
+
else
|
|
500
|
+
held.delete(name);
|
|
501
|
+
// A part whose state is decided by `held` has to be repainted next frame; drop the cached value
|
|
502
|
+
// so the dirty guard doesn't hold the stale one.
|
|
503
|
+
for (const spot of spots) {
|
|
504
|
+
if (!spot.required.includes(name))
|
|
505
|
+
continue;
|
|
506
|
+
for (const ch of spot.channels)
|
|
507
|
+
if (ch.part)
|
|
508
|
+
painted.delete(ch.part);
|
|
509
|
+
}
|
|
510
|
+
// …and the pressed control's own part, which turns green whether or not a step names it.
|
|
511
|
+
const own = litPartOfControl(name);
|
|
512
|
+
if (own)
|
|
513
|
+
painted.delete(own);
|
|
514
|
+
// In "still" + watch the loop is already running and will repaint on the next frame with a live
|
|
515
|
+
// clock; painting here would write a stale one and stutter an in-flight sweep.
|
|
516
|
+
})
|
|
517
|
+
: undefined;
|
|
518
|
+
// ── relayout ────────────────────────────────────────────────────────────────────────────────────
|
|
519
|
+
// A pad swap rebuilds the SVG outright, so every measured caption position is stale.
|
|
520
|
+
const offRebuild = pad.onRebuild(() => {
|
|
521
|
+
painted.clear();
|
|
522
|
+
placeAll();
|
|
523
|
+
// A swap with no loop running (a still, non-watching spotlight) has nobody to repaint it.
|
|
524
|
+
if (mode === "still" && !raf)
|
|
525
|
+
renderStill(0);
|
|
526
|
+
});
|
|
527
|
+
const resize = new ResizeObserver(() => placeAll());
|
|
528
|
+
resize.observe(stage);
|
|
529
|
+
buildSpots(opts.steps);
|
|
530
|
+
begin();
|
|
531
|
+
return {
|
|
532
|
+
root,
|
|
533
|
+
pad,
|
|
534
|
+
setSteps(steps) {
|
|
535
|
+
buildSpots(steps);
|
|
536
|
+
start = 0;
|
|
537
|
+
shown = -1;
|
|
538
|
+
cycles = 0;
|
|
539
|
+
pad.driver().clearAll();
|
|
540
|
+
if (mode === "still") {
|
|
541
|
+
renderStill(0);
|
|
542
|
+
if (watch || stillAnimates())
|
|
543
|
+
startFrames();
|
|
544
|
+
}
|
|
545
|
+
else
|
|
546
|
+
startFrames();
|
|
547
|
+
},
|
|
548
|
+
play: startFrames,
|
|
549
|
+
pause: stopFrames,
|
|
550
|
+
performed: () => done.slice(),
|
|
551
|
+
dispose() {
|
|
552
|
+
stopFrames();
|
|
553
|
+
resize.disconnect();
|
|
554
|
+
offRebuild();
|
|
555
|
+
offButton?.();
|
|
556
|
+
// Release everything before detaching: a control lit at teardown must not survive a re-mount.
|
|
557
|
+
pad.driver().clearAll();
|
|
558
|
+
pad.dispose();
|
|
559
|
+
root.remove();
|
|
560
|
+
},
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
//# sourceMappingURL=control-spotlight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"control-spotlight.js","sourceRoot":"","sources":["../../../src/ui/controller/control-spotlight.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control-trainer.d.ts","sourceRoot":"","sources":["../../../src/ui/controller/control-trainer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzD,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,KAAK,EAAkB,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"control-trainer.d.ts","sourceRoot":"","sources":["../../../src/ui/controller/control-trainer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzD,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,KAAK,EAAkB,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAK3E,OAAO,EAAgD,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAC9G,OAAO,KAAK,EAAE,WAAW,EAAW,MAAM,YAAY,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAE,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAC/E,IAAI,CAAC,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC9B,QAAQ,CAAC,EAAE;QACT,OAAO,EAAE;YAAE,QAAQ,CAAC,EAAE,QAAQ,CAAA;SAAE,CAAC;QACjC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,EAAE;YAAE,QAAQ,CAAC,EAAE,QAAQ,CAAA;SAAE,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;KACzE,CAAC;CACH;AAED,MAAM,WAAW,WAAW;IAC1B,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,mFAAmF;IACnF,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sCAAsC;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,qBAAqB;IACpC,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IAC9B,mGAAmG;IACnG,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,mBAAmB,CAAC;IACjC,uGAAuG;IACvG,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACxD,2FAA2F;IAC3F,UAAU,CAAC,IAAI,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAC;IAC5B,sCAAsC;IACtC,SAAS,IAAI,SAAS,OAAO,EAAE,CAAC;IAChC,sEAAsE;IACtE,KAAK,IAAI,IAAI,CAAC;IACd,OAAO,IAAI,IAAI,CAAC;CACjB;AAsDD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,qBAAqB,GAAG,cAAc,CA4dlG"}
|
|
@@ -55,15 +55,12 @@
|
|
|
55
55
|
*/
|
|
56
56
|
import { createComboStrip } from "./combo-strip.js";
|
|
57
57
|
import { mountControllerPad } from "./controller-pad.js";
|
|
58
|
-
import {
|
|
58
|
+
import { heldParts } from "./live-presses.js";
|
|
59
|
+
import { litPartOfControl } from "./parts-map.js";
|
|
59
60
|
import { STICK_LIT_DEADZONE } from "./pad-svg.js";
|
|
60
61
|
import { injectPadCss } from "./styles.js";
|
|
61
62
|
import { isShellControl, parseToken, STICK_DIR_VECTOR } from "./tokens.js";
|
|
62
63
|
const CHECK = "✓";
|
|
63
|
-
/** Is this one of the four directions the d-pad SHARES with the left stick? */
|
|
64
|
-
function isDpadName(name) {
|
|
65
|
-
return name === "UP" || name === "DOWN" || name === "LEFT" || name === "RIGHT";
|
|
66
|
-
}
|
|
67
64
|
/**
|
|
68
65
|
* Demonstration timings, in ms.
|
|
69
66
|
*
|
|
@@ -369,23 +366,10 @@ export function mountControlTrainer(host, opts) {
|
|
|
369
366
|
// it belongs to has been completed.
|
|
370
367
|
// Pass 2 — the hardware. EVERY held control goes blue, whether or not the lesson mentions it: the
|
|
371
368
|
// pad is a picture of the controller, and a press it ignores looks like a fault.
|
|
372
|
-
const axis = opts.press?.axis;
|
|
373
|
-
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
// UP/DOWN/LEFT/RIGHT come from the d-pad AND from the left stick past its deadzone (see
|
|
377
|
-
// protocol/buttons.ts) — nothing in the canonical stream says which. So when the axes already
|
|
378
|
-
// explain a direction, DON'T light the d-pad: the stick's own cap is the truthful picture, and
|
|
379
|
-
// lighting both makes it look like the rider is pressing a d-pad they never touched.
|
|
380
|
-
// `mountControllerPad` has always done this; the "reflect everything" pass added here skipped it.
|
|
381
|
-
if (axis && isDpadName(name)) {
|
|
382
|
-
const [vx, vy] = DPAD_DIRECTIONS[name];
|
|
383
|
-
if (lx * vx + ly * vy > STICK_LIT_DEADZONE)
|
|
384
|
-
continue;
|
|
385
|
-
}
|
|
386
|
-
const part = litPartOfControl(name);
|
|
387
|
-
if (part)
|
|
388
|
-
wanted.set(part, "on");
|
|
369
|
+
const axis = opts.press?.axis?.bind(opts.press);
|
|
370
|
+
// Blue, not green: in a lesson green is reserved for "this step is finished".
|
|
371
|
+
for (const part of heldParts({ held: held, axis, shellOwned: teachesShellOwned })) {
|
|
372
|
+
wanted.set(part, "on");
|
|
389
373
|
}
|
|
390
374
|
// Both sticks, live, for the same reason — and this overrides any demonstration on that side.
|
|
391
375
|
if (axis) {
|