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,14 +1,15 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import { c as parseEnvironment3D, k as diagnose, n as loadScene, s as horizonColorFromSky, u as sunDirectionFromSky, z as registerBehavior } from "./loader-
|
|
3
|
-
import { C as qualityRendering, _ as AudioPlayer, v as Engine, x as qualityCaps } from "./register-
|
|
2
|
+
import { c as parseEnvironment3D, k as diagnose, n as loadScene, s as horizonColorFromSky, u as sunDirectionFromSky, z as registerBehavior } from "./loader-BC4PNtJX.js";
|
|
3
|
+
import { C as qualityRendering, _ as AudioPlayer, v as Engine, x as qualityCaps } from "./register-3ta-2Xig.js";
|
|
4
4
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { n as attachTouchControls } from "./touch-DEAmqGdf.js";
|
|
6
|
+
import { n as withWebGLContext, r as resolveRendering } from "./webgl-unavailable-N9nQqesw.js";
|
|
7
|
+
import { a as openBundledEditor, c as claimCanvasGestures, i as devServerLibrary, l as audioErrors, n as pauseWhenHidden, o as poseFromRenderer, r as crossFade, s as wireDevChannel, t as teardown } from "./teardown-D2NEmxPB.js";
|
|
7
8
|
import { o as frameStats } from "./frame-report-BSMny7oe.js";
|
|
8
|
-
import { J as createCausticsQuad, K as Node3D, U as PhysicsBody3D, n as registerGameplayBehaviors } from "./gameplay-
|
|
9
|
+
import { J as createCausticsQuad, K as Node3D, U as PhysicsBody3D, n as registerGameplayBehaviors } from "./gameplay-DM1eu_cV.js";
|
|
9
10
|
import { t as debugSources } from "./debug-draw-BM3DsvtT.js";
|
|
10
|
-
import { E as Camera3D, U as TextureCache3D, g as ModelInstance3D, n as registerNodes3D, t as resolveEnvironmentHdri, v as DirectionalLight3D } from "./environment-presets-
|
|
11
|
-
import { n as enablePhysics3D } from "./physics-3d-
|
|
11
|
+
import { E as Camera3D, U as TextureCache3D, g as ModelInstance3D, n as registerNodes3D, t as resolveEnvironmentHdri, v as DirectionalLight3D } from "./environment-presets-CybQXNqS.js";
|
|
12
|
+
import { n as enablePhysics3D } from "./physics-3d-bG3n70Ky.js";
|
|
12
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";
|
|
13
14
|
import { VRMLoaderPlugin, VRMUtils } from "@pixiv/three-vrm";
|
|
14
15
|
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
|
|
@@ -27,6 +28,31 @@ import { Sky } from "three/examples/jsm/objects/Sky.js";
|
|
|
27
28
|
*/
|
|
28
29
|
var AssetStore3D = class {
|
|
29
30
|
engine;
|
|
31
|
+
/**
|
|
32
|
+
* One geometry and one material per distinct SHAPE and per distinct look.
|
|
33
|
+
*
|
|
34
|
+
* `MeshInstance3D._createObject3D` used to `new BoxGeometry()` and
|
|
35
|
+
* `new MeshPhysicalMaterial()` for every node, even when byte-identical.
|
|
36
|
+
* Three identical nodes reported `geometry shared? false false / material
|
|
37
|
+
* shared? false false`, with distinct uuids. Measured at 3,200 units, all
|
|
38
|
+
* three runs at identical 3,209 draw calls / 1,741,932 triangles and an
|
|
39
|
+
* identical screenshot:
|
|
40
|
+
*
|
|
41
|
+
* ```
|
|
42
|
+
* per-node geometry + material 31.0 ms wall (renderMs 27.3)
|
|
43
|
+
* share one geometry 27.0 ms
|
|
44
|
+
* + share two materials 15.4 ms <- 2x the headroom
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* 13.1 ms of that 26.5 ms cliff was GPU state churn from per-node objects,
|
|
48
|
+
* and none of it was visible headlessly: with no renderer `_syncObject3D`
|
|
49
|
+
* never runs, so the bench measured 55 ns/node instead of ~2,000.
|
|
50
|
+
*
|
|
51
|
+
* Renderer-scoped, deliberately — module-level state is the predecessor's
|
|
52
|
+
* fatal flaw and two games in one page must not share a cache.
|
|
53
|
+
*/
|
|
54
|
+
meshGeometries = /* @__PURE__ */ new Map();
|
|
55
|
+
meshMaterials = /* @__PURE__ */ new Map();
|
|
30
56
|
models = /* @__PURE__ */ new Map();
|
|
31
57
|
animations = /* @__PURE__ */ new Map();
|
|
32
58
|
modelKeys = /* @__PURE__ */ new Map();
|
|
@@ -61,22 +87,18 @@ var AssetStore3D = class {
|
|
|
61
87
|
url,
|
|
62
88
|
error
|
|
63
89
|
});
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
url: decl.url,
|
|
77
|
-
error: String(entry.error)
|
|
78
|
-
});
|
|
79
|
-
}
|
|
90
|
+
const modelRefs = new Map([...this.modelKeys].map(([ref, url]) => [url, ref]));
|
|
91
|
+
for (const [url, entry] of this.models) if (entry.error) out.push({
|
|
92
|
+
ref: modelRefs.get(url) ?? url,
|
|
93
|
+
url,
|
|
94
|
+
error: String(entry.error)
|
|
95
|
+
});
|
|
96
|
+
const clipRefs = new Map([...this.animationKeys].map(([ref, decl]) => [decl.url, ref]));
|
|
97
|
+
for (const [url, entry] of this.animations) if (entry.error) out.push({
|
|
98
|
+
ref: clipRefs.get(url) ?? url,
|
|
99
|
+
url,
|
|
100
|
+
error: String(entry.error)
|
|
101
|
+
});
|
|
80
102
|
return out;
|
|
81
103
|
}
|
|
82
104
|
/** Map a scene's `assets` header. */
|
|
@@ -253,10 +275,27 @@ function yieldToFrame(maxWaitMs = DEFAULT_MAX_WAIT_MS, raf = typeof requestAnima
|
|
|
253
275
|
* - hard-validates the merged result NOW (in your call stack), so a bad patch
|
|
254
276
|
* throws where you wrote it — never inside the render loop
|
|
255
277
|
*/
|
|
256
|
-
|
|
278
|
+
/**
|
|
279
|
+
* The environment stages a quality tier is allowed to touch.
|
|
280
|
+
*
|
|
281
|
+
* These are the ones `qualityEnvironment` names, and the ones that must snap
|
|
282
|
+
* back to what the scene file says rather than accumulating.
|
|
283
|
+
*/
|
|
284
|
+
const TIER_KEYS = [
|
|
285
|
+
"shadows",
|
|
286
|
+
"bloom",
|
|
287
|
+
"post",
|
|
288
|
+
"clouds"
|
|
289
|
+
];
|
|
290
|
+
function setEnvironment3D(engine, patch, opts) {
|
|
257
291
|
const scene = engine.scene;
|
|
258
292
|
if (!scene) throw new IncantoError("BAD_FORMAT", "setEnvironment3D: the engine has no scene.");
|
|
259
293
|
const merged = { ...scene.environment ?? {} };
|
|
294
|
+
if (opts?.rebaseTierKeys) {
|
|
295
|
+
const authored = scene.source?.environment ?? {};
|
|
296
|
+
for (const key of TIER_KEYS) if (key in authored) merged[key] = authored[key];
|
|
297
|
+
else delete merged[key];
|
|
298
|
+
}
|
|
260
299
|
for (const [key, value] of Object.entries(patch)) if (value === null) delete merged[key];
|
|
261
300
|
else if (typeof value === "object" && !Array.isArray(value) && typeof merged[key] === "object" && merged[key] !== null && !Array.isArray(merged[key])) merged[key] = {
|
|
262
301
|
...merged[key],
|
|
@@ -266,6 +305,24 @@ function setEnvironment3D(engine, patch) {
|
|
|
266
305
|
parseEnvironment3D(merged);
|
|
267
306
|
scene.environment = merged;
|
|
268
307
|
}
|
|
308
|
+
/**
|
|
309
|
+
* Apply a quality tier — the only caller that may RESET as well as patch.
|
|
310
|
+
*
|
|
311
|
+
* A tier is re-applied every time the player moves the slider, so it cannot
|
|
312
|
+
* compose with what the last one left behind: `low` strips the author's bloom,
|
|
313
|
+
* and `high` then has nothing to put back. This re-derives the four tier-owned
|
|
314
|
+
* stages from the scene FILE first, so every tier is reversible, and so a stage
|
|
315
|
+
* the author never wrote can never appear — the contract `settings.ts` states
|
|
316
|
+
* and `incanto-performance.md` promises: *the tier can only take things away.*
|
|
317
|
+
*
|
|
318
|
+
* Measured before this, on a scene with no `bloom` and no `post` in its JSON:
|
|
319
|
+
* `high` and `medium` both rendered five GL passes at 3.3 ms where `low`
|
|
320
|
+
* rendered one at 1.24 ms — 62% of the frame on a chain the game never asked
|
|
321
|
+
* for, and `environment.bloom` read back `{strength: 0.8, threshold: 1}`.
|
|
322
|
+
*/
|
|
323
|
+
function applyQualityTier3D(engine, patch) {
|
|
324
|
+
setEnvironment3D(engine, patch, { rebaseTierKeys: true });
|
|
325
|
+
}
|
|
269
326
|
//#endregion
|
|
270
327
|
//#region src/3d/adaptive-resolution.ts
|
|
271
328
|
const DEFAULTS = {
|
|
@@ -1430,11 +1487,11 @@ var Renderer3D = class {
|
|
|
1430
1487
|
pixelRatio: opts.pixelRatio,
|
|
1431
1488
|
preserveDrawingBuffer: opts.preserveDrawingBuffer
|
|
1432
1489
|
});
|
|
1433
|
-
this.webgl = new WebGLRenderer({
|
|
1490
|
+
this.webgl = withWebGLContext(() => new WebGLRenderer({
|
|
1434
1491
|
canvas: opts.canvas,
|
|
1435
1492
|
antialias: rendering.antialias,
|
|
1436
1493
|
preserveDrawingBuffer: rendering.preserveDrawingBuffer
|
|
1437
|
-
});
|
|
1494
|
+
}));
|
|
1438
1495
|
this.webgl.info.autoReset = false;
|
|
1439
1496
|
this.engine.picker = (x, y) => this.pick(x, y);
|
|
1440
1497
|
this.basePixelRatio = rendering.pixelRatio;
|
|
@@ -2180,6 +2237,7 @@ async function createGame3D(opts) {
|
|
|
2180
2237
|
const engine = new Engine({
|
|
2181
2238
|
seed: opts.seed,
|
|
2182
2239
|
fixedHz: opts.fixedHz,
|
|
2240
|
+
...opts.settings ? { settings: opts.settings } : {},
|
|
2183
2241
|
...opts._scheduler ? { scheduler: opts._scheduler } : {}
|
|
2184
2242
|
});
|
|
2185
2243
|
const sourceJson = cloneSceneJson(opts.scene);
|
|
@@ -2294,7 +2352,7 @@ async function createGame3D(opts) {
|
|
|
2294
2352
|
cleanups.push(engine.settings.bindAudio(engine.audio));
|
|
2295
2353
|
cleanups.push(engine.settings.bindLocale(engine.locale));
|
|
2296
2354
|
cleanups.push(engine.settings.bindQuality((patch) => {
|
|
2297
|
-
|
|
2355
|
+
applyQualityTier3D(engine, patch);
|
|
2298
2356
|
}));
|
|
2299
2357
|
cleanups.push(engine.settings.bindFrameCap((fps) => {
|
|
2300
2358
|
engine.maxFps = fps;
|
|
@@ -2368,7 +2426,11 @@ async function createGame3D(opts) {
|
|
|
2368
2426
|
}
|
|
2369
2427
|
return {
|
|
2370
2428
|
engine,
|
|
2371
|
-
scene
|
|
2429
|
+
get scene() {
|
|
2430
|
+
const live = engine.scene;
|
|
2431
|
+
if (!live) throw new IncantoError("TREE_VIOLATION", "game.scene: the engine has no scene.");
|
|
2432
|
+
return live;
|
|
2433
|
+
},
|
|
2372
2434
|
sourceJson,
|
|
2373
2435
|
renderer,
|
|
2374
2436
|
physics,
|
|
@@ -2454,4 +2516,4 @@ function wireAudioUnlock(engine, root, canvas) {
|
|
|
2454
2516
|
return detachAll;
|
|
2455
2517
|
}
|
|
2456
2518
|
//#endregion
|
|
2457
|
-
export { Environment3D as a, syncTree as i, create_game_exports as n,
|
|
2519
|
+
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 };
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import { d as computeViewport, f as resolveViewport, k as diagnose, n as loadScene, z as registerBehavior } from "./loader-
|
|
3
|
-
import { _ as AudioPlayer, v as Engine } from "./register-
|
|
2
|
+
import { d as computeViewport, f as resolveViewport, k as diagnose, n as loadScene, z as registerBehavior } from "./loader-BC4PNtJX.js";
|
|
3
|
+
import { _ as AudioPlayer, v as Engine } from "./register-3ta-2Xig.js";
|
|
4
4
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { n as attachTouchControls } from "./touch-DEAmqGdf.js";
|
|
6
|
+
import { n as withWebGLContext, r as resolveRendering } from "./webgl-unavailable-N9nQqesw.js";
|
|
7
|
+
import { a as openBundledEditor, c as claimCanvasGestures, i as devServerLibrary, l as audioErrors, n as pauseWhenHidden, r as crossFade, s as wireDevChannel, t as teardown } from "./teardown-D2NEmxPB.js";
|
|
7
8
|
import { o as frameStats } from "./frame-report-BSMny7oe.js";
|
|
8
|
-
import { n as registerGameplayBehaviors } from "./gameplay-
|
|
9
|
-
import { g as PhysicsBody2D, n as UILayer, t as registerNodes2D, u as Camera2D, y as Node2D } from "./register-
|
|
9
|
+
import { n as registerGameplayBehaviors } from "./gameplay-DM1eu_cV.js";
|
|
10
|
+
import { g as PhysicsBody2D, n as UILayer, t as registerNodes2D, u as Camera2D, y as Node2D } from "./register-ibCjm-wH.js";
|
|
10
11
|
import { t as debugSources } from "./debug-draw-BM3DsvtT.js";
|
|
11
|
-
import { n as enablePhysics2D } from "./physics-2d-
|
|
12
|
+
import { n as enablePhysics2D } from "./physics-2d-B7Y6dPZO.js";
|
|
12
13
|
import { Box3, BufferAttribute, BufferGeometry, Color, LineBasicMaterial, LineSegments, LinearFilter, NearestFilter, OrthographicCamera, Raycaster, SRGBColorSpace, Scene, TextureLoader, Vector2, Vector3, WebGLRenderer } from "three";
|
|
13
14
|
//#region src/2d/assets.ts
|
|
14
15
|
/**
|
|
@@ -444,11 +445,11 @@ var Renderer2D = class {
|
|
|
444
445
|
pixelRatio: 1,
|
|
445
446
|
preserveDrawingBuffer: false
|
|
446
447
|
}, globalThis.devicePixelRatio ?? 1, opts);
|
|
447
|
-
this.webgl = new WebGLRenderer({
|
|
448
|
+
this.webgl = withWebGLContext(() => new WebGLRenderer({
|
|
448
449
|
canvas: opts.canvas,
|
|
449
450
|
antialias: rendering.antialias,
|
|
450
451
|
preserveDrawingBuffer: rendering.preserveDrawingBuffer
|
|
451
|
-
});
|
|
452
|
+
}));
|
|
452
453
|
this.webgl.info.autoReset = false;
|
|
453
454
|
this.engine.picker = (x, y) => this.pick(x, y);
|
|
454
455
|
this.basePixelRatio = rendering.pixelRatio;
|
|
@@ -756,6 +757,7 @@ async function createGame2D(opts) {
|
|
|
756
757
|
const engine = new Engine({
|
|
757
758
|
seed: opts.seed,
|
|
758
759
|
fixedHz: opts.fixedHz,
|
|
760
|
+
...opts.settings ? { settings: opts.settings } : {},
|
|
759
761
|
...opts._scheduler ? { scheduler: opts._scheduler } : {}
|
|
760
762
|
});
|
|
761
763
|
const sourceJson = cloneSceneJson(opts.scene);
|
|
@@ -918,7 +920,11 @@ async function createGame2D(opts) {
|
|
|
918
920
|
}
|
|
919
921
|
return {
|
|
920
922
|
engine,
|
|
921
|
-
scene
|
|
923
|
+
get scene() {
|
|
924
|
+
const live = engine.scene;
|
|
925
|
+
if (!live) throw new IncantoError("TREE_VIOLATION", "game.scene: the engine has no scene.");
|
|
926
|
+
return live;
|
|
927
|
+
},
|
|
922
928
|
sourceJson,
|
|
923
929
|
renderer,
|
|
924
930
|
physics,
|
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-DoFPYrgo.js";
|
|
2
2
|
|
|
3
3
|
//#region src/debug/panel.d.ts
|
|
4
4
|
/** Minimal document surface the overlay needs (injectable for tests). */
|
package/dist/debug.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { t as jsonClone } from "./json-CwwhxQgb.js";
|
|
2
2
|
import { s as mergeStaticProps } from "./registry-CF70EArN.js";
|
|
3
|
+
import { r as capturePointer } from "./touch-DEAmqGdf.js";
|
|
3
4
|
import { t as debugSources } from "./debug-draw-BM3DsvtT.js";
|
|
4
5
|
//#region src/debug/capture.ts
|
|
5
6
|
/**
|
|
@@ -369,7 +370,7 @@ var FloatingPanel = class {
|
|
|
369
370
|
active = e.pointerId;
|
|
370
371
|
lastX = e.clientX;
|
|
371
372
|
lastY = e.clientY;
|
|
372
|
-
grip
|
|
373
|
+
capturePointer(grip, e.pointerId);
|
|
373
374
|
});
|
|
374
375
|
grip.addEventListener("pointermove", (e) => {
|
|
375
376
|
if (e.pointerId !== active) return;
|
|
@@ -565,7 +566,7 @@ function openRegionSelect(opts) {
|
|
|
565
566
|
applyStyle(popup, { display: "none" });
|
|
566
567
|
applyStyle(box, { cursor: next === "move" ? "grabbing" : "crosshair" });
|
|
567
568
|
const id = event.pointerId;
|
|
568
|
-
if (id !== void 0) layer
|
|
569
|
+
if (id !== void 0) capturePointer(layer, id);
|
|
569
570
|
render();
|
|
570
571
|
}
|
|
571
572
|
function render() {
|
package/dist/editor.js
CHANGED
|
@@ -2380,7 +2380,8 @@ var Vn = [
|
|
|
2380
2380
|
"connections",
|
|
2381
2381
|
"input",
|
|
2382
2382
|
"multiplayer",
|
|
2383
|
-
"strings"
|
|
2383
|
+
"strings",
|
|
2384
|
+
"environment"
|
|
2384
2385
|
];
|
|
2385
2386
|
function Hn(e, t, n) {
|
|
2386
2387
|
e.appendChild(Tr("scene")), e.appendChild(Ar("name", String(t.working.name ?? ""), (e) => {
|
|
@@ -2397,19 +2398,25 @@ function Hn(e, t, n) {
|
|
|
2397
2398
|
t.mutate(() => {
|
|
2398
2399
|
t.working.dimension = r.value;
|
|
2399
2400
|
});
|
|
2400
|
-
}), e.appendChild(B("dimension", r))
|
|
2401
|
-
let i =
|
|
2401
|
+
}), e.appendChild(B("dimension", r));
|
|
2402
|
+
let i = document.createElement("input");
|
|
2403
|
+
i.type = "checkbox", i.checked = t.working.fragment === !0, i.addEventListener("change", () => {
|
|
2404
|
+
t.mutate(() => {
|
|
2405
|
+
i.checked ? t.working.fragment = !0 : delete t.working.fragment;
|
|
2406
|
+
});
|
|
2407
|
+
}), e.appendChild(B("fragment", i)), e.appendChild(Tr("physics", "physics"));
|
|
2408
|
+
let a = (t.working.physics ?? {}).gravity ?? (t.working.dimension === "3d" ? [
|
|
2402
2409
|
0,
|
|
2403
2410
|
-9.81,
|
|
2404
2411
|
0
|
|
2405
|
-
] : [0, 980]),
|
|
2412
|
+
] : [0, 980]), o = t.working.dimension === "3d" ? [
|
|
2406
2413
|
0,
|
|
2407
2414
|
-9.81,
|
|
2408
2415
|
0
|
|
2409
2416
|
] : [0, 980];
|
|
2410
|
-
e.appendChild(Mr("gravity",
|
|
2417
|
+
e.appendChild(Mr("gravity", a, (e) => {
|
|
2411
2418
|
t.mutate(() => {
|
|
2412
|
-
if (JSON.stringify(e) === JSON.stringify(
|
|
2419
|
+
if (JSON.stringify(e) === JSON.stringify(o)) {
|
|
2413
2420
|
let e = { ...t.working.physics };
|
|
2414
2421
|
delete e.gravity, Object.keys(e).length === 0 ? delete t.working.physics : t.working.physics = e;
|
|
2415
2422
|
} else t.working.physics = {
|
|
@@ -7842,6 +7849,10 @@ var ss = class {
|
|
|
7842
7849
|
return !0;
|
|
7843
7850
|
}) === !0;
|
|
7844
7851
|
}
|
|
7852
|
+
resume(e) {
|
|
7853
|
+
let t = this.original;
|
|
7854
|
+
this.reset(e), this.original = t;
|
|
7855
|
+
}
|
|
7845
7856
|
reset(e) {
|
|
7846
7857
|
this.working = e, this.working.root && cs(this.working.root), this.original = JSON.stringify(this.working), this.undoStack = [], this.selection = [], this.emit();
|
|
7847
7858
|
}
|
|
@@ -9794,7 +9805,7 @@ async function xc(e = {}) {
|
|
|
9794
9805
|
let t = e.target.matches("input, textarea, select");
|
|
9795
9806
|
(e.metaKey || e.ctrlKey) && e.key === "z" ? (e.preventDefault(), u.undo()) : (e.metaKey || e.ctrlKey) && e.key === "s" ? (e.preventDefault(), p.disabled || p.click()) : !t && e.code === "KeyF" ? P.zoomToFit() : !t && (e.code === "Digit0" || e.code === "Numpad0") ? P.gameView() : !t && e.code === "KeyW" ? P.setMode("move") : !t && e.code === "KeyE" ? P.setMode("rotate") : !t && e.code === "KeyR" && P.setMode("scale");
|
|
9796
9807
|
};
|
|
9797
|
-
return window.addEventListener("keydown", Me), n.push(() => window.removeEventListener("keydown", Me)), F(), e.camera && P.setEditCamera(e.camera.position, e.camera.target), kt.ready(C, w, o.version), {
|
|
9808
|
+
return window.addEventListener("keydown", Me), n.push(() => window.removeEventListener("keydown", Me)), F(), e.scene && u.resume(e.scene), e.camera && P.setEditCamera(e.camera.position, e.camera.target), kt.ready(C, w, o.version), {
|
|
9798
9809
|
working: () => u.working,
|
|
9799
9810
|
camera: () => P.editCamera(),
|
|
9800
9811
|
showError: O,
|
package/dist/env.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as Rng, i as SceneJson, n as NodeJson, s as JsonObject } from "./schema-
|
|
1
|
+
import { a as Rng, i as SceneJson, n as NodeJson, s as JsonObject } from "./schema-B6ugCV1Q.js";
|
|
2
2
|
|
|
3
3
|
//#region src/env/arena.d.ts
|
|
4
4
|
/** Valid `generateArena` themes — drives the catalog options AND validation. */
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { k as diagnose } from "./loader-
|
|
2
|
-
import { L as translateOn, t as registerCoreNodes } from "./register-
|
|
1
|
+
import { k as diagnose } 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 { t as createNoise2D } from "./noise-CGUMx44x.js";
|
|
7
7
|
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";
|
|
8
|
-
import { B as rejectScale, G as StaticBody3D, H as CharacterBody3D, K as Node3D, L as Water3D, U as PhysicsBody3D, V as Area3D, W as RigidBody3D, X as colliderFootDrop, w as tolerateUnknownAction, z as WaterCutout3D } from "./gameplay-
|
|
8
|
+
import { B as rejectScale, G as StaticBody3D, H as CharacterBody3D, I as worldPosition, K as Node3D, L as Water3D, U as PhysicsBody3D, V as Area3D, W as RigidBody3D, X as colliderFootDrop, w as tolerateUnknownAction, z as WaterCutout3D } from "./gameplay-DM1eu_cV.js";
|
|
9
9
|
import { t as checkSheetGrid } from "./sheet-grid-BT6N_Bjs.js";
|
|
10
10
|
import { n as splatWeights, t as buildHeightmap } from "./heightmap-CRK0M4jT.js";
|
|
11
11
|
import { AdditiveBlending, AnimationClip, AnimationMixer, Box3, BoxGeometry, BufferAttribute, BufferGeometry, CanvasTexture, CapsuleGeometry, ClampToEdgeWrapping, Color, ConeGeometry, CylinderGeometry, DataTexture, DirectionalLight, DoubleSide, DynamicDrawUsage, Euler, Group, IcosahedronGeometry, ImageBitmapLoader, InstancedBufferAttribute, InstancedMesh, LinearFilter, LinearMipmapLinearFilter, LoopOnce, LoopRepeat, Matrix4, Mesh, MeshBasicMaterial, MeshDepthMaterial, MeshPhysicalMaterial, MeshStandardMaterial, NearestFilter, NoBlending, NoColorSpace, NormalBlending, PerspectiveCamera, PlaneGeometry, PointLight, Points, PointsMaterial, Quaternion, QuaternionKeyframeTrack, RGBADepthPacking, RGBAFormat, RepeatWrapping, SRGBColorSpace, ShaderChunk, ShaderMaterial, SphereGeometry, Texture, TextureLoader, UniformsLib, UniformsUtils, Vector3, Vector4, VectorKeyframeTrack } from "three";
|
|
@@ -387,6 +387,10 @@ var MeshInstance3D = class extends Node3D {
|
|
|
387
387
|
validateMeshMaterial(node.material, node.name);
|
|
388
388
|
}
|
|
389
389
|
geometryKey = "";
|
|
390
|
+
materialKey = "";
|
|
391
|
+
/** True while this node is using a cache entry it must not dispose. */
|
|
392
|
+
sharedGeometry = false;
|
|
393
|
+
sharedMaterial = false;
|
|
390
394
|
textureKey = "";
|
|
391
395
|
loadedTextures = [];
|
|
392
396
|
_createObject3D() {
|
|
@@ -395,12 +399,48 @@ var MeshInstance3D = class extends Node3D {
|
|
|
395
399
|
_syncObject3D(alpha = 1, assets = null) {
|
|
396
400
|
super._syncObject3D(alpha, assets);
|
|
397
401
|
const m = this._ensureObject3D();
|
|
398
|
-
const key = `${this.mesh}:${
|
|
402
|
+
const key = `${this.mesh}:${this.size.join(",")}`;
|
|
399
403
|
if (key !== this.geometryKey) {
|
|
400
|
-
m.geometry.dispose();
|
|
401
|
-
|
|
404
|
+
if (!this.sharedGeometry) m.geometry.dispose();
|
|
405
|
+
const cached = assets?.meshGeometries.get(key);
|
|
406
|
+
if (cached) {
|
|
407
|
+
m.geometry = cached;
|
|
408
|
+
this.sharedGeometry = true;
|
|
409
|
+
} else {
|
|
410
|
+
const built = buildMeshGeometry(this.mesh, this.size);
|
|
411
|
+
m.geometry = built;
|
|
412
|
+
this.sharedGeometry = Boolean(assets);
|
|
413
|
+
assets?.meshGeometries.set(key, built);
|
|
414
|
+
}
|
|
402
415
|
this.geometryKey = key;
|
|
403
416
|
}
|
|
417
|
+
const look = JSON.stringify(this.material);
|
|
418
|
+
if (assets && look !== this.materialKey) {
|
|
419
|
+
const shared = assets.meshMaterials.get(look);
|
|
420
|
+
if (shared) {
|
|
421
|
+
if (!this.sharedMaterial) m.material.dispose();
|
|
422
|
+
m.material = shared;
|
|
423
|
+
this.sharedMaterial = true;
|
|
424
|
+
this.materialKey = look;
|
|
425
|
+
this.textureKey = "shared";
|
|
426
|
+
m.castShadow = this.castShadow;
|
|
427
|
+
m.receiveShadow = this.receiveShadow;
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
if (this.sharedMaterial) {
|
|
431
|
+
m.material = new MeshPhysicalMaterial();
|
|
432
|
+
this.sharedMaterial = false;
|
|
433
|
+
this.textureKey = "";
|
|
434
|
+
}
|
|
435
|
+
this.materialKey = look;
|
|
436
|
+
const mine = m.material;
|
|
437
|
+
applyMeshMaterial(mine, this.material);
|
|
438
|
+
this.syncTextures(mine, assets);
|
|
439
|
+
assets.meshMaterials.set(look, mine);
|
|
440
|
+
m.castShadow = this.castShadow;
|
|
441
|
+
m.receiveShadow = this.receiveShadow;
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
404
444
|
const mat = m.material;
|
|
405
445
|
applyMeshMaterial(mat, this.material);
|
|
406
446
|
this.syncTextures(mat, assets);
|
|
@@ -2674,6 +2714,7 @@ var CharacterController3D = class extends Node3D {
|
|
|
2674
2714
|
pitchMin: { default: -1.3 },
|
|
2675
2715
|
pitchMax: { default: 1.5 },
|
|
2676
2716
|
cameraCollision: { default: true },
|
|
2717
|
+
platformCarry: { default: true },
|
|
2677
2718
|
turnSpeed: { default: 100 },
|
|
2678
2719
|
camLerp: { default: 25 },
|
|
2679
2720
|
moveAction: { default: "move" },
|
|
@@ -2726,6 +2767,43 @@ var CharacterController3D = class extends Node3D {
|
|
|
2726
2767
|
pitchMax = 1.5;
|
|
2727
2768
|
/** Spring-arm: pull the orbit camera in front of walls/ground instead of clipping. */
|
|
2728
2769
|
cameraCollision = true;
|
|
2770
|
+
/**
|
|
2771
|
+
* Ride whatever you are standing on.
|
|
2772
|
+
*
|
|
2773
|
+
* `false` restores the old behaviour: the controller steers toward a target
|
|
2774
|
+
* velocity in WORLD space, so standing still on a moving floor brakes you off
|
|
2775
|
+
* it. Turn it off for a conveyor you want to be scenery.
|
|
2776
|
+
*/
|
|
2777
|
+
platformCarry = true;
|
|
2778
|
+
/** The node the ground probe hit last step, and where it was. */
|
|
2779
|
+
carriedBy = null;
|
|
2780
|
+
carriedFrom = [0, 0];
|
|
2781
|
+
/**
|
|
2782
|
+
* How fast the floor is moving, in world units per second.
|
|
2783
|
+
*
|
|
2784
|
+
* From the POSITION DELTA, not `linearVelocity`, because the documented way
|
|
2785
|
+
* to author a platform is to move its `position` — a behavior, a `PathFollow`,
|
|
2786
|
+
* an `Oscillate` — and that path calls `body.setTranslation`, which teleports
|
|
2787
|
+
* and carries no momentum at all. The delta sees both kinds.
|
|
2788
|
+
*
|
|
2789
|
+
* Only while grounded on the SAME node two steps running: the first step on a
|
|
2790
|
+
* new platform has nothing to compare against, and inventing a velocity there
|
|
2791
|
+
* would fling the player on every step onto one.
|
|
2792
|
+
*/
|
|
2793
|
+
groundCarry(ground, dt) {
|
|
2794
|
+
if (!this.platformCarry || !this.grounded || !ground || dt <= 0) {
|
|
2795
|
+
this.carriedBy = ground;
|
|
2796
|
+
if (ground) this.carriedFrom = groundXZ(ground);
|
|
2797
|
+
return [0, 0];
|
|
2798
|
+
}
|
|
2799
|
+
const at = groundXZ(ground);
|
|
2800
|
+
const same = this.carriedBy === ground;
|
|
2801
|
+
const was = this.carriedFrom;
|
|
2802
|
+
this.carriedBy = ground;
|
|
2803
|
+
this.carriedFrom = at;
|
|
2804
|
+
if (!same) return [0, 0];
|
|
2805
|
+
return [(at[0] - was[0]) / dt, (at[1] - was[1]) / dt];
|
|
2806
|
+
}
|
|
2729
2807
|
turnSpeed = 100;
|
|
2730
2808
|
/** Camera smoothing rate (1-exp(-rate·dt)); side view uses 5. */
|
|
2731
2809
|
camLerp = 25;
|
|
@@ -2790,6 +2868,8 @@ var CharacterController3D = class extends Node3D {
|
|
|
2790
2868
|
const rayLen = radius + 2;
|
|
2791
2869
|
const originY = pos[1] - halfHeight;
|
|
2792
2870
|
let toi = null;
|
|
2871
|
+
/** What the probe landed on — the platform, when there is one. */
|
|
2872
|
+
let ground = null;
|
|
2793
2873
|
for (const [ox, oz] of [
|
|
2794
2874
|
[0, 0],
|
|
2795
2875
|
[radius, 0],
|
|
@@ -2808,11 +2888,15 @@ var CharacterController3D = class extends Node3D {
|
|
|
2808
2888
|
], rayLen, body);
|
|
2809
2889
|
if (hit) {
|
|
2810
2890
|
const d = hit.distance;
|
|
2811
|
-
|
|
2891
|
+
if (toi === null || d < toi) {
|
|
2892
|
+
toi = d;
|
|
2893
|
+
ground = hit.node;
|
|
2894
|
+
}
|
|
2812
2895
|
if (ox === 0 && oz === 0) break;
|
|
2813
2896
|
}
|
|
2814
2897
|
}
|
|
2815
2898
|
this.grounded = toi !== null && toi < floatingDis * 2;
|
|
2899
|
+
const carry = this.groundCarry(ground, dt);
|
|
2816
2900
|
const footed = toi !== null && toi < floatingDis * 1.3 && (vel[1] ?? 0) <= .5;
|
|
2817
2901
|
let mx = 0;
|
|
2818
2902
|
let mz = 0;
|
|
@@ -2837,17 +2921,17 @@ var CharacterController3D = class extends Node3D {
|
|
|
2837
2921
|
const speedMultiplier = 1 + (this.sprintMultiplier - 1) * intensity;
|
|
2838
2922
|
const targetSpeed = moving ? this.maxSpeed * speedMultiplier : 0;
|
|
2839
2923
|
const control = this.grounded ? 1 : this.airControl;
|
|
2840
|
-
const ax = (mx * targetSpeed - vel[0]) / ACC_DELTA_TIME * control;
|
|
2841
|
-
const az = (mz * targetSpeed - vel[2]) / ACC_DELTA_TIME * control;
|
|
2924
|
+
const ax = (mx * targetSpeed + carry[0] - vel[0]) / ACC_DELTA_TIME * control;
|
|
2925
|
+
const az = (mz * targetSpeed + carry[1] - vel[2]) / ACC_DELTA_TIME * control;
|
|
2842
2926
|
body.applyImpulse([
|
|
2843
2927
|
ax * mass,
|
|
2844
2928
|
0,
|
|
2845
2929
|
az * mass
|
|
2846
2930
|
]);
|
|
2847
2931
|
if (!moving && this.grounded) body.applyImpulse([
|
|
2848
|
-
-vel[0] * DRAG_DAMPING * mass,
|
|
2932
|
+
-(vel[0] - carry[0]) * DRAG_DAMPING * mass,
|
|
2849
2933
|
0,
|
|
2850
|
-
-vel[2] * DRAG_DAMPING * mass
|
|
2934
|
+
-(vel[2] - carry[1]) * DRAG_DAMPING * mass
|
|
2851
2935
|
]);
|
|
2852
2936
|
let springToi = toi;
|
|
2853
2937
|
if (this.stepHeight > 0 && moving && toi !== null && !this.jumping && (vel[1] ?? 0) <= .5) {
|
|
@@ -3097,6 +3181,11 @@ function worldOf(node) {
|
|
|
3097
3181
|
* coyote time incoherent (a held button would re-fire through the whole
|
|
3098
3182
|
* window). Undeclared actions are tolerated, and reported once.
|
|
3099
3183
|
*/
|
|
3184
|
+
/** A node's world x/z — the platform may itself be parented to something. */
|
|
3185
|
+
function groundXZ(node) {
|
|
3186
|
+
const world = worldPosition(node);
|
|
3187
|
+
return [world[0] ?? 0, world[2] ?? 0];
|
|
3188
|
+
}
|
|
3100
3189
|
//#endregion
|
|
3101
3190
|
//#region src/3d/vegetation/grass-placement.ts
|
|
3102
3191
|
const NOISE_SCALE = .15;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { O as Node, P as Behavior, _ as SaveSlots, k as diagnose, n as loadScene, w as effectiveOrder, z as registerBehavior } from "./loader-
|
|
1
|
+
import { O as Node, P as Behavior, _ as SaveSlots, k as diagnose, n as loadScene, w as effectiveOrder, z as registerBehavior } from "./loader-BC4PNtJX.js";
|
|
2
2
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
3
3
|
import { t as jsonClone } from "./json-CwwhxQgb.js";
|
|
4
|
-
import { t as duplicateNode } from "./duplicate-
|
|
4
|
+
import { t as duplicateNode } from "./duplicate-IWIqk0HJ.js";
|
|
5
5
|
import { Color, CubeCamera, DepthTexture, DoubleSide, Euler, FloatType, Frustum, HalfFloatType, LinearMipmapLinearFilter, MathUtils, Matrix4, Mesh, MeshDepthMaterial, Object3D, PerspectiveCamera, Plane, PlaneGeometry, Quaternion, ShaderMaterial, Sphere, Vector2, Vector3, Vector4, WebGLCubeRenderTarget, WebGLRenderTarget } from "three";
|
|
6
6
|
//#region src/3d/frustum.ts
|
|
7
7
|
const scratchFrustum = new Frustum();
|
|
@@ -5016,12 +5016,20 @@ var Health = class extends Behavior {
|
|
|
5016
5016
|
* unlosable. Every checker was green on it, and `incanto-playtest` even
|
|
5017
5017
|
* reported `lost in 19/20` because a single `died` reads as a loss.
|
|
5018
5018
|
*
|
|
5019
|
-
* Wire
|
|
5019
|
+
* **Wire `reviveFull`, not this, to `lifeLost`.** `ScoreKeeper.loseLife()`
|
|
5020
|
+
* emits `lifeLost` WITH the remaining life count, and this method's first
|
|
5021
|
+
* parameter is the HP to come back at — so the documented recipe revived at
|
|
5022
|
+
* 2 HP, then 1, then 0, and nothing warned (the wire-arity guard fires only
|
|
5023
|
+
* when a handler is UNDER-fed, and `hp` is optional):
|
|
5020
5024
|
*
|
|
5021
|
-
* ```
|
|
5022
|
-
*
|
|
5025
|
+
* ```
|
|
5026
|
+
* death 1: lives=2 hp=2
|
|
5027
|
+
* death 2: lives=1 hp=1
|
|
5028
|
+
* death 3: lives=0 hp=0 dead=false <- alive at zero HP
|
|
5023
5029
|
* ```
|
|
5024
5030
|
*
|
|
5031
|
+
* Reach for `revive(hp)` when your game genuinely wants a partial comeback.
|
|
5032
|
+
*
|
|
5025
5033
|
* A no-op on the living — reviving someone who never died would silently
|
|
5026
5034
|
* refill their health, which is a different feature and not this one.
|
|
5027
5035
|
*/
|
|
@@ -5033,6 +5041,21 @@ var Health = class extends Behavior {
|
|
|
5033
5041
|
this.invulnTimer = this.invulnerableFor;
|
|
5034
5042
|
this.emit("revived", this.current);
|
|
5035
5043
|
}
|
|
5044
|
+
/**
|
|
5045
|
+
* Bring a dead one back at FULL health — the handler a life-lost wire wants.
|
|
5046
|
+
*
|
|
5047
|
+
* It exists because `revive` takes an optional `hp` and `lifeLost` carries the
|
|
5048
|
+
* life count, so wiring the two silently made "you have 2 lives left" mean
|
|
5049
|
+
* "come back at 2 HP". This takes no arguments, so no signal's payload can
|
|
5050
|
+
* ever change what it means.
|
|
5051
|
+
*
|
|
5052
|
+
* ```json
|
|
5053
|
+
* { "signal": "lifeLost", "from": "Score", "to": "Player", "handler": "reviveFull" }
|
|
5054
|
+
* ```
|
|
5055
|
+
*/
|
|
5056
|
+
reviveFull() {
|
|
5057
|
+
this.revive();
|
|
5058
|
+
}
|
|
5036
5059
|
/** Drop to 0 and die immediately (ignores i-frames). */
|
|
5037
5060
|
kill() {
|
|
5038
5061
|
if (this.dead) return;
|
|
@@ -6652,6 +6675,20 @@ var SavePoint = class extends Behavior {
|
|
|
6652
6675
|
this.slots ??= new SaveSlots(this.game);
|
|
6653
6676
|
return this.slots;
|
|
6654
6677
|
}
|
|
6678
|
+
/**
|
|
6679
|
+
* Has this run already said the saves are going nowhere?
|
|
6680
|
+
*
|
|
6681
|
+
* `saved(slot)` is the engine's own instrument for "it saved", and it fired
|
|
6682
|
+
* identically in a private window where the write landed in a Map that dies
|
|
6683
|
+
* with the tab. In-session everything looked healthy — the slot read back,
|
|
6684
|
+
* `slotScene()` answered — and across the reload the score went 5 to 0 and
|
|
6685
|
+
* `list()` went `["1"]` to `[]`, with `engine.log.entries().length = 0` and
|
|
6686
|
+
* `stats().errors = 0` in every phase. Byte-identical to a healthy control.
|
|
6687
|
+
*
|
|
6688
|
+
* So it goes through `engine.log`, not just the console: that is what
|
|
6689
|
+
* `incanto-logs` reads and what the `says` rung reports.
|
|
6690
|
+
*/
|
|
6691
|
+
warnedVolatile = false;
|
|
6655
6692
|
pendingRestore = false;
|
|
6656
6693
|
pendingProbe = false;
|
|
6657
6694
|
onReady() {
|
|
@@ -6711,6 +6748,10 @@ var SavePoint = class extends Behavior {
|
|
|
6711
6748
|
savedAt: Date.now(),
|
|
6712
6749
|
playtime: Math.round(this.elapsed)
|
|
6713
6750
|
});
|
|
6751
|
+
if (!this.store().persistent && !this.warnedVolatile) {
|
|
6752
|
+
this.warnedVolatile = true;
|
|
6753
|
+
engine.log.warn(`SavePoint wrote slot '${this.slot}' to memory, not to disk — this browser refused localStorage (private window, storage disabled, or quota exhausted), so the run is lost when the page closes. \`SaveSlots.persistent\` is how a game asks before promising the player anything.`);
|
|
6754
|
+
}
|
|
6714
6755
|
this.emit("saved", this.slot);
|
|
6715
6756
|
}
|
|
6716
6757
|
/**
|