incanto 0.54.0 → 0.56.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/incanto-verify.mjs +46 -0
- package/dist/2d.js +1 -1
- package/dist/3d.js +4 -4
- package/dist/{create-game-Czzp6ZuE.js → create-game-CqJkfMzm.js} +3 -3
- package/dist/{create-game-BRt6XKmP.js → create-game-YvaNWhe6.js} +1 -1
- package/dist/debug.js +3 -1
- package/dist/editor.js +7 -0
- package/dist/{environment-presets-BQ_QsIBY.js → environment-presets-DLHqgNAF.js} +117 -1
- package/dist/{gameplay-CZ2yq37J.js → gameplay-C_SPzmUe.js} +75 -2
- package/dist/gameplay.d.ts +55 -1
- package/dist/gameplay.js +2 -2
- package/dist/index.js +1 -1
- package/dist/{physics-3d-Bes-GIuR.js → physics-3d-NzDwWPrF.js} +2 -2
- package/dist/react.js +1 -1
- package/dist/{src-vNPQeQ1W.js → src-51GA-9m-.js} +1 -1
- package/dist/{test-Tnf1nQl3.js → test-Bu6SPNsV.js} +29 -6
- package/dist/test.d.ts +16 -0
- package/dist/test.js +1 -1
- package/dist/vite.js +2 -2
- package/editor/assets/{agent8-BWaW-D85.js → agent8-X0Nesj-k.js} +1 -1
- package/editor/assets/{debug-DLF8rUtr.js → debug-TXqu-ftX.js} +2 -2
- package/editor/assets/{index-BYfiwbQx.js → index-DTdwKKIv.js} +51 -51
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/schemas/scene.schema.json +177 -0
- package/skills/incanto-building-3d-games.md +38 -2
- package/skills/incanto-gameplay-behaviors.md +19 -0
- package/skills/incanto-node-reference.md +29 -0
- package/skills/incanto-performance.md +4 -0
- package/skills/incanto-verifying-your-game.md +7 -0
- package/templates-app/beacon-isle-3d/package.json +1 -1
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/village-quest-3d/package.json +1 -1
package/bin/incanto-verify.mjs
CHANGED
|
@@ -122,12 +122,16 @@ const rungs = [];
|
|
|
122
122
|
);
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
/** The playtest's JSON, shared by the `plays` and `feels` rungs. */
|
|
126
|
+
let playtestReport = null;
|
|
127
|
+
|
|
125
128
|
// ---- plays ---------------------------------------------------------------
|
|
126
129
|
if (rungs[0].status === 'pass') {
|
|
127
130
|
const args = [scene, '--json', '--runs', '8'];
|
|
128
131
|
if (behaviors) args.push('--behaviors', behaviors);
|
|
129
132
|
const r = run('playtest', args);
|
|
130
133
|
const out = safeJson(r.stdout);
|
|
134
|
+
playtestReport = out;
|
|
131
135
|
const won = out?.runs?.filter((x) => x.outcome === 'won').length ?? 0;
|
|
132
136
|
const total = out?.runs?.length ?? 0;
|
|
133
137
|
// A scene that declares no win is not a scene that cannot be won: a
|
|
@@ -158,6 +162,48 @@ if (rungs[0].status === 'pass') {
|
|
|
158
162
|
rungs.push({ name: 'plays', status: 'skipped', summary: 'not run — the scene does not load' });
|
|
159
163
|
}
|
|
160
164
|
|
|
165
|
+
// ---- feels ---------------------------------------------------------------
|
|
166
|
+
// Sound and effects, from the same playtest that just ran. A game whose
|
|
167
|
+
// feedback is wired but never triggered plays perfectly and feels dead, and
|
|
168
|
+
// every other rung here calls it healthy.
|
|
169
|
+
{
|
|
170
|
+
const fb = playtestReport?.feedback;
|
|
171
|
+
const declared = (fb?.declaredAudio?.length ?? 0) + (fb?.declaredEffects?.length ?? 0);
|
|
172
|
+
const fired = new Set([...(fb?.heard ?? []), ...(fb?.shown ?? [])]);
|
|
173
|
+
if (!fb) {
|
|
174
|
+
rungs.push({
|
|
175
|
+
name: 'feels',
|
|
176
|
+
status: 'skipped',
|
|
177
|
+
summary: 'not run — the playtest did not report',
|
|
178
|
+
});
|
|
179
|
+
} else if (declared === 0) {
|
|
180
|
+
rungs.push({
|
|
181
|
+
name: 'feels',
|
|
182
|
+
status: 'skipped',
|
|
183
|
+
summary: 'this game declares no sound and no effects',
|
|
184
|
+
});
|
|
185
|
+
} else if (fired.size === 0) {
|
|
186
|
+
rungs.push({
|
|
187
|
+
name: 'feels',
|
|
188
|
+
status: 'fail',
|
|
189
|
+
summary: `${declared} sound/effect node(s) declared, and not one fired in any run`,
|
|
190
|
+
fix: 'connect them — a signal to `play` on an AudioPlayer, or `replay()` on a one-shot; `engine.audio.recent()` and `engine.effects.recent()` say what did fire',
|
|
191
|
+
});
|
|
192
|
+
} else {
|
|
193
|
+
const silent = [...(fb.declaredAudio ?? []), ...(fb.declaredEffects ?? [])].filter(
|
|
194
|
+
(path) => !fired.has(path),
|
|
195
|
+
);
|
|
196
|
+
rungs.push({
|
|
197
|
+
name: 'feels',
|
|
198
|
+
status: 'pass',
|
|
199
|
+
summary:
|
|
200
|
+
silent.length === 0
|
|
201
|
+
? `every one of the ${declared} sound/effect nodes fired`
|
|
202
|
+
: `${fired.size} of ${declared} fired — silent: ${silent.slice(0, 3).join(', ')}${silent.length > 3 ? ` +${silent.length - 3}` : ''}`,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
161
207
|
// ---- draws ---------------------------------------------------------------
|
|
162
208
|
{
|
|
163
209
|
const r = run('frame', ['--json']);
|
package/dist/2d.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
2
|
-
import { a as AssetStore2D, i as syncTree2D, r as Renderer2D, t as createGame2D } from "./create-game-
|
|
2
|
+
import { a as AssetStore2D, i as syncTree2D, r as Renderer2D, t as createGame2D } from "./create-game-YvaNWhe6.js";
|
|
3
3
|
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-en63AEZO.js";
|
|
4
4
|
import { n as enablePhysics2D, t as Physics2D } from "./physics-2d-CfWAggJ1.js";
|
|
5
5
|
//#region src/2d/library-sprite.ts
|
package/dist/3d.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
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-
|
|
3
|
-
import { a as Environment3D, c as parseEnvironment3D, d as AssetStore3D, i as syncTree, l as sunDirectionFromElevationAzimuth, o as setEnvironment3D, r as Renderer3D, s as horizonColorFromSky, t as createGame3D, u as sunDirectionFromSky } from "./create-game-
|
|
1
|
+
import { B as Node3D, F as Area3D, I as CharacterBody3D, L as PhysicsBody3D, M as Water3D, N as WATER_CUTOUT_MAX, P as WaterCutout3D, R as RigidBody3D, U as WATER_MAX_RIPPLES, z as StaticBody3D } from "./gameplay-C_SPzmUe.js";
|
|
2
|
+
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-DLHqgNAF.js";
|
|
3
|
+
import { a as Environment3D, c as parseEnvironment3D, d as AssetStore3D, i as syncTree, l as sunDirectionFromElevationAzimuth, o as setEnvironment3D, r as Renderer3D, s as horizonColorFromSky, t as createGame3D, u as sunDirectionFromSky } from "./create-game-CqJkfMzm.js";
|
|
4
4
|
import { a as frameSignature, n as diffSignatures, o as frameStats, r as diffText, s as frameText, t as SIGNATURE_GRID } from "./frame-report-njybhZon.js";
|
|
5
5
|
import { n as splatWeights, t as buildHeightmap } from "./heightmap-CRK0M4jT.js";
|
|
6
|
-
import { n as enablePhysics3D, t as Physics3D } from "./physics-3d-
|
|
6
|
+
import { n as enablePhysics3D, t as Physics3D } from "./physics-3d-NzDwWPrF.js";
|
|
7
7
|
//#region src/3d/model-verdict.ts
|
|
8
8
|
/** Mixamo exports every bone as `mixamorigX`; the retargeter binds by that name. */
|
|
9
9
|
const MIXAMO = /^mixamorig[:_]?/i;
|
|
@@ -5,11 +5,11 @@ import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
|
5
5
|
import { r as parseDrive, t as logReport } from "./log-report-CPFm4OXf.js";
|
|
6
6
|
import { i as resolveRendering, n as attachTouchControls } from "./touch-BnMyy9tr.js";
|
|
7
7
|
import { a as openBundledEditor, c as audioErrors, i as devServerLibrary, n as pauseWhenHidden, o as poseFromRenderer, r as crossFade, s as claimCanvasGestures, t as teardown } from "./teardown-C7uVSJvx.js";
|
|
8
|
-
import {
|
|
8
|
+
import { B as Node3D, H as createCausticsQuad, L as PhysicsBody3D, n as registerGameplayBehaviors } from "./gameplay-C_SPzmUe.js";
|
|
9
9
|
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-
|
|
10
|
+
import { E as Camera3D, U as TextureCache3D, g as ModelInstance3D, n as registerNodes3D, t as resolveEnvironmentHdri, v as DirectionalLight3D } from "./environment-presets-DLHqgNAF.js";
|
|
11
11
|
import { a as frameSignature, i as frameImage, o as frameStats } from "./frame-report-njybhZon.js";
|
|
12
|
-
import { n as enablePhysics3D } from "./physics-3d-
|
|
12
|
+
import { n as enablePhysics3D } from "./physics-3d-NzDwWPrF.js";
|
|
13
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";
|
|
14
14
|
import { VRMLoaderPlugin, VRMUtils } from "@pixiv/three-vrm";
|
|
15
15
|
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
|
|
@@ -4,7 +4,7 @@ import { b as AudioPlayer, x as Engine } from "./register-p48lHE2o.js";
|
|
|
4
4
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
5
5
|
import { i as resolveRendering, n as attachTouchControls } from "./touch-BnMyy9tr.js";
|
|
6
6
|
import { a as openBundledEditor, c as audioErrors, i as devServerLibrary, n as pauseWhenHidden, r as crossFade, s as claimCanvasGestures, t as teardown } from "./teardown-C7uVSJvx.js";
|
|
7
|
-
import { n as registerGameplayBehaviors } from "./gameplay-
|
|
7
|
+
import { n as registerGameplayBehaviors } from "./gameplay-C_SPzmUe.js";
|
|
8
8
|
import { g as PhysicsBody2D, n as UILayer, t as registerNodes2D, u as Camera2D, y as Node2D } from "./register-en63AEZO.js";
|
|
9
9
|
import { t as debugSources } from "./debug-draw-BM3DsvtT.js";
|
|
10
10
|
import { n as enablePhysics2D } from "./physics-2d-CfWAggJ1.js";
|
package/dist/debug.js
CHANGED
|
@@ -1164,7 +1164,9 @@ var DebugOverlay = class {
|
|
|
1164
1164
|
...gpu.triangles !== void 0 ? [`tris ${compactCount(gpu.triangles)}`] : [],
|
|
1165
1165
|
...gpu.drawCalls !== void 0 ? [`calls ${gpu.drawCalls}`] : []
|
|
1166
1166
|
].join(" · ");
|
|
1167
|
-
|
|
1167
|
+
const p = stats.phases;
|
|
1168
|
+
const line3 = `\nphys ${p.fixedMs.toFixed(1)} · logic ${p.updateMs.toFixed(1)} · draw ${p.renderMs.toFixed(1)} · other ${p.otherMs.toFixed(1)}`;
|
|
1169
|
+
chip.textContent = `${Math.round(stats.fps)} fps · ${stats.frameMs.toFixed(1)} ms\n${line2}${line3}`;
|
|
1168
1170
|
}
|
|
1169
1171
|
renderExplorer() {
|
|
1170
1172
|
const panel = this.panels.get("explorer");
|
package/dist/editor.js
CHANGED
|
@@ -1181,6 +1181,13 @@ var kt = {
|
|
|
1181
1181
|
en: "Options are the locales the scene declares in \"strings\", each labeled with its own endonym. Picking one switches the game live and persists it. English is the base: any key a locale omits falls back to it, on purpose.",
|
|
1182
1182
|
ko: "옵션은 씬의 \"strings\"가 선언한 로케일이며, 각 언어의 자기 이름으로 표시됩니다. 선택하면 즉시 바뀌고 저장됩니다. 영어가 기준이라 로케일이 빠뜨린 키는 의도적으로 영어로 표시됩니다."
|
|
1183
1183
|
}),
|
|
1184
|
+
L("Label3D", {
|
|
1185
|
+
en: "Text standing in the world — a nameplate, a sign, a damage number.",
|
|
1186
|
+
ko: "월드에 서 있는 텍스트 — 이름표, 표지판, 데미지 숫자."
|
|
1187
|
+
}, {
|
|
1188
|
+
en: "height is the cap height in METRES and the quad follows what the text measures, so a sign is as wide as its words. Faces the camera by default (a damage number read from behind is a bug) and carries a dark outline so it stays readable over anything. Everything else a sprite knows — anchor, tint, opacity, renderOrder — it inherits. For a damage number: set text, then float it up and fade opacity to 0 and queueFree().",
|
|
1189
|
+
ko: "height는 글자 높이(미터)이고 가로 폭은 텍스트 실측을 따라갑니다. 기본으로 카메라를 향하며(뒤에서 읽히는 데미지 숫자는 버그니까요) 어두운 외곽선이 있어 어떤 배경에서도 읽힙니다. 앵커·틴트·투명도·renderOrder 등 스프라이트의 나머지 기능은 그대로 상속합니다. 데미지 숫자는 text를 넣고 위로 띄우며 opacity를 0으로 보낸 뒤 queueFree() 하면 됩니다."
|
|
1190
|
+
}),
|
|
1184
1191
|
L("UiVolumeSlider", {
|
|
1185
1192
|
en: "A volume slider, already wired to a bus.",
|
|
1186
1193
|
ko: "볼륨 슬라이더 — 버스에 이미 연결되어 있습니다."
|
|
@@ -4,7 +4,7 @@ import { t as Rng } from "./rng-DP-SR7eg.js";
|
|
|
4
4
|
import { i as getNodeSchema, l as registerNode } from "./registry-C7u42TID.js";
|
|
5
5
|
import { t as createNoise2D } from "./noise-CGUMx44x.js";
|
|
6
6
|
import { i as ParticleSim, n as resolveFrames, o as PARTICLE_PRESET_NAMES, r as validateAnimationAliases, s as applyParticlePreset, t as resolveAnimation } from "./sprite-animation-7qvUxF6Z.js";
|
|
7
|
-
import { F as
|
|
7
|
+
import { B as Node3D, F as Area3D, I as CharacterBody3D, L as PhysicsBody3D, M as Water3D, P as WaterCutout3D, R as RigidBody3D, W as colliderFootDrop, z as StaticBody3D } from "./gameplay-C_SPzmUe.js";
|
|
8
8
|
import { n as splatWeights, t as buildHeightmap } from "./heightmap-CRK0M4jT.js";
|
|
9
9
|
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";
|
|
10
10
|
import { clone } from "three/addons/utils/SkeletonUtils.js";
|
|
@@ -5685,6 +5685,121 @@ const colorScratchB$1 = new Color();
|
|
|
5685
5685
|
const colorScratchC$1 = new Color();
|
|
5686
5686
|
const tuftDryScratch = new Color();
|
|
5687
5687
|
//#endregion
|
|
5688
|
+
//#region src/3d/nodes/label-3d.ts
|
|
5689
|
+
/**
|
|
5690
|
+
* Text standing in the world — a damage number, a nameplate, a sign.
|
|
5691
|
+
*
|
|
5692
|
+
* Every action game has these, and the answer used to be a sentence in the 3D
|
|
5693
|
+
* skill: *"There is no 3D text node — a nameplate or damage number is a
|
|
5694
|
+
* canvas-rendered texture on a child Sprite3D."* True, and a real afternoon of
|
|
5695
|
+
* work per game: measure the text, size the canvas, keep the device pixel ratio
|
|
5696
|
+
* honest, build the texture, work out the quad's aspect, re-rasterise when the
|
|
5697
|
+
* number changes, and dispose the old texture so a damage number does not leak
|
|
5698
|
+
* one per hit.
|
|
5699
|
+
*
|
|
5700
|
+
* It IS a canvas-rendered texture on a Sprite3D — which is exactly why it
|
|
5701
|
+
* should be a node. Everything a sprite already knows (billboarding, anchor,
|
|
5702
|
+
* tint, opacity, depth, render order) it inherits, so this only adds the text.
|
|
5703
|
+
*
|
|
5704
|
+
* ```json
|
|
5705
|
+
* { "name": "Name", "type": "Label3D",
|
|
5706
|
+
* "props": { "text": "Goblin", "height": 0.25, "position": [0, 2, 0] } }
|
|
5707
|
+
* ```
|
|
5708
|
+
*
|
|
5709
|
+
* A damage number is this plus two lines of behavior: set `text`, then move it
|
|
5710
|
+
* up and fade `opacity` to 0 over ~0.6 s and `queueFree()`.
|
|
5711
|
+
*/
|
|
5712
|
+
var Label3D = class extends Sprite3D {
|
|
5713
|
+
static typeName = "Label3D";
|
|
5714
|
+
static props = {
|
|
5715
|
+
...Sprite3D.props,
|
|
5716
|
+
text: { default: "" },
|
|
5717
|
+
/** Cap height of the text in METRES — the one number a sign is sized by. */
|
|
5718
|
+
height: { default: .3 },
|
|
5719
|
+
color: { default: "#ffffff" },
|
|
5720
|
+
font: { default: "sans-serif" },
|
|
5721
|
+
/** Outline colour; `""` turns it off. On by default: white text over a
|
|
5722
|
+
* bright wall is unreadable, and a damage number lands anywhere. */
|
|
5723
|
+
outline: { default: "#000000" },
|
|
5724
|
+
/** A damage number read from behind is a bug, so these face the camera. */
|
|
5725
|
+
billboard: {
|
|
5726
|
+
default: "full",
|
|
5727
|
+
options: [
|
|
5728
|
+
"y",
|
|
5729
|
+
"full",
|
|
5730
|
+
"none"
|
|
5731
|
+
]
|
|
5732
|
+
}
|
|
5733
|
+
};
|
|
5734
|
+
text = "";
|
|
5735
|
+
height = .3;
|
|
5736
|
+
color = "#ffffff";
|
|
5737
|
+
font = "sans-serif";
|
|
5738
|
+
outline = "#000000";
|
|
5739
|
+
billboard = "full";
|
|
5740
|
+
/**
|
|
5741
|
+
* A `texture` on a label is a mistake, and a loud one.
|
|
5742
|
+
*
|
|
5743
|
+
* The prop is inherited from `Sprite3D` and cannot be un-inherited (schemas
|
|
5744
|
+
* merge up the chain), so a URL set here would be silently ignored — this
|
|
5745
|
+
* node draws its own text. Silent is the failure mode these rounds keep
|
|
5746
|
+
* finding; naming it costs one check at load.
|
|
5747
|
+
*/
|
|
5748
|
+
static validateJson(node) {
|
|
5749
|
+
const label = node;
|
|
5750
|
+
if (label.texture) throw new IncantoError("BAD_FORMAT", `Label3D '${label.name}' has a "texture" — a label draws its own text, so the image would never be used. Set "text" instead, or use a Sprite3D.`, { prop: "texture" });
|
|
5751
|
+
}
|
|
5752
|
+
canvas = null;
|
|
5753
|
+
/** The rasterised text. Named apart from Sprite3D's `texture` URL prop, which
|
|
5754
|
+
* this node ignores — the text IS the picture. */
|
|
5755
|
+
canvasTexture = null;
|
|
5756
|
+
lastKey = "";
|
|
5757
|
+
resolveTexture(_assets) {
|
|
5758
|
+
if (this.text === "" || typeof document === "undefined") return null;
|
|
5759
|
+
const key = `${this.text}\0${this.height}\0${this.color}\0${this.font}\0${this.outline}`;
|
|
5760
|
+
if (key !== this.lastKey) {
|
|
5761
|
+
this.lastKey = key;
|
|
5762
|
+
this.rasterize();
|
|
5763
|
+
}
|
|
5764
|
+
return this.canvasTexture;
|
|
5765
|
+
}
|
|
5766
|
+
/** Draw the text once, and size the quad to what it measured. */
|
|
5767
|
+
rasterize() {
|
|
5768
|
+
const fontPx = Math.max(8, Math.round(this.height * 256));
|
|
5769
|
+
this.canvas ??= document.createElement("canvas");
|
|
5770
|
+
const ctx = this.canvas.getContext("2d");
|
|
5771
|
+
if (!ctx) return;
|
|
5772
|
+
const stroke = Math.max(1, Math.round(fontPx * .14));
|
|
5773
|
+
const fontSpec = `bold ${fontPx}px ${this.font}`;
|
|
5774
|
+
ctx.font = fontSpec;
|
|
5775
|
+
const width = Math.max(1, Math.ceil(ctx.measureText(this.text).width) + stroke * 2);
|
|
5776
|
+
const lineHeight = Math.ceil(fontPx * 1.35) + stroke * 2;
|
|
5777
|
+
this.canvas.width = width;
|
|
5778
|
+
this.canvas.height = lineHeight;
|
|
5779
|
+
ctx.font = fontSpec;
|
|
5780
|
+
ctx.textBaseline = "middle";
|
|
5781
|
+
ctx.textAlign = "center";
|
|
5782
|
+
if (this.outline !== "") {
|
|
5783
|
+
ctx.lineWidth = stroke;
|
|
5784
|
+
ctx.lineJoin = "round";
|
|
5785
|
+
ctx.strokeStyle = this.outline;
|
|
5786
|
+
ctx.strokeText(this.text, width / 2, lineHeight / 2);
|
|
5787
|
+
}
|
|
5788
|
+
ctx.fillStyle = this.color;
|
|
5789
|
+
ctx.fillText(this.text, width / 2, lineHeight / 2);
|
|
5790
|
+
this.canvasTexture?.dispose();
|
|
5791
|
+
const texture = new CanvasTexture(this.canvas);
|
|
5792
|
+
texture.colorSpace = SRGBColorSpace;
|
|
5793
|
+
this.canvasTexture = texture;
|
|
5794
|
+
this.size = [width / lineHeight * this.height * 1.35, this.height * 1.35];
|
|
5795
|
+
}
|
|
5796
|
+
free() {
|
|
5797
|
+
this.canvasTexture?.dispose();
|
|
5798
|
+
this.canvasTexture = null;
|
|
5799
|
+
super.free();
|
|
5800
|
+
}
|
|
5801
|
+
};
|
|
5802
|
+
//#endregion
|
|
5688
5803
|
//#region src/3d/nodes/lights-3d.ts
|
|
5689
5804
|
/** Sun-style light. Direction comes from the node's rotation. */
|
|
5690
5805
|
var DirectionalLight3D = class extends Node3D {
|
|
@@ -12436,6 +12551,7 @@ function registerNodes3D() {
|
|
|
12436
12551
|
registerNode(MeshInstance3D);
|
|
12437
12552
|
registerNode(LoftMesh3D);
|
|
12438
12553
|
registerNode(Sprite3D);
|
|
12554
|
+
registerNode(Label3D);
|
|
12439
12555
|
registerNode(AnimatedSprite3D);
|
|
12440
12556
|
registerNode(Billboard3D);
|
|
12441
12557
|
registerNode(CharacterController3D);
|
|
@@ -4891,6 +4891,78 @@ function phaseOf(hour) {
|
|
|
4891
4891
|
return "dusk";
|
|
4892
4892
|
}
|
|
4893
4893
|
//#endregion
|
|
4894
|
+
//#region src/gameplay/float-away.ts
|
|
4895
|
+
/**
|
|
4896
|
+
* Rise, fade, and be gone — the second half of a damage number.
|
|
4897
|
+
*
|
|
4898
|
+
* `Label3D` gave a game text standing in the world; every game then writes the
|
|
4899
|
+
* same eight lines to move it up and fade it out, and the interesting part is
|
|
4900
|
+
* not the eight lines, it is the three things they all get wrong: the node is
|
|
4901
|
+
* freed on the frame it becomes invisible rather than a second later, the fade
|
|
4902
|
+
* fights whatever else writes `opacity`, and a hundred numbers a fight each
|
|
4903
|
+
* leave a node behind because nothing ever frees them.
|
|
4904
|
+
*
|
|
4905
|
+
* ```json
|
|
4906
|
+
* { "name": "Hit", "type": "Label3D",
|
|
4907
|
+
* "props": { "text": "12", "height": 0.35, "color": "#ff5a5a" },
|
|
4908
|
+
* "script": { "name": "FloatAway", "props": { "rise": 1.2, "seconds": 0.7 } } }
|
|
4909
|
+
* ```
|
|
4910
|
+
*
|
|
4911
|
+
* Spawning one is then `duplicateNode` + set `text` + `position` — no per-frame
|
|
4912
|
+
* code of your own, and no cleanup to forget.
|
|
4913
|
+
*
|
|
4914
|
+
* It writes `opacity` and the node's own `position`, so it composes with
|
|
4915
|
+
* anything that does not. Works on any node with those props: a `Label3D`, a
|
|
4916
|
+
* `Sprite3D` pickup puff, a 2D `Label`.
|
|
4917
|
+
*/
|
|
4918
|
+
var FloatAway = class extends Behavior {
|
|
4919
|
+
static props = {
|
|
4920
|
+
/** World units travelled over the whole life (negative sinks). */
|
|
4921
|
+
rise: { default: 1 },
|
|
4922
|
+
/** How long the whole thing takes. */
|
|
4923
|
+
seconds: { default: .7 },
|
|
4924
|
+
/** Fraction of the life spent at full opacity before the fade starts. */
|
|
4925
|
+
hold: { default: .3 },
|
|
4926
|
+
/** Sideways drift, so ten numbers at once do not stack into one. */
|
|
4927
|
+
drift: { default: 0 },
|
|
4928
|
+
/** Free the node when it finishes. Off for something you re-use. */
|
|
4929
|
+
freeOnEnd: { default: true }
|
|
4930
|
+
};
|
|
4931
|
+
rise = 1;
|
|
4932
|
+
seconds = .7;
|
|
4933
|
+
hold = .3;
|
|
4934
|
+
drift = 0;
|
|
4935
|
+
freeOnEnd = true;
|
|
4936
|
+
elapsed = 0;
|
|
4937
|
+
start = [];
|
|
4938
|
+
done = false;
|
|
4939
|
+
onReady() {
|
|
4940
|
+
const node = this.node;
|
|
4941
|
+
this.start = [...node.position ?? []];
|
|
4942
|
+
}
|
|
4943
|
+
update(dt) {
|
|
4944
|
+
if (this.done) return;
|
|
4945
|
+
const node = this.node;
|
|
4946
|
+
if (!node.position) return;
|
|
4947
|
+
this.elapsed += dt;
|
|
4948
|
+
const life = Math.max(.001, this.seconds);
|
|
4949
|
+
const t = Math.min(1, this.elapsed / life);
|
|
4950
|
+
const next = [...this.start];
|
|
4951
|
+
const eased = 1 - (1 - t) * (1 - t);
|
|
4952
|
+
next[1] = (this.start[1] ?? 0) + this.rise * eased;
|
|
4953
|
+
if (this.drift !== 0) next[0] = (this.start[0] ?? 0) + this.drift * eased;
|
|
4954
|
+
node.position = next;
|
|
4955
|
+
if (node.opacity !== void 0) {
|
|
4956
|
+
const holdFor = Math.min(.99, Math.max(0, this.hold));
|
|
4957
|
+
node.opacity = t <= holdFor ? 1 : 1 - (t - holdFor) / (1 - holdFor);
|
|
4958
|
+
}
|
|
4959
|
+
if (t >= 1) {
|
|
4960
|
+
this.done = true;
|
|
4961
|
+
if (this.freeOnEnd) this.node.queueFree();
|
|
4962
|
+
}
|
|
4963
|
+
}
|
|
4964
|
+
};
|
|
4965
|
+
//#endregion
|
|
4894
4966
|
//#region src/gameplay/follow-camera.ts
|
|
4895
4967
|
/**
|
|
4896
4968
|
* Make the node it sits on chase a target's position — THE camera-follow
|
|
@@ -6636,7 +6708,8 @@ const GAMEPLAY_BEHAVIORS = {
|
|
|
6636
6708
|
Spawner,
|
|
6637
6709
|
WaveSpawner,
|
|
6638
6710
|
Projectile,
|
|
6639
|
-
Buoyancy
|
|
6711
|
+
Buoyancy,
|
|
6712
|
+
FloatAway
|
|
6640
6713
|
};
|
|
6641
6714
|
/**
|
|
6642
6715
|
* Register all built-in gameplay behaviors. Idempotent and hot-reload tolerant
|
|
@@ -6648,4 +6721,4 @@ function registerGameplayBehaviors(opts) {
|
|
|
6648
6721
|
for (const [name, ctor] of Object.entries(GAMEPLAY_BEHAVIORS)) registerBehavior(name, ctor, { replace });
|
|
6649
6722
|
}
|
|
6650
6723
|
//#endregion
|
|
6651
|
-
export {
|
|
6724
|
+
export { Collector as A, Node3D as B, restartScene as C, phaseOf as D, DayNight as E, Area3D as F, createCausticsQuad as H, CharacterBody3D as I, PhysicsBody3D as L, Water3D as M, WATER_CUTOUT_MAX as N, DamageOnContact as O, WaterCutout3D as P, RigidBody3D as R, goToScene as S, FloatAway as T, WATER_MAX_RIPPLES as U, validateCollider3D as V, colliderFootDrop as W, Cooldown as _, Wander as a, Interactable as b, SavePoint as c, Patrol as d, PathFollow as f, CameraShake as g, Lifetime as h, WaveSpawner as i, Chase as j, Health as k, Projectile as l, MoveTo as m, registerGameplayBehaviors as n, Spawner as o, Oscillate as p, ZombieAI as r, ScoreKeeper as s, GAMEPLAY_BEHAVIORS as t, Pickup as u, hitStop as v, FollowCamera as w, GameFlow as x, screenFlash as y, StaticBody3D as z };
|
package/dist/gameplay.d.ts
CHANGED
|
@@ -678,6 +678,60 @@ declare class DayNight extends Behavior {
|
|
|
678
678
|
}
|
|
679
679
|
declare function phaseOf(hour: number): DayPhase;
|
|
680
680
|
//#endregion
|
|
681
|
+
//#region src/gameplay/float-away.d.ts
|
|
682
|
+
/**
|
|
683
|
+
* Rise, fade, and be gone — the second half of a damage number.
|
|
684
|
+
*
|
|
685
|
+
* `Label3D` gave a game text standing in the world; every game then writes the
|
|
686
|
+
* same eight lines to move it up and fade it out, and the interesting part is
|
|
687
|
+
* not the eight lines, it is the three things they all get wrong: the node is
|
|
688
|
+
* freed on the frame it becomes invisible rather than a second later, the fade
|
|
689
|
+
* fights whatever else writes `opacity`, and a hundred numbers a fight each
|
|
690
|
+
* leave a node behind because nothing ever frees them.
|
|
691
|
+
*
|
|
692
|
+
* ```json
|
|
693
|
+
* { "name": "Hit", "type": "Label3D",
|
|
694
|
+
* "props": { "text": "12", "height": 0.35, "color": "#ff5a5a" },
|
|
695
|
+
* "script": { "name": "FloatAway", "props": { "rise": 1.2, "seconds": 0.7 } } }
|
|
696
|
+
* ```
|
|
697
|
+
*
|
|
698
|
+
* Spawning one is then `duplicateNode` + set `text` + `position` — no per-frame
|
|
699
|
+
* code of your own, and no cleanup to forget.
|
|
700
|
+
*
|
|
701
|
+
* It writes `opacity` and the node's own `position`, so it composes with
|
|
702
|
+
* anything that does not. Works on any node with those props: a `Label3D`, a
|
|
703
|
+
* `Sprite3D` pickup puff, a 2D `Label`.
|
|
704
|
+
*/
|
|
705
|
+
declare class FloatAway extends Behavior {
|
|
706
|
+
static override readonly props: {
|
|
707
|
+
/** World units travelled over the whole life (negative sinks). */rise: {
|
|
708
|
+
default: number;
|
|
709
|
+
}; /** How long the whole thing takes. */
|
|
710
|
+
seconds: {
|
|
711
|
+
default: number;
|
|
712
|
+
}; /** Fraction of the life spent at full opacity before the fade starts. */
|
|
713
|
+
hold: {
|
|
714
|
+
default: number;
|
|
715
|
+
}; /** Sideways drift, so ten numbers at once do not stack into one. */
|
|
716
|
+
drift: {
|
|
717
|
+
default: number;
|
|
718
|
+
}; /** Free the node when it finishes. Off for something you re-use. */
|
|
719
|
+
freeOnEnd: {
|
|
720
|
+
default: boolean;
|
|
721
|
+
};
|
|
722
|
+
};
|
|
723
|
+
rise: number;
|
|
724
|
+
seconds: number;
|
|
725
|
+
hold: number;
|
|
726
|
+
drift: number;
|
|
727
|
+
freeOnEnd: boolean;
|
|
728
|
+
private elapsed;
|
|
729
|
+
private start;
|
|
730
|
+
private done;
|
|
731
|
+
override onReady(): void;
|
|
732
|
+
override update(dt: number): void;
|
|
733
|
+
}
|
|
734
|
+
//#endregion
|
|
681
735
|
//#region src/gameplay/game-flow.d.ts
|
|
682
736
|
/**
|
|
683
737
|
* Reload the CURRENT scene from its source JSON — fresh nodes, reset physics,
|
|
@@ -869,4 +923,4 @@ declare function registerGameplayBehaviors(opts?: {
|
|
|
869
923
|
replace?: boolean;
|
|
870
924
|
}): void;
|
|
871
925
|
//#endregion
|
|
872
|
-
export { CameraShake, Chase, Collector, Cooldown, DamageOnContact, DayNight, type DayPhase, FollowCamera, GAMEPLAY_BEHAVIORS, GameFlow, type GameFlowState, Health, Interactable, Lifetime, MoveTo, Oscillate, PathFollow, Patrol, Pickup, Projectile, SavePoint, ScoreKeeper, Spawner, Wander, WaveSpawner, ZombieAI, goToScene, hitStop, phaseOf, registerGameplayBehaviors, restartScene, screenFlash };
|
|
926
|
+
export { CameraShake, Chase, Collector, Cooldown, DamageOnContact, DayNight, type DayPhase, FloatAway, FollowCamera, GAMEPLAY_BEHAVIORS, GameFlow, type GameFlowState, Health, Interactable, Lifetime, MoveTo, Oscillate, PathFollow, Patrol, Pickup, Projectile, SavePoint, ScoreKeeper, Spawner, Wander, WaveSpawner, ZombieAI, goToScene, hitStop, phaseOf, registerGameplayBehaviors, restartScene, screenFlash };
|
package/dist/gameplay.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
export { CameraShake, Chase, Collector, Cooldown, DamageOnContact, DayNight, FollowCamera, GAMEPLAY_BEHAVIORS, GameFlow, Health, Interactable, Lifetime, MoveTo, Oscillate, PathFollow, Patrol, Pickup, Projectile, SavePoint, ScoreKeeper, Spawner, Wander, WaveSpawner, ZombieAI, goToScene, hitStop, phaseOf, registerGameplayBehaviors, restartScene, screenFlash };
|
|
1
|
+
import { A as Collector, C as restartScene, D as phaseOf, E as DayNight, O as DamageOnContact, S as goToScene, T as FloatAway, _ as Cooldown, a as Wander, b as Interactable, c as SavePoint, d as Patrol, f as PathFollow, g as CameraShake, h as Lifetime, i as WaveSpawner, j as Chase, k as Health, l as Projectile, m as MoveTo, n as registerGameplayBehaviors, o as Spawner, p as Oscillate, r as ZombieAI, s as ScoreKeeper, t as GAMEPLAY_BEHAVIORS, u as Pickup, v as hitStop, w as FollowCamera, x as GameFlow, y as screenFlash } from "./gameplay-C_SPzmUe.js";
|
|
2
|
+
export { CameraShake, Chase, Collector, Cooldown, DamageOnContact, DayNight, FloatAway, FollowCamera, GAMEPLAY_BEHAVIORS, GameFlow, Health, Interactable, Lifetime, MoveTo, Oscillate, PathFollow, Patrol, Pickup, Projectile, SavePoint, ScoreKeeper, Spawner, Wander, WaveSpawner, ZombieAI, goToScene, hitStop, phaseOf, registerGameplayBehaviors, restartScene, screenFlash };
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import { a as nodeRefWarnings, i as describeRefProblem, n as startRecording, o a
|
|
|
8
8
|
import { n as logText, r as parseDrive, t as logReport } from "./log-report-CPFm4OXf.js";
|
|
9
9
|
import { t as createNoise2D } from "./noise-CGUMx44x.js";
|
|
10
10
|
import { a as PARTICLE_PRESETS, i as ParticleSim, n as resolveFrames, o as PARTICLE_PRESET_NAMES, s as applyParticlePreset, t as resolveAnimation } from "./sprite-animation-7qvUxF6Z.js";
|
|
11
|
-
import { a as findPath, i as preloadUrls, n as newUid, o as gridFromRows, r as assetUrls, t as VERSION } from "./src-
|
|
11
|
+
import { a as findPath, i as preloadUrls, n as newUid, o as gridFromRows, r as assetUrls, t as VERSION } from "./src-51GA-9m-.js";
|
|
12
12
|
import { i as resolveRendering, n as attachTouchControls, r as joystickVector, t as TouchControls } from "./touch-BnMyy9tr.js";
|
|
13
13
|
import { t as duplicateNode } from "./duplicate-MNLMAcbz.js";
|
|
14
14
|
export { AudioBuses, AudioPlayer, BASE_LOCALE, Behavior, CONST_REF_KEY, EffectLog, Engine, HudLayer, IncantoError, InputMap, Localization, LogManager, MusicManager, Node, ORDER_GROUP_BASE, PARTICLE_PRESETS, PARTICLE_PRESET_NAMES, ParticleSim, ROLLOFF_MODELS, Rng, SCENE_FORMAT, SFX_PRESETS, SFX_PRESET_NAMES, SaveSlots, Scene, SceneTree, Settings, SfxEngine, Signal, T_PREFIX, Timer, TouchControls, UiBanner, UiBar, UiButton, UiDialogue, UiFrameCapSelect, UiImage, UiLanguageSelect, UiMuteToggle, UiPanel, UiQualitySelect, UiRenderScaleSelect, UiSelect, UiSlider, UiText, UiToggle, UiVolumeSlider, VERSION, WebAudioMusicBackend, applyParticlePreset, assetUrls, attachTouchControls, auditScene, behaviorSchema, behaviorSignals, behaviorsWithoutSave, captureBehaviors, clearBehaviors, clearRegistry, computeViewport, createNode, createNoise2D, createSaveStore, crossfadeGains, describeRefProblem, duplicateNode, effectiveOrder, fadeGain, findPath, getBehavior, getNodeSchema, getNodeSignals, getNodeType, gridFromRows, isAudioContextAvailable, isConstRef, joystickVector, jsonClone, jsonEquals, jsonKind, loadScene, logReport, logText, mergeStaticSignals, newUid, nodeRefWarnings, parseDrive, parseNodePath, preloadUrls, qualityEnvironment, readDeviceHints, registerBehavior, registerCoreNodes, registerNode, registeredBehaviors, registeredTypes, replay, resolveAnimation, resolveConstants, resolveFrames, resolveOrderGroups, resolveRefInJson, resolveRendering, resolveViewport, restoreBehaviors, savesWithoutUid, serializeNode, spatialGain, spatialPan, startRecording, suggestLocale, suggestQuality, synthSfx, translationKey };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
2
|
import { C as diagnose } from "./loader-BTkHYrQn.js";
|
|
3
3
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
4
|
-
import { B as
|
|
4
|
+
import { B as Node3D, F as Area3D, I as CharacterBody3D, L as PhysicsBody3D, R as RigidBody3D, V as validateCollider3D } from "./gameplay-C_SPzmUe.js";
|
|
5
5
|
import { n as registerDebugSource } from "./debug-draw-BM3DsvtT.js";
|
|
6
|
-
import { A as Terrain3D, F as InstancedMesh3D, L as buildMeshGeometry, P as Joint3D } from "./environment-presets-
|
|
6
|
+
import { A as Terrain3D, F as InstancedMesh3D, L as buildMeshGeometry, P as Joint3D } from "./environment-presets-DLHqgNAF.js";
|
|
7
7
|
import { Euler, Matrix4, Quaternion, Vector3 } from "three";
|
|
8
8
|
//#region src/3d/physics/collider-lines.ts
|
|
9
9
|
/**
|
package/dist/react.js
CHANGED
|
@@ -156,7 +156,7 @@ function IncantoCanvas(props) {
|
|
|
156
156
|
pointer: latest.pointer,
|
|
157
157
|
...keyboard !== void 0 ? { keyboard } : {}
|
|
158
158
|
};
|
|
159
|
-
const next = await (_gameFactory ?? (mode === "3d" ? async (o) => (await import("./create-game-
|
|
159
|
+
const next = await (_gameFactory ?? (mode === "3d" ? async (o) => (await import("./create-game-CqJkfMzm.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-YvaNWhe6.js").then((n) => n.n)).createGame2D(o)))(opts);
|
|
160
160
|
if (disposed) {
|
|
161
161
|
next.dispose();
|
|
162
162
|
return;
|
|
@@ -161,6 +161,6 @@ function newUid() {
|
|
|
161
161
|
//#endregion
|
|
162
162
|
//#region src/index.ts
|
|
163
163
|
/** Engine version. Kept in sync with package.json by the release pipeline. */
|
|
164
|
-
const VERSION = "0.
|
|
164
|
+
const VERSION = "0.56.0";
|
|
165
165
|
//#endregion
|
|
166
166
|
export { findPath as a, preloadUrls as i, newUid as n, gridFromRows as o, assetUrls as r, VERSION as t };
|
|
@@ -4,9 +4,9 @@ import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
|
4
4
|
import { n as jsonEquals, t as jsonClone } from "./json-BLk7H2Qa.js";
|
|
5
5
|
import { i as getNodeSchema, s as mergeStaticProps } from "./registry-C7u42TID.js";
|
|
6
6
|
import { n as startRecording } from "./replay-t1pP0gQg.js";
|
|
7
|
-
import { n as registerGameplayBehaviors } from "./gameplay-
|
|
7
|
+
import { n as registerGameplayBehaviors } from "./gameplay-C_SPzmUe.js";
|
|
8
8
|
import { t as registerNodes2D } from "./register-en63AEZO.js";
|
|
9
|
-
import { n as registerNodes3D, t as resolveEnvironmentHdri } from "./environment-presets-
|
|
9
|
+
import { n as registerNodes3D, t as resolveEnvironmentHdri } from "./environment-presets-DLHqgNAF.js";
|
|
10
10
|
import { n as registerNodesNet, t as createSplitScreen } from "./split-screen-CSb_uZ6W.js";
|
|
11
11
|
import { Box3, Euler, Matrix4, PerspectiveCamera, Quaternion, Vector3 } from "three";
|
|
12
12
|
//#region src/test/framing.ts
|
|
@@ -752,6 +752,8 @@ async function runOnce(json, seed, opts) {
|
|
|
752
752
|
}
|
|
753
753
|
}
|
|
754
754
|
driver.release();
|
|
755
|
+
const heard = engine.audio.recent().map((e) => e.from);
|
|
756
|
+
const shown = engine.effects.recent().map((e) => e.from);
|
|
755
757
|
const bad = outcome !== "won";
|
|
756
758
|
const run = {
|
|
757
759
|
seed,
|
|
@@ -762,7 +764,9 @@ async function runOnce(json, seed, opts) {
|
|
|
762
764
|
damageTaken: oracle.damage(),
|
|
763
765
|
errors: engine.stats().errors,
|
|
764
766
|
replay: bad ? recorder.stop() : null,
|
|
765
|
-
endedAt: at
|
|
767
|
+
endedAt: at,
|
|
768
|
+
heard: [...new Set(heard)],
|
|
769
|
+
shown: [...new Set(shown)]
|
|
766
770
|
};
|
|
767
771
|
if (!bad) recorder.stop();
|
|
768
772
|
for (const off of offs) off();
|
|
@@ -796,6 +800,19 @@ async function playtest(json, opts = {}) {
|
|
|
796
800
|
...opts.resolveScene ? { resolveScene: opts.resolveScene } : {}
|
|
797
801
|
});
|
|
798
802
|
const actions = probe.engine.input.declaredActions().map((a) => a.name);
|
|
803
|
+
const feedbackNodes = {
|
|
804
|
+
audio: [],
|
|
805
|
+
effects: []
|
|
806
|
+
};
|
|
807
|
+
const walkFeedback = (node) => {
|
|
808
|
+
const type = node.constructor.typeName ?? "";
|
|
809
|
+
const path = node.getPath();
|
|
810
|
+
if (type === "AudioPlayer") feedbackNodes.audio.push(path);
|
|
811
|
+
if (type === "Particles2D" || type === "Particles3D") feedbackNodes.effects.push(path);
|
|
812
|
+
for (const child of node.children) walkFeedback(child);
|
|
813
|
+
};
|
|
814
|
+
const probeRoot = probe.scene.tree.root;
|
|
815
|
+
if (probeRoot) walkFeedback(probeRoot);
|
|
799
816
|
probe.dispose();
|
|
800
817
|
return {
|
|
801
818
|
runs,
|
|
@@ -804,7 +821,13 @@ async function playtest(json, opts = {}) {
|
|
|
804
821
|
actions,
|
|
805
822
|
inertActions: [],
|
|
806
823
|
declaresWin,
|
|
807
|
-
seconds: opts.seconds ?? 60
|
|
824
|
+
seconds: opts.seconds ?? 60,
|
|
825
|
+
feedback: {
|
|
826
|
+
declaredAudio: feedbackNodes.audio,
|
|
827
|
+
declaredEffects: feedbackNodes.effects,
|
|
828
|
+
heard: [...new Set(runs.flatMap((r) => r.heard))],
|
|
829
|
+
shown: [...new Set(runs.flatMap((r) => r.shown))]
|
|
830
|
+
}
|
|
808
831
|
};
|
|
809
832
|
}
|
|
810
833
|
function pct(n, total) {
|
|
@@ -1733,7 +1756,7 @@ async function runScript(json, opts) {
|
|
|
1733
1756
|
const { enablePhysics2D } = await import("./physics-2d-CfWAggJ1.js").then((n) => n.r);
|
|
1734
1757
|
await enablePhysics2D(engine);
|
|
1735
1758
|
} else if (physics === "3d" || physics === "auto" && scene.dimension === "3d") {
|
|
1736
|
-
const { enablePhysics3D } = await import("./physics-3d-
|
|
1759
|
+
const { enablePhysics3D } = await import("./physics-3d-NzDwWPrF.js").then((n) => n.r);
|
|
1737
1760
|
await enablePhysics3D(engine);
|
|
1738
1761
|
}
|
|
1739
1762
|
const failures = [];
|
|
@@ -1849,7 +1872,7 @@ async function createPlaySession(json, opts = {}) {
|
|
|
1849
1872
|
const { enablePhysics2D } = await import("./physics-2d-CfWAggJ1.js").then((n) => n.r);
|
|
1850
1873
|
await enablePhysics2D(engine);
|
|
1851
1874
|
} else if (physics === "3d" || physics === "auto" && scene.dimension === "3d") {
|
|
1852
|
-
const { enablePhysics3D } = await import("./physics-3d-
|
|
1875
|
+
const { enablePhysics3D } = await import("./physics-3d-NzDwWPrF.js").then((n) => n.r);
|
|
1853
1876
|
await enablePhysics3D(engine);
|
|
1854
1877
|
}
|
|
1855
1878
|
const stepMs = 1e3 / (opts.fixedHz ?? 60);
|
package/dist/test.d.ts
CHANGED
|
@@ -232,6 +232,10 @@ interface PlaytestRun {
|
|
|
232
232
|
replay: ReplayJson | null;
|
|
233
233
|
/** Where the player ended up, for the fall report. */
|
|
234
234
|
endedAt: [number, number, number];
|
|
235
|
+
/** Node paths that made a sound during the run. */
|
|
236
|
+
heard: string[];
|
|
237
|
+
/** Node paths that fired a visible effect during the run. */
|
|
238
|
+
shown: string[];
|
|
235
239
|
}
|
|
236
240
|
interface PlaytestReport {
|
|
237
241
|
runs: PlaytestRun[];
|
|
@@ -261,6 +265,18 @@ interface PlaytestReport {
|
|
|
261
265
|
* up to 1s", hiding the very fact it was reporting.
|
|
262
266
|
*/
|
|
263
267
|
seconds: number;
|
|
268
|
+
/**
|
|
269
|
+
* Sound and effects: what the scene declares, and what actually fired.
|
|
270
|
+
*
|
|
271
|
+
* A game whose feedback is wired but never triggered plays perfectly and
|
|
272
|
+
* feels dead — invisible in every other number in this report.
|
|
273
|
+
*/
|
|
274
|
+
feedback: {
|
|
275
|
+
declaredAudio: string[];
|
|
276
|
+
declaredEffects: string[];
|
|
277
|
+
heard: string[];
|
|
278
|
+
shown: string[];
|
|
279
|
+
};
|
|
264
280
|
}
|
|
265
281
|
/**
|
|
266
282
|
* Who the driver is playing as.
|
package/dist/test.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { r as auditScene } from "./replay-t1pP0gQg.js";
|
|
2
|
-
import { S as framingText, _ as failingReplays, a as registerAllNodes, b as playtestText, c as ladderText, d as multiplayText, f as playMultiplayer, g as facingText, h as facingReport, i as findFloatingProps, l as ladderVerdict, m as feelText, n as createPlaySession, o as runScript, p as feelReport, r as describeCapture, s as validateScene, t as captureScene, u as multiplayProblems, v as findPlayer, x as describeFraming, y as playtest } from "./test-
|
|
2
|
+
import { S as framingText, _ as failingReplays, a as registerAllNodes, b as playtestText, c as ladderText, d as multiplayText, f as playMultiplayer, g as facingText, h as facingReport, i as findFloatingProps, l as ladderVerdict, m as feelText, n as createPlaySession, o as runScript, p as feelReport, r as describeCapture, s as validateScene, t as captureScene, u as multiplayProblems, v as findPlayer, x as describeFraming, y as playtest } from "./test-Bu6SPNsV.js";
|
|
3
3
|
export { auditScene, captureScene, createPlaySession, describeCapture, describeFraming, facingReport, facingText, failingReplays, feelReport, feelText, findFloatingProps, findPlayer, framingText, ladderText, ladderVerdict, multiplayProblems, multiplayText, playMultiplayer, playtest, playtestText, registerAllNodes, runScript, validateScene };
|
package/dist/vite.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as VERSION } from "./src-
|
|
1
|
+
import { t as VERSION } from "./src-51GA-9m-.js";
|
|
2
2
|
import { n as diffSignatures } from "./frame-report-njybhZon.js";
|
|
3
|
-
import { s as validateScene } from "./test-
|
|
3
|
+
import { s as validateScene } from "./test-Bu6SPNsV.js";
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { basename, dirname, join, normalize, relative, resolve, sep } from "node:path";
|
|
6
6
|
//#region src/vite/frame-endpoint.ts
|