@energy8platform/game-engine 0.22.0 → 0.24.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 (45) hide show
  1. package/dist/core.cjs.js +2 -2
  2. package/dist/core.cjs.js.map +1 -1
  3. package/dist/core.d.ts +3 -3
  4. package/dist/core.esm.js +2 -2
  5. package/dist/core.esm.js.map +1 -1
  6. package/dist/host.cjs.js +35 -10
  7. package/dist/host.cjs.js.map +1 -1
  8. package/dist/host.d.ts +22 -9
  9. package/dist/host.esm.js +24 -11
  10. package/dist/host.esm.js.map +1 -1
  11. package/dist/index.cjs.js +2 -2
  12. package/dist/index.cjs.js.map +1 -1
  13. package/dist/index.d.ts +3 -3
  14. package/dist/index.esm.js +2 -2
  15. package/dist/index.esm.js.map +1 -1
  16. package/dist/shell.cjs.js +4 -4
  17. package/dist/shell.d.ts +1 -1
  18. package/dist/shell.esm.js +1 -1
  19. package/dist/slot.cjs.js +226 -75
  20. package/dist/slot.cjs.js.map +1 -1
  21. package/dist/slot.d.ts +137 -19
  22. package/dist/slot.esm.js +224 -76
  23. package/dist/slot.esm.js.map +1 -1
  24. package/package.json +4 -4
  25. package/src/core/GameApplication.ts +3 -3
  26. package/src/host/createSlotGame.ts +6 -3
  27. package/src/host/index.ts +11 -1
  28. package/src/host/shellConfig.ts +22 -12
  29. package/src/host/types.ts +13 -2
  30. package/src/shell/index.ts +4 -3
  31. package/src/slot/cascade/TumbleController.ts +6 -3
  32. package/src/slot/config/ReelSystemConfig.ts +19 -0
  33. package/src/slot/features/extra.ts +1 -2
  34. package/src/slot/features/symbols.ts +10 -10
  35. package/src/slot/features/types.ts +13 -5
  36. package/src/slot/features/wilds.ts +1 -1
  37. package/src/slot/grid/AnimatedSymbol.ts +20 -11
  38. package/src/slot/grid/ReelGrid.ts +80 -41
  39. package/src/slot/grid/SymbolCell.ts +14 -7
  40. package/src/slot/grid/SymbolView.ts +2 -2
  41. package/src/slot/grid/geometry.ts +155 -0
  42. package/src/slot/index.ts +3 -0
  43. package/src/slot/motion/SpinEngine.ts +1 -1
  44. package/src/slot/system/ReelSystem.ts +10 -0
  45. package/src/types.ts +1 -1
package/dist/host.d.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  import { Container, ApplicationOptions, Application, Texture } from 'pixi.js';
2
2
  import { GameModel } from '@energy8platform/platform-core/game-spec';
3
3
  import { LoadingScreenConfig, AssetManifest, PlatformSession } from '@energy8platform/platform-core';
4
- import { CurrencyConfig, GameInfoContent, BonusOption, ShellFeatures, ShellMode, PixiShellConfig, PixiGameShell } from '@energy8platform/pixi-shell';
5
- import * as _energy8platform_platform_core_shell from '@energy8platform/platform-core/shell';
6
- import { Lang } from '@energy8platform/platform-core/shell';
7
- export { socialize } from '@energy8platform/platform-core/shell';
4
+ import { CurrencyConfig, GameInfoContent, BonusOption, ShellFeatures, ShellMode, PixiShellConfig, Shell } from '@energy8platform/shell/pixi';
5
+ export { OverlayHandle, OverlayRequest, PixiRenderer, PixiShellConfig, ResolvedShellConfig, SafeArea, Shell, ShellActions, ShellHost, ShellLayoutMode, ShellRenderer, ShellSurface, ShellTokens, createPixiShell, createShell } from '@energy8platform/shell/pixi';
6
+ import * as _energy8platform_shell_html from '@energy8platform/shell/html';
8
7
  import { BookAdapter, AdapterModule, StakeBridge } from '@energy8platform/stake-bridge';
