@energy8platform/game-engine 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +318 -49
  2. package/dist/animation.cjs.js +191 -1
  3. package/dist/animation.cjs.js.map +1 -1
  4. package/dist/animation.d.ts +117 -1
  5. package/dist/animation.esm.js +192 -3
  6. package/dist/animation.esm.js.map +1 -1
  7. package/dist/audio.cjs.js +66 -16
  8. package/dist/audio.cjs.js.map +1 -1
  9. package/dist/audio.d.ts +4 -0
  10. package/dist/audio.esm.js +66 -16
  11. package/dist/audio.esm.js.map +1 -1
  12. package/dist/core.cjs.js +310 -84
  13. package/dist/core.cjs.js.map +1 -1
  14. package/dist/core.d.ts +60 -1
  15. package/dist/core.esm.js +311 -85
  16. package/dist/core.esm.js.map +1 -1
  17. package/dist/debug.cjs.js +36 -68
  18. package/dist/debug.cjs.js.map +1 -1
  19. package/dist/debug.d.ts +4 -6
  20. package/dist/debug.esm.js +36 -68
  21. package/dist/debug.esm.js.map +1 -1
  22. package/dist/index.cjs.js +1250 -251
  23. package/dist/index.cjs.js.map +1 -1
  24. package/dist/index.d.ts +386 -41
  25. package/dist/index.esm.js +1250 -254
  26. package/dist/index.esm.js.map +1 -1
  27. package/dist/ui.cjs.js +757 -1
  28. package/dist/ui.cjs.js.map +1 -1
  29. package/dist/ui.d.ts +208 -2
  30. package/dist/ui.esm.js +756 -2
  31. package/dist/ui.esm.js.map +1 -1
  32. package/dist/vite.cjs.js +65 -68
  33. package/dist/vite.cjs.js.map +1 -1
  34. package/dist/vite.d.ts +17 -23
  35. package/dist/vite.esm.js +66 -68
  36. package/dist/vite.esm.js.map +1 -1
  37. package/package.json +4 -5
  38. package/src/animation/SpriteAnimation.ts +210 -0
  39. package/src/animation/Tween.ts +27 -1
  40. package/src/animation/index.ts +2 -0
  41. package/src/audio/AudioManager.ts +64 -15
  42. package/src/core/EventEmitter.ts +7 -1
  43. package/src/core/GameApplication.ts +25 -7
  44. package/src/core/SceneManager.ts +3 -1
  45. package/src/debug/DevBridge.ts +49 -80
  46. package/src/index.ts +6 -0
  47. package/src/input/InputManager.ts +26 -0
  48. package/src/loading/CSSPreloader.ts +7 -33
  49. package/src/loading/LoadingScene.ts +17 -41
  50. package/src/loading/index.ts +1 -0
  51. package/src/loading/logo.ts +95 -0
  52. package/src/types.ts +4 -0
  53. package/src/ui/BalanceDisplay.ts +14 -0
  54. package/src/ui/Button.ts +1 -1
  55. package/src/ui/Layout.ts +364 -0
  56. package/src/ui/ScrollContainer.ts +557 -0
  57. package/src/ui/index.ts +4 -0
  58. package/src/viewport/ViewportManager.ts +2 -0
  59. package/src/vite/index.ts +83 -83
