@energy8platform/game-engine 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core.cjs.js +62 -1
- package/dist/core.cjs.js.map +1 -1
- package/dist/core.d.ts +33 -2
- package/dist/core.esm.js +63 -3
- package/dist/core.esm.js.map +1 -1
- package/dist/debug.d.ts +1 -1
- package/dist/game-spec.cjs.js +13 -0
- package/dist/game-spec.cjs.js.map +1 -0
- package/dist/game-spec.d.ts +1 -0
- package/dist/game-spec.esm.js +2 -0
- package/dist/game-spec.esm.js.map +1 -0
- package/dist/host.cjs.js +3346 -0
- package/dist/host.cjs.js.map +1 -0
- package/dist/host.d.ts +915 -0
- package/dist/host.esm.js +3337 -0
- package/dist/host.esm.js.map +1 -0
- package/dist/index.cjs.js +13 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +7 -2
- package/dist/index.esm.js +13 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/react.cjs.js.map +1 -1
- package/dist/react.d.ts +6 -1
- package/dist/react.esm.js.map +1 -1
- package/dist/shell.cjs.js +19 -0
- package/dist/shell.cjs.js.map +1 -0
- package/dist/shell.d.ts +1 -0
- package/dist/shell.esm.js +2 -0
- package/dist/shell.esm.js.map +1 -0
- package/dist/slot.cjs.js +998 -0
- package/dist/slot.cjs.js.map +1 -0
- package/dist/slot.d.ts +333 -0
- package/dist/slot.esm.js +985 -0
- package/dist/slot.esm.js.map +1 -0
- package/package.json +26 -1
- package/src/core/GameApplication.ts +16 -1
- package/src/core/index.ts +2 -0
- package/src/debug/index.ts +1 -1
- package/src/game-spec/index.ts +1 -0
- package/src/host/autoplay.ts +78 -0
- package/src/host/balanceGate.ts +46 -0
- package/src/host/buildConfig.ts +28 -0
- package/src/host/createSlotGame.ts +423 -0
- package/src/host/fatalError.ts +104 -0
- package/src/host/freeSpinsCounter.ts +44 -0
- package/src/host/index.ts +18 -0
- package/src/host/playError.ts +64 -0
- package/src/host/preboot.ts +25 -0
- package/src/host/replay.ts +9 -0
- package/src/host/runRound.ts +63 -0
- package/src/host/sceneController.ts +31 -0
- package/src/host/sceneStart.ts +25 -0
- package/src/host/shellConfig.ts +379 -0
- package/src/host/slotPlay.ts +62 -0
- package/src/host/types.ts +71 -0
- package/src/index.ts +1 -1
- package/src/loading/index.ts +2 -0
- package/src/scenes/IntroScene.ts +66 -0
- package/src/shell/index.ts +20 -0
- package/src/slot/anim/CascadeController.ts +102 -0
- package/src/slot/anim/ReelSpinController.ts +81 -0
- package/src/slot/anim/easing-map.ts +14 -0
- package/src/slot/freeSpins/FreeSpinsSession.ts +40 -0
- package/src/slot/grid/AnimatedSymbol.ts +68 -0
- package/src/slot/grid/ReelGrid.ts +92 -0
- package/src/slot/grid/SymbolCell.ts +127 -0
- package/src/slot/grid/SymbolView.ts +13 -0
- package/src/slot/index.ts +21 -0
- package/src/slot/multiplier/MultiplierAccumulator.ts +29 -0
- package/src/slot/overlay/BigWinOverlay.ts +89 -0
- package/src/slot/overlay/CountUpDisplay.ts +56 -0
- package/src/slot/overlay/tiers.ts +29 -0
- package/src/types.ts +3 -0
package/dist/host.d.ts
ADDED
|
@@ -0,0 +1,915 @@
|
|
|
1
|
+
import { Container, ApplicationOptions, Application, Texture } from 'pixi.js';
|
|
2
|
+
import { GameModel } from '@energy8platform/platform-core/game-spec';
|
|
3
|
+
import { LoadingScreenConfig, AssetManifest, PlatformSession } from '@energy8platform/platform-core';
|
|
4
|
+
import * as _energy8platform_platform_core_shell from '@energy8platform/platform-core/shell';
|
|
5
|
+
import { CurrencyConfig, GameInfoContent, BonusOption, ShellFeatures, ShellMode, ShellConfig, GameShell } from '@energy8platform/platform-core/shell';
|
|
6
|
+
export { socialize } from '@energy8platform/platform-core/shell';
|
|
7
|
+
import { BookAdapter, AdapterModule, StakeBridge } from '@energy8platform/stake-bridge';
|
|
8
|
+
import { CasinoGameSDK, InitData, GameConfigData, SessionData } from '@energy8platform/game-sdk';
|
|
9
|
+
import { SlotSpinResultBase, SlotResultNormalizer } from '@energy8platform/platform-core/slot-result';
|
|
10
|
+
|
|
11
|
+
declare enum ScaleMode {
|
|
12
|
+
/** Fit inside container, maintain aspect ratio (letterbox/pillarbox) */
|
|
13
|
+
FIT = "FIT",
|
|
14
|
+
/** Fill container, maintain aspect ratio (crop edges) */
|
|
15
|
+
FILL = "FILL",
|
|
16
|
+
/** Stretch to fill (distorts) */
|
|
17
|
+
STRETCH = "STRETCH"
|
|
18
|
+
}
|
|
19
|
+
declare enum Orientation {
|
|
20
|
+
LANDSCAPE = "landscape",
|
|
21
|
+
PORTRAIT = "portrait",
|
|
22
|
+
ANY = "any"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface AudioConfig {
|
|
26
|
+
/** Default volumes per category (0..1) */
|
|
27
|
+
music?: number;
|
|
28
|
+
sfx?: number;
|
|
29
|
+
ui?: number;
|
|
30
|
+
ambient?: number;
|
|
31
|
+
/** Persist mute state in localStorage */
|
|
32
|
+
persist?: boolean;
|
|
33
|
+
/** LocalStorage key prefix */
|
|
34
|
+
storageKey?: string;
|
|
35
|
+
}
|
|
36
|
+
interface GameApplicationConfig {
|
|
37
|
+
/** Container element or CSS selector to mount canvas into */
|
|
38
|
+
container?: HTMLElement | string;
|
|
39
|
+
/** Reference design width (fallback: GameConfigData.viewport.width or 1920) */
|
|
40
|
+
designWidth?: number;
|
|
41
|
+
/** Reference design height (fallback: GameConfigData.viewport.height or 1080) */
|
|
42
|
+
designHeight?: number;
|
|
43
|
+
/** How to scale the game to fit the container */
|
|
44
|
+
scaleMode?: ScaleMode;
|
|
45
|
+
/** Preferred orientation */
|
|
46
|
+
orientation?: Orientation;
|
|
47
|
+
/** Loading screen configuration */
|
|
48
|
+
loading?: LoadingScreenConfig;
|
|
49
|
+
/** Asset manifest — what to load */
|
|
50
|
+
manifest?: AssetManifest;
|
|
51
|
+
/** Audio configuration */
|
|
52
|
+
audio?: AudioConfig;
|
|
53
|
+
/** SDK options. Set to false to disable SDK (offline/development mode) */
|
|
54
|
+
sdk?: {
|
|
55
|
+
parentOrigin?: string;
|
|
56
|
+
timeout?: number;
|
|
57
|
+
debug?: boolean;
|
|
58
|
+
/** Use in-memory channel instead of postMessage (no iframe required) */
|
|
59
|
+
devMode?: boolean;
|
|
60
|
+
} | false;
|
|
61
|
+
/** PixiJS Application options (pass-through) */
|
|
62
|
+
pixi?: Partial<ApplicationOptions>;
|
|
63
|
+
/** Enable debug overlay (FPS, draw calls) */
|
|
64
|
+
debug?: boolean;
|
|
65
|
+
/** When set, GameApplication mounts the branded game shell after the SDK handshake. */
|
|
66
|
+
shell?: _energy8platform_platform_core_shell.ShellConfig | false;
|
|
67
|
+
}
|
|
68
|
+
interface SceneConstructor {
|
|
69
|
+
new (): IScene;
|
|
70
|
+
[key: string]: any;
|
|
71
|
+
}
|
|
72
|
+
interface IScene {
|
|
73
|
+
/** Root display container for this scene */
|
|
74
|
+
readonly container: Container;
|
|
75
|
+
/** @internal GameApplication reference — set by SceneManager */
|
|
76
|
+
__engineApp?: any;
|
|
77
|
+
/** Called when the scene is entered */
|
|
78
|
+
onEnter?(data?: unknown): Promise<void> | void;
|
|
79
|
+
/** Called when the scene is exited */
|
|
80
|
+
onExit?(): Promise<void> | void;
|
|
81
|
+
/** Called every frame */
|
|
82
|
+
onUpdate?(dt: number): void;
|
|
83
|
+
/** Called when viewport resizes */
|
|
84
|
+
onResize?(width: number, height: number): void;
|
|
85
|
+
/** Called when the scene is destroyed */
|
|
86
|
+
onDestroy?(): void;
|
|
87
|
+
}
|
|
88
|
+
declare enum TransitionType {
|
|
89
|
+
NONE = "none",
|
|
90
|
+
FADE = "fade",
|
|
91
|
+
SLIDE_LEFT = "slide-left",
|
|
92
|
+
SLIDE_RIGHT = "slide-right"
|
|
93
|
+
}
|
|
94
|
+
interface TransitionConfig {
|
|
95
|
+
type: TransitionType;
|
|
96
|
+
duration?: number;
|
|
97
|
+
easing?: (t: number) => number;
|
|
98
|
+
}
|
|
99
|
+
interface GameEngineEvents {
|
|
100
|
+
/** Fired when engine initialization is complete */
|
|
101
|
+
initialized: void;
|
|
102
|
+
/** Fired when all assets are loaded */
|
|
103
|
+
loaded: void;
|
|
104
|
+
/** Fired when the engine starts running */
|
|
105
|
+
started: void;
|
|
106
|
+
/** Fired on viewport resize */
|
|
107
|
+
resize: {
|
|
108
|
+
width: number;
|
|
109
|
+
height: number;
|
|
110
|
+
};
|
|
111
|
+
/** Fired on orientation change */
|
|
112
|
+
orientationChange: Orientation;
|
|
113
|
+
/** Fired on scene change */
|
|
114
|
+
sceneChange: {
|
|
115
|
+
from: string | null;
|
|
116
|
+
to: string;
|
|
117
|
+
};
|
|
118
|
+
/** Fired when player balance changes (forwarded from SDK) */
|
|
119
|
+
balanceUpdate: {
|
|
120
|
+
balance: number;
|
|
121
|
+
};
|
|
122
|
+
/** Fired on error */
|
|
123
|
+
error: Error;
|
|
124
|
+
/** Fired when engine is destroyed */
|
|
125
|
+
destroyed: void;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Minimal typed event emitter.
|
|
130
|
+
* Used internally by GameApplication, SceneManager, AudioManager, etc.
|
|
131
|
+
*
|
|
132
|
+
* Supports `void` event types — events that carry no data can be emitted
|
|
133
|
+
* without arguments: `emitter.emit('eventName')`.
|
|
134
|
+
*/
|
|
135
|
+
declare class EventEmitter<TEvents extends {}> {
|
|
136
|
+
private listeners;
|
|
137
|
+
on<K extends keyof TEvents>(event: K, handler: (data: TEvents[K]) => void): this;
|
|
138
|
+
once<K extends keyof TEvents>(event: K, handler: (data: TEvents[K]) => void): this;
|
|
139
|
+
off<K extends keyof TEvents>(event: K, handler: (data: TEvents[K]) => void): this;
|
|
140
|
+
emit<K extends keyof TEvents>(...args: TEvents[K] extends void ? [event: K] : [event: K, data: TEvents[K]]): void;
|
|
141
|
+
removeAllListeners(event?: keyof TEvents): this;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
interface SceneEntry$1 {
|
|
145
|
+
scene: IScene;
|
|
146
|
+
key: string;
|
|
147
|
+
}
|
|
148
|
+
interface SceneManagerEvents {
|
|
149
|
+
change: {
|
|
150
|
+
from: string | null;
|
|
151
|
+
to: string;
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Manages the scene stack and transitions between scenes.
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* ```ts
|
|
159
|
+
* const scenes = new SceneManager(app.stage);
|
|
160
|
+
* scenes.register('loading', LoadingScene);
|
|
161
|
+
* scenes.register('game', GameScene);
|
|
162
|
+
* await scenes.goto('loading');
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
declare class SceneManager extends EventEmitter<SceneManagerEvents> {
|
|
166
|
+
private static MAX_TRANSITION_DEPTH;
|
|
167
|
+
/** Root container that scenes are added to */
|
|
168
|
+
root: Container;
|
|
169
|
+
private registry;
|
|
170
|
+
private stack;
|
|
171
|
+
private _transitionDepth;
|
|
172
|
+
/** Current viewport dimensions — set by ViewportManager */
|
|
173
|
+
private _width;
|
|
174
|
+
private _height;
|
|
175
|
+
/** @internal GameApplication reference — passed to scenes */
|
|
176
|
+
private _app;
|
|
177
|
+
constructor(root?: Container);
|
|
178
|
+
/** @internal Set the root container (called by GameApplication after PixiJS init) */
|
|
179
|
+
setRoot(root: Container): void;
|
|
180
|
+
/** @internal Set the app reference (called by GameApplication) */
|
|
181
|
+
setApp(app: any): void;
|
|
182
|
+
/** Register a scene class by key */
|
|
183
|
+
register(key: string, ctor: SceneConstructor): this;
|
|
184
|
+
/** Get the current (topmost) scene entry */
|
|
185
|
+
get current(): SceneEntry$1 | null;
|
|
186
|
+
/** Get the current scene key */
|
|
187
|
+
get currentKey(): string | null;
|
|
188
|
+
/** Whether a scene transition is in progress */
|
|
189
|
+
get isTransitioning(): boolean;
|
|
190
|
+
/**
|
|
191
|
+
* Navigate to a scene, replacing the entire stack.
|
|
192
|
+
*/
|
|
193
|
+
goto(key: string, data?: unknown, transition?: TransitionConfig): Promise<void>;
|
|
194
|
+
/**
|
|
195
|
+
* Push a scene onto the stack (the previous scene stays underneath).
|
|
196
|
+
* Useful for overlays, modals, pause screens.
|
|
197
|
+
*/
|
|
198
|
+
push(key: string, data?: unknown, transition?: TransitionConfig): Promise<void>;
|
|
199
|
+
/**
|
|
200
|
+
* Pop the top scene from the stack.
|
|
201
|
+
*/
|
|
202
|
+
pop(transition?: TransitionConfig): Promise<void>;
|
|
203
|
+
/**
|
|
204
|
+
* Replace the top scene with a new one.
|
|
205
|
+
*/
|
|
206
|
+
replace(key: string, data?: unknown, transition?: TransitionConfig): Promise<void>;
|
|
207
|
+
/**
|
|
208
|
+
* Called every frame by GameApplication.
|
|
209
|
+
*/
|
|
210
|
+
update(dt: number): void;
|
|
211
|
+
/**
|
|
212
|
+
* Called on viewport resize.
|
|
213
|
+
*/
|
|
214
|
+
resize(width: number, height: number): void;
|
|
215
|
+
/**
|
|
216
|
+
* Destroy all scenes and clear the manager.
|
|
217
|
+
*/
|
|
218
|
+
destroy(): void;
|
|
219
|
+
private createScene;
|
|
220
|
+
private pushInternal;
|
|
221
|
+
private popInternal;
|
|
222
|
+
private transitionIn;
|
|
223
|
+
private transitionOut;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Manages game asset loading with progress tracking, bundle support, and
|
|
228
|
+
* automatic base path resolution from SDK's assetsUrl.
|
|
229
|
+
*
|
|
230
|
+
* Wraps PixiJS Assets API with a typed, game-oriented interface.
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* ```ts
|
|
234
|
+
* const assets = new AssetManager('https://cdn.example.com/game/', manifest);
|
|
235
|
+
* await assets.init();
|
|
236
|
+
* await assets.loadBundle('preload', (p) => console.log(p));
|
|
237
|
+
* const texture = assets.get<Texture>('hero');
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
declare class AssetManager {
|
|
241
|
+
private _initialized;
|
|
242
|
+
private _basePath;
|
|
243
|
+
private _manifest;
|
|
244
|
+
private _loadedBundles;
|
|
245
|
+
constructor(basePath?: string, manifest?: AssetManifest);
|
|
246
|
+
/** Whether the asset system has been initialized */
|
|
247
|
+
get initialized(): boolean;
|
|
248
|
+
/** Base path for all assets (usually from SDK's assetsUrl) */
|
|
249
|
+
get basePath(): string;
|
|
250
|
+
/** Set of loaded bundle names */
|
|
251
|
+
get loadedBundles(): ReadonlySet<string>;
|
|
252
|
+
/**
|
|
253
|
+
* Initialize the asset system.
|
|
254
|
+
* Must be called before loading any assets.
|
|
255
|
+
*/
|
|
256
|
+
init(): Promise<void>;
|
|
257
|
+
/**
|
|
258
|
+
* Load a single bundle by name.
|
|
259
|
+
*
|
|
260
|
+
* @param name - Bundle name (must exist in the manifest)
|
|
261
|
+
* @param onProgress - Progress callback (0..1)
|
|
262
|
+
* @returns Loaded assets map
|
|
263
|
+
*/
|
|
264
|
+
loadBundle(name: string, onProgress?: (progress: number) => void): Promise<Record<string, unknown>>;
|
|
265
|
+
/**
|
|
266
|
+
* Load multiple bundles simultaneously.
|
|
267
|
+
* Progress is aggregated across all bundles.
|
|
268
|
+
*
|
|
269
|
+
* @param names - Bundle names
|
|
270
|
+
* @param onProgress - Progress callback (0..1)
|
|
271
|
+
*/
|
|
272
|
+
loadBundles(names: string[], onProgress?: (progress: number) => void): Promise<Record<string, unknown>>;
|
|
273
|
+
/**
|
|
274
|
+
* Load individual assets by URL or alias.
|
|
275
|
+
*
|
|
276
|
+
* @param urls - Asset URLs or aliases
|
|
277
|
+
* @param onProgress - Progress callback (0..1)
|
|
278
|
+
*/
|
|
279
|
+
load<T = unknown>(urls: string | string[], onProgress?: (progress: number) => void): Promise<T>;
|
|
280
|
+
/**
|
|
281
|
+
* Get a loaded asset synchronously from cache.
|
|
282
|
+
*
|
|
283
|
+
* @param alias - Asset alias
|
|
284
|
+
* @throws if not loaded
|
|
285
|
+
*/
|
|
286
|
+
get<T = unknown>(alias: string): T;
|
|
287
|
+
/**
|
|
288
|
+
* Unload a bundle to free memory.
|
|
289
|
+
*/
|
|
290
|
+
unloadBundle(name: string): Promise<void>;
|
|
291
|
+
/**
|
|
292
|
+
* Start background loading a bundle (low-priority preload).
|
|
293
|
+
* Useful for loading bonus round assets while player is in base game.
|
|
294
|
+
*/
|
|
295
|
+
backgroundLoad(name: string): Promise<void>;
|
|
296
|
+
/**
|
|
297
|
+
* Get all bundle names from the manifest.
|
|
298
|
+
*/
|
|
299
|
+
getBundleNames(): string[];
|
|
300
|
+
/**
|
|
301
|
+
* Check if a bundle is loaded.
|
|
302
|
+
*/
|
|
303
|
+
isBundleLoaded(name: string): boolean;
|
|
304
|
+
private ensureInitialized;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
type AudioCategoryName = 'music' | 'sfx' | 'ui' | 'ambient';
|
|
308
|
+
/**
|
|
309
|
+
* Manages all game audio: music, SFX, UI sounds, ambient.
|
|
310
|
+
*
|
|
311
|
+
* Optional dependency on @pixi/sound — if not installed, AudioManager
|
|
312
|
+
* operates as a silent no-op (graceful degradation).
|
|
313
|
+
*
|
|
314
|
+
* Features:
|
|
315
|
+
* - Per-category volume control (music, sfx, ui, ambient)
|
|
316
|
+
* - Music crossfade and looping
|
|
317
|
+
* - Mobile audio unlock on first interaction
|
|
318
|
+
* - Mute state persistence in localStorage
|
|
319
|
+
* - Global mute/unmute
|
|
320
|
+
*
|
|
321
|
+
* @example
|
|
322
|
+
* ```ts
|
|
323
|
+
* const audio = new AudioManager({ music: 0.5, sfx: 0.8 });
|
|
324
|
+
* await audio.init();
|
|
325
|
+
* audio.playMusic('bg-music');
|
|
326
|
+
* audio.play('spin-click', 'sfx');
|
|
327
|
+
* ```
|
|
328
|
+
*/
|
|
329
|
+
declare class AudioManager {
|
|
330
|
+
private _soundModule;
|
|
331
|
+
private _initialized;
|
|
332
|
+
private _globalMuted;
|
|
333
|
+
private _persist;
|
|
334
|
+
private _storageKey;
|
|
335
|
+
private _categories;
|
|
336
|
+
private _currentMusic;
|
|
337
|
+
private _unlocked;
|
|
338
|
+
private _unlockHandler;
|
|
339
|
+
constructor(config?: AudioConfig);
|
|
340
|
+
/** Whether the audio system is initialized */
|
|
341
|
+
get initialized(): boolean;
|
|
342
|
+
/** Whether audio is globally muted */
|
|
343
|
+
get muted(): boolean;
|
|
344
|
+
/**
|
|
345
|
+
* Initialize the audio system.
|
|
346
|
+
* Dynamically imports @pixi/sound to keep it optional.
|
|
347
|
+
*/
|
|
348
|
+
init(): Promise<void>;
|
|
349
|
+
/**
|
|
350
|
+
* Play a sound effect.
|
|
351
|
+
*
|
|
352
|
+
* @param alias - Sound alias (must be loaded via AssetManager)
|
|
353
|
+
* @param category - Audio category (default: 'sfx')
|
|
354
|
+
* @param options - Additional play options
|
|
355
|
+
*/
|
|
356
|
+
play(alias: string, category?: AudioCategoryName, options?: {
|
|
357
|
+
volume?: number;
|
|
358
|
+
loop?: boolean;
|
|
359
|
+
speed?: number;
|
|
360
|
+
}): void;
|
|
361
|
+
/**
|
|
362
|
+
* Play background music with optional crossfade.
|
|
363
|
+
*
|
|
364
|
+
* @param alias - Music alias
|
|
365
|
+
* @param fadeDuration - Crossfade duration in ms (default: 500)
|
|
366
|
+
*/
|
|
367
|
+
playMusic(alias: string, fadeDuration?: number): void;
|
|
368
|
+
/**
|
|
369
|
+
* Stop current music.
|
|
370
|
+
*/
|
|
371
|
+
stopMusic(): void;
|
|
372
|
+
/**
|
|
373
|
+
* Stop all sounds.
|
|
374
|
+
*/
|
|
375
|
+
stopAll(): void;
|
|
376
|
+
/**
|
|
377
|
+
* Set volume for a category.
|
|
378
|
+
*/
|
|
379
|
+
setVolume(category: AudioCategoryName, volume: number): void;
|
|
380
|
+
/**
|
|
381
|
+
* Get volume for a category.
|
|
382
|
+
*/
|
|
383
|
+
getVolume(category: AudioCategoryName): number;
|
|
384
|
+
/**
|
|
385
|
+
* Mute a specific category.
|
|
386
|
+
*/
|
|
387
|
+
muteCategory(category: AudioCategoryName): void;
|
|
388
|
+
/**
|
|
389
|
+
* Unmute a specific category.
|
|
390
|
+
*/
|
|
391
|
+
unmuteCategory(category: AudioCategoryName): void;
|
|
392
|
+
/**
|
|
393
|
+
* Toggle mute for a category.
|
|
394
|
+
*/
|
|
395
|
+
toggleCategory(category: AudioCategoryName): boolean;
|
|
396
|
+
/**
|
|
397
|
+
* Mute all audio globally.
|
|
398
|
+
*/
|
|
399
|
+
muteAll(): void;
|
|
400
|
+
/**
|
|
401
|
+
* Unmute all audio globally.
|
|
402
|
+
*/
|
|
403
|
+
unmuteAll(): void;
|
|
404
|
+
/**
|
|
405
|
+
* Toggle global mute.
|
|
406
|
+
*/
|
|
407
|
+
toggleMute(): boolean;
|
|
408
|
+
/**
|
|
409
|
+
* Duck music volume (e.g., during big win presentation).
|
|
410
|
+
*
|
|
411
|
+
* @param factor - Volume multiplier (0..1), e.g. 0.3 = 30% of normal
|
|
412
|
+
*/
|
|
413
|
+
duckMusic(factor: number): void;
|
|
414
|
+
/**
|
|
415
|
+
* Restore music to normal volume after ducking.
|
|
416
|
+
*/
|
|
417
|
+
unduckMusic(): void;
|
|
418
|
+
/**
|
|
419
|
+
* Destroy the audio manager and free resources.
|
|
420
|
+
*/
|
|
421
|
+
destroy(): void;
|
|
422
|
+
/**
|
|
423
|
+
* Smoothly fade a sound's volume from `fromVol` to `toVol` over `durationMs`.
|
|
424
|
+
*/
|
|
425
|
+
private fadeVolume;
|
|
426
|
+
private applyVolumes;
|
|
427
|
+
private setupMobileUnlock;
|
|
428
|
+
private removeMobileUnlock;
|
|
429
|
+
private saveState;
|
|
430
|
+
private restoreState;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
interface InputEvents {
|
|
434
|
+
tap: {
|
|
435
|
+
x: number;
|
|
436
|
+
y: number;
|
|
437
|
+
};
|
|
438
|
+
press: {
|
|
439
|
+
x: number;
|
|
440
|
+
y: number;
|
|
441
|
+
};
|
|
442
|
+
release: {
|
|
443
|
+
x: number;
|
|
444
|
+
y: number;
|
|
445
|
+
};
|
|
446
|
+
move: {
|
|
447
|
+
x: number;
|
|
448
|
+
y: number;
|
|
449
|
+
};
|
|
450
|
+
swipe: {
|
|
451
|
+
direction: 'up' | 'down' | 'left' | 'right';
|
|
452
|
+
velocity: number;
|
|
453
|
+
};
|
|
454
|
+
keydown: {
|
|
455
|
+
key: string;
|
|
456
|
+
code: string;
|
|
457
|
+
};
|
|
458
|
+
keyup: {
|
|
459
|
+
key: string;
|
|
460
|
+
code: string;
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Unified input manager for touch, mouse, and keyboard.
|
|
465
|
+
*
|
|
466
|
+
* Features:
|
|
467
|
+
* - Unified pointer events (works with touch + mouse)
|
|
468
|
+
* - Swipe gesture detection
|
|
469
|
+
* - Keyboard input with isKeyDown state
|
|
470
|
+
* - Input locking (block input during animations)
|
|
471
|
+
*
|
|
472
|
+
* @example
|
|
473
|
+
* ```ts
|
|
474
|
+
* const input = new InputManager(app.canvas);
|
|
475
|
+
*
|
|
476
|
+
* input.on('tap', ({ x, y }) => console.log('Tapped at', x, y));
|
|
477
|
+
* input.on('swipe', ({ direction }) => console.log('Swiped', direction));
|
|
478
|
+
* input.on('keydown', ({ key }) => {
|
|
479
|
+
* if (key === ' ') spin();
|
|
480
|
+
* });
|
|
481
|
+
*
|
|
482
|
+
* // Block input during animations
|
|
483
|
+
* input.lock();
|
|
484
|
+
* await playAnimation();
|
|
485
|
+
* input.unlock();
|
|
486
|
+
* ```
|
|
487
|
+
*/
|
|
488
|
+
declare class InputManager extends EventEmitter<InputEvents> {
|
|
489
|
+
private _canvas;
|
|
490
|
+
private _locked;
|
|
491
|
+
private _keysDown;
|
|
492
|
+
private _destroyed;
|
|
493
|
+
private _viewportScale;
|
|
494
|
+
private _viewportOffsetX;
|
|
495
|
+
private _viewportOffsetY;
|
|
496
|
+
private _pointerStart;
|
|
497
|
+
private _swipeThreshold;
|
|
498
|
+
private _swipeMaxTime;
|
|
499
|
+
constructor(canvas: HTMLCanvasElement);
|
|
500
|
+
/** Whether input is currently locked */
|
|
501
|
+
get locked(): boolean;
|
|
502
|
+
/** Lock all input (e.g., during animations) */
|
|
503
|
+
lock(): void;
|
|
504
|
+
/** Unlock input */
|
|
505
|
+
unlock(): void;
|
|
506
|
+
/** Check if a key is currently pressed */
|
|
507
|
+
isKeyDown(key: string): boolean;
|
|
508
|
+
/**
|
|
509
|
+
* Update the viewport transform used for DOM→world coordinate mapping.
|
|
510
|
+
* Called automatically by GameApplication when ViewportManager emits resize.
|
|
511
|
+
*/
|
|
512
|
+
setViewportTransform(scale: number, offsetX: number, offsetY: number): void;
|
|
513
|
+
/**
|
|
514
|
+
* Convert a DOM canvas position to game-world coordinates,
|
|
515
|
+
* accounting for viewport scaling and offset.
|
|
516
|
+
*/
|
|
517
|
+
getWorldPosition(canvasX: number, canvasY: number): {
|
|
518
|
+
x: number;
|
|
519
|
+
y: number;
|
|
520
|
+
};
|
|
521
|
+
/** Destroy the input manager */
|
|
522
|
+
destroy(): void;
|
|
523
|
+
private setupPointerEvents;
|
|
524
|
+
private onPointerDown;
|
|
525
|
+
private onPointerUp;
|
|
526
|
+
private onPointerMove;
|
|
527
|
+
private getCanvasPosition;
|
|
528
|
+
private setupKeyboardEvents;
|
|
529
|
+
private onKeyDown;
|
|
530
|
+
private onKeyUp;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
interface ViewportConfig {
|
|
534
|
+
designWidth: number;
|
|
535
|
+
designHeight: number;
|
|
536
|
+
scaleMode: ScaleMode;
|
|
537
|
+
orientation: Orientation;
|
|
538
|
+
}
|
|
539
|
+
interface ViewportEvents {
|
|
540
|
+
resize: {
|
|
541
|
+
width: number;
|
|
542
|
+
height: number;
|
|
543
|
+
scale: number;
|
|
544
|
+
};
|
|
545
|
+
orientationChange: Orientation;
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Manages responsive scaling of the game canvas to fit its container.
|
|
549
|
+
*
|
|
550
|
+
* Supports three scale modes:
|
|
551
|
+
* - **FIT** — letterbox/pillarbox to maintain aspect ratio (industry standard)
|
|
552
|
+
* - **FILL** — fill container, crop edges
|
|
553
|
+
* - **STRETCH** — stretch to fill (distorts)
|
|
554
|
+
*
|
|
555
|
+
* Also handles:
|
|
556
|
+
* - Orientation detection (landscape/portrait)
|
|
557
|
+
* - Safe areas (mobile notch)
|
|
558
|
+
* - ResizeObserver for smooth container resizing
|
|
559
|
+
*
|
|
560
|
+
* @example
|
|
561
|
+
* ```ts
|
|
562
|
+
* const viewport = new ViewportManager(app, container, {
|
|
563
|
+
* designWidth: 1920,
|
|
564
|
+
* designHeight: 1080,
|
|
565
|
+
* scaleMode: ScaleMode.FIT,
|
|
566
|
+
* orientation: Orientation.LANDSCAPE,
|
|
567
|
+
* });
|
|
568
|
+
*
|
|
569
|
+
* viewport.on('resize', ({ width, height, scale }) => {
|
|
570
|
+
* console.log(`New size: ${width}x${height} @ ${scale}x`);
|
|
571
|
+
* });
|
|
572
|
+
* ```
|
|
573
|
+
*/
|
|
574
|
+
declare class ViewportManager extends EventEmitter<ViewportEvents> {
|
|
575
|
+
private _app;
|
|
576
|
+
private _container;
|
|
577
|
+
private _config;
|
|
578
|
+
private _resizeObserver;
|
|
579
|
+
private _currentOrientation;
|
|
580
|
+
private _currentWidth;
|
|
581
|
+
private _currentHeight;
|
|
582
|
+
private _currentScale;
|
|
583
|
+
private _destroyed;
|
|
584
|
+
private _resizeTimeout;
|
|
585
|
+
constructor(app: Application, container: HTMLElement, config: ViewportConfig);
|
|
586
|
+
/** Current canvas width in game units */
|
|
587
|
+
get width(): number;
|
|
588
|
+
/** Current canvas height in game units */
|
|
589
|
+
get height(): number;
|
|
590
|
+
/** Current scale factor */
|
|
591
|
+
get scale(): number;
|
|
592
|
+
/** Current orientation */
|
|
593
|
+
get orientation(): Orientation;
|
|
594
|
+
/** Design reference width */
|
|
595
|
+
get designWidth(): number;
|
|
596
|
+
/** Design reference height */
|
|
597
|
+
get designHeight(): number;
|
|
598
|
+
/**
|
|
599
|
+
* Force a resize calculation. Called automatically on container size change.
|
|
600
|
+
*/
|
|
601
|
+
refresh(): void;
|
|
602
|
+
/**
|
|
603
|
+
* Destroy the viewport manager.
|
|
604
|
+
*/
|
|
605
|
+
destroy(): void;
|
|
606
|
+
private setupObserver;
|
|
607
|
+
private onWindowResize;
|
|
608
|
+
private debouncedRefresh;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* FPS overlay for debugging performance.
|
|
613
|
+
*
|
|
614
|
+
* Shows FPS, frame time, and draw call count in the corner of the screen.
|
|
615
|
+
*
|
|
616
|
+
* @example
|
|
617
|
+
* ```ts
|
|
618
|
+
* const fps = new FPSOverlay(app);
|
|
619
|
+
* fps.show();
|
|
620
|
+
* ```
|
|
621
|
+
*/
|
|
622
|
+
declare class FPSOverlay {
|
|
623
|
+
private _app;
|
|
624
|
+
private _container;
|
|
625
|
+
private _fpsText;
|
|
626
|
+
private _visible;
|
|
627
|
+
private _samples;
|
|
628
|
+
private _maxSamples;
|
|
629
|
+
private _lastUpdate;
|
|
630
|
+
private _tickFn;
|
|
631
|
+
constructor(app: Application);
|
|
632
|
+
/** Show the FPS overlay */
|
|
633
|
+
show(): void;
|
|
634
|
+
/** Hide the FPS overlay */
|
|
635
|
+
hide(): void;
|
|
636
|
+
/** Toggle visibility */
|
|
637
|
+
toggle(): void;
|
|
638
|
+
/** Destroy the overlay */
|
|
639
|
+
destroy(): void;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* The main entry point for a game built on @energy8platform/game-engine.
|
|
644
|
+
*
|
|
645
|
+
* Orchestrates the full lifecycle:
|
|
646
|
+
* 1. Create PixiJS Application
|
|
647
|
+
* 2. Initialize SDK (or run offline)
|
|
648
|
+
* 3. Show CSS preloader → Canvas loading screen with progress bar
|
|
649
|
+
* 4. Load asset manifest
|
|
650
|
+
* 5. Transition to the first game scene
|
|
651
|
+
*
|
|
652
|
+
* @example
|
|
653
|
+
* ```ts
|
|
654
|
+
* import { GameApplication, ScaleMode } from '@energy8platform/game-engine';
|
|
655
|
+
* import { GameScene } from './scenes/GameScene';
|
|
656
|
+
*
|
|
657
|
+
* const game = new GameApplication({
|
|
658
|
+
* container: '#game',
|
|
659
|
+
* designWidth: 1920,
|
|
660
|
+
* designHeight: 1080,
|
|
661
|
+
* scaleMode: ScaleMode.FIT,
|
|
662
|
+
* manifest: { bundles: [
|
|
663
|
+
* { name: 'preload', assets: [{ alias: 'logo', src: 'logo.png' }] },
|
|
664
|
+
* { name: 'game', assets: [{ alias: 'bg', src: 'background.png' }] },
|
|
665
|
+
* ]},
|
|
666
|
+
* loading: { tapToStart: true },
|
|
667
|
+
* });
|
|
668
|
+
*
|
|
669
|
+
* game.scenes.register('game', GameScene);
|
|
670
|
+
* await game.start('game');
|
|
671
|
+
* ```
|
|
672
|
+
*/
|
|
673
|
+
declare class GameApplication extends EventEmitter<GameEngineEvents> {
|
|
674
|
+
/** PixiJS Application instance */
|
|
675
|
+
app: Application;
|
|
676
|
+
/** Scene manager */
|
|
677
|
+
scenes: SceneManager;
|
|
678
|
+
/** Asset manager */
|
|
679
|
+
assets: AssetManager;
|
|
680
|
+
/** Audio manager */
|
|
681
|
+
audio: AudioManager;
|
|
682
|
+
/** Input manager */
|
|
683
|
+
input: InputManager;
|
|
684
|
+
/** Viewport manager */
|
|
685
|
+
viewport: ViewportManager;
|
|
686
|
+
/** SDK instance (null in offline mode) */
|
|
687
|
+
sdk: CasinoGameSDK | null;
|
|
688
|
+
/** FPS overlay instance (only when debug: true) */
|
|
689
|
+
fpsOverlay: FPSOverlay | null;
|
|
690
|
+
/** Data received from SDK initialization */
|
|
691
|
+
initData: InitData | null;
|
|
692
|
+
/** Platform session (SDK + optional DevBridge). null until start() runs. */
|
|
693
|
+
platformSession: PlatformSession | null;
|
|
694
|
+
/** Branded game shell (only when config.shell is set). */
|
|
695
|
+
shell?: _energy8platform_platform_core_shell.GameShell;
|
|
696
|
+
/** Configuration */
|
|
697
|
+
readonly config: GameApplicationConfig;
|
|
698
|
+
private _running;
|
|
699
|
+
private _destroyed;
|
|
700
|
+
private _container;
|
|
701
|
+
constructor(config?: GameApplicationConfig);
|
|
702
|
+
/** Current game config from SDK (or null in offline mode) */
|
|
703
|
+
get gameConfig(): GameConfigData | null;
|
|
704
|
+
/** Current session data */
|
|
705
|
+
get session(): SessionData | null;
|
|
706
|
+
/** Current balance */
|
|
707
|
+
get balance(): number;
|
|
708
|
+
/** Current currency */
|
|
709
|
+
get currency(): string;
|
|
710
|
+
/** Whether the engine is running */
|
|
711
|
+
get isRunning(): boolean;
|
|
712
|
+
/**
|
|
713
|
+
* Start the game engine. This is the main entry point.
|
|
714
|
+
*
|
|
715
|
+
* @param firstScene - Key of the first scene to show after loading (must be registered)
|
|
716
|
+
* @param sceneData - Optional data to pass to the first scene's onEnter
|
|
717
|
+
*/
|
|
718
|
+
start(firstScene: string, sceneData?: unknown): Promise<void>;
|
|
719
|
+
/**
|
|
720
|
+
* Destroy the engine and free all resources.
|
|
721
|
+
*/
|
|
722
|
+
destroy(): Promise<void>;
|
|
723
|
+
private resolveContainer;
|
|
724
|
+
private initPixi;
|
|
725
|
+
private initSDK;
|
|
726
|
+
private applySDKConfig;
|
|
727
|
+
private initSubSystems;
|
|
728
|
+
private loadAssets;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
interface WinTier {
|
|
732
|
+
id: string;
|
|
733
|
+
minMultiplier: number;
|
|
734
|
+
title: string;
|
|
735
|
+
accentColor: number;
|
|
736
|
+
bannerTexture?: Texture;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
interface SlotShellOptions {
|
|
740
|
+
mount?: HTMLElement;
|
|
741
|
+
/** Override the derived currency (normally taken from initData). */
|
|
742
|
+
currency?: CurrencyConfig;
|
|
743
|
+
/** Author-supplied info sections, MERGED over the host-derived set by section identity (an author
|
|
744
|
+
* section REPLACES the derived one of the same `type`/`kind`; a new `type` is appended; derived
|
|
745
|
+
* sections without an override are kept).
|
|
746
|
+
*
|
|
747
|
+
* Pass a plain `GameInfoContent`, OR a function `(t) => GameInfoContent` where `t` is the
|
|
748
|
+
* social-aware translator (it rewrites restricted gambling words when in social mode, and is the
|
|
749
|
+
* identity otherwise) — wrap player-facing copy in `t(...)` so it socializes. Either way the
|
|
750
|
+
* merged result is also run through `socialize` in social mode as a safety net, so forbidden
|
|
751
|
+
* words never leak even if `t()` was forgotten. */
|
|
752
|
+
gameInfo?: GameInfoContent | ((t: (text: string) => string) => GameInfoContent);
|
|
753
|
+
/** Override the derived buy/ante options. In social mode the card copy is socialized too. */
|
|
754
|
+
buyBonus?: BonusOption[];
|
|
755
|
+
tiers?: WinTier[];
|
|
756
|
+
features?: Partial<ShellFeatures>;
|
|
757
|
+
}
|
|
758
|
+
/** Runtime context from the SDK handshake (initData) + the resolved mode. */
|
|
759
|
+
interface ShellRuntime {
|
|
760
|
+
balance: number;
|
|
761
|
+
/** Resolved shell currency, derived from `initData.config.currency` (the SAME meta the Stake
|
|
762
|
+
* bridge builds). Pass a full `CurrencyConfig` — see `resolveCurrency`. */
|
|
763
|
+
currency?: CurrencyConfig;
|
|
764
|
+
language?: string;
|
|
765
|
+
mode: ShellMode;
|
|
766
|
+
/** Social-casino mode from initData (`config.socialMode`); swaps shell vocabulary. */
|
|
767
|
+
social?: boolean;
|
|
768
|
+
/** Stake-required disclaimer lines from initData (`config.disclaimerLines`); when
|
|
769
|
+
* absent (non-stake/dev) no disclaimer section is rendered. */
|
|
770
|
+
disclaimerLines?: string[];
|
|
771
|
+
/** Jurisdiction flags from initData (`config.jurisdiction`). Restrict shell features — applied
|
|
772
|
+
* OVER the author's features so a jurisdiction restriction always wins. */
|
|
773
|
+
jurisdiction?: JurisdictionRestrictions;
|
|
774
|
+
/** Bet ladder from `/wallet/authenticate` (`initData.config.betLevels`, major units). Stake ladders
|
|
775
|
+
* are CURRENCY-SPECIFIC (us_/non_us_/social_), so this overrides the spec's static `betLevels` on a
|
|
776
|
+
* Stake launch; falls back to the spec on dev/devBridge. */
|
|
777
|
+
betLevels?: number[];
|
|
778
|
+
/** Per-currency default bet from `/wallet/authenticate` (the bridge surfaces it as
|
|
779
|
+
* `config.stake.defaultBetLevel`). Stake requires the selector to start here on every entry. */
|
|
780
|
+
defaultBet?: number;
|
|
781
|
+
}
|
|
782
|
+
/** The subset of Stake's jurisdiction flags the shell can enforce via `ShellFeatures`. */
|
|
783
|
+
interface JurisdictionRestrictions {
|
|
784
|
+
/** No turbo at all → `features.turbo = 0`. */
|
|
785
|
+
disabledTurbo?: boolean;
|
|
786
|
+
/** Basic turbo allowed, but no super-turbo → cap `features.turbo` at 1. */
|
|
787
|
+
disabledSuperTurbo?: boolean;
|
|
788
|
+
/** No spacebar quick-spin → `features.spacebar = false`. */
|
|
789
|
+
disabledSpacebar?: boolean;
|
|
790
|
+
/** No autoplay → `features.autoplay = null`. */
|
|
791
|
+
disabledAutoplay?: boolean;
|
|
792
|
+
/** No buy-feature → `features.buyBonus = false`. */
|
|
793
|
+
disabledBuyFeature?: boolean;
|
|
794
|
+
}
|
|
795
|
+
/** Total stake for an action = bet × the action's cost multiplier (1 for a base spin; e.g. 100 for
|
|
796
|
+
* a buy bonus). The host uses this to block a play the balance can't cover. */
|
|
797
|
+
declare function stakeForAction(model: GameModel, action: string, bet: number): number;
|
|
798
|
+
/** Pure: assemble a ShellConfig from the model + runtime context (currency/balance/language/mode). */
|
|
799
|
+
declare function buildShellConfig(opts: SlotShellOptions, model: GameModel, runtime: ShellRuntime): ShellConfig;
|
|
800
|
+
|
|
801
|
+
interface StakeIntegration {
|
|
802
|
+
/** The game's BookAdapter (or its module). modeMap + gameId come from the model. */
|
|
803
|
+
adapter: BookAdapter | AdapterModule;
|
|
804
|
+
}
|
|
805
|
+
/** One scene registered with the host: a key + its constructor. The list order matters — the
|
|
806
|
+
* first scene that is eligible for the current launch mode is the start scene (unless an explicit
|
|
807
|
+
* `startScene` overrides it). */
|
|
808
|
+
interface SceneRegistration {
|
|
809
|
+
key: string;
|
|
810
|
+
scene: SceneConstructor;
|
|
811
|
+
/** Skip this scene as a START scene on a replay launch (e.g. an intro). It is still registered
|
|
812
|
+
* (other scenes can `goto` it), it just isn't auto-started — the first non-skipped scene is. */
|
|
813
|
+
skipOnReplay?: boolean;
|
|
814
|
+
}
|
|
815
|
+
/** @deprecated alias kept for one release — use {@link SceneRegistration}. */
|
|
816
|
+
type SceneEntry = SceneRegistration;
|
|
817
|
+
/** Navigation injected into the start data of EVERY scene the host registers.
|
|
818
|
+
* Any scene (intro, game, …) reads it from its `onEnter(data)` to navigate. */
|
|
819
|
+
interface SceneNavData {
|
|
820
|
+
/** Switch to another registered scene by key. */
|
|
821
|
+
goto: (key: string, data?: unknown) => void;
|
|
822
|
+
}
|
|
823
|
+
interface CreateSlotGameOptions<T extends SlotSpinResultBase = SlotSpinResultBase> {
|
|
824
|
+
model: GameModel;
|
|
825
|
+
/** REQUIRED: maps the raw play result into the game's typed result. The host calls it on every play. */
|
|
826
|
+
normalize: SlotResultNormalizer<T>;
|
|
827
|
+
/** ALL scenes the game uses, registered up front, in order. The first scene eligible for the
|
|
828
|
+
* launch mode is the start scene — so a replay launch skips any leading `skipOnReplay` scene
|
|
829
|
+
* (e.g. the intro) and starts directly on the game scene. */
|
|
830
|
+
scenes: SceneRegistration[];
|
|
831
|
+
/** Optional explicit start scene key. Defaults to the first scene eligible for the launch mode
|
|
832
|
+
* (honoured only when that scene is itself eligible; otherwise the first eligible one wins). */
|
|
833
|
+
startScene?: string;
|
|
834
|
+
/** Start data passed to the start scene's `onEnter` (merged with the injected `goto`). */
|
|
835
|
+
startData?: unknown;
|
|
836
|
+
manifest: AssetManifest;
|
|
837
|
+
container?: HTMLElement | string;
|
|
838
|
+
design?: {
|
|
839
|
+
width: number;
|
|
840
|
+
height: number;
|
|
841
|
+
};
|
|
842
|
+
scaleMode?: ScaleMode;
|
|
843
|
+
orientation?: Orientation;
|
|
844
|
+
loading?: LoadingScreenConfig;
|
|
845
|
+
audio?: AudioConfig;
|
|
846
|
+
pixi?: Partial<ApplicationOptions>;
|
|
847
|
+
fonts?: string[];
|
|
848
|
+
textureDefaults?: boolean;
|
|
849
|
+
dev?: boolean;
|
|
850
|
+
stake?: StakeIntegration;
|
|
851
|
+
shell?: SlotShellOptions;
|
|
852
|
+
onFatalError?: (message: string) => void;
|
|
853
|
+
}
|
|
854
|
+
interface SlotGameHandle {
|
|
855
|
+
game: GameApplication;
|
|
856
|
+
stakeBridge: StakeBridge | null;
|
|
857
|
+
shell: GameShell | null;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/**
|
|
861
|
+
* One-call slot bootstrap: preboot → (optional Stake bridge) → GameApplication
|
|
862
|
+
* → register scene → start. Collapses the per-game main.ts boilerplate.
|
|
863
|
+
*
|
|
864
|
+
* Not unit-tested: GameApplication.init() drives Pixi, which hangs in headless
|
|
865
|
+
* environments. The pure helpers it sequences are unit-tested individually.
|
|
866
|
+
*/
|
|
867
|
+
declare function createSlotGame<T extends SlotSpinResultBase = SlotSpinResultBase>(opts: CreateSlotGameOptions<T>): Promise<SlotGameHandle>;
|
|
868
|
+
|
|
869
|
+
/** Reverse the model's modeMap (Stake bet mode → SDK action key) for replay labelling/cost. */
|
|
870
|
+
declare function resolveReplayBonusId(model: GameModel, stakeMode: string): string;
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* Pick the scene to START with, given the registered scenes (in order) and the launch mode.
|
|
874
|
+
*
|
|
875
|
+
* Rules:
|
|
876
|
+
* - On a replay launch, scenes flagged `skipOnReplay` are NOT eligible to start (they stay
|
|
877
|
+
* registered for `goto`, they just aren't auto-started) — so a leading intro is skipped and
|
|
878
|
+
* the game scene starts directly.
|
|
879
|
+
* - An explicit `startScene` wins, but only if that scene is itself eligible; otherwise the first
|
|
880
|
+
* eligible scene wins.
|
|
881
|
+
* - Falls back to the first scene unconditionally if nothing is eligible (degenerate config).
|
|
882
|
+
*/
|
|
883
|
+
declare function resolveStartScene(scenes: SceneRegistration[], isReplay: boolean, explicitStart?: string): string;
|
|
884
|
+
|
|
885
|
+
/** Everything a scene needs to render one result. The host builds it once per round. */
|
|
886
|
+
interface RenderContext {
|
|
887
|
+
/** Bet for this round (major units). Stable for the whole round (a bonus is one round). */
|
|
888
|
+
bet: number;
|
|
889
|
+
/** Trigger action in the game's own vocabulary (gameSpec.actions keys): 'spin' | 'ante' |
|
|
890
|
+
* 'buy_bonus' | … Stable for the whole round. */
|
|
891
|
+
action: string;
|
|
892
|
+
/** Stake bet-mode of the round (model.spec.modeMap[action]): 'BASE' | 'ANTE' | 'BONUS' | …
|
|
893
|
+
* Canonical per-round identifier of WHICH bonus/feature this is. Stable for the whole round. */
|
|
894
|
+
mode: string;
|
|
895
|
+
/** Currency-aware money formatter. win/totalWin get variable decimals (0.0041 stays 0.0041). */
|
|
896
|
+
formatAmount(value: number): string;
|
|
897
|
+
/** LIVE turbo level (0 = off, 1..3 = escalating speed), matching the shell's state.turbo. Read at
|
|
898
|
+
* the moment of access (getter) so a mid-round toggle is reflected. */
|
|
899
|
+
readonly turbo: number;
|
|
900
|
+
}
|
|
901
|
+
/** The contract a slot scene implements. The HOST owns the play→present→ack→drain loop and calls
|
|
902
|
+
* these; the scene only renders. The game never sees play/ack/roundId. */
|
|
903
|
+
interface SlotSceneController<T extends SlotSpinResultBase = SlotSpinResultBase> {
|
|
904
|
+
/** Render ONE segment (a spin, or one free spin). All pacing/pauses/overlays live here
|
|
905
|
+
* (await your own animations). The host calls this once per segment. */
|
|
906
|
+
present(result: T, ctx: RenderContext): Promise<void>;
|
|
907
|
+
/** Optional. Fires EXACTLY before the first free spin of a bonus (intro; spin counts in
|
|
908
|
+
* trigger.freeSpins). */
|
|
909
|
+
onBonusEnter?(trigger: T, ctx: RenderContext): Promise<void>;
|
|
910
|
+
/** Optional. Fires after the last free spin of a bonus (summary; last.totalWin = bonus total). */
|
|
911
|
+
onBonusExit?(last: T, ctx: RenderContext): Promise<void>;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
export { buildShellConfig, createSlotGame, resolveReplayBonusId, resolveStartScene, stakeForAction };
|
|
915
|
+
export type { CreateSlotGameOptions, RenderContext, SceneEntry, SceneNavData, SceneRegistration, SlotGameHandle, SlotSceneController, SlotShellOptions, StakeIntegration };
|