@energy8platform/game-engine 0.10.11 → 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 (48) 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 +8 -18
  8. package/dist/lua.cjs.js.map +1 -1
  9. package/dist/lua.d.ts +0 -2
  10. package/dist/lua.esm.js +8 -18
  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/react/applyProps.ts +86 -0
  31. package/src/react/extendAll.ts +27 -6
  32. package/src/react/index.ts +1 -1
  33. package/src/react/jsx.d.ts +222 -0
  34. package/src/react/reconciler.ts +22 -5
  35. package/src/ui/BalanceDisplay.ts +31 -38
  36. package/src/ui/Button.ts +217 -53
  37. package/src/ui/FlexContainer.ts +479 -0
  38. package/src/ui/Label.ts +13 -0
  39. package/src/ui/Layout.ts +86 -87
  40. package/src/ui/Modal.ts +11 -1
  41. package/src/ui/Panel.ts +108 -36
  42. package/src/ui/ProgressBar.ts +85 -31
  43. package/src/ui/ScrollContainer.ts +397 -45
  44. package/src/ui/Toast.ts +47 -17
  45. package/src/ui/WinDisplay.ts +51 -39
  46. package/src/ui/index.ts +5 -11
  47. package/src/ui/view.ts +28 -0
  48. package/src/vite/index.ts +1 -11
@@ -14,6 +14,10 @@ import {
14
14
  HTMLText,
15
15
  } from 'pixi.js';
16
16
  import { extend } from './catalogue';
17
+ import {
18
+ Button, Label, Panel, FlexContainer, ProgressBar,
19
+ ScrollContainer, Modal, Toast, BalanceDisplay, WinDisplay, Layout,
20
+ } from '../ui';
17
21
 
18
22
  /**
19
23
  * Register all standard PixiJS display objects for JSX use.
@@ -38,14 +42,31 @@ export function extendPixiElements(): void {
38
42
  }
39
43
 
40
44
  /**
41
- * Register @pixi/layout components for JSX use.
42
- * Pass the dynamically imported module:
45
+ * Register all engine UI components for JSX use.
46
+ * Call once at app startup before rendering React scenes that use UI components.
43
47
  *
48
+ * @example
44
49
  * ```ts
45
- * const layout = await import('@pixi/layout/components');
46
- * extendLayoutElements(layout);
50
+ * extendPixiElements();
51
+ * extendUIElements();
52
+ *
53
+ * // Now you can use:
54
+ * // <button text="SPIN" onPress={handler} />
55
+ * // <flexContainer direction="row" gap={16}>...</flexContainer>
56
+ * // <label text="Hello" style-fontSize={24} />
47
57
  * ```
48
58
  */
