incanto 0.68.0 → 0.70.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/assets/catalog.json +9 -5
- package/bin/_behaviors-loader.mjs +22 -0
- package/bin/_read-json.mjs +28 -0
- package/bin/incanto-assets.mjs +19 -5
- package/bin/incanto-check.mjs +92 -15
- package/bin/incanto-editor.mjs +128 -5
- package/bin/incanto-env.mjs +3 -2
- package/bin/incanto-feel.mjs +24 -13
- package/bin/incanto-frame.mjs +8 -1
- package/bin/incanto-multiplay.mjs +11 -9
- package/bin/incanto-new.mjs +128 -5
- package/bin/incanto-play.mjs +158 -16
- package/bin/incanto-playtest.mjs +66 -23
- package/bin/incanto-serve.mjs +160 -0
- package/bin/incanto-skills.mjs +14 -2
- package/bin/incanto-verify.mjs +165 -44
- package/bin/incanto.mjs +4 -2
- package/dist/2d.d.ts +285 -36
- package/dist/2d.js +4 -4
- package/dist/3d.d.ts +158 -10
- package/dist/3d.js +8 -8
- package/dist/{agent8-CvsfVskX.js → agent8-CmNF01gA.js} +61 -8
- package/dist/{audio-player-DOrq7sP-.d.ts → audio-player-DaMxqfNE.d.ts} +33 -14
- package/dist/{behavior-DoFPYrgo.d.ts → behavior-DZExDn9o.d.ts} +809 -44
- package/dist/{create-game-IZIydDwI.js → create-game-Bwvh6q8A.js} +148 -61
- package/dist/{create-game-DbWtVTxD.js → create-game-C7ffQWW7.js} +103 -49
- package/dist/debug.d.ts +1 -1
- package/dist/debug.js +2 -3
- package/dist/diagnostics-Cu85N3tL.d.ts +12 -0
- package/dist/{editor-switch-CnIOiyNJ.d.ts → editor-switch-CFU9mCec.d.ts} +22 -13
- package/dist/editor.js +1066 -864
- package/dist/env.d.ts +1 -1
- package/dist/env.js +5 -3
- package/dist/{environment-presets-CybQXNqS.js → environment-presets-D6Q5BxeE.js} +299 -46
- package/dist/{frame-report-DCnHFmto.d.ts → frame-report-DNxDAb1w.d.ts} +8 -0
- package/dist/{frame-report-BSMny7oe.js → frame-report-Dlq13Gyj.js} +1 -0
- package/dist/{gameplay-DM1eu_cV.js → gameplay-BfHkuzVb.js} +825 -221
- package/dist/gameplay.d.ts +187 -7
- package/dist/gameplay.js +1 -1
- package/dist/{heightmap-CRK0M4jT.js → heightmap-BYgD5Edk.js} +1 -1
- package/dist/index.d.ts +156 -13
- package/dist/index.js +10 -12
- package/dist/json-CfTjpvW8.js +67 -0
- package/dist/{loader-CcB533FR.d.ts → loader-Cff09LMm.d.ts} +2 -2
- package/dist/net.d.ts +27 -3
- package/dist/net.js +2 -2
- package/dist/{noise-CGUMx44x.js → noise-D3nPpmFg.js} +1 -1
- package/dist/{physics-2d-B7Y6dPZO.js → physics-2d-CE0Qvy3V.js} +136 -11
- package/dist/{physics-3d-bG3n70Ky.js → physics-3d-CpH-2gn5.js} +104 -23
- package/dist/{teardown-D2NEmxPB.js → picking-CQJ_PJKh.js} +106 -14
- package/dist/react.d.ts +2 -2
- package/dist/react.js +2 -2
- package/dist/{register-3ta-2Xig.js → register-6DYnKZcy.js} +652 -831
- package/dist/{register-ibCjm-wH.js → register-Bkk0wSDB.js} +348 -30
- package/dist/{replay-CYvhVHHN.js → replay-DjAkAzMq.js} +224 -14
- package/dist/{replay-Dvn8aeBd.d.ts → replay-Dmw-PKQu.d.ts} +20 -3
- package/dist/{schema-B6ugCV1Q.d.ts → rng-Bb-IutXB.d.ts} +38 -21
- package/dist/{rng-DP-SR7eg.js → rng-CDOMybym.js} +22 -0
- package/dist/{loader-BC4PNtJX.js → save-slots-BXVg148r.js} +4558 -2294
- package/dist/{sheet-grid-BT6N_Bjs.js → sheet-grid-Cea343VO.js} +6 -2
- package/dist/{split-screen-DhrSzZIB.d.ts → split-screen--k-XpBjr.d.ts} +36 -4
- package/dist/{split-screen-CYwDkbLF.js → split-screen-PL78oVXP.js} +159 -26
- package/dist/{sprite-animation-CY-mrr1L.js → sprite-animation-CqR2o3SA.js} +39 -8
- package/dist/{src-D7RIqXYF.js → src-Cxfiv1Hg.js} +2 -17
- package/dist/test-iHYVUcDK.js +4036 -0
- package/dist/test.d.ts +542 -30
- package/dist/test.js +3 -3
- package/dist/touch-BnCyPA0G.js +519 -0
- package/dist/vite.d.ts +54 -3
- package/dist/vite.js +349 -17
- package/dist/{webgl-unavailable-N9nQqesw.js → webgl-unavailable-C8aDbGmR.js} +56 -1
- package/editor/assets/agent8-D0MS174y.js +1 -0
- package/editor/assets/{debug-BBhKuBNV.js → debug-BnXkKuYu.js} +2 -2
- package/editor/assets/index-CIu3uc3l.js +11046 -0
- package/editor/index.html +1 -1
- package/package.json +7 -16
- package/schemas/scene.schema.json +30 -3
- package/skills/incanto-3d-character.md +14 -1
- package/skills/incanto-3d-models.md +12 -0
- package/skills/incanto-assets.md +25 -3
- package/skills/incanto-audio.md +27 -5
- package/skills/incanto-behaviors-and-scripts.md +83 -6
- package/skills/incanto-building-2d-games.md +106 -8
- package/skills/incanto-building-3d-games.md +118 -6
- package/skills/incanto-editor.md +46 -7
- package/skills/incanto-environment.md +19 -1
- package/skills/incanto-game-feel.md +70 -0
- package/skills/incanto-gameplay-behaviors.md +121 -14
- package/skills/incanto-hud.md +128 -7
- package/skills/incanto-localization.md +13 -5
- package/skills/incanto-multiplayer.md +83 -3
- package/skills/incanto-node-reference.md +222 -58
- package/skills/incanto-performance.md +52 -0
- package/skills/incanto-physics-and-input.md +123 -24
- package/skills/incanto-playtesting.md +78 -2
- package/skills/incanto-save-slots.md +188 -6
- package/skills/incanto-scene-json-authoring.md +69 -12
- package/skills/incanto-verifying-your-game.md +292 -12
- package/skills/incanto-web-integration.md +28 -0
- package/skills/incanto-your-first-game.md +5 -2
- package/templates-app/beacon-isle-3d/generate-world.ts +77 -9
- package/templates-app/beacon-isle-3d/package.json +2 -2
- package/templates-app/beacon-isle-3d/src/behaviors.ts +22 -0
- package/templates-app/beacon-isle-3d/src/game.scene.json +103 -378
- package/templates-app/beacon-isle-3d/src/main.ts +24 -4
- package/templates-app/beacon-isle-3d/tsconfig.json +1 -1
- package/templates-app/beacon-isle-3d/verify.ts +3 -1
- package/templates-app/molehill-2d/.incanto/playtest/lost-seed1.json +4277 -0
- package/templates-app/molehill-2d/PROJECT/Context.md +58 -0
- package/templates-app/molehill-2d/PROJECT/Requirements.md +39 -0
- package/templates-app/molehill-2d/PROJECT/Status.md +27 -0
- package/templates-app/molehill-2d/PROJECT/Structure.md +48 -0
- package/templates-app/molehill-2d/docs/project-2d-rules.md +44 -0
- package/templates-app/molehill-2d/index.html +73 -0
- package/templates-app/molehill-2d/package.json +23 -0
- package/templates-app/molehill-2d/src/behaviors.ts +198 -0
- package/templates-app/molehill-2d/src/game.scene.json +1255 -0
- package/templates-app/molehill-2d/src/main.ts +41 -0
- package/templates-app/molehill-2d/tsconfig.json +13 -0
- package/templates-app/molehill-2d/verify.ts +247 -0
- package/templates-app/molehill-2d/vite.config.ts +12 -0
- package/templates-app/platformer-2d/index.html +0 -23
- package/templates-app/platformer-2d/package.json +2 -2
- package/templates-app/platformer-2d/src/behaviors.ts +26 -16
- package/templates-app/platformer-2d/src/game.scene.json +143 -625
- package/templates-app/platformer-2d/src/main.ts +35 -13
- package/templates-app/platformer-2d/tsconfig.json +1 -1
- package/templates-app/star-survivor/package.json +2 -2
- package/templates-app/star-survivor/src/game.scene.json +41 -195
- package/templates-app/star-survivor/src/main.ts +28 -7
- package/templates-app/star-survivor/tsconfig.json +1 -1
- package/templates-app/tps-3d/PROJECT/Context.md +1 -1
- package/templates-app/tps-3d/package.json +2 -2
- package/templates-app/tps-3d/src/behaviors.ts +19 -1
- package/templates-app/tps-3d/src/game.scene.json +78 -217
- package/templates-app/tps-3d/src/main.ts +39 -17
- package/templates-app/tps-3d/tsconfig.json +1 -1
- package/templates-app/village-quest-3d/.incanto/playtest/swapped-seed1.json +1735 -0
- package/templates-app/village-quest-3d/package.json +2 -2
- package/templates-app/village-quest-3d/src/behaviors.ts +21 -0
- package/templates-app/village-quest-3d/src/grove.scene.json +54 -221
- package/templates-app/village-quest-3d/src/main.ts +24 -4
- package/templates-app/village-quest-3d/src/village.scene.json +199 -838
- package/templates-app/village-quest-3d/tsconfig.json +1 -1
- package/templates-app/village-quest-3d/verify.ts +14 -1
- package/dist/duplicate-IWIqk0HJ.js +0 -22
- package/dist/json-CwwhxQgb.js +0 -36
- package/dist/registry-CF70EArN.js +0 -212
- package/dist/rolldown-runtime-D7D4PA-g.js +0 -13
- package/dist/test-8hoHeRmo.js +0 -2340
- package/dist/touch-DEAmqGdf.js +0 -225
- package/editor/assets/agent8-BrrHOjMJ.js +0 -1
- package/editor/assets/index-eVd0BToA.js +0 -10958
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { _ as AudioPlayer, v as Engine } from "./register-
|
|
1
|
+
import { o as __exportAll } from "./json-CfTjpvW8.js";
|
|
2
|
+
import { E as resolveViewport, T as computeViewport, it as registerBehavior, p as loadScene, q as diagnose } from "./save-slots-BXVg148r.js";
|
|
3
|
+
import { C as qualityRendering, _ as AudioPlayer, v as Engine } from "./register-6DYnKZcy.js";
|
|
4
4
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
5
|
-
import { n as attachTouchControls } from "./touch-
|
|
6
|
-
import { n as withWebGLContext, r as resolveRendering } from "./webgl-unavailable-
|
|
7
|
-
import { a as
|
|
8
|
-
import { o as frameStats } from "./frame-report-
|
|
9
|
-
import { n as registerGameplayBehaviors } from "./gameplay-
|
|
10
|
-
import { g as PhysicsBody2D, n as UILayer, t as registerNodes2D, u as Camera2D, y as Node2D } from "./register-
|
|
5
|
+
import { n as attachTouchControls } from "./touch-BnCyPA0G.js";
|
|
6
|
+
import { n as withWebGLContext, r as resolveRendering } from "./webgl-unavailable-C8aDbGmR.js";
|
|
7
|
+
import { a as devServerLibrary, c as applyDriveStep, d as audioErrors, i as crossFade, l as wireDevChannel, n as teardown, o as openBundledEditor, r as pauseWhenHidden, t as nodeFromHits, u as claimCanvasGestures } from "./picking-CQJ_PJKh.js";
|
|
8
|
+
import { o as frameStats } from "./frame-report-Dlq13Gyj.js";
|
|
9
|
+
import { n as registerGameplayBehaviors } from "./gameplay-BfHkuzVb.js";
|
|
10
|
+
import { g as PhysicsBody2D, n as UILayer, t as registerNodes2D, u as Camera2D, y as Node2D } from "./register-Bkk0wSDB.js";
|
|
11
11
|
import { t as debugSources } from "./debug-draw-BM3DsvtT.js";
|
|
12
|
-
import { n as enablePhysics2D } from "./physics-2d-
|
|
12
|
+
import { n as enablePhysics2D } from "./physics-2d-CE0Qvy3V.js";
|
|
13
13
|
import { Box3, BufferAttribute, BufferGeometry, Color, LineBasicMaterial, LineSegments, LinearFilter, NearestFilter, OrthographicCamera, Raycaster, SRGBColorSpace, Scene, TextureLoader, Vector2, Vector3, WebGLRenderer } from "three";
|
|
14
14
|
//#region src/2d/assets.ts
|
|
15
15
|
/**
|
|
@@ -101,6 +101,20 @@ var AssetStore2D = class {
|
|
|
101
101
|
getTexture(ref) {
|
|
102
102
|
return this.resolve(ref).texture;
|
|
103
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Hand the GPU back.
|
|
106
|
+
*
|
|
107
|
+
* `AssetStore3D` has had this since it was written and the 2D twin never got
|
|
108
|
+
* one, so a game that swapped scenes or unmounted kept every texture it had
|
|
109
|
+
* ever decoded. `Renderer2D.dispose` walked its scene graph and disposed
|
|
110
|
+
* `material.map` instead — which frees only what is CURRENTLY drawn (a sheet
|
|
111
|
+
* loaded and never used stayed resident), and disposes the STORE'S texture
|
|
112
|
+
* out from under a caller who injected their own store.
|
|
113
|
+
*/
|
|
114
|
+
dispose() {
|
|
115
|
+
for (const entry of this.entries.values()) entry.texture?.dispose();
|
|
116
|
+
this.entries.clear();
|
|
117
|
+
}
|
|
104
118
|
getSheet(ref) {
|
|
105
119
|
const entry = this.resolve(ref);
|
|
106
120
|
if (entry.type !== "spritesheet") throw new IncantoError("BAD_FORMAT", `Asset '${ref}' is a plain texture; a spritesheet (with frameWidth/frameHeight) is required here.`);
|
|
@@ -433,18 +447,32 @@ var Renderer2D = class {
|
|
|
433
447
|
engine;
|
|
434
448
|
/** The texture store. Public so `renderer.assets.status(key)` is reachable. */
|
|
435
449
|
assets;
|
|
450
|
+
ownsAssets;
|
|
436
451
|
loadedAssetScenes = /* @__PURE__ */ new WeakSet();
|
|
437
452
|
disconnect;
|
|
438
453
|
canvas;
|
|
439
454
|
constructor(opts) {
|
|
440
455
|
this.canvas = opts.canvas;
|
|
441
456
|
this.engine = opts.engine;
|
|
457
|
+
const cw = opts.canvas.clientWidth || opts.canvas.width || 1;
|
|
458
|
+
const ch = opts.canvas.clientHeight || opts.canvas.height || 1;
|
|
459
|
+
this.lastView = {
|
|
460
|
+
cx: cw / 2,
|
|
461
|
+
cy: ch / 2,
|
|
462
|
+
zoom: 1,
|
|
463
|
+
w: cw,
|
|
464
|
+
h: ch
|
|
465
|
+
};
|
|
442
466
|
this.assets = opts.assets ?? new AssetStore2D(void 0, this.engine);
|
|
467
|
+
this.ownsAssets = !opts.assets;
|
|
443
468
|
const rendering = resolveRendering(opts.engine.scene?.environment, {
|
|
444
469
|
antialias: true,
|
|
445
470
|
pixelRatio: 1,
|
|
446
471
|
preserveDrawingBuffer: false
|
|
447
472
|
}, globalThis.devicePixelRatio ?? 1, opts);
|
|
473
|
+
const tier = qualityRendering(opts.engine.settings.get("quality"));
|
|
474
|
+
rendering.antialias = rendering.antialias && tier.antialias;
|
|
475
|
+
rendering.pixelRatio = Math.min(rendering.pixelRatio, tier.pixelRatio);
|
|
448
476
|
this.webgl = withWebGLContext(() => new WebGLRenderer({
|
|
449
477
|
canvas: opts.canvas,
|
|
450
478
|
antialias: rendering.antialias,
|
|
@@ -452,6 +480,20 @@ var Renderer2D = class {
|
|
|
452
480
|
}));
|
|
453
481
|
this.webgl.info.autoReset = false;
|
|
454
482
|
this.engine.picker = (x, y) => this.pick(x, y);
|
|
483
|
+
this.engine.toWorld = (x, y) => {
|
|
484
|
+
if (!this.hasRendered) return null;
|
|
485
|
+
const w = this.worldFromScreen(x, y);
|
|
486
|
+
return [w.x, w.y];
|
|
487
|
+
};
|
|
488
|
+
this.engine.toScreen = (world) => {
|
|
489
|
+
if (!this.hasRendered) return null;
|
|
490
|
+
const s = this.screenFromWorld(world[0] ?? 0, world[1] ?? 0);
|
|
491
|
+
return {
|
|
492
|
+
x: s.x,
|
|
493
|
+
y: s.y,
|
|
494
|
+
behind: false
|
|
495
|
+
};
|
|
496
|
+
};
|
|
455
497
|
this.basePixelRatio = rendering.pixelRatio;
|
|
456
498
|
this.webgl.setPixelRatio(rendering.pixelRatio);
|
|
457
499
|
this.webgl.info.autoReset = false;
|
|
@@ -568,6 +610,7 @@ var Renderer2D = class {
|
|
|
568
610
|
h
|
|
569
611
|
};
|
|
570
612
|
this.lastViewport = view;
|
|
613
|
+
this.hasRendered = true;
|
|
571
614
|
if (view && (view.offsetX > .5 || view.offsetY > .5)) {
|
|
572
615
|
this.webgl.setScissorTest(false);
|
|
573
616
|
this.webgl.setViewport(0, 0, w, h);
|
|
@@ -595,13 +638,29 @@ var Renderer2D = class {
|
|
|
595
638
|
h: 1
|
|
596
639
|
};
|
|
597
640
|
lastViewport = null;
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
641
|
+
/**
|
|
642
|
+
* The view the LAST render used — and, before there has been one, the
|
|
643
|
+
* documented default: the whole canvas, centred, at zoom 1.
|
|
644
|
+
*
|
|
645
|
+
* It started as `{cx: 0, cy: 0, w: 1, h: 1}`, a one-pixel canvas nobody has,
|
|
646
|
+
* and `worldFromScreen` answered from it with a straight face. Measured in a
|
|
647
|
+
* browser on a 960x540 canvas with the camera at (160, 80), before the first
|
|
648
|
+
* frame: `toWorld(480, 270)` returned `[479.5, 269.5]` — the arithmetic of a
|
|
649
|
+
* 1x1 window, and a confident number rather than a refusal.
|
|
650
|
+
*/
|
|
651
|
+
lastView;
|
|
652
|
+
/**
|
|
653
|
+
* Has a frame been DRAWN?
|
|
654
|
+
*
|
|
655
|
+
* `Renderer3D` refuses every screen-space question until it has one
|
|
656
|
+
* (`if (!this.lastCamera) return null`, in `pick`, `rayFromScreen` and
|
|
657
|
+
* `worldFromScreen`); the flat renderer had no equivalent, so the engine-level
|
|
658
|
+
* `toWorld`/`toScreen` and `pick` answered from a view no frame had used. The
|
|
659
|
+
* hazard is real in exactly the place agents work: a background tab does not
|
|
660
|
+
* run rAF, so `await createGame2D(...)` there is followed by any number of
|
|
661
|
+
* questions with no frame behind them.
|
|
662
|
+
*/
|
|
663
|
+
hasRendered = false;
|
|
605
664
|
/** The world view used by the LAST render (center/zoom/canvas size). */
|
|
606
665
|
view() {
|
|
607
666
|
return { ...this.lastView };
|
|
@@ -630,11 +689,13 @@ var Renderer2D = class {
|
|
|
630
689
|
* Hits resolve through `userData.incantoNode` and prefer higher renderOrder.
|
|
631
690
|
*/
|
|
632
691
|
pick(sx, sy) {
|
|
692
|
+
if (!this.hasRendered) return null;
|
|
633
693
|
const { w, h } = this.lastView;
|
|
634
694
|
const ox = this.lastViewport?.offsetX ?? 0;
|
|
635
695
|
const oy = this.lastViewport?.offsetY ?? 0;
|
|
636
696
|
const cw = this.lastViewport?.contentW ?? w;
|
|
637
697
|
const ch = this.lastViewport?.contentH ?? h;
|
|
698
|
+
if (sx < ox || sx > ox + cw || sy < oy || sy > oy + ch) return null;
|
|
638
699
|
pickNdc.set((sx - ox) / cw * 2 - 1, -((sy - oy) / ch * 2 - 1));
|
|
639
700
|
const ui = this.hit(this.uiScene, this.uiCam);
|
|
640
701
|
if (ui) return ui;
|
|
@@ -642,20 +703,7 @@ var Renderer2D = class {
|
|
|
642
703
|
}
|
|
643
704
|
hit(scene, cam) {
|
|
644
705
|
raycaster.setFromCamera(pickNdc, cam);
|
|
645
|
-
|
|
646
|
-
let best = null;
|
|
647
|
-
for (const hit of hits) {
|
|
648
|
-
let obj = hit.object;
|
|
649
|
-
while (obj && !obj.userData.incantoNode) obj = obj.parent;
|
|
650
|
-
const node = obj?.userData.incantoNode;
|
|
651
|
-
if (!node) continue;
|
|
652
|
-
const order = hit.object.renderOrder ?? 0;
|
|
653
|
-
if (!best || order >= best.order) best = {
|
|
654
|
-
node,
|
|
655
|
-
order
|
|
656
|
-
};
|
|
657
|
-
}
|
|
658
|
-
return best?.node ?? null;
|
|
706
|
+
return nodeFromHits(raycaster.intersectObjects(scene.children, true), { byRenderOrder: true });
|
|
659
707
|
}
|
|
660
708
|
/** Does the UI pass draw this node (i.e. is it under a UILayer)? */
|
|
661
709
|
isUiNode(node) {
|
|
@@ -714,16 +762,16 @@ var Renderer2D = class {
|
|
|
714
762
|
}
|
|
715
763
|
dispose() {
|
|
716
764
|
if (this.engine.picker) this.engine.picker = null;
|
|
765
|
+
if (this.engine.toWorld) this.engine.toWorld = null;
|
|
766
|
+
if (this.engine.toScreen) this.engine.toScreen = null;
|
|
717
767
|
this.disconnect();
|
|
718
768
|
for (const scene of [this.worldScene, this.uiScene]) scene.traverse((obj) => {
|
|
719
769
|
const mesh = obj;
|
|
720
770
|
if (!mesh.isMesh) return;
|
|
721
771
|
const mats = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
|
|
722
|
-
for (const mat of mats)
|
|
723
|
-
mat?.map?.dispose();
|
|
724
|
-
mat?.dispose();
|
|
725
|
-
}
|
|
772
|
+
for (const mat of mats) mat?.dispose();
|
|
726
773
|
});
|
|
774
|
+
if (this.ownsAssets) this.assets.dispose();
|
|
727
775
|
this.webgl.dispose();
|
|
728
776
|
}
|
|
729
777
|
};
|
|
@@ -768,13 +816,25 @@ async function createGame2D(opts) {
|
|
|
768
816
|
engine.setScene(scene);
|
|
769
817
|
let physics = null;
|
|
770
818
|
if (opts.physics === true || (opts.physics ?? "auto") === "auto" && hasBody(scene.root)) physics = await enablePhysics2D(engine);
|
|
819
|
+
if (opts.physics !== false) engine.physicsProvider = async (root) => {
|
|
820
|
+
if (engine.physics || !hasBody(root)) return;
|
|
821
|
+
physics = await enablePhysics2D(engine);
|
|
822
|
+
};
|
|
771
823
|
const cleanups = [];
|
|
772
824
|
const keyboard = opts.keyboard ?? (typeof window !== "undefined" ? window : false);
|
|
773
825
|
if (keyboard) engine.input.attachKeyboard(keyboard);
|
|
774
|
-
|
|
826
|
+
let pointerAttached = false;
|
|
827
|
+
const attachPointerIfWanted = () => {
|
|
828
|
+
if (pointerAttached || opts.pointer === false) return;
|
|
829
|
+
const root = engine.scene?.root;
|
|
830
|
+
if (!root) return;
|
|
831
|
+
if (opts.pointer === void 0 && !wantsPointer(root)) return;
|
|
832
|
+
pointerAttached = true;
|
|
775
833
|
const lockOnClick = typeof opts.pointer === "object" ? opts.pointer.lockOnClick ?? false : false;
|
|
776
834
|
engine.input.attachPointer(opts.canvas, { lockOnClick });
|
|
777
|
-
}
|
|
835
|
+
};
|
|
836
|
+
attachPointerIfWanted();
|
|
837
|
+
cleanups.push(engine.sceneChanged.connect(() => attachPointerIfWanted()));
|
|
778
838
|
cleanups.push(wireAudioUnlock(engine, scene.root, opts.canvas));
|
|
779
839
|
cleanups.push(engine.settings.bindAudio(engine.audio));
|
|
780
840
|
cleanups.push(pauseWhenHidden(engine));
|
|
@@ -803,6 +863,7 @@ async function createGame2D(opts) {
|
|
|
803
863
|
canvas: opts.canvas,
|
|
804
864
|
engine,
|
|
805
865
|
...opts.pixelRatio !== void 0 ? { pixelRatio: opts.pixelRatio } : {},
|
|
866
|
+
...opts.preserveDrawingBuffer !== void 0 ? { preserveDrawingBuffer: opts.preserveDrawingBuffer } : {},
|
|
806
867
|
...opts.antialias !== void 0 ? { antialias: opts.antialias } : {}
|
|
807
868
|
});
|
|
808
869
|
cleanups.push(engine.settings.bindFrameCap((fps) => {
|
|
@@ -827,26 +888,15 @@ async function createGame2D(opts) {
|
|
|
827
888
|
requestAnimationFrame(tick);
|
|
828
889
|
};
|
|
829
890
|
if (typeof requestAnimationFrame === "function") requestAnimationFrame(tick);
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
break;
|
|
840
|
-
case "key":
|
|
841
|
-
engine.input.handleKey(step.code, step.down);
|
|
842
|
-
break;
|
|
843
|
-
case "pointer":
|
|
844
|
-
engine.input.handlePointerMove(step.dx, step.dy);
|
|
845
|
-
break;
|
|
846
|
-
case "step":
|
|
847
|
-
await new Promise((r) => setTimeout(r, step.ms));
|
|
848
|
-
break;
|
|
849
|
-
}
|
|
891
|
+
const target = {
|
|
892
|
+
engine,
|
|
893
|
+
project: (x, y) => {
|
|
894
|
+
const at = renderer.screenFromWorld;
|
|
895
|
+
return at ? at.call(renderer, x, y) : null;
|
|
896
|
+
},
|
|
897
|
+
wait: (ms) => new Promise((r) => setTimeout(r, ms))
|
|
898
|
+
};
|
|
899
|
+
for (const step of steps) await applyDriveStep(target, step);
|
|
850
900
|
watching = false;
|
|
851
901
|
return frames;
|
|
852
902
|
};
|
|
@@ -872,10 +922,11 @@ async function createGame2D(opts) {
|
|
|
872
922
|
if (!editorSwitch) return;
|
|
873
923
|
const open = editorSwitch.open ?? openBundledEditor;
|
|
874
924
|
const reboot = async (working) => {
|
|
875
|
-
await createGame2D({
|
|
925
|
+
const next = await createGame2D({
|
|
876
926
|
...opts,
|
|
877
927
|
scene: working
|
|
878
928
|
});
|
|
929
|
+
editorSwitch?.onReboot?.(next);
|
|
879
930
|
};
|
|
880
931
|
await crossFade(async () => {
|
|
881
932
|
disposeGame();
|
|
@@ -888,7 +939,7 @@ async function createGame2D(opts) {
|
|
|
888
939
|
...editorSwitch.library ? { library: editorSwitch.library === true ? devServerLibrary() : editorSwitch.library } : {}
|
|
889
940
|
});
|
|
890
941
|
} catch (error) {
|
|
891
|
-
|
|
942
|
+
diagnose(engine, "error", "[incanto] could not open the editor:", error);
|
|
892
943
|
await reboot(sourceJson);
|
|
893
944
|
}
|
|
894
945
|
}, "opening the editor…");
|
|
@@ -908,9 +959,24 @@ async function createGame2D(opts) {
|
|
|
908
959
|
}
|
|
909
960
|
}
|
|
910
961
|
engine.start();
|
|
962
|
+
/**
|
|
963
|
+
* Has this handle already been torn down?
|
|
964
|
+
*
|
|
965
|
+
* After one ✎→▶ editor round trip the handle `createGame*` returned is
|
|
966
|
+
* ORPHANED: `reboot()` builds a replacement and the app still holds the old
|
|
967
|
+
* one. Calling `game.openEditor()` on it then booted a THIRD game on top of
|
|
968
|
+
* the live one, and `game.dispose()` tore down a game that was already gone
|
|
969
|
+
* while leaving the live one running. A second `dispose()` also re-entered
|
|
970
|
+
* Rapier's already-nulled `world.free()` and printed an engine error.
|
|
971
|
+
*
|
|
972
|
+
* A stale handle does nothing rather than something destructive.
|
|
973
|
+
*/
|
|
974
|
+
let disposed = false;
|
|
911
975
|
function disposeGame() {
|
|
976
|
+
if (disposed) return;
|
|
977
|
+
disposed = true;
|
|
912
978
|
teardown([
|
|
913
|
-
|
|
979
|
+
["physics", () => physics?.dispose()],
|
|
914
980
|
["app cleanups", () => {
|
|
915
981
|
for (const cleanup of cleanups) cleanup();
|
|
916
982
|
}],
|
|
@@ -950,7 +1016,10 @@ async function createGame2D(opts) {
|
|
|
950
1016
|
...renderer.stats?.()
|
|
951
1017
|
};
|
|
952
1018
|
},
|
|
953
|
-
openEditor: editorSwitch ?
|
|
1019
|
+
openEditor: editorSwitch ? async () => {
|
|
1020
|
+
if (disposed) return;
|
|
1021
|
+
await editorAction.run();
|
|
1022
|
+
} : null,
|
|
954
1023
|
dispose: disposeGame
|
|
955
1024
|
};
|
|
956
1025
|
}
|
|
@@ -1009,5 +1078,23 @@ function wireAudioUnlock(engine, root, canvas) {
|
|
|
1009
1078
|
for (const s of sources) s.target.addEventListener?.(s.type, unlock, { passive: true });
|
|
1010
1079
|
return detachAll;
|
|
1011
1080
|
}
|
|
1081
|
+
/**
|
|
1082
|
+
* Does this tree have anything the CURSOR drives?
|
|
1083
|
+
*
|
|
1084
|
+
* `physics: 'auto'` has always meant "on iff the tree needs it"; the pointer had
|
|
1085
|
+
* no such default, so a board of `Clickable`s booted without `pointer: true` did
|
|
1086
|
+
* nothing when clicked — and the skill that introduces the behaviour says it
|
|
1087
|
+
* "needs no code at all". This makes that true.
|
|
1088
|
+
*
|
|
1089
|
+
* Only `Clickable`, deliberately: a behaviour of your own that reads
|
|
1090
|
+
* `pointerPosition()` is invisible from here, and guessing wider would attach a
|
|
1091
|
+
* pointer to games that never asked (and, with `lockOnClick`, capture a cursor
|
|
1092
|
+
* nobody wanted).
|
|
1093
|
+
*/
|
|
1094
|
+
function wantsPointer(root) {
|
|
1095
|
+
if (root.behavior?.constructor.name === "Clickable") return true;
|
|
1096
|
+
for (const child of root.children) if (wantsPointer(child)) return true;
|
|
1097
|
+
return false;
|
|
1098
|
+
}
|
|
1012
1099
|
//#endregion
|
|
1013
1100
|
export { AssetStore2D as a, syncTree2D as i, create_game_exports as n, Renderer2D as r, createGame2D as t };
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { C as qualityRendering, _ as AudioPlayer, v as Engine, x as qualityCaps } from "./register-
|
|
1
|
+
import { o as __exportAll } from "./json-CfTjpvW8.js";
|
|
2
|
+
import { it as registerBehavior, p as loadScene, q as diagnose, v as horizonColorFromSky, x as sunDirectionFromSky, y as parseEnvironment3D } from "./save-slots-BXVg148r.js";
|
|
3
|
+
import { C as qualityRendering, S as qualityEnvironment, _ as AudioPlayer, v as Engine, x as qualityCaps } from "./register-6DYnKZcy.js";
|
|
4
4
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
5
|
-
import { n as attachTouchControls } from "./touch-
|
|
6
|
-
import { n as withWebGLContext, r as resolveRendering } from "./webgl-unavailable-
|
|
7
|
-
import { a as
|
|
8
|
-
import { o as frameStats } from "./frame-report-
|
|
9
|
-
import {
|
|
5
|
+
import { n as attachTouchControls } from "./touch-BnCyPA0G.js";
|
|
6
|
+
import { n as withWebGLContext, r as resolveRendering } from "./webgl-unavailable-C8aDbGmR.js";
|
|
7
|
+
import { a as devServerLibrary, c as applyDriveStep, d as audioErrors, i as crossFade, l as wireDevChannel, n as teardown, o as openBundledEditor, r as pauseWhenHidden, s as poseFromRenderer, t as nodeFromHits, u as claimCanvasGestures } from "./picking-CQJ_PJKh.js";
|
|
8
|
+
import { o as frameStats } from "./frame-report-Dlq13Gyj.js";
|
|
9
|
+
import { G as Node3D, H as PhysicsBody3D, n as registerGameplayBehaviors, q as createCausticsQuad } from "./gameplay-BfHkuzVb.js";
|
|
10
10
|
import { t as debugSources } from "./debug-draw-BM3DsvtT.js";
|
|
11
|
-
import { E as Camera3D, U as TextureCache3D, g as ModelInstance3D, n as registerNodes3D, t as resolveEnvironmentHdri, v as DirectionalLight3D } from "./environment-presets-
|
|
12
|
-
import { n as enablePhysics3D } from "./physics-3d-
|
|
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";
|
|
11
|
+
import { E as Camera3D, U as TextureCache3D, g as ModelInstance3D, n as registerNodes3D, t as resolveEnvironmentHdri, v as DirectionalLight3D } from "./environment-presets-D6Q5BxeE.js";
|
|
12
|
+
import { n as enablePhysics3D } from "./physics-3d-CpH-2gn5.js";
|
|
13
|
+
import { ACESFilmicToneMapping, AmbientLight, Box3, BufferAttribute, BufferGeometry, Color, DepthTexture, EquirectangularReflectionMapping, FloatType, Fog, HalfFloatType, LineBasicMaterial, LineSegments, LoadingManager, 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";
|
|
16
16
|
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
|
|
@@ -72,7 +72,12 @@ var AssetStore3D = class {
|
|
|
72
72
|
*/
|
|
73
73
|
constructor(engine = null) {
|
|
74
74
|
this.engine = engine;
|
|
75
|
-
|
|
75
|
+
const manager = new LoadingManager();
|
|
76
|
+
manager.onError = (url) => {
|
|
77
|
+
if (this.models.has(url) || this.animations.has(url)) return;
|
|
78
|
+
this.textures.noteError(url, "a model needs this texture and it did not load");
|
|
79
|
+
};
|
|
80
|
+
this.loader = new GLTFLoader(manager);
|
|
76
81
|
this.loader.register((parser) => new VRMLoaderPlugin(parser));
|
|
77
82
|
this.textures = new TextureCache3D({ diagnostics: engine ? { warn: (...parts) => diagnose(engine, "warn", ...parts) } : null });
|
|
78
83
|
}
|
|
@@ -1353,7 +1358,7 @@ function walk(node, parentObj, state) {
|
|
|
1353
1358
|
for (const child of node.children) walk(child, nextParent, state);
|
|
1354
1359
|
if (node instanceof Node3D) {
|
|
1355
1360
|
const obj = node._ensureObject3D();
|
|
1356
|
-
if (node.static && !state.ignoreStatic) {
|
|
1361
|
+
if (node.static && !state.ignoreStatic && node._staticReady(state.assets ?? null)) {
|
|
1357
1362
|
obj.userData.incantoStaticSynced = true;
|
|
1358
1363
|
obj.userData.incantoStatic = true;
|
|
1359
1364
|
warnStaticHazards(node);
|
|
@@ -1374,7 +1379,7 @@ function warnStaticHazards(root) {
|
|
|
1374
1379
|
for (const c of n.children) scan(c);
|
|
1375
1380
|
};
|
|
1376
1381
|
scan(root);
|
|
1377
|
-
if (hazards.length > 0)
|
|
1382
|
+
if (hazards.length > 0) diagnose(root.tree?.engine ?? null, "warn", `[incanto] static subtree '${root.name}' freezes animated/per-frame nodes: ${hazards.join(", ")} — they will stop updating. Unmark static or move them out.`);
|
|
1378
1383
|
}
|
|
1379
1384
|
function prune(obj, visited) {
|
|
1380
1385
|
const ch = obj.children;
|
|
@@ -1494,6 +1499,16 @@ var Renderer3D = class {
|
|
|
1494
1499
|
}));
|
|
1495
1500
|
this.webgl.info.autoReset = false;
|
|
1496
1501
|
this.engine.picker = (x, y) => this.pick(x, y);
|
|
1502
|
+
this.engine.toWorld = (x, y) => {
|
|
1503
|
+
const w = this.worldFromScreen(x, y);
|
|
1504
|
+
return w ? [
|
|
1505
|
+
w.x,
|
|
1506
|
+
w.y,
|
|
1507
|
+
w.z
|
|
1508
|
+
] : null;
|
|
1509
|
+
};
|
|
1510
|
+
this.engine.toScreen = (world) => this.screenFromWorld(world[0] ?? 0, world[1] ?? 0, world[2] ?? 0);
|
|
1511
|
+
this.engine.screenRay = (x, y) => this.rayFromScreen(x, y);
|
|
1497
1512
|
this.basePixelRatio = rendering.pixelRatio;
|
|
1498
1513
|
this.webgl.setPixelRatio(rendering.pixelRatio);
|
|
1499
1514
|
this.adaptive = opts.adaptiveResolution === false ? null : new AdaptiveResolution();
|
|
@@ -2067,6 +2082,7 @@ var Renderer3D = class {
|
|
|
2067
2082
|
rayFromScreen(sx, sy) {
|
|
2068
2083
|
const cam = this.lastCamera;
|
|
2069
2084
|
if (!cam) return null;
|
|
2085
|
+
if (sx < 0 || sx > this.lastSize.w || sy < 0 || sy > this.lastSize.h) return null;
|
|
2070
2086
|
pickNdc.set(sx / this.lastSize.w * 2 - 1, -(sy / this.lastSize.h * 2 - 1));
|
|
2071
2087
|
pickRaycaster.setFromCamera(pickNdc, cam);
|
|
2072
2088
|
const o = pickRaycaster.ray.origin;
|
|
@@ -2095,14 +2111,7 @@ var Renderer3D = class {
|
|
|
2095
2111
|
if (!cam) return null;
|
|
2096
2112
|
pickNdc.set(sx / this.lastSize.w * 2 - 1, -(sy / this.lastSize.h * 2 - 1));
|
|
2097
2113
|
pickRaycaster.setFromCamera(pickNdc, cam);
|
|
2098
|
-
|
|
2099
|
-
for (const hit of hits) {
|
|
2100
|
-
let obj = hit.object;
|
|
2101
|
-
while (obj && !obj.userData.incantoNode) obj = obj.parent;
|
|
2102
|
-
const node = obj?.userData.incantoNode;
|
|
2103
|
-
if (node) return node;
|
|
2104
|
-
}
|
|
2105
|
-
return null;
|
|
2114
|
+
return nodeFromHits(pickRaycaster.intersectObjects(this.threeScene.children, true));
|
|
2106
2115
|
}
|
|
2107
2116
|
/**
|
|
2108
2117
|
* GPU counters for the last rendered frame — EVERY pass of it, summed (see
|
|
@@ -2154,6 +2163,9 @@ var Renderer3D = class {
|
|
|
2154
2163
|
}
|
|
2155
2164
|
dispose() {
|
|
2156
2165
|
if (this.engine.picker) this.engine.picker = null;
|
|
2166
|
+
if (this.engine.toWorld) this.engine.toWorld = null;
|
|
2167
|
+
if (this.engine.toScreen) this.engine.toScreen = null;
|
|
2168
|
+
if (this.engine.screenRay) this.engine.screenRay = null;
|
|
2157
2169
|
this.disconnect();
|
|
2158
2170
|
this.threeScene.traverse((obj) => {
|
|
2159
2171
|
const mesh = obj;
|
|
@@ -2249,14 +2261,26 @@ async function createGame3D(opts) {
|
|
|
2249
2261
|
await report(.06, "scene");
|
|
2250
2262
|
let physics = null;
|
|
2251
2263
|
if (opts.physics === true || (opts.physics ?? "auto") === "auto" && hasBody(scene.root)) physics = await enablePhysics3D(engine);
|
|
2264
|
+
if (opts.physics !== false) engine.physicsProvider = async (root) => {
|
|
2265
|
+
if (engine.physics || !hasBody(root)) return;
|
|
2266
|
+
physics = await enablePhysics3D(engine);
|
|
2267
|
+
};
|
|
2252
2268
|
await report(.1, "physics");
|
|
2253
2269
|
const cleanups = [];
|
|
2254
2270
|
const keyboard = opts.keyboard ?? (typeof window !== "undefined" ? window : false);
|
|
2255
2271
|
if (keyboard) engine.input.attachKeyboard(keyboard);
|
|
2256
|
-
|
|
2272
|
+
let pointerAttached = false;
|
|
2273
|
+
const attachPointerIfWanted = () => {
|
|
2274
|
+
if (pointerAttached || opts.pointer === false) return;
|
|
2275
|
+
const root = engine.scene?.root;
|
|
2276
|
+
if (!root) return;
|
|
2277
|
+
if (opts.pointer === void 0 && !wantsPointer(root)) return;
|
|
2278
|
+
pointerAttached = true;
|
|
2257
2279
|
const lockOnClick = typeof opts.pointer === "object" ? opts.pointer.lockOnClick ?? true : true;
|
|
2258
2280
|
engine.input.attachPointer(opts.canvas, { lockOnClick });
|
|
2259
|
-
}
|
|
2281
|
+
};
|
|
2282
|
+
attachPointerIfWanted();
|
|
2283
|
+
cleanups.push(engine.sceneChanged.connect(() => attachPointerIfWanted()));
|
|
2260
2284
|
cleanups.push(wireAudioUnlock(engine, scene.root, opts.canvas));
|
|
2261
2285
|
if (!opts.pageGestures) claimCanvasGestures(opts.canvas);
|
|
2262
2286
|
const touchMode = opts.touch ?? "auto";
|
|
@@ -2301,10 +2325,11 @@ async function createGame3D(opts) {
|
|
|
2301
2325
|
const camera = poseFromRenderer(renderer);
|
|
2302
2326
|
const open = editorSwitch.open ?? openBundledEditor;
|
|
2303
2327
|
const reboot = async (working) => {
|
|
2304
|
-
await createGame3D({
|
|
2328
|
+
const next = await createGame3D({
|
|
2305
2329
|
...opts,
|
|
2306
2330
|
scene: working
|
|
2307
2331
|
});
|
|
2332
|
+
editorSwitch?.onReboot?.(next);
|
|
2308
2333
|
};
|
|
2309
2334
|
await crossFade(async () => {
|
|
2310
2335
|
disposeGame();
|
|
@@ -2318,7 +2343,7 @@ async function createGame3D(opts) {
|
|
|
2318
2343
|
...editorSwitch.library ? { library: editorSwitch.library === true ? devServerLibrary() : editorSwitch.library } : {}
|
|
2319
2344
|
});
|
|
2320
2345
|
} catch (error) {
|
|
2321
|
-
|
|
2346
|
+
diagnose(engine, "error", "[incanto] could not open the editor:", error);
|
|
2322
2347
|
await reboot(sourceJson);
|
|
2323
2348
|
}
|
|
2324
2349
|
}, "opening the editor…");
|
|
@@ -2351,8 +2376,12 @@ async function createGame3D(opts) {
|
|
|
2351
2376
|
} catch {}
|
|
2352
2377
|
cleanups.push(engine.settings.bindAudio(engine.audio));
|
|
2353
2378
|
cleanups.push(engine.settings.bindLocale(engine.locale));
|
|
2354
|
-
|
|
2379
|
+
const applyTier = (patch) => {
|
|
2355
2380
|
applyQualityTier3D(engine, patch);
|
|
2381
|
+
};
|
|
2382
|
+
cleanups.push(engine.settings.bindQuality(applyTier));
|
|
2383
|
+
cleanups.push(engine.sceneChanged.connect(() => {
|
|
2384
|
+
applyTier(qualityEnvironment(engine.settings.get("quality")));
|
|
2356
2385
|
}));
|
|
2357
2386
|
cleanups.push(engine.settings.bindFrameCap((fps) => {
|
|
2358
2387
|
engine.maxFps = fps;
|
|
@@ -2382,26 +2411,15 @@ async function createGame3D(opts) {
|
|
|
2382
2411
|
requestAnimationFrame(tick);
|
|
2383
2412
|
};
|
|
2384
2413
|
requestAnimationFrame(tick);
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
break;
|
|
2395
|
-
case "key":
|
|
2396
|
-
engine.input.handleKey(s.code, s.down);
|
|
2397
|
-
break;
|
|
2398
|
-
case "pointer":
|
|
2399
|
-
engine.input.handlePointerMove(s.dx, s.dy);
|
|
2400
|
-
break;
|
|
2401
|
-
case "step":
|
|
2402
|
-
await new Promise((r) => setTimeout(r, s.ms));
|
|
2403
|
-
break;
|
|
2404
|
-
}
|
|
2414
|
+
const target = {
|
|
2415
|
+
engine,
|
|
2416
|
+
project: (x, y, z) => {
|
|
2417
|
+
const at = renderer.screenFromWorld;
|
|
2418
|
+
return at ? at.call(renderer, x, y, z) : null;
|
|
2419
|
+
},
|
|
2420
|
+
wait: (ms) => new Promise((r) => setTimeout(r, ms))
|
|
2421
|
+
};
|
|
2422
|
+
for (const s of steps) await applyDriveStep(target, s);
|
|
2405
2423
|
watching = false;
|
|
2406
2424
|
return frames;
|
|
2407
2425
|
};
|
|
@@ -2414,9 +2432,24 @@ async function createGame3D(opts) {
|
|
|
2414
2432
|
}));
|
|
2415
2433
|
cleanups.push(pauseWhenHidden(engine));
|
|
2416
2434
|
await report(1, "ready");
|
|
2435
|
+
/**
|
|
2436
|
+
* Has this handle already been torn down?
|
|
2437
|
+
*
|
|
2438
|
+
* After one ✎→▶ editor round trip the handle `createGame*` returned is
|
|
2439
|
+
* ORPHANED: `reboot()` builds a replacement and the app still holds the old
|
|
2440
|
+
* one. Calling `game.openEditor()` on it then booted a THIRD game on top of
|
|
2441
|
+
* the live one, and `game.dispose()` tore down a game that was already gone
|
|
2442
|
+
* while leaving the live one running. A second `dispose()` also re-entered
|
|
2443
|
+
* Rapier's already-nulled `world.free()` and printed an engine error.
|
|
2444
|
+
*
|
|
2445
|
+
* A stale handle does nothing rather than something destructive.
|
|
2446
|
+
*/
|
|
2447
|
+
let disposed = false;
|
|
2417
2448
|
function disposeGame() {
|
|
2449
|
+
if (disposed) return;
|
|
2450
|
+
disposed = true;
|
|
2418
2451
|
teardown([
|
|
2419
|
-
|
|
2452
|
+
["physics", () => physics?.dispose()],
|
|
2420
2453
|
["app cleanups", () => {
|
|
2421
2454
|
for (const cleanup of cleanups) cleanup();
|
|
2422
2455
|
}],
|
|
@@ -2456,7 +2489,10 @@ async function createGame3D(opts) {
|
|
|
2456
2489
|
...renderer.stats?.()
|
|
2457
2490
|
};
|
|
2458
2491
|
},
|
|
2459
|
-
openEditor: editorSwitch ?
|
|
2492
|
+
openEditor: editorSwitch ? async () => {
|
|
2493
|
+
if (disposed) return;
|
|
2494
|
+
await editorAction.run();
|
|
2495
|
+
} : null,
|
|
2460
2496
|
dispose: disposeGame
|
|
2461
2497
|
};
|
|
2462
2498
|
}
|
|
@@ -2515,5 +2551,23 @@ function wireAudioUnlock(engine, root, canvas) {
|
|
|
2515
2551
|
for (const s of sources) s.target.addEventListener?.(s.type, unlock, { passive: true });
|
|
2516
2552
|
return detachAll;
|
|
2517
2553
|
}
|
|
2554
|
+
/**
|
|
2555
|
+
* Does this tree have anything the CURSOR drives?
|
|
2556
|
+
*
|
|
2557
|
+
* `physics: 'auto'` has always meant "on iff the tree needs it"; the pointer had
|
|
2558
|
+
* no such default, so a board of `Clickable`s booted without `pointer: true` did
|
|
2559
|
+
* nothing when clicked — and the skill that introduces the behaviour says it
|
|
2560
|
+
* "needs no code at all". This makes that true.
|
|
2561
|
+
*
|
|
2562
|
+
* Only `Clickable`, deliberately: a behaviour of your own that reads
|
|
2563
|
+
* `pointerPosition()` is invisible from here, and guessing wider would attach a
|
|
2564
|
+
* pointer to games that never asked (and, with `lockOnClick`, capture a cursor
|
|
2565
|
+
* nobody wanted).
|
|
2566
|
+
*/
|
|
2567
|
+
function wantsPointer(root) {
|
|
2568
|
+
if (root.behavior?.constructor.name === "Clickable") return true;
|
|
2569
|
+
for (const child of root.children) if (wantsPointer(child)) return true;
|
|
2570
|
+
return false;
|
|
2571
|
+
}
|
|
2518
2572
|
//#endregion
|
|
2519
2573
|
export { Environment3D as a, AssetStore3D as c, syncTree as i, create_game_exports as n, applyQualityTier3D as o, Renderer3D as r, setEnvironment3D as s, createGame3D as t };
|
package/dist/debug.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ft as LogLevel, T as RendererStats, b as Engine } from "./behavior-
|
|
1
|
+
import { Ft as LogLevel, T as RendererStats, b as Engine } from "./behavior-DZExDn9o.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,6 +1,5 @@
|
|
|
1
|
-
import { t as jsonClone } from "./json-
|
|
2
|
-
import {
|
|
3
|
-
import { r as capturePointer } from "./touch-DEAmqGdf.js";
|
|
1
|
+
import { t as jsonClone } from "./json-CfTjpvW8.js";
|
|
2
|
+
import { d as mergeStaticProps, r as capturePointer } from "./touch-BnCyPA0G.js";
|
|
4
3
|
import { t as debugSources } from "./debug-draw-BM3DsvtT.js";
|
|
5
4
|
//#region src/debug/capture.ts
|
|
6
5
|
/**
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/core/diagnostics.d.ts
|
|
2
|
+
/** The minimum an engine has to look like to receive a diagnostic. */
|
|
3
|
+
interface DiagnosticSink {
|
|
4
|
+
log: {
|
|
5
|
+
warn(...parts: unknown[]): void;
|
|
6
|
+
error(...parts: unknown[]): void;
|
|
7
|
+
info(...parts: unknown[]): void;
|
|
8
|
+
debug(...parts: unknown[]): void;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { DiagnosticSink as t };
|