@rydr/game-sdk 8.2.0 → 8.3.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.
@@ -9,5 +9,5 @@
9
9
  export declare const RYDR_PROTOCOL_VERSION: 30;
10
10
  /** Semver of this SDK build. Sent in the handshake for telemetry/debugging.
11
11
  * (Bumped to 2.0.0 by `npm version major` on release — see CHANGELOG [Unreleased].) */
12
- export declare const RYDR_SDK_VERSION = "8.2.0";
12
+ export declare const RYDR_SDK_VERSION = "8.3.0";
13
13
  //# sourceMappingURL=version.d.ts.map
@@ -176,5 +176,5 @@
176
176
  export const RYDR_PROTOCOL_VERSION = 30;
177
177
  /** Semver of this SDK build. Sent in the handshake for telemetry/debugging.
178
178
  * (Bumped to 2.0.0 by `npm version major` on release — see CHANGELOG [Unreleased].) */
179
- export const RYDR_SDK_VERSION = "8.2.0";
179
+ export const RYDR_SDK_VERSION = "8.3.0";
180
180
  //# sourceMappingURL=version.js.map
package/dist/ui/README.md CHANGED
@@ -47,6 +47,7 @@ feed its `{x, y, onScreen}` into `setScreenPos`/`setVisible` — don't compute p
47
47
  | `mountSubtitle(host, opts)` | fn | A boxless, self-dismissing caption (the voice-over "auto" look): big centred line + speaker name, no frame. `anchor`/`offset` place it; `showName` toggles the name. |
48
48
  | `mountChoiceCard(host, opts)` | fn | A player choice menu: prompt + highlighted option list (▲▼ move, A confirm). |
49
49
  | `mountRarityCard(host, opts)` / `buildRarityCard(opts)` | fn | A generic rarity-tinted item/creature card. |
50
+ | `createActionMarker(host, opts)` | fn | **"There is something to do THERE"** — a card pinned to a point in the world (you `place(x, y)` it every frame with the point projected to screen), the keycap joining it once the rider is close enough (`setNear`), and a caret clamping it to the border with an arrow while the point is off screen. Two independent axes: `tone` is MOTION (`hint` still · `action` breathing · `urgent` hammering), `color` is SUBJECT (`red`/`danger`, `green`/`success`, `cyan`/`accent`, … — see `MARKER_COLORS`). |
50
51
  | `createPowerRace(opts)` | fn | A timed **power-vs-deadline race** overlay — the "push NOW or lose it" beat. A charge bar (∫power dt) races a clock; first to 100% wins. `start()` then `setPower(w)`+`tick(dt)` per frame; `won`/`lost` via `onResolve`. Recolour with `theme`, relabel with `text`. |
51
52
  | `mountHeatVignette(host, opts)` | fn | **Screen-edge heat vignette alone** — the peripheral amber→orange→red glow that escalates with heat. Take this when your game draws its own heat bar. `setHeat(heat01, locked)` per frame. |
52
53
  | `mountOverheatGlitch(host, opts)` | fn | **Lockout glitch overlay alone** — one-shot RGB chromatic burst on entry, then scanlines + red noise for the lockout. `setLocked(bool)` per frame. |
