incanto 0.36.1 → 0.37.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 (56) hide show
  1. package/bin/incanto-feel.mjs +93 -0
  2. package/bin/incanto-playtest.mjs +136 -0
  3. package/dist/2d.d.ts +2 -2
  4. package/dist/2d.js +3 -3
  5. package/dist/3d.d.ts +14 -3
  6. package/dist/3d.js +4 -4
  7. package/dist/{behavior-DibCwrW7.d.ts → behavior-5o1EkbLD.d.ts} +11 -0
  8. package/dist/{create-game-D-StzrTj.js → create-game-DmGc-Mrn.js} +5 -5
  9. package/dist/{create-game-DnOu2aAa.js → create-game-QqrrK3yr.js} +5 -5
  10. package/dist/debug.d.ts +1 -1
  11. package/dist/{duplicate-Cvb1BSca.js → duplicate-CRtihGmC.js} +1 -1
  12. package/dist/editor.js +1 -1
  13. package/dist/{environment-presets-fK9oyrrR.js → environment-presets-DWHxLHM5.js} +75 -9
  14. package/dist/{gameplay-C0MUMSak.js → gameplay-CsJDUQh_.js} +2 -2
  15. package/dist/gameplay.d.ts +1 -1
  16. package/dist/gameplay.js +1 -1
  17. package/dist/index.d.ts +4 -50
  18. package/dist/index.js +5 -75
  19. package/dist/{loader-BqigZsfF.d.ts → loader-iGq2PT_k.d.ts} +1 -1
  20. package/dist/{loader-DAZlgqeC.js → loader-r49nDwB4.js} +15 -0
  21. package/dist/net.d.ts +1 -1
  22. package/dist/net.js +3 -3
  23. package/dist/{pathfinding-B3QtdhvZ.d.ts → pathfinding-pPrqtlWS.d.ts} +1 -1
  24. package/dist/{physics-2d-B4v39hca.js → physics-2d-C8Hi40qb.js} +2 -2
  25. package/dist/{physics-3d-DFJVMYET.js → physics-3d-C0mWoWXC.js} +3 -3
  26. package/dist/react.d.ts +1 -1
  27. package/dist/react.js +1 -1
  28. package/dist/{register-COGCNitr.js → register-DFSW2Y30.js} +2 -2
  29. package/dist/{register-BHAwM4bK.js → register-DOWGnxe1.js} +1 -1
  30. package/dist/{register-DXwtIVqP.js → register-t15rydSm.js} +2 -2
  31. package/dist/{audit-D7F3n3Nt.js → replay-BuOTl5rM.js} +72 -2
  32. package/dist/replay-DGzhZWrM.d.ts +86 -0
  33. package/dist/test-BiIO9ULW.js +1404 -0
  34. package/dist/test.d.ts +113 -4
  35. package/dist/test.js +3 -3
  36. package/dist/vite.js +1 -1
  37. package/editor/assets/{agent8-tvTJH3IX.js → agent8-CeeI7P_0.js} +1 -1
  38. package/editor/assets/{debug-CPbraUYm.js → debug-DN9X9G6z.js} +1 -1
  39. package/editor/assets/{index-DPJkbebq.js → index-C833FBzB.js} +42 -42
  40. package/editor/index.html +1 -1
  41. package/package.json +3 -1
  42. package/schemas/scene.schema.json +24 -0
  43. package/skills/incanto-3d-character.md +35 -0
  44. package/skills/incanto-building-2d-games.md +6 -0
  45. package/skills/incanto-building-3d-games.md +6 -0
  46. package/skills/incanto-game-feel.md +76 -0
  47. package/skills/incanto-node-reference.md +6 -0
  48. package/skills/incanto-playtesting.md +143 -0
  49. package/skills/incanto-verifying-your-game.md +41 -0
  50. package/templates-app/beacon-isle-3d/package.json +1 -1
  51. package/templates-app/tps-3d/package.json +1 -1
  52. package/templates-app/village-quest-3d/package.json +1 -1
  53. package/templates-app/village-quest-3d/src/village.scene.json +3 -1
  54. package/templates-app/village-quest-3d/verify.ts +55 -5
  55. package/dist/errors-DGRtWlSx.d.ts +0 -39
  56. package/dist/test-X_xE6Yay.js +0 -642
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { $ as translationKey, A as qualityEnvironment, At as LogManager, B as restoreBehaviors, C as EngineStats, Ct as AudioBuses, D as QualityTier, Dt as SceneTree, E as DeviceHints, Et as NodeLifecycle, F as RestoreReport, G as OrderGroupTable, H as createSaveStore, I as SaveSlot, J as BASE_LOCALE, K as effectiveOrder, L as SaveSlots, M as suggestQuality, Mt as SignalListener, N as Scene, O as Settings, Ot as LogEntry, P as BehaviorState, Q as suggestLocale, R as behaviorsWithoutSave, S as Scheduler, St as PlayMusicOptions, T as RendererStats, Tt as Node, U as ORDER_GROUP_BASE, V as SaveStore, W as OrderGroup, X as Localization, Y as LocaleTables, Z as T_PREFIX, _ as mergeStaticSignals, _t as SynthOptions, a as clearBehaviors, at as VoicePreset, b as Engine, bt as MusicManager, c as registeredBehaviors, ct as RolloffModel, d as PropSchema, dt as spatialGain, et as InputMap, f as clearRegistry, ft as spatialPan, g as getNodeType, gt as SfxWave, h as getNodeSignals, ht as SfxParams, i as behaviorSignals, it as Voice, j as readDeviceHints, jt as Signal, k as SettingsValues, kt as LogLevel, l as NodeCtor, lt as SpatialParams, m as getNodeSchema, mt as SFX_PRESET_NAMES, n as BehaviorCtor, nt as SfxPlayOptions, o as getBehavior, ot as Listener, p as createNode, pt as SFX_PRESETS, q as resolveOrderGroups, r as behaviorSchema, rt as isAudioContextAvailable, s as registerBehavior, st as ROLLOFF_MODELS, t as Behavior, tt as SfxEngine, u as PropDef, ut as Vec3, v as registerNode, vt as synthSfx, w as GameStats, wt as BusName, x as EngineOptions, xt as MusicTrack, y as registeredTypes, yt as MusicBackend, z as captureBehaviors } from "./behavior-DibCwrW7.js";
1
+ import { $ as translationKey, A as qualityEnvironment, At as LogManager, B as restoreBehaviors, C as EngineStats, Ct as AudioBuses, D as QualityTier, Dt as SceneTree, E as DeviceHints, Et as NodeLifecycle, F as RestoreReport, G as OrderGroupTable, H as createSaveStore, I as SaveSlot, J as BASE_LOCALE, K as effectiveOrder, L as SaveSlots, M as suggestQuality, Mt as SignalListener, N as Scene, O as Settings, Ot as LogEntry, P as BehaviorState, Q as suggestLocale, R as behaviorsWithoutSave, S as Scheduler, St as PlayMusicOptions, T as RendererStats, Tt as Node, U as ORDER_GROUP_BASE, V as SaveStore, W as OrderGroup, X as Localization, Y as LocaleTables, Z as T_PREFIX, _ as mergeStaticSignals, _t as SynthOptions, a as clearBehaviors, at as VoicePreset, b as Engine, bt as MusicManager, c as registeredBehaviors, ct as RolloffModel, d as PropSchema, dt as spatialGain, et as InputMap, f as clearRegistry, ft as spatialPan, g as getNodeType, gt as SfxWave, h as getNodeSignals, ht as SfxParams, i as behaviorSignals, it as Voice, j as readDeviceHints, jt as Signal, k as SettingsValues, kt as LogLevel, l as NodeCtor, lt as SpatialParams, m as getNodeSchema, mt as SFX_PRESET_NAMES, n as BehaviorCtor, nt as SfxPlayOptions, o as getBehavior, ot as Listener, p as createNode, pt as SFX_PRESETS, q as resolveOrderGroups, r as behaviorSchema, rt as isAudioContextAvailable, s as registerBehavior, st as ROLLOFF_MODELS, t as Behavior, tt as SfxEngine, u as PropDef, ut as Vec3, v as registerNode, vt as synthSfx, w as GameStats, wt as BusName, x as EngineOptions, xt as MusicTrack, y as registeredTypes, yt as MusicBackend, z as captureBehaviors } from "./behavior-5o1EkbLD.js";
2
2
  import { a as Rng, c as JsonValue, d as jsonKind, i as SceneJson, l as jsonClone, n as NodeJson, o as JsonKind, r as SCENE_FORMAT, s as JsonObject, t as ConnectionJson, u as jsonEquals } from "./schema-CFeioQRE.js";
