incanto 0.53.0 → 0.55.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-check.mjs +56 -1
- package/bin/incanto-verify.mjs +46 -0
- package/dist/2d.d.ts +46 -3
- package/dist/2d.js +3 -3
- package/dist/3d.d.ts +105 -6
- package/dist/3d.js +10 -6
- package/dist/{behavior-Do0Da56m.d.ts → behavior-CyQoSu4n.d.ts} +114 -10
- package/dist/{create-game-9D87XaiX.js → create-game-BRt6XKmP.js} +10 -6
- package/dist/{create-game-CDJ1lVqK.js → create-game-CzK9_pzg.js} +10 -6
- package/dist/debug.d.ts +1 -1
- package/dist/debug.js +4 -2
- package/dist/editor.js +7 -0
- package/dist/{environment-presets-SkGanr2s.js → environment-presets-DAbEdEwh.js} +326 -11
- package/dist/{gameplay-CRYw_Q-T.js → gameplay-CZ2yq37J.js} +30 -21
- package/dist/gameplay.d.ts +8 -3
- package/dist/gameplay.js +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.js +5 -5
- package/dist/{loader-Cu_7kJDy.d.ts → loader-DhI1jFW_.d.ts} +1 -1
- package/dist/net.d.ts +2 -2
- package/dist/net.js +1 -1
- package/dist/{particle-sim-BzJ1yxoE.d.ts → particle-sim-C5OfBbmU.d.ts} +11 -0
- package/dist/{pathfinding-HNGFqGUZ.d.ts → pathfinding-CXGCpRQe.d.ts} +1 -1
- package/dist/{physics-2d-BhZZ-HAp.js → physics-2d-CfWAggJ1.js} +1 -1
- package/dist/{physics-3d-DmyO2oaN.js → physics-3d-C_ZJ6f_d.js} +2 -2
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{register-DROK2l7J.js → register-en63AEZO.js} +163 -4
- package/dist/{register-BTomIiYG.js → register-p48lHE2o.js} +130 -13
- package/dist/{replay-9Fy6C10F.d.ts → replay-ePMz26jw.d.ts} +1 -1
- package/dist/{replay-DYNUL4BU.js → replay-t1pP0gQg.js} +1 -1
- package/dist/{split-screen-DNmcX1Pz.d.ts → split-screen-BsdOHbzP.d.ts} +1 -1
- package/dist/{split-screen-CaF9hO7g.js → split-screen-CSb_uZ6W.js} +1 -1
- package/dist/{sprite-animation-D_p28jwU.js → sprite-animation-7qvUxF6Z.js} +19 -0
- package/dist/{src-C9xyZW7M.js → src-BVOVHRL0.js} +1 -1
- package/dist/{teardown-Bw2aeyGI.js → teardown-C7uVSJvx.js} +1 -1
- package/dist/{test-CJRsciFk.js → test-Ca5eqELC.js} +49 -14
- package/dist/test.d.ts +20 -4
- package/dist/test.js +2 -2
- package/dist/vite.js +2 -2
- package/editor/assets/{agent8-PlFHzJsh.js → agent8-Cz4oHPtm.js} +1 -1
- package/editor/assets/{debug-CebV7CDW.js → debug-r-Fi9nZh.js} +2 -2
- package/editor/assets/{index-CdbsY31G.js → index-BfqmvXYD.js} +91 -91
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/schemas/scene.schema.json +185 -0
- package/skills/incanto-assets.md +45 -2
- package/skills/incanto-building-2d-games.md +35 -0
- package/skills/incanto-building-3d-games.md +28 -3
- package/skills/incanto-environment.md +3 -1
- package/skills/incanto-gameplay-behaviors.md +51 -4
- package/skills/incanto-node-reference.md +31 -0
- package/skills/incanto-performance.md +51 -0
- package/skills/incanto-verifying-your-game.md +38 -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
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
2
|
import { A as registerBehavior, C as diagnose, n as loadScene } from "./loader-BTkHYrQn.js";
|
|
3
|
-
import { C as qualityCaps, T as qualityRendering, b as AudioPlayer, x as Engine } from "./register-
|
|
3
|
+
import { C as qualityCaps, T as qualityRendering, b as AudioPlayer, x as Engine } from "./register-p48lHE2o.js";
|
|
4
4
|
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
|
-
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-
|
|
8
|
-
import { I as PhysicsBody3D, V as createCausticsQuad, n as registerGameplayBehaviors, z as Node3D } from "./gameplay-
|
|
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 { I as PhysicsBody3D, V as createCausticsQuad, n as registerGameplayBehaviors, z as Node3D } from "./gameplay-CZ2yq37J.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-DAbEdEwh.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-C_ZJ6f_d.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";
|
|
@@ -1670,7 +1670,11 @@ var Renderer3D = class {
|
|
|
1670
1670
|
this.selectionLines.renderOrder = 1e4;
|
|
1671
1671
|
this.selectionLines.visible = false;
|
|
1672
1672
|
this.threeScene.add(this.selectionLines);
|
|
1673
|
-
this.disconnect = this.engine.updated.connect(() =>
|
|
1673
|
+
this.disconnect = this.engine.updated.connect(() => {
|
|
1674
|
+
const started = performance.now();
|
|
1675
|
+
this.render();
|
|
1676
|
+
this.engine.noteRenderMs(performance.now() - started);
|
|
1677
|
+
});
|
|
1674
1678
|
}
|
|
1675
1679
|
/** The pixel ratio the scene ASKED for — the governor scales this, never replaces it. */
|
|
1676
1680
|
basePixelRatio;
|
package/dist/debug.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Pt as LogLevel, T as RendererStats, b as Engine } from "./behavior-CyQoSu4n.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
|
@@ -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");
|
|
@@ -1600,7 +1602,7 @@ var DebugOverlay = class {
|
|
|
1600
1602
|
*/
|
|
1601
1603
|
nextFrame() {
|
|
1602
1604
|
if (!this.paused) this.setPaused(true);
|
|
1603
|
-
this.engine.step();
|
|
1605
|
+
this.engine.step({ ignorePause: true });
|
|
1604
1606
|
this.syncTime();
|
|
1605
1607
|
}
|
|
1606
1608
|
/** Push the engine's current time state into the panel, without rebuilding it. */
|
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: "볼륨 슬라이더 — 버스에 이미 연결되어 있습니다."
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { t as registerCoreNodes } from "./register-
|
|
1
|
+
import { t as registerCoreNodes } from "./register-p48lHE2o.js";
|
|
2
2
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
3
3
|
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
|
-
import { i as ParticleSim, n as resolveFrames, o as PARTICLE_PRESET_NAMES, r as validateAnimationAliases, s as applyParticlePreset, t as resolveAnimation } from "./sprite-animation-
|
|
7
|
-
import { F as CharacterBody3D, I as PhysicsBody3D, L as RigidBody3D, N as WaterCutout3D, P as Area3D, R as StaticBody3D, U as colliderFootDrop, j as Water3D, z as Node3D } from "./gameplay-
|
|
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 CharacterBody3D, I as PhysicsBody3D, L as RigidBody3D, N as WaterCutout3D, P as Area3D, R as StaticBody3D, U as colliderFootDrop, j as Water3D, z as Node3D } from "./gameplay-CZ2yq37J.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";
|
|
@@ -104,6 +104,25 @@ var TextureCache3D = class {
|
|
|
104
104
|
return master ? master.clone() : null;
|
|
105
105
|
}
|
|
106
106
|
/**
|
|
107
|
+
* File a failure this store did not load itself.
|
|
108
|
+
*
|
|
109
|
+
* `Tree3D` keeps its own cache on purpose (it decodes off-thread and its
|
|
110
|
+
* page-session lifetime is load-bearing for the editor), which left its bark
|
|
111
|
+
* and leaf textures outside the one place `assetErrors()` reads. A 404'd leaf
|
|
112
|
+
* texture is a forest of bare branches and no message anywhere.
|
|
113
|
+
*/
|
|
114
|
+
noteError(url, error) {
|
|
115
|
+
const key = `external|${url}`;
|
|
116
|
+
if (this.entries.has(key)) return;
|
|
117
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
118
|
+
this.entries.set(key, {
|
|
119
|
+
url,
|
|
120
|
+
texture: null,
|
|
121
|
+
error: message
|
|
122
|
+
});
|
|
123
|
+
this.diagnostics?.warn(`texture failed to load: ${url} — ${message}`);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
107
126
|
* What 404'd, by URL — reported raw, never by cache key, because an agent
|
|
108
127
|
* greps this against the URL it wrote.
|
|
109
128
|
*/
|
|
@@ -5666,6 +5685,109 @@ const colorScratchB$1 = new Color();
|
|
|
5666
5685
|
const colorScratchC$1 = new Color();
|
|
5667
5686
|
const tuftDryScratch = new Color();
|
|
5668
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
|
+
canvas = null;
|
|
5741
|
+
/** The rasterised text. Named apart from Sprite3D's `texture` URL prop, which
|
|
5742
|
+
* this node ignores — the text IS the picture. */
|
|
5743
|
+
canvasTexture = null;
|
|
5744
|
+
lastKey = "";
|
|
5745
|
+
resolveTexture(_assets) {
|
|
5746
|
+
if (this.text === "" || typeof document === "undefined") return null;
|
|
5747
|
+
const key = `${this.text}\0${this.height}\0${this.color}\0${this.font}\0${this.outline}`;
|
|
5748
|
+
if (key !== this.lastKey) {
|
|
5749
|
+
this.lastKey = key;
|
|
5750
|
+
this.rasterize();
|
|
5751
|
+
}
|
|
5752
|
+
return this.canvasTexture;
|
|
5753
|
+
}
|
|
5754
|
+
/** Draw the text once, and size the quad to what it measured. */
|
|
5755
|
+
rasterize() {
|
|
5756
|
+
const fontPx = Math.max(8, Math.round(this.height * 256));
|
|
5757
|
+
this.canvas ??= document.createElement("canvas");
|
|
5758
|
+
const ctx = this.canvas.getContext("2d");
|
|
5759
|
+
if (!ctx) return;
|
|
5760
|
+
const stroke = Math.max(1, Math.round(fontPx * .14));
|
|
5761
|
+
const fontSpec = `bold ${fontPx}px ${this.font}`;
|
|
5762
|
+
ctx.font = fontSpec;
|
|
5763
|
+
const width = Math.max(1, Math.ceil(ctx.measureText(this.text).width) + stroke * 2);
|
|
5764
|
+
const lineHeight = Math.ceil(fontPx * 1.35) + stroke * 2;
|
|
5765
|
+
this.canvas.width = width;
|
|
5766
|
+
this.canvas.height = lineHeight;
|
|
5767
|
+
ctx.font = fontSpec;
|
|
5768
|
+
ctx.textBaseline = "middle";
|
|
5769
|
+
ctx.textAlign = "center";
|
|
5770
|
+
if (this.outline !== "") {
|
|
5771
|
+
ctx.lineWidth = stroke;
|
|
5772
|
+
ctx.lineJoin = "round";
|
|
5773
|
+
ctx.strokeStyle = this.outline;
|
|
5774
|
+
ctx.strokeText(this.text, width / 2, lineHeight / 2);
|
|
5775
|
+
}
|
|
5776
|
+
ctx.fillStyle = this.color;
|
|
5777
|
+
ctx.fillText(this.text, width / 2, lineHeight / 2);
|
|
5778
|
+
this.canvasTexture?.dispose();
|
|
5779
|
+
const texture = new CanvasTexture(this.canvas);
|
|
5780
|
+
texture.colorSpace = SRGBColorSpace;
|
|
5781
|
+
this.canvasTexture = texture;
|
|
5782
|
+
this.size = [width / lineHeight * this.height * 1.35, this.height * 1.35];
|
|
5783
|
+
}
|
|
5784
|
+
free() {
|
|
5785
|
+
this.canvasTexture?.dispose();
|
|
5786
|
+
this.canvasTexture = null;
|
|
5787
|
+
super.free();
|
|
5788
|
+
}
|
|
5789
|
+
};
|
|
5790
|
+
//#endregion
|
|
5669
5791
|
//#region src/3d/nodes/lights-3d.ts
|
|
5670
5792
|
/** Sun-style light. Direction comes from the node's rotation. */
|
|
5671
5793
|
var DirectionalLight3D = class extends Node3D {
|
|
@@ -5806,6 +5928,25 @@ var LoftMesh3D = class extends Node3D {
|
|
|
5806
5928
|
m.castShadow = this.castShadow;
|
|
5807
5929
|
m.receiveShadow = this.receiveShadow;
|
|
5808
5930
|
}
|
|
5931
|
+
/**
|
|
5932
|
+
* Hand the GPU back.
|
|
5933
|
+
*
|
|
5934
|
+
* Every other node that builds geometry disposes it here; these four did not,
|
|
5935
|
+
* so a scene swap left their buffers and materials resident and a game with
|
|
5936
|
+
* level transitions climbed until the tab died — with `stats().geometries` the
|
|
5937
|
+
* only witness, and only if someone was watching it.
|
|
5938
|
+
*/
|
|
5939
|
+
free() {
|
|
5940
|
+
const object = this._builtObject3D;
|
|
5941
|
+
if (object) object.traverse((child) => {
|
|
5942
|
+
const mesh = child;
|
|
5943
|
+
mesh.geometry?.dispose();
|
|
5944
|
+
const material = mesh.material;
|
|
5945
|
+
const list = Array.isArray(material) ? material : material ? [material] : [];
|
|
5946
|
+
for (const m of list) m.dispose?.();
|
|
5947
|
+
});
|
|
5948
|
+
super.free();
|
|
5949
|
+
}
|
|
5809
5950
|
};
|
|
5810
5951
|
function validateSections(sections, name) {
|
|
5811
5952
|
const fail = (message) => {
|
|
@@ -6764,6 +6905,8 @@ var Particles3D = class Particles3D extends Node3D {
|
|
|
6764
6905
|
options: ["custom", ...PARTICLE_PRESET_NAMES]
|
|
6765
6906
|
},
|
|
6766
6907
|
emitting: { default: true },
|
|
6908
|
+
/** Particles keep the place they were born instead of following the node. */
|
|
6909
|
+
worldSpace: { default: false },
|
|
6767
6910
|
rate: { default: 40 },
|
|
6768
6911
|
burst: { default: 0 },
|
|
6769
6912
|
lifetime: { default: [.6, 1.2] },
|
|
@@ -6789,6 +6932,18 @@ var Particles3D = class Particles3D extends Node3D {
|
|
|
6789
6932
|
};
|
|
6790
6933
|
preset = "custom";
|
|
6791
6934
|
emitting = true;
|
|
6935
|
+
/**
|
|
6936
|
+
* Where a particle LIVES once it is born.
|
|
6937
|
+
*
|
|
6938
|
+
* `false` (default) — the emitter's local space: the plume moves with the
|
|
6939
|
+
* node, which is what a torch on a moving platform wants.
|
|
6940
|
+
*
|
|
6941
|
+
* `true` — the world: a particle keeps the place it was born, so dust is left
|
|
6942
|
+
* BEHIND a running player instead of glued to it, and moving a one-shot to
|
|
6943
|
+
* the next explosion (the documented `p.position = …; p.replay()` recipe)
|
|
6944
|
+
* stops dragging the previous one across the level.
|
|
6945
|
+
*/
|
|
6946
|
+
worldSpace = false;
|
|
6792
6947
|
rate = 40;
|
|
6793
6948
|
burst = 0;
|
|
6794
6949
|
lifetime = [.6, 1.2];
|
|
@@ -6855,12 +7010,38 @@ var Particles3D = class Particles3D extends Node3D {
|
|
|
6855
7010
|
}
|
|
6856
7011
|
return this.sim;
|
|
6857
7012
|
}
|
|
7013
|
+
/** Particles alive this frame — an emitter that is running, told from one
|
|
7014
|
+
* that is not. `framing()` says where it IS; this says whether it is going. */
|
|
7015
|
+
get aliveCount() {
|
|
7016
|
+
return this.sim?.count ?? 0;
|
|
7017
|
+
}
|
|
7018
|
+
/** A snapshot of the live particles — positions are in whichever space
|
|
7019
|
+
* `worldSpace` selects, relative to the emitter. */
|
|
7020
|
+
particles() {
|
|
7021
|
+
const out = [];
|
|
7022
|
+
this.sim?.forEach((p) => {
|
|
7023
|
+
out.push({ ...p });
|
|
7024
|
+
});
|
|
7025
|
+
return out;
|
|
7026
|
+
}
|
|
7027
|
+
/** The emitter's world position last frame, for world-space counter-motion. */
|
|
7028
|
+
lastWorld = null;
|
|
7029
|
+
/** Last frame's `emitting`, so turning one ON is a reportable event. */
|
|
7030
|
+
wasEmitting = false;
|
|
6858
7031
|
update(dt) {
|
|
6859
7032
|
const sim = this._ensureSim();
|
|
7033
|
+
this.holdWorldSpace(sim);
|
|
6860
7034
|
sim.setRate(this.emitting ? this.rate : 0);
|
|
6861
|
-
if (this.burst > 0 && !this.bursted) {
|
|
7035
|
+
if (this.burst > 0 && !this.bursted && (this.emitting || this.armedByReplay)) {
|
|
6862
7036
|
this.bursted = true;
|
|
7037
|
+
this.armedByReplay = false;
|
|
6863
7038
|
sim.burst(this.burst);
|
|
7039
|
+
this.report("burst", this.burst);
|
|
7040
|
+
}
|
|
7041
|
+
const on = this.emitting && this.rate > 0;
|
|
7042
|
+
if (on !== this.wasEmitting) {
|
|
7043
|
+
this.wasEmitting = on;
|
|
7044
|
+
if (on) this.report("emit", this.rate);
|
|
6864
7045
|
}
|
|
6865
7046
|
sim.update(dt);
|
|
6866
7047
|
if ((!this.emitting || this.rate === 0) && this.bursted && sim.done && !this.finishedEmitted) {
|
|
@@ -6868,11 +7049,52 @@ var Particles3D = class Particles3D extends Node3D {
|
|
|
6868
7049
|
this.emit("finished");
|
|
6869
7050
|
}
|
|
6870
7051
|
}
|
|
7052
|
+
/**
|
|
7053
|
+
* Undo the emitter's own motion on everything already alive.
|
|
7054
|
+
*
|
|
7055
|
+
* Ancestor-summed like the physics helpers, so it follows a moving PARENT
|
|
7056
|
+
* (dust under a player) as well as the node's own position.
|
|
7057
|
+
*/
|
|
7058
|
+
holdWorldSpace(sim) {
|
|
7059
|
+
if (!this.worldSpace) {
|
|
7060
|
+
this.lastWorld = null;
|
|
7061
|
+
return;
|
|
7062
|
+
}
|
|
7063
|
+
let x = 0;
|
|
7064
|
+
let y = 0;
|
|
7065
|
+
let z = 0;
|
|
7066
|
+
for (let n = this; n; n = n.parent) if (n instanceof Node3D) {
|
|
7067
|
+
x += n.position[0] ?? 0;
|
|
7068
|
+
y += n.position[1] ?? 0;
|
|
7069
|
+
z += n.position[2] ?? 0;
|
|
7070
|
+
}
|
|
7071
|
+
const prev = this.lastWorld;
|
|
7072
|
+
if (prev) sim.translateAll(prev[0] - x, prev[1] - y, prev[2] - z);
|
|
7073
|
+
this.lastWorld = [
|
|
7074
|
+
x,
|
|
7075
|
+
y,
|
|
7076
|
+
z
|
|
7077
|
+
];
|
|
7078
|
+
}
|
|
7079
|
+
/** Tell the engine an effect fired — the visual half of the audio record. */
|
|
7080
|
+
report(kind, amount) {
|
|
7081
|
+
const engine = this.tree?.engine;
|
|
7082
|
+
engine?.effects.record({
|
|
7083
|
+
kind,
|
|
7084
|
+
name: this.preset,
|
|
7085
|
+
from: this.getPath(),
|
|
7086
|
+
amount,
|
|
7087
|
+
at: engine.time
|
|
7088
|
+
});
|
|
7089
|
+
}
|
|
6871
7090
|
/** Restart a one-shot (re-burst + allow finished to fire again). */
|
|
6872
7091
|
replay() {
|
|
6873
7092
|
this.bursted = false;
|
|
6874
7093
|
this.finishedEmitted = false;
|
|
7094
|
+
this.armedByReplay = true;
|
|
6875
7095
|
}
|
|
7096
|
+
/** `replay()` asked for this burst, so `emitting: false` does not hold it. */
|
|
7097
|
+
armedByReplay = false;
|
|
6876
7098
|
points = null;
|
|
6877
7099
|
positions = null;
|
|
6878
7100
|
colors = null;
|
|
@@ -6958,6 +7180,25 @@ var Particles3D = class Particles3D extends Node3D {
|
|
|
6958
7180
|
geometry.getAttribute("position").needsUpdate = true;
|
|
6959
7181
|
geometry.getAttribute("color").needsUpdate = true;
|
|
6960
7182
|
}
|
|
7183
|
+
/**
|
|
7184
|
+
* Hand the GPU back.
|
|
7185
|
+
*
|
|
7186
|
+
* Every other node that builds geometry disposes it here; these four did not,
|
|
7187
|
+
* so a scene swap left their buffers and materials resident and a game with
|
|
7188
|
+
* level transitions climbed until the tab died — with `stats().geometries` the
|
|
7189
|
+
* only witness, and only if someone was watching it.
|
|
7190
|
+
*/
|
|
7191
|
+
free() {
|
|
7192
|
+
const object = this._builtObject3D;
|
|
7193
|
+
if (object) object.traverse((child) => {
|
|
7194
|
+
const mesh = child;
|
|
7195
|
+
mesh.geometry?.dispose();
|
|
7196
|
+
const material = mesh.material;
|
|
7197
|
+
const list = Array.isArray(material) ? material : material ? [material] : [];
|
|
7198
|
+
for (const m of list) m.dispose?.();
|
|
7199
|
+
});
|
|
7200
|
+
super.free();
|
|
7201
|
+
}
|
|
6961
7202
|
};
|
|
6962
7203
|
const colorScratchA = new Color();
|
|
6963
7204
|
const colorScratchB = new Color();
|
|
@@ -9008,8 +9249,21 @@ var Trail3D = class extends Node3D {
|
|
|
9008
9249
|
group.add(this.mesh);
|
|
9009
9250
|
return group;
|
|
9010
9251
|
}
|
|
9252
|
+
/** Last frame's `emitting`, so laying ribbon is a reportable event. */
|
|
9253
|
+
wasEmitting = false;
|
|
9011
9254
|
update(dt) {
|
|
9012
9255
|
this.clock += dt;
|
|
9256
|
+
if (this.emitting !== this.wasEmitting) {
|
|
9257
|
+
this.wasEmitting = this.emitting;
|
|
9258
|
+
const engine = this.tree?.engine;
|
|
9259
|
+
if (this.emitting && engine) engine.effects.record({
|
|
9260
|
+
kind: "trail",
|
|
9261
|
+
name: this.color,
|
|
9262
|
+
from: this.getPath(),
|
|
9263
|
+
amount: this.seconds,
|
|
9264
|
+
at: engine.time
|
|
9265
|
+
});
|
|
9266
|
+
}
|
|
9013
9267
|
while (this.points.length > 0 && this.clock - this.points[0].t > this.seconds) this.points.shift();
|
|
9014
9268
|
}
|
|
9015
9269
|
_onRender3D(ctx) {
|
|
@@ -9066,10 +9320,38 @@ var Trail3D = class extends Node3D {
|
|
|
9066
9320
|
}
|
|
9067
9321
|
geometry.setDrawRange(0, (n - 1) * 6);
|
|
9068
9322
|
}
|
|
9069
|
-
/**
|
|
9323
|
+
/**
|
|
9324
|
+
* How many points the ribbon is made of.
|
|
9325
|
+
*
|
|
9326
|
+
* ZERO with no renderer, and that is not a bug: the ribbon is laid in the
|
|
9327
|
+
* render sync, because each point needs the node's real world transform (a
|
|
9328
|
+
* sword arc is a ROTATING parent, which an ancestor position sum cannot see).
|
|
9329
|
+
* Assert it from a browser check; read a headless 0 as "nothing rendered"
|
|
9330
|
+
* rather than "my trail is broken", and use the `trail` effect record for the
|
|
9331
|
+
* wiring question.
|
|
9332
|
+
*/
|
|
9070
9333
|
get pointCount() {
|
|
9071
9334
|
return this.points.length;
|
|
9072
9335
|
}
|
|
9336
|
+
/**
|
|
9337
|
+
* Hand the GPU back.
|
|
9338
|
+
*
|
|
9339
|
+
* Every other node that builds geometry disposes it here; these four did not,
|
|
9340
|
+
* so a scene swap left their buffers and materials resident and a game with
|
|
9341
|
+
* level transitions climbed until the tab died — with `stats().geometries` the
|
|
9342
|
+
* only witness, and only if someone was watching it.
|
|
9343
|
+
*/
|
|
9344
|
+
free() {
|
|
9345
|
+
const object = this._builtObject3D;
|
|
9346
|
+
if (object) object.traverse((child) => {
|
|
9347
|
+
const mesh = child;
|
|
9348
|
+
mesh.geometry?.dispose();
|
|
9349
|
+
const material = mesh.material;
|
|
9350
|
+
const list = Array.isArray(material) ? material : material ? [material] : [];
|
|
9351
|
+
for (const m of list) m.dispose?.();
|
|
9352
|
+
});
|
|
9353
|
+
super.free();
|
|
9354
|
+
}
|
|
9073
9355
|
};
|
|
9074
9356
|
const worldScratch = new Vector3();
|
|
9075
9357
|
const dirScratch = new Vector3();
|
|
@@ -10863,10 +11145,23 @@ var Tree3D = class Tree3D extends Node3D {
|
|
|
10863
11145
|
this.leafFadeUniform.end.value = this.leafFadeEnd;
|
|
10864
11146
|
}
|
|
10865
11147
|
}
|
|
10866
|
-
|
|
10867
|
-
|
|
11148
|
+
/** The scene's asset store, so a 404'd bark or leaf reaches assetErrors(). */
|
|
11149
|
+
assets = null;
|
|
11150
|
+
_syncObject3D(alpha = 1, assets = null) {
|
|
11151
|
+
super._syncObject3D(alpha, assets);
|
|
11152
|
+
this.assets = assets;
|
|
10868
11153
|
this.rebuildIfNeeded();
|
|
10869
11154
|
}
|
|
11155
|
+
/**
|
|
11156
|
+
* File a texture failure where the ladder reads it.
|
|
11157
|
+
*
|
|
11158
|
+
* This node keeps its own cache on purpose, and that left its textures
|
|
11159
|
+
* outside the one place `assetErrors()` looks — so a leaf URL that 404s was a
|
|
11160
|
+
* forest of bare branches with no message anywhere.
|
|
11161
|
+
*/
|
|
11162
|
+
noteTextureError = (url, error) => {
|
|
11163
|
+
this.assets?.textures.noteError(url, error);
|
|
11164
|
+
};
|
|
10870
11165
|
/** Re-grow only when a grove-shaping prop changed (the JSON IS the grove). */
|
|
10871
11166
|
rebuildIfNeeded() {
|
|
10872
11167
|
if (!this.grove) {
|
|
@@ -10975,7 +11270,7 @@ var Tree3D = class Tree3D extends Node3D {
|
|
|
10975
11270
|
if (loader) material.map = loadSharedTexture(loader, url, (texture) => {
|
|
10976
11271
|
texture.colorSpace = SRGBColorSpace;
|
|
10977
11272
|
texture.flipY = false;
|
|
10978
|
-
}, (texture) => greenMatte(texture));
|
|
11273
|
+
}, (texture) => greenMatte(texture), this.noteTextureError);
|
|
10979
11274
|
wireLeafWind(material, this.swayUniform, LEAF_ENV_CUT, this.leafFadeUniform ?? void 0);
|
|
10980
11275
|
leaves = new InstancedMesh(geometry.leaves, material, instances);
|
|
10981
11276
|
leaves.userData.incantoWaterPassSkip = true;
|
|
@@ -11040,7 +11335,7 @@ var Tree3D = class Tree3D extends Node3D {
|
|
|
11040
11335
|
texture.anisotropy = 4;
|
|
11041
11336
|
texture.flipY = false;
|
|
11042
11337
|
if (srgb) texture.colorSpace = SRGBColorSpace;
|
|
11043
|
-
});
|
|
11338
|
+
}, void 0, this.noteTextureError);
|
|
11044
11339
|
}
|
|
11045
11340
|
/** Per-instance placement + jitter — ONE rng draw order for both paths. */
|
|
11046
11341
|
scatter(rng, place) {
|
|
@@ -11098,7 +11393,7 @@ var Tree3D = class Tree3D extends Node3D {
|
|
|
11098
11393
|
* grove color VARIATION lives on the material tint, not the shared map.)
|
|
11099
11394
|
*/
|
|
11100
11395
|
const sharedTextures = /* @__PURE__ */ new Map();
|
|
11101
|
-
function loadSharedTexture(loader, url, configure, onReady) {
|
|
11396
|
+
function loadSharedTexture(loader, url, configure, onReady, onError) {
|
|
11102
11397
|
const cached = sharedTextures.get(url);
|
|
11103
11398
|
if (cached) return cached;
|
|
11104
11399
|
const texture = new Texture();
|
|
@@ -11107,7 +11402,7 @@ function loadSharedTexture(loader, url, configure, onReady) {
|
|
|
11107
11402
|
texture.image = bitmap;
|
|
11108
11403
|
if (onReady) onReady(texture);
|
|
11109
11404
|
else texture.needsUpdate = true;
|
|
11110
|
-
});
|
|
11405
|
+
}, void 0, (error) => onError?.(url, error));
|
|
11111
11406
|
sharedTextures.set(url, texture);
|
|
11112
11407
|
return texture;
|
|
11113
11408
|
}
|
|
@@ -12169,6 +12464,25 @@ var VoxelGrid3D = class extends Node3D {
|
|
|
12169
12464
|
this.mesh.count = i;
|
|
12170
12465
|
this.mesh.instanceMatrix.needsUpdate = true;
|
|
12171
12466
|
}
|
|
12467
|
+
/**
|
|
12468
|
+
* Hand the GPU back.
|
|
12469
|
+
*
|
|
12470
|
+
* Every other node that builds geometry disposes it here; these four did not,
|
|
12471
|
+
* so a scene swap left their buffers and materials resident and a game with
|
|
12472
|
+
* level transitions climbed until the tab died — with `stats().geometries` the
|
|
12473
|
+
* only witness, and only if someone was watching it.
|
|
12474
|
+
*/
|
|
12475
|
+
free() {
|
|
12476
|
+
const object = this._builtObject3D;
|
|
12477
|
+
if (object) object.traverse((child) => {
|
|
12478
|
+
const mesh = child;
|
|
12479
|
+
mesh.geometry?.dispose();
|
|
12480
|
+
const material = mesh.material;
|
|
12481
|
+
const list = Array.isArray(material) ? material : material ? [material] : [];
|
|
12482
|
+
for (const m of list) m.dispose?.();
|
|
12483
|
+
});
|
|
12484
|
+
super.free();
|
|
12485
|
+
}
|
|
12172
12486
|
};
|
|
12173
12487
|
const FACE_ATTRS = [
|
|
12174
12488
|
"colorFront",
|
|
@@ -12225,6 +12539,7 @@ function registerNodes3D() {
|
|
|
12225
12539
|
registerNode(MeshInstance3D);
|
|
12226
12540
|
registerNode(LoftMesh3D);
|
|
12227
12541
|
registerNode(Sprite3D);
|
|
12542
|
+
registerNode(Label3D);
|
|
12228
12543
|
registerNode(AnimatedSprite3D);
|
|
12229
12544
|
registerNode(Billboard3D);
|
|
12230
12545
|
registerNode(CharacterController3D);
|
|
@@ -5242,6 +5242,16 @@ var Cooldown = class {
|
|
|
5242
5242
|
}
|
|
5243
5243
|
};
|
|
5244
5244
|
const SHAKE_FALLOFF_SECONDS = .28;
|
|
5245
|
+
/** File the effect with the engine, if this one is attached to a running game. */
|
|
5246
|
+
function recordEffect(engine, kind, name, from, amount) {
|
|
5247
|
+
engine?.effects.record({
|
|
5248
|
+
kind,
|
|
5249
|
+
name,
|
|
5250
|
+
from,
|
|
5251
|
+
amount,
|
|
5252
|
+
at: engine.time
|
|
5253
|
+
});
|
|
5254
|
+
}
|
|
5245
5255
|
/**
|
|
5246
5256
|
* Camera shake as a standalone behavior for cameras WITHOUT another script.
|
|
5247
5257
|
* (FollowCamera has this built in — one script per node.) Composes with any
|
|
@@ -5273,6 +5283,7 @@ var CameraShake = class extends Behavior {
|
|
|
5273
5283
|
if (this.engine?.settings.get("reduceMotion") === true) return;
|
|
5274
5284
|
this.magnitude = Math.max(this.magnitude, magnitude);
|
|
5275
5285
|
this.t = seconds ?? this.falloff;
|
|
5286
|
+
recordEffect(this.engine, "shake", "", this.node?.getPath() ?? "engine", magnitude);
|
|
5276
5287
|
}
|
|
5277
5288
|
update(dt) {
|
|
5278
5289
|
const node = requirePosition(this);
|
|
@@ -5300,9 +5311,15 @@ var CameraShake = class extends Behavior {
|
|
|
5300
5311
|
};
|
|
5301
5312
|
/**
|
|
5302
5313
|
* Full-screen color flash (damage red, pickup white). DOM overlay above
|
|
5303
|
-
* everything; headless
|
|
5314
|
+
* everything; headless it draws nothing but still REPORTS, so a damage flash
|
|
5315
|
+
* can be checked in a run with no screen (`engine.effects.countOf('#ff0000')`).
|
|
5316
|
+
*
|
|
5317
|
+
* The engine is the first argument because it is the only juice primitive that
|
|
5318
|
+
* did not take one — which is also why nothing in the repo called it, and why a
|
|
5319
|
+
* flash was the one piece of feedback no test could see.
|
|
5304
5320
|
*/
|
|
5305
|
-
function screenFlash(color = "#ffffff", opacity = .35, seconds = .25) {
|
|
5321
|
+
function screenFlash(engine, color = "#ffffff", opacity = .35, seconds = .25) {
|
|
5322
|
+
recordEffect(engine, "flash", color, "engine", opacity);
|
|
5306
5323
|
if (typeof document === "undefined") return;
|
|
5307
5324
|
const id = "incanto-screen-flash";
|
|
5308
5325
|
let el = document.getElementById(id);
|
|
@@ -5327,36 +5344,28 @@ function screenFlash(color = "#ffffff", opacity = .35, seconds = .25) {
|
|
|
5327
5344
|
* previous timeScale. Stacking calls extend the freeze instead of fighting.
|
|
5328
5345
|
*/
|
|
5329
5346
|
function hitStop(engine, seconds = .08) {
|
|
5347
|
+
recordEffect(engine, "hitstop", "", "engine", seconds);
|
|
5330
5348
|
const state = hitStops.get(engine);
|
|
5331
5349
|
if (state) {
|
|
5332
|
-
state.until = Math.max(state.until,
|
|
5350
|
+
state.until = Math.max(state.until, engine.unscaledTime + seconds);
|
|
5333
5351
|
return;
|
|
5334
5352
|
}
|
|
5335
5353
|
const restore = engine.timeScale;
|
|
5336
5354
|
const entry = {
|
|
5337
|
-
until:
|
|
5338
|
-
restore
|
|
5355
|
+
until: engine.unscaledTime + seconds,
|
|
5356
|
+
restore,
|
|
5357
|
+
release: () => {}
|
|
5339
5358
|
};
|
|
5340
5359
|
hitStops.set(engine, entry);
|
|
5341
5360
|
engine.timeScale = 0;
|
|
5342
|
-
|
|
5343
|
-
if (
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
schedule(pump);
|
|
5349
|
-
};
|
|
5350
|
-
schedule(pump);
|
|
5361
|
+
entry.release = engine.updated.connect(() => {
|
|
5362
|
+
if (engine.unscaledTime < entry.until) return;
|
|
5363
|
+
engine.timeScale = entry.restore;
|
|
5364
|
+
entry.release();
|
|
5365
|
+
hitStops.delete(engine);
|
|
5366
|
+
});
|
|
5351
5367
|
}
|
|
5352
5368
|
const hitStops = /* @__PURE__ */ new Map();
|
|
5353
|
-
function realNow() {
|
|
5354
|
-
return typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
5355
|
-
}
|
|
5356
|
-
function schedule(fn) {
|
|
5357
|
-
if (typeof requestAnimationFrame === "function") requestAnimationFrame(fn);
|
|
5358
|
-
else setTimeout(fn, 16);
|
|
5359
|
-
}
|
|
5360
5369
|
//#endregion
|
|
5361
5370
|
//#region src/gameplay/lifetime.ts
|
|
5362
5371
|
/**
|
package/dist/gameplay.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { At as Node, b as Engine, d as PropSchema, n as BehaviorCtor, t as Behavior } from "./behavior-CyQoSu4n.js";
|
|
2
2
|
import { c as JsonValue, s as JsonObject } from "./schema-CFeioQRE.js";
|
|
3
3
|
|
|
4
4
|
//#region src/gameplay/chase.d.ts
|
|
@@ -813,9 +813,14 @@ declare class CameraShake extends Behavior {
|
|
|
813
813
|
}
|
|
814
814
|
/**
|
|
815
815
|
* Full-screen color flash (damage red, pickup white). DOM overlay above
|
|
816
|
-
* everything; headless
|
|
816
|
+
* everything; headless it draws nothing but still REPORTS, so a damage flash
|
|
817
|
+
* can be checked in a run with no screen (`engine.effects.countOf('#ff0000')`).
|
|
818
|
+
*
|
|
819
|
+
* The engine is the first argument because it is the only juice primitive that
|
|
820
|
+
* did not take one — which is also why nothing in the repo called it, and why a
|
|
821
|
+
* flash was the one piece of feedback no test could see.
|
|
817
822
|
*/
|
|
818
|
-
declare function screenFlash(color?: string, opacity?: number, seconds?: number): void;
|
|
823
|
+
declare function screenFlash(engine: Engine, color?: string, opacity?: number, seconds?: number): void;
|
|
819
824
|
/**
|
|
820
825
|
* Hit-stop: freeze game time for `seconds` of REAL time, then restore the
|
|
821
826
|
* previous timeScale. Stacking calls extend the freeze instead of fighting.
|
package/dist/gameplay.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as Chase, C as restartScene, D as DamageOnContact, E as phaseOf, O as Health, S as goToScene, T as DayNight, _ 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, k as Collector, 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-
|
|
1
|
+
import { A as Chase, C as restartScene, D as DamageOnContact, E as phaseOf, O as Health, S as goToScene, T as DayNight, _ 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, k as Collector, 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-CZ2yq37J.js";
|
|
2
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 };
|