@energy8platform/game-engine 0.7.1 → 0.9.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,871 @@
1
+ import { Container, ApplicationOptions, Application } from 'pixi.js';
2
+ import * as react from 'react';
3
+ import { ReactElement } from 'react';
4
+ import { CasinoGameSDK, InitData, GameConfigData, SessionData } from '@energy8platform/game-sdk';
5
+
6
+ interface PixiRoot {
7
+ render(element: ReactElement): void;
8
+ unmount(): void;
9
+ }
10
+ declare function createPixiRoot(container: Container): PixiRoot;
11
+
12
+ /**
13
+ * Register PixiJS classes for use as JSX elements.
14
+ * Keys must be PascalCase; JSX uses the camelCase equivalent.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * import { Container, Sprite, Text } from 'pixi.js';
19
+ * extend({ Container, Sprite, Text });
20
+ * // Now <container>, <sprite>, <text> work in JSX
21
+ * ```
22
+ */
23
+ declare function extend(components: Record<string, any>): void;
24
+
25
+ /**
26
+ * Register all standard PixiJS display objects for JSX use.
27
+ * Call once at app startup before rendering any React scenes.
28
+ */
29
+ declare function extendPixiElements(): void;
30
+ /**
31
+ * Register @pixi/layout components for JSX use.
32
+ * Pass the dynamically imported module:
33
+ *
34
+ * ```ts
35
+ * const layout = await import('@pixi/layout/components');
36
+ * extendLayoutElements(layout);
37
+ * ```
38
+ */
39
+ declare function extendLayoutElements(layoutModule: Record<string, any>): void;
40
+
41
+ declare enum ScaleMode {
42
+ /** Fit inside container, maintain aspect ratio (letterbox/pillarbox) */
43
+ FIT = "FIT",
44
+ /** Fill container, maintain aspect ratio (crop edges) */
45
+ FILL = "FILL",
46
+ /** Stretch to fill (distorts) */
47
+ STRETCH = "STRETCH"
48
+ }
49
+ declare enum Orientation {
50
+ LANDSCAPE = "landscape",
51
+ PORTRAIT = "portrait",
52
+ ANY = "any"
53
+ }
54
+ interface LoadingScreenConfig {
55
+ /** Background color (hex number or CSS string) */
56
+ backgroundColor?: number | string;
57
+ /** Background gradient (CSS string applied to the CSS preloader) */
58
+ backgroundGradient?: string;
59
+ /** Logo texture alias (must be in 'preload' bundle) */
60
+ logoAsset?: string;
61
+ /** Logo scale (default: 1) */
62
+ logoScale?: number;
63
+ /** Show percentage text below the loader bar */
64
+ showPercentage?: boolean;
65
+ /** Custom progress text formatter */
66
+ progressTextFormatter?: (progress: number) => string;
67
+ /** Show "Tap to start" after loading (needed for mobile audio unlock) */
68
+ tapToStart?: boolean;
69
+ /** "Tap to start" label text */
70
+ tapToStartText?: string;
71
+ /** Minimum display time in ms (so the user sees the brand, even if loading is fast) */
72
+ minDisplayTime?: number;
73
+ /** CSS preloader custom HTML (shown before PixiJS is ready) */
74
+ cssPreloaderHTML?: string;
75
+ }
76
+ interface AssetEntry {
77
+ alias: string;
78
+ src: string | string[];
79
+ /** Optional data to pass to the loader */
80
+ data?: Record<string, unknown>;
81
+ }
82
+ interface AssetBundle {
83
+ name: string;
84
+ assets: AssetEntry[];
85
+ }
86
+ interface AssetManifest {
87
+ bundles: AssetBundle[];
88
+ }
89
+ interface AudioConfig {
90
+ /** Default volumes per category (0..1) */
91
+ music?: number;
92
+ sfx?: number;
93
+ ui?: number;
94
+ ambient?: number;
95
+ /** Persist mute state in localStorage */
96
+ persist?: boolean;
97
+ /** LocalStorage key prefix */
98
+ storageKey?: string;
99
+ }
100
+ interface GameApplicationConfig {
101
+ /** Container element or CSS selector to mount canvas into */
102
+ container?: HTMLElement | string;
103
+ /** Reference design width (fallback: GameConfigData.viewport.width or 1920) */
104
+ designWidth?: number;
105
+ /** Reference design height (fallback: GameConfigData.viewport.height or 1080) */
106
+ designHeight?: number;
107
+ /** How to scale the game to fit the container */
108
+ scaleMode?: ScaleMode;
109
+ /** Preferred orientation */
110
+ orientation?: Orientation;
111
+ /** Loading screen configuration */
112
+ loading?: LoadingScreenConfig;
113
+ /** Asset manifest — what to load */
114
+ manifest?: AssetManifest;
115
+ /** Audio configuration */
116
+ audio?: AudioConfig;
117
+ /** SDK options. Set to false to disable SDK (offline/development mode) */
118
+ sdk?: {
119
+ parentOrigin?: string;
120
+ timeout?: number;
121
+ debug?: boolean;
122
+ /** Use in-memory channel instead of postMessage (no iframe required) */
123
+ devMode?: boolean;
124
+ } | false;
125
+ /** PixiJS Application options (pass-through) */
126
+ pixi?: Partial<ApplicationOptions>;
127
+ /** Enable debug overlay (FPS, draw calls) */
128
+ debug?: boolean;
129
+ }
130
+ interface SceneConstructor {
131
+ new (): IScene;
132
+ [key: string]: any;
133
+ }
134
+ interface IScene {
135
+ /** Root display container for this scene */
136
+ readonly container: Container;
137
+ /** @internal GameApplication reference — set by SceneManager */
138
+ __engineApp?: any;
139
+ /** Called when the scene is entered */
140
+ onEnter?(data?: unknown): Promise<void> | void;
141
+ /** Called when the scene is exited */
142
+ onExit?(): Promise<void> | void;
143
+ /** Called every frame */
144
+ onUpdate?(dt: number): void;
145
+ /** Called when viewport resizes */
146
+ onResize?(width: number, height: number): void;
147
+ /** Called when the scene is destroyed */
148
+ onDestroy?(): void;
149
+ }
150
+ declare enum TransitionType {
151
+ NONE = "none",
152
+ FADE = "fade",
153
+ SLIDE_LEFT = "slide-left",
154
+ SLIDE_RIGHT = "slide-right"
155
+ }
156
+ interface TransitionConfig {
157
+ type: TransitionType;
158
+ duration?: number;
159
+ easing?: (t: number) => number;
160
+ }
161
+ interface GameEngineEvents {
162
+ /** Fired when engine initialization is complete */
163
+ initialized: void;
164
+ /** Fired when all assets are loaded */
165
+ loaded: void;
166
+ /** Fired when the engine starts running */
167
+ started: void;
168
+ /** Fired on viewport resize */
169
+ resize: {
170
+ width: number;
171
+ height: number;
172
+ };
173
+ /** Fired on orientation change */
174
+ orientationChange: Orientation;
175
+ /** Fired on scene change */
176
+ sceneChange: {
177
+ from: string | null;
178
+ to: string;
179
+ };
180
+ /** Fired when player balance changes (forwarded from SDK) */
181
+ balanceUpdate: {
182
+ balance: number;
183
+ };
184
+ /** Fired on error */
185
+ error: Error;
186
+ /** Fired when engine is destroyed */
187
+ destroyed: void;
188
+ }
189
+
190
+ /**
191
+ * Base class for all scenes.
192
+ * Provides a root PixiJS Container and lifecycle hooks.
193
+ *
194
+ * @example
195
+ * ```ts
196
+ * class MenuScene extends Scene {
197
+ * async onEnter() {
198
+ * const bg = Sprite.from('menu-bg');
199
+ * this.container.addChild(bg);
200
+ * }
201
+ *
202
+ * onUpdate(dt: number) {
203
+ * // per-frame logic
204
+ * }
205
+ *
206
+ * onResize(width: number, height: number) {
207
+ * // reposition UI
208
+ * }
209
+ * }
210
+ * ```
211
+ */
212
+ declare abstract class Scene implements IScene {
213
+ readonly container: Container;
214
+ constructor();
215
+ /** Called when this scene becomes active. Override in subclass. */
216
+ onEnter?(data?: unknown): Promise<void> | void;
217
+ /** Called when this scene is deactivated. Override in subclass. */
218
+ onExit?(): Promise<void> | void;
219
+ /** Called every frame with delta time (in seconds). Override in subclass. */
220
+ onUpdate?(dt: number): void;
221
+ /** Called when the viewport resizes. Override in subclass. */
222
+ onResize?(width: number, height: number): void;
223
+ /** Cleanup — called when the scene is permanently removed. */
224
+ onDestroy?(): void;
225
+ }
226
+
227
+ /**
228
+ * Minimal typed event emitter.
229
+ * Used internally by GameApplication, SceneManager, AudioManager, etc.
230
+ *
231
+ * Supports `void` event types — events that carry no data can be emitted
232
+ * without arguments: `emitter.emit('eventName')`.
233
+ */
234
+ declare class EventEmitter<TEvents extends {}> {
235
+ private listeners;
236
+ on<K extends keyof TEvents>(event: K, handler: (data: TEvents[K]) => void): this;
237
+ once<K extends keyof TEvents>(event: K, handler: (data: TEvents[K]) => void): this;
238
+ off<K extends keyof TEvents>(event: K, handler: (data: TEvents[K]) => void): this;
239
+ emit<K extends keyof TEvents>(...args: TEvents[K] extends void ? [event: K] : [event: K, data: TEvents[K]]): void;
240
+ removeAllListeners(event?: keyof TEvents): this;
241
+ }
242
+
243
+ interface SceneEntry {
244
+ scene: IScene;
245
+ key: string;
246
+ }
247
+ interface SceneManagerEvents {
248
+ change: {
249
+ from: string | null;
250
+ to: string;
251
+ };
252
+ }
253
+ /**
254
+ * Manages the scene stack and transitions between scenes.
255
+ *
256
+ * @example
257
+ * ```ts
258
+ * const scenes = new SceneManager(app.stage);
259
+ * scenes.register('loading', LoadingScene);
260
+ * scenes.register('game', GameScene);
261
+ * await scenes.goto('loading');
262
+ * ```
263
+ */
264
+ declare class SceneManager extends EventEmitter<SceneManagerEvents> {
265
+ private static MAX_TRANSITION_DEPTH;
266
+ /** Root container that scenes are added to */
267
+ root: Container;
268
+ private registry;
269
+ private stack;
270
+ private _transitionDepth;
271
+ /** Current viewport dimensions — set by ViewportManager */
272
+ private _width;
273
+ private _height;
274
+ /** @internal GameApplication reference — passed to scenes */
275
+ private _app;
276
+ constructor(root?: Container);
277
+ /** @internal Set the root container (called by GameApplication after PixiJS init) */
278
+ setRoot(root: Container): void;
279
+ /** @internal Set the app reference (called by GameApplication) */
280
+ setApp(app: any): void;
281
+ /** Register a scene class by key */
282
+ register(key: string, ctor: SceneConstructor): this;
283
+ /** Get the current (topmost) scene entry */
284
+ get current(): SceneEntry | null;
285
+ /** Get the current scene key */
286
+ get currentKey(): string | null;
287
+ /** Whether a scene transition is in progress */
288
+ get isTransitioning(): boolean;
289
+ /**
290
+ * Navigate to a scene, replacing the entire stack.
291
+ */
292
+ goto(key: string, data?: unknown, transition?: TransitionConfig): Promise<void>;
293
+ /**
294
+ * Push a scene onto the stack (the previous scene stays underneath).
295
+ * Useful for overlays, modals, pause screens.
296
+ */
297
+ push(key: string, data?: unknown, transition?: TransitionConfig): Promise<void>;
298
+ /**
299
+ * Pop the top scene from the stack.
300
+ */
301
+ pop(transition?: TransitionConfig): Promise<void>;
302
+ /**
303
+ * Replace the top scene with a new one.
304
+ */
305
+ replace(key: string, data?: unknown, transition?: TransitionConfig): Promise<void>;
306
+ /**
307
+ * Called every frame by GameApplication.
308
+ */
309
+ update(dt: number): void;
310
+ /**
311
+ * Called on viewport resize.
312
+ */
313
+ resize(width: number, height: number): void;
314
+ /**
315
+ * Destroy all scenes and clear the manager.
316
+ */
317
+ destroy(): void;
318
+ private createScene;
319
+ private pushInternal;
320
+ private popInternal;
321
+ private transitionIn;
322
+ private transitionOut;
323
+ }
324
+
325
+ /**
326
+ * Manages game asset loading with progress tracking, bundle support, and
327
+ * automatic base path resolution from SDK's assetsUrl.
328
+ *
329
+ * Wraps PixiJS Assets API with a typed, game-oriented interface.
330
+ *
331
+ * @example
332
+ * ```ts
333
+ * const assets = new AssetManager('https://cdn.example.com/game/', manifest);
334
+ * await assets.init();
335
+ * await assets.loadBundle('preload', (p) => console.log(p));
336
+ * const texture = assets.get<Texture>('hero');
337
+ * ```
338
+ */
339
+ declare class AssetManager {
340
+ private _initialized;
341
+ private _basePath;
342
+ private _manifest;
343
+ private _loadedBundles;
344
+ constructor(basePath?: string, manifest?: AssetManifest);
345
+ /** Whether the asset system has been initialized */
346
+ get initialized(): boolean;
347
+ /** Base path for all assets (usually from SDK's assetsUrl) */
348
+ get basePath(): string;
349
+ /** Set of loaded bundle names */
350
+ get loadedBundles(): ReadonlySet<string>;
351
+ /**
352
+ * Initialize the asset system.
353
+ * Must be called before loading any assets.
354
+ */
355
+ init(): Promise<void>;
356
+ /**
357
+ * Load a single bundle by name.
358
+ *
359
+ * @param name - Bundle name (must exist in the manifest)
360
+ * @param onProgress - Progress callback (0..1)
361
+ * @returns Loaded assets map
362
+ */
363
+ loadBundle(name: string, onProgress?: (progress: number) => void): Promise<Record<string, unknown>>;
364
+ /**
365
+ * Load multiple bundles simultaneously.
366
+ * Progress is aggregated across all bundles.
367
+ *
368
+ * @param names - Bundle names
369
+ * @param onProgress - Progress callback (0..1)
370
+ */
371
+ loadBundles(names: string[], onProgress?: (progress: number) => void): Promise<Record<string, unknown>>;
372
+ /**
373
+ * Load individual assets by URL or alias.
374
+ *
375
+ * @param urls - Asset URLs or aliases
376
+ * @param onProgress - Progress callback (0..1)
377
+ */
378
+ load<T = unknown>(urls: string | string[], onProgress?: (progress: number) => void): Promise<T>;
379
+ /**
380
+ * Get a loaded asset synchronously from cache.
381
+ *
382
+ * @param alias - Asset alias
383
+ * @throws if not loaded
384
+ */
385
+ get<T = unknown>(alias: string): T;
386
+ /**
387
+ * Unload a bundle to free memory.
388
+ */
389
+ unloadBundle(name: string): Promise<void>;
390
+ /**
391
+ * Start background loading a bundle (low-priority preload).
392
+ * Useful for loading bonus round assets while player is in base game.
393
+ */
394
+ backgroundLoad(name: string): Promise<void>;
395
+ /**
396
+ * Get all bundle names from the manifest.
397
+ */
398
+ getBundleNames(): string[];
399
+ /**
400
+ * Check if a bundle is loaded.
401
+ */
402
+ isBundleLoaded(name: string): boolean;
403
+ private ensureInitialized;
404
+ }
405
+
406
+ type AudioCategoryName = 'music' | 'sfx' | 'ui' | 'ambient';
407
+ /**
408
+ * Manages all game audio: music, SFX, UI sounds, ambient.
409
+ *
410
+ * Optional dependency on @pixi/sound — if not installed, AudioManager
411
+ * operates as a silent no-op (graceful degradation).
412
+ *
413
+ * Features:
414
+ * - Per-category volume control (music, sfx, ui, ambient)
415
+ * - Music crossfade and looping
416
+ * - Mobile audio unlock on first interaction
417
+ * - Mute state persistence in localStorage
418
+ * - Global mute/unmute
419
+ *
420
+ * @example
421
+ * ```ts
422
+ * const audio = new AudioManager({ music: 0.5, sfx: 0.8 });
423
+ * await audio.init();
424
+ * audio.playMusic('bg-music');
425
+ * audio.play('spin-click', 'sfx');
426
+ * ```
427
+ */
428
+ declare class AudioManager {
429
+ private _soundModule;
430
+ private _initialized;
431
+ private _globalMuted;
432
+ private _persist;
433
+ private _storageKey;
434
+ private _categories;
435
+ private _currentMusic;
436
+ private _unlocked;
437
+ private _unlockHandler;
438
+ constructor(config?: AudioConfig);
439
+ /** Whether the audio system is initialized */
440
+ get initialized(): boolean;
441
+ /** Whether audio is globally muted */
442
+ get muted(): boolean;
443
+ /**
444
+ * Initialize the audio system.
445
+ * Dynamically imports @pixi/sound to keep it optional.
446
+ */
447
+ init(): Promise<void>;
448
+ /**
449
+ * Play a sound effect.
450
+ *
451
+ * @param alias - Sound alias (must be loaded via AssetManager)
452
+ * @param category - Audio category (default: 'sfx')
453
+ * @param options - Additional play options
454
+ */
455
+ play(alias: string, category?: AudioCategoryName, options?: {
456
+ volume?: number;
457
+ loop?: boolean;
458
+ speed?: number;
459
+ }): void;
460
+ /**
461
+ * Play background music with optional crossfade.
462
+ *
463
+ * @param alias - Music alias
464
+ * @param fadeDuration - Crossfade duration in ms (default: 500)
465
+ */
466
+ playMusic(alias: string, fadeDuration?: number): void;
467
+ /**
468
+ * Stop current music.
469
+ */
470
+ stopMusic(): void;
471
+ /**
472
+ * Stop all sounds.
473
+ */
474
+ stopAll(): void;
475
+ /**
476
+ * Set volume for a category.
477
+ */
478
+ setVolume(category: AudioCategoryName, volume: number): void;
479
+ /**
480
+ * Get volume for a category.
481
+ */
482
+ getVolume(category: AudioCategoryName): number;
483
+ /**
484
+ * Mute a specific category.
485
+ */
486
+ muteCategory(category: AudioCategoryName): void;
487
+ /**
488
+ * Unmute a specific category.
489
+ */
490
+ unmuteCategory(category: AudioCategoryName): void;
491
+ /**
492
+ * Toggle mute for a category.
493
+ */
494
+ toggleCategory(category: AudioCategoryName): boolean;
495
+ /**
496
+ * Mute all audio globally.
497
+ */
498
+ muteAll(): void;
499
+ /**
500
+ * Unmute all audio globally.
501
+ */
502
+ unmuteAll(): void;
503
+ /**
504
+ * Toggle global mute.
505
+ */
506
+ toggleMute(): boolean;
507
+ /**
508
+ * Duck music volume (e.g., during big win presentation).
509
+ *
510
+ * @param factor - Volume multiplier (0..1), e.g. 0.3 = 30% of normal
511
+ */
512
+ duckMusic(factor: number): void;
513
+ /**
514
+ * Restore music to normal volume after ducking.
515
+ */
516
+ unduckMusic(): void;
517
+ /**
518
+ * Destroy the audio manager and free resources.
519
+ */
520
+ destroy(): void;
521
+ /**
522
+ * Smoothly fade a sound's volume from `fromVol` to `toVol` over `durationMs`.
523
+ */
524
+ private fadeVolume;
525
+ private applyVolumes;
526
+ private setupMobileUnlock;
527
+ private removeMobileUnlock;
528
+ private saveState;
529
+ private restoreState;
530
+ }
531
+
532
+ interface InputEvents {
533
+ tap: {
534
+ x: number;
535
+ y: number;
536
+ };
537
+ press: {
538
+ x: number;
539
+ y: number;
540
+ };
541
+ release: {
542
+ x: number;
543
+ y: number;
544
+ };
545
+ move: {
546
+ x: number;
547
+ y: number;
548
+ };
549
+ swipe: {
550
+ direction: 'up' | 'down' | 'left' | 'right';
551
+ velocity: number;
552
+ };
553
+ keydown: {
554
+ key: string;
555
+ code: string;
556
+ };
557
+ keyup: {
558
+ key: string;
559
+ code: string;
560
+ };
561
+ }
562
+ /**
563
+ * Unified input manager for touch, mouse, and keyboard.
564
+ *
565
+ * Features:
566
+ * - Unified pointer events (works with touch + mouse)
567
+ * - Swipe gesture detection
568
+ * - Keyboard input with isKeyDown state
569
+ * - Input locking (block input during animations)
570
+ *
571
+ * @example
572
+ * ```ts
573
+ * const input = new InputManager(app.canvas);
574
+ *
575
+ * input.on('tap', ({ x, y }) => console.log('Tapped at', x, y));
576
+ * input.on('swipe', ({ direction }) => console.log('Swiped', direction));
577
+ * input.on('keydown', ({ key }) => {
578
+ * if (key === ' ') spin();
579
+ * });
580
+ *
581
+ * // Block input during animations
582
+ * input.lock();
583
+ * await playAnimation();
584
+ * input.unlock();
585
+ * ```
586
+ */
587
+ declare class InputManager extends EventEmitter<InputEvents> {
588
+ private _canvas;
589
+ private _locked;
590
+ private _keysDown;
591
+ private _destroyed;
592
+ private _viewportScale;
593
+ private _viewportOffsetX;
594
+ private _viewportOffsetY;
595
+ private _pointerStart;
596
+ private _swipeThreshold;
597
+ private _swipeMaxTime;
598
+ constructor(canvas: HTMLCanvasElement);
599
+ /** Whether input is currently locked */
600
+ get locked(): boolean;
601
+ /** Lock all input (e.g., during animations) */
602
+ lock(): void;
603
+ /** Unlock input */
604
+ unlock(): void;
605
+ /** Check if a key is currently pressed */
606
+ isKeyDown(key: string): boolean;
607
+ /**
608
+ * Update the viewport transform used for DOM→world coordinate mapping.
609
+ * Called automatically by GameApplication when ViewportManager emits resize.
610
+ */
611
+ setViewportTransform(scale: number, offsetX: number, offsetY: number): void;
612
+ /**
613
+ * Convert a DOM canvas position to game-world coordinates,
614
+ * accounting for viewport scaling and offset.
615
+ */
616
+ getWorldPosition(canvasX: number, canvasY: number): {
617
+ x: number;
618
+ y: number;
619
+ };
620
+ /** Destroy the input manager */
621
+ destroy(): void;
622
+ private setupPointerEvents;
623
+ private onPointerDown;
624
+ private onPointerUp;
625
+ private onPointerMove;
626
+ private getCanvasPosition;
627
+ private setupKeyboardEvents;
628
+ private onKeyDown;
629
+ private onKeyUp;
630
+ }
631
+
632
+ interface ViewportConfig {
633
+ designWidth: number;
634
+ designHeight: number;
635
+ scaleMode: ScaleMode;
636
+ orientation: Orientation;
637
+ }
638
+ interface ViewportEvents {
639
+ resize: {
640
+ width: number;
641
+ height: number;
642
+ scale: number;
643
+ };
644
+ orientationChange: Orientation;
645
+ }
646
+ /**
647
+ * Manages responsive scaling of the game canvas to fit its container.
648
+ *
649
+ * Supports three scale modes:
650
+ * - **FIT** — letterbox/pillarbox to maintain aspect ratio (industry standard)
651
+ * - **FILL** — fill container, crop edges
652
+ * - **STRETCH** — stretch to fill (distorts)
653
+ *
654
+ * Also handles:
655
+ * - Orientation detection (landscape/portrait)
656
+ * - Safe areas (mobile notch)
657
+ * - ResizeObserver for smooth container resizing
658
+ *
659
+ * @example
660
+ * ```ts
661
+ * const viewport = new ViewportManager(app, container, {
662
+ * designWidth: 1920,
663
+ * designHeight: 1080,
664
+ * scaleMode: ScaleMode.FIT,
665
+ * orientation: Orientation.LANDSCAPE,
666
+ * });
667
+ *
668
+ * viewport.on('resize', ({ width, height, scale }) => {
669
+ * console.log(`New size: ${width}x${height} @ ${scale}x`);
670
+ * });
671
+ * ```
672
+ */
673
+ declare class ViewportManager extends EventEmitter<ViewportEvents> {
674
+ private _app;
675
+ private _container;
676
+ private _config;
677
+ private _resizeObserver;
678
+ private _currentOrientation;
679
+ private _currentWidth;
680
+ private _currentHeight;
681
+ private _currentScale;
682
+ private _destroyed;
683
+ private _resizeTimeout;
684
+ constructor(app: Application, container: HTMLElement, config: ViewportConfig);
685
+ /** Current canvas width in game units */
686
+ get width(): number;
687
+ /** Current canvas height in game units */
688
+ get height(): number;
689
+ /** Current scale factor */
690
+ get scale(): number;
691
+ /** Current orientation */
692
+ get orientation(): Orientation;
693
+ /** Design reference width */
694
+ get designWidth(): number;
695
+ /** Design reference height */
696
+ get designHeight(): number;
697
+ /**
698
+ * Force a resize calculation. Called automatically on container size change.
699
+ */
700
+ refresh(): void;
701
+ /**
702
+ * Destroy the viewport manager.
703
+ */
704
+ destroy(): void;
705
+ private setupObserver;
706
+ private onWindowResize;
707
+ private debouncedRefresh;
708
+ }
709
+
710
+ /**
711
+ * FPS overlay for debugging performance.
712
+ *
713
+ * Shows FPS, frame time, and draw call count in the corner of the screen.
714
+ *
715
+ * @example
716
+ * ```ts
717
+ * const fps = new FPSOverlay(app);
718
+ * fps.show();
719
+ * ```
720
+ */
721
+ declare class FPSOverlay {
722
+ private _app;
723
+ private _container;
724
+ private _fpsText;
725
+ private _visible;
726
+ private _samples;
727
+ private _maxSamples;
728
+ private _lastUpdate;
729
+ private _tickFn;
730
+ constructor(app: Application);
731
+ /** Show the FPS overlay */
732
+ show(): void;
733
+ /** Hide the FPS overlay */
734
+ hide(): void;
735
+ /** Toggle visibility */
736
+ toggle(): void;
737
+ /** Destroy the overlay */
738
+ destroy(): void;
739
+ }
740
+
741
+ /**
742
+ * The main entry point for a game built on @energy8platform/game-engine.
743
+ *
744
+ * Orchestrates the full lifecycle:
745
+ * 1. Create PixiJS Application
746
+ * 2. Initialize SDK (or run offline)
747
+ * 3. Show CSS preloader → Canvas loading screen with progress bar
748
+ * 4. Load asset manifest
749
+ * 5. Transition to the first game scene
750
+ *
751
+ * @example
752
+ * ```ts
753
+ * import { GameApplication, ScaleMode } from '@energy8platform/game-engine';
754
+ * import { GameScene } from './scenes/GameScene';
755
+ *
756
+ * const game = new GameApplication({
757
+ * container: '#game',
758
+ * designWidth: 1920,
759
+ * designHeight: 1080,
760
+ * scaleMode: ScaleMode.FIT,
761
+ * manifest: { bundles: [
762
+ * { name: 'preload', assets: [{ alias: 'logo', src: 'logo.png' }] },
763
+ * { name: 'game', assets: [{ alias: 'bg', src: 'background.png' }] },
764
+ * ]},
765
+ * loading: { tapToStart: true },
766
+ * });
767
+ *
768
+ * game.scenes.register('game', GameScene);
769
+ * await game.start('game');
770
+ * ```
771
+ */
772
+ declare class GameApplication extends EventEmitter<GameEngineEvents> {
773
+ /** PixiJS Application instance */
774
+ app: Application;
775
+ /** Scene manager */
776
+ scenes: SceneManager;
777
+ /** Asset manager */
778
+ assets: AssetManager;
779
+ /** Audio manager */
780
+ audio: AudioManager;
781
+ /** Input manager */
782
+ input: InputManager;
783
+ /** Viewport manager */
784
+ viewport: ViewportManager;
785
+ /** SDK instance (null in offline mode) */
786
+ sdk: CasinoGameSDK | null;
787
+ /** FPS overlay instance (only when debug: true) */
788
+ fpsOverlay: FPSOverlay | null;
789
+ /** Data received from SDK initialization */
790
+ initData: InitData | null;
791
+ /** Configuration */
792
+ readonly config: GameApplicationConfig;
793
+ private _running;
794
+ private _destroyed;
795
+ private _container;
796
+ constructor(config?: GameApplicationConfig);
797
+ /** Current game config from SDK (or null in offline mode) */
798
+ get gameConfig(): GameConfigData | null;
799
+ /** Current session data */
800
+ get session(): SessionData | null;
801
+ /** Current balance */
802
+ get balance(): number;
803
+ /** Current currency */
804
+ get currency(): string;
805
+ /** Whether the engine is running */
806
+ get isRunning(): boolean;
807
+ /**
808
+ * Start the game engine. This is the main entry point.
809
+ *
810
+ * @param firstScene - Key of the first scene to show after loading (must be registered)
811
+ * @param sceneData - Optional data to pass to the first scene's onEnter
812
+ */
813
+ start(firstScene: string, sceneData?: unknown): Promise<void>;
814
+ /**
815
+ * Destroy the engine and free all resources.
816
+ */
817
+ destroy(): void;
818
+ private resolveContainer;
819
+ private initPixi;
820
+ private initSDK;
821
+ private applySDKConfig;
822
+ private initSubSystems;
823
+ private loadAssets;
824
+ }
825
+
826
+ declare abstract class ReactScene extends Scene {
827
+ private _pixiRoot;
828
+ private _contextValue;
829
+ /** Subclasses implement this to return their React element tree. */
830
+ abstract render(): ReactElement;
831
+ /** Access the GameApplication instance. */
832
+ protected getApp(): GameApplication;
833
+ onEnter(data?: unknown): Promise<void>;
834
+ onExit(): Promise<void>;
835
+ onResize(width: number, height: number): void;
836
+ onDestroy(): void;
837
+ private _mountReactTree;
838
+ }
839
+
840
+ interface EngineContextValue {
841
+ app: GameApplication;
842
+ sdk: CasinoGameSDK | null;
843
+ audio: AudioManager;
844
+ input: InputManager;
845
+ viewport: ViewportManager;
846
+ gameConfig: GameConfigData | null;
847
+ screen: {
848
+ width: number;
849
+ height: number;
850
+ scale: number;
851
+ };
852
+ isPortrait: boolean;
853
+ }
854
+ declare const EngineContext: react.Context<EngineContextValue | null>;
855
+ declare function useEngine(): EngineContextValue;
856
+
857
+ declare function useSDK(): CasinoGameSDK | null;
858
+ declare function useAudio(): AudioManager;
859
+ declare function useInput(): InputManager;
860
+ declare function useViewport(): {
861
+ width: number;
862
+ height: number;
863
+ scale: number;
864
+ isPortrait: boolean;
865
+ };
866
+ declare function useBalance(): number;
867
+ declare function useSession(): SessionData | null;
868
+ declare function useGameConfig<T = GameConfigData>(): T | null;
869
+
870
+ export { EngineContext, ReactScene, createPixiRoot, extend, extendLayoutElements, extendPixiElements, useAudio, useBalance, useEngine, useGameConfig, useInput, useSDK, useSession, useViewport };
871
+ export type { EngineContextValue, PixiRoot };