3
- import { n as loadScene, t as LoadSceneOptions } from "./loader-BqigZsfF.js";
3
+ import { n as loadScene, t as LoadSceneOptions } from "./loader-iGq2PT_k.js";
4
4
  import { n as ParticleSimConfig, r as ParticleView, t as ParticleSim } from "./particle-sim-BzJ1yxoE.js";
5
- import { a as AudioElementLike, i as gridFromRows, n as PathGrid, o as AudioPlayer, r as findPath, t as FindPathOptions } from "./pathfinding-B3QtdhvZ.js";
6
- import { i as auditScene, n as IncantoErrorCode, r as IncantoErrorDetails, t as IncantoError } from "./errors-DGRtWlSx.js";
5
+ import { a as AudioElementLike, i as gridFromRows, n as PathGrid, o as AudioPlayer, r as findPath, t as FindPathOptions } from "./pathfinding-pPrqtlWS.js";
6
+ import { a as startRecording, c as IncantoErrorDetails, i as replay, l as auditScene, n as ReplayEvent, o as IncantoError, r as ReplayJson, s as IncantoErrorCode, t as Recorder } from "./replay-DGzhZWrM.js";
7
7
 
8
8
  //#region src/core/audio/crossfade.d.ts
9
9
  /**
@@ -586,52 +586,6 @@ declare function resolveRendering(environment: JsonObject | undefined, fallback:
586
586
  preserveDrawingBuffer?: boolean;
587
587
  }): ResolvedRendering;
588
588
  //#endregion
589
- //#region src/core/replay.d.ts
590
- /**
591
- * Deterministic input replay — the engine already runs on a seeded RNG and
592
- * an injectable clock, so a recording of (tick timestamps + input events)
593
- * replays BIT-IDENTICALLY into a fresh engine with the same seed and scene.
594
- * That turns "does my game still work?" into an assertion an agent can run
595
- * headlessly: record once while playing, replay forever as a regression.
596
- *
597
- * const rec = startRecording(engine); // play the game...
598
- * const replayJson = rec.stop(); // ...save this JSON
599
- *
600
- * const fresh = new Engine({ seed: SAME_SEED }); // same seed + scene JSON
601
- * fresh.setScene(loadScene(sceneJson));
602
- * replay(fresh, replayJson); // now assert game state
603
- *
604
- * Caveats (documented, by design): game code must use `engine.rng` (not
605
- * Math.random) and dt/engine.time (not Date.now) — the engine's own rules.
606
- * Gamepad polling is not recorded (bind pads to actions; actions replay).
607
- */
608
- /** [tickIndex, methodName, ...primitive args] */
609
- type ReplayEvent = [number, string, ...(string | number | boolean)[]];
610
- interface ReplayJson {
611
- /** File-format marker for forward compatibility. */
612
- replay: 1;
613
- /** Tick timestamps (ms) exactly as the loop delivered them. */
614
- ticks: number[];
615
- events: ReplayEvent[];
616
- }
617
- interface Recorder {
618
- /** Detach the taps and return the recording. */
619
- stop(): ReplayJson;
620
- }
621
- /**
622
- * Tap the engine's input entry points and tick clock. Recording starts at
623
- * the CURRENT moment — start it before the first tick for full-run replays.
624
- */
625
- declare function startRecording(engine: Engine): Recorder;
626
- /**
627
- * Drive a fresh engine through a recording: for each recorded tick, feed
628
- * that tick's input events first, then tick with the recorded timestamp.
629
- * `onTick(index)` lets tests assert mid-run state.
630
- */
631
- declare function replay(engine: Engine, recording: ReplayJson, opts?: {
632
- onTick?: (index: number) => void;
633
- }): void;
634
- //#endregion
635
589
  //#region src/core/scene/constants.d.ts
