@energy8platform/game-engine 0.10.10 → 0.11.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 (49) hide show
  1. package/README.md +185 -74
  2. package/dist/index.cjs.js +1280 -296
  3. package/dist/index.cjs.js.map +1 -1
  4. package/dist/index.d.ts +362 -46
  5. package/dist/index.esm.js +1281 -298
  6. package/dist/index.esm.js.map +1 -1
  7. package/dist/lua.cjs.js +16 -21
  8. package/dist/lua.cjs.js.map +1 -1
  9. package/dist/lua.d.ts +0 -2
  10. package/dist/lua.esm.js +16 -21
  11. package/dist/lua.esm.js.map +1 -1
  12. package/dist/react.cjs.js +2372 -11
  13. package/dist/react.cjs.js.map +1 -1
  14. package/dist/react.d.ts +17 -6
  15. package/dist/react.esm.js +2372 -12
  16. package/dist/react.esm.js.map +1 -1
  17. package/dist/ui.cjs.js +1553 -632
  18. package/dist/ui.cjs.js.map +1 -1
  19. package/dist/ui.d.ts +374 -46
  20. package/dist/ui.esm.js +1553 -634
  21. package/dist/ui.esm.js.map +1 -1
  22. package/dist/vite.cjs.js +1 -11
  23. package/dist/vite.cjs.js.map +1 -1
  24. package/dist/vite.d.ts +1 -1
  25. package/dist/vite.esm.js +1 -11
  26. package/dist/vite.esm.js.map +1 -1
  27. package/package.json +3 -18
  28. package/src/index.ts +3 -3
  29. package/src/lua/LuaEngine.ts +8 -18
  30. package/src/lua/SimulationRunner.ts +7 -3
  31. package/src/react/applyProps.ts +86 -0
  32. package/src/react/extendAll.ts +27 -6
  33. package/src/react/index.ts +1 -1
  34. package/src/react/jsx.d.ts +222 -0
  35. package/src/react/reconciler.ts +22 -5
  36. package/src/ui/BalanceDisplay.ts +31 -38
  37. package/src/ui/Button.ts +217 -53
  38. package/src/ui/FlexContainer.ts +479 -0
  39. package/src/ui/Label.ts +13 -0
  40. package/src/ui/Layout.ts +86 -87
  41. package/src/ui/Modal.ts +11 -1
  42. package/src/ui/Panel.ts +108 -36
  43. package/src/ui/ProgressBar.ts +85 -31
  44. package/src/ui/ScrollContainer.ts +397 -45
  45. package/src/ui/Toast.ts +47 -17
  46. package/src/ui/WinDisplay.ts +51 -39
  47. package/src/ui/index.ts +5 -11
  48. package/src/ui/view.ts +28 -0
  49. package/src/vite/index.ts +1 -11