@@ -67,7 +68,9 @@ Types: `DiamondButton` (`"DIAMOND_UP"\|"DIAMOND_DOWN"\|"DIAMOND_LEFT"\|"DIAMOND_
67
68
  `ButtonSource`, `Keycap`, `Card`, `ActionCard`, `ButtonPrompt`, `DialogueCard`, `ChoiceCard`,
68
69
  `LabeledDiamond`, `ActionDiamond`, `Rarity`, `RarityCard`, `OptionMenu`, `OptionMenuItem`, `OptionMenuOptions`,
69
70
  `ControlAction`, `ComboToken`, `StickDir`, `StickPictogramStyle`, `PadInputSource`, `PadBrandKey`, `PadPart`, `PadLayout`,
70
- `ControllerPad`, `ControllerMap`, `ComboDemo`, `InputChip`, `ComboStrip`.
71
+ `ControllerPad`, `ControllerMap`, `ComboDemo`, `InputChip`, `ComboStrip`,
72
+ `ActionMarker`, `ActionMarkerOptions`, `ActionMarkerTone`, `ActionMarkerColor`, `ActionMarkerRole`
73
+ (+ the `MARKER_COLORS` / `MARKER_ROLE_COLORS` tables).
71
74
 
72
75
  ---
73
76
 
@@ -0,0 +1,187 @@
1
+ /**
2
+ * ACTION MARKER — "there is something to do THERE": a big screen-space card pinned to a point in the
3
+ * world, the A/B/Y/Z keycap joining it once the rider can act, and an arrow at the screen's edge while
4
+ * the point is off screen (PLAT-1283).
5
+ *
6
+ * Grown in the platform's tutorial, where it could be tuned against a real arena, and moved here
7
+ * unchanged (PLAT-1367) — it was written for this from the start, which is why nothing in it reaches for
8
+ * a host: the only dependency is the keycap, and even that comes in through
9
+ * {@link ActionMarkerOptions.keycap} so a host with its own lettering rules (the shell's `shellKeycap`)
10
+ * can supply one. Keep it that way.
11
+ *
12
+ * ## Screen space, not world space
13
+ *
14
+ * The prompt this replaces was painted into the arena's own canvas, in WORLD units — so the 3D arena's
15
+ * floor squash (`GROUND_TILT`) squashed the lettering with it, and the camera zoom decided how big the
16
+ * type was. A prompt is chrome: it is drawn at a fixed size, upright, in CSS pixels, and only its
17
+ * ANCHOR comes from the world (`game.worldToScreen`). Hence DOM rather than canvas — which also buys
18
+ * the real SDK keycap, real text rendering and CSS animation instead of a hand-rolled blink.
19
+ *
20
+ * ## Three states, one component (and the consumer only feeds it a point)
21
+ *
22
+ * 1. **Far** — the card alone. "There's a thing over there, and this is what it is."
23
+ * 2. **Near** — the same card plus the keycap, and the card SETTLES: it eases out of its pulse and
24
+ * comes to rest half way up its own swell, lit and ringed ({@link ActionMarker.setNear}). The whole
25
+ * arrival crossfades on one interpolated number (`--rydr-marker-near`), never a class snap.
26
+ * 3. **Off screen** — the card CLAMPS to the border with a caret pointing at the anchor, so the rider
27
+ * is never told to do something they can't see the location of. When the anchor comes back into
28
+ * view the card doesn't jump: it glides to its exact point (the follow smoothing below).
29
+ *
30
+ * The consumer calls {@link ActionMarker.place} every frame with the anchor projected to screen pixels
31
+ * and the marker decides which of the three it is. It never reads the camera, the world or the player.
32
+ *
33
+ * ## The card never moves to get out of the way
34
+ *
35
+ * A card sits where its anchor is, full stop: a prompt that slides about to dodge the character is a
36
+ * prompt whose position stops meaning anything, and it moves at the worst possible moment — as the
37
+ * rider walks up to press the button. That was tried and it is wrong.
38
+ *
39
+ * The overlap it exists in — the rider walks INTO the thing they're told to take, so the two share the
40
+ * same few hundred pixels — is a LAYERING question, and it belongs to the game, not to this component.
41
+ * Mount the layer over the drawing surface (the tutorial does) and markers are chrome that nothing can
42
+ * hide. Mount it under a surface that clears TRANSPARENT and the world covers them instead, so the
43
+ * character walks in front of a card. The marker behaves identically either way; a game picks the one
44
+ * its own scene wants. The cheap half-measure for whichever is chosen: anchor in WORLD space above the
45
+ * object (a metre over a chest, not a fixed pixel lift), and most of the overlap goes away by itself.
46
+ *
47
+ * ## Two axes: how LOUD it is, and what it's ABOUT
48
+ *
49
+ * {@link ActionMarkerTone} is the volume — a standing possibility (`hint`, still), the thing the game
50
+ * is asking for (`action`, breathing), something demanded right now (`urgent`, hammering). It is
51
+ * MOTION, nothing else.
52
+ *
53
+ * {@link ActionMarkerColor} is the subject — go/take (`green`), mind this (`orange`), danger
54
+ * (`red`), objective (`cyan`), special (`purple`), or the chrome default (`steel`). It is PALETTE,
55
+ * nothing else.
56
+ *
57
+ * The two are free of each other on purpose: any colour pairs with any tone, because a game routinely
58
+ * needs to say "danger, calmly" and "the good thing, NOW", and a component that bundles the two makes
59
+ * it pick one. A marker that never names a colour keeps the tone's own historic paint, so the default
60
+ * look is unchanged.
61
+ *
62
+ * Both axes are only CSS custom properties (`--rydr-marker-*`) underneath — the palettes above are a
63
+ * vocabulary for the common cases, not a wall: a game with its own colours sets those properties from
64
+ * its own stylesheet through {@link ActionMarkerOptions.className} and never touches this file.
65
+ */
66
+ import { type DiamondButton, type Keycap } from "./keycap.js";
67
+ /**
68
+ * What kind of prompt this is. The tone drives the palette AND the motion, because those two say the
69
+ * same thing: a possibility sits still, a demand moves.
70
+ *
71
+ * - `hint` — "here's what you can do here". Quiet, no animation. Several can be on screen at once.
72
+ * - `action` — the offer this beat is about. Breathes gently, accented.
73
+ * - `urgent` — "you must do this NOW". Pulses and blinks; meant to be the only one on screen.
74
+ */
75
+ export type ActionMarkerTone = "hint" | "action" | "urgent";
76
+ /**
77
+ * What the prompt is ABOUT, as a COLOUR — the second axis, and free of the first.
78
+ *
79
+ * A tone says how loud the marker is (still, breathing, hammering); a colour says what KIND of thing is
80
+ * at the other end of it: take this, go here, don't, this one is special. Games need both and they do
81
+ * not line up — "get out of the lava" and "the boss chest is open" are equally urgent and must not be
82
+ * the same colour, while a red hint and a red demand are the same subject at two volumes. Bundling them
83
+ * (which is where this component started) forces a game to choose which of the two things it wants
84
+ * to say.
85
+ *
86
+ * So a colour ONLY re-points the palette properties, and a tone only carries motion: every one of the
87
+ * eight pairs with every one of the three tones, and all 24 are valid markers.
88
+ *
89
+ * ## Every colour has two names, and both are the API
90
+ *
91
+ * The hue is what a designer sees and argues about; the ROLE is what a game means. Both are real names
92
+ * here — {@link ActionMarkerRole} is a set of aliases that resolve to these, so `color: "danger"` and
93
+ * `color: "red"` build the same marker. Two names because the two readings are both legitimate and
94
+ * neither survives being made the only one: a palette page that says "danger, warning, warningLight" is
95
+ * unreviewable, and game code that says "orange" has hard-coded a look it doesn't mean.
96
+ *
97
+ * | colour | role | what it's for |
98
+ * |----------|----------------|---------------|
99
+ * | `white` | `default` | a plain prompt — no meaning beyond "there is something here" |
100
+ * | `green` | `success` | a gain, a safe direction, the thing that went right |
101
+ * | `yellow` | `warningLight` | a soft caution: notice this, nothing has gone wrong yet |
102
+ * | `orange` | `warning` | a real warning: a cost, a timer running down, act or lose it |
103
+ * | `red` | `danger` | damage, a hazard, a refusal |
104
+ * | `cyan` | `accent` | the game's own highlight — objectives, waypoints, what it's asking |
105
+ * | `purple` | `accent2` | a second highlight for a different KIND of special: rare, secret, boss |
106
+ * | `steel` | `accentLight` | the RYDR chrome blue — quiet emphasis, the shell's own colour |
107
+ *
108
+ * `white` is the DEFAULT: a marker that names no colour is neutral, and the tone adds no colour of its
109
+ * own. That is the whole point of the split — before it, `urgent` came out amber whether or not the
110
+ * thing at the other end was a warning, which is exactly the confusion this table exists to end.
111
+ */
112
+ export type ActionMarkerColor = "white" | "green" | "yellow" | "orange" | "red" | "cyan" | "purple" | "steel";
113
+ /**
114
+ * The same eight, named for what they MEAN — the name game code should be written in.
115
+ *
116
+ * A game that says `color: "danger"` keeps saying the right thing after someone decides danger is
117
+ * crimson rather than red; a game that says `"red"` doesn't. Accepted anywhere a colour is.
118
+ */
119
+ export type ActionMarkerRole = "default" | "success" | "warningLight" | "warning" | "danger" | "accent" | "accent2" | "accentLight";
120
+ /** Every role → the colour it paints. The one place the two vocabularies are tied together. */
121
+ export declare const MARKER_ROLE_COLORS: Record<ActionMarkerRole, ActionMarkerColor>;
122
+ /**
123
+ * ...and the way back, plus the one-line brief for each — what a palette page prints under the swatch,
124
+ * and what a reviewer argues with. In severity order (neutral → success → the warning ladder) and then
125
+ * the three accents, because that order IS part of the vocabulary: two colours next to each other in
126
+ * this list are two things a rider is meant to be able to tell apart.
127
+ */
128
+ export declare const MARKER_COLORS: {
129
+ color: ActionMarkerColor;
130
+ role: ActionMarkerRole;
131
+ use: string;
132
+ }[];
133
+ export interface ActionMarkerOptions {
134
+ /** The line on the card. Short and in the rider's language — it's read while pedalling. */
135
+ label: string;
136
+ /**
137
+ * Fill the card yourself instead of printing {@link label} — a second line, a cost badge, an icon.
138
+ *
139
+ * The marker owns the BOX (where it sits, how it clamps, when the caret and the keycap show); what is
140
+ * printed inside it is the game's business, and a component that tries to own both grows an option
141
+ * per game. The element handed over is the card itself, already styled and already placed.
142
+ */
143
+ content?: (card: HTMLElement) => void;
144
+ /** Which prompt this is (default `"action"`) — the MOTION axis. */
145
+ tone?: ActionMarkerTone;
146
+ /**
147
+ * What it's about (the COLOUR axis) — independent of {@link tone}, and takes either vocabulary:
148
+ * `"danger"` and `"red"` are the same marker. Defaults to `"white"` / `"default"`, the neutral.
149
+ */
150
+ color?: ActionMarkerColor | ActionMarkerRole;
151
+ /** The button that takes the action — shown once {@link ActionMarker.setNear} is on. */
152
+ button?: DiamondButton;
153
+ /**
154
+ * Build the keycap. Defaults to the SDK's own full A/B/Y/Z diamond; a HOST with its own lettering
155
+ * rules passes its wrapper here (in the shell: `shellKeycap`, so the letter matches the rider's pad).
156
+ */
157
+ keycap?: (button: DiamondButton) => Keycap;
158
+ /** Extra class on the root, for a consumer's own CSS-variable overrides. */
159
+ className?: string;
160
+ /** How far the card stays from the edge when clamped, in CSS pixels (default {@link EDGE_INSET}). */
161
+ edgeInset?: number;
162
+ /** Start hidden (default `false`) — for a marker built ahead of the beat that reveals it. */
163
+ hidden?: boolean;
164
+ }
165
+ export interface ActionMarker {
166
+ /** The root element, already parented into the host it was mounted in. */
167
+ readonly el: HTMLElement;
168
+ /** The card itself — what {@link ActionMarkerOptions.content} was handed, for later edits. */
169
+ readonly card: HTMLElement;
170
+ /** Is the anchor currently off screen (i.e. the card is clamped and the caret showing)? */
171
+ readonly offScreen: boolean;
172
+ /** Move the anchor: a point in the HOST's pixel space, as `worldToScreen` answers it. */
173
+ place(x: number, y: number): void;
174
+ setLabel(text: string): void;
175
+ setTone(tone: ActionMarkerTone): void;
176
+ /** Repaint it, by colour or by role. `null` goes back to the neutral default. */
177
+ setColor(color: ActionMarkerColor | ActionMarkerRole | null): void;
178
+ /** Close enough to act: reveals the keycap and settles the card out of its pulse. */
179
+ setNear(on: boolean): void;
180
+ /** The button is being held — the keycap shows its pressed sink. */
181
+ setPressed(on: boolean): void;
182
+ setVisible(on: boolean): void;
183
+ dispose(): void;
184
+ }
185
+ /** Mount an action marker into `host` (which must be a positioned element covering the play area). */
186
+ export declare function createActionMarker(host: HTMLElement, opts: ActionMarkerOptions): ActionMarker;
187
+ //# sourceMappingURL=action-marker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-marker.d.ts","sourceRoot":"","sources":["../../src/ui/action-marker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,OAAO,EAAgB,KAAK,aAAa,EAAE,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAE5E;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,MAAM,iBAAiB,GACzB,OAAO,GACP,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,KAAK,GACL,MAAM,GACN,QAAQ,GACR,OAAO,CAAC;AAEZ;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,SAAS,GACT,cAAc,GACd,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,aAAa,CAAC;AAElB,+FAA+F;AAC/F,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,gBAAgB,EAAE,iBAAiB,CAS1E,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE;IAAE,KAAK,EAAE,iBAAiB,CAAC;IAAC,IAAI,EAAE,gBAAgB,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAS5F,CAAC;AAUF,MAAM,WAAW,mBAAmB;IAClC,2FAA2F;IAC3F,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IACtC,mEAAmE;IACnE,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB;;;OAGG;IACH,KAAK,CAAC,EAAE,iBAAiB,GAAG,gBAAgB,CAAC;IAC7C,wFAAwF;IACxF,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,MAAM,CAAC;IAC3C,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qGAAqG;IACrG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6FAA6F;IAC7F,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,EAAE,WAAW,CAAC;IACzB,8FAA8F;IAC9F,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,2FAA2F;IAC3F,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,yFAAyF;IACzF,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACtC,iFAAiF;IACjF,QAAQ,CAAC,KAAK,EAAE,iBAAiB,GAAG,gBAAgB,GAAG,IAAI,GAAG,IAAI,CAAC;IACnE,qFAAqF;IACrF,OAAO,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;IAC3B,oEAAoE;IACpE,UAAU,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9B,UAAU,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9B,OAAO,IAAI,IAAI,CAAC;CACjB;AA2CD,sGAAsG;AACtG,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,mBAAmB,GAAG,YAAY,CAwJ7F"}