9
8
  import { CasinoGameSDK, InitData, GameConfigData, SessionData } from '@energy8platform/game-sdk';
9
+ import { Lang } from '@energy8platform/shell';
10
+ export { socialize } from '@energy8platform/shell';
10
11
  import { SlotSpinResultBase, SlotResultNormalizer } from '@energy8platform/platform-core/slot-result';
11
12
 
12
13
  declare enum ScaleMode {
@@ -64,7 +65,7 @@ interface GameApplicationConfig {
64
65
  /** Enable debug overlay (FPS, draw calls) */
65
66
  debug?: boolean;
66
67
  /** When set, GameApplication mounts the branded game shell after the SDK handshake. */
67
- shell?: _energy8platform_platform_core_shell.ShellConfig | false;
68
+ shell?: _energy8platform_shell_html.ShellConfig | false;
68
69
  }
69
70
  interface SceneConstructor {
70
71
  new (): IScene;
@@ -705,7 +706,7 @@ declare class GameApplication extends EventEmitter<GameEngineEvents> {
705
706
  /** Platform session (SDK + optional DevBridge). null until start() runs. */
706
707
  platformSession: PlatformSession | null;
707
708
  /** Branded game shell (only when config.shell is set). */
708
- shell?: _energy8platform_platform_core_shell.GameShell;
709
+ shell?: _energy8platform_shell_html.GameShell;
709
710
  /** Configuration */
710
711
  readonly config: GameApplicationConfig;
711
712
  private _running;
@@ -814,7 +815,9 @@ interface JurisdictionRestrictions {
814
815
  declare function stakeForAction(model: GameModel, action: string, bet: number): number;
815
816
  /** Pure: assemble the shell config (sans mount target) from the model + runtime context
816
817
  * (currency/balance/language/mode). The host adds `app` at the call site. */
817
- declare function buildShellConfig(opts: SlotShellOptions, model: GameModel, runtime: ShellRuntime): Omit<PixiShellConfig, 'app' | 'parent'>;
818
+ declare function buildShellConfig(opts: SlotShellOptions, model: GameModel, runtime: ShellRuntime): Omit<PixiShellConfig, 'app' | 'parent' | 'gameInfo'> & {
819
+ gameInfo: GameInfoContent;
820
+ };
818
821
 
819
822
  interface StakeIntegration {
820
823
  /** The game's BookAdapter (or its module). modeMap + gameId come from the model. */
@@ -865,15 +868,25 @@ interface CreateSlotGameOptions<T extends SlotSpinResultBase = SlotSpinResultBas
865
868
  dev?: boolean;
866
869
  stake?: StakeIntegration;
867
870
  shell?: SlotShellOptions;
871
+ /** Override how the control-bar shell is built. The host resolves the full shell config (theme,
872
+ * features, gameInfo, currency, balance) and the Pixi mount (`app`/`parent`) and hands it to this
873
+ * factory; return any `Shell` — e.g. `createShell({ renderer: new MyRenderer(...), ...config })`
874
+ * to plug a custom renderer while the shell core still drives bet/balance/overlays. A custom
875
+ * renderer can ignore `app`/`parent` and mount elsewhere (a DOM overlay, another canvas).
876
+ * Default: the built-in Pixi shell (`createPixiShell`). */
877
+ shellFactory?: ShellFactory;
868
878
  /** Double-tap on the play area to skip the current spin animation. Default `true`. Set `false`
869
879
  * to disable the gesture (e.g. games where a tap means something else). */
870
880
  skipGesture?: boolean;
871
881
  onFatalError?: (message: string) => void;
872
882
  }
883
+ /** Builds the shell the host drives. Receives the fully-resolved Pixi shell config (a custom
884
+ * renderer may ignore the `app`/`parent` mount fields). Must return a `Shell`. */
885
+ type ShellFactory = (config: PixiShellConfig) => Shell;
873
886
  interface SlotGameHandle {
874
887
  game: GameApplication;
875
888
  stakeBridge: StakeBridge | null;
876
- shell: PixiGameShell | null;
889
+ shell: Shell | null;
877
890
  }
878
891
 
879
892
  /**
@@ -1001,4 +1014,4 @@ interface SlotSceneController<T extends SlotSpinResultBase = SlotSpinResultBase>
1001
1014
  }
1002
1015
 
1003
1016
  export { buildShellConfig, createSlotGame, resolveReplayBonusId, resolveStartScene, stakeForAction };
1004
- export type { AutoplaySceneState, CreateSlotGameOptions, OverlayShowOptions, RenderContext, SceneApi, SceneAudio, SceneNavData, SceneOverlay, SceneRegistration, SceneShell, SlotGameHandle, SlotSceneController, SlotShellOptions, StakeIntegration };
1017
+ export type { AutoplaySceneState, CreateSlotGameOptions, OverlayShowOptions, RenderContext, SceneApi, SceneAudio, SceneNavData, SceneOverlay, SceneRegistration, SceneShell, ShellFactory, SlotGameHandle, SlotSceneController, SlotShellOptions, StakeIntegration };
package/dist/host.esm.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import { Ticker, Assets, Container, Text, Application, Graphics, TextureSource } from 'pixi.js';
2
2
  import { createPlatformSession } from '@energy8platform/platform-core';
3
3
  import { waitCSSPreloaderTap, removeCSSPreloader, setCSSPreloaderProgress, createCSSPreloader } from '@energy8platform/platform-core/loading';
4
- import { socialize, createI18n } from '@energy8platform/platform-core/shell';
5
- export { socialize } from '@energy8platform/platform-core/shell';
4
+ export { PixiRenderer, createPixiShell, createShell } from '@energy8platform/shell/pixi';
5
+ import { socialize, createI18n } from '@energy8platform/shell';
6
+ export { socialize } from '@energy8platform/shell';
6
7
 
7
8
  // ─── Scale Modes ───────────────────────────────────────────
8
9
  var ScaleMode;
@@ -1886,7 +1887,7 @@ class GameApplication extends EventEmitter {
1886
1887
  await this.initSDK();
1887
1888
  // 4b. Mount the branded game shell after the SDK handshake (optional)
1888
1889
  if (this.config.shell) {
1889
- const { createGameShell } = await import('@energy8platform/platform-core/shell');
1890
+ const { createGameShell } = await import('@energy8platform/shell/html');
1890
1891
  this.shell = createGameShell(this.config.shell);
1891
1892
  }
1892
1893
  // 5. Merge design dimensions from SDK config
@@ -1921,7 +1922,7 @@ class GameApplication extends EventEmitter {
1921
1922
  this._destroyed = true;
1922
1923
  this._running = false;
1923
1924
  if (this.shell) {
1924
- const { removeGameShell } = await import('@energy8platform/platform-core/shell');
1925
+ const { removeGameShell } = await import('@energy8platform/shell/html');
1925
1926
  await removeGameShell();
1926
1927
  this.shell = undefined;
1927
1928
  }
@@ -2368,7 +2369,7 @@ async function createSlotGame(opts) {
2368
2369
  ack: (raw) => game.platformSession.playAck(raw),
2369
2370
  });
2370
2371
  if (opts.shell) {
2371
- const { createPixiShell } = await import('@energy8platform/pixi-shell');
2372
+ const { createPixiShell } = await import('@energy8platform/shell/pixi');
2372
2373
  const { buildShellConfig } = await Promise.resolve().then(function () { return shellConfig; });
2373
2374
  const { resolveReplayBonusId } = await Promise.resolve().then(function () { return replay; });
2374
2375
  const ps = game.platformSession;
@@ -2418,7 +2419,10 @@ async function createSlotGame(opts) {
2418
2419
  // pixi-shell mounts its root onto the engine's unscaled, screen-space UI layer (above the
2419
2420
  // scaled world/scene root) so the control bar fills the real screen, not the letterboxed game.
2420
2421
  // The host adds the mount target (`app`) + parent; buildShellConfig produces everything else.
2421
- shell = createPixiShell({ ...buildShellConfig(opts.shell, opts.model, runtime), app: game.app, parent: game.uiLayer });
2422
+ const pixiShellCfg = { ...buildShellConfig(opts.shell, opts.model, runtime), app: game.app, parent: game.uiLayer };
2423
+ // The game may swap in its own shell (a custom renderer over the same core) via shellFactory;
2424
+ // default is the built-in Pixi shell. The host drives whichever it gets through the Shell contract.
2425
+ shell = (opts.shellFactory ?? createPixiShell)(pixiShellCfg);
2422
2426
  // Scope the bar to the slot scene: show only when a SlotSceneController scene is current
2423
2427
  // (hidden over the intro / non-slot scenes). Applies in BOTH base and replay modes.
2424
2428
  shell.setVisible(!!gameScene());
@@ -2770,8 +2774,9 @@ async function createSlotGame(opts) {
2770
2774
  }
2771
2775
 
2772
2776
  // packages/game-engine/src/host/shellConfig.ts
2773
- // `socialize` / `createI18n` are runtime helpers sourced from platform-core/shell;
2774
- // pixi-shell re-exports only types so we import directly from platform-core.
2777
+ // `socialize` / `createI18n` / `Lang` are renderer-agnostic helpers from the shell core
2778
+ // (@energy8platform/shell). Renderer-agnostic types (GameInfoSection/Content, PaytableRow,
2779
+ // GameMode) and the pixi-specific surface types come from the @energy8platform/shell/pixi entry.
2775
2780
  /**
2776
2781
  * Apply jurisdiction restrictions over the resolved shell features, in place. A restriction ALWAYS
2777
2782
  * wins over the author's intent (a forbidden control must stay off even if the game enabled it).
@@ -2971,13 +2976,19 @@ function sectionKey(s) {
2971
2976
  function mergeGameInfo(derived, override) {
2972
2977
  if (!override)
2973
2978
  return derived;
2979
+ // `GameInfoContent.sections` is typed with the core GameInfoSection (node?: unknown) because
2980
+ // shell/pixi re-exports GameInfoContent unchanged from core. Host-built sections never set `node`,
2981
+ // so they are structurally compatible with pixi's GameInfoSection (node?: Container). Cast once
2982
+ // here so the rest of the function works against the pixi-widened type.
2983
+ const derivedSections = (derived.sections ?? []);
2984
+ const overrideSections = (override.sections ?? []);
2974
2985
  const authorByKey = new Map();
2975
- for (const s of override.sections ?? [])
2986
+ for (const s of overrideSections)
2976
2987
  authorByKey.set(sectionKey(s), s);
2977
2988
  const out = [];
2978
2989
  const used = new Set();
2979
2990
  // Keep derived order; swap in the author's version where identities collide.
2980
- for (const s of derived.sections ?? []) {
2991
+ for (const s of derivedSections) {
2981
2992
  const k = sectionKey(s);
2982
2993
  const replacement = authorByKey.get(k);
2983
2994
  if (replacement) {
@@ -2988,7 +2999,7 @@ function mergeGameInfo(derived, override) {
2988
2999
  out.push(s);
2989
3000
  }
2990
3001
  // Append author sections whose identity wasn't in the derived set, in author order.
2991
- for (const s of override.sections ?? []) {
3002
+ for (const s of overrideSections) {
2992
3003
  const k = sectionKey(s);
2993
3004
  if (!used.has(k)) {
2994
3005
  out.push(s);
@@ -3053,6 +3064,8 @@ function buildShellConfig(opts, model, runtime) {
3053
3064
  const authored = typeof opts.gameInfo === 'function' ? opts.gameInfo(t) : opts.gameInfo;
3054
3065
  // Pass t() through to spec-derived sections so symbol names, mode titles, and descriptions are
3055
3066
  // pre-translated before they reach the shell renderer.
3067
+ // Cast to { sections?: GameInfoSection[] } (pixi-widened) so downstream map/filter calls work
3068
+ // against the pixi section union. Host-derived sections never set `node`, so the widening is safe.
3056
3069
  let gameInfo = mergeGameInfo(defaultGameInfo(model, runtime, t), authored);
3057
3070
  // The DISCLAIMER is required legal copy and must be shown VERBATIM — never socialized (its
3058
3071
  // wording is mandated, and word-swaps like "bet → play" would corrupt the legal text).