incanto 0.50.0 → 0.52.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 (57) hide show
  1. package/dist/2d.d.ts +38 -2
  2. package/dist/2d.js +3 -3
  3. package/dist/3d.d.ts +4 -3
  4. package/dist/3d.js +4 -4
  5. package/dist/{behavior-62q0HWBO.d.ts → behavior-uPEuZrUB.d.ts} +47 -3
  6. package/dist/{create-game-BLDjy_PW.js → create-game-B_e9hJf7.js} +92 -10
  7. package/dist/{create-game-DqqxEax1.js → create-game-CGnoypjL.js} +6 -6
  8. package/dist/debug.d.ts +1 -1
  9. package/dist/debug.js +1 -1
  10. package/dist/{duplicate-CI9WF_bg.js → duplicate-B-OtSRFL.js} +1 -1
  11. package/dist/editor.js +1917 -1579
  12. package/dist/{environment-presets-XFuqu5jv.js → environment-presets-DSZwsKPs.js} +3 -3
  13. package/dist/{gameplay-DbaI313d.js → gameplay-B6jqvYeM.js} +163 -6
  14. package/dist/gameplay.d.ts +79 -2
  15. package/dist/gameplay.js +2 -2
  16. package/dist/index.d.ts +74 -5
  17. package/dist/index.js +7 -7
  18. package/dist/{loader-DwazzlQb.js → loader-B-Gft32x.js} +372 -23
  19. package/dist/{loader-CeyU_bm1.d.ts → loader-B9iTqs27.d.ts} +1 -1
  20. package/dist/net.d.ts +15 -410
  21. package/dist/net.js +1 -822
  22. package/dist/{pathfinding-C49JSNNq.d.ts → pathfinding-CAR9DjQQ.d.ts} +1 -1
  23. package/dist/{physics-2d-_9VBOHn6.js → physics-2d-Dns-oZlE.js} +7 -2
  24. package/dist/{physics-3d-DrpF5hcG.js → physics-3d-BhI0ehpe.js} +8 -3
  25. package/dist/react.d.ts +1 -1
  26. package/dist/react.js +1 -1
  27. package/dist/{register-uvaZj1KX.js → register-DVwlnZAZ.js} +20 -266
  28. package/dist/{register-BNPZYJmd.js → register-Trx7WHnD.js} +3 -3
  29. package/dist/{registry-IyWCGe4q.js → registry-C7u42TID.js} +23 -1
  30. package/dist/{replay-DYdy1wb0.d.ts → replay-BU1CCM15.d.ts} +1 -1
  31. package/dist/{replay-j-m6lJ4W.js → replay-BicPOMX0.js} +214 -2
  32. package/dist/split-screen-CZ9ccBBQ.js +1267 -0
  33. package/dist/split-screen-paxkQs_q.d.ts +441 -0
  34. package/dist/{src-B3HrKuAi.js → src-5gbZO47I.js} +1 -1
  35. package/dist/{teardown-BKTCzLek.js → teardown-ks3d5W9n.js} +2 -1
  36. package/dist/{test-D16igj4C.js → test-DAojuFdb.js} +119 -22
  37. package/dist/test.d.ts +60 -5
  38. package/dist/test.js +3 -3
  39. package/dist/vite.js +2 -2
  40. package/editor/assets/{agent8-t3kl5q9K.js → agent8-CCvckvbw.js} +1 -1
  41. package/editor/assets/{debug-Bu3eeAlO.js → debug-CLNCOnbc.js} +1 -1
  42. package/editor/assets/{index-Df5g8ofT.js → index-Cb5Brupb.js} +99 -92
  43. package/editor/index.html +1 -1
  44. package/package.json +1 -1
  45. package/skills/incanto-assets.md +4 -1
  46. package/skills/incanto-building-2d-games.md +6 -1
  47. package/skills/incanto-editor.md +48 -11
  48. package/skills/incanto-gameplay-behaviors.md +14 -5
  49. package/skills/incanto-multiplayer.md +39 -3
  50. package/skills/incanto-node-reference.md +13 -0
  51. package/skills/incanto-save-slots.md +134 -12
  52. package/skills/incanto-verifying-your-game.md +56 -0
  53. package/templates/agent8-server.ts +79 -2
  54. package/templates-app/beacon-isle-3d/package.json +1 -1
  55. package/templates-app/tps-3d/package.json +1 -1
  56. package/templates-app/village-quest-3d/package.json +1 -1
  57. package/dist/register-CB11yp21.js +0 -374
package/dist/2d.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { Et as Node, P as Scene$1, S as Scheduler, T as RendererStats, b as Engine, d as PropSchema, n as BehaviorCtor, w as GameStats } from "./behavior-62q0HWBO.js";
1
+ import { Dt as Node, P as Scene$1, S as Scheduler, T as RendererStats, b as Engine, d as PropSchema, n as BehaviorCtor, w as GameStats } from "./behavior-uPEuZrUB.js";
2
2
  import { n as DiagnosticSink, t as EditorSwitchOptions } from "./editor-switch-DAvWQeld.js";