package/README.md CHANGED
@@ -35,7 +35,6 @@ A casino game engine built on [PixiJS v8](https://pixijs.com/) and [@energy8plat
35
35
  npm install pixi.js @energy8platform/game-sdk @energy8platform/game-engine
36
36
 
37
37
  # Optional peer dependencies
38
- npm install @pixi/ui @pixi/layout yoga-layout # UI components (Layout, Panel, ScrollContainer)
39
38
  npm install @pixi/sound # Audio
40
39
  npm install @esotericsoftware/spine-pixi-v8 # Spine animations
41
40
  npm install react react-dom react-reconciler # React integration
@@ -88,9 +87,6 @@ bootstrap();
88
87
  | --- | --- | --- |
89
88
  | `pixi.js` | `^8.16.0` | Yes |
90
89
  | `@energy8platform/game-sdk` | `^2.7.0` | Yes |
91
- | `@pixi/ui` | `^2.3.0` | Optional — Button, ScrollContainer, ProgressBar |
92
- | `@pixi/layout` | `^3.2.0` | Optional — Layout, Panel (Yoga flexbox) |
93
- | `yoga-layout` | `^3.0.0` | Optional — peer dep of `@pixi/layout` |
94
90
  | `@pixi/sound` | `^6.0.0` | Optional — audio |
95
91
  | `@esotericsoftware/spine-pixi-v8` | `~4.2.0` | Optional — Spine animations |
96
92
  | `react`, `react-dom` | `>=18.0.0` | Optional — ReactScene |
@@ -104,10 +100,10 @@ import { GameApplication } from '@energy8platform/game-engine'; // f
104
100
  import { Scene, SceneManager } from '@energy8platform/game-engine/core';
105
101
  import { AssetManager } from '@energy8platform/game-engine/assets';
106
102
  import { AudioManager } from '@energy8platform/game-engine/audio';
107
- import { Button, Label, Modal, Layout, ScrollContainer, Panel, Toast } from '@energy8platform/game-engine/ui';
103
+ import { FlexContainer, Button, Label, Panel, Modal, Layout, ScrollContainer, Toast, ProgressBar, BalanceDisplay, WinDisplay, resolveView } from '@energy8platform/game-engine/ui';
108
104
  import { Tween, Timeline, Easing, SpriteAnimation } from '@energy8platform/game-engine/animation';
109
105
  import { DevBridge, FPSOverlay } from '@energy8platform/game-engine/debug';
110
- import { ReactScene, createPixiRoot, useSDK, useViewport } from '@energy8platform/game-engine/react';
106
+ import { ReactScene, extendPixiElements, extendUIElements, useSDK, useViewport } from '@energy8platform/game-engine/react';
111
107
  import { defineGameConfig } from '@energy8platform/game-engine/vite';
112
108
  import { LuaEngine, ActionRouter } from '@energy8platform/game-engine/lua';
113
109
  ```
@@ -396,17 +392,45 @@ SpineHelper.setSkin(spine, 'warrior');
396
392
 
397
393
  ## UI Components
398
394
 
399
- > Requires `@pixi/ui` and `@pixi/layout` + `yoga-layout` as peer dependencies. Import from `@energy8platform/game-engine/ui`.
400
- >
401
- > For components not wrapped by the engine (`Slider`, `CheckBox`, `Input`, `Select`, etc.), import directly from `@pixi/ui`.
395
+ > Built-in UI system with zero external dependencies. No `@pixi/ui`, `@pixi/layout`, or `yoga-layout` required. Import from `@energy8platform/game-engine/ui`.
402
396
 
403
- ### Layout
397
+ ### Asset Skinning (`ViewInput`)
404
398
 
405
- Responsive flexbox container powered by `@pixi/layout`. Supports `horizontal`, `vertical`, `grid`, `wrap` modes with alignment, padding, gap, anchoring, and viewport breakpoints.
399
+ Every visual component supports custom artwork via the `ViewInput` type:
406
400
 
407
401
  ```typescript
408
- import { Layout } from '@energy8platform/game-engine/ui';
402
+ type ViewInput = string | Texture | Container;
403
+ ```
404
+
405
+ - `string` — texture name, resolved via `Sprite.from()` from the asset manager
406
+ - `Texture` — wrapped in a `Sprite`
407
+ - `Container` — used as-is (NineSliceSprite, AnimatedSprite, custom artwork, etc.)
408
+
409
+ If no custom view is provided, components fall back to Graphics-based rendering (colors, rounded rects). This means you can prototype with Graphics and later swap to production art with no API changes.
410
+
411
+ ### FlexContainer
409
412
 
413
+ Lightweight flexbox-like layout container. Children added via `addChild()` automatically participate in flex layout.
414
+
415
+ ```typescript
416
+ const toolbar = new FlexContainer({
417
+ direction: 'row',
418
+ justifyContent: 'space-between',
419
+ alignItems: 'center',
420
+ gap: 16,
421
+ padding: 12,
422
+ });
423
+ toolbar.addChild(button1); // auto flex layout
424
+ toolbar.addChild(button2);
425
+ toolbar.addFlexChild(spacer, { flexGrow: 1 }); // with flex config
426
+ toolbar.resize(800, 60);
427
+ ```
428
+
429
+ ### Layout
430
+
431
+ Higher-level layout with direction presets (`horizontal`/`vertical`/`grid`/`wrap`), viewport anchoring, and responsive breakpoints. Wraps FlexContainer.
432
+
433
+ ```typescript
410
434
  const toolbar = new Layout({
411
435
  direction: 'horizontal',
412
436
  gap: 20,
@@ -422,9 +446,57 @@ toolbar.updateViewport(width, height);
422
446
 
423
447
  **Anchors:** `top-left`, `top-center`, `top-right`, `center-left`, `center`, `center-right`, `bottom-left`, `bottom-center`, `bottom-right`
424
448
 
449
+ ### Button
450
+
451
+ Per-state views with Tween animations. Each state accepts a `ViewInput` for full asset control:
452
+
453
+ ```typescript
454
+ // Graphics-based (prototyping)
455
+ const btn = new Button({
456
+ width: 200, height: 60, borderRadius: 12,
457
+ colors: { default: 0xffd700, hover: 0xffe44d, pressed: 0xccac00, disabled: 0x666666 },
458
+ text: 'SPIN',
459
+ onPress: () => spin(),
460
+ });
461
+
462
+ // Asset-based (production)
463
+ const btn = new Button({
464
+ defaultView: 'btn-idle', // texture name
465
+ hoverView: 'btn-hover',
466
+ pressedView: 'btn-pressed',
467
+ disabledView: 'btn-disabled',
468
+ text: 'SPIN',
469
+ onPress: () => spin(),
470
+ });
471
+
472
+ // NineSlice button
473
+ const btn = new Button({
474
+ defaultView: new NineSliceSprite({ texture: Texture.from('btn-9s'), leftWidth: 20, topHeight: 20, rightWidth: 20, bottomHeight: 20 }),
475
+ text: 'BET MAX',
476
+ });
477
+ ```
478
+
479
+ ### ProgressBar
480
+
481
+ Animated fill bar with optional custom track/fill views:
482
+
483
+ ```typescript
484
+ // Graphics-based
485
+ const bar = new ProgressBar({ width: 400, height: 20, fillColor: 0x00ff00, animated: true });
486
+ bar.progress = 0.75;
487
+ bar.update(dt);
488
+
489
+ // Asset-based
490
+ const bar = new ProgressBar({
491
+ width: 400, height: 20,
492
+ trackView: 'bar-track', // texture name
493
+ fillView: new NineSliceSprite({ ... }), // or any Container
494
+ });
495
+ ```
496
+
425
497
  ### ScrollContainer
426
498
 
427
- Touch/drag scrollable container with mouse wheel, inertia, and dynamic rendering. Extends `@pixi/ui` ScrollBox.
499
+ Touch/drag scrolling with mouse wheel, inertia, and optional visual scrollbar:
428
500
 
429
501
  ```typescript
430
502
  const scroll = new ScrollContainer({
@@ -432,66 +504,60 @@ const scroll = new ScrollContainer({
432
504
  direction: 'vertical',
433
505
  elementsMargin: 8,
434
506
  backgroundColor: 0x1a1a2e,
507
+ scrollbar: true, // show scrollbar indicator
508
+ scrollbarColor: 0xaaaaaa, // or provide custom view:
509
+ // thumbView: 'scrollbar-thumb', // texture name, Texture, or Container
435
510
  });
436
511
  for (let i = 0; i < 50; i++) scroll.addItem(createRow(i));
437
512
  ```
438
513
 
439
- ### Button
514
+ ### Panel
440
515
 
441
- Extends `@pixi/ui` FancyButton. Supports Graphics-based and texture-based states, press animation, and text.
516
+ Background panel with FlexContainer content layout. Supports Graphics or 9-slice backgrounds:
442
517
 
443
518
  ```typescript
444
- const spinBtn = new Button({
445
- width: 200, height: 60,
446
- borderRadius: 12,
447
- colors: { default: 0xffd700, hover: 0xffe44d, pressed: 0xccac00, disabled: 0x666666 },
448
- pressScale: 0.95,
449
- text: 'SPIN',
519
+ // Graphics background
520
+ const panel = new Panel({ width: 600, height: 400, backgroundColor: 0x1a1a2e, borderRadius: 16, padding: 16 });
521
+ panel.addContent(myLabel);
522
+
523
+ // 9-slice texture background
524
+ const panel = new Panel({
525
+ nineSliceTexture: 'panel-bg',
526
+ nineSliceBorders: [20, 20, 20, 20],
527
+ width: 600, height: 400, padding: 16,
450
528
  });
451
- spinBtn.onPress.connect(() => console.log('Spin!'));
452
- spinBtn.disable();
453
- spinBtn.enable();
454
529
  ```
455
530
 
456
531
  ### Other UI Components
457
532
 
458
- **Label** — styled text wrapper:
533
+ **Label** — styled text with auto-fit and currency formatting:
459
534
  ```typescript
460
- new Label({ text: 'TOTAL WIN', style: { fontSize: 36, fill: 0xffffff } });
535
+ const label = new Label({ text: 'TOTAL WIN', style: { fontSize: 36, fill: 0xffffff }, maxWidth: 300, autoFit: true });
536
+ label.setCurrency(1500, 'USD'); // → "$1,500.00"
461
537
  ```
462
538
 
463
- **BalanceDisplay** — animated currency display:
539
+ **BalanceDisplay** — animated currency countup/countdown:
464
540
  ```typescript
465
- const balance = new BalanceDisplay({ currency: 'USD', animated: true });
466
- balance.setValue(9500);
541
+ const balance = new BalanceDisplay({ prefix: 'BALANCE', currency: 'USD', animated: true });
542
+ balance.setValue(9500); // smooth countup
467
543
  ```
468
544
 
469
- **WinDisplay** — countup win animation:
545
+ **WinDisplay** — dramatic countup with scale pop:
470
546
  ```typescript
471
- await winDisplay.showWin(5000); // countup over 2 seconds
472
- ```
473
-
474
- **ProgressBar** — animated fill bar (wraps `@pixi/ui`):
475
- ```typescript
476
- const bar = new ProgressBar({ width: 400, height: 20, fillColor: 0x00ff00, animated: true });
477
- bar.progress = 0.75;
478
- bar.update(dt); // call each frame for animation
479
- ```
480
-
481
- **Panel** — background panel with flex layout (Graphics or 9-slice):
482
- ```typescript
483
- const panel = new Panel({ width: 600, height: 400, backgroundColor: 0x1a1a2e, borderRadius: 16, padding: 16 });
484
- panel.content.addChild(myText); // children participate in flexbox
547
+ await winDisplay.showWin(5000);
548
+ winDisplay.hide();
485
549
  ```
486
550
 
487
- **Modal** — full-screen overlay dialog:
551
+ **Modal** — full-screen overlay with enter/exit animations:
488
552
  ```typescript
489
553
  const modal = new Modal({ overlayAlpha: 0.7, closeOnOverlay: true, animationDuration: 300 });
554
+ modal.content.addChild(settingsPanel);
490
555
  await modal.show(viewWidth, viewHeight);
491
556
  ```
492
557
 
493
- **Toast** — notification messages (`info`, `success`, `warning`, `error`):
558
+ **Toast** — transient notifications with optional custom background:
494
559
  ```typescript
560
+ const toast = new Toast({ duration: 3000, backgroundView: 'toast-bg' });
495
561
  await toast.show('Free spins activated!', 'success', viewWidth, viewHeight);
496
562
  ```
497
563
 
@@ -533,7 +599,7 @@ export default defineGameConfig({
533
599
  });
534
600
  ```
535
601
 
536
- **What `defineGameConfig` provides:** ESNext build target (for yoga-layout WASM), asset inlining (<8KB), PixiJS chunk splitting, DevBridge auto-injection in dev mode, dependency deduplication (`pixi.js`, `@pixi/layout`, `react`, etc.), and pre-bundling optimization.
602
+ **What `defineGameConfig` provides:** ESNext build target, asset inlining (<8KB), PixiJS chunk splitting, DevBridge auto-injection in dev mode, dependency deduplication (`pixi.js`, `react`, etc.), and pre-bundling optimization.
537
603
 
538
604
  ---
539
605
 
@@ -686,42 +752,87 @@ bridge.destroy();
686
752
 
687
753
  ## React Integration
688
754
 
689
- Built-in React reconciler for PixiJS. No `@pixi/react` needed — renders React trees directly into PixiJS Containers.
755
+ Built-in React reconciler for PixiJS. No `@pixi/react` needed — renders React trees directly into PixiJS Containers. All engine UI components work declaratively in JSX with full TypeScript autocompletion.
690
756
 
691
- ```tsx
692
- import { ReactScene, extendPixiElements, useSDK, useViewport, useBalance } from '@energy8platform/game-engine/react';
757
+ ### Setup
758
+
759
+ ```typescript
760
+ import { extendPixiElements, extendUIElements } from '@energy8platform/game-engine/react';
761
+
762
+ extendPixiElements(); // Container, Sprite, Text, Graphics, etc.
763
+ extendUIElements(); // Button, Label, FlexContainer, Panel, etc.
764
+ ```
765
+
766
+ ### ReactScene
693
767
 
694
- extendPixiElements(); // register PixiJS elements for JSX (call once)
768
+ ```tsx
769
+ import { ReactScene, useEngine, useBalance } from '@energy8platform/game-engine/react';
770
+ import { useState } from 'react';
695
771
 
696
- export class GameScene extends ReactScene {
697
- render() { return <GameRoot />; }
772
+ export class SlotScene extends ReactScene {
773
+ render() { return <SlotUI />; }
698
774
  }
699
775
 
700
- function GameRoot() {
701
- const { width, height } = useViewport();
776
+ function SlotUI() {
777
+ const { screen } = useEngine();
702
778
  const balance = useBalance();
703
- const sdk = useSDK();
779
+ const [bet, setBet] = useState(1);
704
780
 
705
781
  return (
706
- <container>
707
- <text text={`Balance: $${balance.toFixed(2)}`} style={{ fontSize: 32, fill: 0xffffff }} />
708
- <text text="SPIN" style={{ fontSize: 48, fill: 0xffd700 }}
709
- eventMode="static" cursor="pointer"
710
- onClick={async () => {
711
- const result = await sdk?.play({ action: 'spin', bet: 1 });
712
- sdk?.playAck(result!);
713
- }}
714
- />
715
- </container>
782
+ <flexContainer direction="column" width={screen.width} height={screen.height} padding={20}>
783
+ {/* Top bar */}
784
+ <flexContainer direction="row" justifyContent="space-between" alignItems="center">
785
+ <balanceDisplay currency="USD" animated value={balance} />
786
+ <label text={`BET: $${bet.toFixed(2)}`} style-fontSize={18} style-fill={0xcccccc} />
787
+ </flexContainer>
788
+
789
+ {/* Controls */}
790
+ <flexContainer direction="row" justifyContent="center" gap={12} alignItems="center">
791
+ <button width={50} height={50} borderRadius={25} text="-"
792
+ colors-default={0x444444}
793
+ onPress={() => setBet(b => Math.max(0.2, b - 0.5))} />
794
+ <button width={140} height={60} borderRadius={30} text="SPIN"
795
+ colors-default={0x22aa44} colors-hover={0x33cc55}
796
+ onPress={() => { /* spin */ }} />
797
+ <button width={50} height={50} borderRadius={25} text="+"
798
+ colors-default={0x444444}
799
+ onPress={() => setBet(b => b + 0.5)} />
800
+ </flexContainer>
801
+ </flexContainer>
716
802
  );
717
803
  }
718
804
  ```
719
805
 
806
+ ### Declarative UI Components
807
+
808
+ All engine UI components are config-based: the reconciler passes JSX props as a config object to the constructor, and calls `updateConfig()` on prop changes. Children of `<flexContainer>`, `<panel>`, and `<scrollContainer>` automatically participate in their layout system.
809
+
810
+ ```tsx
811
+ {/* Asset-skinned button */}
812
+ <button defaultView="btn-idle" hoverView="btn-hover" text="SPIN" onPress={handler} />
813
+
814
+ {/* Panel with 9-slice background */}
815
+ <panel nineSliceTexture="panel-bg" nineSliceBorders={[20,20,20,20]} width={400} height={300}>
816
+ <label text="Settings" style-fontSize={24} />
817
+ </panel>
818
+
819
+ {/* Progress bar with custom track/fill */}
820
+ <progressBar trackView="bar-track" fillView="bar-fill" progress={0.75} width={300} height={20} />
821
+
822
+ {/* Scrollable list */}
823
+ <scrollContainer width={500} height={400} direction="vertical" scrollbar elementsMargin={8}>
824
+ <label text="Item 1" />
825
+ <label text="Item 2" />
826
+ </scrollContainer>
827
+ ```
828
+
829
+ **Dash-notation** for nested config: `colors-default={0xff0000}` → `{ colors: { default: 0xff0000 } }`, `style-fontSize={24}` → `{ style: { fontSize: 24 } }`.
830
+
720
831
  ### Hooks
721
832
 
722
833
  | Hook | Returns | Description |
723
834
  | --- | --- | --- |
724
- | `useEngine()` | `EngineContextValue` | Full engine context |
835
+ | `useEngine()` | `EngineContextValue` | Full engine context (app, sdk, audio, screen, etc.) |
725
836
  | `useSDK()` | `CasinoGameSDK \| null` | SDK instance |
726
837
  | `useAudio()` | `AudioManager` | Audio manager |
727
838
  | `useInput()` | `InputManager` | Input manager |
@@ -733,18 +844,18 @@ function GameRoot() {
733
844
  ### Element Registration
734
845
 
735
846
  ```typescript
736
- import { extendPixiElements, extendLayoutElements, extend } from '@energy8platform/game-engine/react';
847
+ import { extendPixiElements, extendUIElements, extendCustomElements } from '@energy8platform/game-engine/react';
737
848
 
738
- extendPixiElements(); // Container, Sprite, Text, Graphics, etc.
739
- extendLayoutElements(await import('@pixi/layout/components')); // @pixi/layout (if installed)
740
- extend({ Button, Panel }); // custom classes
849
+ extendPixiElements(); // Container, Sprite, Text, Graphics, AnimatedSprite, etc.
850
+ extendUIElements(); // Button, Label, FlexContainer, Panel, ProgressBar, etc.
851
+ extendCustomElements({ MyWidget }); // your own classes
741
852
  ```
742
853
 
743
- After registration, use as lowercase JSX: `<container>`, `<sprite>`, `<text>`, `<graphics>`, `<button>`.
854
+ After registration, use as lowercase JSX: `<container>`, `<sprite>`, `<text>`, `<graphics>`, `<button>`, `<flexContainer>`, `<panel>`, etc.
744
855
 
745
- **Props:** Regular props set directly (`alpha`, `visible`, `scale`). Nested via dash: `position-x`, `scale-y`. Events: `onClick` `onclick`, `onPointerDown` → `onpointerdown`.
856
+ **Props:** Regular props set directly (`alpha`, `visible`, `scale`). Nested via dash: `position-x`, `scale-y`, `colors-default`. Events: `onClick`, `onPointerDown`, `onPress` (Button).
746
857
 
747
- > React is entirely optional. Imperative (`Scene`) and React (`ReactScene`) scenes can coexist.
858
+ > React is entirely optional. Imperative (`Scene`) and React (`ReactScene`) scenes can coexist in the same game.
748
859
 
749
860
  ---
750
861