636
590
  /** The single reserved key marking a prop value as a constant reference. */
637
591
  declare const CONST_REF_KEY = "@const";
package/dist/index.js CHANGED
@@ -1,14 +1,14 @@
1
- import { C as getBehavior, E as Signal, S as clearBehaviors, T as registeredBehaviors, _ as InputMap, a as SCENE_FORMAT, b as behaviorSchema, c as SceneTree, d as resolveConstants, f as Node, g as resolveOrderGroups, h as effectiveOrder, i as serializeNode, l as CONST_REF_KEY, m as ORDER_GROUP_BASE, n as loadScene, o as computeViewport, p as parseNodePath, r as Scene, s as resolveViewport, u as isConstRef, w as registerBehavior, x as behaviorSignals, y as Behavior } from "./loader-DAZlgqeC.js";
2
- import { A as translationKey, B as WebAudioMusicBackend, C as restoreBehaviors, D as Localization, E as BASE_LOCALE, F as spatialPan, H as fadeGain, I as SFX_PRESETS, L as SFX_PRESET_NAMES, M as isAudioContextAvailable, N as ROLLOFF_MODELS, O as T_PREFIX, P as spatialGain, R as synthSfx, S as captureBehaviors, T as LogManager, U as AudioBuses, V as crossfadeGains, _ as qualityEnvironment, a as UiSlider, b as SaveSlots, c as UiButton, d as UiBanner, f as UiBar, g as Settings, h as Engine, i as UiSelect, j as SfxEngine, k as suggestLocale, l as UiDialogue, m as AudioPlayer, n as UiImage, o as UiToggle, p as UiText, r as UiPanel, s as Timer, t as registerCoreNodes, u as HudLayer, v as readDeviceHints, w as createSaveStore, x as behaviorsWithoutSave, y as suggestQuality, z as MusicManager } from "./register-BHAwM4bK.js";
1
+ import { C as getBehavior, E as Signal, S as clearBehaviors, T as registeredBehaviors, _ as InputMap, a as SCENE_FORMAT, b as behaviorSchema, c as SceneTree, d as resolveConstants, f as Node, g as resolveOrderGroups, h as effectiveOrder, i as serializeNode, l as CONST_REF_KEY, m as ORDER_GROUP_BASE, n as loadScene, o as computeViewport, p as parseNodePath, r as Scene, s as resolveViewport, u as isConstRef, w as registerBehavior, x as behaviorSignals, y as Behavior } from "./loader-r49nDwB4.js";
2
+ import { A as translationKey, B as WebAudioMusicBackend, C as restoreBehaviors, D as Localization, E as BASE_LOCALE, F as spatialPan, H as fadeGain, I as SFX_PRESETS, L as SFX_PRESET_NAMES, M as isAudioContextAvailable, N as ROLLOFF_MODELS, O as T_PREFIX, P as spatialGain, R as synthSfx, S as captureBehaviors, T as LogManager, U as AudioBuses, V as crossfadeGains, _ as qualityEnvironment, a as UiSlider, b as SaveSlots, c as UiButton, d as UiBanner, f as UiBar, g as Settings, h as Engine, i as UiSelect, j as SfxEngine, k as suggestLocale, l as UiDialogue, m as AudioPlayer, n as UiImage, o as UiToggle, p as UiText, r as UiPanel, s as Timer, t as registerCoreNodes, u as HudLayer, v as readDeviceHints, w as createSaveStore, x as behaviorsWithoutSave, y as suggestQuality, z as MusicManager } from "./register-DOWGnxe1.js";
3
3
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
4
4
  import { t as Rng } from "./rng-DP-SR7eg.js";
