@rydr/game-sdk 7.0.0 → 7.1.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/README.md +19 -0
- package/dist/protocol/glyphs.d.ts +7 -0
- package/dist/protocol/glyphs.d.ts.map +1 -1
- package/dist/protocol/glyphs.js +26 -0
- package/dist/protocol/glyphs.js.map +1 -1
- package/dist/protocol/version.d.ts +1 -1
- package/dist/protocol/version.js +1 -1
- package/dist/three/perf-overlay.d.ts +52 -2
- package/dist/three/perf-overlay.d.ts.map +1 -1
- package/dist/three/perf-overlay.js +188 -3
- package/dist/three/perf-overlay.js.map +1 -1
- package/dist/ui/README.md +119 -0
- package/dist/ui/controller/combo-demo.d.ts +74 -0
- package/dist/ui/controller/combo-demo.d.ts.map +1 -0
- package/dist/ui/controller/combo-demo.js +295 -0
- package/dist/ui/controller/combo-demo.js.map +1 -0
- package/dist/ui/controller/combo-strip.d.ts +36 -0
- package/dist/ui/controller/combo-strip.d.ts.map +1 -0
- package/dist/ui/controller/combo-strip.js +66 -0
- package/dist/ui/controller/combo-strip.js.map +1 -0
- package/dist/ui/controller/control-trainer.d.ts +132 -0
- package/dist/ui/controller/control-trainer.d.ts.map +1 -0
- package/dist/ui/controller/control-trainer.js +570 -0
- package/dist/ui/controller/control-trainer.js.map +1 -0
- package/dist/ui/controller/controller-map.d.ts +82 -0
- package/dist/ui/controller/controller-map.d.ts.map +1 -0
- package/dist/ui/controller/controller-map.js +291 -0
- package/dist/ui/controller/controller-map.js.map +1 -0
- package/dist/ui/controller/controller-pad.d.ts +122 -0
- package/dist/ui/controller/controller-pad.d.ts.map +1 -0
- package/dist/ui/controller/controller-pad.js +200 -0
- package/dist/ui/controller/controller-pad.js.map +1 -0
- package/dist/ui/controller/index.d.ts +26 -0
- package/dist/ui/controller/index.d.ts.map +1 -0
- package/dist/ui/controller/index.js +26 -0
- package/dist/ui/controller/index.js.map +1 -0
- package/dist/ui/controller/input-chip.d.ts +100 -0
- package/dist/ui/controller/input-chip.d.ts.map +1 -0
- package/dist/ui/controller/input-chip.js +322 -0
- package/dist/ui/controller/input-chip.js.map +1 -0
- package/dist/ui/controller/layouts/dualsense.d.ts +12 -0
- package/dist/ui/controller/layouts/dualsense.d.ts.map +1 -0
- package/dist/ui/controller/layouts/dualsense.js +64 -0
- package/dist/ui/controller/layouts/dualsense.js.map +1 -0
- package/dist/ui/controller/layouts/index.d.ts +20 -0
- package/dist/ui/controller/layouts/index.d.ts.map +1 -0
- package/dist/ui/controller/layouts/index.js +34 -0
- package/dist/ui/controller/layouts/index.js.map +1 -0
- package/dist/ui/controller/layouts/joycon.d.ts +21 -0
- package/dist/ui/controller/layouts/joycon.d.ts.map +1 -0
- package/dist/ui/controller/layouts/joycon.js +76 -0
- package/dist/ui/controller/layouts/joycon.js.map +1 -0
- package/dist/ui/controller/layouts/switch-pro.d.ts +12 -0
- package/dist/ui/controller/layouts/switch-pro.d.ts.map +1 -0
- package/dist/ui/controller/layouts/switch-pro.js +60 -0
- package/dist/ui/controller/layouts/switch-pro.js.map +1 -0
- package/dist/ui/controller/layouts/xbox.d.ts +11 -0
- package/dist/ui/controller/layouts/xbox.d.ts.map +1 -0
- package/dist/ui/controller/layouts/xbox.js +59 -0
- package/dist/ui/controller/layouts/xbox.js.map +1 -0
- package/dist/ui/controller/layouts/zwift.d.ts +27 -0
- package/dist/ui/controller/layouts/zwift.d.ts.map +1 -0
- package/dist/ui/controller/layouts/zwift.js +62 -0
- package/dist/ui/controller/layouts/zwift.js.map +1 -0
- package/dist/ui/controller/pad-svg.d.ts +105 -0
- package/dist/ui/controller/pad-svg.d.ts.map +1 -0
- package/dist/ui/controller/pad-svg.js +289 -0
- package/dist/ui/controller/pad-svg.js.map +1 -0
- package/dist/ui/controller/parts-map.d.ts +96 -0
- package/dist/ui/controller/parts-map.d.ts.map +1 -0
- package/dist/ui/controller/parts-map.js +187 -0
- package/dist/ui/controller/parts-map.js.map +1 -0
- package/dist/ui/controller/styles.d.ts +19 -0
- package/dist/ui/controller/styles.d.ts.map +1 -0
- package/dist/ui/controller/styles.js +364 -0
- package/dist/ui/controller/styles.js.map +1 -0
- package/dist/ui/controller/tokens.d.ts +106 -0
- package/dist/ui/controller/tokens.d.ts.map +1 -0
- package/dist/ui/controller/tokens.js +50 -0
- package/dist/ui/controller/tokens.js.map +1 -0
- package/dist/ui/controller/types.d.ts +157 -0
- package/dist/ui/controller/types.d.ts.map +1 -0
- package/dist/ui/controller/types.js +61 -0
- package/dist/ui/controller/types.js.map +1 -0
- package/dist/ui/index.d.ts +8 -0
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +8 -0
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/keycap.d.ts +14 -0
- package/dist/ui/keycap.d.ts.map +1 -1
- package/dist/ui/keycap.js +5 -0
- package/dist/ui/keycap.js.map +1 -1
- package/dist/ui/showcase/gallery.d.ts +13 -1
- package/dist/ui/showcase/gallery.d.ts.map +1 -1
- package/dist/ui/showcase/gallery.js.map +1 -1
- package/dist/ui/showcase/index.d.ts.map +1 -1
- package/dist/ui/showcase/index.js +257 -1
- package/dist/ui/showcase/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scoped `.rydr-pad-*` CSS for the controller family — the pad drawing, the input chips, the static
|
|
3
|
+
* controller map and the combo demo — injected once into `<head>`.
|
|
4
|
+
*
|
|
5
|
+
* Self-contained like the rest of `@rydr/game-sdk/ui`: no global styles, no dependence on host CSS
|
|
6
|
+
* variables, no font loading. The pad's own tokens (`--pad-body`, `--pad-accent`, …) are declared on
|
|
7
|
+
* `.rydr-pad` itself, so a caller theming the accent sets ONE property via the `accent` option rather
|
|
8
|
+
* than reaching into this stylesheet.
|
|
9
|
+
*
|
|
10
|
+
* Ported from the platform shell's `rydr-controller-diagram.ts`, with `:host` rewritten to `.rydr-pad`
|
|
11
|
+
* and every internal class prefixed — the shell had shadow DOM to scope bare `.part` / `.shape` names;
|
|
12
|
+
* we don't, so the prefix IS the scoping.
|
|
13
|
+
*
|
|
14
|
+
* The type sizes and dimensions here are tuned for a **trainer screen read from a few feet away while
|
|
15
|
+
* pedaling**, exactly as the rest of the kit. They are part of the contract, not defaults to override.
|
|
16
|
+
*/
|
|
17
|
+
const STYLE_ID = "rydr-pad-css";
|
|
18
|
+
let cssInjected = false;
|
|
19
|
+
/** Force the controller CSS into `<head>`. Components call this for you; idempotent. */
|
|
20
|
+
export function injectPadCss() {
|
|
21
|
+
if (cssInjected || document.getElementById(STYLE_ID)) {
|
|
22
|
+
cssInjected = true;
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const style = document.createElement("style");
|
|
26
|
+
style.id = STYLE_ID;
|
|
27
|
+
style.textContent = CSS;
|
|
28
|
+
document.head.appendChild(style);
|
|
29
|
+
cssInjected = true;
|
|
30
|
+
}
|
|
31
|
+
// NOTE: this is a template literal, so a stray backtick inside a CSS comment ENDS THE STRING and the
|
|
32
|
+
// file stops parsing. Quote CSS identifiers with "double quotes" in comments below, not backticks.
|
|
33
|
+
const CSS = `
|
|
34
|
+
/* ══════════════════════════════ THE PAD DRAWING ══════════════════════════════ */
|
|
35
|
+
.rydr-pad {
|
|
36
|
+
--pad-body: rgba(255, 255, 255, 0.10);
|
|
37
|
+
--pad-body-stroke: rgba(255, 255, 255, 0.40);
|
|
38
|
+
--pad-fill: rgba(255, 255, 255, 0.22);
|
|
39
|
+
--pad-stroke: rgba(255, 255, 255, 0.55);
|
|
40
|
+
--pad-glyph: rgba(255, 255, 255, 0.88);
|
|
41
|
+
--pad-accent: #38bdf8;
|
|
42
|
+
--pad-glow: rgba(56, 189, 248, 0.45);
|
|
43
|
+
display: block;
|
|
44
|
+
width: 100%;
|
|
45
|
+
}
|
|
46
|
+
.rydr-pad.sm { max-width: 220px; }
|
|
47
|
+
.rydr-pad.md { max-width: 340px; }
|
|
48
|
+
.rydr-pad.lg { max-width: 460px; }
|
|
49
|
+
/* ghost = static art with no live subscription; used as a placeholder or a backdrop. */
|
|
50
|
+
.rydr-pad.ghost { opacity: 0.25; }
|
|
51
|
+
.rydr-pad > svg { display: block; width: 100%; height: auto; overflow: visible; }
|
|
52
|
+
|
|
53
|
+
.rydr-pad-body { fill: var(--pad-body); stroke: var(--pad-body-stroke); stroke-width: 1.2; }
|
|
54
|
+
/* Every control sits ON a fixed drop shadow and drops onto it when pressed. Colour alone says "this one
|
|
55
|
+
is involved"; landing on its own shadow says "this one is held down". The face is a group so the
|
|
56
|
+
glyph travels with it — a shape that moved while its letter stayed put reads as broken, not pressed. */
|
|
57
|
+
.rydr-pad-shadow { fill: rgba(0,0,0,0.55); stroke: none; pointer-events: none; }
|
|
58
|
+
.rydr-pad-face { transition: transform 70ms cubic-bezier(0.3, 0, 0.2, 1); }
|
|
59
|
+
.rydr-pad-part.on .rydr-pad-face { transform: translateY(1.4px); }
|
|
60
|
+
/* Only a real press sinks. A white "do this" prompt must stay RAISED — it is an instruction, and a
|
|
61
|
+
sunk-looking prompt would claim the rider is already holding it. */
|
|
62
|
+
.rydr-pad-decor .rydr-pad-shape { fill: rgba(255,255,255,0.06); stroke: var(--pad-body-stroke); stroke-width: 1; }
|
|
63
|
+
.rydr-pad-well { fill: rgba(0,0,0,0.30); stroke: var(--pad-body-stroke); stroke-width: 1; }
|
|
64
|
+
|
|
65
|
+
.rydr-pad-part .rydr-pad-shape {
|
|
66
|
+
fill: var(--pad-fill);
|
|
67
|
+
stroke: var(--pad-stroke);
|
|
68
|
+
stroke-width: 1;
|
|
69
|
+
transition: fill 60ms linear;
|
|
70
|
+
}
|
|
71
|
+
.rydr-pad-part .rydr-pad-glyph {
|
|
72
|
+
fill: var(--pad-glyph);
|
|
73
|
+
font: 600 9px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
74
|
+
text-anchor: middle;
|
|
75
|
+
dominant-baseline: central;
|
|
76
|
+
pointer-events: none;
|
|
77
|
+
transition: fill 60ms linear;
|
|
78
|
+
}
|
|
79
|
+
.rydr-pad-part .rydr-pad-glyph.sm { font-size: 7px; }
|
|
80
|
+
.rydr-pad-part .rydr-pad-glyph-vec {
|
|
81
|
+
fill: none;
|
|
82
|
+
stroke: var(--pad-glyph);
|
|
83
|
+
stroke-width: 1.6;
|
|
84
|
+
stroke-linecap: round;
|
|
85
|
+
pointer-events: none;
|
|
86
|
+
transition: stroke 60ms linear;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* Pressed. The glow is decoration; the fill change is the information. */
|
|
90
|
+
.rydr-pad-part.on .rydr-pad-shape {
|
|
91
|
+
fill: var(--pad-accent);
|
|
92
|
+
filter: drop-shadow(0 0 5px var(--pad-glow));
|
|
93
|
+
}
|
|
94
|
+
.rydr-pad-part.on .rydr-pad-glyph { fill: #06121a; }
|
|
95
|
+
.rydr-pad-part.on .rydr-pad-glyph-vec { stroke: #06121a; }
|
|
96
|
+
|
|
97
|
+
/* Shell-owned: the resistance bumpers / paddles, View, Guide. A game can never bind or light these,
|
|
98
|
+
so they are drawn faint — the rider can tell "not used by this game" from "not the game's to use". */
|
|
99
|
+
.rydr-pad-part.shell-owned { opacity: 0.4; }
|
|
100
|
+
/* Two exceptions, both cases where the control IS the subject rather than someone else's business:
|
|
101
|
+
a lesson about them (lit), and a callout pointing at them (referenced). Drawing the subject at 40%
|
|
102
|
+
reads as "disabled". */
|
|
103
|
+
.rydr-pad-part.shell-owned.referenced,
|
|
104
|
+
.rydr-pad-part.shell-owned.hint,
|
|
105
|
+
.rydr-pad-part.shell-owned.on,
|
|
106
|
+
.rydr-pad-part.shell-owned.done { opacity: 1; }
|
|
107
|
+
|
|
108
|
+
/* ── lesson states ──────────────────────────────────────────────────────────
|
|
109
|
+
Three things a part has to be able to say, in order:
|
|
110
|
+
.hint "do this" — whitish, pulsing. An INSTRUCTION, so it must not look like a press.
|
|
111
|
+
.on "you're doing it" — the accent, identical to a real press.
|
|
112
|
+
.done "that's finished" — green, and it stays.
|
|
113
|
+
The pulse is what distinguishes the instruction from a press: colour alone would be ambiguous on a
|
|
114
|
+
pad whose pressed state is also a solid fill. */
|
|
115
|
+
.rydr-pad-part.hint .rydr-pad-shape {
|
|
116
|
+
fill: rgba(255,255,255,0.92); stroke: #ffffff; stroke-width: 1.4;
|
|
117
|
+
animation: rydr-pad-pulse 1.6s ease-in-out infinite;
|
|
118
|
+
}
|
|
119
|
+
.rydr-pad-part.hint .rydr-pad-glyph { fill: #0b0f17; }
|
|
120
|
+
.rydr-pad-part.hint .rydr-pad-glyph-vec { stroke: #0b0f17; }
|
|
121
|
+
.rydr-pad-part.done .rydr-pad-shape {
|
|
122
|
+
fill: var(--pad-ok, #22c55e); stroke: var(--pad-ok, #22c55e);
|
|
123
|
+
filter: drop-shadow(0 0 5px rgba(34,197,94,0.45));
|
|
124
|
+
}
|
|
125
|
+
.rydr-pad-part.done .rydr-pad-glyph { fill: #05210f; }
|
|
126
|
+
.rydr-pad-part.done .rydr-pad-glyph-vec { stroke: #05210f; }
|
|
127
|
+
@keyframes rydr-pad-pulse {
|
|
128
|
+
0%, 100% { opacity: 0.6; }
|
|
129
|
+
50% { opacity: 1; }
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* Not present on the rider's current device (opt-in, via the "available" option). */
|
|
133
|
+
.rydr-pad-part.unavailable { opacity: 0.22; }
|
|
134
|
+
|
|
135
|
+
.rydr-pad-meter { fill: var(--pad-accent); opacity: 0.85; }
|
|
136
|
+
|
|
137
|
+
.rydr-pad-stick.deflected .rydr-pad-shape {
|
|
138
|
+
fill: var(--pad-accent);
|
|
139
|
+
filter: drop-shadow(0 0 5px var(--pad-glow));
|
|
140
|
+
}
|
|
141
|
+
/* A stick a LESSON is demonstrating, as opposed to one the rider is moving. Same whitish pulse as a
|
|
142
|
+
hinted button: the accent has to stay reserved for "you are doing this", or the demonstration claims
|
|
143
|
+
the rider is already steering. Declared after .deflected so it wins when both could apply. */
|
|
144
|
+
.rydr-pad-stick.hint .rydr-pad-shape {
|
|
145
|
+
fill: rgba(255,255,255,0.92); stroke: #ffffff;
|
|
146
|
+
animation: rydr-pad-pulse 1.6s ease-in-out infinite;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/* NOTE: there is deliberately no \`prefers-color-scheme: light\` block. An earlier version had one and
|
|
150
|
+
it was wrong: it fires off the OS theme rather than the surface the pad is drawn on, so a light-mode
|
|
151
|
+
OS turned the pad black-on-dark inside a dark game. A light surface must opt in explicitly. */
|
|
152
|
+
|
|
153
|
+
/* ══════════════════════════════ INPUT CHIPS ══════════════════════════════ */
|
|
154
|
+
/* One chip per control in a combination. Face/d-pad chips are real keycaps from the kit; these are the
|
|
155
|
+
shapes the kit didn't have: the shoulder rectangle (your "L1 in a rectangle"), the stick pictogram,
|
|
156
|
+
and a small system chip. Sized to sit on the same baseline as a 38px keycap pip. */
|
|
157
|
+
.rydr-pad-chip {
|
|
158
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
159
|
+
min-width: 38px; height: 38px; box-sizing: border-box; padding: 0 10px;
|
|
160
|
+
border-radius: 9px; background: rgba(255,255,255,0.10);
|
|
161
|
+
border: 1.5px solid rgba(255,255,255,0.55); color: #eef4ff;
|
|
162
|
+
font: 800 15px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
163
|
+
letter-spacing: 0.02em; white-space: nowrap;
|
|
164
|
+
transition: background 60ms linear, transform 60ms linear, border-color 60ms linear;
|
|
165
|
+
}
|
|
166
|
+
.rydr-pad-chip.pressed, .rydr-pad-chip.active {
|
|
167
|
+
background: var(--pad-chip-accent, #38bdf8); border-color: var(--pad-chip-accent, #38bdf8);
|
|
168
|
+
color: #06121a; transform: translateY(2px);
|
|
169
|
+
}
|
|
170
|
+
/* the stick pictogram is a fixed-size SVG chip with no text padding */
|
|
171
|
+
.rydr-pad-chip.stick { padding: 0; width: 38px; }
|
|
172
|
+
.rydr-pad-chip.stick > svg { width: 30px; height: 30px; display: block; }
|
|
173
|
+
.rydr-pad-stickpic-well { fill: rgba(0,0,0,0.35); stroke: currentColor; stroke-width: 1.4; opacity: 0.75; }
|
|
174
|
+
.rydr-pad-stickpic-cap { fill: currentColor; opacity: 0.9; transition: transform 120ms ease-out; }
|
|
175
|
+
.rydr-pad-stickpic-arrow { fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
|
|
176
|
+
/* the recessed collar of the top-down style's CLICK: the cap sits sunk inside it */
|
|
177
|
+
.rydr-pad-stickpic-ring { fill: none; stroke: currentColor; stroke-width: 1.3; opacity: 0.55; }
|
|
178
|
+
/* solid arrows — the profile and glyph styles use filled triangles rather than outline chevrons,
|
|
179
|
+
which is most of why they read better than the top-down style at chip size */
|
|
180
|
+
.rydr-pad-stickpic-fill { fill: currentColor; }
|
|
181
|
+
/* the side-view stick of the profile style: its socket and its tapering shaft */
|
|
182
|
+
.rydr-pad-stickpic-base { fill: none; stroke: currentColor; stroke-width: 1.5; opacity: 0.6; }
|
|
183
|
+
.rydr-pad-stickpic-shaft { fill: currentColor; opacity: 0.5; }
|
|
184
|
+
/* the glyph style's heavy outer ring */
|
|
185
|
+
.rydr-pad-stickpic-bigring { fill: none; stroke: currentColor; stroke-width: 2; }
|
|
186
|
+
/* the glyph style is ringless — its solid disc carries the whole mark */
|
|
187
|
+
.rydr-pad-chip.stick.glyph .rydr-pad-stickpic-cap { opacity: 1; }
|
|
188
|
+
|
|
189
|
+
/* ── chip lesson states, mirroring the pad's ─────────────────────────────────
|
|
190
|
+
Only the PLAIN chips (shoulder rectangle, stick pictogram, system chip) are styled here. The
|
|
191
|
+
keycap-backed chips carry their face colour in an inline "--c" custom property, which a stylesheet
|
|
192
|
+
rule cannot outrank — so those go through Keycap.setAccent in JS instead. See input-chip.ts. */
|
|
193
|
+
.rydr-pad-chip.rydr-pad-hint { background: rgba(255,255,255,0.92); border-color: #ffffff; color: #0b0f17;
|
|
194
|
+
animation: rydr-pad-pulse 1.6s ease-in-out infinite; }
|
|
195
|
+
.rydr-pad-chip.rydr-pad-active { background: var(--pad-chip-accent, #38bdf8); border-color: var(--pad-chip-accent, #38bdf8); color: #06121a; }
|
|
196
|
+
.rydr-pad-chip.rydr-pad-done { background: var(--pad-ok, #22c55e); border-color: var(--pad-ok, #22c55e); color: #05210f; }
|
|
197
|
+
|
|
198
|
+
/* A strip of chips: [L2] + [stick◀] · or [L2] → [A] for a sequence. */
|
|
199
|
+
.rydr-pad-strip { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; justify-content: center; }
|
|
200
|
+
.rydr-pad-strip-sep {
|
|
201
|
+
color: rgba(255,255,255,0.55); font: 800 16px system-ui, sans-serif; padding: 0 1px;
|
|
202
|
+
user-select: none;
|
|
203
|
+
}
|
|
204
|
+
/* the "current step" cue in a sequence demo */
|
|
205
|
+
.rydr-pad-strip-sep.done { color: var(--pad-chip-accent, #38bdf8); }
|
|
206
|
+
|
|
207
|
+
/* ══════════════════════════════ STATIC MAP ══════════════════════════════ */
|
|
208
|
+
/* "width: 100%" + a max is deliberate: the component fills whatever host the game gives it (a pause
|
|
209
|
+
overlay, a settings panel) instead of sizing to content. Without a definite width here, the 1fr
|
|
210
|
+
callout tracks below have nothing to resolve against inside a flex host. */
|
|
211
|
+
.rydr-padmap {
|
|
212
|
+
position: relative; display: grid; align-items: center;
|
|
213
|
+
grid-template-columns: minmax(0, 1fr) var(--rydr-padmap-pad-w) minmax(0, 1fr);
|
|
214
|
+
gap: clamp(10px, 2.2vw, 32px);
|
|
215
|
+
width: 100%; max-width: 1100px; margin-inline: auto; box-sizing: border-box;
|
|
216
|
+
font-family: system-ui, sans-serif; color: #eef4ff;
|
|
217
|
+
}
|
|
218
|
+
/* The pad column carries a DEFINITE width. An "auto" track holding a 100%-wide SVG is circular — the
|
|
219
|
+
track sizes to the content and the content sizes to the track — which browsers resolve
|
|
220
|
+
inconsistently and can collapse. --rydr-padmap-pad-w is set from the size preset. */
|
|
221
|
+
.rydr-padmap-padcell { min-width: 0; }
|
|
222
|
+
.rydr-padmap-padcell > .rydr-pad { max-width: none; }
|
|
223
|
+
.rydr-padmap-title {
|
|
224
|
+
grid-column: 1 / -1; text-align: center; margin: 0 0 4px;
|
|
225
|
+
font: 800 clamp(1rem, 2.4vw, 1.35rem) system-ui, sans-serif; letter-spacing: 0.02em;
|
|
226
|
+
}
|
|
227
|
+
.rydr-padmap-col { display: flex; flex-direction: column; gap: 14px; list-style: none; margin: 0; padding: 0; }
|
|
228
|
+
.rydr-padmap-col.left { align-items: flex-end; }
|
|
229
|
+
.rydr-padmap-col.right { align-items: flex-start; }
|
|
230
|
+
.rydr-padmap-item { display: block; transition: opacity 120ms linear; }
|
|
231
|
+
.rydr-padmap-item.disabled { opacity: 0.45; }
|
|
232
|
+
/* the callout's own content inside the shared dark card */
|
|
233
|
+
.rydr-padmap-callout { display: flex; flex-direction: column; align-items: center; gap: 8px; min-width: 96px; }
|
|
234
|
+
.rydr-padmap-label { font: 800 19px system-ui, sans-serif; letter-spacing: 0.03em; line-height: 1.15; text-align: center; overflow-wrap: anywhere; }
|
|
235
|
+
.rydr-padmap-hint { font: 600 13px system-ui, sans-serif; opacity: 0.6; line-height: 1.25; text-align: center; margin-top: -4px; }
|
|
236
|
+
/* connector layer: sits over everything, never intercepts input */
|
|
237
|
+
.rydr-padmap-lines { position: absolute; inset: 0; pointer-events: none; overflow: visible; z-index: 1; }
|
|
238
|
+
.rydr-padmap-line { fill: none; stroke: rgba(255,255,255,0.42); stroke-width: 1.6; stroke-linejoin: round; stroke-linecap: round; }
|
|
239
|
+
/* A DOT terminates the leader, not an arrowhead — the callout convention in technical illustration and
|
|
240
|
+
on console control screens alike. Two reasons it matters more than usual here:
|
|
241
|
+
· a leader line states IDENTITY ("this label names that button"), while an arrowhead states
|
|
242
|
+
direction or flow, which is the wrong thing to say;
|
|
243
|
+
· on a CONTROLLER diagram an arrow already means "push the stick this way" — the pictograms in the
|
|
244
|
+
cards use exactly that glyph — so an arrowhead on a leader overloads a symbol that is already
|
|
245
|
+
spoken for, and can be misread as an input direction. */
|
|
246
|
+
.rydr-padmap-dot { fill: rgba(255,255,255,0.62); }
|
|
247
|
+
/* A disabled action's leader fades WITH its card. Left at full strength it reads as a stray line to
|
|
248
|
+
nowhere, because the card it belongs to has faded out from under it. */
|
|
249
|
+
.rydr-padmap-line.dim { stroke: rgba(255,255,255,0.18); }
|
|
250
|
+
.rydr-padmap-dot.dim { fill: rgba(255,255,255,0.26); }
|
|
251
|
+
|
|
252
|
+
/* Narrow hosts: one column above the pad rather than a squeezed three-column grid. The connector
|
|
253
|
+
layer notices the reflow through its ResizeObserver and re-routes; below this width it hides the
|
|
254
|
+
lines instead of drawing a tangle (the chips still say which button each action uses). */
|
|
255
|
+
@media (max-width: 620px) {
|
|
256
|
+
.rydr-padmap { grid-template-columns: minmax(0, 1fr); }
|
|
257
|
+
.rydr-padmap-padcell { justify-self: center; width: min(100%, var(--rydr-padmap-pad-w)); }
|
|
258
|
+
.rydr-padmap-col.left, .rydr-padmap-col.right { align-items: center; }
|
|
259
|
+
.rydr-padmap-lines { display: none; }
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/* ══════════════════════════════ COMBO DEMO ══════════════════════════════ */
|
|
263
|
+
/* Same reasoning as the map: a definite width, so the pad inside has something to be 100% of. */
|
|
264
|
+
.rydr-paddemo {
|
|
265
|
+
display: flex; flex-direction: column; align-items: center; gap: 12px;
|
|
266
|
+
width: 100%; max-width: 560px; margin-inline: auto; box-sizing: border-box;
|
|
267
|
+
font-family: system-ui, sans-serif; color: #eef4ff;
|
|
268
|
+
}
|
|
269
|
+
.rydr-paddemo-label { font: 800 20px system-ui, sans-serif; letter-spacing: 0.03em; text-align: center; }
|
|
270
|
+
.rydr-paddemo-hint { font: 600 13px system-ui, sans-serif; opacity: 0.6; text-align: center; margin-top: -6px; }
|
|
271
|
+
|
|
272
|
+
/* ══════════════════════════════ GUIDED LESSON ══════════════════════════════ */
|
|
273
|
+
/* Title, then one panel per step across the width, then ONE shared controller beneath them all — both
|
|
274
|
+
halves of a paired lesson live on the same physical pad, and showing that is the teaching. */
|
|
275
|
+
.rydr-padtrainer {
|
|
276
|
+
display: flex; flex-direction: column; align-items: stretch; gap: clamp(12px, 2vw, 22px);
|
|
277
|
+
width: 100%; max-width: 900px; margin-inline: auto; box-sizing: border-box;
|
|
278
|
+
font-family: system-ui, sans-serif; color: #eef4ff;
|
|
279
|
+
}
|
|
280
|
+
.rydr-padtrainer-title {
|
|
281
|
+
margin: 0; text-align: center; letter-spacing: 0.02em;
|
|
282
|
+
font: 800 clamp(1.25rem, 3vw, 1.7rem) system-ui, sans-serif;
|
|
283
|
+
}
|
|
284
|
+
.rydr-padtrainer-row {
|
|
285
|
+
display: grid; gap: clamp(10px, 2vw, 22px);
|
|
286
|
+
grid-template-columns: repeat(var(--rydr-padtrainer-cols, 2), minmax(0, 1fr));
|
|
287
|
+
}
|
|
288
|
+
.rydr-padtrainer-step {
|
|
289
|
+
display: flex; flex-direction: column; align-items: center; gap: 10px;
|
|
290
|
+
padding: 16px 14px; border-radius: 14px; box-sizing: border-box;
|
|
291
|
+
background: rgba(8,14,24,0.7); border: 1.5px solid rgba(255,255,255,0.16);
|
|
292
|
+
backdrop-filter: blur(8px);
|
|
293
|
+
transition: border-color 140ms linear, background 140ms linear;
|
|
294
|
+
}
|
|
295
|
+
.rydr-padtrainer-step.active { border-color: var(--pad-chip-accent, #38bdf8); }
|
|
296
|
+
.rydr-padtrainer-step.done {
|
|
297
|
+
border-color: var(--pad-ok, #22c55e); background: rgba(10,32,20,0.7);
|
|
298
|
+
animation: rydr-padtrainer-flash 640ms ease-out;
|
|
299
|
+
}
|
|
300
|
+
.rydr-padtrainer-head { display: flex; align-items: center; justify-content: center; gap: 8px; }
|
|
301
|
+
.rydr-padtrainer-label { font: 800 19px system-ui, sans-serif; letter-spacing: 0.02em; text-align: center; }
|
|
302
|
+
.rydr-padtrainer-hint { font: 600 13px system-ui, sans-serif; opacity: 0.6; text-align: center; margin-top: -4px; }
|
|
303
|
+
/* The tick beside a finished step. Since the controller drawing carries NO completion colour, this and
|
|
304
|
+
the panel frame are the only confirmation the rider gets — so it has to land, not merely fade in. It
|
|
305
|
+
overshoots and settles, and the panel pulses a green ring once as the class arrives. */
|
|
306
|
+
.rydr-padtrainer-check {
|
|
307
|
+
font: 800 22px system-ui, sans-serif; color: var(--pad-ok, #22c55e);
|
|
308
|
+
opacity: 0; transform: scale(0.5); transition: opacity 160ms ease-out, transform 160ms ease-out;
|
|
309
|
+
}
|
|
310
|
+
.rydr-padtrainer-check.show {
|
|
311
|
+
opacity: 1; transform: scale(1);
|
|
312
|
+
animation: rydr-padtrainer-pop 360ms cubic-bezier(0.2, 1.7, 0.4, 1);
|
|
313
|
+
}
|
|
314
|
+
@keyframes rydr-padtrainer-pop {
|
|
315
|
+
0% { opacity: 0; transform: scale(0.3); }
|
|
316
|
+
60% { opacity: 1; transform: scale(1.28); }
|
|
317
|
+
100% { opacity: 1; transform: scale(1); }
|
|
318
|
+
}
|
|
319
|
+
/* One-shot: it runs as .done is added and does not repeat. */
|
|
320
|
+
@keyframes rydr-padtrainer-flash {
|
|
321
|
+
0% { box-shadow: 0 0 0 0 rgba(34,197,94,0.5); }
|
|
322
|
+
100% { box-shadow: 0 0 0 16px rgba(34,197,94,0); }
|
|
323
|
+
}
|
|
324
|
+
.rydr-padtrainer-pad { display: flex; justify-content: center; }
|
|
325
|
+
/* A lesson's highlights fade; a live pad's do not. The base rule is a 60ms snap so that reflecting a real
|
|
326
|
+
press feels instant — right there, wrong here, where a control appearing or handing over should glide.
|
|
327
|
+
Scoped to the lesson so the live pad is untouched. */
|
|
328
|
+
.rydr-padtrainer .rydr-pad-part .rydr-pad-shape {
|
|
329
|
+
transition: fill 260ms ease, stroke 260ms ease, filter 260ms ease;
|
|
330
|
+
}
|
|
331
|
+
.rydr-padtrainer .rydr-pad-part .rydr-pad-glyph,
|
|
332
|
+
.rydr-padtrainer .rydr-pad-part .rydr-pad-glyph-vec {
|
|
333
|
+
transition: fill 260ms ease, stroke 260ms ease;
|
|
334
|
+
}
|
|
335
|
+
.rydr-padtrainer .rydr-pad-chip {
|
|
336
|
+
transition: background 260ms ease, border-color 260ms ease, color 260ms ease, transform 90ms linear;
|
|
337
|
+
}
|
|
338
|
+
/* …except a press, which must still read as instant even mid-lesson. */
|
|
339
|
+
.rydr-padtrainer .rydr-pad-part.on .rydr-pad-shape { transition: fill 70ms linear, filter 70ms linear; }
|
|
340
|
+
.rydr-padtrainer .rydr-pad-chip.rydr-pad-active { transition: background 70ms linear, border-color 70ms linear, color 70ms linear; }
|
|
341
|
+
/* The shared drawing spans the whole width, so it reads as one pad serving both panels rather than as a
|
|
342
|
+
third column. Its own size preset still caps it. */
|
|
343
|
+
.rydr-padtrainer-pad > .rydr-pad { max-width: min(100%, 520px); }
|
|
344
|
+
|
|
345
|
+
@media (max-width: 620px) {
|
|
346
|
+
.rydr-padtrainer-row { grid-template-columns: minmax(0, 1fr); }
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
@media (prefers-reduced-motion: reduce) {
|
|
350
|
+
.rydr-pad-part .rydr-pad-shape,
|
|
351
|
+
.rydr-pad-part .rydr-pad-glyph,
|
|
352
|
+
.rydr-pad-part .rydr-pad-glyph-vec,
|
|
353
|
+
.rydr-pad-chip,
|
|
354
|
+
.rydr-pad-stickpic-cap { transition: none; }
|
|
355
|
+
.rydr-pad-part.on .rydr-pad-shape,
|
|
356
|
+
.rydr-pad-stick.deflected .rydr-pad-shape { filter: none; }
|
|
357
|
+
/* The lesson's "do this" pulse is suppressed, so the hint has to carry on colour alone — hold it at
|
|
358
|
+
full opacity rather than leaving it stuck at whatever frame the animation was cancelled on. */
|
|
359
|
+
.rydr-pad-part.hint .rydr-pad-shape,
|
|
360
|
+
.rydr-pad-chip.rydr-pad-hint { animation: none; opacity: 1; }
|
|
361
|
+
.rydr-padtrainer-check, .rydr-padtrainer-step { transition: none; animation: none; }
|
|
362
|
+
}
|
|
363
|
+
`;
|
|
364
|
+
//# sourceMappingURL=styles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styles.js","sourceRoot":"","sources":["../../../src/ui/controller/styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,QAAQ,GAAG,cAAc,CAAC;AAChC,IAAI,WAAW,GAAG,KAAK,CAAC;AAExB,wFAAwF;AACxF,MAAM,UAAU,YAAY;IAC1B,IAAI,WAAW,IAAI,QAAQ,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;QACrD,WAAW,GAAG,IAAI,CAAC;QACnB,OAAO;IACT,CAAC;IACD,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAC9C,KAAK,CAAC,EAAE,GAAG,QAAQ,CAAC;IACpB,KAAK,CAAC,WAAW,GAAG,GAAG,CAAC;IACxB,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IACjC,WAAW,GAAG,IAAI,CAAC;AACrB,CAAC;AAED,qGAAqG;AACrG,mGAAmG;AACnG,MAAM,GAAG,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0UX,CAAC"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The vocabulary a game uses to describe its own controls.
|
|
3
|
+
*
|
|
4
|
+
* A game hands the controller views a list of {@link ControlAction}s — "Se déplacer" is the left stick,
|
|
5
|
+
* "Freiner" is the left trigger, "Tir chargé" is the right trigger *plus* the confirm button — and the
|
|
6
|
+
* SDK owns everything else: which physical button that is on the rider's pad, what letter is printed on
|
|
7
|
+
* it, where the callout card sits, how the line routes to it, and how the demo animates it.
|
|
8
|
+
*
|
|
9
|
+
* The tokens are deliberately the SAME canonical vocabulary a game already reads input with
|
|
10
|
+
* (`ButtonName`, `StickName` from `protocol/buttons.ts`), extended only to say "this stick, pushed this
|
|
11
|
+
* way". So a game never maintains a second, parallel naming scheme for its help screen: whatever it
|
|
12
|
+
* passed to `session.isDown(...)` is what it passes here.
|
|
13
|
+
*/
|
|
14
|
+
import type { ButtonName, StickName } from "../../protocol/buttons.js";
|
|
15
|
+
/** A direction a stick can be pushed, for describing a control rather than reading one. */
|
|
16
|
+
export type StickDir = "up" | "down" | "left" | "right";
|
|
17
|
+
/**
|
|
18
|
+
* A control the SHELL owns, which is therefore not a {@link ButtonName} and never arrives on a game's
|
|
19
|
+
* `onButton`.
|
|
20
|
+
*
|
|
21
|
+
* `LB`/`RB` are the UPPER shoulders. Named by position, not by function, for the same reason every other
|
|
22
|
+
* control here is: the position is the fixed fact, while what it *does* belongs on the label. On this
|
|
23
|
+
* platform they drive the rider's trainer resistance − / +.
|
|
24
|
+
*
|
|
25
|
+
* They exist here for one reason: **teaching a rider where they are.** A rider who doesn't know the
|
|
26
|
+
* upper shoulders change resistance can't use them, so the shell needs to draw them and build a guided
|
|
27
|
+
* lesson around them — which means naming them in the same vocabulary as everything else.
|
|
28
|
+
*
|
|
29
|
+
* ⚠️ **A game cannot observe these.** `protocol/buttons.ts` lists the shell-owned keys (`W`/`X` for
|
|
30
|
+
* resistance among them) and the SDK client forwards them as intents rather than button edges, so
|
|
31
|
+
* `session.onButton` will never fire for `LB`/`RB`. A control-legend built by a game may
|
|
32
|
+
* *mention* them, and `mountControllerMap` will happily point a callout at them; only the interactive
|
|
33
|
+
* `mountControlTrainer` needs a source that actually sees them, and today that source is the shell.
|
|
34
|
+
*/
|
|
35
|
+
export type ShellControl = "LB" | "RB";
|
|
36
|
+
/** Anything nameable in a combination: a game button or a shell-owned control. */
|
|
37
|
+
export type ControlName = ButtonName | ShellControl;
|
|
38
|
+
/** Is this a control the shell owns (and a game therefore can't receive)? */
|
|
39
|
+
export declare function isShellControl(name: string): name is ShellControl;
|
|
40
|
+
/**
|
|
41
|
+
* One input inside a combination:
|
|
42
|
+
*
|
|
43
|
+
* - a plain button — `"LT"`, `"DIAMOND_DOWN"`, `"UP"`
|
|
44
|
+
* - a whole stick — `"LSTICK"` (free movement in any direction: "steer", "move")
|
|
45
|
+
* - a stick pushed one way — `"LSTICK:left"`
|
|
46
|
+
*
|
|
47
|
+
* `"LEFT"` and `"LSTICK:left"` are both legitimate and mean different things: `"LEFT"` is the canonical
|
|
48
|
+
* direction (a d-pad press, or the left stick past its deadzone — the platform emits it for both),
|
|
49
|
+
* while `"LSTICK:left"` says specifically "push the stick left", which is what you want when the action
|
|
50
|
+
* needs analog travel or when you're pairing it with another button.
|
|
51
|
+
*/
|
|
52
|
+
export type ComboToken = ControlName | StickName | `${StickName}:${StickDir}`;
|
|
53
|
+
/** One thing the rider can do, and what they press to do it. */
|
|
54
|
+
export interface ControlAction {
|
|
55
|
+
/**
|
|
56
|
+
* What the action does — the text on the callout card.
|
|
57
|
+
*
|
|
58
|
+
* Keep it short. The type size is fixed (a trainer screen read from a few feet away while pedaling),
|
|
59
|
+
* so a long label wraps rather than shrinking. If it doesn't fit, shorten the words.
|
|
60
|
+
*/
|
|
61
|
+
label: string;
|
|
62
|
+
/**
|
|
63
|
+
* The input required. One token = a simple control; several = a combination.
|
|
64
|
+
*
|
|
65
|
+
* Rendered as a chip strip under the label: `[L2] + [✕]` for a simultaneous hold, or
|
|
66
|
+
* `[L2] → [✕]` when {@link sequence} is set.
|
|
67
|
+
*/
|
|
68
|
+
combo: readonly ComboToken[];
|
|
69
|
+
/** The steps happen in ORDER rather than together. Changes the separator and the demo timeline. */
|
|
70
|
+
sequence?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Which token the callout line points at. Defaults to the first **non-generic** token — see
|
|
73
|
+
* `anchorTokenOf` in `parts-map.ts` for the rule and why it exists.
|
|
74
|
+
*/
|
|
75
|
+
anchor?: ComboToken;
|
|
76
|
+
/** An optional second, quieter line under the label. */
|
|
77
|
+
hint?: string;
|
|
78
|
+
/** Which side of the pad the card sits on. Default: the side the anchor button is physically on. */
|
|
79
|
+
side?: "left" | "right";
|
|
80
|
+
/** Greyed out — the action exists but is currently unavailable (locked, on cooldown, out of ammo). */
|
|
81
|
+
disabled?: boolean;
|
|
82
|
+
/** Stable key, for addressing one action in the dynamic view. Falls back to `label`. */
|
|
83
|
+
id?: string;
|
|
84
|
+
}
|
|
85
|
+
/** A token, taken apart. */
|
|
86
|
+
export type ParsedToken = {
|
|
87
|
+
kind: "button";
|
|
88
|
+
name: ControlName;
|
|
89
|
+
} | {
|
|
90
|
+
kind: "stick";
|
|
91
|
+
stick: StickName;
|
|
92
|
+
dir?: StickDir;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Split a token into its parts.
|
|
96
|
+
*
|
|
97
|
+
* The three forms are unambiguous by shape: a `:` means "stick with a direction", a bare `LSTICK`/
|
|
98
|
+
* `RSTICK` means the whole stick, and everything else is a {@link ButtonName} — note that `LSTICK_PRESS`
|
|
99
|
+
* is a *button* (clicking the stick), not a stick, which is why the check is exact rather than a prefix.
|
|
100
|
+
*/
|
|
101
|
+
export declare function parseToken(token: ComboToken): ParsedToken;
|
|
102
|
+
/** The unit vector a stick direction pushes towards, in the SDK's axis convention (right = +1, up = +1). */
|
|
103
|
+
export declare const STICK_DIR_VECTOR: Readonly<Record<StickDir, readonly [number, number]>>;
|
|
104
|
+
/** A stable key for an action — its `id` if it has one, else its label. */
|
|
105
|
+
export declare function actionKey(action: ControlAction): string;
|
|
106
|
+
//# sourceMappingURL=tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../../src/ui/controller/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAEvE,2FAA2F;AAC3F,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAExD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,IAAI,CAAC;AAEvC,kFAAkF;AAClF,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,YAAY,CAAC;AAIpD,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,YAAY,CAEjE;AAED;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,SAAS,GAAG,GAAG,SAAS,IAAI,QAAQ,EAAE,CAAC;AAE9E,gEAAgE;AAChE,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IAC7B,mGAAmG;IACnG,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oGAAoG;IACpG,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACxB,sGAAsG;IACtG,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wFAAwF;IACxF,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,4BAA4B;AAC5B,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,WAAW,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,SAAS,CAAC;IAAC,GAAG,CAAC,EAAE,QAAQ,CAAA;CAAE,CAAC;AAIxD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,WAAW,CASzD;AAED,4GAA4G;AAC5G,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAKlF,CAAC;AAEF,2EAA2E;AAC3E,wBAAgB,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAEvD"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The vocabulary a game uses to describe its own controls.
|
|
3
|
+
*
|
|
4
|
+
* A game hands the controller views a list of {@link ControlAction}s — "Se déplacer" is the left stick,
|
|
5
|
+
* "Freiner" is the left trigger, "Tir chargé" is the right trigger *plus* the confirm button — and the
|
|
6
|
+
* SDK owns everything else: which physical button that is on the rider's pad, what letter is printed on
|
|
7
|
+
* it, where the callout card sits, how the line routes to it, and how the demo animates it.
|
|
8
|
+
*
|
|
9
|
+
* The tokens are deliberately the SAME canonical vocabulary a game already reads input with
|
|
10
|
+
* (`ButtonName`, `StickName` from `protocol/buttons.ts`), extended only to say "this stick, pushed this
|
|
11
|
+
* way". So a game never maintains a second, parallel naming scheme for its help screen: whatever it
|
|
12
|
+
* passed to `session.isDown(...)` is what it passes here.
|
|
13
|
+
*/
|
|
14
|
+
const SHELL_CONTROLS = ["LB", "RB"];
|
|
15
|
+
/** Is this a control the shell owns (and a game therefore can't receive)? */
|
|
16
|
+
export function isShellControl(name) {
|
|
17
|
+
return SHELL_CONTROLS.includes(name);
|
|
18
|
+
}
|
|
19
|
+
const STICKS = ["LSTICK", "RSTICK"];
|
|
20
|
+
/**
|
|
21
|
+
* Split a token into its parts.
|
|
22
|
+
*
|
|
23
|
+
* The three forms are unambiguous by shape: a `:` means "stick with a direction", a bare `LSTICK`/
|
|
24
|
+
* `RSTICK` means the whole stick, and everything else is a {@link ButtonName} — note that `LSTICK_PRESS`
|
|
25
|
+
* is a *button* (clicking the stick), not a stick, which is why the check is exact rather than a prefix.
|
|
26
|
+
*/
|
|
27
|
+
export function parseToken(token) {
|
|
28
|
+
const colon = token.indexOf(":");
|
|
29
|
+
if (colon > 0) {
|
|
30
|
+
const stick = token.slice(0, colon);
|
|
31
|
+
const dir = token.slice(colon + 1);
|
|
32
|
+
if (STICKS.includes(stick))
|
|
33
|
+
return { kind: "stick", stick: stick, dir: dir };
|
|
34
|
+
}
|
|
35
|
+
if (STICKS.includes(token))
|
|
36
|
+
return { kind: "stick", stick: token };
|
|
37
|
+
return { kind: "button", name: token };
|
|
38
|
+
}
|
|
39
|
+
/** The unit vector a stick direction pushes towards, in the SDK's axis convention (right = +1, up = +1). */
|
|
40
|
+
export const STICK_DIR_VECTOR = {
|
|
41
|
+
up: [0, 1],
|
|
42
|
+
down: [0, -1],
|
|
43
|
+
left: [-1, 0],
|
|
44
|
+
right: [1, 0],
|
|
45
|
+
};
|
|
46
|
+
/** A stable key for an action — its `id` if it has one, else its label. */
|
|
47
|
+
export function actionKey(action) {
|
|
48
|
+
return action.id ?? action.label;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../../src/ui/controller/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AA8BH,MAAM,cAAc,GAAsB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAEvD,6EAA6E;AAC7E,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACvC,CAAC;AAsDD,MAAM,MAAM,GAAsB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAEvD;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,KAAiB;IAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACd,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QACpC,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QACnC,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAkB,EAAE,GAAG,EAAE,GAAe,EAAE,CAAC;IACxG,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAkB,EAAE,CAAC;IAChF,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAoB,EAAE,CAAC;AACxD,CAAC;AAED,4GAA4G;AAC5G,MAAM,CAAC,MAAM,gBAAgB,GAA0D;IACrF,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IACV,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACb,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACb,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;CACd,CAAC;AAEF,2EAA2E;AAC3E,MAAM,UAAU,SAAS,CAAC,MAAqB;IAC7C,OAAO,MAAM,CAAC,EAAE,IAAI,MAAM,CAAC,KAAK,CAAC;AACnC,CAAC"}
|