3
3
  import { i as SceneJson, s as JsonObject } from "./schema-CFeioQRE.js";
4
- import { t as LoadSceneOptions } from "./loader-CeyU_bm1.js";
4
+ import { t as LoadSceneOptions } from "./loader-B9iTqs27.js";
5
5
  import { n as AnimationEntry } from "./sprite-animation-CMr6f1K2.js";
6
6
  import { t as ParticleSim } from "./particle-sim-BzJ1yxoE.js";
7
7
  import { Group, Mesh, Object3D, Scene, Texture } from "three";
@@ -36,6 +36,19 @@ declare class AssetStore2D {
36
36
  constructor(loader?: (url: string, callbacks?: TextureLoadCallbacks) => Texture, engine?: DiagnosticSink | null);
37
37
  /** Load (or extend with) a scene's `assets` declarations. Hard-validates each entry. */
38
38
  load(assets: Record<string, JsonObject>): void;
39
+ /**
40
+ * Every declared asset that FAILED to load, `$ref` and url included.
41
+ *
42
+ * The 3D store has answered this since 0.32.0 — the 2D one only tracked a
43
+ * per-key status, which the console printed and nothing else could read. A
44
+ * 404'd texture is invisible, and "invisible" is the one symptom an author
45
+ * cannot tell apart from "I put it in the wrong place".
46
+ */
47
+ errors(): Array<{
48
+ ref: string;
49
+ url: string;
50
+ error: string;
51
+ }>;
39
52
  /** Load status of a declared asset KEY (no '$'), or undefined if unknown. */
40
53
  status(key: string): AssetStatus | undefined;
41
54
  getTexture(ref: string): Texture;
@@ -206,6 +219,7 @@ declare class Physics2D {
206
219
  private readonly world;
207
220
  private readonly events;
208
221
  private readonly kcc;
222
+ private readonly disconnectScene;
209
223
  private readonly disconnect;
210
224
  private lastDt;
211
225
  private readonly optsGravity;
@@ -840,6 +854,8 @@ declare class Renderer2D {
840
854
  private syncSelectionOutline;
841
855
  private syncDebugLines;
842
856
  private render;
857
+ /** UI-pass extent from the last render — the space HUD nodes are posed in. */
858
+ private lastUi;
843
859
  private lastViewport;
844
860
  private lastView;
845
861
  /** The world view used by the LAST render (center/zoom/canvas size). */
@@ -862,6 +878,26 @@ declare class Renderer2D {
862
878
  * Hits resolve through `userData.incantoNode` and prefer higher renderOrder.
863
879
  */
864
880
  pick(sx: number, sy: number): Node | null;
881
+ private hit;
882
+ /** Does the UI pass draw this node (i.e. is it under a UILayer)? */
883
+ isUiNode(node: Node): boolean;
884
+ /**
885
+ * UI-space px → canvas px.
886
+ *
887
+ * A UILayer's children are posed in the UI pass's own space (origin top-left,
888
+ * `uiW × uiH`), NOT in world space — so mapping them with `screenFromWorld`
889
+ * puts them wherever the game camera happens to be looking.
890
+ */
891
+ screenFromUi(ux: number, uy: number): {
892
+ x: number;
893
+ y: number;
894
+ };
895
+ /** Canvas px → UI-space px (the inverse of `screenFromUi`). */
896
+ uiFromScreen(sx: number, sy: number): {
897
+ x: number;
898
+ y: number;
899
+ };
900
+ private uiView;
865
901
  /**
866
902
  * Canvas-pixel AABB of a node's rendered world-pass objects, or null when it
867
903
  * has no renderable extent (plain Nodes, empty containers).
package/dist/2d.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
2
- import { a as AssetStore2D, i as syncTree2D, r as Renderer2D, t as createGame2D } from "./create-game-BLDjy_PW.js";
3
- import { _ as RigidBody2D, a as parseCells, c as ColorRect2D, d as AnimatedSprite2D, f as Sprite2D, g as PhysicsBody2D, h as CharacterBody2D, i as mergeSolidRects, l as CharacterController2D, m as Area2D, n as UILayer, o as Particles2D, p as Joint2D, r as TileMap2D, s as Label, t as registerNodes2D, u as Camera2D, v as StaticBody2D, y as Node2D } from "./register-BNPZYJmd.js";
4
- import { n as enablePhysics2D, t as Physics2D } from "./physics-2d-_9VBOHn6.js";
2
+ import { a as AssetStore2D, i as syncTree2D, r as Renderer2D, t as createGame2D } from "./create-game-B_e9hJf7.js";
3
+ import { _ as RigidBody2D, a as parseCells, c as ColorRect2D, d as AnimatedSprite2D, f as Sprite2D, g as PhysicsBody2D, h as CharacterBody2D, i as mergeSolidRects, l as CharacterController2D, m as Area2D, n as UILayer, o as Particles2D, p as Joint2D, r as TileMap2D, s as Label, t as registerNodes2D, u as Camera2D, v as StaticBody2D, y as Node2D } from "./register-Trx7WHnD.js";
4
+ import { n as enablePhysics2D, t as Physics2D } from "./physics-2d-Dns-oZlE.js";
5
5
  //#region src/2d/library-sprite.ts
6
6
  /**
7
7
  * What a `CharacterController2D`/`3D` will ask a skin to play, and the clip in
package/dist/3d.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { D as QualityCaps, Et as Node, P as Scene$1, S as Scheduler, T as RendererStats, b as Engine, d as PropSchema, n as BehaviorCtor, w as GameStats } from "./behavior-62q0HWBO.js";
1
+ import { D as QualityCaps, Dt as Node, P as Scene$1, S as Scheduler, T as RendererStats, b as Engine, d as PropSchema, n as BehaviorCtor, w as GameStats } from "./behavior-uPEuZrUB.js";
2
2
  import { n as DiagnosticSink, t as EditorSwitchOptions } from "./editor-switch-DAvWQeld.js";
3
3
  import { i as SceneJson$1, s as JsonObject } from "./schema-CFeioQRE.js";
4
- import { t as LoadSceneOptions } from "./loader-CeyU_bm1.js";
4
+ import { t as LoadSceneOptions } from "./loader-B9iTqs27.js";
5
5
  import { t as ParticleSim } from "./particle-sim-BzJ1yxoE.js";
6
6
  import { a as GridCell, c as diffText, d as frameText, i as FrameStatsOptions, l as frameSignature, n as FrameSignature, o as SIGNATURE_GRID, r as FrameStats, s as diffSignatures, t as FrameDiff, u as frameStats } from "./frame-report-DZ70IY26.js";
7
- import { n as PathGrid, s as SpatialPose } from "./pathfinding-C49JSNNq.js";
7
+ import { n as PathGrid, s as SpatialPose } from "./pathfinding-CAR9DjQQ.js";
8
8
  import { AnimationClip, AnimationMixer, BufferGeometry, Color, DirectionalLight, Group, InstancedMesh, Mesh, MeshPhysicalMaterial, Object3D, PerspectiveCamera, Scene, ShaderMaterial, Texture, Vector3, WebGLRenderer } from "three";
9
9
  import { VRM } from "@pixiv/three-vrm";
10
10
  import { Sky } from "three/examples/jsm/objects/Sky.js";
@@ -439,6 +439,7 @@ declare class Physics3D {
439
439
  private readonly world;
440
440
  private readonly events;
441
441
  private readonly kcc;
442
+ private readonly disconnectScene;
442
443
  private readonly disconnect;
443
444
  private lastDt;
444
445
  private readonly optsGravity;
package/dist/3d.js CHANGED
@@ -1,9 +1,9 @@
1
- import { A as Water3D, F as PhysicsBody3D, I as RigidBody3D, L as StaticBody3D, M as WaterCutout3D, N as Area3D, P as CharacterBody3D, R as Node3D, V as WATER_MAX_RIPPLES, j as WATER_CUTOUT_MAX } from "./gameplay-DbaI313d.js";
2
- import { A as Terrain3D, B as keyboardIntensity, C as resolveFlowerDensity, D as BoneLookAt3D, E as Camera3D, F as InstancedMesh3D, G as acquireTexture, H as rigPose, I as MeshInstance3D, M as TERRAIN_THEMES, N as terrainThemeLayers, O as BoneAttachment3D, P as Joint3D, R as QUARTER_PITCH, S as Flowers3D, T as CharacterController3D, U as TextureCache3D, V as movementState, W as acquireOwnTexture, _ as LoftMesh3D, a as Tree3D, b as Foliage3D, c as buildRiverRings, d as riverCarveChannels, f as riverStepFor, g as ModelInstance3D, h as Particles3D, i as VoxelGrid3D, j as DEFAULT_TERRAIN_TEXTURE_BASE, k as Billboard3D, l as findRiverCoverageGaps, m as traceDownhillPath, n as registerNodes3D, o as Trail3D, p as smoothCourse, r as VOXEL_PALETTE, s as River3D, u as projectToRiver, v as DirectionalLight3D, w as FLOWER_VARIETIES, x as DENSITY_PRESETS, y as OmniLight3D, z as cameraRelative } from "./environment-presets-XFuqu5jv.js";
3
- import { a as Environment3D, c as parseEnvironment3D, d as AssetStore3D, i as syncTree, l as sunDirectionFromElevationAzimuth, o as setEnvironment3D, r as Renderer3D, s as horizonColorFromSky, t as createGame3D, u as sunDirectionFromSky } from "./create-game-DqqxEax1.js";
1
+ import { F as CharacterBody3D, H as WATER_MAX_RIPPLES, I as PhysicsBody3D, L as RigidBody3D, M as WATER_CUTOUT_MAX, N as WaterCutout3D, P as Area3D, R as StaticBody3D, j as Water3D, z as Node3D } from "./gameplay-B6jqvYeM.js";
2
+ import { A as Terrain3D, B as keyboardIntensity, C as resolveFlowerDensity, D as BoneLookAt3D, E as Camera3D, F as InstancedMesh3D, G as acquireTexture, H as rigPose, I as MeshInstance3D, M as TERRAIN_THEMES, N as terrainThemeLayers, O as BoneAttachment3D, P as Joint3D, R as QUARTER_PITCH, S as Flowers3D, T as CharacterController3D, U as TextureCache3D, V as movementState, W as acquireOwnTexture, _ as LoftMesh3D, a as Tree3D, b as Foliage3D, c as buildRiverRings, d as riverCarveChannels, f as riverStepFor, g as ModelInstance3D, h as Particles3D, i as VoxelGrid3D, j as DEFAULT_TERRAIN_TEXTURE_BASE, k as Billboard3D, l as findRiverCoverageGaps, m as traceDownhillPath, n as registerNodes3D, o as Trail3D, p as smoothCourse, r as VOXEL_PALETTE, s as River3D, u as projectToRiver, v as DirectionalLight3D, w as FLOWER_VARIETIES, x as DENSITY_PRESETS, y as OmniLight3D, z as cameraRelative } from "./environment-presets-DSZwsKPs.js";
3
+ import { a as Environment3D, c as parseEnvironment3D, d as AssetStore3D, i as syncTree, l as sunDirectionFromElevationAzimuth, o as setEnvironment3D, r as Renderer3D, s as horizonColorFromSky, t as createGame3D, u as sunDirectionFromSky } from "./create-game-CGnoypjL.js";
4
4
  import { a as frameSignature, n as diffSignatures, o as frameStats, r as diffText, s as frameText, t as SIGNATURE_GRID } from "./frame-report-njybhZon.js";
5
5
  import { n as splatWeights, t as buildHeightmap } from "./heightmap-CRK0M4jT.js";
6
- import { n as enablePhysics3D, t as Physics3D } from "./physics-3d-DrpF5hcG.js";
6
+ import { n as enablePhysics3D, t as Physics3D } from "./physics-3d-BhI0ehpe.js";
7
7
  //#region src/3d/model-verdict.ts
8
8
  /** Mixamo exports every bone as `mixamorigX`; the retargeter binds by that name. */
9
9
  const MIXAMO = /^mixamorig[:_]?/i;
@@ -151,6 +151,14 @@ declare class SceneTree {
151
151
  _queueFree(node: Node): void;
152
152
  /** @internal Called by Node.free(): a freed root must not be re-drivable. */
153
153
  _detachRoot(node: Node): void;
154
+ /**
155
+ * @internal Is this node already on its way out?
156
+ *
157
+ * A save taken from a `collected` handler runs BEFORE the deferred free, so
158
+ * the pickup that triggered the checkpoint is still in the tree — and came
159
+ * back on the next run while its score did not.
160
+ */
161
+ _isQueuedFree(node: Node): boolean;
154
162
  private flushFreeQueue;
155
163
  }
156
164
  //#endregion
@@ -968,6 +976,18 @@ interface RestoreReport {
968
976
  skipped: string[];
969
977
  /** How many behaviors actually took their state back. */
970
978
  restored: number;
979
+ /**
980
+ * How much state this tree HAD to give back — every behavior in it with a
981
+ * `serialize`, addressable or not.
982
+ *
983
+ * `restored: 0` is ambiguous on its own and the ambiguity is expensive: a
984
+ * hand-written scene with no uids saves `{}`, and the documented check
985
+ * (`if (report.missing.length)`) passes on it while the whole run is gone.
986
+ * `restored: 0, expected: 2` cannot be read as success.
987
+ */
988
+ expected: number;
989
+ /** Authored nodes the save says the run consumed, and that were freed again. */
990
+ freed: number;
971
991
  }
972
992
  /**
973
993
  * Every behavior in the tree that has something to say, by uid.
@@ -980,9 +1000,10 @@ interface RestoreReport {
980
1000
  * cannot be given back. The editor assigns a uid to every node it touches; a
981
1001
  * hand-written scene may not have.
982
1002
  */
983
- declare function captureBehaviors(root: Node): {
1003
+ declare function captureBehaviors(root: Node, source?: JsonValue): {
984
1004
  state: BehaviorState; /** Paths of nodes that had state to save and no uid to save it under. */
985
- unaddressable: string[];
1005
+ unaddressable: string[]; /** Behaviors in this tree that have something to save — addressable or not. */
1006
+ saveable: number;
986
1007
  };
987
1008
  /**
988
1009
  * Hand each behavior its state back.
@@ -1032,6 +1053,19 @@ declare class SaveSlots {
1032
1053
  * and the caller decides.
1033
1054
  */
1034
1055
  declare function behaviorsWithoutSave(root: Node): string[];
1056
+ /**
1057
+ * Behaviors that DO save, on nodes with no uid to save them under — the state
1058
+ * that goes nowhere.
1059
+ *
1060
+ * `incanto-check` warns about the built-ins it can recognise from the JSON
1061
+ * (Health, ScoreKeeper, Collector), but a scene file cannot be asked whether
1062
+ * YOUR behavior has a `serialize`. This can: it walks the live tree, so a quest
1063
+ * log or a wallet of your own is named too.
1064
+ *
1065
+ * The pair to read before shipping: `behaviorsWithoutSave` is "did you forget
1066
+ * to write serialize", this is "did you forget the uid that makes it count".
1067
+ */
1068
+ declare function savesWithoutUid(root: Node): string[];
1035
1069
  //#endregion
1036
1070
  //#region src/core/scene/scene.d.ts
1037
1071
  /**
@@ -1547,6 +1581,16 @@ interface PropDef {
1547
1581
  */
1548
1582
  nodePath?: boolean;
1549
1583
  /**
1584
+ * The prop must be given a value — an empty string is not a configuration.
1585
+ *
1586
+ * `Chase.target` and `FollowCamera.target` threw for this in `onReady`, which
1587
+ * is the browser, at the moment the level starts. Declared here it is a LOAD
1588
+ * error instead: `incanto-check` reports it while you are still writing the
1589
+ * scene, and the editor knows not to "unlink" a prop whose emptiness the
1590
+ * engine refuses.
1591
+ */
1592
+ required?: boolean;
1593
+ /**
1550
1594
  * For an OBJECT-valued prop shaped as a tagged union — `collider`, whose
1551
1595
  * `shape` decides which other keys are required. One declaration gives an
1552
1596
  * inspector both the dropdown AND something valid to write when the tag
@@ -1682,4 +1726,4 @@ declare function registeredBehaviors(): string[];
1682
1726
  /** Test isolation helper. */
1683
1727
  declare function clearBehaviors(): void;
1684
1728
  //#endregion
1685
- export { suggestLocale as $, SettingsValues as A, LogLevel as At, captureBehaviors as B, EngineStats as C, PlayMusicOptions as Ct, QualityCaps as D, NodeLifecycle as Dt, DeviceHints as E, Node as Et, BehaviorState as F, OrderGroup as G, SaveStore as H, RestoreReport as I, resolveOrderGroups as J, OrderGroupTable as K, SaveSlot as L, readDeviceHints as M, Signal as Mt, suggestQuality as N, SignalListener as Nt, QualityTier as O, SceneTree as Ot, Scene as P, T_PREFIX as Q, SaveSlots as R, Scheduler as S, MusicTrack as St, RendererStats as T, BusName as Tt, createSaveStore as U, restoreBehaviors as V, ORDER_GROUP_BASE as W, LocaleTables as X, BASE_LOCALE as Y, Localization as Z, mergeStaticSignals as _, SfxWave as _t, clearBehaviors as a, Voice as at, Engine as b, MusicBackend as bt, registeredBehaviors as c, ROLLOFF_MODELS as ct, PropSchema as d, Vec3 as dt, translationKey as et, clearRegistry as f, spatialGain as ft, getNodeType as g, SfxParams as gt, getNodeSignals as h, SFX_PRESET_NAMES as ht, behaviorSignals as i, isAudioContextAvailable as it, qualityEnvironment as j, LogManager as jt, Settings as k, LogEntry as kt, NodeCtor as l, RolloffModel as lt, getNodeSchema as m, SFX_PRESETS as mt, BehaviorCtor as n, SfxEngine as nt, getBehavior as o, VoicePreset as ot, createNode as p, spatialPan as pt, effectiveOrder as q, behaviorSchema as r, SfxPlayOptions as rt, registerBehavior as s, Listener as st, Behavior as t, InputMap as tt, PropDef as u, SpatialParams as ut, registerNode as v, SynthOptions as vt, GameStats as w, AudioBuses as wt, EngineOptions as x, MusicManager as xt, registeredTypes as y, synthSfx as yt, behaviorsWithoutSave as z };
1729
+ export { T_PREFIX as $, SettingsValues as A, LogEntry as At, captureBehaviors as B, EngineStats as C, MusicTrack as Ct, QualityCaps as D, Node as Dt, DeviceHints as E, BusName as Et, BehaviorState as F, ORDER_GROUP_BASE as G, savesWithoutUid as H, RestoreReport as I, effectiveOrder as J, OrderGroup as K, SaveSlot as L, readDeviceHints as M, LogManager as Mt, suggestQuality as N, Signal as Nt, QualityTier as O, NodeLifecycle as Ot, Scene as P, SignalListener as Pt, Localization as Q, SaveSlots as R, Scheduler as S, MusicManager as St, RendererStats as T, AudioBuses as Tt, SaveStore as U, restoreBehaviors as V, createSaveStore as W, BASE_LOCALE as X, resolveOrderGroups as Y, LocaleTables as Z, mergeStaticSignals as _, SfxParams as _t, clearBehaviors as a, isAudioContextAvailable as at, Engine as b, synthSfx as bt, registeredBehaviors as c, Listener as ct, PropSchema as d, SpatialParams as dt, suggestLocale as et, clearRegistry as f, Vec3 as ft, getNodeType as g, SFX_PRESET_NAMES as gt, getNodeSignals as h, SFX_PRESETS as ht, behaviorSignals as i, SfxPlayOptions as it, qualityEnvironment as j, LogLevel as jt, Settings as k, SceneTree as kt, NodeCtor as l, ROLLOFF_MODELS as lt, getNodeSchema as m, spatialPan as mt, BehaviorCtor as n, InputMap as nt, getBehavior as o, Voice as ot, createNode as p, spatialGain as pt, OrderGroupTable as q, behaviorSchema as r, SfxEngine as rt, registerBehavior as s, VoicePreset as st, Behavior as t, translationKey as tt, PropDef as u, RolloffModel as ut, registerNode as v, SfxWave as vt, GameStats as w, PlayMusicOptions as wt, EngineOptions as x, MusicBackend as xt, registeredTypes as y, SynthOptions as yt, behaviorsWithoutSave as z };
@@ -1,13 +1,13 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { n as loadScene, o as computeViewport, s as resolveViewport, v as diagnose, w as registerBehavior } from "./loader-DwazzlQb.js";
3
- import { h as Engine, m as AudioPlayer } from "./register-uvaZj1KX.js";
2
+ import { A as registerBehavior, C as diagnose, n as loadScene, o as computeViewport, s as resolveViewport } from "./loader-B-Gft32x.js";
3
+ import { h as Engine, m as AudioPlayer } from "./register-DVwlnZAZ.js";
4
4
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
5
5
  import { i as resolveRendering, n as attachTouchControls } from "./touch-BoNg_MnF.js";
6
- import { a as openBundledEditor, i as devServerLibrary, n as pauseWhenHidden, r as crossFade, t as teardown } from "./teardown-BKTCzLek.js";
7
- import { n as registerGameplayBehaviors } from "./gameplay-DbaI313d.js";
8
- import { g as PhysicsBody2D, n as UILayer, t as registerNodes2D, u as Camera2D, y as Node2D } from "./register-BNPZYJmd.js";
6
+ import { a as openBundledEditor, i as devServerLibrary, n as pauseWhenHidden, r as crossFade, t as teardown } from "./teardown-ks3d5W9n.js";
7
+ import { n as registerGameplayBehaviors } from "./gameplay-B6jqvYeM.js";
8
+ import { g as PhysicsBody2D, n as UILayer, t as registerNodes2D, u as Camera2D, y as Node2D } from "./register-Trx7WHnD.js";
9
9
  import { t as debugSources } from "./debug-draw-BM3DsvtT.js";
10
- import { n as enablePhysics2D } from "./physics-2d-_9VBOHn6.js";
10
+ import { n as enablePhysics2D } from "./physics-2d-Dns-oZlE.js";
11
11
  import { Box3, BufferAttribute, BufferGeometry, Color, LineBasicMaterial, LineSegments, LinearFilter, NearestFilter, OrthographicCamera, Raycaster, SRGBColorSpace, Scene, TextureLoader, Vector2, Vector3, WebGLRenderer } from "three";
12
12
  //#region src/2d/assets.ts
13
13
  /**
@@ -50,6 +50,7 @@ var AssetStore2D = class {
50
50
  },
51
51
  onError: (err) => {
52
52
  state.status = "error";
53
+ state.error = err instanceof Error ? err.message : `could not load ${decl.url}`;
53
54
  diagnose(this.engine, "error", `[incanto] failed to load texture '${key}' (${decl.url}):`, err);
54
55
  }
55
56
  });
@@ -66,10 +67,31 @@ var AssetStore2D = class {
66
67
  type,
67
68
  frameWidth,
68
69
  frameHeight,
70
+ url: decl.url,
69
71
  state
70
72
  });
71
73
  }
72
74
  }
75
+ /**
76
+ * Every declared asset that FAILED to load, `$ref` and url included.
77
+ *
78
+ * The 3D store has answered this since 0.32.0 — the 2D one only tracked a
79
+ * per-key status, which the console printed and nothing else could read. A
80
+ * 404'd texture is invisible, and "invisible" is the one symptom an author
81
+ * cannot tell apart from "I put it in the wrong place".
82
+ */
83
+ errors() {
84
+ const out = [];
85
+ for (const [key, entry] of this.entries) {
86
+ if (entry.state.status !== "error") continue;
87
+ out.push({
88
+ ref: `$${key}`,
89
+ url: entry.url,
90
+ error: entry.state.error ?? `could not load ${entry.url}`
91
+ });
92
+ }
93
+ return out;
94
+ }
73
95
  /** Load status of a declared asset KEY (no '$'), or undefined if unknown. */
74
96
  status(key) {
75
97
  return this.entries.get(key)?.state.status;
@@ -107,6 +129,18 @@ function worldFromScreen(view, sx, sy) {
107
129
  y: (sy - view.h / 2) / view.zoom + view.cy
108
130
  };
109
131
  }
132
+ function screenFromUi(v, ux, uy) {
133
+ return {
134
+ x: v.offsetX + ux / v.uiW * v.contentW,
135
+ y: v.offsetY + uy / v.uiH * v.contentH
136
+ };
137
+ }
138
+ function uiFromScreen(v, sx, sy) {
139
+ return {
140
+ x: (sx - v.offsetX) / v.contentW * v.uiW,
141
+ y: (sy - v.offsetY) / v.contentH * v.uiH
142
+ };
143
+ }
110
144
  //#endregion
111
145
  //#region src/2d/selection-outline.ts
112
146
  /** 2D twin of the 3D selection outline: a world-space (px, y-down) rectangle
@@ -355,6 +389,10 @@ var Renderer2D = class {
355
389
  const view = vp ? computeViewport(w, h, vp) : null;
356
390
  const uiW = view ? view.viewW : w;
357
391
  const uiH = view ? view.viewH : h;
392
+ this.lastUi = {
393
+ w: uiW,
394
+ h: uiH
395
+ };
358
396
  const { activeCamera } = syncTree2D(scene.root, this.worldScene, this.uiScene, this.assets, {
359
397
  width: uiW,
360
398
  height: uiH
@@ -406,6 +444,11 @@ var Renderer2D = class {
406
444
  this.webgl.render(this.uiScene, this.uiCam);
407
445
  this.webgl.autoClear = true;
408
446
  }
447
+ /** UI-pass extent from the last render — the space HUD nodes are posed in. */
448
+ lastUi = {
449
+ w: 1,
450
+ h: 1
451
+ };
409
452
  lastViewport = null;
410
453
  lastView = {
411
454
  cx: 0,
@@ -448,8 +491,13 @@ var Renderer2D = class {
448
491
  const cw = this.lastViewport?.contentW ?? w;
449
492
  const ch = this.lastViewport?.contentH ?? h;
450
493
  pickNdc.set((sx - ox) / cw * 2 - 1, -((sy - oy) / ch * 2 - 1));
451
- raycaster.setFromCamera(pickNdc, this.worldCam);
452
- const hits = raycaster.intersectObjects(this.worldScene.children, true);
494
+ const ui = this.hit(this.uiScene, this.uiCam);
495
+ if (ui) return ui;
496
+ return this.hit(this.worldScene, this.worldCam);
497
+ }
498
+ hit(scene, cam) {
499
+ raycaster.setFromCamera(pickNdc, cam);
500
+ const hits = raycaster.intersectObjects(scene.children, true);
453
501
  let best = null;
454
502
  for (const hit of hits) {
455
503
  let obj = hit.object;
@@ -464,6 +512,39 @@ var Renderer2D = class {
464
512
  }
465
513
  return best?.node ?? null;
466
514
  }
515
+ /** Does the UI pass draw this node (i.e. is it under a UILayer)? */
516
+ isUiNode(node) {
517
+ let obj = node._object2D ?? node._group ?? null;
518
+ while (obj) {
519
+ if (obj === this.uiScene) return true;
520
+ obj = obj.parent;
521
+ }
522
+ return false;
523
+ }
524
+ /**
525
+ * UI-space px → canvas px.
526
+ *
527
+ * A UILayer's children are posed in the UI pass's own space (origin top-left,
528
+ * `uiW × uiH`), NOT in world space — so mapping them with `screenFromWorld`
529
+ * puts them wherever the game camera happens to be looking.
530
+ */
531
+ screenFromUi(ux, uy) {
532
+ return screenFromUi(this.uiView(), ux, uy);
533
+ }
534
+ /** Canvas px → UI-space px (the inverse of `screenFromUi`). */
535
+ uiFromScreen(sx, sy) {
536
+ return uiFromScreen(this.uiView(), sx, sy);
537
+ }
538
+ uiView() {
539
+ return {
540
+ offsetX: this.lastViewport?.offsetX ?? 0,
541
+ offsetY: this.lastViewport?.offsetY ?? 0,
542
+ contentW: this.lastViewport?.contentW ?? this.lastView.w,
543
+ contentH: this.lastViewport?.contentH ?? this.lastView.h,
544
+ uiW: this.lastUi.w,
545
+ uiH: this.lastUi.h
546
+ };
547
+ }
467
548
  /**
468
549
  * Canvas-pixel AABB of a node's rendered world-pass objects, or null when it
469
550
  * has no renderable extent (plain Nodes, empty containers).
@@ -476,8 +557,9 @@ var Renderer2D = class {
476
557
  if (boundsScratch.isEmpty()) return null;
477
558
  boundsScratch.getSize(sizeScratch3);
478
559
  if (sizeScratch3.x === 0 && sizeScratch3.y === 0) return null;
479
- const a = this.screenFromWorld(boundsScratch.min.x, -boundsScratch.max.y);
480
- const b = this.screenFromWorld(boundsScratch.max.x, -boundsScratch.min.y);
560
+ const map = this.isUiNode(node) ? (x, y) => this.screenFromUi(x, y) : (x, y) => this.screenFromWorld(x, y);
561
+ const a = map(boundsScratch.min.x, -boundsScratch.max.y);
562
+ const b = map(boundsScratch.max.x, -boundsScratch.min.y);
481
563
  return {
482
564
  x: a.x,
483
565
  y: a.y,
@@ -1,15 +1,15 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { n as loadScene, v as diagnose, w as registerBehavior } from "./loader-DwazzlQb.js";
3
- import { _ as qualityCaps, h as Engine, m as AudioPlayer, y as qualityRendering } from "./register-uvaZj1KX.js";
2
+ import { A as registerBehavior, C as diagnose, n as loadScene } from "./loader-B-Gft32x.js";
3
+ import { _ as qualityCaps, h as Engine, m as AudioPlayer, y as qualityRendering } from "./register-DVwlnZAZ.js";
4
4
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
5
5
  import { r as parseDrive, t as logReport } from "./log-report-lxrQY9cH.js";
6
6
  import { i as resolveRendering, n as attachTouchControls } from "./touch-BoNg_MnF.js";
7
- import { a as openBundledEditor, i as devServerLibrary, n as pauseWhenHidden, o as poseFromRenderer, r as crossFade, t as teardown } from "./teardown-BKTCzLek.js";
8
- import { B as createCausticsQuad, F as PhysicsBody3D, R as Node3D, n as registerGameplayBehaviors } from "./gameplay-DbaI313d.js";
7
+ import { a as openBundledEditor, i as devServerLibrary, n as pauseWhenHidden, o as poseFromRenderer, r as crossFade, t as teardown } from "./teardown-ks3d5W9n.js";
8
+ import { I as PhysicsBody3D, V as createCausticsQuad, n as registerGameplayBehaviors, z as Node3D } from "./gameplay-B6jqvYeM.js";
9
9
  import { t as debugSources } from "./debug-draw-BM3DsvtT.js";
10
- import { E as Camera3D, U as TextureCache3D, g as ModelInstance3D, n as registerNodes3D, t as resolveEnvironmentHdri, v as DirectionalLight3D } from "./environment-presets-XFuqu5jv.js";
10
+ import { E as Camera3D, U as TextureCache3D, g as ModelInstance3D, n as registerNodes3D, t as resolveEnvironmentHdri, v as DirectionalLight3D } from "./environment-presets-DSZwsKPs.js";
11
11
  import { a as frameSignature, i as frameImage, o as frameStats } from "./frame-report-njybhZon.js";
12
- import { n as enablePhysics3D } from "./physics-3d-DrpF5hcG.js";
12
+ import { n as enablePhysics3D } from "./physics-3d-BhI0ehpe.js";
13
13
  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";
14
14
  import { VRMLoaderPlugin, VRMUtils } from "@pixiv/three-vrm";
15
15
  import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
package/dist/debug.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { At as LogLevel, T as RendererStats, b as Engine } from "./behavior-62q0HWBO.js";
1
+ import { T as RendererStats, b as Engine, jt as LogLevel } from "./behavior-uPEuZrUB.js";
2
2
 
3
3
  //#region src/debug/panel.d.ts
4
4
  /** Minimal document surface the overlay needs (injectable for tests). */
package/dist/debug.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as jsonClone } from "./json-BLk7H2Qa.js";
2
- import { s as mergeStaticProps } from "./registry-IyWCGe4q.js";
2
+ import { s as mergeStaticProps } from "./registry-C7u42TID.js";
3
3
  import { t as debugSources } from "./debug-draw-BM3DsvtT.js";
4
4
  //#region src/debug/capture.ts
5
5
  /**
@@ -1,4 +1,4 @@
1
- import { i as serializeNode, t as buildNodeJson } from "./loader-DwazzlQb.js";
1
+ import { i as serializeNode, t as buildNodeJson } from "./loader-B-Gft32x.js";
2
2
  //#region src/core/scene/duplicate.ts
3
3
  function stripUids(json) {
4
4
  const { uid: _uid, ...rest } = json;