5
- import { t as auditScene } from "./audit-D7F3n3Nt.js";
5
+ import { n as startRecording, r as auditScene, t as replay } from "./replay-BuOTl5rM.js";
6
6
  import { n as jsonEquals, r as jsonKind, t as jsonClone } from "./json-BLk7H2Qa.js";
7
7
  import { a as getNodeSignals, c as mergeStaticSignals, i as getNodeSchema, l as registerNode, n as clearRegistry, o as getNodeType, r as createNode, u as registeredTypes } from "./registry-IyWCGe4q.js";
8
8
  import { t as createNoise2D } from "./noise-CGUMx44x.js";
9
9
  import { i as applyParticlePreset, n as PARTICLE_PRESETS, r as PARTICLE_PRESET_NAMES, t as ParticleSim } from "./particle-sim-Bw7hB93B.js";
10
10
  import { i as resolveRendering, n as attachTouchControls, r as joystickVector, t as TouchControls } from "./touch-BoNg_MnF.js";
11
- import { t as duplicateNode } from "./duplicate-Cvb1BSca.js";
11
+ import { t as duplicateNode } from "./duplicate-CRtihGmC.js";
12
12
  //#region src/core/pathfinding.ts
13
13
  /**
14
14
  * A* from `from` to `to` (inclusive cell coords). Returns the cell path
@@ -155,76 +155,6 @@ function assetUrls(assets) {
155
155
  return Object.values(assets ?? {}).map((a) => a.url).filter((u) => typeof u === "string" && !u.startsWith("data:"));
156
156
  }
157
157
  //#endregion
158
- //#region src/core/replay.ts
159
- const RECORDED_METHODS = [
160
- "handleKey",
161
- "handleMouseButton",
162
- "handlePointerMove",
163
- "handleWheel",
164
- "pressAction",
165
- "releaseAction"
166
- ];
167
- /**
168
- * Tap the engine's input entry points and tick clock. Recording starts at
169
- * the CURRENT moment — start it before the first tick for full-run replays.
170
- */
171
- function startRecording(engine) {
172
- const ticks = [];
173
- const events = [];
174
- const input = engine.input;
175
- const originals = /* @__PURE__ */ new Map();
176
- for (const name of RECORDED_METHODS) {
177
- const original = input[name];
178
- originals.set(name, original);
179
- input[name] = (...args) => {
180
- events.push([
181
- ticks.length,
182
- name,
183
- ...args
184
- ]);
185
- return original.apply(engine.input, args);
186
- };
187
- }
188
- const originalTick = engine.tick.bind(engine);
189
- const engineAny = engine;
190
- engineAny.tick = (nowMs) => {
191
- ticks.push(nowMs);
192
- originalTick(nowMs);
193
- };
194
- let stopped = false;
195
- return { stop() {
196
- if (!stopped) {
197
- stopped = true;
198
- for (const [name, fn] of originals) input[name] = fn;
199
- engineAny.tick = originalTick;
200
- }
201
- return {
202
- replay: 1,
203
- ticks,
204
- events
205
- };
206
- } };
207
- }
208
- /**
209
- * Drive a fresh engine through a recording: for each recorded tick, feed
210
- * that tick's input events first, then tick with the recorded timestamp.
211
- * `onTick(index)` lets tests assert mid-run state.
212
- */
213
- function replay(engine, recording, opts) {
214
- const input = engine.input;
215
- let cursor = 0;
216
- for (let i = 0; i < recording.ticks.length; i++) {
217
- while (cursor < recording.events.length && recording.events[cursor][0] === i) {
218
- const [, method, ...args] = recording.events[cursor];
219
- const fn = input[method];
220
- if (typeof fn === "function") fn.apply(engine.input, args);
221
- cursor += 1;
222
- }
223
- engine.tick(recording.ticks[i]);
224
- opts?.onTick?.(i);
225
- }
226
- }
227
- //#endregion
228
158
  //#region src/core/uid-gen.ts