49
- export function extendLayoutElements(layoutModule: Record<string, any>): void {
50
- extend(layoutModule);
59
+ export function extendUIElements(): void {
60
+ extend({
61
+ Button, Label, Panel, FlexContainer, ProgressBar,
62
+ ScrollContainer, Modal, Toast, BalanceDisplay, WinDisplay, Layout,
63
+ });
64
+ }
65
+
66
+ /**
67
+ * Register additional custom components for JSX use.
68
+ * Pass an object mapping component names to their constructors.
69
+ */
70
+ export function extendCustomElements(components: Record<string, any>): void {
71
+ extend(components);
51
72
  }
@@ -4,7 +4,7 @@ export type { PixiRoot } from './createPixiRoot';
4
4
 
5
5
  // Catalogue
6
6
  export { extend } from './catalogue';
7
- export { extendPixiElements, extendLayoutElements } from './extendAll';
7
+ export { extendPixiElements, extendUIElements, extendCustomElements } from './extendAll';
8
8
 
9
9
  // Scene
10
10
  export { ReactScene } from './ReactScene';
@@ -0,0 +1,222 @@
1
+ /**
2
+ * JSX IntrinsicElements type declarations for the PixiJS React reconciler.
3
+ *
4
+ * Provides TypeScript autocompletion and type safety for:
5
+ * - Standard PixiJS elements: <container>, <sprite>, <text>, <graphics>
6
+ * - Engine UI components: <button>, <label>, <panel>, <flexContainer>, etc.
7
+ *
8
+ * Dash-notation is supported for nested config objects:
9
+ * <button colors-default={0xff0000} colors-hover={0x00ff00} />
10
+ */
11
+
12
+ import type { Container, Texture, TextStyle } from 'pixi.js';
13
+ import type { ViewInput } from '../ui/view';
14
+ import type { ButtonConfig, ButtonState } from '../ui/Button';
15
+ import type { LabelConfig } from '../ui/Label';
16
+ import type { PanelConfig } from '../ui/Panel';
17
+ import type { FlexContainerConfig } from '../ui/FlexContainer';
18
+ import type { ProgressBarConfig } from '../ui/ProgressBar';
19
+ import type { ScrollContainerConfig } from '../ui/ScrollContainer';
20
+ import type { ModalConfig } from '../ui/Modal';
21
+ import type { ToastConfig } from '../ui/Toast';
22
+ import type { BalanceDisplayConfig } from '../ui/BalanceDisplay';
23
+ import type { WinDisplayConfig } from '../ui/WinDisplay';
24
+ import type { LayoutConfig } from '../ui/Layout';
25
+
26
+ // ─── Event props ─────────────────────────────────────────
27
+
28
+ interface PixiEventProps {
29
+ onClick?: (e: any) => void;
30
+ onPointerDown?: (e: any) => void;
31
+ onPointerUp?: (e: any) => void;
32
+ onPointerMove?: (e: any) => void;
33
+ onPointerOver?: (e: any) => void;
34
+ onPointerOut?: (e: any) => void;
35
+ onPointerEnter?: (e: any) => void;
36
+ onPointerLeave?: (e: any) => void;
37
+ onPointerCancel?: (e: any) => void;
38
+ onPointerTap?: (e: any) => void;
39
+ onPointerUpOutside?: (e: any) => void;
40
+ onMouseDown?: (e: any) => void;
41
+ onMouseUp?: (e: any) => void;
42
+ onMouseMove?: (e: any) => void;
43
+ onMouseOver?: (e: any) => void;
44
+ onMouseOut?: (e: any) => void;
45
+ onWheel?: (e: any) => void;
46
+ onTap?: (e: any) => void;
47
+ onRightClick?: (e: any) => void;
48
+ }
49
+
50
+ // ─── Base container props (shared by all elements) ───────
51
+
52
+ interface BaseProps extends PixiEventProps {
53
+ key?: React.Key;
54
+ ref?: React.Ref<any>;
55
+ children?: React.ReactNode;
56
+
57
+ // Container props
58
+ x?: number;
59
+ y?: number;
60
+ width?: number;
61
+ height?: number;
62
+ alpha?: number;
63
+ visible?: boolean;
64
+ rotation?: number;
65
+ angle?: number;
66
+ zIndex?: number;
67
+ label?: string;
68
+ cursor?: string;
69
+ eventMode?: 'auto' | 'none' | 'passive' | 'static' | 'dynamic';
70
+
71
+ // Nested via dash-notation
72
+ 'scale-x'?: number;
73
+ 'scale-y'?: number;
74
+ 'pivot-x'?: number;
75
+ 'pivot-y'?: number;
76
+ 'position-x'?: number;
77
+ 'position-y'?: number;
78
+ 'anchor-x'?: number;
79
+ 'anchor-y'?: number;
80
+
81
+ // Allow scale as number (uniform)
82
+ scale?: number | { x: number; y: number };
83
+ }
84
+
85
+ // ─── PixiJS primitive elements ───────────────────────────
86
+
87
+ interface SpriteProps extends BaseProps {
88
+ texture?: string | Texture;
89
+ anchor?: number | { x: number; y: number };
90
+ tint?: number;
91
+ }
92
+
93
+ interface TextProps extends BaseProps {
94
+ text?: string;
95
+ style?: Partial<TextStyle>;
96
+ anchor?: number | { x: number; y: number };
97
+ // Dash-notation for style
98
+ 'style-fontSize'?: number;
99
+ 'style-fill'?: number | string;
100
+ 'style-fontFamily'?: string;
101
+ 'style-fontWeight'?: string;
102
+ 'style-letterSpacing'?: number;
103
+ }
104
+
105
+ interface GraphicsProps extends BaseProps {
106
+ /** Draw function: receives the Graphics instance, called on mount and updates */
107
+ draw?: (g: any) => void;
108
+ }
109
+
110
+ // ─── Engine UI component props ───────────────────────────
111
+
112
+ interface ButtonComponentProps extends BaseProps, Omit<ButtonConfig, 'colors' | 'textStyle'> {
113
+ // Full object form
114
+ colors?: Partial<Record<ButtonState, number>>;
115
+ textStyle?: Record<string, unknown>;
116
+ // Custom views (ViewInput: string texture name, Texture, or Container)
117
+ defaultView?: ViewInput;
118
+ hoverView?: ViewInput;
119
+ pressedView?: ViewInput;
120
+ disabledView?: ViewInput;
121
+ // Dash-notation for nested objects
122
+ 'colors-default'?: number;
123
+ 'colors-hover'?: number;
124
+ 'colors-pressed'?: number;
125
+ 'colors-disabled'?: number;
126
+ 'textStyle-fontSize'?: number;
127
+ 'textStyle-fill'?: number | string;
128
+ 'textStyle-fontFamily'?: string;
129
+ 'textStyle-fontWeight'?: string;
130
+ 'textStyle-fontStyle'?: string;
131
+ 'textStyle-letterSpacing'?: number;
132
+ // Component callbacks
133
+ onPress?: () => void;
134
+ }
135
+
136
+ interface LabelComponentProps extends BaseProps, Omit<LabelConfig, 'style'> {
137
+ style?: Partial<TextStyle>;
138
+ 'style-fontSize'?: number;
139
+ 'style-fill'?: number | string;
140
+ 'style-fontFamily'?: string;
141
+ 'style-fontWeight'?: string;
142
+ 'style-letterSpacing'?: number;
143
+ }
144
+
145
+ interface PanelComponentProps extends BaseProps, Omit<PanelConfig, 'layout'> {
146
+ layout?: Partial<FlexContainerConfig>;
147
+ }
148
+
149
+ interface FlexContainerComponentProps extends BaseProps, FlexContainerConfig {
150
+ padding?: number | [number, number, number, number];
151
+ }
152
+
153
+ interface ProgressBarComponentProps extends BaseProps, ProgressBarConfig {
154
+ progress?: number;
155
+ /** Custom track background (string texture name, Texture, or Container) */
156
+ trackView?: ViewInput;
157
+ /** Custom fill bar */
158
+ fillView?: ViewInput;
159
+ }
160
+
161
+ interface ScrollContainerComponentProps extends BaseProps, Omit<ScrollContainerConfig, 'width' | 'height'> {
162
+ width: number;
163
+ height: number;
164
+ /** Show scrollbar indicator */
165
+ scrollbar?: boolean;
166
+ /** Custom scrollbar thumb view */
167
+ thumbView?: ViewInput;
168
+ scrollbarWidth?: number;
169
+ scrollbarPadding?: number;
170
+ scrollbarColor?: number;
171
+ scrollbarAlpha?: number;
172
+ }
173
+
174
+ interface ModalComponentProps extends BaseProps, ModalConfig {
175
+ onClose?: () => void;
176
+ }
177
+
178
+ interface ToastComponentProps extends BaseProps, ToastConfig {
179
+ /** Custom background view */
180
+ backgroundView?: ViewInput;
181
+ }
182
+
183
+ interface BalanceDisplayComponentProps extends BaseProps, BalanceDisplayConfig {
184
+ /** Current balance value */
185
+ value?: number;
186
+ }
187
+
188
+ interface WinDisplayComponentProps extends BaseProps, WinDisplayConfig {}
189
+
190
+ interface LayoutComponentProps extends BaseProps, LayoutConfig {}
191
+
192
+ // ─── JSX IntrinsicElements ───────────────────────────────
193
+
194
+ declare global {
195
+ namespace JSX {
196
+ interface IntrinsicElements {
197
+ // PixiJS primitives
198
+ container: BaseProps;
199
+ sprite: SpriteProps;
200
+ text: TextProps;
201
+ graphics: GraphicsProps;
202
+ animatedSprite: BaseProps & { textures?: Texture[]; animationSpeed?: number; loop?: boolean; playing?: boolean };
203
+ nineSliceSprite: BaseProps & { texture?: string | Texture; leftWidth?: number; topHeight?: number; rightWidth?: number; bottomHeight?: number };
204
+ tilingSprite: BaseProps & { texture?: string | Texture; tilePosition?: { x: number; y: number } };
205
+
206
+ // Engine UI components
207
+ button: ButtonComponentProps;
208
+ label: LabelComponentProps;
209
+ panel: PanelComponentProps;
210
+ flexContainer: FlexContainerComponentProps;
211
+ progressBar: ProgressBarComponentProps;
212
+ scrollContainer: ScrollContainerComponentProps;
213
+ modal: ModalComponentProps;
214
+ toast: ToastComponentProps;
215
+ balanceDisplay: BalanceDisplayComponentProps;
216
+ winDisplay: WinDisplayComponentProps;
217
+ layout: LayoutComponentProps;
218
+ }
219
+ }
220
+ }
221
+
222
+ export {};
@@ -2,7 +2,7 @@ import Reconciler from 'react-reconciler';
2
2
  import { DefaultEventPriority } from 'react-reconciler/constants';
3
3
  import { Container } from 'pixi.js';
4
4
  import { catalogue } from './catalogue';
5
- import { applyProps, hasEventProps } from './applyProps';
5
+ import { applyProps, hasEventProps, extractConfig, diffConfig, applyEventProps } from './applyProps';
6
6
 
7
7
  function toPascalCase(str: string): string {
8
8
  return str.charAt(0).toUpperCase() + str.slice(1);
@@ -37,10 +37,19 @@ const hostConfig: Reconciler.HostConfig<
37
37
  `Call extend({ ${name} }) before rendering.`,
38
38
  );
39
39
  }
