@energy8platform/game-engine 0.34.2 → 0.35.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/audio.cjs.js +114 -59
  2. package/dist/audio.cjs.js.map +1 -1
  3. package/dist/audio.d.ts +25 -0
  4. package/dist/audio.esm.js +114 -59
  5. package/dist/audio.esm.js.map +1 -1
  6. package/dist/core.cjs.js +222 -66
  7. package/dist/core.cjs.js.map +1 -1
  8. package/dist/core.d.ts +25 -0
  9. package/dist/core.esm.js +223 -67
  10. package/dist/core.esm.js.map +1 -1
  11. package/dist/flow.cjs.js +246 -0
  12. package/dist/flow.cjs.js.map +1 -1
  13. package/dist/flow.d.ts +192 -33
  14. package/dist/flow.esm.js +238 -1
  15. package/dist/flow.esm.js.map +1 -1
  16. package/dist/host.cjs.js +343 -82
  17. package/dist/host.cjs.js.map +1 -1
  18. package/dist/host.d.ts +82 -2
  19. package/dist/host.esm.js +344 -83
  20. package/dist/host.esm.js.map +1 -1
  21. package/dist/index.cjs.js +222 -66
  22. package/dist/index.cjs.js.map +1 -1
  23. package/dist/index.d.ts +72 -0
  24. package/dist/index.esm.js +223 -67
  25. package/dist/index.esm.js.map +1 -1
  26. package/dist/scene-devtools.cjs.js +529 -115
  27. package/dist/scene-devtools.cjs.js.map +1 -1
  28. package/dist/scene-devtools.d.ts +187 -34
  29. package/dist/scene-devtools.esm.js +529 -115
  30. package/dist/scene-devtools.esm.js.map +1 -1
  31. package/dist/scene.cjs.js +704 -46
  32. package/dist/scene.cjs.js.map +1 -1
  33. package/dist/scene.d.ts +228 -41
  34. package/dist/scene.esm.js +698 -47
  35. package/dist/scene.esm.js.map +1 -1
  36. package/package.json +2 -2
  37. package/src/audio/AudioManager.ts +111 -53
  38. package/src/core/GameApplication.ts +47 -5
  39. package/src/host/buildConfig.ts +17 -4
  40. package/src/host/createSlotGame.ts +114 -12
  41. package/src/host/index.ts +3 -0
  42. package/src/host/types.ts +58 -0
  43. package/src/loading/LoadingScene.ts +76 -2
  44. package/src/loading/index.ts +6 -0
  45. package/src/types.ts +2 -0
@@ -5,13 +5,17 @@ interface SceneDoc {
5
5
  version: 1;
6
6
  /** Stable document id (usually the game id). */
7
7
  id: string;
8
+ /**
9
+ * Another scene this one builds on. The resolved doc is base ⊕ this, merged by node id,
10
+ * so a per-mode scene carries only its delta instead of a copy of the whole board.
11
+ * Resolved by `resolveExtends` before the doc reaches the engine.
12
+ */
13
+ extends?: string;
8
14
  /** Design space the layout rules are authored in (viewport arrives in these units). */
9
15
  design: {
10
16
  width: number;
11
17
  height: number;
12
18
  };
13
- /** Authoring modes the scene has views for (e.g. ['base','free_spins']); base is implicit. */
14
- modes?: string[];
15
19
  root: SceneNode;
16
20
  }