229
159
  /**
230
160
  * The ONE way to mint a node uid: crypto-strength, `n_` + 16 base36 chars
@@ -242,6 +172,6 @@ function newUid() {
242
172
  //#endregion
243
173
  //#region src/index.ts
244
174
  /** Engine version. Kept in sync with package.json by the release pipeline. */
245
- const VERSION = "0.36.1";
175
+ const VERSION = "0.37.0";
246
176
  //#endregion
247
177
  export { AudioBuses, AudioPlayer, BASE_LOCALE, Behavior, CONST_REF_KEY, Engine, HudLayer, IncantoError, InputMap, Localization, LogManager, MusicManager, Node, ORDER_GROUP_BASE, PARTICLE_PRESETS, PARTICLE_PRESET_NAMES, ParticleSim, ROLLOFF_MODELS, Rng, SCENE_FORMAT, SFX_PRESETS, SFX_PRESET_NAMES, SaveSlots, Scene, SceneTree, Settings, SfxEngine, Signal, T_PREFIX, Timer, TouchControls, UiBanner, UiBar, UiButton, UiDialogue, UiImage, UiPanel, UiSelect, UiSlider, UiText, UiToggle, VERSION, WebAudioMusicBackend, applyParticlePreset, assetUrls, attachTouchControls, auditScene, behaviorSchema, behaviorSignals, behaviorsWithoutSave, captureBehaviors, clearBehaviors, clearRegistry, computeViewport, createNode, createNoise2D, createSaveStore, crossfadeGains, duplicateNode, effectiveOrder, fadeGain, findPath, getBehavior, getNodeSchema, getNodeSignals, getNodeType, gridFromRows, isAudioContextAvailable, isConstRef, joystickVector, jsonClone, jsonEquals, jsonKind, loadScene, mergeStaticSignals, newUid, parseNodePath, preloadUrls, qualityEnvironment, readDeviceHints, registerBehavior, registerCoreNodes, registerNode, registeredBehaviors, registeredTypes, replay, resolveConstants, resolveOrderGroups, resolveRendering, resolveViewport, restoreBehaviors, serializeNode, spatialGain, spatialPan, startRecording, suggestLocale, suggestQuality, synthSfx, translationKey };
@@ -1,4 +1,4 @@
1
- import { N as Scene, b as Engine } from "./behavior-DibCwrW7.js";
1
+ import { N as Scene, b as Engine } from "./behavior-5o1EkbLD.js";
2
2
  import { i as SceneJson } from "./schema-CFeioQRE.js";
3
3
 
4
4
  //#region src/core/scene/loader.d.ts
