@stakeplate/core 0.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.
@@ -0,0 +1,276 @@
1
+ import { m as Round } from "./protocol-C1KamF3t.js";
2
+ import { t as NetworkManager } from "./network-pBW8CX1o.js";
3
+ import { n as RootStore } from "./index-BBlN97RY.js";
4
+ import { a as Ticker, t as HudPort } from "./hud-port-DNfotn4U.js";
5
+ import { BootedHud } from "@open-slot-ui/pixi";
6
+ import { Bus, BusGroup, Engine } from "@schmooky/zvuk";
7
+ //#region src/engine/turbo.d.ts
8
+ /** Delay multipliers per turbo level: off (1×) / turbo (0.4×) / super (0.12×). */
9
+ declare const DEFAULT_TURBO_SPEEDS: number[];
10
+ interface TurboState {
11
+ /** 0 = off, 1 = turbo, 2 = super — the HUD turbo cycler's index. */
12
+ readonly level: number;
13
+ /** The delay multiplier for the current level (`ctx.turbo.delay` already applies it). */
14
+ readonly speed: number;
15
+ /** True once the player slam-stopped this round (delays resolve instantly). */
16
+ readonly skipped: boolean;
17
+ /** A turbo- + slam-stop-aware delay for GAME animations. NOT for compliance waits. */
18
+ delay(ms: number): Promise<void>;
19
+ }
20
+ declare class TurboClock implements TurboState {
21
+ private readonly speeds;
22
+ private _level;
23
+ private _skipped;
24
+ private readonly pending;
25
+ constructor(speeds?: number[]);
26
+ get level(): number;
27
+ get speed(): number;
28
+ get skipped(): boolean;
29
+ /** Set the turbo level (clamped to the configured speeds). */
30
+ setLevel(index: number): void;
31
+ /** Slam-stop: resolve every in-flight delay + make the rest of the round instant. */
32
+ skip(): void;
33
+ /** Clear the slam-stop flag — the core calls this at the start of each spin. */
34
+ resetSkip(): void;
35
+ delay(ms: number): Promise<void>;
36
+ }
37
+ //#endregion
38
+ //#region src/engine/round.d.ts
39
+ /** Money facts of a settled round (all MAJOR units except `multiplier`, a ratio). */
40
+ interface RoundInfo {
41
+ mode: string;
42
+ /** Base bet (before the mode's cost multiplier). */
43
+ bet: number;
44
+ /** Mode cost multiplier (× base bet) — the stake charged. */
45
+ cost: number;
46
+ /** The amount staked = bet × cost. */
47
+ stake: number;
48
+ /** Round multiplier relative to the BASE bet (totalWin / bet). */
49
+ multiplier: number;
50
+ /** Total win credited this round (derived: multiplier × bet). */
51
+ totalWin: number;
52
+ /** The server's AUTHORITATIVE win (`raw.payout`, major units); falls back to totalWin
53
+ * when the round carries no explicit payout. Trust this over `totalWin` when they differ. */
54
+ payout: number;
55
+ }
56
+ /**
57
+ * A resolved round: money facts + the game's parsed model + the raw wire round. `E` is the
58
+ * game's book-event type — declared once on `createStakeGame`, so `raw` (and `interpretBook`)
59
+ * are TYPED rather than `unknown`.
60
+ */
61
+ interface GameRound<T = unknown, E = unknown> extends RoundInfo {
62
+ /** The game's parsed model, from `interpretBook` — driven to the Present phase. */
63
+ data: T;
64
+ /** Whether the raw round still needs `/wallet/end-round` (the engine settles it). */
65
+ active: boolean;
66
+ /** Authoritative post-settlement balance (major units) — applied by the Settle phase. */
67
+ balance: number;
68
+ /** The raw, fully-typed wire round — the game's real server data. */
69
+ raw: Round<E>;
70
+ }
71
+ /** The game's ONE money-logic seam: parse the raw round's (typed) events into your model. Pure. */
72
+ type InterpretBook<T, E = unknown> = (raw: Round<E>, info: RoundInfo) => T;
73
+ /** Build the money facts from a raw wire round + the base bet + the mode cost. */
74
+ declare function roundInfo<E = unknown>(raw: Round<E>, bet: number, cost: number): RoundInfo;
75
+ //#endregion
76
+ //#region src/game/config.d.ts
77
+ interface ModeConfig {
78
+ /** Cost multiplier (× base bet). `base` is 1. */
79
+ cost: number;
80
+ /** A one-shot bought bonus — shown as a `Buy` card in the buy-feature modal (opened by the
81
+ * bonus button). Buying it spins this mode once (through the jurisdiction confirm gate). */
82
+ buy?: boolean;
83
+ /** An activatable per-spin bet surcharge — shown as an `Activate` card (vs a one-tap `Buy`). */
84
+ boost?: boolean;
85
+ /** Display name for the feature card (defaults to the capitalized mode key). */
86
+ name?: string;
87
+ /** Card art (URL or data URI) for the feature card. A neutral gradient is used when absent. */
88
+ image?: string;
89
+ }
90
+ interface GameConfig {
91
+ title: string;
92
+ version?: string;
93
+ /** Fallback currency code; the session/`?currency=` wins. */
94
+ currency?: string;
95
+ /**
96
+ * Theoretical RTP percentage — DISPLAY ONLY (the RTP readout + rules). NOT authoritative:
97
+ * the server (`auth.config.rtp`) wins, and the certified math report is the source of
98
+ * truth. This is only a last-resort fallback; prefer never hand-typing it (drift risk).
99
+ */
100
+ rtp?: number;
101
+ /** Mode key → cost multiplier (a number) or a `ModeConfig`. `base` defaults to 1. */
102
+ modes?: Record<string, ModeConfig | number>;
103
+ /** Rules/info menu for the HUD (`@open-slot-ui` `MenuSpec`) — build it with `@stakeplate/core/rules` `buildRules`. */
104
+ rules?: unknown;
105
+ /** i18n messages per locale (`{ en: { key: text }, es: {…} }`) for the HUD + rules text. */
106
+ messages?: Record<string, Record<string, string>>;
107
+ /** SOCIAL/sweepstakes wording per locale — swapped in when social mode is on. Merge in
108
+ * `buildRules().socialEn` so the core's disclaimer/guide are social-safe. */
109
+ socialMessages?: Record<string, Record<string, string>>;
110
+ /** Delay multipliers per turbo level (off / turbo / super). Default `[1, 0.4, 0.12]`. */
111
+ turboSpeeds?: number[];
112
+ /** Pause (ms) between autoplay/hold spins. Default 250. Scales with turbo. */
113
+ autoplayGapMs?: number;
114
+ /** Extra `@open-slot-ui` `UISpec` fields merged into the built spec (escape hatch). */
115
+ spec?: Record<string, unknown>;
116
+ }
117
+ /** The cost multiplier for a mode key (default 1 for `base` / unknown modes). */
118
+ declare function modeCostOf(config: GameConfig, mode: string): number;
119
+ //#endregion
120
+ //#region src/engine/fsm.d.ts
121
+ /** The minimal audio surface a game's phases call (satisfied by `@stakeplate/core/audio`). */
122
+ interface AudioPort {
123
+ play(name: string, opts?: {
124
+ bus?: string;
125
+ volume?: number;
126
+ }): void;
127
+ music(name: string, opts?: {
128
+ fadeIn?: number;
129
+ }): void;
130
+ stopMusic(opts?: {
131
+ fade?: number;
132
+ }): void;
133
+ }
134
+ /** Handed to every phase. `round` is set by SpinPhase and read by Present/Settle. `E` is
135
+ * the game's book-event type, so `interpretBook`/`round.raw` are typed. */
136
+ interface PhaseContext<T = unknown, V = unknown, E = unknown> {
137
+ readonly config: GameConfig;
138
+ readonly stores: RootStore;
139
+ readonly network: NetworkManager;
140
+ readonly hud: HudPort;
141
+ readonly ticker: Ticker;
142
+ /** The game's mounted view (scene/presenter/stores) — whatever `mountView` returned. */
143
+ readonly view: V;
144
+ readonly audio: AudioPort | null;
145
+ /** Turbo speed + slam-stop (core-owned). Use `ctx.turbo.delay(ms)` for spin/anim timing. */
146
+ readonly turbo: TurboState;
147
+ readonly interpretBook: InterpretBook<T, E>;
148
+ readonly fsm: FSM<T, V, E>;
149
+ round: GameRound<T, E> | null;
150
+ /** Cost multiplier for a mode key (from `config.modes`, default 1). */
151
+ modeCost(mode: string): number;
152
+ }
153
+ interface Phase<T = unknown, V = unknown, E = unknown> {
154
+ readonly name: string;
155
+ enter(ctx: PhaseContext<T, V, E>): Promise<void> | void;
156
+ exit?(ctx: PhaseContext<T, V, E>): void;
157
+ }
158
+ declare class FSM<T = unknown, V = unknown, E = unknown> {
159
+ private ctx;
160
+ private readonly byName;
161
+ private _current;
162
+ /** Later phases with the same `name` win — so a game's Present overrides the default. */
163
+ constructor(phases: Phase<T, V, E>[]);
164
+ bind(ctx: PhaseContext<T, V, E>): void;
165
+ get current(): string;
166
+ has(name: string): boolean;
167
+ transition(name: string): Promise<void>;
168
+ }
169
+ //#endregion
170
+ //#region src/audio/bind.d.ts
171
+ /** The two volume groups the HUD's two sliders drive. */
172
+ type MixerGroup = 'music' | 'effects';
173
+ /**
174
+ * The minimal mixer surface the HUD binding needs. The HUD has exactly two sliders —
175
+ * **Music** and **Effects** — so the port is group-level, not per-bus. GameAudio satisfies
176
+ * it structurally.
177
+ */
178
+ interface MixerLike {
179
+ /** Set a group's level (0..1) — applies to every bus in the group. */
180
+ setGroupLevel(group: MixerGroup, level: number): void;
181
+ /** Read a group's level (average across its buses). */
182
+ getGroupLevel(group: MixerGroup): number;
183
+ /** Mute/unmute everything (the Sound toggle). */
184
+ setMuted(muted: boolean): void;
185
+ /** Resume the AudioContext from a user gesture. Present on GameAudio. */
186
+ unlock?(): Promise<void> | void;
187
+ }
188
+ /**
189
+ * Bind the HUD sound controls to a mixer: the **Music slider → `music` group**, the
190
+ * **Effects slider → `effects` group** (both persisted to localStorage + restored), and the
191
+ * **Sound toggle → mute** everything. Returns a disposer.
192
+ */
193
+ declare function bindMixerToHud(mixer: MixerLike, hud: BootedHud, opts?: {
194
+ storageKey?: string;
195
+ }): () => void;
196
+ //#endregion
197
+ //#region src/audio/index.d.ts
198
+ /** Buses in the MUSIC group (driven by the Music slider). */
199
+ type MusicBus = 'music' | 'ambience';
200
+ /** Buses in the EFFECTS group (driven by the Effects slider). */
201
+ type SfxBus = 'reels' | 'symbols' | 'anticipation' | 'wins' | 'voiceover' | 'ui' | 'reverb';
202
+ type BusName = MusicBus | SfxBus;
203
+ interface GameAudioOptions {
204
+ /** Initial MUSIC-group level (music + ambience), 0..1. Default 0.8. */
205
+ music?: number;
206
+ /** Initial EFFECTS-group level (reels…reverb), 0..1. Default 1. */
207
+ effects?: number;
208
+ masterHeadroom?: number;
209
+ /** Duck the MUSIC group while THESE sfx buses are active (default `['wins']`; `null` = off). */
210
+ duckMusicFrom?: SfxBus | SfxBus[] | null;
211
+ duckAmount?: number;
212
+ }
213
+ /**
214
+ * A sound to load onto a bus, or a music track (intro/loop/outro → the `music` bus).
215
+ * For a PLAIN loop with no authored intro/outro, set `loopCrossfadeMs`: zvuk equal-power
216
+ * crossfades the loop boundary so a single file loops seamlessly (no click, no stinger/tail
217
+ * needed) — just the crossfade window in ms.
218
+ */
219
+ type SoundEntry = {
220
+ name: string;
221
+ kind?: 'sound';
222
+ url: string | string[];
223
+ bus?: BusName;
224
+ } | {
225
+ name: string;
226
+ kind: 'music';
227
+ loop: string | string[];
228
+ intro?: string | string[];
229
+ outro?: string | string[];
230
+ loopCrossfadeMs?: number;
231
+ };
232
+ /** The pre-wired game mixer. Satisfies {@link AudioPort} + {@link MixerLike}. */
233
+ declare class GameAudio implements AudioPort, MixerLike {
234
+ readonly engine: Engine<BusName>;
235
+ private readonly musicGroup;
236
+ private readonly effectsGroup;
237
+ private readonly duckFrom;
238
+ private readonly duckAmount;
239
+ private unlocked;
240
+ private currentMusic;
241
+ constructor(opts?: GameAudioOptions);
242
+ /** Resume the AudioContext from a user gesture + arm ducking. Idempotent. */
243
+ unlock(): Promise<void>;
244
+ setGroupLevel(group: MixerGroup, level: number): void;
245
+ getGroupLevel(group: MixerGroup): number;
246
+ setMuted(muted: boolean): void;
247
+ /** A single bus (for per-sound routing, sends, FX inserts). */
248
+ bus(name: BusName): Bus;
249
+ /** A volume group handle (`'music'` or `'effects'`). */
250
+ group(name: MixerGroup): BusGroup;
251
+ /** Preload a manifest of sounds + music onto their buses. */
252
+ load(entries: SoundEntry[]): Promise<void>;
253
+ play(name: string, opts?: {
254
+ bus?: BusName;
255
+ volume?: number;
256
+ }): void;
257
+ music(name: string, opts?: {
258
+ fadeIn?: number;
259
+ }): void;
260
+ stopMusic(opts?: {
261
+ fade?: number;
262
+ }): void;
263
+ }
264
+ declare function createGameAudio(opts?: GameAudioOptions): GameAudio;
265
+ /**
266
+ * Bind the HUD's sound controls to the audio groups (Music slider → music group, Effects →
267
+ * effects group, persisted; Sound toggle → mute). Returns a disposer. `createStakeGame` calls
268
+ * this for you (via {@link bindMixerToHud}) when you pass `audio`; use it directly only to opt
269
+ * out or wire a mixer the core didn't get.
270
+ */
271
+ declare function bindAudioToHud(audio: GameAudio, hud: BootedHud, opts?: {
272
+ storageKey?: string;
273
+ }): () => void;
274
+ //#endregion
275
+ export { DEFAULT_TURBO_SPEEDS as C, roundInfo as S, TurboState as T, ModeConfig as _, SfxBus as a, InterpretBook as b, createGameAudio as c, bindMixerToHud as d, AudioPort as f, GameConfig as g, PhaseContext as h, MusicBus as i, MixerGroup as l, Phase as m, GameAudio as n, SoundEntry as o, FSM as p, GameAudioOptions as r, bindAudioToHud as s, BusName as t, MixerLike as u, modeCostOf as v, TurboClock as w, RoundInfo as x, GameRound as y };
276
+ //# sourceMappingURL=index-BcTRfis9.d.ts.map
@@ -0,0 +1,96 @@
1
+ import { a as Balance, c as JurisdictionConfig, d as ReplayParams, f as RgsConfig, h as roundEvents, i as BOOK_AMOUNT_MULTIPLIER, l as PlayRequest, m as Round, n as AuthenticateRequest, o as EndRoundRequest, p as RgsError, r as AuthenticateResponse, s as EndRoundResponse, t as API_AMOUNT_MULTIPLIER, u as PlayResponse } from "./protocol-C1KamF3t.js";
2
+ import { a as MockOptions, c as RuntimeConfig, i as MockNetworkManager, l as readRuntime, n as PlayArgs, o as ScriptedRound, r as createNetwork, s as ReplayLaunch, t as NetworkManager, u as urlParam } from "./network-pBW8CX1o.js";
3
+ import { i as UiStore, n as RootStore, r as SessionStore, t as BalanceStore } from "./index-BBlN97RY.js";
4
+ import { a as Ticker, i as RealTicker, n as ReplayInfo, r as InstantTicker, t as HudPort } from "./hud-port-DNfotn4U.js";
5
+ import { C as DEFAULT_TURBO_SPEEDS, S as roundInfo, T as TurboState, _ as ModeConfig, b as InterpretBook, f as AudioPort, g as GameConfig, h as PhaseContext, m as Phase, o as SoundEntry, p as FSM, r as GameAudioOptions, v as modeCostOf, w as TurboClock, x as RoundInfo, y as GameRound } from "./index-BcTRfis9.js";
6
+ import { StakeNetworkManager, StakeNetworkOptions } from "./rgs.js";
7
+ import { BootedHud } from "@open-slot-ui/pixi";
8
+ //#region src/engine/phases.d.ts
9
+ /** Wait for the next spin (the HUD's `spinRequested` drives the transition to Spin). */
10
+ declare class IdlePhase<T = unknown, V = unknown, E = unknown> implements Phase<T, V, E> {
11
+ readonly name = "idle";
12
+ enter(ctx: PhaseContext<T, V, E>): void;
13
+ }
14
+ /** Pick the mode, take the stake, ask the RGS, parse the round, hand off to Present. */
15
+ declare class SpinPhase<T = unknown, V = unknown, E = unknown> implements Phase<T, V, E> {
16
+ readonly name = "spin";
17
+ enter(ctx: PhaseContext<T, V, E>): Promise<void>;
18
+ }
19
+ /** DEFAULT Present — nothing to animate. The game overrides `present` to play its scene. */
20
+ declare class PresentPhase<T = unknown, V = unknown, E = unknown> implements Phase<T, V, E> {
21
+ readonly name = "present";
22
+ enter(ctx: PhaseContext<T, V, E>): Promise<void>;
23
+ }
24
+ /** Apply the authoritative balance + win, settle the round for the HUD, then Idle. */
25
+ declare class SettlePhase<T = unknown, V = unknown, E = unknown> implements Phase<T, V, E> {
26
+ readonly name = "settle";
27
+ private roundStartedAt;
28
+ enter(ctx: PhaseContext<T, V, E>): Promise<void>;
29
+ /** Called by SpinPhase-adjacent boot wiring to timestamp the round start. */
30
+ markStart(now: number): void;
31
+ }
32
+ /** The default phase set (game phases are appended after → same-name overrides win). */
33
+ declare function defaultPhases<T = unknown, V = unknown, E = unknown>(): Phase<T, V, E>[];
34
+ //#endregion
35
+ //#region src/game/createStakeGame.d.ts
36
+ /**
37
+ * Declarative audio: hand the core your sounds and it lazily creates the `@schmooky/zvuk`
38
+ * mixer (master → music/sfx/ambience buses + ducking), preloads them, and auto-binds it to
39
+ * the HUD (sliders/mute + unlock). zvuk loads in its OWN async chunk — a game without sound
40
+ * pays nothing. (Advanced: pass a ready `GameAudio` instance instead, for custom buses/FX.)
41
+ */
42
+ interface AudioSpec extends GameAudioOptions {
43
+ sounds: SoundEntry[];
44
+ }
45
+ /** Passed to `mountView` — everything the game's scene needs (not the round/fsm yet). */
46
+ interface ViewContext {
47
+ config: GameConfig;
48
+ stores: RootStore;
49
+ hud: BootedHud;
50
+ ticker: Ticker;
51
+ audio: AudioPort | null;
52
+ /** Turbo speed + slam-stop (core-owned) — the scene may branch on `turbo.level`/`speed`. */
53
+ turbo: TurboState;
54
+ }
55
+ interface CreateStakeGameOptions<T = unknown, V = unknown, E = unknown> {
56
+ config: GameConfig;
57
+ /** The game's ONE money seam: typed RGS round (`Round<E>`) → your model. Pure. */
58
+ interpretBook: InterpretBook<T, E>;
59
+ /** Mount the game's pixi scene/presenter/stores; returns the view handed to phases. */
60
+ mountView: (host: HTMLElement, ctx: ViewContext) => V;
61
+ /** The game's Present phase (+ any overrides); Idle/Spin/Settle are provided. */
62
+ phases?: Phase<T, V, E>[];
63
+ /** Sounds (declarative — the core builds + wires the mixer) OR a ready `GameAudio`/AudioPort. */
64
+ audio?: AudioPort | AudioSpec | null;
65
+ /** Override the transport (tests / a supplied mock). */
66
+ network?: NetworkManager;
67
+ /** Override launch params (tests / embedding). */
68
+ runtime?: Partial<RuntimeConfig>;
69
+ /** Host for the pixi HUD canvas (default document.body). */
70
+ hudHost?: HTMLElement;
71
+ /** Host for the game scene (default = hudHost). */
72
+ sceneHost?: HTMLElement;
73
+ /** Passthrough to `mountHud` (spinSkin, icons, gsap, menu:false, hooks, …). */
74
+ hudOptions?: Record<string, unknown>;
75
+ }
76
+ /** A read-only snapshot of the running game — for the dev harness, tests and debugging. */
77
+ interface GameSnapshot {
78
+ phase: string;
79
+ balance: number;
80
+ bet: number;
81
+ lastWin: number;
82
+ currency: string;
83
+ spinning: boolean;
84
+ }
85
+ interface StakeGame {
86
+ start(): Promise<void>;
87
+ dispose(): void;
88
+ /** Current phase + store values. The declarative harness asserts on this (no screenshots). */
89
+ inspect(): GameSnapshot;
90
+ /** Trigger a spin the same way the HUD spin button does — only from Idle. For harness/tests. */
91
+ requestSpin(): boolean;
92
+ }
93
+ declare function createStakeGame<T = unknown, V = unknown, E = unknown>(opts: CreateStakeGameOptions<T, V, E>): StakeGame;
94
+ //#endregion
95
+ export { API_AMOUNT_MULTIPLIER, AudioPort, AudioSpec, AuthenticateRequest, AuthenticateResponse, BOOK_AMOUNT_MULTIPLIER, Balance, BalanceStore, CreateStakeGameOptions, DEFAULT_TURBO_SPEEDS, EndRoundRequest, EndRoundResponse, FSM, GameConfig, GameRound, GameSnapshot, HudPort, IdlePhase, InstantTicker, InterpretBook, JurisdictionConfig, MockNetworkManager, MockOptions, ModeConfig, NetworkManager, Phase, PhaseContext, PlayArgs, PlayRequest, PlayResponse, PresentPhase, RealTicker, ReplayInfo, ReplayLaunch, ReplayParams, RgsConfig, RgsError, RootStore, Round, RoundInfo, RuntimeConfig, ScriptedRound, SessionStore, SettlePhase, SpinPhase, StakeGame, StakeNetworkManager, StakeNetworkOptions, Ticker, TurboClock, TurboState, UiStore, ViewContext, createNetwork, createStakeGame, defaultPhases, modeCostOf, readRuntime, roundEvents, roundInfo, urlParam };
96
+ //# sourceMappingURL=index.d.ts.map