incanto 0.7.0 → 0.8.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/bin/incanto-check.mjs +6 -4
  2. package/dist/2d.d.ts +93 -3
  3. package/dist/2d.js +4 -4
  4. package/dist/3d.d.ts +45 -4
  5. package/dist/3d.js +4 -4
  6. package/dist/{audio-player-VSjk_vG8.d.ts → audio-player-Dyjg5k92.d.ts} +1 -1
  7. package/dist/audit-C6rMyict.js +58 -0
  8. package/dist/{behavior-e3kAmPGC.d.ts → behavior-Bod1AyJS.d.ts} +211 -174
  9. package/dist/{create-game-CEFoFN8d.js → create-game-66c4iYJE.js} +76 -6
  10. package/dist/{create-game-X5iRu6pf.js → create-game-BNDSbhy-.js} +88 -6
  11. package/dist/debug.d.ts +1 -1
  12. package/dist/debug.js +3 -0
  13. package/dist/{errors-BMFaY68Q.d.ts → errors-1dXlIwoR.d.ts} +7 -1
  14. package/dist/{gameplay-CDFgSG6z.js → gameplay-DPMgZk9W.js} +108 -1
  15. package/dist/gameplay.d.ts +44 -3
  16. package/dist/gameplay.js +2 -2
  17. package/dist/index.d.ts +151 -9
  18. package/dist/index.js +189 -4
  19. package/dist/{loader-y_X4zYO0.d.ts → loader-BcUDfNHn.d.ts} +1 -1
  20. package/dist/net.d.ts +46 -2
  21. package/dist/net.js +54 -2
  22. package/dist/particle-sim-CyUU7HVU.js +281 -0
  23. package/dist/{physics-2d-DmQ540uR.js → physics-2d-q2N362ph.js} +45 -4
  24. package/dist/{physics-3d-CDjuhtt_.js → physics-3d-CvHr31d4.js} +1 -1
  25. package/dist/react.d.ts +1 -1
  26. package/dist/react.js +1 -1
  27. package/dist/{register-CscQqB7V.js → register-Cbs7QxoV.js} +139 -4
  28. package/dist/{register-ClKnoILk.js → register-Cqjxrfq0.js} +254 -4
  29. package/dist/{register-C35HDZpm.js → register-CxmuI9FL.js} +1 -1
  30. package/dist/{particle-sim-CrTE7c02.js → register-DRPIjrKG.js} +1185 -278
  31. package/dist/test.d.ts +4 -10
  32. package/dist/test.js +11 -16
  33. package/editor/assets/{agent8-1WJU-yXr.js → agent8-B8QnWCeP.js} +1 -1
  34. package/editor/assets/{index-DzYFgZbl.js → index-CVhnNkb3.js} +81 -81
  35. package/editor/index.html +1 -1
  36. package/package.json +1 -1
  37. package/schemas/scene.schema.json +427 -0
  38. package/skills/incanto-3d-character.md +14 -0
  39. package/skills/incanto-audio.md +23 -0
  40. package/skills/incanto-building-2d-games.md +31 -0
  41. package/skills/incanto-building-3d-games.md +21 -0
  42. package/skills/incanto-gameplay-behaviors.md +28 -0
  43. package/skills/incanto-hud.md +24 -0
  44. package/skills/incanto-multiplayer.md +27 -0
  45. package/skills/incanto-node-reference.md +73 -0
  46. package/skills/incanto-physics-and-input.md +9 -0
  47. package/skills/incanto-verifying-your-game.md +40 -0
  48. package/dist/register-Dl_ixIJe.js +0 -834
@@ -1,5 +1,68 @@
1
1
  import { a as Rng, c as JsonValue, i as SceneJson, s as JsonObject } from "./schema-CcoWb32N.js";
2
2
 