@@ -221,6 +221,21 @@ var InputMap = class {
221
221
  * scripted gameplay tests and touch buttons drive the game by intent
222
222
  * (`press('jump')`) instead of reverse-engineering keybinds.
223
223
  */
224
+ /**
225
+ * Every action this scene declared, with its kind.
226
+ *
227
+ * A scene states its own control vocabulary in `input{}`, which means a tool
228
+ * can drive a game it has never seen — the automated playtester's entire
229
+ * premise. Nothing else could ask: `actions` was private.
230
+ */
231
+ declaredActions() {
232
+ const out = [];
233
+ for (const [name, def] of this.actions) out.push({
234
+ name,
235
+ type: def.type
236
+ });
237
+ return out;
238
+ }
224
239
  pressAction(action) {
225
240
  this.button(action);
226
241
  if (!this.injectedDown.has(action)) {
package/dist/net.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Tt as Node, b as Engine, d as PropSchema, jt as Signal } from "./behavior-DibCwrW7.js";
1
+ import { Tt as Node, b as Engine, d as PropSchema, jt as Signal } from "./behavior-5o1EkbLD.js";
2
2
  import { c as JsonValue, i as SceneJson, s as JsonObject } from "./schema-CFeioQRE.js";
3
3
 
4
4
  //#region src/net/types.d.ts
package/dist/net.js CHANGED
@@ -1,9 +1,9 @@
1
- import { n as loadScene } from "./loader-DAZlgqeC.js";
2
- import { h as Engine } from "./register-BHAwM4bK.js";
1
+ import { n as loadScene } from "./loader-r49nDwB4.js";
2
+ import { h as Engine } from "./register-DOWGnxe1.js";
3
3
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
4
4
  import { t as jsonClone } from "./json-BLk7H2Qa.js";
5
5
  import { n as createAgent8Server } from "./agent8-CvsfVskX.js";
6
- import { i as applySyncPatch, n as NetworkSpawner, r as NetworkManager, t as registerNodesNet } from "./register-DXwtIVqP.js";
6
+ import { i as applySyncPatch, n as NetworkSpawner, r as NetworkManager, t as registerNodesNet } from "./register-t15rydSm.js";
7
7
  //#region src/net/loopback.ts
8
8
  /**
9
9
  * In-memory multiplayer hub implementing the SAME kernel contract as
@@ -1,4 +1,4 @@
1
- import { Tt as Node, d as PropSchema, ot as Listener } from "./behavior-DibCwrW7.js";
1
+ import { Tt as Node, d as PropSchema, ot as Listener } from "./behavior-5o1EkbLD.js";
2
2
 
3
3
  //#region src/core/nodes/audio-player.d.ts
4
4
  /**
@@ -1,7 +1,7 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { v as diagnose } from "./loader-DAZlgqeC.js";
2
+ import { v as diagnose } from "./loader-r49nDwB4.js";
3
3
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
4
- import { _ as RigidBody2D, b as validateCollider2D, g as PhysicsBody2D, h as CharacterBody2D, m as Area2D, p as Joint2D, y as Node2D } from "./register-COGCNitr.js";
4
+ import { _ as RigidBody2D, b as validateCollider2D, g as PhysicsBody2D, h as CharacterBody2D, m as Area2D, p as Joint2D, y as Node2D } from "./register-DFSW2Y30.js";
5
5
  import { n as registerDebugSource } from "./debug-draw-BM3DsvtT.js";
6
6
  //#region src/2d/physics/physics-2d.ts
7
7
  var physics_2d_exports = /* @__PURE__ */ __exportAll({
@@ -1,9 +1,9 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { v as diagnose } from "./loader-DAZlgqeC.js";
2
+ import { v as diagnose } from "./loader-r49nDwB4.js";
3
3
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
4
- import { F as PhysicsBody3D, I as RigidBody3D, N as Area3D, P as CharacterBody3D, R as Node3D, z as validateCollider3D } from "./gameplay-C0MUMSak.js";
4
+ import { F as PhysicsBody3D, I as RigidBody3D, N as Area3D, P as CharacterBody3D, R as Node3D, z as validateCollider3D } from "./gameplay-CsJDUQh_.js";
5
5
  import { n as registerDebugSource } from "./debug-draw-BM3DsvtT.js";
6
- import { A as Terrain3D, F as InstancedMesh3D, L as buildMeshGeometry, P as Joint3D } from "./environment-presets-fK9oyrrR.js";
6
+ import { A as Terrain3D, F as InstancedMesh3D, L as buildMeshGeometry, P as Joint3D } from "./environment-presets-DWHxLHM5.js";
7
7
  import { Euler, Matrix4, Quaternion, Vector3 } from "three";
8
8
  //#region src/3d/physics/collider-lines.ts
9
9
  /**
package/dist/react.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { N as Scene, b as Engine, n as BehaviorCtor } from "./behavior-DibCwrW7.js";
1
+ import { N as Scene, b as Engine, n as BehaviorCtor } from "./behavior-5o1EkbLD.js";
2
2
  import { c as JsonValue } from "./schema-CFeioQRE.js";
3
3
  import { CSSProperties, ReactNode } from "react";
4
4
 
package/dist/react.js CHANGED
@@ -156,7 +156,7 @@ function IncantoCanvas(props) {
156
156
  pointer: latest.pointer,
157
157
  ...keyboard !== void 0 ? { keyboard } : {}
158
158
  };
159
- const next = await (_gameFactory ?? (mode === "3d" ? async (o) => (await import("./create-game-DnOu2aAa.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-D-StzrTj.js").then((n) => n.n)).createGame2D(o)))(opts);
159
+ const next = await (_gameFactory ?? (mode === "3d" ? async (o) => (await import("./create-game-QqrrK3yr.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-DmGc-Mrn.js").then((n) => n.n)).createGame2D(o)))(opts);
160
160
  if (disposed) {
161
161
  next.dispose();
162
162
  return;
@@ -1,5 +1,5 @@
1
- import { f as Node, h as effectiveOrder } from "./loader-DAZlgqeC.js";
2
- import { t as registerCoreNodes } from "./register-BHAwM4bK.js";
1
+ import { f as Node, h as effectiveOrder } from "./loader-r49nDwB4.js";
2
+ import { t as registerCoreNodes } from "./register-DOWGnxe1.js";
3
3
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
4
4
  import { t as Rng } from "./rng-DP-SR7eg.js";
5
5
  import { i as getNodeSchema, l as registerNode } from "./registry-IyWCGe4q.js";
@@ -1,4 +1,4 @@
1
- import { E as Signal, _ as InputMap, b as behaviorSchema, f as Node, g as resolveOrderGroups, m as ORDER_GROUP_BASE, v as diagnose } from "./loader-DAZlgqeC.js";
1
+ import { E as Signal, _ as InputMap, b as behaviorSchema, f as Node, g as resolveOrderGroups, m as ORDER_GROUP_BASE, v as diagnose } from "./loader-r49nDwB4.js";
2
2
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
3
3
  import { t as Rng } from "./rng-DP-SR7eg.js";
4
4
  import { l as registerNode } from "./registry-IyWCGe4q.js";
@@ -1,5 +1,5 @@
1
- import { E as Signal, f as Node, t as buildNodeJson } from "./loader-DAZlgqeC.js";
2
- import { t as registerCoreNodes } from "./register-BHAwM4bK.js";
1
+ import { E as Signal, f as Node, t as buildNodeJson } from "./loader-r49nDwB4.js";
2
+ import { t as registerCoreNodes } from "./register-DOWGnxe1.js";
3
3
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
4
4
  import { n as jsonEquals, t as jsonClone } from "./json-BLk7H2Qa.js";
5
5
  import { l as registerNode } from "./registry-IyWCGe4q.js";
@@ -1,4 +1,4 @@
1
- import { A as translationKey } from "./register-BHAwM4bK.js";
1
+ import { A as translationKey } from "./register-DOWGnxe1.js";
2
2
  //#region src/core/audit.ts
3
3
  const ANIMATED_TYPES = new Set([
4
4
  "Particles2D",
@@ -108,4 +108,74 @@ function auditStrings(scene) {
108
108
  return [`${listed.length} translation key${listed.length === 1 ? "" : "s"} nothing declares: ${listed.join(", ")}. The key itself renders on screen. Add them under "strings": { "en": { ... } } — other locales may omit any of them and fall back to English.`];
109
109
  }
110
110
  //#endregion
111
- export { auditScene as t };
111
+ //#region src/core/replay.ts
112
+ const RECORDED_METHODS = [
113
+ "handleKey",
114
+ "handleMouseButton",
115
+ "handlePointerMove",
116
+ "handleWheel",
117
+ "pressAction",
118
+ "releaseAction"
119
+ ];
120
+ /**
121
+ * Tap the engine's input entry points and tick clock. Recording starts at
122
+ * the CURRENT moment — start it before the first tick for full-run replays.
123
+ */
124
+ function startRecording(engine) {
125
+ const ticks = [];
126
+ const events = [];
127
+ const input = engine.input;
128
+ const originals = /* @__PURE__ */ new Map();
129
+ for (const name of RECORDED_METHODS) {
130
+ const original = input[name];
131
+ originals.set(name, original);
132
+ input[name] = (...args) => {
133
+ events.push([
134
+ ticks.length,
135
+ name,
136
+ ...args
137
+ ]);
138
+ return original.apply(engine.input, args);
139
+ };
140
+ }
141
+ const originalTick = engine.tick.bind(engine);
142
+ const engineAny = engine;
143
+ engineAny.tick = (nowMs) => {
144
+ ticks.push(nowMs);
145
+ originalTick(nowMs);
146
+ };
147
+ let stopped = false;
148
+ return { stop() {
149
+ if (!stopped) {
150
+ stopped = true;
151
+ for (const [name, fn] of originals) input[name] = fn;
152
+ engineAny.tick = originalTick;
153
+ }
154
+ return {
155
+ replay: 1,
156
+ ticks,
157
+ events
158
+ };
159
+ } };
160
+ }
161
+ /**
162
+ * Drive a fresh engine through a recording: for each recorded tick, feed
163
+ * that tick's input events first, then tick with the recorded timestamp.
164
+ * `onTick(index)` lets tests assert mid-run state.
165
+ */
166
+ function replay(engine, recording, opts) {
167
+ const input = engine.input;
168
+ let cursor = 0;
169
+ for (let i = 0; i < recording.ticks.length; i++) {
170
+ while (cursor < recording.events.length && recording.events[cursor][0] === i) {
171
+ const [, method, ...args] = recording.events[cursor];
172
+ const fn = input[method];
173
+ if (typeof fn === "function") fn.apply(engine.input, args);
174
+ cursor += 1;
175
+ }
176
+ engine.tick(recording.ticks[i]);
177
+ opts?.onTick?.(i);
178
+ }
179
+ }
180
+ //#endregion
181
+ export { startRecording as n, auditScene as r, replay as t };
@@ -0,0 +1,86 @@
1
+ import { b as Engine } from "./behavior-5o1EkbLD.js";
2
+ import { s as JsonObject } from "./schema-CFeioQRE.js";
3
+
4
+ //#region src/core/audit.d.ts
5
+ /** Human-readable warnings (empty = clean). Pure JSON walk, no registry. */
6
+ declare function auditScene(scene: JsonObject): string[];
7
+ //#endregion
8
+ //#region src/core/errors.d.ts
9
+ /**
10
+ * Stable error codes for every hard failure in the engine.
11
+ *
12
+ * Incanto fails loudly with machine-readable codes and messages that list valid
13
+ * alternatives — AI agents self-correct on hard failures, not warnings.
14
+ */
15
+ type IncantoErrorCode = "BAD_FORMAT" | "BAD_NODE_PATH" | "DANGLING_CONNECTION" | "DUPLICATE_BEHAVIOR" | "DUPLICATE_NODE_TYPE" | "DUPLICATE_UNIQUE_NAME" | "DUPLICATE_UID" | "NODE_NOT_FOUND" | "PROP_TYPE_MISMATCH" | "TREE_VIOLATION" | "UNKNOWN_ANIMATION" | "UNKNOWN_ASSET" | "UNKNOWN_CONSTANT" | "UNKNOWN_BEHAVIOR" | "UNKNOWN_HANDLER" | "UNKNOWN_NODE_TYPE" | "UNKNOWN_PROP" | "UNKNOWN_SIGNAL" | "UNRESOLVED_INSTANCE";
16
+ /**
17
+ * Structured context mirrored from the prose message — tools and agents parse
18
+ * THESE instead of regexing the message (which is for humans and may reword).
19
+ */
20
+ interface IncantoErrorDetails {
21
+ /** Absolute path of the offending node in the scene being loaded ('/Root/Child'). */
22
+ readonly path?: string;
23
+ /** The offending node's uid, when the scene JSON declared one. */
24
+ readonly uid?: string;
25
+ /** The offending node's type name. */
26
+ readonly nodeType?: string;
27
+ /** The offending prop key. */
28
+ readonly prop?: string;
29
+ /** The offending signal name. */
30
+ readonly signal?: string;
31
+ /** The valid alternatives the message also lists — for programmatic self-correction. */
32
+ readonly validOptions?: readonly string[];
33
+ }
34
+ declare class IncantoError extends Error {
35
+ readonly code: IncantoErrorCode;
36
+ readonly details: IncantoErrorDetails;
37
+ constructor(code: IncantoErrorCode, message: string, details?: IncantoErrorDetails);
38
+ }
39
+ //#endregion
40
+ //#region src/core/replay.d.ts
41
+ /**
42
+ * Deterministic input replay — the engine already runs on a seeded RNG and
43
+ * an injectable clock, so a recording of (tick timestamps + input events)
44
+ * replays BIT-IDENTICALLY into a fresh engine with the same seed and scene.
45
+ * That turns "does my game still work?" into an assertion an agent can run
46
+ * headlessly: record once while playing, replay forever as a regression.
47
+ *
48
+ * const rec = startRecording(engine); // play the game...
49
+ * const replayJson = rec.stop(); // ...save this JSON
50
+ *
51
+ * const fresh = new Engine({ seed: SAME_SEED }); // same seed + scene JSON
52
+ * fresh.setScene(loadScene(sceneJson));
53
+ * replay(fresh, replayJson); // now assert game state
54
+ *
55
+ * Caveats (documented, by design): game code must use `engine.rng` (not
56
+ * Math.random) and dt/engine.time (not Date.now) — the engine's own rules.
57
+ * Gamepad polling is not recorded (bind pads to actions; actions replay).
58
+ */
59
+ /** [tickIndex, methodName, ...primitive args] */
60
+ type ReplayEvent = [number, string, ...(string | number | boolean)[]];
61
+ interface ReplayJson {
62
+ /** File-format marker for forward compatibility. */
63
+ replay: 1;
64
+ /** Tick timestamps (ms) exactly as the loop delivered them. */
65
+ ticks: number[];
66
+ events: ReplayEvent[];
67
+ }
68
+ interface Recorder {
69
+ /** Detach the taps and return the recording. */
70
+ stop(): ReplayJson;
71
+ }
72
+ /**
73
+ * Tap the engine's input entry points and tick clock. Recording starts at
74
+ * the CURRENT moment — start it before the first tick for full-run replays.
75
+ */
76
+ declare function startRecording(engine: Engine): Recorder;
77
+ /**
78
+ * Drive a fresh engine through a recording: for each recorded tick, feed
79
+ * that tick's input events first, then tick with the recorded timestamp.
80
+ * `onTick(index)` lets tests assert mid-run state.
81
+ */
82
+ declare function replay(engine: Engine, recording: ReplayJson, opts?: {
83
+ onTick?: (index: number) => void;
84
+ }): void;
85
+ //#endregion
86
+ export { startRecording as a, IncantoErrorDetails as c, replay as i, auditScene as l, ReplayEvent as n, IncantoError as o, ReplayJson as r, IncantoErrorCode as s, Recorder as t };