incanto 0.47.0 → 0.49.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-frame.mjs +58 -1
- package/bin/incanto-logs.mjs +127 -0
- package/bin/incanto-model.mjs +13 -1
- package/bin/incanto-playtest.mjs +7 -0
- package/bin/incanto-verify.mjs +41 -2
- package/dist/2d.d.ts +21 -12
- package/dist/2d.js +57 -3
- package/dist/3d.d.ts +38 -1
- package/dist/3d.js +114 -4
- package/dist/{create-game-CniOiWzN.js → create-game-BgV6UbVA.js} +5 -5
- package/dist/{create-game-D16MVIPO.js → create-game-DFBjMetZ.js} +83 -6
- package/dist/{duplicate-CRtihGmC.js → duplicate-CI9WF_bg.js} +1 -1
- package/dist/{environment-presets-D1b0ydTS.js → environment-presets-CZOH5TY5.js} +15 -20
- package/dist/{gameplay-BQOeAid6.js → gameplay-02Btmmjn.js} +80 -9
- package/dist/gameplay.d.ts +17 -0
- package/dist/gameplay.js +1 -1
- package/dist/index.d.ts +139 -1
- package/dist/index.js +8 -7
- package/dist/{loader-r49nDwB4.js → loader-DwazzlQb.js} +36 -6
- package/dist/log-report-lxrQY9cH.js +0 -0
- package/dist/net.js +3 -3
- package/dist/{physics-2d-BmgXBNDB.js → physics-2d-vyCBfACH.js} +3 -3
- package/dist/{physics-3d-CSoGjM8P.js → physics-3d-DpRqw8Mz.js} +4 -4
- package/dist/react.js +1 -1
- package/dist/{register-R2JTnIMw.js → register-BNPZYJmd.js} +23 -22
- package/dist/{register-D651it1J.js → register-CB11yp21.js} +2 -2
- package/dist/{register-BSXV8T9F.js → register-uvaZj1KX.js} +62 -2
- package/dist/{replay-DilbZgQI.js → replay-C0XJIsO7.js} +1 -1
- package/dist/sprite-animation-CMr6f1K2.d.ts +44 -0
- package/dist/{particle-sim-Bw7hB93B.js → sprite-animation-D_p28jwU.js} +63 -1
- package/dist/{src-Ca3oV1fe.js → src-DF4gCsqO.js} +1 -1
- package/dist/{test-E4-otKqK.js → test-DRna_BQU.js} +210 -30
- package/dist/test.d.ts +38 -6
- package/dist/test.js +2 -2
- package/dist/vite.js +41 -4
- package/editor/assets/{agent8-_007gPF8.js → agent8-DCW4TgDt.js} +1 -1
- package/editor/assets/{debug-0DI_MJaq.js → debug-RC6qts6S.js} +1 -1
- package/editor/assets/{index-B-6eYZEi.js → index-5dEIhvsf.js} +92 -92
- package/editor/index.html +1 -1
- package/package.json +3 -2
- package/schemas/scene.schema.json +10 -3
- package/skills/incanto-3d-character.md +13 -2
- package/skills/incanto-3d-models.md +40 -0
- package/skills/incanto-assets.md +15 -0
- package/skills/incanto-building-2d-games.md +57 -5
- package/skills/incanto-building-3d-games.md +7 -0
- package/skills/incanto-gameplay-behaviors.md +18 -1
- package/skills/incanto-hud.md +39 -0
- package/skills/incanto-node-reference.md +5 -3
- package/skills/incanto-physics-and-input.md +1 -1
- package/skills/incanto-playtesting.md +9 -2
- package/skills/incanto-verifying-your-game.md +67 -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/dist/3d.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { A as Water3D, F as PhysicsBody3D, I as RigidBody3D, L as StaticBody3D, M as WaterCutout3D, N as Area3D, P as CharacterBody3D, R as Node3D, V as WATER_MAX_RIPPLES, j as WATER_CUTOUT_MAX } from "./gameplay-
|
|
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 { A as Water3D, F as PhysicsBody3D, I as RigidBody3D, L as StaticBody3D, M as WaterCutout3D, N as Area3D, P as CharacterBody3D, R as Node3D, V as WATER_MAX_RIPPLES, j as WATER_CUTOUT_MAX } from "./gameplay-02Btmmjn.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-CZOH5TY5.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-DFBjMetZ.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-DpRqw8Mz.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;
|
|
@@ -25,6 +25,115 @@ function rigOf(facts) {
|
|
|
25
25
|
if (facts.nodes.some((n) => MIXAMO.test(n.name))) return "mixamorig";
|
|
26
26
|
return facts.skins > 0 ? "other" : "none";
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* The locomotion clips every mixamorig character can play as they are.
|
|
30
|
+
*
|
|
31
|
+
* They are not art you go looking for — they are the rig's vocabulary, they
|
|
32
|
+
* retarget onto anything mixamo-shaped (and through the humanoid map onto a
|
|
33
|
+
* VRM), and this repo's own examples reference them 29 times. Nothing shipped
|
|
34
|
+
* ever told an agent where they live, so a character found through an asset MCP
|
|
35
|
+
* arrived with no way to walk.
|
|
36
|
+
*
|
|
37
|
+
* Keyed by the states `CharacterController3D` emits, so the map below IS the
|
|
38
|
+
* controller's `animations` prop.
|
|
39
|
+
*/
|
|
40
|
+
const CLIP_BASE = "https://agent8-games.verse8.io/assets/3d/animations/mixamorig";
|
|
41
|
+
const LOCOMOTION_CLIPS = {
|
|
42
|
+
idle: `${CLIP_BASE}/idle-00.glb`,
|
|
43
|
+
walk: `${CLIP_BASE}/walk.glb`,
|
|
44
|
+
run: `${CLIP_BASE}/run-medium.glb`,
|
|
45
|
+
fastRun: `${CLIP_BASE}/run-fast.glb`,
|
|
46
|
+
airborne: `${CLIP_BASE}/jump.glb`
|
|
47
|
+
};
|
|
48
|
+
/** The capsule the shipped templates use, and the skin fitted to it. */
|
|
49
|
+
const BODY = {
|
|
50
|
+
radius: .34,
|
|
51
|
+
height: 1
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* A found character, as a thing that walks.
|
|
55
|
+
*
|
|
56
|
+
* The model alone draws a statue: `CharacterController3D` must sit under a
|
|
57
|
+
* dynamic body, the skin has to be sized to the capsule and dropped to its
|
|
58
|
+
* feet, the clips have to be declared, and the input actions have to exist or
|
|
59
|
+
* nothing reads the keyboard. Every one of those is a documented step and all
|
|
60
|
+
* of them are needed at once, which is exactly the shape of thing worth
|
|
61
|
+
* printing rather than describing.
|
|
62
|
+
*/
|
|
63
|
+
function characterJson(target) {
|
|
64
|
+
const assets = { [target.key]: {
|
|
65
|
+
type: "model",
|
|
66
|
+
url: target.url
|
|
67
|
+
} };
|
|
68
|
+
const animations = {};
|
|
69
|
+
for (const [state, url] of Object.entries(LOCOMOTION_CLIPS)) {
|
|
70
|
+
const key = `anims/${state}`;
|
|
71
|
+
assets[key] = {
|
|
72
|
+
type: "animation",
|
|
73
|
+
url
|
|
74
|
+
};
|
|
75
|
+
animations[state] = `$${key}`;
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
assets,
|
|
79
|
+
input: {
|
|
80
|
+
move: {
|
|
81
|
+
type: "vector2",
|
|
82
|
+
keys: {
|
|
83
|
+
up: ["KeyW"],
|
|
84
|
+
down: ["KeyS"],
|
|
85
|
+
left: ["KeyA"],
|
|
86
|
+
right: ["KeyD"]
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
jump: {
|
|
90
|
+
type: "button",
|
|
91
|
+
keys: ["Space"]
|
|
92
|
+
},
|
|
93
|
+
sprint: {
|
|
94
|
+
type: "button",
|
|
95
|
+
keys: ["ShiftLeft"]
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
node: {
|
|
99
|
+
name: "Player",
|
|
100
|
+
type: "RigidBody3D",
|
|
101
|
+
groups: ["player"],
|
|
102
|
+
props: {
|
|
103
|
+
fixedRotation: true,
|
|
104
|
+
friction: 0,
|
|
105
|
+
collider: {
|
|
106
|
+
shape: "capsule",
|
|
107
|
+
radius: BODY.radius,
|
|
108
|
+
height: BODY.height
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
children: [{
|
|
112
|
+
name: "Controller",
|
|
113
|
+
type: "CharacterController3D",
|
|
114
|
+
props: {
|
|
115
|
+
view: "free",
|
|
116
|
+
coyoteSeconds: .12,
|
|
117
|
+
jumpBufferSeconds: .15,
|
|
118
|
+
animations
|
|
119
|
+
}
|
|
120
|
+
}, {
|
|
121
|
+
name: "Skin",
|
|
122
|
+
type: "ModelInstance3D",
|
|
123
|
+
props: {
|
|
124
|
+
model: `$${target.key}`,
|
|
125
|
+
targetHeight: BODY.height + 2 * BODY.radius,
|
|
126
|
+
position: [
|
|
127
|
+
0,
|
|
128
|
+
-(BODY.height / 2 + BODY.radius),
|
|
129
|
+
0
|
|
130
|
+
],
|
|
131
|
+
castShadow: true
|
|
132
|
+
}
|
|
133
|
+
}]
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
28
137
|
function modelVerdict(facts, target) {
|
|
29
138
|
const rig = rigOf(facts);
|
|
30
139
|
const kind = facts.meshes === 0 && facts.animations.length > 0 ? "animation" : "model";
|
|
@@ -51,6 +160,7 @@ function modelVerdict(facts, target) {
|
|
|
51
160
|
originOffset: offset,
|
|
52
161
|
groundedAtOrigin: grounded,
|
|
53
162
|
embeddedClips: facts.animations.map((a) => a.name),
|
|
163
|
+
characterJson: target && kind === "model" && (rig === "mixamorig" || rig === "vrm-humanoid") ? characterJson(target) : null,
|
|
54
164
|
sceneJson: target ? {
|
|
55
165
|
assets: { [target.key]: {
|
|
56
166
|
type: kind,
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import { n as loadScene, o as computeViewport, s as resolveViewport, v as diagnose, w as registerBehavior } from "./loader-
|
|
3
|
-
import { h as Engine, m as AudioPlayer } from "./register-
|
|
2
|
+
import { n as loadScene, o as computeViewport, s as resolveViewport, v as diagnose, w as registerBehavior } from "./loader-DwazzlQb.js";
|
|
3
|
+
import { h as Engine, m as AudioPlayer } from "./register-uvaZj1KX.js";
|
|
4
4
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
5
5
|
import { i as resolveRendering, n as attachTouchControls } from "./touch-BoNg_MnF.js";
|
|
6
6
|
import { a as openBundledEditor, i as devServerLibrary, n as pauseWhenHidden, r as crossFade, t as teardown } from "./teardown-BKTCzLek.js";
|
|
7
|
-
import { n as registerGameplayBehaviors } from "./gameplay-
|
|
8
|
-
import { g as PhysicsBody2D, n as UILayer, t as registerNodes2D, u as Camera2D, y as Node2D } from "./register-
|
|
7
|
+
import { n as registerGameplayBehaviors } from "./gameplay-02Btmmjn.js";
|
|
8
|
+
import { g as PhysicsBody2D, n as UILayer, t as registerNodes2D, u as Camera2D, y as Node2D } from "./register-BNPZYJmd.js";
|
|
9
9
|
import { t as debugSources } from "./debug-draw-BM3DsvtT.js";
|
|
10
|
-
import { n as enablePhysics2D } from "./physics-2d-
|
|
10
|
+
import { n as enablePhysics2D } from "./physics-2d-vyCBfACH.js";
|
|
11
11
|
import { Box3, BufferAttribute, BufferGeometry, Color, LineBasicMaterial, LineSegments, LinearFilter, NearestFilter, OrthographicCamera, Raycaster, SRGBColorSpace, Scene, TextureLoader, Vector2, Vector3, WebGLRenderer } from "three";
|
|
12
12
|
//#region src/2d/assets.ts
|
|
13
13
|
/**
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import { n as loadScene, v as diagnose, w as registerBehavior } from "./loader-
|
|
3
|
-
import { _ as qualityCaps, h as Engine, m as AudioPlayer, y as qualityRendering } from "./register-
|
|
2
|
+
import { n as loadScene, v as diagnose, w as registerBehavior } from "./loader-DwazzlQb.js";
|
|
3
|
+
import { _ as qualityCaps, h as Engine, m as AudioPlayer, y as qualityRendering } from "./register-uvaZj1KX.js";
|
|
4
4
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
5
|
+
import { r as parseDrive, t as logReport } from "./log-report-lxrQY9cH.js";
|
|
5
6
|
import { i as resolveRendering, n as attachTouchControls } from "./touch-BoNg_MnF.js";
|
|
6
7
|
import { a as openBundledEditor, i as devServerLibrary, n as pauseWhenHidden, o as poseFromRenderer, r as crossFade, t as teardown } from "./teardown-BKTCzLek.js";
|
|
7
|
-
import { B as createCausticsQuad, F as PhysicsBody3D, R as Node3D, n as registerGameplayBehaviors } from "./gameplay-
|
|
8
|
+
import { B as createCausticsQuad, F as PhysicsBody3D, R as Node3D, n as registerGameplayBehaviors } from "./gameplay-02Btmmjn.js";
|
|
8
9
|
import { t as debugSources } from "./debug-draw-BM3DsvtT.js";
|
|
9
|
-
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-CZOH5TY5.js";
|
|
10
11
|
import { a as frameSignature, i as frameImage, o as frameStats } from "./frame-report-njybhZon.js";
|
|
11
|
-
import { n as enablePhysics3D } from "./physics-3d-
|
|
12
|
+
import { n as enablePhysics3D } from "./physics-3d-DpRqw8Mz.js";
|
|
12
13
|
import { ACESFilmicToneMapping, AmbientLight, Box3, BufferAttribute, BufferGeometry, Color, DepthTexture, EquirectangularReflectionMapping, FloatType, Fog, HalfFloatType, LineBasicMaterial, LineSegments, Matrix4, Mesh, PCFShadowMap, PMREMGenerator, PerspectiveCamera, PlaneGeometry, Quaternion, Raycaster, Scene, ShaderMaterial, Vector2, Vector3, WebGLRenderTarget, WebGLRenderer } from "three";
|
|
13
14
|
import { VRMLoaderPlugin, VRMUtils } from "@pixiv/three-vrm";
|
|
14
15
|
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
|
|
@@ -2455,17 +2456,69 @@ async function createGame3D(opts) {
|
|
|
2455
2456
|
if (!capture) throw new IncantoError("TREE_VIOLATION", "this renderer cannot capture frames");
|
|
2456
2457
|
return capture.call(renderer);
|
|
2457
2458
|
};
|
|
2459
|
+
/**
|
|
2460
|
+
* Run a drive script against the LIVE game, in real time.
|
|
2461
|
+
*
|
|
2462
|
+
* `step` waits on the wall clock rather than advancing a fixed timestep: this
|
|
2463
|
+
* is the running game, with its own loop, its own physics and its own
|
|
2464
|
+
* animation. Stepping it by hand would fight all three.
|
|
2465
|
+
*/
|
|
2466
|
+
const applyDrive = async (steps) => {
|
|
2467
|
+
let frames = 0;
|
|
2468
|
+
let watching = true;
|
|
2469
|
+
const tick = () => {
|
|
2470
|
+
if (!watching) return;
|
|
2471
|
+
frames += 1;
|
|
2472
|
+
requestAnimationFrame(tick);
|
|
2473
|
+
};
|
|
2474
|
+
requestAnimationFrame(tick);
|
|
2475
|
+
for (const s of steps) switch (s.kind) {
|
|
2476
|
+
case "press":
|
|
2477
|
+
engine.input.pressAction(s.action);
|
|
2478
|
+
break;
|
|
2479
|
+
case "release":
|
|
2480
|
+
engine.input.releaseAction(s.action);
|
|
2481
|
+
break;
|
|
2482
|
+
case "vector":
|
|
2483
|
+
engine.input.setActionVector(s.action, s.x, s.y);
|
|
2484
|
+
break;
|
|
2485
|
+
case "key":
|
|
2486
|
+
engine.input.handleKey(s.code, s.down);
|
|
2487
|
+
break;
|
|
2488
|
+
case "pointer":
|
|
2489
|
+
engine.input.handlePointerMove(s.dx, s.dy);
|
|
2490
|
+
break;
|
|
2491
|
+
case "step":
|
|
2492
|
+
await new Promise((r) => setTimeout(r, s.ms));
|
|
2493
|
+
break;
|
|
2494
|
+
}
|
|
2495
|
+
watching = false;
|
|
2496
|
+
return frames;
|
|
2497
|
+
};
|
|
2458
2498
|
const hot = import.meta.hot;
|
|
2459
2499
|
if (hot) {
|
|
2460
2500
|
const onFrameRequest = (data) => {
|
|
2461
2501
|
const request = data;
|
|
2462
2502
|
const id = request?.id;
|
|
2463
2503
|
hot.send("incanto:frame-ack", { id });
|
|
2464
|
-
|
|
2504
|
+
const drive = request?.drive ? parseDrive(request.drive) : null;
|
|
2505
|
+
if (drive?.error) {
|
|
2506
|
+
hot.send("incanto:frame-report", {
|
|
2507
|
+
id,
|
|
2508
|
+
report: { error: drive.error }
|
|
2509
|
+
});
|
|
2510
|
+
return;
|
|
2511
|
+
}
|
|
2512
|
+
let droveFrames = null;
|
|
2513
|
+
(drive ? applyDrive(drive.steps) : Promise.resolve(null)).then((frames) => {
|
|
2514
|
+
droveFrames = frames;
|
|
2515
|
+
return captureFramePixels();
|
|
2516
|
+
}).then((shot) => hot.send("incanto:frame-report", {
|
|
2465
2517
|
id,
|
|
2466
2518
|
report: {
|
|
2467
2519
|
...frameStats(shot.pixels, shot.width, shot.height, { grid: request?.grid }),
|
|
2468
2520
|
signature: frameSignature(shot.pixels, shot.width, shot.height),
|
|
2521
|
+
...droveFrames === null ? {} : { droveFrames },
|
|
2469
2522
|
...request?.image ? { image: frameImage(shot.pixels, shot.width, shot.height, (w, h) => Object.assign(document.createElement("canvas"), {
|
|
2470
2523
|
width: w,
|
|
2471
2524
|
height: h
|
|
@@ -2478,6 +2531,30 @@ async function createGame3D(opts) {
|
|
|
2478
2531
|
};
|
|
2479
2532
|
hot.on("incanto:frame-request", onFrameRequest);
|
|
2480
2533
|
cleanups.push(() => hot.off?.("incanto:frame-request", onFrameRequest));
|
|
2534
|
+
const onLogsRequest = (data) => {
|
|
2535
|
+
const id = data?.id;
|
|
2536
|
+
try {
|
|
2537
|
+
hot.send("incanto:logs-report", {
|
|
2538
|
+
id,
|
|
2539
|
+
report: logReport({
|
|
2540
|
+
entries: engine.log.entries(),
|
|
2541
|
+
stats: {
|
|
2542
|
+
...engine.stats(),
|
|
2543
|
+
...renderer.stats?.() ?? {}
|
|
2544
|
+
},
|
|
2545
|
+
assetErrors: renderer.assets?.errors() ?? [],
|
|
2546
|
+
hidden: typeof document !== "undefined" && document.hidden
|
|
2547
|
+
})
|
|
2548
|
+
});
|
|
2549
|
+
} catch (error) {
|
|
2550
|
+
hot.send("incanto:logs-report", {
|
|
2551
|
+
id,
|
|
2552
|
+
report: { error: error instanceof Error ? error.message : String(error) }
|
|
2553
|
+
});
|
|
2554
|
+
}
|
|
2555
|
+
};
|
|
2556
|
+
hot.on("incanto:logs-request", onLogsRequest);
|
|
2557
|
+
cleanups.push(() => hot.off?.("incanto:logs-request", onLogsRequest));
|
|
2481
2558
|
}
|
|
2482
2559
|
cleanups.push(pauseWhenHidden(engine));
|
|
2483
2560
|
await report(1, "ready");
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { t as registerCoreNodes } from "./register-
|
|
1
|
+
import { t as registerCoreNodes } from "./register-uvaZj1KX.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-IyWCGe4q.js";
|
|
5
5
|
import { t as createNoise2D } from "./noise-CGUMx44x.js";
|
|
6
|
-
import { i as
|
|
7
|
-
import { A as Water3D, F as PhysicsBody3D, H as colliderFootDrop, I as RigidBody3D, L as StaticBody3D, M as WaterCutout3D, N as Area3D, P as CharacterBody3D, R 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-D_p28jwU.js";
|
|
7
|
+
import { A as Water3D, F as PhysicsBody3D, H as colliderFootDrop, I as RigidBody3D, L as StaticBody3D, M as WaterCutout3D, N as Area3D, P as CharacterBody3D, R as Node3D } from "./gameplay-02Btmmjn.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";
|
|
@@ -2112,13 +2112,19 @@ var AnimatedSprite3D = class extends Sprite3D {
|
|
|
2112
2112
|
frameList = [];
|
|
2113
2113
|
frameIndex = 0;
|
|
2114
2114
|
frameTime = 0;
|
|
2115
|
+
currentDef = null;
|
|
2115
2116
|
sheetTexture = null;
|
|
2116
2117
|
loadedSheetRef = "";
|
|
2118
|
+
/**
|
|
2119
|
+
* `currentAnimation` reports the clip that is RUNNING, not the name asked
|
|
2120
|
+
* for: an alias is a rename, and a state machine that reads back `fall` while
|
|
2121
|
+
* the idle frames play is describing something that is not happening.
|
|
2122
|
+
*/
|
|
2117
2123
|
play(name) {
|
|
2118
|
-
const def = this.animations
|
|
2119
|
-
|
|
2120
|
-
this.
|
|
2121
|
-
this.frameList = resolveFrames(def.frames,
|
|
2124
|
+
const { name: resolved, def } = resolveAnimation(this.animations, name, this.name);
|
|
2125
|
+
this.currentAnimation = resolved;
|
|
2126
|
+
this.currentDef = def;
|
|
2127
|
+
this.frameList = resolveFrames(def.frames, resolved);
|
|
2122
2128
|
this.frameIndex = 0;
|
|
2123
2129
|
this.currentFrame = this.frameList[0] ?? 0;
|
|
2124
2130
|
this.frameTime = 0;
|
|
@@ -2128,11 +2134,12 @@ var AnimatedSprite3D = class extends Sprite3D {
|
|
|
2128
2134
|
this.playing = false;
|
|
2129
2135
|
}
|
|
2130
2136
|
onReady() {
|
|
2137
|
+
validateAnimationAliases(this.animations, this.name);
|
|
2131
2138
|
if (this.autoplay) this.play(this.autoplay);
|
|
2132
2139
|
}
|
|
2133
2140
|
update(dt) {
|
|
2134
2141
|
if (!this.playing || this.currentAnimation === "") return;
|
|
2135
|
-
const def = this.
|
|
2142
|
+
const def = this.currentDef;
|
|
2136
2143
|
if (!def || !Number.isFinite(def.fps) || def.fps <= 0) return;
|
|
2137
2144
|
const perFrame = 1 / def.fps;
|
|
2138
2145
|
this.frameTime += dt;
|
|
@@ -2173,18 +2180,6 @@ var AnimatedSprite3D = class extends Sprite3D {
|
|
|
2173
2180
|
super.free();
|
|
2174
2181
|
}
|
|
2175
2182
|
};
|
|
2176
|
-
function resolveFrames(frames, name) {
|
|
2177
|
-
if (!Array.isArray(frames) || frames.length === 0) throw new IncantoError("BAD_FORMAT", `Animation '${name}': "frames" must be a non-empty array.`);
|
|
2178
|
-
if (frames.length === 2) {
|
|
2179
|
-
const [start, end] = frames;
|
|
2180
|
-
if (end >= start) {
|
|
2181
|
-
const out = [];
|
|
2182
|
-
for (let f = start; f <= end; f++) out.push(f);
|
|
2183
|
-
return out;
|
|
2184
|
-
}
|
|
2185
|
-
}
|
|
2186
|
-
return [...frames];
|
|
2187
|
-
}
|
|
2188
2183
|
//#endregion
|
|
2189
2184
|
//#region src/3d/nodes/billboard-3d.ts
|
|
2190
2185
|
const _parentQ = new Quaternion();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { f as Node, h as effectiveOrder, n as loadScene, w as registerBehavior, y as Behavior } from "./loader-
|
|
1
|
+
import { f as Node, h as effectiveOrder, n as loadScene, w as registerBehavior, y as Behavior } from "./loader-DwazzlQb.js";
|
|
2
2
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
3
3
|
import { t as jsonClone } from "./json-BLk7H2Qa.js";
|
|
4
|
-
import { t as duplicateNode } from "./duplicate-
|
|
4
|
+
import { t as duplicateNode } from "./duplicate-CI9WF_bg.js";
|
|
5
5
|
import { Color, CubeCamera, DepthTexture, DoubleSide, Euler, FloatType, Frustum, HalfFloatType, LinearMipmapLinearFilter, MathUtils, Matrix4, Mesh, MeshDepthMaterial, Object3D, PerspectiveCamera, Plane, PlaneGeometry, Quaternion, ShaderMaterial, Sphere, Vector2, Vector3, Vector4, WebGLCubeRenderTarget, WebGLRenderTarget } from "three";
|
|
6
6
|
//#region src/3d/frustum.ts
|
|
7
7
|
const scratchFrustum = new Frustum();
|
|
@@ -2553,7 +2553,7 @@ var CharacterBody3D = class extends PhysicsBody3D {
|
|
|
2553
2553
|
0,
|
|
2554
2554
|
0
|
|
2555
2555
|
] },
|
|
2556
|
-
|
|
2556
|
+
stickToGround: { default: true },
|
|
2557
2557
|
slopeLimitDeg: { default: 45 }
|
|
2558
2558
|
};
|
|
2559
2559
|
/** m/s, y-up (up = +y). */
|
|
@@ -2562,9 +2562,30 @@ var CharacterBody3D = class extends PhysicsBody3D {
|
|
|
2562
2562
|
0,
|
|
2563
2563
|
0
|
|
2564
2564
|
];
|
|
2565
|
-
|
|
2565
|
+
/**
|
|
2566
|
+
* Hold the capsule against the surface while it moves, so walking down a
|
|
2567
|
+
* slope does not become a series of small falls.
|
|
2568
|
+
*
|
|
2569
|
+
* Named apart from `Node3D.snapToGround` (which PLACES a node on the ground
|
|
2570
|
+
* at load) because sharing the name silently broke both: the boolean shadowed
|
|
2571
|
+
* the placement prop, its `true` default made the placement pass teleport
|
|
2572
|
+
* every character body in a terrain scene, and a lift value was rejected
|
|
2573
|
+
* outright — so the only escape from the teleport also turned this off.
|
|
2574
|
+
*/
|
|
2575
|
+
stickToGround = true;
|
|
2566
2576
|
/** Max climbable slope angle. */
|
|
2567
2577
|
slopeLimitDeg = 45;
|
|
2578
|
+
/**
|
|
2579
|
+
* `snapToGround` on a character body is ambiguous, so it is refused.
|
|
2580
|
+
*
|
|
2581
|
+
* A boolean here used to mean the ground-stick, and as placement it means
|
|
2582
|
+
* "leave y alone" — which is already the default. Rather than pick one and
|
|
2583
|
+
* silently do the other thing to an existing scene, say which prop was meant.
|
|
2584
|
+
*/
|
|
2585
|
+
onReady() {
|
|
2586
|
+
if (typeof this.snapToGround === "boolean") throw new IncantoError("PROP_TYPE_MISMATCH", `'${this.getPath()}' sets snapToGround to a boolean. On a character body that is ambiguous: 'stickToGround' holds the capsule against the surface while it moves (default true), while 'snapToGround' PLACES the node on the ground at load and takes a lift in metres (e.g. 0.8) — omit it to leave y exactly as authored.`, { prop: "snapToGround" });
|
|
2587
|
+
super.onReady();
|
|
2588
|
+
}
|
|
2568
2589
|
/** @internal Updated by Physics3D.moveAndSlide. */
|
|
2569
2590
|
_grounded = false;
|
|
2570
2591
|
moveAndSlide() {
|
|
@@ -4553,7 +4574,8 @@ var Health = class extends Behavior {
|
|
|
4553
4574
|
static signals = [
|
|
4554
4575
|
"damaged",
|
|
4555
4576
|
"healed",
|
|
4556
|
-
"died"
|
|
4577
|
+
"died",
|
|
4578
|
+
"healthChanged"
|
|
4557
4579
|
];
|
|
4558
4580
|
max = 100;
|
|
4559
4581
|
regenPerSec = 0;
|
|
@@ -4602,13 +4624,29 @@ var Health = class extends Behavior {
|
|
|
4602
4624
|
this.setCurrent(0);
|
|
4603
4625
|
this.die();
|
|
4604
4626
|
}
|
|
4627
|
+
/**
|
|
4628
|
+
* The one funnel every change goes through — and therefore the one place a
|
|
4629
|
+
* HUD can be told about.
|
|
4630
|
+
*
|
|
4631
|
+
* `damaged`/`healed` both lead with the DELTA, which is the number a bar must
|
|
4632
|
+
* not show; wire either to `UiBar.setValue` and the bar paints the damage as
|
|
4633
|
+
* the health and looks like it works. Regeneration is worse: it moves
|
|
4634
|
+
* `current` through here and emits NEITHER, so a regen bar sat still while
|
|
4635
|
+
* the character healed. `healthChanged(current, max)` carries the max too, so
|
|
4636
|
+
* a raised ceiling needs no second wire.
|
|
4637
|
+
*
|
|
4638
|
+
* Silent when nothing moved: a bar that repaints on nothing is noise.
|
|
4639
|
+
*/
|
|
4605
4640
|
setCurrent(value) {
|
|
4606
|
-
|
|
4641
|
+
const next = Math.max(0, Math.min(this.max, value));
|
|
4642
|
+
if (next === this.current) return;
|
|
4643
|
+
this.current = next;
|
|
4644
|
+
this.emit("healthChanged", this.current, this.max);
|
|
4607
4645
|
}
|
|
4608
4646
|
die() {
|
|
4609
4647
|
if (this.dead) return;
|
|
4610
4648
|
this.dead = true;
|
|
4611
|
-
this.
|
|
4649
|
+
this.setCurrent(0);
|
|
4612
4650
|
this.emit("died");
|
|
4613
4651
|
if (this.freeOnDeath) this.node.queueFree();
|
|
4614
4652
|
}
|
|
@@ -5675,6 +5713,10 @@ var Patrol = class extends Behavior {
|
|
|
5675
5713
|
* On overlap with a node in `collectorGroup`, emits `collected(value, other)`
|
|
5676
5714
|
* then `queueFree()`s itself. Wire `collected → ScoreKeeper.addScore` (the
|
|
5677
5715
|
* value is the first arg) or `collected → Collector.collect`.
|
|
5716
|
+
*
|
|
5717
|
+
* `other` is the ENTITY, not whichever of its parts touched — see
|
|
5718
|
+
* {@link findCollector}. A player is a body with children (a reach sensor, a
|
|
5719
|
+
* hitbox), and the part that enters first is rarely the part carrying the group.
|
|
5678
5720
|
*/
|
|
5679
5721
|
var Pickup = class extends Behavior {
|
|
5680
5722
|
static props = {
|
|
@@ -5695,12 +5737,41 @@ var Pickup = class extends Behavior {
|
|
|
5695
5737
|
}
|
|
5696
5738
|
tryCollect(other) {
|
|
5697
5739
|
if (this.collectedAlready) return;
|
|
5698
|
-
|
|
5740
|
+
const collector = findCollector(other, this.collectorGroup);
|
|
5741
|
+
if (!collector) return;
|
|
5699
5742
|
this.collectedAlready = true;
|
|
5700
|
-
this.emit("collected", this.value,
|
|
5743
|
+
this.emit("collected", this.value, collector);
|
|
5701
5744
|
this.node.queueFree();
|
|
5702
5745
|
}
|
|
5703
5746
|
};
|
|
5747
|
+
/**
|
|
5748
|
+
* The entity in `group` that this contact belongs to, or null.
|
|
5749
|
+
*
|
|
5750
|
+
* The same search `DamageOnContact` has always used for `Health` — the
|
|
5751
|
+
* contacted node, its descendants, then the ancestor CHAIN — and for the same
|
|
5752
|
+
* reason. An entity is a body with children (a reach sensor, a hitbox, a skin),
|
|
5753
|
+
* the group sits on one node of it, and whichever part happens to touch first
|
|
5754
|
+
* is rarely that node.
|
|
5755
|
+
*
|
|
5756
|
+
* Matching the literal entering node was a silent failure in exactly the shape
|
|
5757
|
+
* the gameplay skill recommends: a player with a `Grab` area collected almost
|
|
5758
|
+
* nothing, because the sensor is wider than the body and entered first. Five
|
|
5759
|
+
* pickups fired `triggerEnter`; one fired `collected`.
|
|
5760
|
+
*
|
|
5761
|
+
* The ancestor climb deliberately does NOT descend into siblings: past a shared
|
|
5762
|
+
* parent every other entity hangs off it too, and a coin would be collected by
|
|
5763
|
+
* a crate standing next to the player.
|
|
5764
|
+
*/
|
|
5765
|
+
function findCollector(node, group) {
|
|
5766
|
+
const queue = [node];
|
|
5767
|
+
while (queue.length > 0) {
|
|
5768
|
+
const n = queue.shift();
|
|
5769
|
+
if (n.isInGroup(group)) return n;
|
|
5770
|
+
queue.push(...n.children);
|
|
5771
|
+
}
|
|
5772
|
+
for (let n = node.parent; n; n = n.parent) if (n.isInGroup(group)) return n;
|
|
5773
|
+
return null;
|
|
5774
|
+
}
|
|
5704
5775
|
//#endregion
|
|
5705
5776
|
//#region src/gameplay/projectile.ts
|
|
5706
5777
|
/**
|
package/dist/gameplay.d.ts
CHANGED
|
@@ -97,6 +97,19 @@ declare class Health extends Behavior {
|
|
|
97
97
|
heal(n: number): void;
|
|
98
98
|
/** Drop to 0 and die immediately (ignores i-frames). */
|
|
99
99
|
kill(): void;
|
|
100
|
+
/**
|
|
101
|
+
* The one funnel every change goes through — and therefore the one place a
|
|
102
|
+
* HUD can be told about.
|
|
103
|
+
*
|
|
104
|
+
* `damaged`/`healed` both lead with the DELTA, which is the number a bar must
|
|
105
|
+
* not show; wire either to `UiBar.setValue` and the bar paints the damage as
|
|
106
|
+
* the health and looks like it works. Regeneration is worse: it moves
|
|
107
|
+
* `current` through here and emits NEITHER, so a regen bar sat still while
|
|
108
|
+
* the character healed. `healthChanged(current, max)` carries the max too, so
|
|
109
|
+
* a raised ceiling needs no second wire.
|
|
110
|
+
*
|
|
111
|
+
* Silent when nothing moved: a bar that repaints on nothing is noise.
|
|
112
|
+
*/
|
|
100
113
|
private setCurrent;
|
|
101
114
|
private die;
|
|
102
115
|
/** Current HP and whether death already fired — `max` comes back from JSON. */
|
|
@@ -318,6 +331,10 @@ declare class Patrol extends Behavior {
|
|
|
318
331
|
* On overlap with a node in `collectorGroup`, emits `collected(value, other)`
|
|
319
332
|
* then `queueFree()`s itself. Wire `collected → ScoreKeeper.addScore` (the
|
|
320
333
|
* value is the first arg) or `collected → Collector.collect`.
|
|
334
|
+
*
|
|
335
|
+
* `other` is the ENTITY, not whichever of its parts touched — see
|
|
336
|
+
* {@link findCollector}. A player is a body with children (a reach sensor, a
|
|
337
|
+
* hitbox), and the part that enters first is rarely the part carrying the group.
|
|
321
338
|
*/
|
|
322
339
|
declare class Pickup extends Behavior {
|
|
323
340
|
static readonly props: PropSchema;
|
package/dist/gameplay.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as FollowCamera, D as Health, E as DamageOnContact, O as Collector, S as restartScene, T as phaseOf, _ as hitStop, a as Wander, b as GameFlow, c as Projectile, d as PathFollow, f as Oscillate, g as Cooldown, h as CameraShake, i as WaveSpawner, k as Chase, l as Pickup, m as Lifetime, n as registerGameplayBehaviors, o as Spawner, p as MoveTo, r as ZombieAI, s as ScoreKeeper, t as GAMEPLAY_BEHAVIORS, u as Patrol, v as screenFlash, w as DayNight, x as goToScene, y as Interactable } from "./gameplay-
|
|
1
|
+
import { C as FollowCamera, D as Health, E as DamageOnContact, O as Collector, S as restartScene, T as phaseOf, _ as hitStop, a as Wander, b as GameFlow, c as Projectile, d as PathFollow, f as Oscillate, g as Cooldown, h as CameraShake, i as WaveSpawner, k as Chase, l as Pickup, m as Lifetime, n as registerGameplayBehaviors, o as Spawner, p as MoveTo, r as ZombieAI, s as ScoreKeeper, t as GAMEPLAY_BEHAVIORS, u as Patrol, v as screenFlash, w as DayNight, x as goToScene, y as Interactable } from "./gameplay-02Btmmjn.js";
|
|
2
2
|
export { CameraShake, Chase, Collector, Cooldown, DamageOnContact, DayNight, FollowCamera, GAMEPLAY_BEHAVIORS, GameFlow, Health, Interactable, Lifetime, MoveTo, Oscillate, PathFollow, Patrol, Pickup, Projectile, ScoreKeeper, Spawner, Wander, WaveSpawner, ZombieAI, goToScene, hitStop, phaseOf, registerGameplayBehaviors, restartScene, screenFlash };
|