incanto 0.67.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/_pick-server.mjs +48 -0
- package/bin/incanto-check.mjs +16 -2
- package/bin/incanto-frame.mjs +10 -25
- package/bin/incanto-logs.mjs +8 -18
- package/dist/2d.d.ts +6 -6
- package/dist/2d.js +6 -4
- package/dist/3d.d.ts +30 -6
- package/dist/3d.js +8 -6
- package/dist/{audio-player-BNUHo0JF.d.ts → audio-player-DOrq7sP-.d.ts} +62 -4
- package/dist/{behavior-ZnfzKDo3.d.ts → behavior-DoFPYrgo.d.ts} +78 -1
- package/dist/boot-failure-CKYrEcGF.js +47 -0
- package/dist/{create-game-FAXdHta3.js → create-game-DbWtVTxD.js} +21 -25
- package/dist/{create-game-DMetd3vR.js → create-game-IZIydDwI.js} +9 -9
- package/dist/debug.d.ts +1 -1
- package/dist/{duplicate-B8QnX2-r.js → duplicate-IWIqk0HJ.js} +1 -1
- package/dist/{editor-switch-DAvWQeld.d.ts → editor-switch-CnIOiyNJ.d.ts} +1 -1
- package/dist/editor.js +11 -5
- package/dist/env.d.ts +1 -1
- package/dist/{environment-presets-C-5g2pMh.js → environment-presets-CybQXNqS.js} +57 -8
- package/dist/{gameplay-CGSv-9XR.js → gameplay-DM1eu_cV.js} +20 -2
- package/dist/gameplay.d.ts +51 -3
- package/dist/gameplay.js +2 -2
- package/dist/index.d.ts +6 -6
- package/dist/index.js +8 -7
- package/dist/{loader-KZESOZmE.js → loader-BC4PNtJX.js} +26 -1
- package/dist/{loader-DyT30ArB.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-_B6zj3zU.js → physics-2d-B7Y6dPZO.js} +2 -2
- package/dist/{physics-3d-i1IeAY0F.js → physics-3d-bG3n70Ky.js} +3 -3
- package/dist/react.d.ts +2 -2
- package/dist/react.js +1 -1
- package/dist/{register-Cs53CY7s.js → register-3ta-2Xig.js} +68 -7
- package/dist/{register-vIxU3Xbv.js → register-ibCjm-wH.js} +3 -3
- package/dist/{replay-BkpDV51O.js → replay-CYvhVHHN.js} +28 -5
- package/dist/{replay-D0GcEHYU.d.ts → replay-Dvn8aeBd.d.ts} +38 -4
- package/dist/{schema-CFeioQRE.d.ts → schema-B6ugCV1Q.d.ts} +11 -0
- package/dist/{split-screen-DS8RaUV_.js → split-screen-CYwDkbLF.js} +2 -2
- package/dist/{split-screen-DD5eYIgW.d.ts → split-screen-DhrSzZIB.d.ts} +3 -3
- package/dist/{src-BLlsGJW9.js → src-D7RIqXYF.js} +1 -1
- package/dist/{teardown-LB9cTnXJ.js → teardown-D2NEmxPB.js} +2 -2
- package/dist/{test-81uZ967a.js → test-8hoHeRmo.js} +26 -21
- package/dist/test.d.ts +6 -6
- package/dist/test.js +3 -3
- package/dist/vite.js +2 -2
- package/dist/{rendering-options-ByAdi8PF.js → webgl-unavailable-N9nQqesw.js} +45 -1
- package/editor/assets/{agent8-C2jtNKbx.js → agent8-BrrHOjMJ.js} +1 -1
- package/editor/assets/{debug-DoU3KeqS.js → debug-BBhKuBNV.js} +1 -1
- package/editor/assets/{index-CNqHojcC.js → index-eVd0BToA.js} +92 -92
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/schemas/scene.schema.json +4 -0
- 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 +37 -0
- package/skills/incanto-gameplay-behaviors.md +28 -1
- package/skills/incanto-node-reference.md +1 -0
- package/skills/incanto-physics-and-input.md +23 -0
- package/skills/incanto-scene-json-authoring.md +12 -1
- package/templates-app/beacon-isle-3d/package.json +1 -1
- 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/main.ts +8 -1
- package/templates-app/star-survivor/package.json +1 -1
- 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/main.ts +8 -1
- package/templates-app/village-quest-3d/package.json +1 -1
- package/templates-app/village-quest-3d/src/main.ts +8 -1
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which of the dev servers that answered is THIS project's — the one decision
|
|
3
|
+
* `incanto-frame` and `incanto-logs` must never get wrong, in one place.
|
|
4
|
+
*
|
|
5
|
+
* They each had their own copy, and each copy had the same hatch: when exactly
|
|
6
|
+
* one server answered and it did not say whose it was, take it anyway ("an
|
|
7
|
+
* older incanto that predates the identity field"). A server that will not
|
|
8
|
+
* identify itself is the ONE kind the check cannot clear, so the hatch let
|
|
9
|
+
* through precisely what it was written to stop. Measured in an empty temp
|
|
10
|
+
* directory with one foreign page listening, on a scene with two nodes and no
|
|
11
|
+
* assets:
|
|
12
|
+
*
|
|
13
|
+
* ✓ draws — subject fills 37.5% of the frame
|
|
14
|
+
* ✗ says — asset …/NO-SUCH-TEXTURE.jpg never loaded: 404
|
|
15
|
+
*
|
|
16
|
+
* A ✓ measured off another game, and a ✗ naming a texture that appears nowhere
|
|
17
|
+
* in this project. It surfaced in this repo as an intermittent failure of
|
|
18
|
+
* `incanto-verify.test.ts`, which is a gate that lies in both directions.
|
|
19
|
+
*
|
|
20
|
+
* The real dev server has sent `root` since 0.41 (`frame-endpoint.test.ts`
|
|
21
|
+
* pins it), so nothing is lost; `--port` still names a server on purpose.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/** Is `body.root` this directory, an ancestor of it, or inside it? */
|
|
25
|
+
export function sameProject(body, cwd) {
|
|
26
|
+
if (!body?.root) return false;
|
|
27
|
+
const root = String(body.root);
|
|
28
|
+
return root === cwd || cwd.startsWith(`${root}/`) || root.startsWith(`${cwd}/`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* `{ pick }` when one of them is ours, `{ refusal }` when some answered and
|
|
33
|
+
* none is, `{}` when nothing answered at all (the caller says "no dev server").
|
|
34
|
+
*/
|
|
35
|
+
export function pickServer(answered, cwd, what) {
|
|
36
|
+
const mine = answered.filter((a) => sameProject(a, cwd));
|
|
37
|
+
if (mine.length > 0) return { pick: mine[0] };
|
|
38
|
+
if (answered.length === 0) return {};
|
|
39
|
+
const listed = answered
|
|
40
|
+
.map((a) => ` :${a.port} ${a.project || '(unnamed)'} ${a.root ?? "doesn't say"}`)
|
|
41
|
+
.join('\n');
|
|
42
|
+
return {
|
|
43
|
+
refusal:
|
|
44
|
+
`${answered.length} incanto dev server(s) are running and none of them is this ` +
|
|
45
|
+
`directory (${cwd}):\n${listed}\n\n${what}\n` +
|
|
46
|
+
"Start this project's dev server, or pass --port.",
|
|
47
|
+
};
|
|
48
|
+
}
|
package/bin/incanto-check.mjs
CHANGED
|
@@ -155,7 +155,9 @@ if (files.length === 0) {
|
|
|
155
155
|
process.exit(1);
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
const { auditScene, validateScene } = await import(
|
|
158
|
+
const { auditScene, instancedScenes, validateScene } = await import(
|
|
159
|
+
pathToFileURL(join(DIST, 'test.js')).href
|
|
160
|
+
);
|
|
159
161
|
|
|
160
162
|
/**
|
|
161
163
|
* Every `strings` table in the corpus, unioned.
|
|
@@ -176,9 +178,16 @@ const declaredElsewhere = {};
|
|
|
176
178
|
* part of its project it can.
|
|
177
179
|
*/
|
|
178
180
|
const declaredAssets = {};
|
|
181
|
+
/**
|
|
182
|
+
* Every scene some OTHER scene embeds — the only honest way to tell a fragment
|
|
183
|
+
* from a first draft. Without it a 3D scene with no camera and no light checked
|
|
184
|
+
* CLEAN, because "has no camera" was doing double duty as "is a fragment".
|
|
185
|
+
*/
|
|
186
|
+
const corpus = [];
|
|
179
187
|
for (const file of files) {
|
|
180
188
|
try {
|
|
181
189
|
const doc = JSON.parse(readFileSync(file, 'utf-8'));
|
|
190
|
+
corpus.push({ path: resolve(file), json: doc });
|
|
182
191
|
for (const [locale, entries] of Object.entries(doc?.strings ?? {})) {
|
|
183
192
|
if (typeof entries !== 'object' || entries === null) continue;
|
|
184
193
|
declaredElsewhere[locale] = { ...(declaredElsewhere[locale] ?? {}), ...entries };
|
|
@@ -191,6 +200,8 @@ for (const file of files) {
|
|
|
191
200
|
}
|
|
192
201
|
}
|
|
193
202
|
|
|
203
|
+
const instanced = instancedScenes(corpus, (from, ref) => resolve(dirname(from), ref));
|
|
204
|
+
|
|
194
205
|
const results = files.map((file) => {
|
|
195
206
|
let json;
|
|
196
207
|
try {
|
|
@@ -207,7 +218,10 @@ const results = files.map((file) => {
|
|
|
207
218
|
return {
|
|
208
219
|
file,
|
|
209
220
|
ok: true,
|
|
210
|
-
warnings: [
|
|
221
|
+
warnings: [
|
|
222
|
+
...auditScene(json, { declaredElsewhere, instanced: instanced.has(resolve(file)) }),
|
|
223
|
+
...missingArt(file, json),
|
|
224
|
+
],
|
|
211
225
|
};
|
|
212
226
|
}
|
|
213
227
|
return {
|
package/bin/incanto-frame.mjs
CHANGED
|
@@ -20,6 +20,7 @@ import { readFileSync, writeFileSync } from 'node:fs';
|
|
|
20
20
|
import { createRequire } from 'node:module';
|
|
21
21
|
import { dirname, join } from 'node:path';
|
|
22
22
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
23
|
+
import { pickServer } from './_pick-server.mjs';
|
|
23
24
|
|
|
24
25
|
const PKG = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
25
26
|
const { listeningPorts, parseProcNetTcp } = await import(
|
|
@@ -171,11 +172,6 @@ const answered = [];
|
|
|
171
172
|
// is plainly on screen.
|
|
172
173
|
const HOSTS = ['localhost', '127.0.0.1'];
|
|
173
174
|
const CWD = process.cwd();
|
|
174
|
-
const sameProject = (b) => {
|
|
175
|
-
if (!b?.root) return false;
|
|
176
|
-
const r = String(b.root);
|
|
177
|
-
return r === CWD || CWD.startsWith(`${r}/`) || r.startsWith(`${CWD}/`);
|
|
178
|
-
};
|
|
179
175
|
for (const port of ports) {
|
|
180
176
|
for (const host of HOSTS) {
|
|
181
177
|
try {
|
|
@@ -197,26 +193,15 @@ for (const port of ports) {
|
|
|
197
193
|
if (explicit) {
|
|
198
194
|
found = answered[0] ?? null;
|
|
199
195
|
} else {
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
);
|
|
210
|
-
} else if (answered.length > 0) {
|
|
211
|
-
console.error(
|
|
212
|
-
`${answered.length} incanto dev server(s) are running and NONE of them is this ` +
|
|
213
|
-
`directory (${CWD}):\n` +
|
|
214
|
-
answered
|
|
215
|
-
.map((a) => ` :${a.port} ${a.project || '(unnamed)'} ${a.root ?? '?'}`)
|
|
216
|
-
.join('\n') +
|
|
217
|
-
"\n\nMeasuring one project on another project's page reports a number that has\n" +
|
|
218
|
-
"nothing to do with this game. Start this project's dev server, or pass --port.",
|
|
219
|
-
);
|
|
196
|
+
const { pick, refusal } = pickServer(
|
|
197
|
+
answered,
|
|
198
|
+
CWD,
|
|
199
|
+
"Measuring one project on another project's page reports a number that has\n" +
|
|
200
|
+
'nothing to do with this game.',
|
|
201
|
+
);
|
|
202
|
+
if (pick) found = pick;
|
|
203
|
+
else if (refusal) {
|
|
204
|
+
console.error(refusal);
|
|
220
205
|
process.exit(1);
|
|
221
206
|
}
|
|
222
207
|
}
|
package/bin/incanto-logs.mjs
CHANGED
|
@@ -21,6 +21,7 @@ import { readFileSync } from 'node:fs';
|
|
|
21
21
|
import { createRequire } from 'node:module';
|
|
22
22
|
import { dirname, join } from 'node:path';
|
|
23
23
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
24
|
+
import { pickServer } from './_pick-server.mjs';
|
|
24
25
|
|
|
25
26
|
const PKG = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
26
27
|
const { listeningPorts } = await import(pathToFileURL(join(PKG, 'dist', 'vite.js')).href);
|
|
@@ -80,11 +81,6 @@ let found = null;
|
|
|
80
81
|
// project's page answered both the frame and the logs request.
|
|
81
82
|
const answered = [];
|
|
82
83
|
const CWD = process.cwd();
|
|
83
|
-
const sameProject = (b) => {
|
|
84
|
-
if (!b?.root) return false;
|
|
85
|
-
const r = String(b.root);
|
|
86
|
-
return r === CWD || CWD.startsWith(`${r}/`) || r.startsWith(`${CWD}/`);
|
|
87
|
-
};
|
|
88
84
|
for (const port of ports) {
|
|
89
85
|
for (const host of ['localhost', '127.0.0.1']) {
|
|
90
86
|
try {
|
|
@@ -105,20 +101,14 @@ for (const port of ports) {
|
|
|
105
101
|
if (explicit) {
|
|
106
102
|
found = answered[0] ? { host: answered[0].host, port: answered[0].port } : null;
|
|
107
103
|
} else {
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
104
|
+
const { pick, refusal } = pickServer(
|
|
105
|
+
answered,
|
|
106
|
+
CWD,
|
|
107
|
+
"Reading another project's log buffer would report its errors as yours.",
|
|
108
|
+
);
|
|
111
109
|
if (pick) found = { host: pick.host, port: pick.port };
|
|
112
|
-
else if (
|
|
113
|
-
console.error(
|
|
114
|
-
`${answered.length} incanto dev server(s) are running and none is this directory ` +
|
|
115
|
-
`(${CWD}):\n` +
|
|
116
|
-
answered
|
|
117
|
-
.map((a) => ` :${a.port} ${a.project || '(unnamed)'} ${a.root ?? '?'}`)
|
|
118
|
-
.join('\n') +
|
|
119
|
-
"\n\nReading another project's log buffer would report its errors as yours.\n" +
|
|
120
|
-
"Start this project's dev server, or pass --port.",
|
|
121
|
-
);
|
|
110
|
+
else if (refusal) {
|
|
111
|
+
console.error(refusal);
|
|
122
112
|
process.exit(1);
|
|
123
113
|
}
|
|
124
114
|
}
|
package/dist/2d.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as
|
|
3
|
-
import {
|
|
1
|
+
import { c as showBootFailure, i as ParticleSim, o as ParticleView, r as SpatialPose, s as isWebGLAvailable } from "./audio-player-DOrq7sP-.js";
|
|
2
|
+
import { P as Scene$1, S as Scheduler, T as RendererStats, b as Engine, d as PropSchema, jt as Node, n as BehaviorCtor, w as GameStats, x as EngineOptions } from "./behavior-DoFPYrgo.js";
|
|
3
|
+
import { n as DiagnosticSink, t as EditorSwitchOptions } from "./editor-switch-CnIOiyNJ.js";
|
|
4
|
+
import { i as SceneJson, s as JsonObject } from "./schema-B6ugCV1Q.js";
|
|
4
5
|
import { r as FrameStats } from "./frame-report-DCnHFmto.js";
|
|
5
|
-
import { t as LoadSceneOptions } from "./loader-
|
|
6
|
+
import { t as LoadSceneOptions } from "./loader-CcB533FR.js";
|
|
6
7
|
import { n as AnimationEntry } from "./sprite-animation-CMr6f1K2.js";
|
|
7
|
-
import { i as ParticleSim, o as ParticleView, r as SpatialPose } from "./audio-player-BNUHo0JF.js";
|
|
8
8
|
import { Group, Mesh, Object3D, Scene, Texture } from "three";
|
|
9
9
|
import * as RapierNs from "@dimforge/rapier2d-compat";
|
|
10
10
|
|
|
@@ -1247,4 +1247,4 @@ declare function parseCells(cells: readonly (string | readonly number[])[], lege
|
|
|
1247
1247
|
*/
|
|
1248
1248
|
declare function mergeSolidRects(grid: readonly (readonly number[])[], solid: Set<number>): TileRect[];
|
|
1249
1249
|
//#endregion
|
|
1250
|
-
export { AnimatedSprite2D, Area2D, type AssetStatus, AssetStore2D, Camera2D, CharacterBody2D, CharacterController2D, ColorRect2D, type CreateGame2DOptions, type Game2D, Joint2D, type JointType2D, Label, Node2D, Particles2D, Physics2D, type Physics2DOptions, PhysicsBody2D, Renderer2D, type Renderer2DOptions, type ResolvedSpriteTexture, RigidBody2D, type SheetInfo, Sprite2D, type SpriteFromLibraryResult, StaticBody2D, type Sync2DResult, type TextureLoadCallbacks, TileMap2D, type TileRect, type UIAnchor, UILayer, createGame2D, enablePhysics2D, mergeSolidRects, parseCells, registerNodes2D, spriteFromLibraryMeta, syncTree2D };
|
|
1250
|
+
export { AnimatedSprite2D, Area2D, type AssetStatus, AssetStore2D, Camera2D, CharacterBody2D, CharacterController2D, ColorRect2D, type CreateGame2DOptions, type Game2D, Joint2D, type JointType2D, Label, Node2D, Particles2D, Physics2D, type Physics2DOptions, PhysicsBody2D, Renderer2D, type Renderer2DOptions, type ResolvedSpriteTexture, RigidBody2D, type SheetInfo, Sprite2D, type SpriteFromLibraryResult, StaticBody2D, type Sync2DResult, type TextureLoadCallbacks, TileMap2D, type TileRect, type UIAnchor, UILayer, createGame2D, enablePhysics2D, isWebGLAvailable, mergeSolidRects, parseCells, registerNodes2D, showBootFailure, spriteFromLibraryMeta, syncTree2D };
|
package/dist/2d.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { t as showBootFailure } from "./boot-failure-CKYrEcGF.js";
|
|
3
|
+
import { t as isWebGLAvailable } from "./webgl-unavailable-N9nQqesw.js";
|
|
4
|
+
import { a as AssetStore2D, i as syncTree2D, r as Renderer2D, t as createGame2D } from "./create-game-IZIydDwI.js";
|
|
5
|
+
import { _ as RigidBody2D, a as parseCells, c as ColorRect2D, d as AnimatedSprite2D, f as Sprite2D, g as PhysicsBody2D, h as CharacterBody2D, i as mergeSolidRects, l as CharacterController2D, m as Area2D, n as UILayer, o as Particles2D, p as Joint2D, r as TileMap2D, s as Label, t as registerNodes2D, u as Camera2D, v as StaticBody2D, y as Node2D } from "./register-ibCjm-wH.js";
|
|
6
|
+
import { n as enablePhysics2D, t as Physics2D } from "./physics-2d-B7Y6dPZO.js";
|
|
5
7
|
//#region src/2d/library-sprite.ts
|
|
6
8
|
/**
|
|
7
9
|
* What a `CharacterController2D`/`3D` will ask a skin to play, and the clip in
|
|
@@ -95,4 +97,4 @@ function spriteFromLibraryMeta(meta, opts) {
|
|
|
95
97
|
};
|
|
96
98
|
}
|
|
97
99
|
//#endregion
|
|
98
|
-
export { AnimatedSprite2D, Area2D, AssetStore2D, Camera2D, CharacterBody2D, CharacterController2D, ColorRect2D, Joint2D, Label, Node2D, Particles2D, Physics2D, PhysicsBody2D, Renderer2D, RigidBody2D, Sprite2D, StaticBody2D, TileMap2D, UILayer, createGame2D, enablePhysics2D, mergeSolidRects, parseCells, registerNodes2D, spriteFromLibraryMeta, syncTree2D };
|
|
100
|
+
export { AnimatedSprite2D, Area2D, AssetStore2D, Camera2D, CharacterBody2D, CharacterController2D, ColorRect2D, Joint2D, Label, Node2D, Particles2D, Physics2D, PhysicsBody2D, Renderer2D, RigidBody2D, Sprite2D, StaticBody2D, TileMap2D, UILayer, createGame2D, enablePhysics2D, isWebGLAvailable, mergeSolidRects, parseCells, registerNodes2D, showBootFailure, spriteFromLibraryMeta, syncTree2D };
|
package/dist/3d.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as
|
|
3
|
-
import {
|
|
1
|
+
import { c as showBootFailure, i as ParticleSim, o as ParticleView, r as SpatialPose, s as isWebGLAvailable } from "./audio-player-DOrq7sP-.js";
|
|
2
|
+
import { D as QualityCaps, P as Scene$1, S as Scheduler, T as RendererStats, b as Engine, d as PropSchema, jt as Node, n as BehaviorCtor, w as GameStats, x as EngineOptions } from "./behavior-DoFPYrgo.js";
|
|
3
|
+
import { n as DiagnosticSink, t as EditorSwitchOptions } from "./editor-switch-CnIOiyNJ.js";
|
|
4
|
+
import { i as SceneJson$1, s as JsonObject } from "./schema-B6ugCV1Q.js";
|
|
4
5
|
import { a as GridCell, c as diffText, d as frameText, i as FrameStatsOptions, l as frameSignature, n as FrameSignature, o as SIGNATURE_GRID, r as FrameStats, s as diffSignatures, t as FrameDiff, u as frameStats } from "./frame-report-DCnHFmto.js";
|
|
5
|
-
import { t as LoadSceneOptions } from "./loader-
|
|
6
|
-
import { i as ParticleSim, o as ParticleView, r as SpatialPose } from "./audio-player-BNUHo0JF.js";
|
|
6
|
+
import { t as LoadSceneOptions } from "./loader-CcB533FR.js";
|
|
7
7
|
import { n as PathGrid } from "./pathfinding-_fGrCFmH.js";
|
|
8
8
|
import { AnimationClip, AnimationMixer, BufferGeometry, Color, DirectionalLight, Group, InstancedMesh, Mesh, MeshPhysicalMaterial, Object3D, PerspectiveCamera, Scene, ShaderMaterial, Texture, Vector3, WebGLRenderer } from "three";
|
|
9
9
|
import { VRM } from "@pixiv/three-vrm";
|
|
@@ -1404,6 +1404,30 @@ declare class CharacterController3D extends Node3D {
|
|
|
1404
1404
|
pitchMax: number;
|
|
1405
1405
|
/** Spring-arm: pull the orbit camera in front of walls/ground instead of clipping. */
|
|
1406
1406
|
cameraCollision: boolean;
|
|
1407
|
+
/**
|
|
1408
|
+
* Ride whatever you are standing on.
|
|
1409
|
+
*
|
|
1410
|
+
* `false` restores the old behaviour: the controller steers toward a target
|
|
1411
|
+
* velocity in WORLD space, so standing still on a moving floor brakes you off
|
|
1412
|
+
* it. Turn it off for a conveyor you want to be scenery.
|
|
1413
|
+
*/
|
|
1414
|
+
platformCarry: boolean;
|
|
1415
|
+
/** The node the ground probe hit last step, and where it was. */
|
|
1416
|
+
private carriedBy;
|
|
1417
|
+
private carriedFrom;
|
|
1418
|
+
/**
|
|
1419
|
+
* How fast the floor is moving, in world units per second.
|
|
1420
|
+
*
|
|
1421
|
+
* From the POSITION DELTA, not `linearVelocity`, because the documented way
|
|
1422
|
+
* to author a platform is to move its `position` — a behavior, a `PathFollow`,
|
|
1423
|
+
* an `Oscillate` — and that path calls `body.setTranslation`, which teleports
|
|
1424
|
+
* and carries no momentum at all. The delta sees both kinds.
|
|
1425
|
+
*
|
|
1426
|
+
* Only while grounded on the SAME node two steps running: the first step on a
|
|
1427
|
+
* new platform has nothing to compare against, and inventing a velocity there
|
|
1428
|
+
* would fling the player on every step onto one.
|
|
1429
|
+
*/
|
|
1430
|
+
private groundCarry;
|
|
1407
1431
|
turnSpeed: number;
|
|
1408
1432
|
/** Camera smoothing rate (1-exp(-rate·dt)); side view uses 5. */
|
|
1409
1433
|
camLerp: number;
|
|
@@ -4106,4 +4130,4 @@ declare function createNavDebugNode(nav: TerrainNav, opts?: {
|
|
|
4106
4130
|
name?: string;
|
|
4107
4131
|
}): InstancedMesh3D;
|
|
4108
4132
|
//#endregion
|
|
4109
|
-
export { Area3D, AssetStore3D, type BedSampler, Billboard3D, type BillboardGroupMode, BoneAttachment3D, BoneLookAt3D, Camera3D, CharacterBody3D, CharacterController3D, type CreateGame3DOptions, DEFAULT_TERRAIN_TEXTURE_BASE, DirectionalLight3D, type DownhillTraceOptions, Environment3D, type Environment3DConfig, DENSITY_PRESETS as FLOWER_DENSITY_PRESETS, FLOWER_VARIETIES, type FlowerVariety, Flowers3D, type FogEnvironment, Foliage3D, type FoliageKind, type FoliageStyle, type FrameDiff, type FrameSignature, type FrameStats, type FrameStatsOptions, type Game3D, type GridCell, type HeightSampler, type Heightmap, type HeightmapOptions, InstancedMesh3D, Joint3D, type JointType3D, LoftMesh3D, type LoftSection, MeshInstance3D, type MeshKind, type MeshMaterialProps, type ModelEntry, type ModelFacts, ModelInstance3D, type ModelVerdict, Node3D, OmniLight3D, Particles3D, Physics3D, type Physics3DOptions, PhysicsBody3D, QUARTER_PITCH, type RenderContext3D, type RenderHook3D, Renderer3D, type Renderer3DOptions, type Rig, type RigView, RigidBody3D, type Ripple, River3D, type RiverCarveOptions, type RiverCoverageGap, type RiverHit, type RiverRing, type RiverRingOptions, SIGNATURE_GRID, type SceneJson, type ShadowsEnvironment, type SkyEnvironment, StaticBody3D, type SunConsumer3D, type SyncOptions, type SyncResult, TERRAIN_THEMES, Terrain3D, type TerrainLayer, type TerrainNav, type TerrainNavOptions, type TerrainTheme, TextureCache3D, type TextureSpec, Trail3D, Tree3D, type TreeTier, type TreeType, VOXEL_PALETTE, type VoxelBlock, VoxelGrid3D, WATER_CUTOUT_MAX, WATER_MAX_RIPPLES, Water3D, WaterCutout3D, acquireOwnTexture, acquireTexture, applyQualityTier3D, buildHeightmap, buildRiverRings, buildTerrainNav, cameraRelative, createGame3D, createNavDebugNode, diffSignatures, diffText, enablePhysics3D, findRiverCoverageGaps, frameSignature, frameStats, frameText, horizonColorFromSky, keyboardIntensity, modelVerdict, movementState, parseEnvironment3D, projectToRiver, registerNodes3D, resolveFlowerDensity, rigPose, riverCarveChannels, riverStepFor, setEnvironment3D, smoothCourse, splatWeights, sunDirectionFromElevationAzimuth, sunDirectionFromSky, syncTree, terrainThemeLayers, traceDownhillPath, verdictText };
|
|
4133
|
+
export { Area3D, AssetStore3D, type BedSampler, Billboard3D, type BillboardGroupMode, BoneAttachment3D, BoneLookAt3D, Camera3D, CharacterBody3D, CharacterController3D, type CreateGame3DOptions, DEFAULT_TERRAIN_TEXTURE_BASE, DirectionalLight3D, type DownhillTraceOptions, Environment3D, type Environment3DConfig, DENSITY_PRESETS as FLOWER_DENSITY_PRESETS, FLOWER_VARIETIES, type FlowerVariety, Flowers3D, type FogEnvironment, Foliage3D, type FoliageKind, type FoliageStyle, type FrameDiff, type FrameSignature, type FrameStats, type FrameStatsOptions, type Game3D, type GridCell, type HeightSampler, type Heightmap, type HeightmapOptions, InstancedMesh3D, Joint3D, type JointType3D, LoftMesh3D, type LoftSection, MeshInstance3D, type MeshKind, type MeshMaterialProps, type ModelEntry, type ModelFacts, ModelInstance3D, type ModelVerdict, Node3D, OmniLight3D, Particles3D, Physics3D, type Physics3DOptions, PhysicsBody3D, QUARTER_PITCH, type RenderContext3D, type RenderHook3D, Renderer3D, type Renderer3DOptions, type Rig, type RigView, RigidBody3D, type Ripple, River3D, type RiverCarveOptions, type RiverCoverageGap, type RiverHit, type RiverRing, type RiverRingOptions, SIGNATURE_GRID, type SceneJson, type ShadowsEnvironment, type SkyEnvironment, StaticBody3D, type SunConsumer3D, type SyncOptions, type SyncResult, TERRAIN_THEMES, Terrain3D, type TerrainLayer, type TerrainNav, type TerrainNavOptions, type TerrainTheme, TextureCache3D, type TextureSpec, Trail3D, Tree3D, type TreeTier, type TreeType, VOXEL_PALETTE, type VoxelBlock, VoxelGrid3D, WATER_CUTOUT_MAX, WATER_MAX_RIPPLES, Water3D, WaterCutout3D, acquireOwnTexture, acquireTexture, applyQualityTier3D, buildHeightmap, buildRiverRings, buildTerrainNav, cameraRelative, createGame3D, createNavDebugNode, diffSignatures, diffText, enablePhysics3D, findRiverCoverageGaps, frameSignature, frameStats, frameText, horizonColorFromSky, isWebGLAvailable, keyboardIntensity, modelVerdict, movementState, parseEnvironment3D, projectToRiver, registerNodes3D, resolveFlowerDensity, rigPose, riverCarveChannels, riverStepFor, setEnvironment3D, showBootFailure, smoothCourse, splatWeights, sunDirectionFromElevationAzimuth, sunDirectionFromSky, syncTree, terrainThemeLayers, traceDownhillPath, verdictText };
|
package/dist/3d.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { c as parseEnvironment3D, l as sunDirectionFromElevationAzimuth, s as horizonColorFromSky, u as sunDirectionFromSky } from "./loader-
|
|
1
|
+
import { c as parseEnvironment3D, l as sunDirectionFromElevationAzimuth, s as horizonColorFromSky, u as sunDirectionFromSky } from "./loader-BC4PNtJX.js";
|
|
2
|
+
import { t as showBootFailure } from "./boot-failure-CKYrEcGF.js";
|
|
3
|
+
import { t as isWebGLAvailable } from "./webgl-unavailable-N9nQqesw.js";
|
|
2
4
|
import { a as frameSignature, n as diffSignatures, o as frameStats, r as diffText, s as frameText, t as SIGNATURE_GRID } from "./frame-report-BSMny7oe.js";
|
|
3
|
-
import { G as StaticBody3D, H as CharacterBody3D, K as Node3D, L as Water3D, R as WATER_CUTOUT_MAX, U as PhysicsBody3D, V as Area3D, W as RigidBody3D, Y as WATER_MAX_RIPPLES, z as WaterCutout3D } from "./gameplay-
|
|
4
|
-
import { A as Terrain3D, B as keyboardIntensity, C as resolveFlowerDensity, D as BoneLookAt3D, E as Camera3D, F as InstancedMesh3D, G as acquireTexture, H as rigPose, I as MeshInstance3D, M as TERRAIN_THEMES, N as terrainThemeLayers, O as BoneAttachment3D, P as Joint3D, R as QUARTER_PITCH, S as Flowers3D, T as CharacterController3D, U as TextureCache3D, V as movementState, W as acquireOwnTexture, _ as LoftMesh3D, a as Tree3D, b as Foliage3D, c as buildRiverRings, d as riverCarveChannels, f as riverStepFor, g as ModelInstance3D, h as Particles3D, i as VoxelGrid3D, j as DEFAULT_TERRAIN_TEXTURE_BASE, k as Billboard3D, l as findRiverCoverageGaps, m as traceDownhillPath, n as registerNodes3D, o as Trail3D, p as smoothCourse, r as VOXEL_PALETTE, s as River3D, u as projectToRiver, v as DirectionalLight3D, w as FLOWER_VARIETIES, x as DENSITY_PRESETS, y as OmniLight3D, z as cameraRelative } from "./environment-presets-
|
|
5
|
-
import { a as Environment3D, c as AssetStore3D, i as syncTree, o as applyQualityTier3D, r as Renderer3D, s as setEnvironment3D, t as createGame3D } from "./create-game-
|
|
5
|
+
import { G as StaticBody3D, H as CharacterBody3D, K as Node3D, L as Water3D, R as WATER_CUTOUT_MAX, U as PhysicsBody3D, V as Area3D, W as RigidBody3D, Y as WATER_MAX_RIPPLES, z as WaterCutout3D } from "./gameplay-DM1eu_cV.js";
|
|
6
|
+
import { A as Terrain3D, B as keyboardIntensity, C as resolveFlowerDensity, D as BoneLookAt3D, E as Camera3D, F as InstancedMesh3D, G as acquireTexture, H as rigPose, I as MeshInstance3D, M as TERRAIN_THEMES, N as terrainThemeLayers, O as BoneAttachment3D, P as Joint3D, R as QUARTER_PITCH, S as Flowers3D, T as CharacterController3D, U as TextureCache3D, V as movementState, W as acquireOwnTexture, _ as LoftMesh3D, a as Tree3D, b as Foliage3D, c as buildRiverRings, d as riverCarveChannels, f as riverStepFor, g as ModelInstance3D, h as Particles3D, i as VoxelGrid3D, j as DEFAULT_TERRAIN_TEXTURE_BASE, k as Billboard3D, l as findRiverCoverageGaps, m as traceDownhillPath, n as registerNodes3D, o as Trail3D, p as smoothCourse, r as VOXEL_PALETTE, s as River3D, u as projectToRiver, v as DirectionalLight3D, w as FLOWER_VARIETIES, x as DENSITY_PRESETS, y as OmniLight3D, z as cameraRelative } from "./environment-presets-CybQXNqS.js";
|
|
7
|
+
import { a as Environment3D, c as AssetStore3D, i as syncTree, o as applyQualityTier3D, r as Renderer3D, s as setEnvironment3D, t as createGame3D } from "./create-game-DbWtVTxD.js";
|
|
6
8
|
import { n as splatWeights, t as buildHeightmap } from "./heightmap-CRK0M4jT.js";
|
|
7
|
-
import { n as enablePhysics3D, t as Physics3D } from "./physics-3d-
|
|
9
|
+
import { n as enablePhysics3D, t as Physics3D } from "./physics-3d-bG3n70Ky.js";
|
|
8
10
|
//#region src/3d/model-verdict.ts
|
|
9
11
|
/** Mixamo exports every bone as `mixamorigX`; the retargeter binds by that name. */
|
|
10
12
|
const MIXAMO = /^mixamorig[:_]?/i;
|
|
@@ -317,4 +319,4 @@ function createNavDebugNode(nav, opts) {
|
|
|
317
319
|
return node;
|
|
318
320
|
}
|
|
319
321
|
//#endregion
|
|
320
|
-
export { Area3D, AssetStore3D, Billboard3D, BoneAttachment3D, BoneLookAt3D, Camera3D, CharacterBody3D, CharacterController3D, DEFAULT_TERRAIN_TEXTURE_BASE, DirectionalLight3D, Environment3D, DENSITY_PRESETS as FLOWER_DENSITY_PRESETS, FLOWER_VARIETIES, Flowers3D, Foliage3D, InstancedMesh3D, Joint3D, LoftMesh3D, MeshInstance3D, ModelInstance3D, Node3D, OmniLight3D, Particles3D, Physics3D, PhysicsBody3D, QUARTER_PITCH, Renderer3D, RigidBody3D, River3D, SIGNATURE_GRID, StaticBody3D, TERRAIN_THEMES, Terrain3D, TextureCache3D, Trail3D, Tree3D, VOXEL_PALETTE, VoxelGrid3D, WATER_CUTOUT_MAX, WATER_MAX_RIPPLES, Water3D, WaterCutout3D, acquireOwnTexture, acquireTexture, applyQualityTier3D, buildHeightmap, buildRiverRings, buildTerrainNav, cameraRelative, createGame3D, createNavDebugNode, diffSignatures, diffText, enablePhysics3D, findRiverCoverageGaps, frameSignature, frameStats, frameText, horizonColorFromSky, keyboardIntensity, modelVerdict, movementState, parseEnvironment3D, projectToRiver, registerNodes3D, resolveFlowerDensity, rigPose, riverCarveChannels, riverStepFor, setEnvironment3D, smoothCourse, splatWeights, sunDirectionFromElevationAzimuth, sunDirectionFromSky, syncTree, terrainThemeLayers, traceDownhillPath, verdictText };
|
|
322
|
+
export { Area3D, AssetStore3D, Billboard3D, BoneAttachment3D, BoneLookAt3D, Camera3D, CharacterBody3D, CharacterController3D, DEFAULT_TERRAIN_TEXTURE_BASE, DirectionalLight3D, Environment3D, DENSITY_PRESETS as FLOWER_DENSITY_PRESETS, FLOWER_VARIETIES, Flowers3D, Foliage3D, InstancedMesh3D, Joint3D, LoftMesh3D, MeshInstance3D, ModelInstance3D, Node3D, OmniLight3D, Particles3D, Physics3D, PhysicsBody3D, QUARTER_PITCH, Renderer3D, RigidBody3D, River3D, SIGNATURE_GRID, StaticBody3D, TERRAIN_THEMES, Terrain3D, TextureCache3D, Trail3D, Tree3D, VOXEL_PALETTE, VoxelGrid3D, WATER_CUTOUT_MAX, WATER_MAX_RIPPLES, Water3D, WaterCutout3D, acquireOwnTexture, acquireTexture, applyQualityTier3D, buildHeightmap, buildRiverRings, buildTerrainNav, cameraRelative, createGame3D, createNavDebugNode, diffSignatures, diffText, enablePhysics3D, findRiverCoverageGaps, frameSignature, frameStats, frameText, horizonColorFromSky, isWebGLAvailable, keyboardIntensity, modelVerdict, movementState, parseEnvironment3D, projectToRiver, registerNodes3D, resolveFlowerDensity, rigPose, riverCarveChannels, riverStepFor, setEnvironment3D, showBootFailure, smoothCourse, splatWeights, sunDirectionFromElevationAzimuth, sunDirectionFromSky, syncTree, terrainThemeLayers, traceDownhillPath, verdictText };
|
|
@@ -1,6 +1,42 @@
|
|
|
1
|
-
import { d as PropSchema, dt as Listener, jt as Node } from "./behavior-
|
|
2
|
-
import { a as Rng } from "./schema-
|
|
1
|
+
import { d as PropSchema, dt as Listener, jt as Node } from "./behavior-DoFPYrgo.js";
|
|
2
|
+
import { a as Rng } from "./schema-B6ugCV1Q.js";
|
|
3
3
|
|
|
4
|
+
//#region src/core/boot-failure.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Put a boot failure on the SCREEN, where the player is looking.
|
|
7
|
+
*
|
|
8
|
+
* Every shipped starter preloads its assets behind a `#loading` overlay and
|
|
9
|
+
* removes that overlay on the line after `await createGame*`. So anything that
|
|
10
|
+
* rejects — no WebGL context, a scene that will not load, a 404 the preload
|
|
11
|
+
* insisted on — leaves the overlay standing at "100%" forever, with the reason
|
|
12
|
+
* in a console the player will never open. Measured on a pristine scaffold with
|
|
13
|
+
* WebGL denied:
|
|
14
|
+
*
|
|
15
|
+
* ```
|
|
16
|
+
* control #loading gone "Talk to the Elder [E] …"
|
|
17
|
+
* no webgl #loading present "Emberwood 100%" … and still there 8.6 s later
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* A bar that reaches 100% and stops reads as a hang, which is the one failure
|
|
21
|
+
* mode a player cannot report usefully. This turns it into a sentence.
|
|
22
|
+
*
|
|
23
|
+
* Headless it does nothing but rethrow-safe logging, so a test that boots a
|
|
24
|
+
* game without a document is unaffected.
|
|
25
|
+
*/
|
|
26
|
+
declare function showBootFailure(error: unknown, opts?: {
|
|
27
|
+
into?: string;
|
|
28
|
+
}): void;
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/core/webgl-unavailable.d.ts
|
|
31
|
+
/**
|
|
32
|
+
* Can this document make a WebGL context at all?
|
|
33
|
+
*
|
|
34
|
+
* The question a game wants BEFORE it boots, so the answer can be a screen
|
|
35
|
+
* instead of an exception. Uses a throwaway canvas and never touches yours;
|
|
36
|
+
* `false` outside a browser, where the question is not meaningful.
|
|
37
|
+
*/
|
|
38
|
+
declare function isWebGLAvailable(): boolean;
|
|
39
|
+
//#endregion
|
|
4
40
|
//#region src/core/particle-sim.d.ts
|
|
5
41
|
/**
|
|
6
42
|
* Deterministic particle pool — pure math, renderer-agnostic, headless-
|
|
@@ -197,7 +233,29 @@ declare class AudioPlayer extends Node {
|
|
|
197
233
|
private spatialElementGain;
|
|
198
234
|
/** The `spatial` option for the SFX path, or undefined when not spatial. */
|
|
199
235
|
private spatialPlay;
|
|
200
|
-
|
|
236
|
+
/**
|
|
237
|
+
* Start the sound. `at` schedules it on the AUDIO clock instead of now.
|
|
238
|
+
*
|
|
239
|
+
* A frame is a 16.67 ms grid at 60 Hz and 33.33 ms at 30, so a sound fired
|
|
240
|
+
* from `update()` cannot land closer than one frame to where a chart wants
|
|
241
|
+
* it — measured 0 of 64 notes scheduled on the documented path. Queue it a
|
|
242
|
+
* lead ahead of `engine.sfx.now` and the same notes land at |mean| 0.000000
|
|
243
|
+
* ms:
|
|
244
|
+
*
|
|
245
|
+
* ```ts
|
|
246
|
+
* const lead = 0.08; // schedule this far ahead
|
|
247
|
+
* for (const note of dueSoon(engine.sfx.now + lead)) {
|
|
248
|
+
* hit.play(engine.sfx.now + (note.atSec - songSeconds));
|
|
249
|
+
* }
|
|
250
|
+
* ```
|
|
251
|
+
*
|
|
252
|
+
* PRESETS only — a `src` clip goes through an `<audio>` element, which has no
|
|
253
|
+
* scheduling clock, and passing `at` for one is ignored rather than silently
|
|
254
|
+
* approximated. Everything else is unchanged: the bus gain applies, the sound
|
|
255
|
+
* lands in `engine.audio.recent()`, and `muted` still silences it, none of
|
|
256
|
+
* which a hand-rolled `AudioContext` scheduler keeps.
|
|
257
|
+
*/
|
|
258
|
+
play(at?: number): void;
|
|
201
259
|
/**
|
|
202
260
|
* Procedural-SFX path: synthesize the preset and fire it through WebAudio
|
|
203
261
|
* (low-latency, overlap-friendly). Headless (no AudioContext) plays nothing —
|
|
@@ -222,4 +280,4 @@ declare class AudioPlayer extends Node {
|
|
|
222
280
|
override update(_dt: number): void;
|
|
223
281
|
}
|
|
224
282
|
//#endregion
|
|
225
|
-
export { ParticleSimConfig as a, ParticleSim as i, AudioPlayer as n, ParticleView as o, SpatialPose as r, AudioElementLike as t };
|
|
283
|
+
export { ParticleSimConfig as a, showBootFailure as c, ParticleSim as i, AudioPlayer as n, ParticleView as o, SpatialPose as r, isWebGLAvailable as s, AudioElementLike as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as Rng, c as JsonValue, i as SceneJson, s as JsonObject } from "./schema-
|
|
1
|
+
import { a as Rng, c as JsonValue, i as SceneJson, s as JsonObject } from "./schema-B6ugCV1Q.js";
|
|
2
2
|
|
|
3
3
|
//#region src/core/signal.d.ts
|
|
4
4
|
/**
|
|
@@ -419,6 +419,12 @@ interface MusicTrack {
|
|
|
419
419
|
play(): void;
|
|
420
420
|
/** Stop and release the underlying element/source. */
|
|
421
421
|
stop(): void;
|
|
422
|
+
/**
|
|
423
|
+
* Seconds into the track, or `null` when the backend cannot say.
|
|
424
|
+
*
|
|
425
|
+
* Optional so an existing custom backend keeps compiling.
|
|
426
|
+
*/
|
|
427
|
+
playhead?(): number | null;
|
|
422
428
|
}
|
|
423
429
|
/** Backend that mints {@link MusicTrack}s — WebAudio-backed in the browser. */
|
|
424
430
|
interface MusicBackend {
|
|
@@ -470,6 +476,22 @@ declare class MusicManager {
|
|
|
470
476
|
constructor(buses: AudioBuses);
|
|
471
477
|
/** Source of the logically-current track, or null when nothing is playing. */
|
|
472
478
|
get current(): string | null;
|
|
479
|
+
/**
|
|
480
|
+
* Seconds into the current track — the playhead of the thing you can HEAR.
|
|
481
|
+
*
|
|
482
|
+
* `null` when nothing is playing, or when the backend has no clock (headless,
|
|
483
|
+
* or a custom backend that does not implement it).
|
|
484
|
+
*
|
|
485
|
+
* A game charted to a soundtrack needs this and there was no way to get it:
|
|
486
|
+
* `current` gave the src and the backend was private, so the only option was
|
|
487
|
+
* an `engine.time` counter started next to `music.play()` — a different clock
|
|
488
|
+
* from the one the audio is on, and one that keeps counting through a
|
|
489
|
+
* gesture-block, a stall or a seek. This is the audio's own answer.
|
|
490
|
+
*
|
|
491
|
+
* It is the RAW element time, so a looping track wraps to 0 at each pass:
|
|
492
|
+
* that is a loop boundary you can chart against, not a fault.
|
|
493
|
+
*/
|
|
494
|
+
get playhead(): number | null;
|
|
473
495
|
/** Per-manager volume (0..1), on top of the bus gain. */
|
|
474
496
|
get volume(): number;
|
|
475
497
|
/**
|
|
@@ -694,6 +716,23 @@ interface SfxPlayOptions {
|
|
|
694
716
|
seed?: number;
|
|
695
717
|
/** When set, the sound is spatialized through a PannerNode at this emitter. */
|
|
696
718
|
spatial?: SpatialPlay;
|
|
719
|
+
/**
|
|
720
|
+
* Start at this point on the AUDIO clock (`engine.sfx.now` + lead), instead
|
|
721
|
+
* of "as soon as this call returns".
|
|
722
|
+
*
|
|
723
|
+
* Nothing in the audio API took a time, so a sound could only be fired from a
|
|
724
|
+
* frame — and a frame is a 16.67 ms grid at 60 Hz, 33.33 ms at 30. Measured
|
|
725
|
+
* on a metronome: firing from `update()` gave 0 of 64 notes a scheduled
|
|
726
|
+
* start; the same notes queued one frame ahead with `when` land at
|
|
727
|
+
* |mean| 0.000000 ms. That difference is the whole of a rhythm game, and
|
|
728
|
+
* anything charted to a soundtrack.
|
|
729
|
+
*
|
|
730
|
+
* A time already past plays immediately (Web Audio's own rule), so a late
|
|
731
|
+
* scheduler degrades instead of going silent. The bus gain and the
|
|
732
|
+
* `engine.audio.recent()` record apply either way — which is what a
|
|
733
|
+
* hand-rolled `AudioContext` scheduler loses.
|
|
734
|
+
*/
|
|
735
|
+
when?: number;
|
|
697
736
|
}
|
|
698
737
|
/**
|
|
699
738
|
* One AudioContext + a PCM cache. Instance-scoped (held by the renderer's game
|
|
@@ -741,6 +780,14 @@ declare class SfxEngine {
|
|
|
741
780
|
/** Resume a gesture-suspended context (wired to the first user gesture). */
|
|
742
781
|
unlock(): void;
|
|
743
782
|
/**
|
|
783
|
+
* The AUDIO clock, in seconds — the one a sound is actually scheduled
|
|
784
|
+
* against, and the only clock that does not drift relative to what you hear.
|
|
785
|
+
*
|
|
786
|
+
* `0` headless and before the first sound, since there is no context yet.
|
|
787
|
+
* Schedule with a lead: `sfx.play(params, 1, { when: sfx.now + 0.08 })`.
|
|
788
|
+
*/
|
|
789
|
+
get now(): number;
|
|
790
|
+
/**
|
|
744
791
|
* Play a preset at `gain` (0..1, already bus-multiplied by the caller). Each
|
|
745
792
|
* call spawns its own source so rapid repeats overlap instead of cutting off.
|
|
746
793
|
* Headless → no-op.
|
|
@@ -1182,6 +1229,25 @@ interface SaveStore {
|
|
|
1182
1229
|
remove(key: string): void;
|
|
1183
1230
|
/** Wipe THIS namespace only. */
|
|
1184
1231
|
clear(): void;
|
|
1232
|
+
/**
|
|
1233
|
+
* Does a write here survive the page closing?
|
|
1234
|
+
*
|
|
1235
|
+
* `false` means this store is a Map that lives as long as the tab does — a
|
|
1236
|
+
* private window, storage disabled, quota exhausted, or headless. Everything
|
|
1237
|
+
* still works IN the session, which is exactly what made it undetectable:
|
|
1238
|
+
* measured in Safari-private conditions, `set('highScore', 4200)` then
|
|
1239
|
+
* `get('highScore', 0)` returned 4200, zero warnings, and the interface had
|
|
1240
|
+
* no way to ask. On reload the score was 0, the slot list empty, and
|
|
1241
|
+
* `SavePoint` had emitted `saved(1)` for a write that went nowhere.
|
|
1242
|
+
*
|
|
1243
|
+
* Read it once and tell the player their progress will not be kept.
|
|
1244
|
+
*
|
|
1245
|
+
* Optional so a game that injects its OWN store (a cloud backend, a test
|
|
1246
|
+
* double) keeps compiling; absent counts as persistent, which is what a store
|
|
1247
|
+
* somebody wrote on purpose almost always is. Every store the engine makes
|
|
1248
|
+
* says so explicitly.
|
|
1249
|
+
*/
|
|
1250
|
+
readonly persistent?: boolean;
|
|
1185
1251
|
}
|
|
1186
1252
|
declare function createSaveStore(namespace: string): SaveStore;
|
|
1187
1253
|
//#endregion
|
|
@@ -1261,6 +1327,15 @@ declare function restoreBehaviors(root: Node, state: BehaviorState): RestoreRepo
|
|
|
1261
1327
|
declare class SaveSlots {
|
|
1262
1328
|
private readonly store;
|
|
1263
1329
|
constructor(namespace?: string, store?: SaveStore);
|
|
1330
|
+
/**
|
|
1331
|
+
* Will these slots survive the page closing?
|
|
1332
|
+
*
|
|
1333
|
+
* `false` in a private window, with storage disabled, or headless — every
|
|
1334
|
+
* write still succeeds and every read still answers, for as long as the tab
|
|
1335
|
+
* lives. A load menu should say so instead of offering a slot that will not
|
|
1336
|
+
* be there tomorrow.
|
|
1337
|
+
*/
|
|
1338
|
+
get persistent(): boolean;
|
|
1264
1339
|
/** Slot ids that have a save, newest first. */
|
|
1265
1340
|
list(): string[];
|
|
1266
1341
|
/** Every slot with its contents — what a load menu actually renders. */
|
|
@@ -1331,6 +1406,8 @@ declare class Scene {
|
|
|
1331
1406
|
readonly orderGroups: Record<string, number> | undefined;
|
|
1332
1407
|
/** Physics config (gravity etc. — interpreted by the physics modules). */
|
|
1333
1408
|
readonly physics: JsonObject | undefined;
|
|
1409
|
+
/** Declared piece of another scene — see `SceneJson.fragment`. */
|
|
1410
|
+
readonly fragment: boolean | undefined;
|
|
1334
1411
|
/** Multiplayer config (room etc. — interpreted by incanto/net). */
|
|
1335
1412
|
readonly multiplayer: JsonObject | undefined;
|
|
1336
1413
|
/** Design-resolution viewport (consumed by renderers via resolveViewport). */
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
2
|
+
//#region src/core/boot-failure.ts
|
|
3
|
+
/**
|
|
4
|
+
* Put a boot failure on the SCREEN, where the player is looking.
|
|
5
|
+
*
|
|
6
|
+
* Every shipped starter preloads its assets behind a `#loading` overlay and
|
|
7
|
+
* removes that overlay on the line after `await createGame*`. So anything that
|
|
8
|
+
* rejects — no WebGL context, a scene that will not load, a 404 the preload
|
|
9
|
+
* insisted on — leaves the overlay standing at "100%" forever, with the reason
|
|
10
|
+
* in a console the player will never open. Measured on a pristine scaffold with
|
|
11
|
+
* WebGL denied:
|
|
12
|
+
*
|
|
13
|
+
* ```
|
|
14
|
+
* control #loading gone "Talk to the Elder [E] …"
|
|
15
|
+
* no webgl #loading present "Emberwood 100%" … and still there 8.6 s later
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* A bar that reaches 100% and stops reads as a hang, which is the one failure
|
|
19
|
+
* mode a player cannot report usefully. This turns it into a sentence.
|
|
20
|
+
*
|
|
21
|
+
* Headless it does nothing but rethrow-safe logging, so a test that boots a
|
|
22
|
+
* game without a document is unaffected.
|
|
23
|
+
*/
|
|
24
|
+
function showBootFailure(error, opts = {}) {
|
|
25
|
+
const message = error instanceof IncantoError ? error.message : String(error?.message ?? error);
|
|
26
|
+
const code = error instanceof IncantoError ? error.code : null;
|
|
27
|
+
console.error(`[incanto] the game could not start${code ? ` (${code})` : ""}: ${message}`);
|
|
28
|
+
const doc = globalThis.document;
|
|
29
|
+
if (!doc?.body) return;
|
|
30
|
+
const host = opts.into ? doc.querySelector(opts.into) : doc.querySelector("#loading");
|
|
31
|
+
const panel = host ?? doc.createElement("div");
|
|
32
|
+
if (!host) {
|
|
33
|
+
panel.style.cssText = "position:fixed;inset:0;display:grid;place-content:center;gap:12px;padding:32px;background:#10131a;color:#e6e9ef;z-index:2147483647;font:14px/1.6 system-ui,sans-serif;text-align:center";
|
|
34
|
+
doc.body.appendChild(panel);
|
|
35
|
+
}
|
|
36
|
+
panel.textContent = "";
|
|
37
|
+
const title = doc.createElement("div");
|
|
38
|
+
title.textContent = "This game could not start";
|
|
39
|
+
title.style.cssText = "font-size:18px;font-weight:600;margin-bottom:8px";
|
|
40
|
+
const detail = doc.createElement("div");
|
|
41
|
+
detail.textContent = message;
|
|
42
|
+
detail.style.cssText = "opacity:.85;max-width:52ch;margin:0 auto";
|
|
43
|
+
panel.appendChild(title);
|
|
44
|
+
panel.appendChild(detail);
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { showBootFailure as t };
|