incanto 0.66.0 → 0.68.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.
- package/bin/_behaviors-loader.mjs +87 -0
- package/bin/_pick-server.mjs +48 -0
- package/bin/incanto-check.mjs +16 -2
- package/bin/incanto-editor.mjs +39 -2
- package/bin/incanto-feel.mjs +4 -1
- package/bin/incanto-frame.mjs +37 -3
- package/bin/incanto-logs.mjs +23 -3
- package/bin/incanto-multiplay.mjs +50 -1
- package/bin/incanto-play.mjs +3 -1
- package/bin/incanto-playtest.mjs +3 -25
- package/bin/incanto-verify.mjs +15 -2
- package/dist/2d.d.ts +44 -7
- package/dist/2d.js +6 -4
- package/dist/3d.d.ts +112 -16
- package/dist/3d.js +8 -6
- package/dist/{audio-player-_UAcHxnC.d.ts → audio-player-DOrq7sP-.d.ts} +62 -4
- package/dist/{behavior-BXNLfIJk.d.ts → behavior-DoFPYrgo.d.ts} +117 -2
- package/dist/boot-failure-CKYrEcGF.js +47 -0
- package/dist/{create-game-DYJCIzO0.js → create-game-DbWtVTxD.js} +91 -29
- package/dist/{create-game-Dd7H4bJV.js → create-game-IZIydDwI.js} +16 -10
- package/dist/debug.d.ts +1 -1
- package/dist/debug.js +3 -2
- package/dist/{duplicate-CqSAtdrh.js → duplicate-IWIqk0HJ.js} +1 -1
- package/dist/{editor-switch-DAvWQeld.d.ts → editor-switch-CnIOiyNJ.d.ts} +1 -1
- package/dist/editor.js +18 -7
- package/dist/env.d.ts +1 -1
- package/dist/{environment-presets-BlPsEmq6.js → environment-presets-CybQXNqS.js} +100 -11
- package/dist/{gameplay-D1RADWu3.js → gameplay-DM1eu_cV.js} +46 -5
- package/dist/gameplay.d.ts +75 -6
- package/dist/gameplay.js +2 -2
- package/dist/index.d.ts +52 -8
- package/dist/index.js +9 -7
- package/dist/{loader-lQDCwNag.js → loader-BC4PNtJX.js} +173 -1
- package/dist/{loader-Cga7FVP4.d.ts → loader-CcB533FR.d.ts} +2 -2
- package/dist/net.d.ts +2 -2
- package/dist/net.js +1 -1
- package/dist/{physics-2d-Cji5A6sX.js → physics-2d-B7Y6dPZO.js} +29 -2
- package/dist/{physics-3d-BkHjwJgI.js → physics-3d-bG3n70Ky.js} +30 -3
- package/dist/react.d.ts +2 -2
- package/dist/react.js +1 -1
- package/dist/{register-BmuqYTiY.js → register-3ta-2Xig.js} +148 -22
- package/dist/{register-D0CxCveZ.js → register-ibCjm-wH.js} +3 -3
- package/dist/{replay-IsZbNu6d.js → replay-CYvhVHHN.js} +28 -4
- package/dist/{replay-BHoB6fCU.d.ts → replay-Dvn8aeBd.d.ts} +38 -4
- package/dist/{schema-CFeioQRE.d.ts → schema-B6ugCV1Q.d.ts} +11 -0
- package/dist/{split-screen-D_i7GRcY.js → split-screen-CYwDkbLF.js} +2 -2
- package/dist/{split-screen-eJUFjhi1.d.ts → split-screen-DhrSzZIB.d.ts} +3 -3
- package/dist/{src-BTLbXFPZ.js → src-D7RIqXYF.js} +1 -1
- package/dist/{teardown-byR9USax.js → teardown-D2NEmxPB.js} +2 -2
- package/dist/{test-DgrD0jHD.js → test-8hoHeRmo.js} +140 -32
- package/dist/test.d.ts +9 -7
- package/dist/test.js +3 -3
- package/dist/{touch-DESwnpOc.js → touch-DEAmqGdf.js} +30 -186
- package/dist/vite.d.ts +0 -7
- package/dist/vite.js +13 -3
- package/dist/webgl-unavailable-N9nQqesw.js +228 -0
- package/editor/assets/{agent8-9N-Pd_YS.js → agent8-BrrHOjMJ.js} +1 -1
- package/editor/assets/debug-BBhKuBNV.js +3 -0
- package/editor/assets/{index-CeDhIPTC.js → index-eVd0BToA.js} +92 -92
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/schemas/scene.schema.json +5 -1
- package/skills/incanto-3d-character.md +1 -0
- package/skills/incanto-audio.md +49 -0
- package/skills/incanto-building-2d-games.md +37 -0
- package/skills/incanto-building-3d-games.md +56 -0
- package/skills/incanto-editor.md +5 -1
- package/skills/incanto-environment.md +2 -2
- package/skills/incanto-gameplay-behaviors.md +29 -2
- package/skills/incanto-hud.md +14 -1
- package/skills/incanto-multiplayer.md +14 -0
- package/skills/incanto-node-reference.md +2 -1
- package/skills/incanto-performance.md +35 -0
- package/skills/incanto-physics-and-input.md +23 -0
- package/skills/incanto-playtesting.md +15 -1
- package/skills/incanto-save-slots.md +21 -1
- package/skills/incanto-scene-json-authoring.md +12 -1
- package/skills/incanto-verifying-your-game.md +19 -0
- package/skills/incanto-your-first-game.md +4 -4
- package/templates-app/beacon-isle-3d/package.json +1 -1
- package/templates-app/beacon-isle-3d/src/game.scene.json +38 -19
- package/templates-app/beacon-isle-3d/src/main.ts +8 -1
- package/templates-app/platformer-2d/package.json +1 -1
- package/templates-app/platformer-2d/src/game.scene.json +99 -53
- package/templates-app/platformer-2d/src/main.ts +8 -1
- package/templates-app/star-survivor/package.json +1 -1
- package/templates-app/star-survivor/src/game.scene.json +31 -17
- package/templates-app/star-survivor/src/main.ts +8 -1
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/tps-3d/src/game.scene.json +14 -8
- package/templates-app/tps-3d/src/main.ts +8 -1
- package/templates-app/village-quest-3d/package.json +1 -1
- package/templates-app/village-quest-3d/src/grove.scene.json +20 -11
- package/templates-app/village-quest-3d/src/main.ts +8 -1
- package/templates-app/village-quest-3d/src/village.scene.json +10 -7
- package/editor/assets/debug-CkbJICYp.js +0 -3
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import { k as diagnose } from "./loader-
|
|
2
|
+
import { k as diagnose } from "./loader-BC4PNtJX.js";
|
|
3
3
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
4
|
-
import { H as CharacterBody3D, K as Node3D, U as PhysicsBody3D, V as Area3D, W as RigidBody3D, q as validateCollider3D } from "./gameplay-
|
|
4
|
+
import { H as CharacterBody3D, K as Node3D, U as PhysicsBody3D, V as Area3D, W as RigidBody3D, q as validateCollider3D } from "./gameplay-DM1eu_cV.js";
|
|
5
5
|
import { n as registerDebugSource } from "./debug-draw-BM3DsvtT.js";
|
|
6
6
|
import { n as jointContacts, t as withoutRapierInitNoise } from "./quiet-rapier-C6fW4zcW.js";
|
|
7
|
-
import { A as Terrain3D, F as InstancedMesh3D, L as buildMeshGeometry, P as Joint3D } from "./environment-presets-
|
|
7
|
+
import { A as Terrain3D, F as InstancedMesh3D, L as buildMeshGeometry, P as Joint3D } from "./environment-presets-CybQXNqS.js";
|
|
8
8
|
import { Euler, Matrix4, Quaternion, Vector3 } from "three";
|
|
9
9
|
//#region src/3d/physics/collider-lines.ts
|
|
10
10
|
/**
|
|
@@ -262,6 +262,7 @@ var Physics3D = class {
|
|
|
262
262
|
this.unregisterDebug = registerDebugSource(this);
|
|
263
263
|
this.disconnect = engine.fixedUpdated.connect((dt) => this.step(dt));
|
|
264
264
|
this.disconnectScene = engine.sceneChanged.connect(() => {
|
|
265
|
+
this.pruneOverlaps();
|
|
265
266
|
if (engine.scene) this.syncBodies();
|
|
266
267
|
});
|
|
267
268
|
engine.physics = this;
|
|
@@ -609,6 +610,7 @@ var Physics3D = class {
|
|
|
609
610
|
this.byColliderHandle.delete(e.collider.handle);
|
|
610
611
|
this.world.removeRigidBody(e.body);
|
|
611
612
|
this.entries.delete(node);
|
|
613
|
+
this.forgetOverlaps(node);
|
|
612
614
|
node._physics = null;
|
|
613
615
|
node._interpPrev = node._interpCurr = null;
|
|
614
616
|
}
|
|
@@ -824,6 +826,31 @@ var Physics3D = class {
|
|
|
824
826
|
}
|
|
825
827
|
}
|
|
826
828
|
/**
|
|
829
|
+
* Forget every overlap whose node has left the tree.
|
|
830
|
+
*
|
|
831
|
+
* The lazy drop inside `overlapping()` only cleans the SETS of a key someone
|
|
832
|
+
* asks about, and only when they ask. This clears the KEYS, which is where the
|
|
833
|
+
* dead nodes were pinned.
|
|
834
|
+
*/
|
|
835
|
+
/** Drop one node from the overlap map, both as a key and as a member. */
|
|
836
|
+
forgetOverlaps(node) {
|
|
837
|
+
this.overlaps.delete(node);
|
|
838
|
+
for (const [self, others] of this.overlaps) {
|
|
839
|
+
others.delete(node);
|
|
840
|
+
if (others.size === 0) this.overlaps.delete(self);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
pruneOverlaps() {
|
|
844
|
+
for (const [self, others] of this.overlaps) {
|
|
845
|
+
if (self.tree === null) {
|
|
846
|
+
this.overlaps.delete(self);
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
for (const other of others) if (other.tree === null) others.delete(other);
|
|
850
|
+
if (others.size === 0) this.overlaps.delete(self);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
827
854
|
* The bodies currently overlapping `node` — the standing answer behind
|
|
828
855
|
* `triggerEnter`/`triggerExit`.
|
|
829
856
|
*
|
package/dist/react.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as Scene, b as Engine, n as BehaviorCtor } from "./behavior-
|
|
2
|
-
import { c as JsonValue } from "./schema-
|
|
1
|
+
import { P as Scene, b as Engine, n as BehaviorCtor } from "./behavior-DoFPYrgo.js";
|
|
2
|
+
import { c as JsonValue } from "./schema-B6ugCV1Q.js";
|
|
3
3
|
import { CSSProperties, ReactNode } from "react";
|
|
4
4
|
|
|
5
5
|
//#region src/react/index.d.ts
|
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-
|
|
159
|
+
const next = await (_gameFactory ?? (mode === "3d" ? async (o) => (await import("./create-game-DbWtVTxD.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-IZIydDwI.js").then((n) => n.n)).createGame2D(o)))(opts);
|
|
160
160
|
if (disposed) {
|
|
161
161
|
next.dispose();
|
|
162
162
|
return;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import { C as ORDER_GROUP_BASE, E as InputMap, O as Node, S as createSaveStore, T as resolveOrderGroups, V as Signal, b as restoreBehaviors, k as diagnose, y as captureBehaviors } from "./loader-
|
|
2
|
+
import { C as ORDER_GROUP_BASE, E as InputMap, O as Node, S as createSaveStore, T as resolveOrderGroups, V as Signal, b as restoreBehaviors, k as diagnose, y as captureBehaviors } from "./loader-BC4PNtJX.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 { l as registerNode } from "./registry-CF70EArN.js";
|
|
6
|
+
import { r as capturePointer } from "./touch-DEAmqGdf.js";
|
|
6
7
|
//#region src/core/audio/buses.ts
|
|
7
8
|
const AUDIO_LOG_LIMIT = 200;
|
|
8
9
|
var AudioBuses = class {
|
|
@@ -178,6 +179,11 @@ var WebAudioTrack = class {
|
|
|
178
179
|
setLoop(loop) {
|
|
179
180
|
this.el.loop = loop;
|
|
180
181
|
}
|
|
182
|
+
/** The element's own playhead — the clock the sound is actually on. */
|
|
183
|
+
playhead() {
|
|
184
|
+
const at = this.el.currentTime;
|
|
185
|
+
return typeof at === "number" && Number.isFinite(at) ? at : null;
|
|
186
|
+
}
|
|
181
187
|
play() {
|
|
182
188
|
this.el.play().catch((error) => {
|
|
183
189
|
const name = error?.name ?? "";
|
|
@@ -242,7 +248,8 @@ const SILENT_BACKEND = {
|
|
|
242
248
|
setGain() {},
|
|
243
249
|
setLoop() {},
|
|
244
250
|
play() {},
|
|
245
|
-
stop() {}
|
|
251
|
+
stop() {},
|
|
252
|
+
playhead: () => null
|
|
246
253
|
}),
|
|
247
254
|
unlock() {}
|
|
248
255
|
};
|
|
@@ -282,6 +289,24 @@ var MusicManager = class {
|
|
|
282
289
|
get current() {
|
|
283
290
|
return this.active?.track.src ?? null;
|
|
284
291
|
}
|
|
292
|
+
/**
|
|
293
|
+
* Seconds into the current track — the playhead of the thing you can HEAR.
|
|
294
|
+
*
|
|
295
|
+
* `null` when nothing is playing, or when the backend has no clock (headless,
|
|
296
|
+
* or a custom backend that does not implement it).
|
|
297
|
+
*
|
|
298
|
+
* A game charted to a soundtrack needs this and there was no way to get it:
|
|
299
|
+
* `current` gave the src and the backend was private, so the only option was
|
|
300
|
+
* an `engine.time` counter started next to `music.play()` — a different clock
|
|
301
|
+
* from the one the audio is on, and one that keeps counting through a
|
|
302
|
+
* gesture-block, a stall or a seek. This is the audio's own answer.
|
|
303
|
+
*
|
|
304
|
+
* It is the RAW element time, so a looping track wraps to 0 at each pass:
|
|
305
|
+
* that is a loop boundary you can chart against, not a fault.
|
|
306
|
+
*/
|
|
307
|
+
get playhead() {
|
|
308
|
+
return this.active?.track.playhead?.() ?? null;
|
|
309
|
+
}
|
|
285
310
|
/** Per-manager volume (0..1), on top of the bus gain. */
|
|
286
311
|
get volume() {
|
|
287
312
|
return this._volume;
|
|
@@ -1035,6 +1060,16 @@ var SfxEngine = class {
|
|
|
1035
1060
|
if (ctx && ctx.state === "suspended") ctx.resume();
|
|
1036
1061
|
}
|
|
1037
1062
|
/**
|
|
1063
|
+
* The AUDIO clock, in seconds — the one a sound is actually scheduled
|
|
1064
|
+
* against, and the only clock that does not drift relative to what you hear.
|
|
1065
|
+
*
|
|
1066
|
+
* `0` headless and before the first sound, since there is no context yet.
|
|
1067
|
+
* Schedule with a lead: `sfx.play(params, 1, { when: sfx.now + 0.08 })`.
|
|
1068
|
+
*/
|
|
1069
|
+
get now() {
|
|
1070
|
+
return this.ctx?.currentTime ?? 0;
|
|
1071
|
+
}
|
|
1072
|
+
/**
|
|
1038
1073
|
* Play a preset at `gain` (0..1, already bus-multiplied by the caller). Each
|
|
1039
1074
|
* call spawns its own source so rapid repeats overlap instead of cutting off.
|
|
1040
1075
|
* Headless → no-op.
|
|
@@ -1076,7 +1111,7 @@ var SfxEngine = class {
|
|
|
1076
1111
|
const source = ctx.createBufferSource();
|
|
1077
1112
|
source.buffer = buffer;
|
|
1078
1113
|
source.connect(gainNode);
|
|
1079
|
-
source.start();
|
|
1114
|
+
source.start(opts.when === void 0 ? 0 : Math.max(0, opts.when));
|
|
1080
1115
|
}
|
|
1081
1116
|
};
|
|
1082
1117
|
//#endregion
|
|
@@ -1339,11 +1374,29 @@ var HudLayer = class extends Node {
|
|
|
1339
1374
|
}
|
|
1340
1375
|
}
|
|
1341
1376
|
}
|
|
1342
|
-
/**
|
|
1377
|
+
/**
|
|
1378
|
+
* Focusable widgets under this layer, in tree order — **on screen only**.
|
|
1379
|
+
*
|
|
1380
|
+
* This filtered on each widget's OWN `visible` and recursed into hidden
|
|
1381
|
+
* containers anyway, so arrow-keys walked into closed panels and Enter really
|
|
1382
|
+
* pressed what it found. Measured on a title screen: 23 focus stops, 21 of
|
|
1383
|
+
* them under an ancestor with `visible = false`, and
|
|
1384
|
+
*
|
|
1385
|
+
* gold before = 150 -> Enter on the hidden Shop/Row0/Buy -> gold 120,
|
|
1386
|
+
* screen still 'title'
|
|
1387
|
+
*
|
|
1388
|
+
* The layer's own visibility was not a gate either. It is now: a HUD nobody
|
|
1389
|
+
* can see has no ring, and a subtree nobody can see is not walked.
|
|
1390
|
+
*
|
|
1391
|
+
* PUBLIC, because a game that wants to know what its ring contains could not
|
|
1392
|
+
* ask — and "why is focus on that" is exactly the question this answers.
|
|
1393
|
+
*/
|
|
1343
1394
|
focusables() {
|
|
1344
1395
|
const out = [];
|
|
1396
|
+
if (!this.visible) return out;
|
|
1345
1397
|
const walk = (node) => {
|
|
1346
|
-
if (node instanceof HudWidgetBase && node.
|
|
1398
|
+
if (node instanceof HudWidgetBase && !node.visible) return;
|
|
1399
|
+
if (node instanceof HudWidgetBase && node.focusable) out.push(node);
|
|
1347
1400
|
for (const child of node.children) walk(child);
|
|
1348
1401
|
};
|
|
1349
1402
|
walk(this);
|
|
@@ -1513,7 +1566,7 @@ var HudWidgetBase = class extends Node {
|
|
|
1513
1566
|
el.addEventListener("pointerdown", (event) => {
|
|
1514
1567
|
if (!this.draggable) return;
|
|
1515
1568
|
event.preventDefault();
|
|
1516
|
-
el
|
|
1569
|
+
capturePointer(el, event.pointerId);
|
|
1517
1570
|
dragging = this;
|
|
1518
1571
|
draggingFrom = dropTargetFromElement(el.parentElement);
|
|
1519
1572
|
el.style.opacity = "0.6";
|
|
@@ -1553,10 +1606,16 @@ var HudWidgetBase = class extends Node {
|
|
|
1553
1606
|
this._mountWidget();
|
|
1554
1607
|
if (!this._element) return;
|
|
1555
1608
|
}
|
|
1556
|
-
this._element.style.display = this.visible ? "" : "none";
|
|
1557
1609
|
this._followAnchor();
|
|
1558
1610
|
this._sync();
|
|
1611
|
+
if (this._element.style.display !== "none") this._shownDisplay = this._element.style.display;
|
|
1612
|
+
this._element.style.display = this.visible ? this._shownDisplay : "none";
|
|
1559
1613
|
}
|
|
1614
|
+
/**
|
|
1615
|
+
* The display this widget's own css asks for while shown — `grid` for a grid
|
|
1616
|
+
* panel, `flex` for a row, `''` for a widget that never says.
|
|
1617
|
+
*/
|
|
1618
|
+
_shownDisplay = "";
|
|
1560
1619
|
/** Which anchor slot this widget was mounted into. */
|
|
1561
1620
|
mountedAnchor = null;
|
|
1562
1621
|
/**
|
|
@@ -2280,14 +2339,9 @@ function qualityEnvironment(tier) {
|
|
|
2280
2339
|
};
|
|
2281
2340
|
if (tier === "medium") return {
|
|
2282
2341
|
shadows: { static: true },
|
|
2283
|
-
bloom: {},
|
|
2284
2342
|
post: null
|
|
2285
2343
|
};
|
|
2286
|
-
return {
|
|
2287
|
-
shadows: true,
|
|
2288
|
-
bloom: {},
|
|
2289
|
-
post: {}
|
|
2290
|
-
};
|
|
2344
|
+
return { shadows: true };
|
|
2291
2345
|
}
|
|
2292
2346
|
const QUALITY_CAPS = Object.freeze({
|
|
2293
2347
|
low: Object.freeze({
|
|
@@ -2436,7 +2490,7 @@ var Engine = class {
|
|
|
2436
2490
|
* reset on reload. Reading it here costs nothing (localStorage, once) and
|
|
2437
2491
|
* `bindAudio` makes the volumes write themselves.
|
|
2438
2492
|
*/
|
|
2439
|
-
settings
|
|
2493
|
+
settings;
|
|
2440
2494
|
/**
|
|
2441
2495
|
* Translations, and the locale in force. English is the base and the default;
|
|
2442
2496
|
* anything a locale does not declare falls back to it silently.
|
|
@@ -2510,6 +2564,7 @@ var Engine = class {
|
|
|
2510
2564
|
this.scheduler = opts.scheduler ?? rafScheduler;
|
|
2511
2565
|
this.rng = new Rng(opts.seed ?? Math.random() * 4294967295 >>> 0);
|
|
2512
2566
|
this.maxFps = opts.maxFps ?? 0;
|
|
2567
|
+
this.settings = opts.settings instanceof Settings ? opts.settings : new Settings(opts.settings?.namespace ?? "settings");
|
|
2513
2568
|
this.sfx._setBuses(this.audio);
|
|
2514
2569
|
this.music.onTrackError = (src, reason) => {
|
|
2515
2570
|
diagnose(this, "error", `[incanto] music '${src}' will not play — ${reason}.`);
|
|
@@ -2636,8 +2691,9 @@ var Engine = class {
|
|
|
2636
2691
|
captureState() {
|
|
2637
2692
|
const root = this.scene?.root;
|
|
2638
2693
|
if (!root) return {};
|
|
2639
|
-
const { state, unaddressable } = captureBehaviors(root, this.scene?.source);
|
|
2694
|
+
const { state, unaddressable, unrecordable } = captureBehaviors(root, this.scene?.source);
|
|
2640
2695
|
for (const path of unaddressable) diagnose(this, "warn", `save: ${path} has state to save and no uid to key it under.`);
|
|
2696
|
+
if (unrecordable.length > 0) diagnose(this, "error", `[incanto] save: ${unrecordable.length} authored node(s) this run consumed have no uid, so nothing records that they are gone and LOADING THIS SAVE BRINGS THEM BACK: ${unrecordable.slice(0, 8).join(", ")}${unrecordable.length > 8 ? `, +${unrecordable.length - 8} more` : ""}. Give each one a "uid" from newUid().`);
|
|
2641
2697
|
return state;
|
|
2642
2698
|
}
|
|
2643
2699
|
/** Give it back, after the scene is loaded and onReady has run. */
|
|
@@ -2983,10 +3039,32 @@ var AudioPlayer = class extends Node {
|
|
|
2983
3039
|
rolloff: this.rolloff
|
|
2984
3040
|
};
|
|
2985
3041
|
}
|
|
2986
|
-
|
|
3042
|
+
/**
|
|
3043
|
+
* Start the sound. `at` schedules it on the AUDIO clock instead of now.
|
|
3044
|
+
*
|
|
3045
|
+
* A frame is a 16.67 ms grid at 60 Hz and 33.33 ms at 30, so a sound fired
|
|
3046
|
+
* from `update()` cannot land closer than one frame to where a chart wants
|
|
3047
|
+
* it — measured 0 of 64 notes scheduled on the documented path. Queue it a
|
|
3048
|
+
* lead ahead of `engine.sfx.now` and the same notes land at |mean| 0.000000
|
|
3049
|
+
* ms:
|
|
3050
|
+
*
|
|
3051
|
+
* ```ts
|
|
3052
|
+
* const lead = 0.08; // schedule this far ahead
|
|
3053
|
+
* for (const note of dueSoon(engine.sfx.now + lead)) {
|
|
3054
|
+
* hit.play(engine.sfx.now + (note.atSec - songSeconds));
|
|
3055
|
+
* }
|
|
3056
|
+
* ```
|
|
3057
|
+
*
|
|
3058
|
+
* PRESETS only — a `src` clip goes through an `<audio>` element, which has no
|
|
3059
|
+
* scheduling clock, and passing `at` for one is ignored rather than silently
|
|
3060
|
+
* approximated. Everything else is unchanged: the bus gain applies, the sound
|
|
3061
|
+
* lands in `engine.audio.recent()`, and `muted` still silences it, none of
|
|
3062
|
+
* which a hand-rolled `AudioContext` scheduler keeps.
|
|
3063
|
+
*/
|
|
3064
|
+
play(at) {
|
|
2987
3065
|
if (this.preset !== "custom") {
|
|
2988
3066
|
this.note("preset", this.preset);
|
|
2989
|
-
this.playPreset();
|
|
3067
|
+
this.playPreset(at);
|
|
2990
3068
|
return;
|
|
2991
3069
|
}
|
|
2992
3070
|
if (!this.src) return;
|
|
@@ -3030,7 +3108,7 @@ var AudioPlayer = class extends Node {
|
|
|
3030
3108
|
* the length is arithmetic, not a device. WebAudio needs a gesture; a
|
|
3031
3109
|
* suspended context is resumed by the same unlock listener as `src` players.
|
|
3032
3110
|
*/
|
|
3033
|
-
playPreset() {
|
|
3111
|
+
playPreset(at) {
|
|
3034
3112
|
const params = SFX_PRESETS[this.preset];
|
|
3035
3113
|
if (!params) return;
|
|
3036
3114
|
const engine = this.tree?.engine;
|
|
@@ -3041,7 +3119,8 @@ var AudioPlayer = class extends Node {
|
|
|
3041
3119
|
sfx.play(params, this.gain(), {
|
|
3042
3120
|
pitch: this.pitch,
|
|
3043
3121
|
seed: this.seed,
|
|
3044
|
-
spatial: this.spatialPlay()
|
|
3122
|
+
spatial: this.spatialPlay(),
|
|
3123
|
+
when: at
|
|
3045
3124
|
});
|
|
3046
3125
|
}
|
|
3047
3126
|
/**
|
|
@@ -3077,6 +3156,10 @@ var AudioPlayer = class extends Node {
|
|
|
3077
3156
|
}
|
|
3078
3157
|
if (this.presetEndsAt !== null) {
|
|
3079
3158
|
if ((this.tree?.engine?.unscaledTime ?? 0) >= this.presetEndsAt) {
|
|
3159
|
+
if (this.loop) {
|
|
3160
|
+
this.playPreset();
|
|
3161
|
+
return;
|
|
3162
|
+
}
|
|
3080
3163
|
this.presetEndsAt = null;
|
|
3081
3164
|
this._playing = false;
|
|
3082
3165
|
this.emit("finished");
|
|
@@ -3187,6 +3270,7 @@ var UiDialogue = class extends HudWidgetBase {
|
|
|
3187
3270
|
];
|
|
3188
3271
|
static props = {
|
|
3189
3272
|
...HudWidgetBase.props,
|
|
3273
|
+
focusable: { default: true },
|
|
3190
3274
|
anchor: {
|
|
3191
3275
|
default: "bottom",
|
|
3192
3276
|
options: [
|
|
@@ -3236,6 +3320,49 @@ var UiDialogue = class extends HudWidgetBase {
|
|
|
3236
3320
|
});
|
|
3237
3321
|
}
|
|
3238
3322
|
/**
|
|
3323
|
+
* A dialogue takes focus while it is up.
|
|
3324
|
+
*
|
|
3325
|
+
* It paints a focus ring and the generated reference advertises the prop, and
|
|
3326
|
+
* it was inert: choices are raw DOM buttons inside the widget, so
|
|
3327
|
+
* `HudLayer.focusables()` can never see them, and this class — unlike
|
|
3328
|
+
* `UiButton` a hundred lines above it — never overrode `_activate`. Eleven
|
|
3329
|
+
* keys and eight pad codes did nothing to a waiting choice while the same
|
|
3330
|
+
* Enter pressed a sibling button. Native Tab worked, and is closed by any
|
|
3331
|
+
* game that binds Tab; a pad has no Tab at all.
|
|
3332
|
+
*/
|
|
3333
|
+
focusable = true;
|
|
3334
|
+
/** Which choice the keyboard/pad is on. Reset by every new line. */
|
|
3335
|
+
cursor = 0;
|
|
3336
|
+
/**
|
|
3337
|
+
* Enter / A, and left / right between the options.
|
|
3338
|
+
*
|
|
3339
|
+
* `dir` is 0 for "activate" and ±1 for a nudge, the same shape `UiSelect`
|
|
3340
|
+
* uses — so one key layout drives a menu, a slider and a conversation.
|
|
3341
|
+
*/
|
|
3342
|
+
_activate(dir) {
|
|
3343
|
+
if (!this.active || !this.current) return;
|
|
3344
|
+
const choices = this.current.choices;
|
|
3345
|
+
if (!choices || choices.length === 0) {
|
|
3346
|
+
this.advance();
|
|
3347
|
+
return;
|
|
3348
|
+
}
|
|
3349
|
+
if (dir !== 0) {
|
|
3350
|
+
this.cursor = (this.cursor + dir + choices.length) % choices.length;
|
|
3351
|
+
this.paintCursor();
|
|
3352
|
+
return;
|
|
3353
|
+
}
|
|
3354
|
+
this.choose(this.cursor);
|
|
3355
|
+
}
|
|
3356
|
+
/** Show which option the keyboard is on — a ring nobody can see is not one. */
|
|
3357
|
+
paintCursor() {
|
|
3358
|
+
const buttons = this.choicesEl?.children;
|
|
3359
|
+
if (!buttons) return;
|
|
3360
|
+
for (let i = 0; i < buttons.length; i++) {
|
|
3361
|
+
const el = buttons[i];
|
|
3362
|
+
if (el) el.style.outline = i === this.cursor ? "2px solid #fff" : "none";
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
/**
|
|
3239
3366
|
* Player pressed "next": mid-typing reveals the whole line; a fully-typed
|
|
3240
3367
|
* line without choices advances (choice lines wait for `choose`).
|
|
3241
3368
|
*/
|
|
@@ -3318,12 +3445,11 @@ var UiDialogue = class extends HudWidgetBase {
|
|
|
3318
3445
|
this.revealed = Math.round(progress * this.current.text.length);
|
|
3319
3446
|
if (this.speakerEl) this.speakerEl.textContent = this.current.speaker;
|
|
3320
3447
|
}
|
|
3321
|
-
|
|
3322
|
-
if (wasTyping) this.revealed = Math.min(this.current.text.length, this.revealed + this.charsPerSecond * dt);
|
|
3448
|
+
if (this.revealed < this.current.text.length) this.revealed = Math.min(this.current.text.length, this.revealed + this.charsPerSecond * dt);
|
|
3323
3449
|
const doneTyping = this.revealed >= this.current.text.length;
|
|
3324
3450
|
if (this.textEl) this.textEl.textContent = this.showing;
|
|
3325
3451
|
if (this.hintEl) this.hintEl.style.visibility = doneTyping && !this.current.choices ? "visible" : "hidden";
|
|
3326
|
-
if (doneTyping &&
|
|
3452
|
+
if (doneTyping && this.current.choices && this.choicesEl && this.choicesEl.children.length === 0) this.current.choices.forEach((label, i) => {
|
|
3327
3453
|
const b = document.createElement("button");
|
|
3328
3454
|
b.type = "button";
|
|
3329
3455
|
b.textContent = label;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { O as Node, k as diagnose, w as effectiveOrder } from "./loader-
|
|
2
|
-
import { L as translateOn, t as registerCoreNodes } from "./register-
|
|
1
|
+
import { O as Node, k as diagnose, w as effectiveOrder } from "./loader-BC4PNtJX.js";
|
|
2
|
+
import { L as translateOn, t as registerCoreNodes } from "./register-3ta-2Xig.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-CF70EArN.js";
|
|
6
6
|
import { i as ParticleSim, n as resolveFrames, o as PARTICLE_PRESET_NAMES, r as validateAnimationAliases, s as applyParticlePreset, t as resolveAnimation } from "./sprite-animation-CY-mrr1L.js";
|
|
7
|
-
import { F as localFromWorld, I as worldPosition } from "./gameplay-
|
|
7
|
+
import { F as localFromWorld, I as worldPosition } from "./gameplay-DM1eu_cV.js";
|
|
8
8
|
import { t as checkSheetGrid } from "./sheet-grid-BT6N_Bjs.js";
|
|
9
9
|
import { AdditiveBlending, BufferAttribute, BufferGeometry, CanvasTexture, Color, DynamicDrawUsage, Group, InstancedMesh, MathUtils, Matrix4, Mesh, MeshBasicMaterial, NearestFilter, NormalBlending, Object3D, PlaneGeometry, SRGBColorSpace } from "three";
|
|
10
10
|
//#region src/2d/physics/collider-validate.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { D as handlerShadowWarnings, j as nodeRefWarnings } from "./loader-
|
|
2
|
-
import { R as translationKey, k as HudWidgetBase } from "./register-
|
|
1
|
+
import { D as handlerShadowWarnings, j as nodeRefWarnings } from "./loader-BC4PNtJX.js";
|
|
2
|
+
import { R as translationKey, k as HudWidgetBase } from "./register-3ta-2Xig.js";
|
|
3
3
|
import { o as getNodeType } from "./registry-CF70EArN.js";
|
|
4
4
|
//#region src/core/audit.ts
|
|
5
5
|
const ANIMATED_TYPES = new Set([
|
|
@@ -104,6 +104,28 @@ function transformOf(props) {
|
|
|
104
104
|
if (scaled) return "scale";
|
|
105
105
|
return null;
|
|
106
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Which files in a corpus some OTHER scene embeds with `instance`.
|
|
109
|
+
*
|
|
110
|
+
* A fragment cannot be recognised by looking at itself — `enemy.scene.json` and
|
|
111
|
+
* a broken first level are the same file shape. It is recognised by the project
|
|
112
|
+
* around it, which every tool that audits more than one scene already walks.
|
|
113
|
+
*
|
|
114
|
+
* `resolveRef(fromFile, ref)` turns a scene-relative `instance` path into the
|
|
115
|
+
* same spelling the corpus uses for that file, so the caller owns all path
|
|
116
|
+
* logic and this stays runnable in a browser.
|
|
117
|
+
*/
|
|
118
|
+
function instancedScenes(corpus, resolveRef) {
|
|
119
|
+
const instanced = /* @__PURE__ */ new Set();
|
|
120
|
+
const walk = (node, from) => {
|
|
121
|
+
if (!node || typeof node !== "object") return;
|
|
122
|
+
const ref = node.instance;
|
|
123
|
+
if (typeof ref === "string" && ref) instanced.add(resolveRef(from, ref));
|
|
124
|
+
for (const child of node.children ?? []) walk(child, from);
|
|
125
|
+
};
|
|
126
|
+
for (const { path, json } of corpus) walk(json.root, path);
|
|
127
|
+
return instanced;
|
|
128
|
+
}
|
|
107
129
|
function auditScene(scene, opts = {}) {
|
|
108
130
|
const warnings = [];
|
|
109
131
|
const root = scene.root;
|
|
@@ -166,7 +188,9 @@ function auditScene(scene, opts = {}) {
|
|
|
166
188
|
if (cameraDrivers.length > 0 && followedCameras.length > 0) warnings.push(`${followedCameras.join(", ")}: a FollowCamera writes this camera's position every frame, and so does the CharacterController3D at ${cameraDrivers.join(", ")} — the controller owns the camera in every view it has. Drop one of them: use the controller's own \`view\`/\`camDistance\`, or take the controller off the camera duty.`);
|
|
167
189
|
if (cameras > 0 && currentCameras === 0) warnings.push("no camera has \"current\": true — the screen renders from a default view, probably not what you framed.");
|
|
168
190
|
if (currentCameras > 1) warnings.push(`${currentCameras} cameras claim "current": true — only the first found wins.`);
|
|
169
|
-
|
|
191
|
+
const isFragment = opts.instanced === true || scene.fragment === true;
|
|
192
|
+
if (scene.dimension === "3d" && !isFragment && cameras === 0) warnings.push("this 3D scene has no camera — the screen renders from a default view, which is almost never the shot you want. Add a Camera3D with \"current\": true, or set \"fragment\": true in the scene header if this file is a prefab another scene drops in.");
|
|
193
|
+
if (scene.dimension === "3d" && !isFragment && lights === 0 && !isLit(scene.environment)) warnings.push("nothing lights this 3D scene — no light node, no environment sky/hdri, and no ambient intensity. It will render black. Add a DirectionalLight3D, or an \"environment\": { \"sky\": { \"type\": \"atmosphere\" } } / { \"preset\": \"...\" } — or set \"fragment\": true if another scene lights this one.");
|
|
170
194
|
if (damageGroups.length > 0) {
|
|
171
195
|
const hurtable = hurtableGroups(root);
|
|
172
196
|
for (const { path, group } of damageGroups) {
|
|
@@ -358,4 +382,4 @@ function replay(engine, recording, opts) {
|
|
|
358
382
|
}
|
|
359
383
|
}
|
|
360
384
|
//#endregion
|
|
361
|
-
export { startRecording as n, auditScene as r, replay as t };
|
|
385
|
+
export { instancedScenes as i, startRecording as n, auditScene as r, replay as t };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { b as Engine } from "./behavior-
|
|
2
|
-
import { s as JsonObject } from "./schema-
|
|
1
|
+
import { b as Engine } from "./behavior-DoFPYrgo.js";
|
|
2
|
+
import { s as JsonObject } from "./schema-B6ugCV1Q.js";
|
|
3
3
|
|
|
4
4
|
//#region src/core/audit.d.ts
|
|
5
5
|
/** Human-readable warnings (empty = clean). Pure JSON walk, no registry. */
|
|
@@ -15,7 +15,41 @@ interface AuditOptions {
|
|
|
15
15
|
* has one.
|
|
16
16
|
*/
|
|
17
17
|
declaredElsewhere?: Record<string, Record<string, string>>;
|
|
18
|
+
/**
|
|
19
|
+
* Another scene in this project embeds this file with `instance`.
|
|
20
|
+
*
|
|
21
|
+
* That, and only that, makes a scene a FRAGMENT — a thing lit and framed by
|
|
22
|
+
* whatever drops it in, which must not be told it will render black.
|
|
23
|
+
*
|
|
24
|
+
* The discriminator used to be "it has no camera", which is the same shape a
|
|
25
|
+
* beginner's FIRST 3D scene has. Measured on a ground plane and a capsule
|
|
26
|
+
* with no camera and no light — a guaranteed black screen:
|
|
27
|
+
*
|
|
28
|
+
* $ incanto-check
|
|
29
|
+
* ok src/game.scene.json
|
|
30
|
+
* 1/1 scene(s) valid
|
|
31
|
+
*
|
|
32
|
+
* Not the lighting warning, and not the camera one either. The checker walks
|
|
33
|
+
* the whole project already (it unions `strings` and `assets` the same way),
|
|
34
|
+
* so it can answer this by looking instead of guessing.
|
|
35
|
+
*/
|
|
36
|
+
instanced?: boolean;
|
|
18
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Which files in a corpus some OTHER scene embeds with `instance`.
|
|
40
|
+
*
|
|
41
|
+
* A fragment cannot be recognised by looking at itself — `enemy.scene.json` and
|
|
42
|
+
* a broken first level are the same file shape. It is recognised by the project
|
|
43
|
+
* around it, which every tool that audits more than one scene already walks.
|
|
44
|
+
*
|
|
45
|
+
* `resolveRef(fromFile, ref)` turns a scene-relative `instance` path into the
|
|
46
|
+
* same spelling the corpus uses for that file, so the caller owns all path
|
|
47
|
+
* logic and this stays runnable in a browser.
|
|
48
|
+
*/
|
|
49
|
+
declare function instancedScenes(corpus: {
|
|
50
|
+
path: string;
|
|
51
|
+
json: JsonObject;
|
|
52
|
+
}[], resolveRef: (fromFile: string, ref: string) => string): Set<string>;
|
|
19
53
|
declare function auditScene(scene: JsonObject, opts?: AuditOptions): string[];
|
|
20
54
|
//#endregion
|
|
21
55
|
//#region src/core/errors.d.ts
|
|
@@ -25,7 +59,7 @@ declare function auditScene(scene: JsonObject, opts?: AuditOptions): string[];
|
|
|
25
59
|
* Incanto fails loudly with machine-readable codes and messages that list valid
|
|
26
60
|
* alternatives — AI agents self-correct on hard failures, not warnings.
|
|
27
61
|
*/
|
|
28
|
-
type IncantoErrorCode = "AMBIGUOUS_HANDLER" | "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";
|
|
62
|
+
type IncantoErrorCode = "AMBIGUOUS_HANDLER" | "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" | "WEBGL_UNAVAILABLE";
|
|
29
63
|
/**
|
|
30
64
|
* Structured context mirrored from the prose message — tools and agents parse
|
|
31
65
|
* THESE instead of regexing the message (which is for humans and may reword).
|
|
@@ -100,4 +134,4 @@ declare function replay(engine: Engine, recording: ReplayJson, opts?: {
|
|
|
100
134
|
onTick?: (index: number) => void;
|
|
101
135
|
}): void;
|
|
102
136
|
//#endregion
|
|
103
|
-
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 };
|
|
137
|
+
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, instancedScenes as u };
|
|
@@ -74,6 +74,17 @@ interface SceneJson {
|
|
|
74
74
|
input?: JsonObject;
|
|
75
75
|
/** Physics config — `{gravity: [x,y]}` (2D px/s²) or `{gravity: [x,y,z]}` (3D m/s²). */
|
|
76
76
|
physics?: JsonObject;
|
|
77
|
+
/**
|
|
78
|
+
* This scene is a PIECE of another one — a spawner's prefab, an instanced
|
|
79
|
+
* sub-scene — and is never rendered on its own.
|
|
80
|
+
*
|
|
81
|
+
* It exists because no tool can tell a prefab from a broken first level by
|
|
82
|
+
* looking at the file: both are a small tree with no camera and no light. The
|
|
83
|
+
* audit used to guess ("no camera means fragment") and so went silent on the
|
|
84
|
+
* commonest beginner mistake there is. One key, written by the author who
|
|
85
|
+
* knows, is the honest version. It changes nothing at runtime.
|
|
86
|
+
*/
|
|
87
|
+
fragment?: boolean;
|
|
77
88
|
/** Multiplayer config (interpreted in M6; preserved verbatim until then). */
|
|
78
89
|
multiplayer?: JsonObject;
|
|
79
90
|
/** Scene-level rendering environment (ambient light, background — interpreted by renderers). */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { O as Node, V as Signal, k as diagnose, n as loadScene, t as buildNodeJson } from "./loader-
|
|
2
|
-
import { t as registerCoreNodes, v as Engine } from "./register-
|
|
1
|
+
import { O as Node, V as Signal, k as diagnose, n as loadScene, t as buildNodeJson } from "./loader-BC4PNtJX.js";
|
|
2
|
+
import { t as registerCoreNodes, v as Engine } from "./register-3ta-2Xig.js";
|
|
3
3
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
4
4
|
import { n as jsonEquals, t as jsonClone } from "./json-CwwhxQgb.js";
|
|
5
5
|
import { l as registerNode } from "./registry-CF70EArN.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Lt as Signal, b as Engine, jt as Node } from "./behavior-
|
|
2
|
-
import { c as JsonValue, i as SceneJson, s as JsonObject } from "./schema-
|
|
3
|
-
import { t as LoadSceneOptions } from "./loader-
|
|
1
|
+
import { Lt as Signal, b as Engine, jt as Node } from "./behavior-DoFPYrgo.js";
|
|
2
|
+
import { c as JsonValue, i as SceneJson, s as JsonObject } from "./schema-B6ugCV1Q.js";
|
|
3
|
+
import { t as LoadSceneOptions } from "./loader-CcB533FR.js";
|
|
4
4
|
|
|
5
5
|
//#region src/net/types.d.ts
|
|
6
6
|
type Unsubscribe = () => void;
|
|
@@ -214,6 +214,6 @@ function newUid() {
|
|
|
214
214
|
//#endregion
|
|
215
215
|
//#region src/index.ts
|
|
216
216
|
/** Engine version. Kept in sync with package.json by the release pipeline. */
|
|
217
|
-
const VERSION = "0.
|
|
217
|
+
const VERSION = "0.68.0";
|
|
218
218
|
//#endregion
|
|
219
219
|
export { preloadUrls as a, preloadSceneAssets as i, newUid as n, findPath as o, assetUrls as r, gridFromRows as s, VERSION as t };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { _ as AudioPlayer } from "./register-
|
|
2
|
-
import {
|
|
1
|
+
import { _ as AudioPlayer } from "./register-3ta-2Xig.js";
|
|
2
|
+
import { i as logReport, o as parseDrive } from "./webgl-unavailable-N9nQqesw.js";
|
|
3
3
|
import { a as frameSignature, i as frameImage, o as frameStats } from "./frame-report-BSMny7oe.js";
|
|
4
4
|
//#region src/core/audio-errors.ts
|
|
5
5
|
/** Every AudioPlayer under `root` whose source could not be played. */
|