package/README.md CHANGED
@@ -17,7 +17,21 @@ A universal casino game engine built on [PixiJS v8](https://pixijs.com/) and [@e
17
17
  - [Viewport & Scaling](#viewport--scaling)
18
18
  - [State Machine](#state-machine)
19
19
  - [Animation](#animation)
20
+ - [Tween](#tween)
21
+ - [Timeline](#timeline)
22
+ - [SpriteAnimation](#spriteanimation)
23
+ - [Spine Animations](#spine-animations)
20
24
  - [UI Components](#ui-components)
25
+ - [Layout](#layout)
26
+ - [ScrollContainer](#scrollcontainer)
27
+ - [Button](#button)
28
+ - [Label](#label)
29
+ - [BalanceDisplay](#balancedisplay)
30
+ - [WinDisplay](#windisplay)
31
+ - [ProgressBar](#progressbar)
32
+ - [Panel](#panel)
33
+ - [Modal](#modal)
34
+ - [Toast](#toast)
21
35
  - [Input](#input)
22
36
  - [Vite Configuration](#vite-configuration)
23
37
  - [DevBridge](#devbridge)
@@ -83,7 +97,7 @@ async function bootstrap() {
83
97
  debug: true,
84
98
  });
85
99
 
86
- game.scenes.register('game', GameScene as any);
100
+ game.scenes.register('game', GameScene);
87
101
 
88
102
  game.on('initialized', () => console.log('Engine initialized'));
89
103
  game.on('loaded', () => console.log('Assets loaded'));
@@ -105,7 +119,7 @@ bootstrap();
105
119
  | Package | Version | Required |
106
120
  | --- | --- | --- |
107
121
  | `pixi.js` | `^8.16.0` | Yes |
108
- | `@energy8platform/game-sdk` | `^2.5.0` | Yes |
122
+ | `@energy8platform/game-sdk` | `^2.6.0` | Yes |
109
123
  | `@pixi/sound` | `^6.0.0` | Optional — for audio |
110
124
  | `@esotericsoftware/spine-pixi-v8` | `~4.2.0` | Optional — for Spine animations |
111
125
 
@@ -118,8 +132,8 @@ import { GameApplication } from '@energy8platform/game-engine'; // full b
118
132
  import { Scene, SceneManager } from '@energy8platform/game-engine/core';
119
133
  import { AssetManager } from '@energy8platform/game-engine/assets';
120
134
  import { AudioManager } from '@energy8platform/game-engine/audio';
121
- import { Button, Label, Modal } from '@energy8platform/game-engine/ui';
122
- import { Tween, Timeline, Easing } from '@energy8platform/game-engine/animation';
135
+ import { Button, Label, Modal, Layout, ScrollContainer } from '@energy8platform/game-engine/ui';
136
+ import { Tween, Timeline, Easing, SpriteAnimation } from '@energy8platform/game-engine/animation';
123
137
  import { DevBridge, FPSOverlay } from '@energy8platform/game-engine/debug';
124
138
  import { defineGameConfig } from '@energy8platform/game-engine/vite';
125
139
  ```
@@ -146,7 +160,7 @@ import { defineGameConfig } from '@energy8platform/game-engine/vite';
146
160
 
147
161
  1. **CSS Preloader** — an instant HTML/CSS overlay shown while PixiJS initializes (inline SVG logo with a shimmer animation and "Loading..." text).
148
162
  2. **PixiJS initialization** — creates `Application`, initializes `ResizeObserver`.
149
- 3. **SDK handshake** — connects to the casino host (or DevBridge in dev mode).
163
+ 3. **SDK handshake** — connects to the casino host (or DevBridge in dev mode via shared `MemoryChannel`).
150
164
  4. **Canvas Loading Screen** — `LoadingScene` displays the SVG logo with an animated progress bar, `preload` bundle is loaded first.
151
165
  5. **Asset loading** — remaining bundles are loaded; the progress bar fills in real time.
152
166
  6. **Tap-to-start** — optional screen shown after loading (required on mobile for audio unlock).
@@ -169,6 +183,10 @@ import { defineGameConfig } from '@energy8platform/game-engine/vite';
169
183
  | `manifest` | `AssetManifest` | — | Asset manifest |
170
184
  | `audio` | `AudioConfig` | — | Audio configuration |
171
185
  | `sdk` | `object \| false` | — | SDK options; `false` to disable |
186
+ | `sdk.devMode` | `boolean` | `false` | Use in-memory channel instead of `postMessage` (no iframe needed) |
187
+ | `sdk.parentOrigin` | `string` | — | Expected parent origin for `postMessage` validation |
188
+ | `sdk.timeout` | `number` | — | SDK handshake timeout in ms |
189
+ | `sdk.debug` | `boolean` | — | Enable SDK debug logging |
172
190
  | `pixi` | `Partial<ApplicationOptions>` | — | PixiJS pass-through options |
173
191
  | `debug` | `boolean` | `false` | Enable FPS overlay |
174
192
 
@@ -182,21 +200,21 @@ import { defineGameConfig } from '@energy8platform/game-engine/vite';
182
200
  | `logoScale` | `number` | `1` | Logo scale factor |
183
201
  | `showPercentage` | `boolean` | `true` | Show loading percentage text |
184
202
  | `progressTextFormatter` | `(progress: number) => string` | — | Custom progress text formatter |
185
- | `tapToStart` | `boolean` | `false` | Show "Tap to start" overlay |
203
+ | `tapToStart` | `boolean` | `true` | Show "Tap to start" overlay |
186
204
  | `tapToStartText` | `string` | `'TAP TO START'` | Tap-to-start label |
187
- | `minDisplayTime` | `number` | `0` | Minimum display time (ms) |
205
+ | `minDisplayTime` | `number` | `1500` | Minimum display time (ms) |
188
206
  | `cssPreloaderHTML` | `string` | — | Custom HTML for the CSS preloader |
189
207
 
190
208
  ### `AudioConfig`
191
209
 
192
210
  | Property | Type | Default | Description |
193
211
  | --- | --- | --- | --- |
194
- | `music` | `number` | `1` | Default music volume (0–1) |
212
+ | `music` | `number` | `0.7` | Default music volume (0–1) |
195
213
  | `sfx` | `number` | `1` | Default SFX volume |
196
- | `ui` | `number` | `1` | Default UI sounds volume |
197
- | `ambient` | `number` | `1` | Default ambient volume |
198
- | `persist` | `boolean` | `false` | Save mute state to localStorage |
199
- | `storageKey` | `string` | `'ge-audio'` | localStorage key prefix |
214
+ | `ui` | `number` | `0.8` | Default UI sounds volume |
215
+ | `ambient` | `number` | `0.5` | Default ambient volume |
216
+ | `persist` | `boolean` | `true` | Save mute state to localStorage |
217
+ | `storageKey` | `string` | `'ge_audio'` | localStorage key prefix |
200
218
 
201
219
  ### Enums
202
220
 
@@ -261,9 +279,9 @@ export class GameScene extends Scene {
261
279
  const scenes = game.scenes;
262
280
 
263
281
  // Register scenes
264
- scenes.register('menu', MenuScene as any);
265
- scenes.register('game', GameScene as any);
266
- scenes.register('bonus', BonusScene as any);
282
+ scenes.register('menu', MenuScene);
283
+ scenes.register('game', GameScene);
284
+ scenes.register('bonus', BonusScene);
267
285
 
268
286
  // Navigate (replaces entire stack)
269
287
  await scenes.goto('game');
@@ -400,7 +418,7 @@ const audio = game.audio;
400
418
  audio.play('click', 'ui');
401
419
  audio.play('coin-drop', 'sfx', { volume: 0.8 });
402
420
 
403
- // Music with crossfade
421
+ // Music with crossfade (smooth volume transition between tracks)
404
422
  audio.playMusic('main-theme', 1000); // 1s crossfade
405
423
  audio.stopMusic();
406
424
 
@@ -542,13 +560,17 @@ await Tween.from(sprite, { scale: 0 }, 300, Easing.easeOutBack);
542
560
  // Animate between two sets of values
543
561
  await Tween.fromTo(sprite, { x: -100 }, { x: 500 }, 1000, Easing.easeInOutQuad);
544
562
 
545
- // Wait (useful in timelines or sequences)
563
+ // Wait (uses PixiJS Ticker for consistent timing)
546
564
  await Tween.delay(1000);
547
565
 
548
566
  // Cancel tweens
549
567
  Tween.killTweensOf(sprite);
550
568
  Tween.killAll();
551
569
 
570
+ // Full reset — kill all tweens and remove ticker listener
571
+ // Useful for cleanup between game instances, tests, or hot-reload
572
+ Tween.reset();
573
+
552
574
  // Supports nested properties
553
575
  await Tween.to(sprite, { 'scale.x': 2, 'position.y': 300 }, 500);
554
576
  ```
@@ -592,6 +614,59 @@ All 24 easing functions:
592
614
  | | | | `easeOutElastic` |
593
615
  | | | | `easeInElastic` |
594
616
 
617
+ ### SpriteAnimation
618
+
619
+ Frame-based animation helper wrapping PixiJS `AnimatedSprite`. Cheaper than Spine for simple frame sequences — perfect for coin showers, symbol animations, sparkle trails, and win celebrations.
620
+
621
+ ```typescript
622
+ import { SpriteAnimation } from '@energy8platform/game-engine';
623
+ import { Assets } from 'pixi.js';
624
+
625
+ // From an array of textures
626
+ const coinAnim = SpriteAnimation.create(coinTextures, {
627
+ fps: 30,
628
+ loop: true,
629
+ });
630
+ scene.container.addChild(coinAnim);
631
+
632
+ // From a spritesheet using a name prefix
633
+ const sheet = Assets.get('effects');
634
+ const sparkle = SpriteAnimation.fromSpritesheet(sheet, 'sparkle_', {
635
+ fps: 24,
636
+ loop: true,
637
+ });
638
+
639
+ // From a numbered range (e.g., 'explosion_00' to 'explosion_24')
640
+ const explosion = SpriteAnimation.fromRange(sheet, 'explosion_{i}', 0, 24, {
641
+ fps: 60,
642
+ loop: false,
643
+ onComplete: () => explosion.destroy(),
644
+ });
645
+
646
+ // From pre-loaded texture aliases
647
+ const anim = SpriteAnimation.fromAliases(
648
+ ['frame_0', 'frame_1', 'frame_2', 'frame_3'],
649
+ { fps: 12 },
650
+ );
651
+
652
+ // Fire-and-forget: play once and auto-destroy
653
+ const { sprite, finished } = SpriteAnimation.playOnce(coinTextures, {
654
+ fps: 30,
655
+ });
656
+ scene.container.addChild(sprite);
657
+ await finished; // resolves when animation completes
658
+ ```
659
+
660
+ #### SpriteAnimationConfig
661
+
662
+ | Property | Type | Default | Description |
663
+ | --- | --- | --- | --- |
664
+ | `fps` | `number` | `24` | Frames per second |
665
+ | `loop` | `boolean` | `true` | Whether to loop |
666
+ | `autoPlay` | `boolean` | `true` | Start playing immediately |
667
+ | `onComplete` | `() => void` | — | Callback when animation completes (non-looping) |
668
+ | `anchor` | `number \| { x, y }` | `0.5` | Anchor point |
669
+
595
670
  ### Spine Animations
596
671
 
597
672
  If `@esotericsoftware/spine-pixi-v8` is installed:
@@ -621,6 +696,136 @@ console.log(SpineHelper.getAnimationNames(spine));
621
696
 
622
697
  ## UI Components
623
698
 
699
+ ### Layout
700
+
701
+ Responsive layout container that automatically arranges children. Supports horizontal, vertical, grid, and wrap modes with alignment, padding, gap, anchor positioning, and viewport breakpoints.
702
+
703
+ ```typescript
704
+ import { Layout } from '@energy8platform/game-engine';
705
+
706
+ // Horizontal toolbar anchored to bottom-center
707
+ const toolbar = new Layout({
708
+ direction: 'horizontal',
709
+ gap: 20,
710
+ alignment: 'center',
711
+ anchor: 'bottom-center',
712
+ padding: 16,
713
+ breakpoints: {
714
+ 768: { direction: 'vertical', gap: 10 },
715
+ },
716
+ });
717
+
718
+ toolbar.addItem(spinButton);
719
+ toolbar.addItem(betLabel);
720
+ toolbar.addItem(balanceDisplay);
721
+ scene.container.addChild(toolbar);
722
+
723
+ // Update position on resize
724
+ toolbar.updateViewport(width, height);
725
+ ```
726
+
727
+ ```typescript
728
+ // Grid layout for a symbol paytable
729
+ const grid = new Layout({
730
+ direction: 'grid',
731
+ columns: 3,
732
+ gap: 16,
733
+ alignment: 'center',
734
+ anchor: 'center',
735
+ padding: [20, 40, 20, 40],
736
+ });
737
+
738
+ symbols.forEach((sym) => grid.addItem(sym));
739
+ grid.updateViewport(viewWidth, viewHeight);
740
+ ```
741
+
742
+ ```typescript
743
+ // Wrap layout — items flow and wrap to next line
744
+ const tags = new Layout({
745
+ direction: 'wrap',
746
+ gap: 8,
747
+ maxWidth: 600,
748
+ });
749
+ ```
750
+
751
+ #### LayoutConfig
752
+
753
+ | Property | Type | Default | Description |
754
+ | --- | --- | --- | --- |
755
+ | `direction` | `'horizontal' \| 'vertical' \| 'grid' \| 'wrap'` | `'vertical'` | Layout direction |
756
+ | `gap` | `number` | `0` | Gap between children (px) |
757
+ | `padding` | `number \| [top, right, bottom, left]` | `0` | Inner padding |
758
+ | `alignment` | `'start' \| 'center' \| 'end' \| 'stretch'` | `'start'` | Cross-axis alignment |
759
+ | `anchor` | `LayoutAnchor` | `'top-left'` | Position relative to viewport |
760
+ | `columns` | `number` | `2` | Column count (grid mode only) |
761
+ | `maxWidth` | `number` | `Infinity` | Max width before wrapping (wrap mode) |
762
+ | `autoLayout` | `boolean` | `true` | Auto-recalculate on add/remove |
763
+ | `breakpoints` | `Record<number, Partial<LayoutConfig>>` | — | Override config per viewport width |
764
+
765
+ **Anchor values:** `top-left`, `top-center`, `top-right`, `center-left`, `center`, `center-right`, `bottom-left`, `bottom-center`, `bottom-right`
766
+
767
+ ### ScrollContainer
768
+
769
+ Scrollable container with touch/drag, mouse wheel, inertia deceleration, elastic overscroll bounce, snap-to-item, and auto-hiding scrollbars. Ideal for paytables, settings, bet history, and any content that doesn't fit on screen.
770
+
771
+ ```typescript
772
+ import { ScrollContainer } from '@energy8platform/game-engine';
773
+
774
+ const scroll = new ScrollContainer({
775
+ width: 600,
776
+ height: 400,
777
+ direction: 'vertical',
778
+ showScrollbar: true,
779
+ elasticity: 0.3,
780
+ inertia: 0.92,
781
+ borderRadius: 12,
782
+ backgroundColor: 0x1a1a2e,
783
+ backgroundAlpha: 0.8,
784
+ });
785
+
786
+ // Build scrollable content
787
+ const list = new Container();
788
+ for (let i = 0; i < 50; i++) {
789
+ const row = createRow(i);
790
+ row.y = i * 40;
791
+ list.addChild(row);
792
+ }
793
+ scroll.setContent(list);
794
+
795
+ scene.container.addChild(scroll);
796
+ ```
797
+
798
+ ```typescript
799
+ // Programmatic scrolling
800
+ scroll.scrollTo(0, 200); // scroll to y=200 with animation
801
+ scroll.scrollTo(0, 200, false); // instant jump
802
+ scroll.scrollToItem(5); // snap to item index (when snapSize is set)
803
+
804
+ // Current position
805
+ const { x, y } = scroll.scrollPosition;
806
+
807
+ // Resize viewport
808
+ scroll.resize(newWidth, newHeight);
809
+ ```
810
+
811
+ #### ScrollContainerConfig
812
+
813
+ | Property | Type | Default | Description |
814
+ | --- | --- | --- | --- |
815
+ | `width` | `number` | — | Visible viewport width |
816
+ | `height` | `number` | — | Visible viewport height |
817
+ | `direction` | `'vertical' \| 'horizontal' \| 'both'` | `'vertical'` | Scroll direction |
818
+ | `showScrollbar` | `boolean` | `true` | Show scrollbar(s) |
819
+ | `scrollbarWidth` | `number` | `6` | Scrollbar width (px) |
820
+ | `scrollbarColor` | `number` | `0xffffff` | Scrollbar color |
821
+ | `scrollbarAlpha` | `number` | `0.4` | Scrollbar opacity |
822
+ | `elasticity` | `number` | `0.3` | Overscroll bounce (0 = none) |
823
+ | `inertia` | `number` | `0.92` | Deceleration factor (0 = instant stop) |
824
+ | `snapSize` | `number` | `0` | Snap to fixed increments (0 = disabled) |
825
+ | `backgroundColor` | `number` | — | Background color (transparent if omitted) |
826
+ | `backgroundAlpha` | `number` | `1` | Background opacity |
827
+ | `borderRadius` | `number` | `0` | Mask border radius |
828
+
624
829
  ### Button
625
830
 
626
831
  ```typescript
@@ -677,12 +882,12 @@ Displays player balance with formatting:
677
882
  import { BalanceDisplay } from '@energy8platform/game-engine';
678
883
 
679
884
  const balance = new BalanceDisplay({
680
- initialBalance: 10000,
681
885
  currency: 'USD',
886
+ animated: true,
682
887
  // ... text style options
683
888
  });
684
889
 
685
- balance.updateBalance(9500); // Animates the balance change
890
+ balance.setValue(9500); // Animates the balance change
686
891
  ```
687
892
 
688
893
  ### WinDisplay
@@ -693,10 +898,11 @@ Animated win amount display with countup:
693
898
  import { WinDisplay } from '@energy8platform/game-engine';
694
899
 
695
900
  const winDisplay = new WinDisplay({
901
+ countupDuration: 2000,
696
902
  // ... text style options
697
903
  });
698
904
 
699
- await winDisplay.show(5000, 2000); // Show $50.00, countup over 2 seconds
905
+ await winDisplay.showWin(5000); // Show $50.00, countup over 2 seconds
700
906
  winDisplay.hide();
701
907
  ```
702
908
 
@@ -709,11 +915,11 @@ const bar = new ProgressBar({
709
915
  width: 400,
710
916
  height: 20,
711
917
  fillColor: 0x00ff00,
712
- backgroundColor: 0x333333,
918
+ trackColor: 0x333333,
713
919
  borderRadius: 10,
714
920
  });
715
921
 
716
- bar.setProgress(0.75); // 75%
922
+ bar.progress = 0.75; // 75%
717
923
  ```
718
924
 
719
925
  ### Panel
@@ -728,7 +934,7 @@ const panel = new Panel({
728
934
  height: 400,
729
935
  backgroundColor: 0x1a1a2e,
730
936
  borderRadius: 16,
731
- alpha: 0.9,
937
+ backgroundAlpha: 0.9,
732
938
  });
733
939
  ```
734
940
 
@@ -740,14 +946,13 @@ Full-screen overlay dialog:
740
946
  import { Modal } from '@energy8platform/game-engine';
741
947
 
742
948
  const modal = new Modal({
743
- width: 500,
744
- height: 350,
745
949
  overlayAlpha: 0.7,
746
- backgroundColor: 0x222244,
747
- borderRadius: 16,
950
+ overlayColor: 0x000000,
951
+ closeOnOverlay: true,
952
+ animationDuration: 300,
748
953
  });
749
954
 
750
- await modal.show();
955
+ await modal.show(viewWidth, viewHeight);
751
956
  await modal.hide();
752
957
  ```
753
958
 
@@ -760,11 +965,9 @@ import { Toast } from '@energy8platform/game-engine';
760
965
 
761
966
  const toast = new Toast({
762
967
  duration: 2000,
763
- backgroundColor: 0x333333,
764
- textStyle: { fill: 0xffffff, fontSize: 20 },
765
968
  });
766
969
 
767
- await toast.show('Free spins activated!');
970
+ await toast.show('Free spins activated!', 'success');
768
971
  ```
769
972
 
770
973
  ---
@@ -801,10 +1004,17 @@ if (input.isKeyDown('ArrowLeft')) {
801
1004
  input.lock();
802
1005
  // ... animation plays ...
803
1006
  input.unlock();
1007
+
1008
+ // Convert DOM canvas position to game-world coordinates
1009
+ // (accounts for viewport scaling and offset)
1010
+ const worldPos = input.getWorldPosition(canvasX, canvasY);
1011
+ console.log(worldPos.x, worldPos.y);
804
1012
  ```
805
1013
 
806
1014
  **Events:** `tap`, `press`, `release`, `move`, `swipe`, `keydown`, `keyup`
807
1015
 
1016
+ > **Note:** The viewport transform for coordinate mapping is wired up automatically by `GameApplication`. Call `getWorldPosition()` when you need to convert raw DOM coordinates to game-world space.
1017
+
808
1018
  ---
809
1019
 
810
1020
  ## Vite Configuration
@@ -817,28 +1027,31 @@ import { defineGameConfig } from '@energy8platform/game-engine/vite';
817
1027
 
818
1028
  export default defineGameConfig({
819
1029
  base: '/games/my-slot/',
820
- outDir: 'dist',
821
1030
  devBridge: true, // Auto-inject DevBridge in dev mode
822
- assetExtensions: [
823
- 'png', 'jpg', 'webp', 'avif',
824
- 'mp3', 'ogg', 'wav',
825
- 'json', 'atlas', 'skel',
826
- 'fnt', 'ttf', 'woff2',
827
- ],
1031
+ devBridgeConfig: './dev.config', // Custom config path (optional)
828
1032
  vite: {
829
1033
  // Additional Vite config overrides
830
1034
  },
831
1035
  });
832
1036
  ```
833
1037
 
1038
+ ### `GameConfig`
1039
+
1040
+ | Property | Type | Default | Description |
1041
+ | --- | --- | --- | --- |
1042
+ | `base` | `string` | `'/'` | Vite `base` path for deployment |
1043
+ | `devBridge` | `boolean` | `false` | Auto-inject DevBridge in dev mode |
1044
+ | `devBridgeConfig` | `string` | `'./dev.config'` | Path to DevBridge config file |
1045
+ | `vite` | `UserConfig` | — | Additional Vite config to merge |
1046
+
834
1047
  ### What `defineGameConfig` Provides
835
1048
 
836
- - **PixiJS optimizations**tree-shaking, proper externals handling
1049
+ - **Build target: ES2020+** modern output for all supported casino platforms
1050
+ - **Asset inlining** — files under 8KB are auto-inlined
1051
+ - **PixiJS chunk splitting** — `pixi.js` is extracted into a separate chunk for caching
837
1052
  - **DevBridge injection** — automatically available in dev mode via virtual module
838
- - **Asset handling** — preconfigured loader patterns for game assets
839
- - **HTML minification** — optimized production builds
840
- - **Gzip-friendly output** — chunking strategy optimized for compression
841
- - **Base path** — configurable for deployment subpaths
1053
+ - **Dev server** — port 3000, auto-open browser
1054
+ - **Dependency optimization** — `pixi.js` pre-bundled for faster dev starts
842
1055
 
843
1056
  ### Custom DevBridge Configuration
844
1057
 
@@ -860,13 +1073,15 @@ export default {
860
1073
  } satisfies DevBridgeConfig;
861
1074
  ```
862
1075
 
863
- This file is auto-imported by the Vite plugin when `devBridge: true`.
1076
+ This file is auto-imported by the Vite plugin when `devBridge: true`. The plugin injects a virtual module (`/@dev-bridge-entry.js`) that starts DevBridge **before** importing your app entry point, ensuring the `MemoryChannel` is ready when the SDK calls `ready()`.
864
1077
 
865
1078
  ---
866
1079
 
867
1080
  ## DevBridge
868
1081
 
869
- `DevBridge` simulates a casino host for local development. It intercepts `postMessage` calls from the SDK and responds with mock data.
1082
+ `DevBridge` simulates a casino host for local development. It uses the SDK's `Bridge` class in `devMode`, communicating with `CasinoGameSDK` through a shared in-memory `MemoryChannel` — no `postMessage` or iframe required.
1083
+
1084
+ > **Requires `@energy8platform/game-sdk` >= 2.6.0**
870
1085
 
871
1086
  ```typescript
872
1087
  import { DevBridge } from '@energy8platform/game-engine/debug';
@@ -878,7 +1093,10 @@ const bridge = new DevBridge({
878
1093
  networkDelay: 200,
879
1094
  debug: true,
880
1095
  gameConfig: {
1096
+ id: 'my-slot',
1097
+ type: 'slot',
881
1098
  viewport: { width: 1920, height: 1080 },
1099
+ betLevels: [0.1, 0.2, 0.5, 1, 2, 5, 10],
882
1100
  },
883
1101
  onPlay: ({ action, bet, roundId }) => {
884
1102
  // Return custom play result
@@ -887,7 +1105,7 @@ const bridge = new DevBridge({
887
1105
  },
888
1106
  });
889
1107
 
890
- bridge.start();
1108
+ bridge.start(); // Creates SDK Bridge({ devMode: true }) + registers handlers
891
1109
 
892
1110
  // Update balance programmatically
893
1111
  bridge.setBalance(5000);
@@ -896,6 +1114,8 @@ bridge.setBalance(5000);
896
1114
  bridge.destroy();
897
1115
  ```
898
1116
 
1117
+ When using the Vite plugin with `devBridge: true`, the SDK is automatically configured with `devMode: true` so both sides use the same `MemoryChannel`.
1118
+
899
1119
  ### Handled Messages
900
1120
 
901
1121
  | Message | Description |
@@ -922,7 +1142,7 @@ fps.toggle();
922
1142
  fps.hide();
923
1143
  ```
924
1144
 
925
- When `debug: true` is set in `GameApplicationConfig`, the FPS overlay is enabled automatically.
1145
+ When `debug: true` is set in `GameApplicationConfig`, the FPS overlay is created and shown automatically — no manual setup needed.
926
1146
 
927
1147
  The overlay displays:
928
1148
  - Average FPS
@@ -944,6 +1164,7 @@ class GameApplication extends EventEmitter<GameEngineEvents> {
944
1164
  scenes: SceneManager;
945
1165
  assets: AssetManager;
946
1166
  audio: AudioManager;
1167
+ input: InputManager;
947
1168
  viewport: ViewportManager;
948
1169
  sdk: CasinoGameSDK | null;
949
1170
  initData: InitData | null;
@@ -1078,9 +1299,10 @@ class Tween {
1078
1299
  static to(target: any, props: Record<string, number>, duration: number, easing?: EasingFunction, onUpdate?: (p: number) => void): Promise<void>;
1079
1300
  static from(target: any, props: Record<string, number>, duration: number, easing?, onUpdate?): Promise<void>;
1080
1301
  static fromTo(target: any, fromProps: Record<string, number>, toProps: Record<string, number>, duration: number, easing?, onUpdate?): Promise<void>;
1081
- static delay(ms: number): Promise<void>;
1302
+ static delay(ms: number): Promise<void>; // Uses PixiJS Ticker
1082
1303
  static killTweensOf(target: any): void;
1083
1304
  static killAll(): void;
1305
+ static reset(): void; // Kill all + remove ticker listener
1084
1306
  }
1085
1307
  ```
1086
1308
 
@@ -1111,10 +1333,56 @@ class InputManager extends EventEmitter<InputEvents> {
1111
1333
  lock(): void;
1112
1334
  unlock(): void;
1113
1335
  isKeyDown(key: string): boolean;
1336
+ setViewportTransform(scale: number, offsetX: number, offsetY: number): void;
1337
+ getWorldPosition(canvasX: number, canvasY: number): { x: number; y: number };
1114
1338
  destroy(): void;
1115
1339
  }
1116
1340
  ```
1117
1341
 
1342
+ ### Layout
1343
+
1344
+ ```typescript
1345
+ class Layout extends Container {
1346
+ get items(): readonly Container[];
1347
+
1348
+ constructor(config?: LayoutConfig);
1349
+ addItem(child: Container): this;
1350
+ removeItem(child: Container): this;
1351
+ clearItems(): this;
1352
+ updateViewport(width: number, height: number): void;
1353
+ layout(): void;
1354
+ }
1355
+ ```
1356
+
1357
+ ### ScrollContainer
1358
+
1359
+ ```typescript
1360
+ class ScrollContainer extends Container {
1361
+ get content(): Container | null;
1362
+ get scrollPosition(): { x: number; y: number };
1363
+
1364
+ constructor(config: ScrollContainerConfig);
1365
+ setContent(content: Container): void;
1366
+ scrollTo(x: number, y: number, animate?: boolean): void;
1367
+ scrollToItem(index: number): void;
1368
+ resize(width: number, height: number): void;
1369
+ destroy(): void;
1370
+ }
1371
+ ```
1372
+
1373
+ ### SpriteAnimation
1374
+
1375
+ ```typescript
1376
+ class SpriteAnimation {
1377
+ static create(textures: Texture[], config?: SpriteAnimationConfig): AnimatedSprite;
1378
+ static fromSpritesheet(sheet: Spritesheet, prefix: string, config?: SpriteAnimationConfig): AnimatedSprite;
1379
+ static fromRange(sheet: Spritesheet, pattern: string, start: number, end: number, config?: SpriteAnimationConfig): AnimatedSprite;
1380
+ static fromAliases(aliases: string[], config?: SpriteAnimationConfig): AnimatedSprite;
1381
+ static playOnce(textures: Texture[], config?: SpriteAnimationConfig): { sprite: AnimatedSprite; finished: Promise<void> };
1382
+ static getTexturesByPrefix(sheet: Spritesheet, prefix: string): Texture[];
1383
+ }
1384
+ ```
1385
+
1118
1386
  ### EventEmitter
1119
1387
 
1120
1388
  ```typescript
@@ -1122,7 +1390,8 @@ class EventEmitter<TEvents extends {}> {
1122
1390
  on<K>(event: K, handler: (data: TEvents[K]) => void): this;
1123
1391
  once<K>(event: K, handler: (data: TEvents[K]) => void): this;
1124
1392
  off<K>(event: K, handler: (data: TEvents[K]) => void): this;
1125
- emit<K>(event: K, data: TEvents[K]): void;
1393
+ // Void events can be emitted without a data argument
1394
+ emit<K>(event: K, ...args): void;
1126
1395
  removeAllListeners(event?: keyof TEvents): this;
1127
1396
  }
1128
1397
  ```