3
+ //#region src/core/scene-tree.d.ts
4
+ /**
5
+ * Owns a node tree and drives its lifecycle:
6
+ *
7
+ * - `setRoot` → onEnterTree parent-first, then onReady children-first (once per instance)
8
+ * - `update`/`fixedUpdate` → parent-first traversal, then flush of queued frees
9
+ * - group queries across attached nodes
10
+ *
11
+ * Headless by design — tests step it manually; the render loop (M2) calls it.
12
+ */
13
+ declare class SceneTree {
14
+ private _root;
15
+ private _engine;
16
+ private readonly freeQueue;
17
+ /**
18
+ * name → attached nodes with that name. Maintained by Node enter/exit/rename
19
+ * so `%name` targeting and root-level getNodesByName are O(1) — behaviors
20
+ * resolve targets every frame and a full-tree walk per chaser melted horde
21
+ * scenes (N chasers × whole tree, 60×/s).
22
+ */
23
+ private readonly _nameIndex;
24
+ /**
25
+ * Bumped whenever ANY node attaches or detaches — cheap "did the tree
26
+ * change shape?" check for per-step body gathers and similar caches.
27
+ */
28
+ _structureVersion: number;
29
+ private _frameId;
30
+ /**
31
+ * Monotonic update-frame counter — bumped once per `update()`. Per-frame
32
+ * caches (e.g. the 3d grass-bender body gather) key off this so they rebuild
33
+ * at most once a frame regardless of node visitation order.
34
+ */
35
+ get frameId(): number;
36
+ /** @internal Node attach hook. */
37
+ _indexName(node: Node): void;
38
+ /** @internal Node detach/rename hook (`name` = the entry to remove). */
39
+ _unindexName(node: Node, name?: string): void;
40
+ /** @internal Every ATTACHED node named `name` (undefined = none). */
41
+ _nodesNamed(name: string): ReadonlySet<Node> | undefined;
42
+ /**
43
+ * @internal Opaque per-frame scratch the 3d layer parks its shared moving-body
44
+ * snapshot on (kept here so it is per-tree-instance, never a module global —
45
+ * core stays three-free, the 3d resolver owns the shape).
46
+ */
47
+ _frameScratch: unknown;
48
+ get root(): Node | null;
49
+ /** The engine driving this tree (set by Engine.setScene), or null. */
50
+ get engine(): Engine | null;
51
+ /** @internal */
52
+ _setEngine(engine: Engine | null): void;
53
+ setRoot(node: Node): void;
54
+ update(dt: number): void;
55
+ fixedUpdate(dt: number): void;
56
+ getNodesInGroup(group: string): Node[];
57
+ /** Call `method(...args)` on every group member that implements it. */
58
+ callGroup(group: string, method: string, ...args: unknown[]): void;
59
+ /** @internal */
60
+ _queueFree(node: Node): void;
61
+ /** @internal Called by Node.free(): a freed root must not be re-drivable. */
62
+ _detachRoot(node: Node): void;
63
+ private flushFreeQueue;
64
+ }
65
+ //#endregion
3
66
  //#region src/core/signal.d.ts
4
67
  /**
5
68
  * Minimal typed observer used for every engine event (Godot signal semantics).
@@ -20,6 +83,116 @@ declare class Signal<Args extends unknown[] = unknown[]> {
20
83
  get connectionCount(): number;
21
84
  }
22
85
  //#endregion
86
+ //#region src/core/node.d.ts
87
+ /** Lifecycle hooks a SceneTree drives. Wired in scene-tree.ts. */
88
+ interface NodeLifecycle {
89
+ onEnterTree(): void;
90
+ onReady(): void;
91
+ onExitTree(): void;
92
+ update(dt: number): void;
93
+ fixedUpdate(dt: number): void;
94
+ }
95
+ /**
96
+ * Base class of everything in an Incanto scene (Godot's Node model).
97
+ *
98
+ * Pure data + tree structure: no rendering, no DOM, no three.js — renderer
99
+ * adapters subscribe from the outside.
100
+ */
101
+ declare class Node implements NodeLifecycle {
102
+ static readonly typeName: string;
103
+ /**
104
+ * Signals this node type can emit, declared statically (merged up the class
105
+ * hierarchy). A behavior's `static signals` are declared onto its node at
106
+ * load. Emitting or subscribing an UNDECLARED signal is a hard error —
107
+ * a typo'd signal name must fail loudly, never silently never-fire.
108
+ */
109
+ static readonly signals?: readonly string[];
110
+ private _name;
111
+ private _parent;
112
+ private readonly _children;
113
+ private readonly _groups;
114
+ private readonly _signals;
115
+ private _declared;
116
+ private _tree;
117
+ private _ready;
118
+ /**
119
+ * Optional STABLE identifier (scene JSON `uid`) — unlike names (unique only
120
+ * among siblings) a uid is unique across the whole scene, so scripts and
121
+ * tools can address a node no matter where it moves.
122
+ */
123
+ uid: string | null;
124
+ /** Free-form JSON identity for game logic (e.g. `{kind: 'ITEM', value: 10}`). */
125
+ tags: Record<string, unknown>;
126
+ /** Behavior attachment blob from scene JSON (resolved by the loader). */
127
+ script: JsonObject | null;
128
+ /** The resolved behavior instance (set by the loader from `script`). */
129
+ behavior: Behavior | null;
130
+ /** Replication config blob from scene JSON (interpreted in M6; preserved until then). */
131
+ network: JsonObject | null;
132
+ constructor(name?: string);
133
+ get name(): string;
134
+ set name(value: string);
135
+ get parent(): Node | null;
136
+ get children(): readonly Node[];
137
+ get groups(): ReadonlySet<string>;
138
+ /** The SceneTree this node is attached to, or null while detached. */
139
+ get tree(): SceneTree | null;
140
+ /** Whether onReady has run (it runs at most once per instance). */
141
+ get isReady(): boolean;
142
+ addChild<T extends Node>(child: T): T;
143
+ removeChild(child: Node): void;
144
+ reparent(newParent: Node): void;
145
+ findChild(name: string, recursive?: boolean): Node | null;
146
+ /** Topmost ancestor (the node itself when detached). */
147
+ getRoot(): Node;
148
+ /** Depth-first search of THIS subtree for the node carrying `uid`. */
149
+ getNodeByUid(uid: string): Node | null;
150
+ /** Every node in THIS subtree named `name` — names repeat, so a list. */
151
+ getNodesByName(name: string): Node[];
152
+ getPath(): string;
153
+ getNode(path: string): Node;
154
+ getNodeOrNull(path: string): Node | null;
155
+ private resolve;
156
+ addToGroup(group: string): void;
157
+ removeFromGroup(group: string): void;
158
+ isInGroup(group: string): boolean;
159
+ private declaredSignals;
160
+ /** Declare an ad-hoc signal on THIS instance (static `signals` covers types). */
161
+ declareSignal(name: string): void;
162
+ /** Every signal this instance may emit (static + behavior + ad-hoc). */
163
+ declaredSignalNames(): string[];
164
+ private assertDeclared;
165
+ /** Get the named DECLARED signal (creating its Signal object on demand). */
166
+ signal(name: string): Signal<unknown[]>;
167
+ on(signal: string, fn: SignalListener<unknown[]>, opts?: {
168
+ once?: boolean;
169
+ }): () => void;
170
+ off(signal: string, fn: SignalListener<unknown[]>): void;
171
+ emit(signal: string, ...args: unknown[]): void;
172
+ /**
173
+ * Defer destruction to the end of the current update pass (flushed by the
174
+ * SceneTree). Frees immediately when detached from any tree.
175
+ */
176
+ queueFree(): void;
177
+ /** Immediately detach and tear down this node and its children. */
178
+ free(): void;
179
+ /** @internal */
180
+ _propagateEnterTree(tree: SceneTree): void;
181
+ /** @internal */
182
+ _propagateReady(): void;
183
+ /** @internal */
184
+ _propagateExitTree(): void;
185
+ /** @internal */
186
+ _propagateUpdate(dt: number): void;
187
+ /** @internal */
188
+ _propagateFixedUpdate(dt: number): void;
189
+ onEnterTree(): void;
190
+ onReady(): void;
191
+ onExitTree(): void;
192
+ update(_dt: number): void;
193
+ fixedUpdate(_dt: number): void;
194
+ }
195
+ //#endregion
23
196
  //#region src/core/audio/buses.d.ts