17
21
  interface SceneNode {
@@ -31,13 +35,6 @@ interface SceneNode {
31
35
  * frame's `anticipation` / `bonus` looks. A state is an override, not a new node.
32
36
  */
33
37
  states?: Record<string, NodeOverride>;
34
- /**
35
- * Per-GAME-MODE override, applied automatically when the runtime `mode` var matches
36
- * (base / free_spins / …). This is why base and free_spins are ONE scene, not two: a
37
- * node re-poses per mode instead of being duplicated into a separate scene. Layered
38
- * after orientation, before the active state.
39
- */
40
- modes?: Record<string, NodeOverride>;
41
38
  /** Id of another node in this doc used as this node's mask. */
42
39
  mask?: string;
43
40
  /** Filters applied to the node's view; kinds resolve through the registry (core: blur). */
@@ -157,21 +154,26 @@ interface PinLayout extends LayoutCommon {
157
154
  }
158
155
  type PinEdge = 'top-left' | 'top-center' | 'top-right' | 'center-left' | 'center' | 'center-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
159
156
  type LayoutRule = AbsoluteLayout | ViewportFractionLayout | CoverLayout | FrameFractionLayout | GridCellLayout | PinLayout;
160
- type ScenePatch =
161
- /** `mode` targets that mode's override (node.modes[mode].props); else base props. */
162
- {
157
+ type ScenePatch = {
163
158
  op: 'set-props';
164
159
  id: string;
165
160
  props: Record<string, unknown>;
166
- mode?: string;
167
161
  }
168
- /** Targets, in precedence: `mode` (node.modes[mode]) → `orientation` (responsive) → base. */
162
+ /**
163
+ * Targets `orientation` (responsive) when given, else the base rule. `layout: null` clears
164
+ * that slot — which is how undo removes an override the edit created.
165
+ */
169
166
  | {
170
167
  op: 'set-layout';
171
168
  id: string;
172
- layout: LayoutRule;
169
+ layout: LayoutRule | null;
173
170
  orientation?: Orientation;
174
- mode?: string;
171
+ }
172
+ /** Rename the outliner label. `name` is presentation only — addressing is always by id. */
173
+ | {
174
+ op: 'set-name';
175
+ id: string;
176
+ name: string | undefined;
175
177
  } | {
176
178
  op: 'set-state';
177
179
  id: string;
@@ -203,6 +205,22 @@ type ScenePatch =
203
205
  op: 'duplicate-node';
204
206
  id: string;
205
207
  };
208
+ /**
209
+ * Outcome of applying a patch. Rejections used to be console-only warnings inside the
210
+ * game, so an editor (or agent) saw "ok" for a patch that never applied — the reason now
211
+ * travels back to the caller.
212
+ */
213
+ type PatchResult = {
214
+ ok: true;
215
+ /**
216
+ * The patch that undoes this one. Applying it restores the pre-patch state exactly,
217
+ * which is what the editor's undo stack replays. Absent only when the op had no effect.
218
+ */
219
+ inverse?: ScenePatch;
220
+ } | {
221
+ ok: false;
222
+ error: string;
223
+ };
206
224
  /** Outliner row — the tree the inspector and the agent both read. */
207
225
  interface OutlineNode {
208
226
  id: string;
@@ -214,6 +232,46 @@ interface OutlineNode {
214
232
  children: OutlineNode[];
215
233
  }
216
234
 
235
+ type FieldKind = 'number' | 'text' | 'boolean' | 'color'
236
+ /** One of `options`. */
237
+ | 'enum'
238
+ /** A texture/audio alias from the game's asset manifest — the editor offers a picker. */
239
+ | 'asset'
240
+ /** The id of another node in this doc — the editor offers the node list. */
241
+ | 'nodeRef'
242
+ /** Nested record described by `fields`. */
243
+ | 'object'
244
+ /** Anything else: edited as raw JSON. */
245
+ | 'json';
246
+ interface FieldSchema {
247
+ kind: FieldKind;
248
+ /** Human label (defaults to the key). */
249
+ label?: string;
250
+ /** What the field does — inspector tooltip AND the agent's documentation. */
251
+ doc?: string;
252
+ /**
253
+ * Value the runtime uses when the field is absent. The inspector shows it as the
254
+ * effective value so "not set" never looks like "zero".
255
+ */
256
+ default?: unknown;
257
+ /** `enum`: the allowed values. */
258
+ options?: readonly string[];
259
+ /** `number`: bounds and input step. */
260
+ min?: number;
261
+ max?: number;
262
+ step?: number;
263
+ /** `asset`: which kind of media this field accepts (filters the picker). */
264
+ accept?: 'image' | 'audio' | 'spritesheet';
265
+ /** `nodeRef`: restrict the picker to nodes of this type (e.g. `reelFrame`). */
266
+ ofType?: string;
267
+ /** `object`: the nested fields. */
268
+ fields?: PropsSchema;
269
+ /** Lower sorts first in the inspector; unordered fields follow, alphabetically. */
270
+ order?: number;
271
+ }
272
+ /** A node type's (or prefab's) authorable props, keyed by prop name. */
273
+ type PropsSchema = Record<string, FieldSchema>;
274
+
217
275
  interface Size {
218
276
  width: number;
219
277
  height: number;
@@ -260,11 +318,17 @@ interface SceneHandle {
260
318
  layout(width: number, height: number): void;
261
319
  setVar(name: string, value: unknown): void;
262
320
  setState(id: string, state: string | null): void;
263
- patch(patch: ScenePatch): void;
321
+ /** Apply an edit. Returns `{ok:false, error}` when the patch was rejected. */
322
+ patch(patch: ScenePatch): PatchResult;
264
323
  /** The live doc (mutated by patches) — serialize this to persist the scene. */
265
324
  doc(): SceneDoc;
266
325
  /** Spawnable palette (node kinds with defaults + prefabs) — feeds the editor add-menu. */
267
326
  palette(): PaletteEntry[];
327
+ /**
328
+ * Props schemas by node kind (prefabs keyed `prefab:<name>`) — what the inspector renders
329
+ * its controls from, and what an agent reads as the authoring contract.
330
+ */
331
+ schemas(): Record<string, PropsSchema>;
268
332
  /**
269
333
  * Re-apply every node's doc-effective props, wiping transient presentation writes
270
334
  * (flow tweens/count-ups). "Settle = the doc" — scrub replays call this first.
@@ -273,6 +337,12 @@ interface SceneHandle {
273
337
  destroy(): void;
274
338
  }
275
339
 
340
+ /** One entry of the game's asset manifest, as offered by the asset picker. */
341
+ interface InspectorAsset {
342
+ alias: string;
343
+ path: string;
344
+ kind?: 'image' | 'audio' | string;
345
+ }
276
346
  interface SceneInspectorOptions {
277
347
  handle: SceneHandle;
278
348
  container: HTMLElement;
@@ -286,6 +356,13 @@ interface SceneInspectorOptions {
286
356
  onCalibrate?: (id: string) => void;
287
357
  /** Hide the built-in outliner (a host — e.g. the standalone IDE — provides its own). */
288
358
  showTree?: boolean;
359
+ /**
360
+ * The game's assets, for `kind:'asset'` fields. Without it those fields stay a text box —
361
+ * which is exactly the dead end a fresh sprite used to hit.
362
+ */
363
+ assets?: () => InspectorAsset[];
364
+ /** Map an asset path to a previewable URL (the IDE prefixes the game's origin). */
365
+ assetUrl?: (path: string) => string;
289
366
  }
290
367
  interface SceneInspector {
291
368
  select(id: string | null): void;
@@ -302,8 +379,8 @@ interface LayoutFieldSpec {
302
379
  /** Dot-path inside the rule ('x', 'pxNudge.x', 'anchor.0', 'offset.1'). */
303
380
  path: string;
304
381
  label: string;
305
- kind: 'number' | 'text' | 'readonly';
306
- value: number | string;
382
+ kind: 'number' | 'text' | 'boolean' | 'readonly';
383
+ value: number | string | boolean;
307
384
  /** Input step — fractions and anchors get fine steps, px fields get 1. */
308
385
  step?: number;
309
386
  }
@@ -330,9 +407,27 @@ interface CueDef {
330
407
  jitter?: number;
331
408
  volume?: number;
332
409
  }
410
+ /**
411
+ * Fields every step carries regardless of kind.
412
+ *
413
+ * `id` exists because steps used to be addressed purely by position — three different
414
+ * positional schemes across the codebase — so any insert or delete invalidated every
415
+ * sibling's path, and a trace entry could not say WHICH step it came from. It lives on the
416
+ * step rather than in a document-level map on purpose: the editor→game doc push copies only
417
+ * `on` and `cues`, so a new top-level key would be silently dropped and never saved.
418
+ */
419
+ interface StepCommon {
420
+ /** Stable identity. Assigned by `ensureStepIds` when a hand-written doc omits it. */
421
+ id?: string;
422
+ /** Editor-only: where the node sits on the flow canvas. Absent → auto-laid-out. */
423
+ ui?: {
424
+ x: number;
425
+ y: number;
426
+ };
427
+ }
333
428
  /** Core step vocabulary; plugins contribute additional `do` kinds through the registry. */
334
429
  type Step = TweenStep | SoundStep | SetStateStep | SetVarStep | SetPropsStep | CountUpStep | WaitStep | IfStep | ParallelStep | SeqStep | ForEachStep | CodeStep | PluginStep;
335
- interface TweenStep {
430
+ interface TweenStep extends StepCommon {
336
431
  do: 'tween';
337
432
  node: string;
338
433
  /** Target numeric view properties (alpha, x, y, scale, rotation, …). */
@@ -340,28 +435,28 @@ interface TweenStep {
340
435
  ms: number;
341
436
  ease?: string;
342
437
  }
343
- interface SoundStep {
438
+ interface SoundStep extends StepCommon {
344
439
  do: 'sound';
345
440
  cue: string;
346
441
  action?: 'play' | 'stop';
347
442
  }
348
- interface SetStateStep {
443
+ interface SetStateStep extends StepCommon {
349
444
  do: 'setState';
350
445
  node: string;
351
446
  state: string | null;
352
447
  }
353
- interface SetVarStep {
448
+ interface SetVarStep extends StepCommon {
354
449
  do: 'setVar';
355
450
  name: string;
356
451
  value: unknown;
357
452
  }
358
453
  /** Transient prop write on the live instance (presentation) — does NOT touch the doc. */
359
- interface SetPropsStep {
454
+ interface SetPropsStep extends StepCommon {
360
455
  do: 'setProps';
361
456
  node: string;
362
457
  props: Record<string, unknown>;
363
458
  }
364
- interface CountUpStep {
459
+ interface CountUpStep extends StepCommon {
365
460
  do: 'countUp';
366
461
  node: string;
367
462
  /** Instance prop receiving the formatted value (default 'value' — badge prefabs). */
@@ -372,24 +467,24 @@ interface CountUpStep {
372
467
  ms?: number;
373
468
  format?: 'int' | 'space';
374
469
  }
375
- interface WaitStep {
470
+ interface WaitStep extends StepCommon {
376
471
  do: 'wait';
377
472
  ms?: number;
378
473
  until?: 'tap';
379
474
  }
380
- interface IfStep {
475
+ interface IfStep extends StepCommon {
381
476
  do: 'if';
382
477
  /** Micro-expression over the fire() ctx: `win >= 100`, `mode === 'fs'`, or a bare truthy name. */
383
478
  when: string;
384
479
  then: Step[];
385
480
  else?: Step[];
386
481
  }
387
- interface ParallelStep {
482
+ interface ParallelStep extends StepCommon {
388
483
  do: 'parallel';
389
484
  /** Independent tracks, awaited together. */
390
485
  steps: Step[][];
391
486
  }
392
- interface SeqStep {
487
+ interface SeqStep extends StepCommon {
393
488
  do: 'seq';
394
489
  steps: Step[];
395
490
  }
@@ -398,7 +493,7 @@ interface SeqStep {
398
493
  * flights, per-cell transmutes, cascade histories). Nested steps see the current element
399
494
  * as `$<as>` (default `$item`) and its index as `$<as>Index`.
400
495
  */
401
- interface ForEachStep {
496
+ interface ForEachStep extends StepCommon {
402
497
  do: 'forEach';
403
498
  /** '$path' into the ctx (or an inline array). */
404
499
  items: string | unknown[];
@@ -409,12 +504,13 @@ interface ForEachStep {
409
504
  steps: Step[];
410
505
  }
411
506
  /** Escape hatch: a named code choreography registered on the runner. */
412
- interface CodeStep {
507
+ interface CodeStep extends StepCommon {
413
508
  do: 'code';
414
509
  ref: string;
415
510
  args?: Record<string, unknown>;
416
511
  }
417
- interface PluginStep {
512
+ /** A plugin-contributed kind: open shape, but it still carries the common fields. */
513
+ interface PluginStep extends StepCommon {
418
514
  do: string;
419
515
  [key: string]: unknown;
420
516
  }
@@ -422,6 +518,12 @@ interface TraceEntry {
422
518
  /** ms since fire() (real time, unscaled). */
423
519
  t: number;
424
520
  do: string;
521
+ /**
522
+ * Which step produced this entry. Without it the trace only says a `tween` ran — two
523
+ * identical tweens are indistinguishable — so a canvas cannot highlight what is running
524
+ * and a scrub cannot point at a step.
525
+ */
526
+ stepId?: string;
425
527
  node?: string;
426
528
  info?: string;
427
529
  }
@@ -452,6 +554,16 @@ interface FlowRunner {
452
554
  /** Host reports a user tap (releases `wait until:'tap'`). */
453
555
  tap(): void;
454
556
  events(): string[];
557
+ /**
558
+ * The step kinds this runner accepts, with their authoring metadata. The editor lives in
559
+ * another origin and cannot reach the registry, so the contract travels through here —
560
+ * before this, field lists were hardcoded in the editor (twice, and already drifting).
561
+ */
562
+ stepKinds(): Array<{
563
+ kind: string;
564
+ schema?: PropsSchema;
565
+ agentDoc?: string;
566
+ }>;
455
567
  destroy(): void;
456
568
  }
457
569
 
@@ -486,7 +598,7 @@ type HandleId = 'nw' | 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' | 'rotate' | '
486
598
  interface TransformGizmo {
487
599
  attach(nodeId: string | null): void;
488
600
  refresh(): void;
489
- /** Run a full handle drag programmatically (keyboard/agent/tests). Screen coords. */
601
+ /** Run a full handle drag programmatically (agent/tests). Screen coords. */
490
602
  drag(handle: HandleId, from: {
491
603
  x: number;
492
604
  y: number;
@@ -494,6 +606,11 @@ interface TransformGizmo {
494
606
  x: number;
495
607
  y: number;
496
608
  }): void;
609
+ /**
610
+ * Move the selection by a discrete amount (arrow keys). Deliberately NOT a synthetic drag:
611
+ * a drag ignores travel below the click/drag threshold, which would swallow a 1px nudge.
612
+ */
613
+ nudge(dxScreen: number, dyScreen: number): void;
497
614
  destroy(): void;
498
615
  }
499
616
 
@@ -512,12 +629,38 @@ interface IdeFlowSurface {
512
629
  /** Writable path for the flow doc (idePlugin allowlist), e.g. 'src/scene/flow.json'. */
513
630
  file?: string;
514
631
  /** Step kinds available (built-ins + plugins) for the add-step menu. */
632
+ /** @deprecated the runner reports its own kinds WITH schemas — kept for older hosts. */
515
633
  stepKinds?: string[];
516
634
  /** ctx fields for the `$`/`when` pickers — ideally derived from the game's result schema. */
517
635
  ctx?: CtxField[];
518
636
  /** Build a ctx to fire an event with when the console has no real result (spike/dev). */
519
637
  sampleCtx?: (event: string) => Record<string, unknown>;
520
638
  }
639
+ /** One scene in the stage graph (file-referenced; the host swaps scenes by navigation). */
640
+ interface StageSceneRef {
641
+ key: string;
642
+ title?: string;
643
+ /** Scene doc file (idePlugin-writable), e.g. 'src/scene/scene.json'. */
644
+ file: string;
645
+ /** Transition into this scene, as data on the edge (host animates it at runtime). */
646
+ transition?: 'none' | 'fade' | 'slide-left' | 'slide-right';
647
+ /** Skipped on replay launches (intro idiom). */
648
+ skipOnReplay?: boolean;
649
+ }
650
+ /** The multi-scene graph the IDE Scenes panel lists and edits — data, not a live scene. */
651
+ interface StageManifest {
652
+ scenes: StageSceneRef[];
653
+ }
654
+ /** A game's stage surface: the manifest, which scene this page shows, and how to navigate. */
655
+ interface IdeStageSurface {
656
+ manifest: StageManifest;
657
+ /** Which scene key THIS page is currently rendering. */
658
+ activeKey: string;
659
+ /** Writable path for the manifest (idePlugin allowlist), e.g. 'src/scene/stage.json'. */
660
+ file?: string;
661
+ /** Query param the page reads to pick a scene (default 'scene' → ?scene=<key>). */
662
+ sceneParam?: string;
663
+ }
521
664
  interface IdeBridgeOptions {
522
665
  handle: SceneHandle;
523
666
  /** Pixi app — the bridge draws the selection outline on its stage and drives it on the ticker. */
@@ -532,6 +675,16 @@ interface IdeBridgeOptions {
532
675
  designPerPixel?: () => number;
533
676
  /** Optional flow-IR surface — enables the IDE's Flow tab + spin console. */
534
677
  flow?: IdeFlowSurface;
678
+ /** Optional multi-scene stage — enables the IDE's Scenes panel. */
679
+ stage?: IdeStageSurface;
680
+ /**
681
+ * The game's texture aliases as `alias -> public path`. THIS is what `props.src` holds.
682
+ * Scanning public/assets only yields file names, and for most games those are not the
683
+ * aliases the game registered (paper-duel loads `back.webp` as `th_back`), so a picker
684
+ * built from the file scan alone would write aliases the game cannot resolve. Pass the
685
+ * game's own manifest and the editor offers exactly the values that will work.
686
+ */
687
+ assetAliases?: () => Record<string, string>;
535
688
  targetOrigin?: string;
536
689
  }
537
690
  interface IdeBridge {
@@ -545,4 +698,4 @@ interface IdeBridge {
545
698
  declare function mountIdeBridge(opts: IdeBridgeOptions): IdeBridge;
546
699
 
547
700
  export { createFlowEditor, createFlowTimeline, createSceneInspector, layoutFieldSpecs, mountIdeBridge, propControlKind, setRulePath };
548
- export type { CtxField, FlowEditor, FlowEditorOptions, FlowTimeline, FlowTimelineOptions, IdeBridge, IdeBridgeOptions, IdeFlowSurface, LayoutFieldSpec, PropControlKind, SceneInspector, SceneInspectorOptions };
701
+ export type { CtxField, FlowEditor, FlowEditorOptions, FlowTimeline, FlowTimelineOptions, IdeBridge, IdeBridgeOptions, IdeFlowSurface, IdeStageSurface, LayoutFieldSpec, PropControlKind, SceneInspector, SceneInspectorOptions, StageManifest, StageSceneRef };