@energy8platform/game-engine 0.33.3 → 0.33.4

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,344 @@
1
+ import { Container, Texture } from 'pixi.js';
2
+
3
+ type Orientation = 'landscape' | 'portrait';
4
+ interface SceneDoc {
5
+ version: 1;
6
+ /** Stable document id (usually the game id). */
7
+ id: string;
8
+ /** Design space the layout rules are authored in (viewport arrives in these units). */
9
+ design: {
10
+ width: number;
11
+ height: number;
12
+ };
13
+ root: SceneNode;
14
+ }
15
+ interface SceneNode {
16
+ /** Stable id — flow, code and patches address the node by it. Unique per doc. */
17
+ id: string;
18
+ /** Node kind resolved through the registry (built-in or plugin-contributed). */
19
+ type: string;
20
+ /** Human label for the outliner; never used for addressing. */
21
+ name?: string;
22
+ /** Kind-specific properties (validated by the contribution's schema). */
23
+ props?: Record<string, unknown>;
24
+ layout?: LayoutRule;
25
+ /** Per-orientation override merged over the base node before layout. */
26
+ responsive?: Partial<Record<Orientation, NodeOverride>>;
27
+ /**
28
+ * Named variants toggled at runtime (flow `setState`, editor, code) — e.g. a reel
29
+ * frame's `anticipation` / `bonus` looks. A state is an override, not a new node.
30
+ */
31
+ states?: Record<string, NodeOverride>;
32
+ /** Id of another node in this doc used as this node's mask. */
33
+ mask?: string;
34
+ /**
35
+ * Tiny condition over runtime vars, e.g. `"mode === 'free_spins'"`. Supported form:
36
+ * `<var> <op> <literal>` with `=== !== >= <= > <`. Anything richer belongs in flow/code.
37
+ */
38
+ visibleWhen?: string;
39
+ /** Entry in the shared anchor vocabulary flow/animation use to address the node. */
40
+ anchorName?: string;
41
+ /** Order = z-order. */
42
+ children?: SceneNode[];
43
+ }
44
+ /** Partial node applied on top of the base for an orientation or a named state. */
45
+ interface NodeOverride {
46
+ layout?: LayoutRule;
47
+ props?: Record<string, unknown>;
48
+ visible?: boolean;
49
+ }
50
+ interface LayoutCommon {
51
+ /** Content anchor [ax, ay] in 0..1 (applied via pivot on the node's natural size). */
52
+ anchor?: [number, number];
53
+ /** Final hand-tuned nudge in design px, applied after the rule resolves. */
54
+ pxNudge?: {
55
+ x?: number;
56
+ y?: number;
57
+ };
58
+ /** Static rotation in radians (choreographed rotation belongs to flow/animation). */
59
+ rotation?: number;
60
+ }
61
+ /** Fixed design-space coordinates. */
62
+ interface AbsoluteLayout extends LayoutCommon {
63
+ mode: 'absolute';
64
+ x: number;
65
+ y: number;
66
+ scale?: number;
67
+ rotation?: number;
68
+ }
69
+ /**
70
+ * Position as a fraction of the live viewport. Sizing, strongest first:
71
+ * `widthFrac`/`heightFrac` size the node to that fraction of the viewport (contain when
72
+ * both are given) — the "frame is 92% of the screen in portrait" idiom; otherwise
73
+ * `scaleWith` multiplies `scale` by the viewport/design ratio; otherwise plain `scale`.
74
+ */
75
+ interface ViewportFractionLayout extends LayoutCommon {
76
+ mode: 'viewport-fraction';
77
+ xFrac: number;
78
+ yFrac: number;
79
+ scale?: number;
80
+ /** Multiply `scale` by viewport/design ratio: width | height | min | max. */
81
+ scaleWith?: 'width' | 'height' | 'min' | 'max';
82
+ /** Size the node to this fraction of the viewport width (scale derived from natural size). */
83
+ widthFrac?: number;
84
+ /** Size the node to this fraction of the viewport height. */
85
+ heightFrac?: number;
86
+ /** With both fracs: contain (default) keeps aspect, stretch distorts to fill exactly. */
87
+ fit?: 'contain' | 'stretch';
88
+ }
89
+ /**
90
+ * Center-crop fill of the current space (backgrounds, diorama layers). `scale` multiplies
91
+ * the computed cover scale (the paper-duel `e` idiom); `pxNudge` offsets from center.
92
+ */
93
+ interface CoverLayout extends LayoutCommon {
94
+ mode: 'cover';
95
+ scale?: number;
96
+ }
97
+ /**
98
+ * Place inside another node's laid-out rect using fractions of that rect.
99
+ * `use: 'inner'` reads the target's declared `props.inner` fractions (reel frames
100
+ * declare where their hollow is) instead of spelling the fractions twice.
101
+ * When a box (`wFrac`/`hFrac` or `use`) is given the node is contain-fitted into it
102
+ * (`fit: 'stretch'` distorts instead).
103
+ */
104
+ interface FrameFractionLayout extends LayoutCommon {
105
+ mode: 'frame-fraction';
106
+ frame: string;
107
+ xFrac?: number;
108
+ yFrac?: number;
109
+ wFrac?: number;
110
+ hFrac?: number;
111
+ use?: 'inner';
112
+ fit?: 'contain' | 'stretch';
113
+ }
114
+ /**
115
+ * Center of a reel-grid cell (badges, per-cell VFX slots). `grid` defaults to the sole
116
+ * reelGrid. By default the node scales with the grid (sizes authored in grid-design px);
117
+ * `scaleWithGrid: false` keeps it in absolute design units.
118
+ */
119
+ interface GridCellLayout extends LayoutCommon {
120
+ mode: 'grid-cell';
121
+ grid?: string;
122
+ col: number;
123
+ row: number;
124
+ scale?: number;
125
+ scaleWithGrid?: boolean;
126
+ }
127
+ /** Pin to an edge/center of another node's laid-out rect. */
128
+ interface PinLayout extends LayoutCommon {
129
+ mode: 'pin';
130
+ to: string;
131
+ edge: PinEdge;
132
+ offset?: [number, number];
133
+ scale?: number;
134
+ }
135
+ type PinEdge = 'top-left' | 'top-center' | 'top-right' | 'center-left' | 'center' | 'center-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
136
+ type LayoutRule = AbsoluteLayout | ViewportFractionLayout | CoverLayout | FrameFractionLayout | GridCellLayout | PinLayout;
137
+ type ScenePatch = {
138
+ op: 'set-props';
139
+ id: string;
140
+ props: Record<string, unknown>;
141
+ }
142
+ /** Without `orientation` replaces the base rule; with it, that orientation's override. */
143
+ | {
144
+ op: 'set-layout';
145
+ id: string;
146
+ layout: LayoutRule;
147
+ orientation?: Orientation;
148
+ } | {
149
+ op: 'set-state';
150
+ id: string;
151
+ state: string | null;
152
+ } | {
153
+ op: 'set-var';
154
+ name: string;
155
+ value: unknown;
156
+ };
157
+ /** Outliner row — the tree the inspector and the agent both read. */
158
+ interface OutlineNode {
159
+ id: string;
160
+ type: string;
161
+ name?: string;
162
+ anchorName?: string;
163
+ visible: boolean;
164
+ state: string | null;
165
+ children: OutlineNode[];
166
+ }
167
+
168
+ interface Rect {
169
+ x: number;
170
+ y: number;
171
+ width: number;
172
+ height: number;
173
+ }
174
+ interface Size {
175
+ width: number;
176
+ height: number;
177
+ }
178
+ /** Resolved transform for a node, in parent (scene) coordinates. */
179
+ interface Placement {
180
+ x: number;
181
+ y: number;
182
+ scaleX: number;
183
+ scaleY: number;
184
+ rotation: number;
185
+ /** Content anchor in 0..1 — the engine applies it via pivot over the natural size. */
186
+ anchor: [number, number];
187
+ /** Present when the rule targeted a box (frame-fraction with a width/height). */
188
+ box?: Size;
189
+ }
190
+ /** Layout context the engine provides while resolving a pass. */
191
+ interface LayoutContext {
192
+ viewport: Size;
193
+ design: Size;
194
+ /** Laid-out AABB of a node (undefined until that node resolved this pass). */
195
+ bounds(id: string): Rect | undefined;
196
+ /** Declared `props.inner` fractions of a node mapped onto its laid-out bounds. */
197
+ innerRect(id: string): Rect | undefined;
198
+ /** Scene-space center of a grid cell; `id` undefined targets the doc's sole reelGrid. */
199
+ gridCell(id: string | undefined, col: number, row: number): {
200
+ x: number;
201
+ y: number;
202
+ } | undefined;
203
+ /** World scale of a grid node (for grid-cell nodes that scale with the grid). */
204
+ gridScale(id: string | undefined): {
205
+ x: number;
206
+ y: number;
207
+ } | undefined;
208
+ }
209
+ type LayoutResolution = {
210
+ placement: Placement;
211
+ } | {
212
+ waitingFor: string;
213
+ };
214
+ declare function orientationOf(width: number, height: number, portraitFactor?: number): Orientation;
215
+ /** Base node + orientation override + active state override (state wins), for one frame. */
216
+ declare function effectiveNode(node: SceneNode, orientation: Orientation, state: string | null): {
217
+ layout?: LayoutRule;
218
+ props: Record<string, unknown>;
219
+ visible: boolean;
220
+ };
221
+ /** Which node id (if any) must be laid out before this rule can resolve. */
222
+ declare function dependencyOf(rule: LayoutRule | undefined): string | undefined;
223
+ declare function edgePoint(rect: Rect, edge: PinEdge): {
224
+ x: number;
225
+ y: number;
226
+ };
227
+ /** AABB of a placement over a natural content size (rotation ignored — pin targets should not rotate). */
228
+ declare function placementBounds(p: Placement, natural: Size): Rect;
229
+ /**
230
+ * Resolve one rule. Returns `waitingFor` when a referenced node has no bounds yet — the
231
+ * engine re-queues the node for the next pass (bounded; see engine).
232
+ */
233
+ declare function resolveLayoutRule(rule: LayoutRule, natural: Size, ctx: LayoutContext): LayoutResolution;
234
+ /**
235
+ * Evaluate a `visibleWhen` micro-expression: `<var> <op> <literal>` with
236
+ * `=== !== >= <= > <`. Unknown shapes evaluate to visible (with the engine warning once) —
237
+ * richer conditions belong in flow or code, not in the scene doc.
238
+ */
239
+ declare function evalVisibleWhen(expr: string, vars: Record<string, unknown>): boolean | undefined;
240
+
241
+ /** A symbol's visual. Any Container; optionally implements the lifecycle the cell drives. */
242
+ interface SymbolView extends Container {
243
+ playIdle?(): void;
244
+ playWin?(): Promise<void>;
245
+ showStatic?(): void;
246
+ /** Resize the symbol to the given cell size in pixels (square scalar or rectangular). */
247
+ resize?(size: number | {
248
+ width: number;
249
+ height: number;
250
+ }): void;
251
+ }
252
+ /** Game-supplied factory: build the view for a symbol id (sprite / layered sprites / Spine / composite). */
253
+ type SymbolResolver = (symbolId: string) => SymbolView | null;
254
+
255
+ interface NodeCreateContext {
256
+ registry: SceneRegistry;
257
+ /** Resolve a texture alias (defaults to Pixi `Texture.from`; tests/labs inject). */
258
+ texture(alias: string): Texture;
259
+ /** Game-supplied symbol factory — required by the `reelGrid` kind. */
260
+ resolveSymbol?: SymbolResolver;
261
+ log?: (msg: string) => void;
262
+ }
263
+ /** Live counterpart of one scene node, produced by its contribution. */
264
+ interface NodeInstance {
265
+ view: Container;
266
+ /** Natural (unscaled) content size — drives anchor, cover and box fitting. */
267
+ measure(): Size;
268
+ /** Apply a props patch in place; absent → the engine rebuilds the node instead. */
269
+ applyProps?(props: Record<string, unknown>): void;
270
+ /** Scene-local center of a grid cell (reel-grid kinds only; enables `grid-cell` layout). */
271
+ gridCell?(col: number, row: number): {
272
+ x: number;
273
+ y: number;
274
+ };
275
+ destroy?(): void;
276
+ }
277
+ interface NodeTypeContribution {
278
+ kind: string;
279
+ /** JSON-Schema-ish description of `props` — validation, inspector autogen, agent docs. */
280
+ schema?: Record<string, unknown>;
281
+ /** 3–10 lines for the agent: when to use this kind, patterns, anti-patterns. */
282
+ agentDoc?: string;
283
+ create(node: SceneNode, ctx: NodeCreateContext): NodeInstance;
284
+ }
285
+ /** Reusable game component (meters, HUDs) addressed as `{type:'prefab', props:{prefab:'name'}}`. */
286
+ interface PrefabContribution {
287
+ name: string;
288
+ schema?: Record<string, unknown>;
289
+ agentDoc?: string;
290
+ create(props: Record<string, unknown>, ctx: NodeCreateContext): NodeInstance;
291
+ }
292
+ interface ScenePlugin {
293
+ id: string;
294
+ nodeTypes?: NodeTypeContribution[];
295
+ prefabs?: PrefabContribution[];
296
+ }
297
+ interface SceneRegistry {
298
+ nodeType(kind: string): NodeTypeContribution | undefined;
299
+ prefab(name: string): PrefabContribution | undefined;
300
+ kinds(): string[];
301
+ prefabNames(): string[];
302
+ }
303
+ /** Merge built-ins with plugin contributions; later plugins may override earlier kinds. */
304
+ declare function createSceneRegistry(plugins?: ScenePlugin[], builtins?: NodeTypeContribution[]): SceneRegistry;
305
+
306
+ declare const BUILTIN_NODE_TYPES: NodeTypeContribution[];
307
+
308
+ interface SceneValidationError {
309
+ nodeId?: string;
310
+ message: string;
311
+ }
312
+ declare function validateSceneDoc(doc: SceneDoc, registry: SceneRegistry): SceneValidationError[];
313
+
314
+ interface CreateSceneOptions {
315
+ /** Extra contributions (official + game-local plugins) merged over the built-ins. */
316
+ plugins?: ScenePlugin[];
317
+ /** Texture alias resolver; defaults to Pixi's `Texture.from`. */
318
+ texture?: (alias: string) => Texture;
319
+ /** Symbol factory for `reelGrid` nodes. */
320
+ resolveSymbol?: SymbolResolver;
321
+ /** Initial runtime vars driving `visibleWhen` (e.g. { mode: 'base' }). */
322
+ vars?: Record<string, unknown>;
323
+ /** Treat near-square viewports as portrait below this width/height factor (default 1). */
324
+ portraitFactor?: number;
325
+ log?: (msg: string) => void;
326
+ }
327
+ interface SceneHandle {
328
+ view: Container;
329
+ node(id: string): Container | undefined;
330
+ instance(id: string): NodeInstance | undefined;
331
+ anchor(name: string): Container | undefined;
332
+ tree(): OutlineNode;
333
+ layout(width: number, height: number): void;
334
+ setVar(name: string, value: unknown): void;
335
+ setState(id: string, state: string | null): void;
336
+ patch(patch: ScenePatch): void;
337
+ /** The live doc (mutated by patches) — serialize this to persist the scene. */
338
+ doc(): SceneDoc;
339
+ destroy(): void;
340
+ }
341
+ declare function createSceneFromDoc(doc: SceneDoc, opts?: CreateSceneOptions): SceneHandle;
342
+
343
+ export { BUILTIN_NODE_TYPES, createSceneFromDoc, createSceneRegistry, dependencyOf, edgePoint, effectiveNode, evalVisibleWhen, orientationOf, placementBounds, resolveLayoutRule, validateSceneDoc };
344
+ export type { AbsoluteLayout, CoverLayout, CreateSceneOptions, FrameFractionLayout, GridCellLayout, LayoutContext, LayoutResolution, LayoutRule, NodeCreateContext, NodeInstance, NodeOverride, NodeTypeContribution, Orientation, OutlineNode, PinEdge, PinLayout, Placement, PrefabContribution, Rect, SceneDoc, SceneHandle, SceneNode, ScenePatch, ScenePlugin, SceneRegistry, SceneValidationError, Size, ViewportFractionLayout };