24
197
  /** The two volume buses every sound routes through (plus the global `master`). */
25
198
  type BusName = "sfx" | "music";
@@ -264,6 +437,30 @@ declare function spatialGain(distance: number, params: SpatialParams): number;
264
437
  */
265
438
  declare function spatialPan(sourcePos: Vec3, listener: Listener): number;
266
439
  //#endregion
440
+ //#region src/core/audio/voice.d.ts
441
+ /**
442
+ * Continuous parametric voices — the engine hum, wind rush and thruster
443
+ * roar that one-shot SFX can't do (racing-3d hand-built exactly this graph).
444
+ * A voice is a small WebAudio patch you retune every frame:
445
+ *
446
+ * const voice = engine.sfx.startVoice('engine');
447
+ * voice.set({ pitch: rpm / 4000, volume: throttle * 0.6 });
448
+ * voice.stop(); // fades out and frees the graph
449
+ *
450
+ * Headless (no AudioContext) the handle is inert — same call sites, no ifs.
451
+ */
452
+ type VoicePreset = "engine" | "wind" | "hum" | "noise";
453
+ interface Voice {
454
+ /** Retune live. pitch 1 = the preset's base; volume 0..1. */
455
+ set(params: {
456
+ pitch?: number;
457
+ volume?: number;
458
+ }): void;
459
+ /** Fade out over `seconds` (default 0.15) and free the nodes. */
460
+ stop(seconds?: number): void;
461
+ readonly stopped: boolean;
462
+ }
463
+ //#endregion
267
464
  //#region src/core/audio/webaudio-sfx.d.ts
268
465
  /** A spatial emitter: where the sound is + where the listener is. */