40
- const instance = new Ctor();
41
- applyProps(instance, props);
42
40
 
43
- // Enable interactivity if any event prop is present
41
+ let instance;
42
+ if (Ctor.prototype.__uiComponent) {
43
+ // Config-based UI component: pass props as constructor config
44
+ const config = extractConfig(props);
45
+ instance = new Ctor(config);
46
+ applyEventProps(instance, props);
47
+ } else {
48
+ // Standard PixiJS element
49
+ instance = new Ctor();
50
+ applyProps(instance, props);
51
+ }
52
+
44
53
  if (hasEventProps(props) && instance.eventMode === 'auto') {
45
54
  instance.eventMode = 'static';
46
55
  }
@@ -97,7 +106,15 @@ const hostConfig: Reconciler.HostConfig<
97
106
  },
98
107
 
99
108
  commitUpdate(instance, _updatePayload, _type, oldProps, newProps) {
100
- applyProps(instance, newProps, oldProps);
109
+ if (instance.__uiComponent && typeof instance.updateConfig === 'function') {
110
+ const changed = diffConfig(newProps, oldProps);
111
+ if (Object.keys(changed).length > 0) {
112
+ instance.updateConfig(changed);
113
+ }
114
+ applyEventProps(instance, newProps, oldProps);
115
+ } else {
116
+ applyProps(instance, newProps, oldProps);
117
+ }
101
118
 
102
119
  if (hasEventProps(newProps) && instance.eventMode === 'auto') {
103
120
  instance.eventMode = 'static';
@@ -1,5 +1,6 @@
1
1
  import { Container } from 'pixi.js';
2
2
  import { Label } from './Label';
3
+ import { Tween } from '../animation/Tween';
3
4
  import { Easing } from '../animation/Easing';
4
5
 
5
6
  export interface BalanceDisplayConfig {
@@ -23,7 +24,7 @@ export interface BalanceDisplayConfig {
23
24
  * Reactive balance display component.
24
25
  *
25
26
  * Automatically formats currency and can animate value changes
26
- * with a smooth countup/countdown effect.
27
+ * with a smooth countup/countdown effect using engine Tween.
27
28
  *
28
29
  * @example
29
30
  * ```ts
@@ -35,13 +36,15 @@ export interface BalanceDisplayConfig {
35
36
  * ```
36
37
  */
37
38
  export class BalanceDisplay extends Container {
39
+ readonly __uiComponent = true as const;
40
+
38
41
  private _prefixLabel: Label | null = null;
39
42
  private _valueLabel: Label;
40
43
  private _config: Required<Pick<BalanceDisplayConfig, 'currency' | 'locale' | 'animated' | 'animationDuration'>>;
41
44
  private _currentValue = 0;
42
45
  private _displayedValue = 0;
43
- private _animating = false;
44
- private _animationCancelled = false;
46
+ /** Internal target for Tween animation */
47
+ private _tweenTarget = { value: 0 };
45
48
 
46
49
  constructor(config: BalanceDisplayConfig = {}) {
47
50
  super();
@@ -111,42 +114,21 @@ export class BalanceDisplay extends Container {
111
114
  this.updateDisplay();
112
115
  }
113
116
 
114
- private async animateValue(from: number, to: number): Promise<void> {
115
- if (this._animating) {
116
- this._animationCancelled = true;
117
- }
118
-
119
- this._animating = true;
120
- this._animationCancelled = false;
121
- const duration = this._config.animationDuration;
122
- const startTime = Date.now();
123
-
124
- return new Promise<void>((resolve) => {
125
- const tick = () => {
126
- if (this._animationCancelled) {
127
- this._animating = false;
128
- resolve();
129
- return;
130
- }
131
-
132
- const elapsed = Date.now() - startTime;
133
- const t = Math.min(elapsed / duration, 1);
134
- const eased = Easing.easeOutCubic(t);
135
-
136
- this._displayedValue = from + (to - from) * eased;
117
+ private animateValue(from: number, to: number): void {
118
+ // Cancel any running animation
119
+ Tween.killTweensOf(this._tweenTarget);
120
+
121
+ this._tweenTarget.value = from;
122
+ Tween.to(
123
+ this._tweenTarget,
124
+ { value: to },
125
+ this._config.animationDuration,
126
+ Easing.easeOutCubic,
127
+ () => {
128
+ this._displayedValue = this._tweenTarget.value;
137
129
  this.updateDisplay();
138
-
139
- if (t < 1) {
140
- requestAnimationFrame(tick);
141
- } else {
142
- this._displayedValue = to;
143
- this.updateDisplay();
144
- this._animating = false;
145
- resolve();
146
- }
147
- };
148
- requestAnimationFrame(tick);
149
- });
130
+ },
131
+ );
150
132
  }
151
133
 
152
134
  private updateDisplay(): void {
@@ -163,4 +145,15 @@ export class BalanceDisplay extends Container {
163
145
  this._valueLabel.y = 14;
164
146
  }
165
147
  }
148
+
149
+ /** React reconciler update hook */
150
+ updateConfig(changed: Record<string, any>): void {
151
+ if ('value' in changed) this.setValue(changed.value);
152
+ if ('currency' in changed) this.setCurrency(changed.currency);
153
+ }
154
+
155
+ override destroy(options?: boolean | { children?: boolean; texture?: boolean; textureSource?: boolean }): void {
156
+ Tween.killTweensOf(this._tweenTarget);
157
+ super.destroy(options);
158
+ }
166
159
  }