@stakeplate/core 0.1.0 → 0.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/README.md +11 -0
- package/dist/audio.d.ts +2 -2
- package/dist/audio.js +20 -5
- package/dist/audio.js.map +1 -1
- package/dist/{index-BcTRfis9.d.ts → index-DsA8mDpk.d.ts} +78 -8
- package/dist/index.d.ts +119 -2
- package/dist/index.js +291 -10
- package/dist/index.js.map +1 -1
- package/dist/{bind-BBRxizWR.js → inputs-X3Tc_HTV.js} +26 -2
- package/dist/inputs-X3Tc_HTV.js.map +1 -0
- package/dist/vite.d.ts +39 -0
- package/dist/vite.js +57 -0
- package/dist/vite.js.map +1 -0
- package/package.json +12 -6
- package/dist/bind-BBRxizWR.js.map +0 -1
package/README.md
CHANGED
|
@@ -61,7 +61,18 @@ config.socialMessages = { en: built.socialEn }; // auto-derived social wording
|
|
|
61
61
|
- `@stakeplate/core/rgs` — wire protocol, launch-param runtime, `NetworkManager` +
|
|
62
62
|
Stake/mock adapters, `createNetwork`.
|
|
63
63
|
- `@stakeplate/core/audio` — zvuk bus graph (music/effects groups) + HUD slider/mute binding.
|
|
64
|
+
Loaded one-shots are RMS-normalized by default (consistent levels, no per-file gains) and
|
|
65
|
+
each effects bus is voice-capped so stacked cues don't machine-gun. `play(name, { volume,
|
|
66
|
+
pitch })` takes jitter (`{ base, jitter }`) so repeated cues vary. `bindInputSounds(audio,
|
|
67
|
+
hud, map)` (or an `AudioSpec.inputSounds` map) plays a cue on spin/bet/autoplay/turbo/skip.
|
|
68
|
+
Encode raw clips to shippable web assets (webm/opus + mp3 codec ladder, all metadata
|
|
69
|
+
stripped) with `scripts/encode-audio.sh OUTDIR raw/*.wav`, then load the pair with
|
|
70
|
+
`{ url: ['clip.webm', 'clip.mp3'] }`.
|
|
64
71
|
- `@stakeplate/core/rules` — `buildRules` compliant menu + `toSocial`/`findRestricted` + dict.
|
|
72
|
+
- Boot loader — pass `loader: { title, backgroundImage, logo, accent, … }` to `createStakeGame`
|
|
73
|
+
for a configurable loading screen (pulsing logo/spinner + progress bar over your backdrop)
|
|
74
|
+
that shows before anything heavy loads and pops away when ready. `manual: true` +
|
|
75
|
+
`ctx.loader.done()` to hold it until your scene's art is in. `createLoader()` is also standalone.
|
|
65
76
|
- `@stakeplate/core/stores` — the MobX stores (balance, ui) for composing game state.
|
|
66
77
|
- `@stakeplate/core/testing` — the mock RGS, scriptable network, instant ticker.
|
|
67
78
|
- (soon) `/scene`, `/i18n`.
|
package/dist/audio.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as SfxBus, c as createGameAudio, d as
|
|
2
|
-
export { BusName, GameAudio, GameAudioOptions, type MixerGroup, type MixerLike, MusicBus, SfxBus, SoundEntry, bindAudioToHud, bindMixerToHud, createGameAudio };
|
|
1
|
+
import { a as SfxBus, c as createGameAudio, d as bindInputSounds, f as MixerGroup, i as MusicBus, l as InputSoundMap, m as bindMixerToHud, n as GameAudio, o as SoundEntry, p as MixerLike, r as GameAudioOptions, s as bindAudioToHud, t as BusName, u as InputSoundOptions } from "./index-DsA8mDpk.js";
|
|
2
|
+
export { BusName, GameAudio, GameAudioOptions, type InputSoundMap, type InputSoundOptions, type MixerGroup, type MixerLike, MusicBus, SfxBus, SoundEntry, bindAudioToHud, bindInputSounds, bindMixerToHud, createGameAudio };
|
package/dist/audio.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as bindMixerToHud, t as bindInputSounds } from "./inputs-X3Tc_HTV.js";
|
|
2
2
|
import { Ducker, createEngine } from "@schmooky/zvuk";
|
|
3
3
|
//#region src/audio/index.ts
|
|
4
4
|
const MUSIC_BUSES = ["music", "ambience"];
|
|
@@ -18,14 +18,23 @@ var GameAudio = class {
|
|
|
18
18
|
effectsGroup;
|
|
19
19
|
duckFrom;
|
|
20
20
|
duckAmount;
|
|
21
|
+
normalizeDefault;
|
|
21
22
|
unlocked = false;
|
|
22
23
|
currentMusic = null;
|
|
23
24
|
constructor(opts = {}) {
|
|
24
25
|
const musicLevel = opts.music ?? .8;
|
|
25
26
|
const fxLevel = opts.effects ?? 1;
|
|
27
|
+
const conc = opts.sfxConcurrency === void 0 ? {
|
|
28
|
+
max: 8,
|
|
29
|
+
steal: "oldest"
|
|
30
|
+
} : opts.sfxConcurrency;
|
|
31
|
+
this.normalizeDefault = opts.normalize ?? true;
|
|
26
32
|
const buses = {};
|
|
27
33
|
for (const b of MUSIC_BUSES) buses[b] = { level: musicLevel };
|
|
28
|
-
for (const b of SFX_BUSES) buses[b] = {
|
|
34
|
+
for (const b of SFX_BUSES) buses[b] = {
|
|
35
|
+
level: fxLevel,
|
|
36
|
+
...conc ? { concurrency: conc } : {}
|
|
37
|
+
};
|
|
29
38
|
this.engine = createEngine({
|
|
30
39
|
buses,
|
|
31
40
|
master: {
|
|
@@ -77,12 +86,18 @@ var GameAudio = class {
|
|
|
77
86
|
loop: e.loop,
|
|
78
87
|
...e.intro ? { intro: e.intro } : {},
|
|
79
88
|
...e.outro ? { outro: e.outro } : {}
|
|
80
|
-
}, e.loopCrossfadeMs != null ? { loopCrossfade: e.loopCrossfadeMs / 1e3 } : void 0) : this.engine.loadSound(e.name, e.url, {
|
|
89
|
+
}, e.loopCrossfadeMs != null ? { loopCrossfade: e.loopCrossfadeMs / 1e3 } : void 0) : this.engine.loadSound(e.name, e.url, {
|
|
90
|
+
bus: e.bus ?? "ui",
|
|
91
|
+
normalize: e.normalize ?? this.normalizeDefault
|
|
92
|
+
})));
|
|
81
93
|
}
|
|
94
|
+
/** Fire a one-shot. `volume`/`pitch` accept a `VoiceJitter` (`{ base, jitter }`) so
|
|
95
|
+
* repeated cues — contacts, ticks — vary per voice instead of sounding cloned. */
|
|
82
96
|
play(name, opts) {
|
|
83
97
|
this.engine.sound(name).play({
|
|
84
98
|
...opts?.bus ? { bus: opts.bus } : {},
|
|
85
|
-
...opts?.volume != null ? { volume: opts.volume } : {}
|
|
99
|
+
...opts?.volume != null ? { volume: opts.volume } : {},
|
|
100
|
+
...opts?.pitch != null ? { pitch: opts.pitch } : {}
|
|
86
101
|
});
|
|
87
102
|
}
|
|
88
103
|
music(name, opts) {
|
|
@@ -107,6 +122,6 @@ function bindAudioToHud(audio, hud, opts = {}) {
|
|
|
107
122
|
return bindMixerToHud(audio, hud, opts);
|
|
108
123
|
}
|
|
109
124
|
//#endregion
|
|
110
|
-
export { GameAudio, bindAudioToHud, bindMixerToHud, createGameAudio };
|
|
125
|
+
export { GameAudio, bindAudioToHud, bindInputSounds, bindMixerToHud, createGameAudio };
|
|
111
126
|
|
|
112
127
|
//# sourceMappingURL=audio.js.map
|
package/dist/audio.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio.js","names":[],"sources":["../src/audio/index.ts"],"sourcesContent":["// @stakeplate/core/audio — a thin PRE-WIRING over @schmooky/zvuk. The core stands up the\n// standard slot bus graph (nine buses in TWO groups) + master, binds the two groups to the\n// HUD's Music/Effects sliders (persisted) and mutes on the Sound toggle, and ducks music\n// while chosen sfx play. The game just declares sounds and calls `audio.play('win','wins')`.\n//\n// master\n// ├── MUSIC group (Music slider) → music · ambience\n// └── EFFECTS group (Effects slider) → reels · symbols · anticipation · wins ·\n// voiceover · ui · reverb\n//\n// Groups are zvuk BusGroups (a logical handle: a slider sets every member's level). Keep the\n// per-sound mix in the sound volumes; the sliders scale the whole group.\n\nimport { createEngine, Ducker, type Engine, type Bus, type BusConfig, type BusGroup } from '@schmooky/zvuk';\nimport type { BootedHud } from '@open-slot-ui/pixi';\nimport type { AudioPort } from '../engine/fsm';\nimport { bindMixerToHud, type MixerLike, type MixerGroup } from './bind';\n\nexport { bindMixerToHud, type MixerLike, type MixerGroup } from './bind';\n\n/** Buses in the MUSIC group (driven by the Music slider). */\nexport type MusicBus = 'music' | 'ambience';\n/** Buses in the EFFECTS group (driven by the Effects slider). */\nexport type SfxBus = 'reels' | 'symbols' | 'anticipation' | 'wins' | 'voiceover' | 'ui' | 'reverb';\nexport type BusName = MusicBus | SfxBus;\n\nconst MUSIC_BUSES: MusicBus[] = ['music', 'ambience'];\nconst SFX_BUSES: SfxBus[] = ['reels', 'symbols', 'anticipation', 'wins', 'voiceover', 'ui', 'reverb'];\n\nexport interface GameAudioOptions {\n /** Initial MUSIC-group level (music + ambience), 0..1. Default 0.8. */\n music?: number;\n /** Initial EFFECTS-group level (reels…reverb), 0..1. Default 1. */\n effects?: number;\n masterHeadroom?: number;\n /** Duck the MUSIC group while THESE sfx buses are active (default `['wins']`; `null` = off). */\n duckMusicFrom?: SfxBus | SfxBus[] | null;\n duckAmount?: number;\n}\n\n/**\n * A sound to load onto a bus, or a music track (intro/loop/outro → the `music` bus).\n * For a PLAIN loop with no authored intro/outro, set `loopCrossfadeMs`: zvuk equal-power\n * crossfades the loop boundary so a single file loops seamlessly (no click, no stinger/tail\n * needed) — just the crossfade window in ms.\n */\nexport type SoundEntry =\n | { name: string; kind?: 'sound'; url: string | string[]; bus?: BusName }\n | { name: string; kind: 'music'; loop: string | string[]; intro?: string | string[]; outro?: string | string[]; loopCrossfadeMs?: number };\n\n/** The pre-wired game mixer. Satisfies {@link AudioPort} + {@link MixerLike}. */\nexport class GameAudio implements AudioPort, MixerLike {\n readonly engine: Engine<BusName>;\n private readonly musicGroup: BusGroup;\n private readonly effectsGroup: BusGroup;\n private readonly duckFrom: SfxBus[];\n private readonly duckAmount: number;\n private unlocked = false;\n private currentMusic: { stop(opts?: { fade?: number }): void } | null = null;\n\n constructor(opts: GameAudioOptions = {}) {\n const musicLevel = opts.music ?? 0.8;\n const fxLevel = opts.effects ?? 1;\n const buses = {} as Record<BusName, BusConfig>;\n for (const b of MUSIC_BUSES) buses[b] = { level: musicLevel };\n for (const b of SFX_BUSES) buses[b] = { level: fxLevel };\n this.engine = createEngine<BusName>({\n buses,\n master: { headroom: opts.masterHeadroom ?? -3, limiter: { threshold: -1 } },\n });\n this.musicGroup = this.engine.busGroup('music', MUSIC_BUSES.map((b) => this.engine.bus(b)));\n this.effectsGroup = this.engine.busGroup('effects', SFX_BUSES.map((b) => this.engine.bus(b)));\n const df = opts.duckMusicFrom === undefined ? (['wins'] as SfxBus[]) : opts.duckMusicFrom;\n this.duckFrom = df == null ? [] : Array.isArray(df) ? df : [df];\n this.duckAmount = opts.duckAmount ?? 0.5;\n }\n\n /** Resume the AudioContext from a user gesture + arm ducking. Idempotent. */\n async unlock(): Promise<void> {\n if (this.unlocked) return;\n await this.engine.unlock();\n this.unlocked = true;\n // Duck music + ambience while each chosen sfx bus is active.\n for (const src of this.duckFrom) {\n for (const target of MUSIC_BUSES) {\n const ducker = new Ducker(this.engine.context, this.engine.bus(src), { amount: this.duckAmount, attack: 0.05, release: 0.3 });\n this.engine.bus(target).addFx(ducker);\n }\n }\n }\n\n // ── MixerLike (the HUD sliders + mute drive these) ────────────────────────────\n setGroupLevel(group: MixerGroup, level: number): void {\n (group === 'music' ? this.musicGroup : this.effectsGroup).level = level;\n }\n getGroupLevel(group: MixerGroup): number {\n return (group === 'music' ? this.musicGroup : this.effectsGroup).level;\n }\n setMuted(muted: boolean): void {\n this.musicGroup.muted = muted;\n this.effectsGroup.muted = muted;\n }\n\n /** A single bus (for per-sound routing, sends, FX inserts). */\n bus(name: BusName): Bus {\n return this.engine.bus(name);\n }\n /** A volume group handle (`'music'` or `'effects'`). */\n group(name: MixerGroup): BusGroup {\n return name === 'music' ? this.musicGroup : this.effectsGroup;\n }\n\n /** Preload a manifest of sounds + music onto their buses. */\n async load(entries: SoundEntry[]): Promise<void> {\n await Promise.all(\n entries.map((e) =>\n 'kind' in e && e.kind === 'music'\n ? this.engine.loadMusic(\n e.name,\n { loop: e.loop, ...(e.intro ? { intro: e.intro } : {}), ...(e.outro ? { outro: e.outro } : {}) },\n e.loopCrossfadeMs != null ? { loopCrossfade: e.loopCrossfadeMs / 1000 } : undefined,\n )\n : this.engine.loadSound(e.name, e.url, { bus: e.bus ?? 'ui' }),\n ),\n );\n }\n\n play(name: string, opts?: { bus?: BusName; volume?: number }): void {\n this.engine.sound(name).play({ ...(opts?.bus ? { bus: opts.bus } : {}), ...(opts?.volume != null ? { volume: opts.volume } : {}) });\n }\n\n music(name: string, opts?: { fadeIn?: number }): void {\n this.currentMusic?.stop({ fade: 0.3 });\n this.currentMusic = this.engine.music(name).play({ fadeIn: opts?.fadeIn ?? 0.4 });\n }\n\n stopMusic(opts?: { fade?: number }): void {\n this.currentMusic?.stop({ fade: opts?.fade ?? 0.3 });\n this.currentMusic = null;\n }\n}\n\nexport function createGameAudio(opts?: GameAudioOptions): GameAudio {\n return new GameAudio(opts);\n}\n\n/**\n * Bind the HUD's sound controls to the audio groups (Music slider → music group, Effects →\n * effects group, persisted; Sound toggle → mute). Returns a disposer. `createStakeGame` calls\n * this for you (via {@link bindMixerToHud}) when you pass `audio`; use it directly only to opt\n * out or wire a mixer the core didn't get.\n */\nexport function bindAudioToHud(audio: GameAudio, hud: BootedHud, opts: { storageKey?: string } = {}): () => void {\n return bindMixerToHud(audio, hud, opts);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"audio.js","names":[],"sources":["../src/audio/index.ts"],"sourcesContent":["// @stakeplate/core/audio — a thin PRE-WIRING over @schmooky/zvuk. The core stands up the\n// standard slot bus graph (nine buses in TWO groups) + master, binds the two groups to the\n// HUD's Music/Effects sliders (persisted) and mutes on the Sound toggle, and ducks music\n// while chosen sfx play. The game just declares sounds and calls `audio.play('win','wins')`.\n//\n// master\n// ├── MUSIC group (Music slider) → music · ambience\n// └── EFFECTS group (Effects slider) → reels · symbols · anticipation · wins ·\n// voiceover · ui · reverb\n//\n// Groups are zvuk BusGroups (a logical handle: a slider sets every member's level). Keep the\n// per-sound mix in the sound volumes; the sliders scale the whole group.\n\nimport { createEngine, Ducker, type Engine, type Bus, type BusConfig, type BusGroup, type VoiceJitter, type ConcurrencyConfig, type LoudnessOptions } from '@schmooky/zvuk';\nimport type { BootedHud } from '@open-slot-ui/pixi';\nimport type { AudioPort } from '../engine/fsm';\nimport { bindMixerToHud, type MixerLike, type MixerGroup } from './bind';\n\nexport { bindMixerToHud, type MixerLike, type MixerGroup } from './bind';\nexport { bindInputSounds, type InputSoundMap, type InputSoundOptions } from './inputs';\n\n/** Buses in the MUSIC group (driven by the Music slider). */\nexport type MusicBus = 'music' | 'ambience';\n/** Buses in the EFFECTS group (driven by the Effects slider). */\nexport type SfxBus = 'reels' | 'symbols' | 'anticipation' | 'wins' | 'voiceover' | 'ui' | 'reverb';\nexport type BusName = MusicBus | SfxBus;\n\nconst MUSIC_BUSES: MusicBus[] = ['music', 'ambience'];\nconst SFX_BUSES: SfxBus[] = ['reels', 'symbols', 'anticipation', 'wins', 'voiceover', 'ui', 'reverb'];\n\nexport interface GameAudioOptions {\n /** Initial MUSIC-group level (music + ambience), 0..1. Default 0.8. */\n music?: number;\n /** Initial EFFECTS-group level (reels…reverb), 0..1. Default 1. */\n effects?: number;\n masterHeadroom?: number;\n /** Duck the MUSIC group while THESE sfx buses are active (default `['wins']`; `null` = off). */\n duckMusicFrom?: SfxBus | SfxBus[] | null;\n duckAmount?: number;\n /**\n * RMS loudness normalization applied to every loaded one-shot so the whole manifest sits\n * at a consistent level (no hand-tuned per-file gains). A per-entry `normalize` overrides\n * this. Default `true`. Pass a `LoudnessOptions` object to tune the target, or `false` off.\n */\n normalize?: boolean | LoudnessOptions;\n /**\n * Voice cap applied to EACH effects bus so stacked one-shots (a multi-pay cascade, rapid\n * contacts) don't machine-gun — steals the oldest voice when the cap is hit. Default\n * `{ max: 8, steal: 'oldest' }`. Pass `null` to leave the effects buses uncapped.\n */\n sfxConcurrency?: ConcurrencyConfig | null;\n}\n\n/**\n * A sound to load onto a bus, or a music track (intro/loop/outro → the `music` bus).\n * For a PLAIN loop with no authored intro/outro, set `loopCrossfadeMs`: zvuk equal-power\n * crossfades the loop boundary so a single file loops seamlessly (no click, no stinger/tail\n * needed) — just the crossfade window in ms.\n */\nexport type SoundEntry =\n | { name: string; kind?: 'sound'; url: string | string[]; bus?: BusName; normalize?: boolean | LoudnessOptions }\n | { name: string; kind: 'music'; loop: string | string[]; intro?: string | string[]; outro?: string | string[]; loopCrossfadeMs?: number };\n\n/** The pre-wired game mixer. Satisfies {@link AudioPort} + {@link MixerLike}. */\nexport class GameAudio implements AudioPort, MixerLike {\n readonly engine: Engine<BusName>;\n private readonly musicGroup: BusGroup;\n private readonly effectsGroup: BusGroup;\n private readonly duckFrom: SfxBus[];\n private readonly duckAmount: number;\n private readonly normalizeDefault: boolean | LoudnessOptions;\n private unlocked = false;\n private currentMusic: { stop(opts?: { fade?: number }): void } | null = null;\n\n constructor(opts: GameAudioOptions = {}) {\n const musicLevel = opts.music ?? 0.8;\n const fxLevel = opts.effects ?? 1;\n // Cap each effects bus so stacked one-shots don't machine-gun (opt out with `null`).\n const conc = opts.sfxConcurrency === undefined ? ({ max: 8, steal: 'oldest' } as ConcurrencyConfig) : opts.sfxConcurrency;\n this.normalizeDefault = opts.normalize ?? true;\n const buses = {} as Record<BusName, BusConfig>;\n for (const b of MUSIC_BUSES) buses[b] = { level: musicLevel };\n for (const b of SFX_BUSES) buses[b] = { level: fxLevel, ...(conc ? { concurrency: conc } : {}) };\n this.engine = createEngine<BusName>({\n buses,\n master: { headroom: opts.masterHeadroom ?? -3, limiter: { threshold: -1 } },\n });\n this.musicGroup = this.engine.busGroup('music', MUSIC_BUSES.map((b) => this.engine.bus(b)));\n this.effectsGroup = this.engine.busGroup('effects', SFX_BUSES.map((b) => this.engine.bus(b)));\n const df = opts.duckMusicFrom === undefined ? (['wins'] as SfxBus[]) : opts.duckMusicFrom;\n this.duckFrom = df == null ? [] : Array.isArray(df) ? df : [df];\n this.duckAmount = opts.duckAmount ?? 0.5;\n }\n\n /** Resume the AudioContext from a user gesture + arm ducking. Idempotent. */\n async unlock(): Promise<void> {\n if (this.unlocked) return;\n await this.engine.unlock();\n this.unlocked = true;\n // Duck music + ambience while each chosen sfx bus is active.\n for (const src of this.duckFrom) {\n for (const target of MUSIC_BUSES) {\n const ducker = new Ducker(this.engine.context, this.engine.bus(src), { amount: this.duckAmount, attack: 0.05, release: 0.3 });\n this.engine.bus(target).addFx(ducker);\n }\n }\n }\n\n // ── MixerLike (the HUD sliders + mute drive these) ────────────────────────────\n setGroupLevel(group: MixerGroup, level: number): void {\n (group === 'music' ? this.musicGroup : this.effectsGroup).level = level;\n }\n getGroupLevel(group: MixerGroup): number {\n return (group === 'music' ? this.musicGroup : this.effectsGroup).level;\n }\n setMuted(muted: boolean): void {\n this.musicGroup.muted = muted;\n this.effectsGroup.muted = muted;\n }\n\n /** A single bus (for per-sound routing, sends, FX inserts). */\n bus(name: BusName): Bus {\n return this.engine.bus(name);\n }\n /** A volume group handle (`'music'` or `'effects'`). */\n group(name: MixerGroup): BusGroup {\n return name === 'music' ? this.musicGroup : this.effectsGroup;\n }\n\n /** Preload a manifest of sounds + music onto their buses. */\n async load(entries: SoundEntry[]): Promise<void> {\n await Promise.all(\n entries.map((e) =>\n 'kind' in e && e.kind === 'music'\n ? this.engine.loadMusic(\n e.name,\n { loop: e.loop, ...(e.intro ? { intro: e.intro } : {}), ...(e.outro ? { outro: e.outro } : {}) },\n e.loopCrossfadeMs != null ? { loopCrossfade: e.loopCrossfadeMs / 1000 } : undefined,\n )\n : this.engine.loadSound(e.name, e.url, { bus: e.bus ?? 'ui', normalize: e.normalize ?? this.normalizeDefault }),\n ),\n );\n }\n\n /** Fire a one-shot. `volume`/`pitch` accept a `VoiceJitter` (`{ base, jitter }`) so\n * repeated cues — contacts, ticks — vary per voice instead of sounding cloned. */\n play(name: string, opts?: { bus?: BusName; volume?: number | VoiceJitter; pitch?: number | VoiceJitter }): void {\n this.engine.sound(name).play({\n ...(opts?.bus ? { bus: opts.bus } : {}),\n ...(opts?.volume != null ? { volume: opts.volume } : {}),\n ...(opts?.pitch != null ? { pitch: opts.pitch } : {}),\n });\n }\n\n music(name: string, opts?: { fadeIn?: number }): void {\n this.currentMusic?.stop({ fade: 0.3 });\n this.currentMusic = this.engine.music(name).play({ fadeIn: opts?.fadeIn ?? 0.4 });\n }\n\n stopMusic(opts?: { fade?: number }): void {\n this.currentMusic?.stop({ fade: opts?.fade ?? 0.3 });\n this.currentMusic = null;\n }\n}\n\nexport function createGameAudio(opts?: GameAudioOptions): GameAudio {\n return new GameAudio(opts);\n}\n\n/**\n * Bind the HUD's sound controls to the audio groups (Music slider → music group, Effects →\n * effects group, persisted; Sound toggle → mute). Returns a disposer. `createStakeGame` calls\n * this for you (via {@link bindMixerToHud}) when you pass `audio`; use it directly only to opt\n * out or wire a mixer the core didn't get.\n */\nexport function bindAudioToHud(audio: GameAudio, hud: BootedHud, opts: { storageKey?: string } = {}): () => void {\n return bindMixerToHud(audio, hud, opts);\n}\n"],"mappings":"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"}
|
|
@@ -3,31 +3,42 @@ import { t as NetworkManager } from "./network-pBW8CX1o.js";
|
|
|
3
3
|
import { n as RootStore } from "./index-BBlN97RY.js";
|
|
4
4
|
import { a as Ticker, t as HudPort } from "./hud-port-DNfotn4U.js";
|
|
5
5
|
import { BootedHud } from "@open-slot-ui/pixi";
|
|
6
|
-
import { Bus, BusGroup, Engine } from "@schmooky/zvuk";
|
|
6
|
+
import { Bus, BusGroup, ConcurrencyConfig, Engine, LoudnessOptions, VoiceJitter } from "@schmooky/zvuk";
|
|
7
7
|
//#region src/engine/turbo.d.ts
|
|
8
8
|
/** Delay multipliers per turbo level: off (1×) / turbo (0.4×) / super (0.12×). */
|
|
9
9
|
declare const DEFAULT_TURBO_SPEEDS: number[];
|
|
10
10
|
interface TurboState {
|
|
11
11
|
/** 0 = off, 1 = turbo, 2 = super — the HUD turbo cycler's index. */
|
|
12
12
|
readonly level: number;
|
|
13
|
-
/** The delay multiplier
|
|
13
|
+
/** The EFFECTIVE delay multiplier right now — the turbo level, floored by the autoplay
|
|
14
|
+
* speed while autoplay is running (`ctx.turbo.delay` already applies it). */
|
|
14
15
|
readonly speed: number;
|
|
16
|
+
/** True while autoplay/hold is running — game animations are shortened even at turbo off. */
|
|
17
|
+
readonly autoplay: boolean;
|
|
15
18
|
/** True once the player slam-stopped this round (delays resolve instantly). */
|
|
16
19
|
readonly skipped: boolean;
|
|
17
|
-
/** A turbo- + slam-stop-aware delay for GAME animations. NOT for compliance waits. */
|
|
20
|
+
/** A turbo- + autoplay- + slam-stop-aware delay for GAME animations. NOT for compliance waits. */
|
|
18
21
|
delay(ms: number): Promise<void>;
|
|
19
22
|
}
|
|
20
23
|
declare class TurboClock implements TurboState {
|
|
21
24
|
private readonly speeds;
|
|
25
|
+
/** Delay multiplier applied while autoplay/hold runs (the floor for `speed`). Defaults to
|
|
26
|
+
* the turbo (level-1) speed, so autoplay plays at least at turbo pace even at turbo off. */
|
|
27
|
+
private readonly autoplaySpeed;
|
|
22
28
|
private _level;
|
|
29
|
+
private _autoplay;
|
|
23
30
|
private _skipped;
|
|
24
31
|
private readonly pending;
|
|
25
|
-
constructor(speeds?: number[]);
|
|
32
|
+
constructor(speeds?: number[], autoplaySpeed?: number);
|
|
26
33
|
get level(): number;
|
|
27
34
|
get speed(): number;
|
|
35
|
+
get autoplay(): boolean;
|
|
28
36
|
get skipped(): boolean;
|
|
29
37
|
/** Set the turbo level (clamped to the configured speeds). */
|
|
30
38
|
setLevel(index: number): void;
|
|
39
|
+
/** Mark autoplay/hold as running so `delay()` shortens game animations. The core sets
|
|
40
|
+
* this on autoplayStarted/holdSpinStarted and clears it when they stop. */
|
|
41
|
+
setAutoplay(active: boolean): void;
|
|
31
42
|
/** Slam-stop: resolve every in-flight delay + make the rest of the round instant. */
|
|
32
43
|
skip(): void;
|
|
33
44
|
/** Clear the slam-stop flag — the core calls this at the start of each spin. */
|
|
@@ -118,11 +129,25 @@ interface GameConfig {
|
|
|
118
129
|
declare function modeCostOf(config: GameConfig, mode: string): number;
|
|
119
130
|
//#endregion
|
|
120
131
|
//#region src/engine/fsm.d.ts
|
|
132
|
+
/** A fixed value or a per-voice random spread (`{ base, jitter }`) — structurally matches
|
|
133
|
+
* zvuk's `VoiceJitter` without importing it, so the engine stays zvuk-free. */
|
|
134
|
+
type AudioValue = number | {
|
|
135
|
+
base?: number;
|
|
136
|
+
jitter?: number;
|
|
137
|
+
};
|
|
138
|
+
/** The minimal boot-loader surface a game's phases touch (satisfied by the core's
|
|
139
|
+
* `GameLoader`) — kept structural so the engine needn't import the loader module. */
|
|
140
|
+
interface LoaderPort {
|
|
141
|
+
setProgress(p: number): void;
|
|
142
|
+
done(): Promise<void>;
|
|
143
|
+
}
|
|
121
144
|
/** The minimal audio surface a game's phases call (satisfied by `@stakeplate/core/audio`). */
|
|
122
145
|
interface AudioPort {
|
|
146
|
+
/** Fire a one-shot. `volume`/`pitch` accept jitter so repeated cues vary per voice. */
|
|
123
147
|
play(name: string, opts?: {
|
|
124
148
|
bus?: string;
|
|
125
|
-
volume?:
|
|
149
|
+
volume?: AudioValue;
|
|
150
|
+
pitch?: AudioValue;
|
|
126
151
|
}): void;
|
|
127
152
|
music(name: string, opts?: {
|
|
128
153
|
fadeIn?: number;
|
|
@@ -144,6 +169,8 @@ interface PhaseContext<T = unknown, V = unknown, E = unknown> {
|
|
|
144
169
|
readonly audio: AudioPort | null;
|
|
145
170
|
/** Turbo speed + slam-stop (core-owned). Use `ctx.turbo.delay(ms)` for spin/anim timing. */
|
|
146
171
|
readonly turbo: TurboState;
|
|
172
|
+
/** The boot loader, if one was configured (else `null`) — drive progress / `done()`. */
|
|
173
|
+
readonly loader: LoaderPort | null;
|
|
147
174
|
readonly interpretBook: InterpretBook<T, E>;
|
|
148
175
|
readonly fsm: FSM<T, V, E>;
|
|
149
176
|
round: GameRound<T, E> | null;
|
|
@@ -194,6 +221,32 @@ declare function bindMixerToHud(mixer: MixerLike, hud: BootedHud, opts?: {
|
|
|
194
221
|
storageKey?: string;
|
|
195
222
|
}): () => void;
|
|
196
223
|
//#endregion
|
|
224
|
+
//#region src/audio/inputs.d.ts
|
|
225
|
+
/** Cue name per HUD input event. Any omitted → that input stays silent. */
|
|
226
|
+
interface InputSoundMap {
|
|
227
|
+
/** Spin button press (and each autoplay/hold re-spin fires `spinRequested`). */
|
|
228
|
+
spin?: string;
|
|
229
|
+
/** Bet +/- (the bet stepper's `valueChanged`). */
|
|
230
|
+
bet?: string;
|
|
231
|
+
/** Autoplay engaged (count picked). */
|
|
232
|
+
autoplay?: string;
|
|
233
|
+
/** Turbo cycler toggled. */
|
|
234
|
+
turbo?: string;
|
|
235
|
+
/** Any switch toggled (turbo/sound/…) — a generic UI click. */
|
|
236
|
+
toggle?: string;
|
|
237
|
+
/** Slam-stop (tap-to-skip the spin). */
|
|
238
|
+
skip?: string;
|
|
239
|
+
}
|
|
240
|
+
interface InputSoundOptions {
|
|
241
|
+
/** Bus to route the cues to (default `'ui'`). */
|
|
242
|
+
bus?: string;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Wire the HUD input events to sounds. Returns a disposer. Safe to call with a partial map —
|
|
246
|
+
* only the mapped inputs subscribe.
|
|
247
|
+
*/
|
|
248
|
+
declare function bindInputSounds(audio: AudioPort, hud: BootedHud, map: InputSoundMap, opts?: InputSoundOptions): () => void;
|
|
249
|
+
//#endregion
|
|
197
250
|
//#region src/audio/index.d.ts
|
|
198
251
|
/** Buses in the MUSIC group (driven by the Music slider). */
|
|
199
252
|
type MusicBus = 'music' | 'ambience';
|
|
@@ -209,6 +262,18 @@ interface GameAudioOptions {
|
|
|
209
262
|
/** Duck the MUSIC group while THESE sfx buses are active (default `['wins']`; `null` = off). */
|
|
210
263
|
duckMusicFrom?: SfxBus | SfxBus[] | null;
|
|
211
264
|
duckAmount?: number;
|
|
265
|
+
/**
|
|
266
|
+
* RMS loudness normalization applied to every loaded one-shot so the whole manifest sits
|
|
267
|
+
* at a consistent level (no hand-tuned per-file gains). A per-entry `normalize` overrides
|
|
268
|
+
* this. Default `true`. Pass a `LoudnessOptions` object to tune the target, or `false` off.
|
|
269
|
+
*/
|
|
270
|
+
normalize?: boolean | LoudnessOptions;
|
|
271
|
+
/**
|
|
272
|
+
* Voice cap applied to EACH effects bus so stacked one-shots (a multi-pay cascade, rapid
|
|
273
|
+
* contacts) don't machine-gun — steals the oldest voice when the cap is hit. Default
|
|
274
|
+
* `{ max: 8, steal: 'oldest' }`. Pass `null` to leave the effects buses uncapped.
|
|
275
|
+
*/
|
|
276
|
+
sfxConcurrency?: ConcurrencyConfig | null;
|
|
212
277
|
}
|
|
213
278
|
/**
|
|
214
279
|
* A sound to load onto a bus, or a music track (intro/loop/outro → the `music` bus).
|
|
@@ -221,6 +286,7 @@ type SoundEntry = {
|
|
|
221
286
|
kind?: 'sound';
|
|
222
287
|
url: string | string[];
|
|
223
288
|
bus?: BusName;
|
|
289
|
+
normalize?: boolean | LoudnessOptions;
|
|
224
290
|
} | {
|
|
225
291
|
name: string;
|
|
226
292
|
kind: 'music';
|
|
@@ -236,6 +302,7 @@ declare class GameAudio implements AudioPort, MixerLike {
|
|
|
236
302
|
private readonly effectsGroup;
|
|
237
303
|
private readonly duckFrom;
|
|
238
304
|
private readonly duckAmount;
|
|
305
|
+
private readonly normalizeDefault;
|
|
239
306
|
private unlocked;
|
|
240
307
|
private currentMusic;
|
|
241
308
|
constructor(opts?: GameAudioOptions);
|
|
@@ -250,9 +317,12 @@ declare class GameAudio implements AudioPort, MixerLike {
|
|
|
250
317
|
group(name: MixerGroup): BusGroup;
|
|
251
318
|
/** Preload a manifest of sounds + music onto their buses. */
|
|
252
319
|
load(entries: SoundEntry[]): Promise<void>;
|
|
320
|
+
/** Fire a one-shot. `volume`/`pitch` accept a `VoiceJitter` (`{ base, jitter }`) so
|
|
321
|
+
* repeated cues — contacts, ticks — vary per voice instead of sounding cloned. */
|
|
253
322
|
play(name: string, opts?: {
|
|
254
323
|
bus?: BusName;
|
|
255
|
-
volume?: number;
|
|
324
|
+
volume?: number | VoiceJitter;
|
|
325
|
+
pitch?: number | VoiceJitter;
|
|
256
326
|
}): void;
|
|
257
327
|
music(name: string, opts?: {
|
|
258
328
|
fadeIn?: number;
|
|
@@ -272,5 +342,5 @@ declare function bindAudioToHud(audio: GameAudio, hud: BootedHud, opts?: {
|
|
|
272
342
|
storageKey?: string;
|
|
273
343
|
}): () => void;
|
|
274
344
|
//#endregion
|
|
275
|
-
export {
|
|
276
|
-
//# sourceMappingURL=index-
|
|
345
|
+
export { TurboState as A, modeCostOf as C, roundInfo as D, RoundInfo as E, DEFAULT_TURBO_SPEEDS as O, ModeConfig as S, InterpretBook as T, FSM as _, SfxBus as a, PhaseContext as b, createGameAudio as c, bindInputSounds as d, MixerGroup as f, AudioValue as g, AudioPort as h, MusicBus as i, TurboClock as k, InputSoundMap as l, bindMixerToHud as m, GameAudio as n, SoundEntry as o, MixerLike as p, GameAudioOptions as r, bindAudioToHud as s, BusName as t, InputSoundOptions as u, LoaderPort as v, GameRound as w, GameConfig as x, Phase as y };
|
|
346
|
+
//# sourceMappingURL=index-DsA8mDpk.d.ts.map
|
package/dist/index.d.ts
CHANGED
|
@@ -2,9 +2,10 @@ import { a as Balance, c as JurisdictionConfig, d as ReplayParams, f as RgsConfi
|
|
|
2
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
3
|
import { i as UiStore, n as RootStore, r as SessionStore, t as BalanceStore } from "./index-BBlN97RY.js";
|
|
4
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
|
|
5
|
+
import { A as TurboState, C as modeCostOf, D as roundInfo, E as RoundInfo, O as DEFAULT_TURBO_SPEEDS, S as ModeConfig, T as InterpretBook, _ as FSM, b as PhaseContext, g as AudioValue, h as AudioPort, k as TurboClock, l as InputSoundMap, o as SoundEntry, r as GameAudioOptions, v as LoaderPort, w as GameRound, x as GameConfig, y as Phase } from "./index-DsA8mDpk.js";
|
|
6
6
|
import { StakeNetworkManager, StakeNetworkOptions } from "./rgs.js";
|
|
7
7
|
import { BootedHud } from "@open-slot-ui/pixi";
|
|
8
|
+
import { CurrencySpec } from "@open-slot-ui/core";
|
|
8
9
|
//#region src/engine/phases.d.ts
|
|
9
10
|
/** Wait for the next spin (the HUD's `spinRequested` drives the transition to Spin). */
|
|
10
11
|
declare class IdlePhase<T = unknown, V = unknown, E = unknown> implements Phase<T, V, E> {
|
|
@@ -32,6 +33,92 @@ declare class SettlePhase<T = unknown, V = unknown, E = unknown> implements Phas
|
|
|
32
33
|
/** The default phase set (game phases are appended after → same-name overrides win). */
|
|
33
34
|
declare function defaultPhases<T = unknown, V = unknown, E = unknown>(): Phase<T, V, E>[];
|
|
34
35
|
//#endregion
|
|
36
|
+
//#region src/engine/beat.d.ts
|
|
37
|
+
interface BeatOptions {
|
|
38
|
+
/** Pause before the announcement shows — lets the reveal settle. Default 0. */
|
|
39
|
+
leadMs?: number;
|
|
40
|
+
/** How long the announcement holds alone on screen. */
|
|
41
|
+
holdMs: number;
|
|
42
|
+
/** Pause after hiding, before play resumes. Default 0. */
|
|
43
|
+
trailMs?: number;
|
|
44
|
+
/** Reveal the announcement (banner/sfx). Called after the lead pause. */
|
|
45
|
+
show: () => void;
|
|
46
|
+
/** Hide it. Called after the hold, before the trailing pause. Optional (some banners
|
|
47
|
+
* self-dismiss). */
|
|
48
|
+
hide?: () => void;
|
|
49
|
+
/**
|
|
50
|
+
* Timing source. `'real'` (default) is a fixed, unskippable beat via the ticker — an
|
|
51
|
+
* announcement is a deliberate moment that a slam-stop shouldn't blow past. `'turbo'`
|
|
52
|
+
* makes the pauses turbo-/autoplay-/slam-stop-aware (shorter under turbo, skippable).
|
|
53
|
+
*/
|
|
54
|
+
timing?: 'real' | 'turbo';
|
|
55
|
+
}
|
|
56
|
+
/** Timing surface the beat needs — a `PhaseContext` satisfies it (`ctx.ticker`/`ctx.turbo`). */
|
|
57
|
+
interface BeatClock {
|
|
58
|
+
ticker: Ticker;
|
|
59
|
+
turbo: TurboState;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Run a blocking announcement beat: lead → show → hold → hide → trail. Awaitable, so a
|
|
63
|
+
* Present phase just `await blockingBeat(ctx, { … })` and the round pauses for the whole
|
|
64
|
+
* thing. Returns when the beat (including the trailing pause) is over.
|
|
65
|
+
*/
|
|
66
|
+
declare function blockingBeat(ctx: BeatClock, opts: BeatOptions): Promise<void>;
|
|
67
|
+
//#endregion
|
|
68
|
+
//#region src/loader/index.d.ts
|
|
69
|
+
/** One feature card on the intro splash — an image with a short (may be multi-line) caption. */
|
|
70
|
+
interface FeatureItem {
|
|
71
|
+
/** Image URL / data-URI. Rendered at a uniform height so mismatched art still lines up. */
|
|
72
|
+
image: string;
|
|
73
|
+
/** Short caption under the image. `\n` for line breaks. */
|
|
74
|
+
text: string;
|
|
75
|
+
}
|
|
76
|
+
interface LoaderConfig {
|
|
77
|
+
/** Big title under the logo (LOADING phase only). Defaults to the game title. */
|
|
78
|
+
title?: string;
|
|
79
|
+
/** Small line under the title (LOADING phase). */
|
|
80
|
+
subtitle?: string;
|
|
81
|
+
/** URL/data-URI of the logo. Pulses while loading; glides to the top for the features
|
|
82
|
+
* splash. Omit for a CSS ring spinner (and no move-up). */
|
|
83
|
+
logo?: string;
|
|
84
|
+
/** Solid colour behind everything. Default `#0b0d12`. */
|
|
85
|
+
background?: string;
|
|
86
|
+
/** Optional cover image (ideally the game's own backdrop, for a seamless reveal). */
|
|
87
|
+
backgroundImage?: string;
|
|
88
|
+
/** Blur the cover image. Default `true`. */
|
|
89
|
+
blur?: boolean;
|
|
90
|
+
/** Accent colour — progress fill + title glow + caption text. Default `#d99000`. */
|
|
91
|
+
accent?: string;
|
|
92
|
+
/** Minimum time the LOADING phase stays up, so the bar always fills. Default `1200`ms. */
|
|
93
|
+
minDurationMs?: number;
|
|
94
|
+
/** Stacking order. Default a very high value so it sits over the game. */
|
|
95
|
+
zIndex?: number;
|
|
96
|
+
/**
|
|
97
|
+
* If `true`, the core shows the loader + advances progress but does NOT auto-advance past
|
|
98
|
+
* LOADING — the game calls `ctx.loader.done()` itself (e.g. after its scene finishes loading
|
|
99
|
+
* art). Default `false`. (The FEATURES splash always waits for the player regardless.)
|
|
100
|
+
*/
|
|
101
|
+
manual?: boolean;
|
|
102
|
+
/** 2–3 feature cards → show a FEATURES splash after loading. Omit for a plain loader. */
|
|
103
|
+
features?: FeatureItem[];
|
|
104
|
+
/** The dismiss prompt under the feature cards. Default `PRESS TO CONTINUE`. */
|
|
105
|
+
continueText?: string;
|
|
106
|
+
}
|
|
107
|
+
interface GameLoader {
|
|
108
|
+
/** The overlay element (already in the document). */
|
|
109
|
+
readonly el: HTMLElement;
|
|
110
|
+
/** Drive the progress bar. Clamped to the range it has already reached (never goes back). */
|
|
111
|
+
setProgress(p: number): void;
|
|
112
|
+
/** Advance out of LOADING: fill to 100%, honour `minDurationMs`, then either show the
|
|
113
|
+
* FEATURES splash (waiting for the player to continue) or — with no features — pop away.
|
|
114
|
+
* Resolves once the overlay is fully gone. Idempotent (first call wins). */
|
|
115
|
+
done(): Promise<void>;
|
|
116
|
+
/** Remove immediately without any outro (used on a boot error). */
|
|
117
|
+
remove(): void;
|
|
118
|
+
}
|
|
119
|
+
/** Create + mount the boot overlay. Safe to call before anything else in boot. */
|
|
120
|
+
declare function createLoader(config?: LoaderConfig): GameLoader;
|
|
121
|
+
//#endregion
|
|
35
122
|
//#region src/game/createStakeGame.d.ts
|
|
36
123
|
/**
|
|
37
124
|
* Declarative audio: hand the core your sounds and it lazily creates the `@schmooky/zvuk`
|
|
@@ -41,6 +128,9 @@ declare function defaultPhases<T = unknown, V = unknown, E = unknown>(): Phase<T
|
|
|
41
128
|
*/
|
|
42
129
|
interface AudioSpec extends GameAudioOptions {
|
|
43
130
|
sounds: SoundEntry[];
|
|
131
|
+
/** Optional: play a cue on HUD input events (spin/bet/autoplay/turbo/toggle/skip). The
|
|
132
|
+
* core auto-binds these after the HUD mounts — the named sounds must be in `sounds`. */
|
|
133
|
+
inputSounds?: InputSoundMap;
|
|
44
134
|
}
|
|
45
135
|
/** Passed to `mountView` — everything the game's scene needs (not the round/fsm yet). */
|
|
46
136
|
interface ViewContext {
|
|
@@ -51,6 +141,10 @@ interface ViewContext {
|
|
|
51
141
|
audio: AudioPort | null;
|
|
52
142
|
/** Turbo speed + slam-stop (core-owned) — the scene may branch on `turbo.level`/`speed`. */
|
|
53
143
|
turbo: TurboState;
|
|
144
|
+
/** The boot loader (if `loader` was configured) — drive `setProgress` while your scene
|
|
145
|
+
* loads art, and (with `loader.manual`) call `done()` when the scene is ready. `null`
|
|
146
|
+
* when no loader is used. */
|
|
147
|
+
loader: GameLoader | null;
|
|
54
148
|
}
|
|
55
149
|
interface CreateStakeGameOptions<T = unknown, V = unknown, E = unknown> {
|
|
56
150
|
config: GameConfig;
|
|
@@ -72,6 +166,14 @@ interface CreateStakeGameOptions<T = unknown, V = unknown, E = unknown> {
|
|
|
72
166
|
sceneHost?: HTMLElement;
|
|
73
167
|
/** Passthrough to `mountHud` (spinSkin, icons, gsap, menu:false, hooks, …). */
|
|
74
168
|
hudOptions?: Record<string, unknown>;
|
|
169
|
+
/**
|
|
170
|
+
* A configurable boot loader — shows the instant boot starts, advances across the boot
|
|
171
|
+
* milestones, then fills + pops away when the game is ready. Pass a {@link LoaderConfig}
|
|
172
|
+
* to enable it (title defaults to `config.title`); omit or `false` to render none (the
|
|
173
|
+
* game keeps its own HTML loader). With `manual: true`, call `ctx.loader.done()` yourself
|
|
174
|
+
* (e.g. after the scene's art finishes loading).
|
|
175
|
+
*/
|
|
176
|
+
loader?: LoaderConfig | false;
|
|
75
177
|
}
|
|
76
178
|
/** A read-only snapshot of the running game — for the dev harness, tests and debugging. */
|
|
77
179
|
interface GameSnapshot {
|
|
@@ -92,5 +194,20 @@ interface StakeGame {
|
|
|
92
194
|
}
|
|
93
195
|
declare function createStakeGame<T = unknown, V = unknown, E = unknown>(opts: CreateStakeGameOptions<T, V, E>): StakeGame;
|
|
94
196
|
//#endregion
|
|
95
|
-
|
|
197
|
+
//#region src/currency/index.d.ts
|
|
198
|
+
/** Three-decimal fiat missing from the @open-slot-ui currency table — the Gulf/Arab
|
|
199
|
+
* dinars & rials (the lib already covers KWD/BHD/JOD). `symbol` follows the lib's short
|
|
200
|
+
* latinised convention for the region (KWD→"KD", …). */
|
|
201
|
+
declare const EXTRA_DECIMALS: Readonly<Record<string, {
|
|
202
|
+
decimals: number;
|
|
203
|
+
symbol: string;
|
|
204
|
+
}>>;
|
|
205
|
+
/**
|
|
206
|
+
* `resolveCurrency`, but with the missing 3-decimal dinars/rials patched in. Use this
|
|
207
|
+
* everywhere a currency code becomes a `CurrencySpec` so precision is correct for every
|
|
208
|
+
* code — `createStakeGame` routes all of its resolution through here.
|
|
209
|
+
*/
|
|
210
|
+
declare function currencyFor(code: string): CurrencySpec;
|
|
211
|
+
//#endregion
|
|
212
|
+
export { API_AMOUNT_MULTIPLIER, AudioPort, AudioSpec, AudioValue, AuthenticateRequest, AuthenticateResponse, BOOK_AMOUNT_MULTIPLIER, Balance, BalanceStore, BeatClock, BeatOptions, CreateStakeGameOptions, DEFAULT_TURBO_SPEEDS, EXTRA_DECIMALS, EndRoundRequest, EndRoundResponse, FSM, FeatureItem, GameConfig, GameLoader, GameRound, GameSnapshot, HudPort, IdlePhase, InstantTicker, InterpretBook, JurisdictionConfig, LoaderConfig, LoaderPort, 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, blockingBeat, createLoader, createNetwork, createStakeGame, currencyFor, defaultPhases, modeCostOf, readRuntime, roundEvents, roundInfo, urlParam };
|
|
96
213
|
//# sourceMappingURL=index.d.ts.map
|