269
466
  interface SpatialPlay {
@@ -296,6 +493,12 @@ declare class SfxEngine {
296
493
  /** True once a backend is present (lazily created on first use). */
297
494
  get available(): boolean;
298
495
  private ensure;
496
+ /**
497
+ * Start a continuous parametric voice (engine hum, wind, thrusters) —
498
+ * retune it per frame with `voice.set({ pitch, volume })`, `stop()` when
499
+ * done. Headless returns an inert handle (same call sites, no ifs).
500
+ */
501
+ startVoice(preset: VoicePreset, gain?: number): Voice;
299
502
  /** Resume a gesture-suspended context (wired to the first user gesture). */
300
503
  unlock(): void;
301
504
  /**
@@ -477,179 +680,6 @@ declare class LogManager {
477
680
  private push;
478
681
  }
479
682
  //#endregion
480
- //#region src/core/scene-tree.d.ts
481
- /**
482
- * Owns a node tree and drives its lifecycle:
483
- *
484
- * - `setRoot` → onEnterTree parent-first, then onReady children-first (once per instance)
485
- * - `update`/`fixedUpdate` → parent-first traversal, then flush of queued frees
486
- * - group queries across attached nodes
487
- *
488
- * Headless by design — tests step it manually; the render loop (M2) calls it.
489
- */
490
- declare class SceneTree {
491
- private _root;
492
- private _engine;
493
- private readonly freeQueue;
494
- /**
495
- * name → attached nodes with that name. Maintained by Node enter/exit/rename
496
- * so `%name` targeting and root-level getNodesByName are O(1) — behaviors
497
- * resolve targets every frame and a full-tree walk per chaser melted horde
498
- * scenes (N chasers × whole tree, 60×/s).
499
- */
500
- private readonly _nameIndex;
501
- /**
502
- * Bumped whenever ANY node attaches or detaches — cheap "did the tree
503
- * change shape?" check for per-step body gathers and similar caches.
504
- */
505
- _structureVersion: number;
506
- private _frameId;
507
- /**
508
- * Monotonic update-frame counter — bumped once per `update()`. Per-frame
509
- * caches (e.g. the 3d grass-bender body gather) key off this so they rebuild
510
- * at most once a frame regardless of node visitation order.
511
- */
512
- get frameId(): number;
513
- /** @internal Node attach hook. */
514
- _indexName(node: Node): void;
515
- /** @internal Node detach/rename hook (`name` = the entry to remove). */
516
- _unindexName(node: Node, name?: string): void;
517
- /** @internal Every ATTACHED node named `name` (undefined = none). */
518
- _nodesNamed(name: string): ReadonlySet<Node> | undefined;
519
- /**
520
- * @internal Opaque per-frame scratch the 3d layer parks its shared moving-body
521
- * snapshot on (kept here so it is per-tree-instance, never a module global —
522
- * core stays three-free, the 3d resolver owns the shape).
523
- */
524
- _frameScratch: unknown;
525
- get root(): Node | null;
526
- /** The engine driving this tree (set by Engine.setScene), or null. */
527
- get engine(): Engine | null;
528
- /** @internal */
529
- _setEngine(engine: Engine | null): void;
530
- setRoot(node: Node): void;
531
- update(dt: number): void;
532
- fixedUpdate(dt: number): void;
533
- getNodesInGroup(group: string): Node[];
534
- /** Call `method(...args)` on every group member that implements it. */
535
- callGroup(group: string, method: string, ...args: unknown[]): void;
536
- /** @internal */
537
- _queueFree(node: Node): void;
538
- /** @internal Called by Node.free(): a freed root must not be re-drivable. */
539
- _detachRoot(node: Node): void;
540
- private flushFreeQueue;
541
- }
542
- //#endregion
543
- //#region src/core/node.d.ts
544
- /** Lifecycle hooks a SceneTree drives. Wired in scene-tree.ts. */
545
- interface NodeLifecycle {
546
- onEnterTree(): void;
547
- onReady(): void;
548
- onExitTree(): void;
549
- update(dt: number): void;
550
- fixedUpdate(dt: number): void;
551
- }
552
- /**
553
- * Base class of everything in an Incanto scene (Godot's Node model).
554
- *
555
- * Pure data + tree structure: no rendering, no DOM, no three.js — renderer
556
- * adapters subscribe from the outside.
557
- */
558
- declare class Node implements NodeLifecycle {
559
- static readonly typeName: string;
560
- /**
561
- * Signals this node type can emit, declared statically (merged up the class
562
- * hierarchy). A behavior's `static signals` are declared onto its node at
563
- * load. Emitting or subscribing an UNDECLARED signal is a hard error —
564
- * a typo'd signal name must fail loudly, never silently never-fire.
565
- */
566
- static readonly signals?: readonly string[];
567
- private _name;
568
- private _parent;
569
- private readonly _children;
570
- private readonly _groups;
571
- private readonly _signals;
572
- private _declared;
573
- private _tree;
574
- private _ready;
575
- /**
576
- * Optional STABLE identifier (scene JSON `uid`) — unlike names (unique only
577
- * among siblings) a uid is unique across the whole scene, so scripts and
578
- * tools can address a node no matter where it moves.
579
- */
580
- uid: string | null;
581
- /** Free-form JSON identity for game logic (e.g. `{kind: 'ITEM', value: 10}`). */
582
- tags: Record<string, unknown>;
583
- /** Behavior attachment blob from scene JSON (resolved by the loader). */
584
- script: JsonObject | null;
585
- /** The resolved behavior instance (set by the loader from `script`). */
586
- behavior: Behavior | null;
587
- /** Replication config blob from scene JSON (interpreted in M6; preserved until then). */
588
- network: JsonObject | null;
589
- constructor(name?: string);
590
- get name(): string;
591
- set name(value: string);
592
- get parent(): Node | null;
593
- get children(): readonly Node[];
594
- get groups(): ReadonlySet<string>;
595
- /** The SceneTree this node is attached to, or null while detached. */
596
- get tree(): SceneTree | null;
597
- /** Whether onReady has run (it runs at most once per instance). */
598
- get isReady(): boolean;
599
- addChild<T extends Node>(child: T): T;
600
- removeChild(child: Node): void;
601
- reparent(newParent: Node): void;
602
- findChild(name: string, recursive?: boolean): Node | null;
603
- /** Topmost ancestor (the node itself when detached). */
604
- getRoot(): Node;
605
- /** Depth-first search of THIS subtree for the node carrying `uid`. */
606
- getNodeByUid(uid: string): Node | null;
607
- /** Every node in THIS subtree named `name` — names repeat, so a list. */
608
- getNodesByName(name: string): Node[];
609
- getPath(): string;
610
- getNode(path: string): Node;
611
- getNodeOrNull(path: string): Node | null;
612
- private resolve;
613
- addToGroup(group: string): void;
614
- removeFromGroup(group: string): void;
615
- isInGroup(group: string): boolean;
616
- private declaredSignals;
617
- /** Declare an ad-hoc signal on THIS instance (static `signals` covers types). */
618
- declareSignal(name: string): void;
619
- /** Every signal this instance may emit (static + behavior + ad-hoc). */
620
- declaredSignalNames(): string[];
621
- private assertDeclared;
622
- /** Get the named DECLARED signal (creating its Signal object on demand). */
623
- signal(name: string): Signal<unknown[]>;
624
- on(signal: string, fn: SignalListener<unknown[]>, opts?: {
625
- once?: boolean;
626
- }): () => void;
627
- off(signal: string, fn: SignalListener<unknown[]>): void;
628
- emit(signal: string, ...args: unknown[]): void;
629
- /**
630
- * Defer destruction to the end of the current update pass (flushed by the
631
- * SceneTree). Frees immediately when detached from any tree.
632
- */
633
- queueFree(): void;
634
- /** Immediately detach and tear down this node and its children. */
635
- free(): void;
636
- /** @internal */
637
- _propagateEnterTree(tree: SceneTree): void;
638
- /** @internal */
639
- _propagateReady(): void;
640
- /** @internal */
641
- _propagateExitTree(): void;
642
- /** @internal */
643
- _propagateUpdate(dt: number): void;
644
- /** @internal */
645
- _propagateFixedUpdate(dt: number): void;
646
- onEnterTree(): void;
647
- onReady(): void;
648
- onExitTree(): void;
649
- update(_dt: number): void;
650
- fixedUpdate(_dt: number): void;
651
- }
652
- //#endregion
653
683
  //#region src/core/scene/scene.d.ts
654
684
  /**
655
685
  * A loaded, live scene: the node tree plus everything needed to round-trip
@@ -773,6 +803,13 @@ declare class Engine {
773
803
  * timers, behaviors all breathe together. See `gameplay` `hitStop()`.
774
804
  */
775
805
  timeScale: number;
806
+ /**
807
+ * The node the dev overlay has selected (renderers draw its bounding box
808
+ * as an orange outline in the game view so you can SEE what you picked).
809
+ * Null when nothing is selected; cleared automatically when it leaves the
810
+ * tree. Set by the debug overlay — games normally never touch this.
811
+ */
812
+ debugSelection: Node | null;
776
813
  private _time;
777
814
  private _unscaledTime;
778
815
  /**
@@ -913,4 +950,4 @@ declare function registeredBehaviors(): string[];
913
950
  /** Test isolation helper. */
914
951
  declare function clearBehaviors(): void;
915
952
  //#endregion
916
- export { BusName as $, LogManager as A, spatialGain as B, RendererStats as C, SceneTree as D, NodeLifecycle as E, Listener as F, SfxWave as G, SFX_PRESETS as H, ROLLOFF_MODELS as I, MusicBackend as J, SynthOptions as K, RolloffModel as L, SfxEngine as M, SfxPlayOptions as N, LogEntry as O, isAudioContextAvailable as P, AudioBuses as Q, SpatialParams as R, GameStats as S, Node as T, SFX_PRESET_NAMES as U, spatialPan as V, SfxParams as W, MusicTrack as X, MusicManager as Y, PlayMusicOptions as Z, registeredTypes as _, registerBehavior as a, Scheduler as b, PropDef as c, createNode as d, Signal as et, getNodeSchema as f, registerNode as g, mergeStaticSignals as h, getBehavior as i, InputMap as j, LogLevel as k, PropSchema as l, getNodeType as m, BehaviorCtor as n, registeredBehaviors as o, getNodeSignals as p, synthSfx as q, clearBehaviors as r, NodeCtor as s, Behavior as t, SignalListener as tt, clearRegistry as u, Engine as v, Scene as w, EngineStats as x, EngineOptions as y, Vec3 as z };
953
+ export { Node as $, SfxPlayOptions as A, spatialPan as B, RendererStats as C, LogManager as D, LogLevel as E, ROLLOFF_MODELS as F, SynthOptions as G, SFX_PRESET_NAMES as H, RolloffModel as I, MusicManager as J, synthSfx as K, SpatialParams as L, Voice as M, VoicePreset as N, InputMap as O, Listener as P, BusName as Q, Vec3 as R, GameStats as S, LogEntry as T, SfxParams as U, SFX_PRESETS as V, SfxWave as W, PlayMusicOptions as X, MusicTrack as Y, AudioBuses as Z, registeredTypes as _, registerBehavior as a, Scheduler as b, PropDef as c, createNode as d, NodeLifecycle as et, getNodeSchema as f, registerNode as g, mergeStaticSignals as h, getBehavior as i, isAudioContextAvailable as j, SfxEngine as k, PropSchema as l, getNodeType as m, BehaviorCtor as n, SignalListener as nt, registeredBehaviors as o, getNodeSignals as p, MusicBackend as q, clearBehaviors as r, SceneTree as rt, NodeCtor as s, Behavior as t, Signal as tt, clearRegistry as u, Engine as v, Scene as w, EngineStats as x, EngineOptions as y, spatialGain as z };
@@ -1,14 +1,13 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
2
  import { _ as registerBehavior, n as loadScene } from "./loader-B4OEXDZ8.js";
3
- import { s as Engine } from "./particle-sim-CrTE7c02.js";
3
+ import { l as AudioPlayer, u as Engine } from "./register-DRPIjrKG.js";
4
4
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
5
- import { s as AudioPlayer } from "./register-Dl_ixIJe.js";
6
5
  import { i as resolveRendering, n as attachTouchControls } from "./touch-031PxtCR.js";
7
- import { n as registerGameplayBehaviors } from "./gameplay-CDFgSG6z.js";
6
+ import { n as registerGameplayBehaviors } from "./gameplay-DPMgZk9W.js";
8
7
  import { t as debugSources } from "./debug-draw-CZmOYjL2.js";
9
- import { O as PhysicsBody3D, c as ModelInstance3D, d as DirectionalLight3D, j as Node3D, t as registerNodes3D, y as Camera3D } from "./register-CscQqB7V.js";
10
- import { n as enablePhysics3D } from "./physics-3d-CDjuhtt_.js";
11
- import { ACESFilmicToneMapping, AmbientLight, BufferAttribute, BufferGeometry, Color, DepthTexture, EquirectangularReflectionMapping, FloatType, Fog, HalfFloatType, LineBasicMaterial, LineSegments, Matrix4, Mesh, PCFShadowMap, PMREMGenerator, PerspectiveCamera, PlaneGeometry, Quaternion, Raycaster, Scene, ShaderMaterial, Vector2, Vector3, WebGLRenderTarget, WebGLRenderer } from "three";
8
+ import { M as Node3D, b as Camera3D, f as DirectionalLight3D, k as PhysicsBody3D, l as ModelInstance3D, t as registerNodes3D } from "./register-Cbs7QxoV.js";
9
+ import { n as enablePhysics3D } from "./physics-3d-CvHr31d4.js";
10
+ import { ACESFilmicToneMapping, AmbientLight, Box3, BufferAttribute, BufferGeometry, Color, DepthTexture, EquirectangularReflectionMapping, FloatType, Fog, HalfFloatType, LineBasicMaterial, LineSegments, Matrix4, Mesh, PCFShadowMap, PMREMGenerator, PerspectiveCamera, PlaneGeometry, Quaternion, Raycaster, Scene, ShaderMaterial, Vector2, Vector3, WebGLRenderTarget, WebGLRenderer } from "three";
12
11
  import { VRMLoaderPlugin, VRMUtils } from "@pixiv/three-vrm";
13
12
  import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
14
13
  import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
@@ -1073,6 +1072,54 @@ function parseIblIntensity(value) {
1073
1072
  return value;
1074
1073
  }
1075
1074
  //#endregion
1075
+ //#region src/3d/selection-outline.ts
1076
+ /**
1077
+ * Where IS the node the dev-overlay selected? — world-space outline segments
1078
+ * for the renderer's highlight pass. A renderable node gets its actual
1079
+ * bounding box (children included); a node with no visual extent gets a
1080
+ * small marker cube at its world position so it is still findable.
1081
+ */
1082
+ const box = new Box3();
1083
+ const size = new Vector3();
1084
+ const MARKER_HALF = .4;
1085
+ /** 12 box edges = 24 points × xyz. */
1086
+ const OUT = new Float32Array(72);
1087
+ function selectionOutline3D(node) {
1088
+ if (!(node instanceof Node3D)) return null;
1089
+ const obj = node._ensureObject3D();
1090
+ obj.updateWorldMatrix(true, true);
1091
+ box.setFromObject(obj);
1092
+ box.getSize(size);
1093
+ if (!Number.isFinite(size.x) || size.x === 0 && size.y === 0 && size.z === 0) {
1094
+ const p = new Vector3().setFromMatrixPosition(obj.matrixWorld);
1095
+ box.min.set(p.x - MARKER_HALF, p.y - MARKER_HALF, p.z - MARKER_HALF);
1096
+ box.max.set(p.x + MARKER_HALF, p.y + MARKER_HALF, p.z + MARKER_HALF);
1097
+ } else box.expandByScalar(.02);
1098
+ const { min, max } = box;
1099
+ let i = 0;
1100
+ const seg = (x1, y1, z1, x2, y2, z2) => {
1101
+ OUT[i++] = x1;
1102
+ OUT[i++] = y1;
1103
+ OUT[i++] = z1;
1104
+ OUT[i++] = x2;
1105
+ OUT[i++] = y2;
1106
+ OUT[i++] = z2;
1107
+ };
1108
+ seg(min.x, min.y, min.z, max.x, min.y, min.z);
1109
+ seg(max.x, min.y, min.z, max.x, min.y, max.z);
1110
+ seg(max.x, min.y, max.z, min.x, min.y, max.z);
1111
+ seg(min.x, min.y, max.z, min.x, min.y, min.z);
1112
+ seg(min.x, max.y, min.z, max.x, max.y, min.z);
1113
+ seg(max.x, max.y, min.z, max.x, max.y, max.z);
1114
+ seg(max.x, max.y, max.z, min.x, max.y, max.z);
1115
+ seg(min.x, max.y, max.z, min.x, max.y, min.z);
1116
+ seg(min.x, min.y, min.z, min.x, max.y, min.z);
1117
+ seg(max.x, min.y, min.z, max.x, max.y, min.z);
1118
+ seg(max.x, min.y, max.z, max.x, max.y, max.z);
1119
+ seg(min.x, min.y, max.z, min.x, max.y, max.z);
1120
+ return OUT;
1121
+ }
1122
+ //#endregion
1076
1123
  //#region src/3d/sync.ts
1077
1124
  function isSpatialConsumer(node) {
1078
1125
  return node.spatial === true && typeof node._setSpatialPose === "function";
@@ -1449,9 +1496,31 @@ var Renderer3D = class {
1449
1496
  this.debugLines.renderOrder = 9999;
1450
1497
  this.debugLines.visible = false;
1451
1498
  this.threeScene.add(this.debugLines);
1499
+ this.selectionLines = new LineSegments(new BufferGeometry(), new LineBasicMaterial({
1500
+ color: "#ffb020",
1501
+ transparent: true,
1502
+ depthTest: false
1503
+ }));
1504
+ this.selectionLines.frustumCulled = false;
1505
+ this.selectionLines.renderOrder = 1e4;
1506
+ this.selectionLines.visible = false;
1507
+ this.threeScene.add(this.selectionLines);
1452
1508
  this.disconnect = this.engine.updated.connect(() => this.render());
1453
1509
  }
1454
1510
  debugLines;
1511
+ selectionLines;
1512
+ syncSelectionOutline() {
1513
+ const node = this.engine.debugSelection;
1514
+ if (node && node.tree !== this.engine.scene?.tree) this.engine.debugSelection = null;
1515
+ const current = this.engine.debugSelection;
1516
+ const vertices = current ? selectionOutline3D(current) : null;
1517
+ this.selectionLines.visible = vertices !== null;
1518
+ if (vertices) {
1519
+ this.selectionLines.geometry.setAttribute("position", new BufferAttribute(vertices, 3));
1520
+ const attr = this.selectionLines.geometry.getAttribute("position");
1521
+ attr.needsUpdate = true;
1522
+ }
1523
+ }
1455
1524
  syncDebugLines() {
1456
1525
  let vertices = null;
1457
1526
  for (const source of debugSources("3d")) {
@@ -1465,6 +1534,7 @@ var Renderer3D = class {
1465
1534
  const scene = this.engine.scene;
1466
1535
  if (!scene) return;
1467
1536
  this.syncDebugLines();
1537
+ this.syncSelectionOutline();
1468
1538
  if (scene.assets && !this.loadedAssetScenes.has(scene)) {
1469
1539
  this.assets.load(scene.assets);
1470
1540
  this.loadedAssetScenes.add(scene);
@@ -1,13 +1,12 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
2
  import { _ as registerBehavior, n as loadScene, o as computeViewport, s as resolveViewport } from "./loader-B4OEXDZ8.js";
3
- import { s as Engine } from "./particle-sim-CrTE7c02.js";
3
+ import { l as AudioPlayer, u as Engine } from "./register-DRPIjrKG.js";
4
4
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
5
- import { s as AudioPlayer } from "./register-Dl_ixIJe.js";
6
5
  import { i as resolveRendering, n as attachTouchControls } from "./touch-031PxtCR.js";
7
- import { n as registerGameplayBehaviors } from "./gameplay-CDFgSG6z.js";
8
- import { g as Node2D, n as UILayer, p as PhysicsBody2D, s as Camera2D, t as registerNodes2D } from "./register-ClKnoILk.js";
6
+ import { n as registerGameplayBehaviors } from "./gameplay-DPMgZk9W.js";
7
+ import { g as PhysicsBody2D, n as UILayer, t as registerNodes2D, u as Camera2D, y as Node2D } from "./register-Cqjxrfq0.js";
9
8
  import { t as debugSources } from "./debug-draw-CZmOYjL2.js";
10
- import { n as enablePhysics2D } from "./physics-2d-DmQ540uR.js";
9
+ import { n as enablePhysics2D } from "./physics-2d-q2N362ph.js";
11
10
  import { Box3, BufferAttribute, BufferGeometry, Color, LineBasicMaterial, LineSegments, LinearFilter, NearestFilter, OrthographicCamera, Raycaster, SRGBColorSpace, Scene, TextureLoader, Vector2, Vector3, WebGLRenderer } from "three";
12
11
  //#region src/2d/assets.ts
13
12
  /**
@@ -101,6 +100,58 @@ function worldFromScreen(view, sx, sy) {
101
100
  };
102
101
  }
103
102
  //#endregion
103
+ //#region src/2d/selection-outline.ts
104
+ /** 2D twin of the 3D selection outline: a world-space (px, y-down) rectangle
105
+ * around the selected node's rendered extent, or a marker square. */
106
+ const box = new Box3();
107
+ const size = new Vector3();
108
+ const MARKER_HALF = 12;
109
+ /** 4 rect edges = 8 points × xy. */
110
+ const OUT = new Float32Array(16);
111
+ function selectionOutline2D(node) {
112
+ if (!(node instanceof Node2D)) return null;
113
+ const obj = node._ensureObject2D();
114
+ obj.updateWorldMatrix(true, true);
115
+ box.setFromObject(obj);
116
+ box.getSize(size);
117
+ let minX;
118
+ let minY;
119
+ let maxX;
120
+ let maxY;
121
+ if (!Number.isFinite(size.x) || size.x === 0 && size.y === 0) {
122
+ const p = new Vector3().setFromMatrixPosition(obj.matrixWorld);
123
+ minX = p.x - MARKER_HALF;
124
+ maxX = p.x + MARKER_HALF;
125
+ minY = p.y - MARKER_HALF;
126
+ maxY = p.y + MARKER_HALF;
127
+ } else {
128
+ minX = box.min.x - 2;
129
+ maxX = box.max.x + 2;
130
+ minY = box.min.y - 2;
131
+ maxY = box.max.y + 2;
132
+ }
133
+ const pts = [
134
+ minX,
135
+ minY,
136
+ maxX,
137
+ minY,
138
+ maxX,
139
+ minY,
140
+ maxX,
141
+ maxY,
142
+ maxX,
143
+ maxY,
144
+ minX,
145
+ maxY,
146
+ minX,
147
+ maxY,
148
+ minX,
149
+ minY
150
+ ];
151
+ OUT.set(pts);
152
+ return OUT;
153
+ }
154
+ //#endregion
104
155
  //#region src/2d/sync.ts
105
156
  function createSync2DScratch() {
106
157
  return {
@@ -217,14 +268,44 @@ var Renderer2D = class {
217
268
  });
218
269
  this.webgl.setPixelRatio(rendering.pixelRatio);
219
270
  this.webgl.info.autoReset = false;
220
- this.debugLines = new LineSegments(new BufferGeometry(), new LineBasicMaterial({ color: "#00ff6e" }));
271
+ this.debugLines = new LineSegments(new BufferGeometry(), new LineBasicMaterial({
272
+ color: "#00ff6e",
273
+ transparent: true,
274
+ depthTest: false
275
+ }));
221
276
  this.debugLines.frustumCulled = false;
222
277
  this.debugLines.renderOrder = 9999;
223
278
  this.debugLines.visible = false;
224
279
  this.worldScene.add(this.debugLines);
280
+ this.selectionLines = new LineSegments(new BufferGeometry(), new LineBasicMaterial({
281
+ color: "#ffb020",
282
+ transparent: true,
283
+ depthTest: false
284
+ }));
285
+ this.selectionLines.frustumCulled = false;
286
+ this.selectionLines.renderOrder = 1e4;
287
+ this.selectionLines.visible = false;
288
+ this.worldScene.add(this.selectionLines);
225
289
  this.disconnect = this.engine.updated.connect(() => this.render());
226
290
  }
227
291
  debugLines;
292
+ selectionLines;
293
+ syncSelectionOutline() {
294
+ const node = this.engine.debugSelection;
295
+ if (node && node.tree !== this.engine.scene?.tree) this.engine.debugSelection = null;
296
+ const current = this.engine.debugSelection;
297
+ const vertices = current ? selectionOutline2D(current) : null;
298
+ this.selectionLines.visible = vertices !== null;
299
+ if (!vertices) return;
300
+ const xyz = new Float32Array(vertices.length / 2 * 3);
301
+ for (let i = 0, j = 0; i < vertices.length; i += 2, j += 3) {
302
+ xyz[j] = vertices[i];
303
+ xyz[j + 1] = vertices[i + 1];
304
+ xyz[j + 2] = 0;
305
+ }
306
+ this.selectionLines.geometry.setAttribute("position", new BufferAttribute(xyz, 3));
307
+ this.selectionLines.geometry.getAttribute("position").needsUpdate = true;
308
+ }
228
309
  syncDebugLines() {
229
310
  let vertices = null;
230
311
  for (const source of debugSources("2d")) {
@@ -246,6 +327,7 @@ var Renderer2D = class {
246
327
  if (!scene) return;
247
328
  this.webgl.info.reset();
248
329
  this.syncDebugLines();
330
+ this.syncSelectionOutline();
249
331
  if (scene.assets && !this.loadedAssetScenes.has(scene)) {
250
332
  this.assets.load(scene.assets);
251
333
  this.loadedAssetScenes.add(scene);
package/dist/debug.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as RendererStats, k as LogLevel, v as Engine } from "./behavior-e3kAmPGC.js";
1
+ import { C as RendererStats, E as LogLevel, v as Engine } from "./behavior-Bod1AyJS.js";
2
2
 
3
3
  //#region src/debug/panel.d.ts
4
4
  /** Minimal document surface the overlay needs (injectable for tests). */