incanto 0.69.0 → 0.71.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/_behaviors-loader.mjs +22 -0
- package/bin/_read-json.mjs +28 -0
- package/bin/incanto-assets.mjs +5 -4
- package/bin/incanto-check.mjs +58 -31
- package/bin/incanto-editor.mjs +128 -5
- package/bin/incanto-env.mjs +3 -2
- package/bin/incanto-feel.mjs +24 -13
- package/bin/incanto-frame.mjs +8 -1
- package/bin/incanto-multiplay.mjs +11 -9
- package/bin/incanto-new.mjs +105 -10
- package/bin/incanto-play.mjs +158 -16
- package/bin/incanto-playtest.mjs +66 -23
- package/bin/incanto-skills.mjs +14 -2
- package/bin/incanto-verify.mjs +121 -102
- package/bin/incanto.mjs +3 -2
- package/dist/2d.d.ts +296 -34
- package/dist/2d.js +4 -4
- package/dist/3d.d.ts +157 -9
- package/dist/3d.js +8 -8
- package/dist/{agent8-CvsfVskX.js → agent8-Cfmd3ar_.js} +61 -8
- package/dist/{audio-player-C0j2k-yB.d.ts → audio-player-L8yccdMP.d.ts} +2 -2
- package/dist/{behavior-rm8vVzu_.d.ts → behavior-Dcz0fr1S.d.ts} +755 -46
- package/dist/{create-game-CwxEDlCm.js → create-game-CH4kt78v.js} +143 -60
- package/dist/{create-game-CvJ1wSRP.js → create-game-LCHBLfA4.js} +98 -48
- package/dist/debug.d.ts +1 -1
- package/dist/debug.js +2 -3
- package/dist/diagnostics-Cu85N3tL.d.ts +12 -0
- package/dist/{editor-switch-CnIOiyNJ.d.ts → editor-switch-CFU9mCec.d.ts} +22 -13
- package/dist/editor.js +1088 -882
- package/dist/env.d.ts +1 -1
- package/dist/env.js +5 -3
- package/dist/{environment-presets-BO-7XoFV.js → environment-presets-99BzP_L-.js} +298 -45
- package/dist/{frame-report-Dlq13Gyj.js → frame-report-D-_7YF2G.js} +1 -1
- package/dist/{gameplay-D1KixWK8.js → gameplay-CNULJvwh.js} +914 -214
- package/dist/gameplay.d.ts +254 -10
- package/dist/gameplay.js +2 -2
- package/dist/{heightmap-CRK0M4jT.js → heightmap-BYgD5Edk.js} +1 -1
- package/dist/index.d.ts +179 -13
- package/dist/index.js +10 -12
- package/dist/json-CfTjpvW8.js +67 -0
- package/dist/{loader-DadV2QCT.d.ts → loader-8-IHvbKD.d.ts} +2 -2
- package/dist/net.d.ts +27 -3
- package/dist/net.js +2 -2
- package/dist/{noise-CGUMx44x.js → noise-D3nPpmFg.js} +1 -1
- package/dist/{physics-2d-CF5yyjbj.js → physics-2d-FhrXUc6B.js} +136 -11
- package/dist/{physics-3d-DbYaEcZy.js → physics-3d-CFEGxBXZ.js} +104 -23
- package/dist/{teardown-CMqFVxO9.js → picking-DVo7fI13.js} +104 -13
- package/dist/react.d.ts +2 -2
- package/dist/react.js +2 -2
- package/dist/{register-DbVArsns.js → register-BYQCBySi.js} +429 -28
- package/dist/{register-mmfugpaQ.js → register-DuVdzxbq.js} +594 -831
- package/dist/{replay-BEHLDcQV.js → replay-DEvp3kyV.js} +224 -14
- package/dist/{replay-BQfkX4x7.d.ts → replay-F7IZHdFR.d.ts} +20 -3
- package/dist/{schema-B6ugCV1Q.d.ts → rng-Bb-IutXB.d.ts} +38 -21
- package/dist/{rng-DP-SR7eg.js → rng-CDOMybym.js} +22 -0
- package/dist/{loader-DZE7B07H.js → save-slots-Bvuh2p_r.js} +4619 -2318
- package/dist/{split-screen-Cg76MCV5.d.ts → split-screen-CIYf1zSf.d.ts} +36 -4
- package/dist/{split-screen-CauMwu6y.js → split-screen-eULetcg-.js} +159 -26
- package/dist/{sprite-animation-CY-mrr1L.js → sprite-animation-CqR2o3SA.js} +39 -8
- package/dist/{src-CYEu-_S9.js → src-_jk0qLsS.js} +2 -17
- package/dist/test-CmN54kWv.js +4264 -0
- package/dist/test.d.ts +639 -30
- package/dist/test.js +3 -3
- package/dist/touch-BnCyPA0G.js +519 -0
- package/dist/vite.d.ts +20 -37
- package/dist/vite.js +177 -7
- package/dist/{webgl-unavailable-Z5_g8x9H.js → webgl-unavailable-C8aDbGmR.js} +44 -0
- package/editor/assets/agent8-yLIEYHbd.js +1 -0
- package/editor/assets/{debug-CMULinBW.js → debug-u31w_yhq.js} +2 -2
- package/editor/assets/index-DK9xMGpW.js +11046 -0
- package/editor/index.html +1 -1
- package/package.json +4 -14
- package/schemas/scene.schema.json +34 -3
- package/skills/incanto-3d-character.md +14 -1
- package/skills/incanto-3d-models.md +12 -0
- package/skills/incanto-assets.md +9 -1
- package/skills/incanto-audio.md +8 -2
- package/skills/incanto-behaviors-and-scripts.md +105 -7
- package/skills/incanto-building-2d-games.md +97 -10
- package/skills/incanto-building-3d-games.md +58 -4
- package/skills/incanto-editor.md +46 -7
- package/skills/incanto-environment.md +19 -1
- package/skills/incanto-game-feel.md +70 -0
- package/skills/incanto-gameplay-behaviors.md +172 -20
- package/skills/incanto-hud.md +142 -7
- package/skills/incanto-localization.md +13 -5
- package/skills/incanto-multiplayer.md +83 -3
- package/skills/incanto-node-reference.md +239 -59
- package/skills/incanto-performance.md +52 -0
- package/skills/incanto-physics-and-input.md +123 -24
- package/skills/incanto-playtesting.md +112 -2
- package/skills/incanto-save-slots.md +217 -7
- package/skills/incanto-scene-json-authoring.md +69 -12
- package/skills/incanto-verifying-your-game.md +207 -10
- package/skills/incanto-web-integration.md +28 -0
- package/skills/incanto-your-first-game.md +3 -1
- package/templates-app/beacon-isle-3d/generate-world.ts +77 -9
- package/templates-app/beacon-isle-3d/package.json +2 -2
- package/templates-app/beacon-isle-3d/src/behaviors.ts +22 -0
- package/templates-app/beacon-isle-3d/src/game.scene.json +143 -600
- package/templates-app/beacon-isle-3d/src/main.ts +11 -1
- package/templates-app/beacon-isle-3d/tsconfig.json +1 -1
- package/templates-app/beacon-isle-3d/verify.ts +3 -1
- package/templates-app/molehill-2d/.incanto/playtest/lost-seed1.json +4277 -0
- package/templates-app/molehill-2d/PROJECT/Context.md +58 -0
- package/templates-app/molehill-2d/PROJECT/Requirements.md +39 -0
- package/templates-app/molehill-2d/PROJECT/Status.md +27 -0
- package/templates-app/molehill-2d/PROJECT/Structure.md +48 -0
- package/templates-app/molehill-2d/docs/project-2d-rules.md +44 -0
- package/templates-app/molehill-2d/index.html +73 -0
- package/templates-app/molehill-2d/package.json +23 -0
- package/templates-app/molehill-2d/src/behaviors.ts +198 -0
- package/templates-app/molehill-2d/src/game.scene.json +1205 -0
- package/templates-app/molehill-2d/src/main.ts +41 -0
- package/templates-app/molehill-2d/tsconfig.json +13 -0
- package/templates-app/molehill-2d/verify.ts +247 -0
- package/templates-app/molehill-2d/vite.config.ts +12 -0
- package/templates-app/platformer-2d/package.json +2 -2
- package/templates-app/platformer-2d/src/game.scene.json +178 -986
- package/templates-app/platformer-2d/src/main.ts +11 -1
- package/templates-app/platformer-2d/tsconfig.json +1 -1
- package/templates-app/star-survivor/package.json +2 -2
- package/templates-app/star-survivor/src/game.scene.json +43 -216
- package/templates-app/star-survivor/src/main.ts +11 -1
- package/templates-app/star-survivor/tsconfig.json +1 -1
- package/templates-app/tps-3d/PROJECT/Context.md +1 -1
- package/templates-app/tps-3d/package.json +2 -2
- package/templates-app/tps-3d/src/behaviors.ts +19 -1
- package/templates-app/tps-3d/src/game.scene.json +66 -263
- package/templates-app/tps-3d/src/main.ts +13 -2
- package/templates-app/tps-3d/tsconfig.json +1 -1
- package/templates-app/village-quest-3d/.incanto/playtest/swapped-seed1.json +1735 -0
- package/templates-app/village-quest-3d/package.json +2 -2
- package/templates-app/village-quest-3d/src/behaviors.ts +42 -4
- package/templates-app/village-quest-3d/src/grove.scene.json +68 -306
- package/templates-app/village-quest-3d/src/main.ts +11 -1
- package/templates-app/village-quest-3d/src/village.scene.json +202 -863
- package/templates-app/village-quest-3d/tsconfig.json +1 -1
- package/templates-app/village-quest-3d/verify.ts +14 -1
- package/dist/duplicate-Dj4L-5AA.js +0 -22
- package/dist/json-CwwhxQgb.js +0 -36
- package/dist/registry-CF70EArN.js +0 -212
- package/dist/rolldown-runtime-D7D4PA-g.js +0 -13
- package/dist/test-D6ISmuFE.js +0 -2383
- package/dist/touch-DEAmqGdf.js +0 -225
- package/editor/assets/agent8-BDvaZMQJ.js +0 -1
- package/editor/assets/index-CZIRr5EL.js +0 -10958
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { o as __exportAll } from "./json-CfTjpvW8.js";
|
|
2
|
+
import { Y as diagnose } from "./save-slots-Bvuh2p_r.js";
|
|
3
3
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
4
|
-
import { _ as RigidBody2D, b as validateCollider2D, g as PhysicsBody2D, h as CharacterBody2D, m as Area2D, p as Joint2D, y as Node2D } from "./register-
|
|
4
|
+
import { _ as RigidBody2D, b as validateCollider2D, g as PhysicsBody2D, h as CharacterBody2D, m as Area2D, p as Joint2D, y as Node2D } from "./register-BYQCBySi.js";
|
|
5
5
|
import { n as registerDebugSource } from "./debug-draw-BM3DsvtT.js";
|
|
6
6
|
import { n as jointContacts, t as withoutRapierInitNoise } from "./quiet-rapier-C6fW4zcW.js";
|
|
7
7
|
//#region src/2d/physics/physics-2d.ts
|
|
@@ -108,8 +108,10 @@ var Physics2D = class {
|
|
|
108
108
|
}
|
|
109
109
|
const lv = e.body.linvel();
|
|
110
110
|
node.linearVelocity = [lv.x * SCALE, lv.y * SCALE];
|
|
111
|
+
node.angularVelocity = e.body.angvel();
|
|
111
112
|
e.lastVX = node.linearVelocity[0] ?? 0;
|
|
112
113
|
e.lastVY = node.linearVelocity[1] ?? 0;
|
|
114
|
+
e.lastAV = node.angularVelocity;
|
|
113
115
|
}
|
|
114
116
|
const t2 = e.body.translation();
|
|
115
117
|
e.lastX = t2.x * SCALE;
|
|
@@ -130,6 +132,29 @@ var Physics2D = class {
|
|
|
130
132
|
}
|
|
131
133
|
});
|
|
132
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* May `candidate` carry `rider`?
|
|
137
|
+
*
|
|
138
|
+
* The rule used to be "never a CharacterBody2D", which is the whole CLASS —
|
|
139
|
+
* and a `CharacterBody2D` animated by POSITION WRITES is the documented
|
|
140
|
+
* moving-platform path (`Oscillate`, `MoveTo`, `PathFollow`, or a behaviour of
|
|
141
|
+
* your own). `incanto-physics-and-input.md` promises twice that a character
|
|
142
|
+
* standing on ANY moving body is carried; it was not, and the rider reported
|
|
143
|
+
* `isOnFloor() === true` the whole time while sliding off in world space.
|
|
144
|
+
*
|
|
145
|
+
* What the guard was really for is a CYCLE: two characters standing on each
|
|
146
|
+
* other would carry each other forever. So the test is the rider's own
|
|
147
|
+
* carrier chain, not the type — a platform that is not (transitively) riding
|
|
148
|
+
* you can carry you.
|
|
149
|
+
*/
|
|
150
|
+
canCarry(rider, candidate) {
|
|
151
|
+
let at = candidate;
|
|
152
|
+
for (let depth = 0; depth < 8 && at; depth++) {
|
|
153
|
+
if (at === rider) return false;
|
|
154
|
+
at = this.entries.get(at)?.ground ?? null;
|
|
155
|
+
}
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
133
158
|
/** @internal Called by CharacterBody2D.moveAndSlide (during tree fixedUpdate). */
|
|
134
159
|
moveAndSlide(node) {
|
|
135
160
|
const e = this.ensureEntry(node);
|
|
@@ -137,6 +162,8 @@ var Physics2D = class {
|
|
|
137
162
|
if (node.stickToGround && vy >= 0) this.kcc.enableSnapToGround(.05);
|
|
138
163
|
else this.kcc.disableSnapToGround();
|
|
139
164
|
this.kcc.setMaxSlopeClimbAngle(node.slopeLimitDeg * DEG);
|
|
165
|
+
if (node.stepHeight > 0) this.kcc.enableAutostep(node.stepHeight / SCALE, node.stepHeight / SCALE / 2, false);
|
|
166
|
+
else this.kcc.disableAutostep();
|
|
140
167
|
let carryX = 0;
|
|
141
168
|
let carryY = 0;
|
|
142
169
|
if (e.ground && e.ground._physics === this) {
|
|
@@ -144,9 +171,10 @@ var Physics2D = class {
|
|
|
144
171
|
carryX = gx - (e.groundX ?? gx);
|
|
145
172
|
carryY = gy - (e.groundY ?? gy);
|
|
146
173
|
} else e.ground = null;
|
|
174
|
+
const vyTerm = carryY < -.1 && node.isOnFloor?.() === true && vy > 0 ? 0 : vy * this.lastDt;
|
|
147
175
|
const desired = {
|
|
148
176
|
x: ((node.velocity[0] ?? 0) * this.lastDt + carryX) / SCALE * 1,
|
|
149
|
-
y: (
|
|
177
|
+
y: (vyTerm + carryY) / SCALE * 1
|
|
150
178
|
};
|
|
151
179
|
const charBottomPx = e.body.translation().y * SCALE + charHalfHeight(node);
|
|
152
180
|
const predicate = (collider) => {
|
|
@@ -171,7 +199,7 @@ var Physics2D = class {
|
|
|
171
199
|
for (let i = 0; i < hits; i++) {
|
|
172
200
|
const n = this.kcc.computedCollision(i)?.normal1;
|
|
173
201
|
if (!n) continue;
|
|
174
|
-
if (
|
|
202
|
+
if (n.y > .7) {
|
|
175
203
|
if (!node._grounded) node._onCeiling = true;
|
|
176
204
|
} else if (Math.abs(n.x) > .7) node._onWall = n.x > 0 ? -1 : 1;
|
|
177
205
|
}
|
|
@@ -179,7 +207,7 @@ var Physics2D = class {
|
|
|
179
207
|
const footX = (t.x + corrected.x) * SCALE;
|
|
180
208
|
const footY = (t.y + corrected.y) * SCALE + charHalfHeight(node) - 2;
|
|
181
209
|
const hit = this.castRay([footX, footY], [0, 1], 8, node);
|
|
182
|
-
if (hit?.node &&
|
|
210
|
+
if (hit?.node && this.canCarry(node, hit.node)) {
|
|
183
211
|
e.ground = hit.node;
|
|
184
212
|
const [gx, gy] = worldPosition2D(hit.node);
|
|
185
213
|
e.groundX = gx;
|
|
@@ -340,13 +368,48 @@ var Physics2D = class {
|
|
|
340
368
|
return [...set];
|
|
341
369
|
}
|
|
342
370
|
/**
|
|
343
|
-
*
|
|
371
|
+
* Mass the solver actually uses (collider-derived unless overridden).
|
|
372
|
+
*
|
|
373
|
+
* The 3D twin has had this since `CharacterController3D` needed it to size an
|
|
374
|
+
* impulse; 2D had no way to ask, so a 2D game scaling a push by mass had to
|
|
375
|
+
* read the AUTHORED `mass` prop and hope it was the number Rapier settled on.
|
|
376
|
+
*/
|
|
377
|
+
massOf(node) {
|
|
378
|
+
return this.entries.get(node)?.body.mass() ?? 1;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Current solver velocity in px/s (fresher than the node prop mid-step).
|
|
382
|
+
*
|
|
383
|
+
* `linearVelocity` is written back once per step, so a behavior reading it
|
|
384
|
+
* from inside `fixedUpdate` — after a collision, before the write-back — sees
|
|
385
|
+
* the value from BEFORE the impact. That is exactly when a game wants to know
|
|
386
|
+
* how hard it hit something.
|
|
387
|
+
*/
|
|
388
|
+
velocityOf(node) {
|
|
389
|
+
const v = this.entries.get(node)?.body.linvel();
|
|
390
|
+
return v ? [v.x * SCALE, v.y * SCALE] : [0, 0];
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* A world-space impulse in px.kg/s (y-down) — the 2D twin of
|
|
394
|
+
* `Physics3D.applyImpulse`, which the physics skill has documented for both
|
|
395
|
+
* adapters while only one of them had it.
|
|
396
|
+
*/
|
|
397
|
+
applyImpulse(node, impulse) {
|
|
398
|
+
this.entries.get(node)?.body.applyImpulse({
|
|
399
|
+
x: impulse[0] / SCALE,
|
|
400
|
+
y: impulse[1] / SCALE
|
|
401
|
+
}, true);
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* World-space raycast in PIXELS (y-down); `dir` may be any length (it is
|
|
405
|
+
* normalized), so `distance` and `maxLen` are pixels. `exclude` skips that body — a
|
|
344
406
|
* shooter probing from inside its own collider needs it. Sensors (Area2D)
|
|
345
407
|
* never block rays. Returns distance (px), the surface normal, and the hit
|
|
346
408
|
* body — or null.
|
|
347
409
|
*/
|
|
348
410
|
castRay(origin, dir, maxLen, exclude, opts) {
|
|
349
|
-
const len = Math.hypot(dir[0], dir[1])
|
|
411
|
+
const len = Math.hypot(dir[0], dir[1]);
|
|
412
|
+
if (!(len > 0)) return null;
|
|
350
413
|
const ray = new this.R.Ray({
|
|
351
414
|
x: origin[0] / SCALE,
|
|
352
415
|
y: origin[1] / SCALE
|
|
@@ -358,9 +421,44 @@ var Physics2D = class {
|
|
|
358
421
|
const flags = opts?.staticOnly ? this.R.QueryFilterFlags.EXCLUDE_DYNAMIC | this.R.QueryFilterFlags.EXCLUDE_KINEMATIC : void 0;
|
|
359
422
|
const hit = this.world.castRayAndGetNormal(ray, maxLen / SCALE, true, flags, void 0, excludeCollider, void 0, (collider) => !collider.isSensor());
|
|
360
423
|
if (!hit) return null;
|
|
424
|
+
const distance = hit.timeOfImpact * SCALE;
|
|
361
425
|
return {
|
|
362
|
-
distance
|
|
426
|
+
distance,
|
|
363
427
|
normal: [hit.normal.x, hit.normal.y],
|
|
428
|
+
node: this.byColliderHandle.get(hit.collider.handle) ?? null,
|
|
429
|
+
point: [origin[0] + dir[0] / len * distance, origin[1] + dir[1] / len * distance]
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Sweep a CIRCLE of `radius` from `origin` along `dir` — a thick ray, in
|
|
434
|
+
* pixels. The 2D twin of `Physics3D.castSphere`.
|
|
435
|
+
*
|
|
436
|
+
* 3D has had this since the camera spring arm needed it and 2D never got it,
|
|
437
|
+
* so the shared `PhysicsQuery` interface declares it OPTIONAL and every 2D
|
|
438
|
+
* game that wanted a probe with THICKNESS — a shot that must not thread a
|
|
439
|
+
* one-pixel gap between two tiles, a ledge feeler, a camera that should not
|
|
440
|
+
* clip a wall it skims — had to fire several thin rays and hope.
|
|
441
|
+
*
|
|
442
|
+
* Same filters as `castRay`: sensors never hit, `staticOnly` ignores dynamic
|
|
443
|
+
* and kinematic bodies, `dir` may be any length. Returns the travel distance
|
|
444
|
+
* of the circle's CENTRE, or null when the sweep is clear.
|
|
445
|
+
*/
|
|
446
|
+
castSphere(origin, dir, radius, maxLen, exclude, opts) {
|
|
447
|
+
const R = this.R;
|
|
448
|
+
const len = Math.hypot(dir[0], dir[1]);
|
|
449
|
+
if (!(len > 0)) return null;
|
|
450
|
+
const excludeEntry = exclude ? this.entries.get(exclude) : void 0;
|
|
451
|
+
const flags = opts?.staticOnly ? R.QueryFilterFlags.EXCLUDE_DYNAMIC | R.QueryFilterFlags.EXCLUDE_KINEMATIC : void 0;
|
|
452
|
+
const hit = this.world.castShape({
|
|
453
|
+
x: origin[0] / SCALE,
|
|
454
|
+
y: origin[1] / SCALE
|
|
455
|
+
}, 0, {
|
|
456
|
+
x: dir[0] / len,
|
|
457
|
+
y: dir[1] / len
|
|
458
|
+
}, new R.Ball(radius / SCALE), 0, maxLen / SCALE, true, flags, void 0, excludeEntry?.collider, excludeEntry?.body, (collider) => !collider.isSensor());
|
|
459
|
+
if (!hit) return null;
|
|
460
|
+
return {
|
|
461
|
+
distance: hit.time_of_impact * SCALE,
|
|
364
462
|
node: this.byColliderHandle.get(hit.collider.handle) ?? null
|
|
365
463
|
};
|
|
366
464
|
}
|
|
@@ -465,13 +563,15 @@ var Physics2D = class {
|
|
|
465
563
|
const x = wx / SCALE;
|
|
466
564
|
const y = wy / SCALE;
|
|
467
565
|
let desc;
|
|
468
|
-
|
|
566
|
+
const dyn = node instanceof RigidBody2D;
|
|
567
|
+
if (dyn) desc = R.RigidBodyDesc.dynamic();
|
|
469
568
|
else if (node instanceof CharacterBody2D || node instanceof Area2D) desc = R.RigidBodyDesc.kinematicPositionBased();
|
|
470
569
|
else desc = R.RigidBodyDesc.fixed();
|
|
471
570
|
desc.setTranslation(x, y).setRotation(node.rotation * DEG);
|
|
472
571
|
if (node instanceof RigidBody2D) {
|
|
473
572
|
desc.setGravityScale(node.gravityScale);
|
|
474
573
|
desc.setLinvel((node.linearVelocity[0] ?? 0) / SCALE, (node.linearVelocity[1] ?? 0) / SCALE);
|
|
574
|
+
desc.setAngvel(node.fixedRotation ? 0 : node.angularVelocity);
|
|
475
575
|
if (node.fixedRotation) desc.lockRotations();
|
|
476
576
|
}
|
|
477
577
|
const body = this.world.createRigidBody(desc);
|
|
@@ -488,7 +588,12 @@ var Physics2D = class {
|
|
|
488
588
|
lastY: wy,
|
|
489
589
|
lastRot: node.rotation,
|
|
490
590
|
lastVX: 0,
|
|
491
|
-
lastVY: 0
|
|
591
|
+
lastVY: 0,
|
|
592
|
+
lastAV: dyn ? node.angularVelocity : 0,
|
|
593
|
+
lastMass: dyn ? node.mass : 1,
|
|
594
|
+
lastFriction: dyn ? node.friction : .5,
|
|
595
|
+
lastRestitution: dyn ? node.restitution : 0,
|
|
596
|
+
lastGravityScale: dyn ? node.gravityScale : 1
|
|
492
597
|
};
|
|
493
598
|
this.entries.set(node, e);
|
|
494
599
|
this.byColliderHandle.set(collider.handle, node);
|
|
@@ -530,6 +635,26 @@ var Physics2D = class {
|
|
|
530
635
|
e.lastVX = vx;
|
|
531
636
|
e.lastVY = vy;
|
|
532
637
|
}
|
|
638
|
+
if (node.angularVelocity !== e.lastAV) {
|
|
639
|
+
e.body.setAngvel(node.angularVelocity, true);
|
|
640
|
+
e.lastAV = node.angularVelocity;
|
|
641
|
+
}
|
|
642
|
+
if (node.gravityScale !== e.lastGravityScale) {
|
|
643
|
+
e.body.setGravityScale(node.gravityScale, true);
|
|
644
|
+
e.lastGravityScale = node.gravityScale;
|
|
645
|
+
}
|
|
646
|
+
if (node.mass !== e.lastMass) {
|
|
647
|
+
e.collider.setMass(node.mass);
|
|
648
|
+
e.lastMass = node.mass;
|
|
649
|
+
}
|
|
650
|
+
if (node.friction !== e.lastFriction) {
|
|
651
|
+
e.collider.setFriction(node.friction);
|
|
652
|
+
e.lastFriction = node.friction;
|
|
653
|
+
}
|
|
654
|
+
if (node.restitution !== e.lastRestitution) {
|
|
655
|
+
e.collider.setRestitution(node.restitution);
|
|
656
|
+
e.lastRestitution = node.restitution;
|
|
657
|
+
}
|
|
533
658
|
}
|
|
534
659
|
return e;
|
|
535
660
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { o as __exportAll } from "./json-CfTjpvW8.js";
|
|
2
|
+
import { Y as diagnose } from "./save-slots-Bvuh2p_r.js";
|
|
3
3
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
4
|
-
import { H as CharacterBody3D, K as Node3D, U as PhysicsBody3D, V as Area3D, W as RigidBody3D, q as validateCollider3D } from "./gameplay-
|
|
4
|
+
import { H as CharacterBody3D, K as Node3D, U as PhysicsBody3D, V as Area3D, W as RigidBody3D, q as validateCollider3D } from "./gameplay-CNULJvwh.js";
|
|
5
5
|
import { n as registerDebugSource } from "./debug-draw-BM3DsvtT.js";
|
|
6
6
|
import { n as jointContacts, t as withoutRapierInitNoise } from "./quiet-rapier-C6fW4zcW.js";
|
|
7
|
-
import { A as Terrain3D, F as InstancedMesh3D, L as buildMeshGeometry, P as Joint3D } from "./environment-presets-
|
|
7
|
+
import { A as Terrain3D, F as InstancedMesh3D, L as buildMeshGeometry, P as Joint3D } from "./environment-presets-99BzP_L-.js";
|
|
8
8
|
import { Euler, Matrix4, Quaternion, Vector3 } from "three";
|
|
9
9
|
//#region src/3d/physics/collider-lines.ts
|
|
10
10
|
/**
|
|
@@ -386,6 +386,21 @@ var Physics3D = class {
|
|
|
386
386
|
this.world.propagateModifiedBodyPositionsToColliders();
|
|
387
387
|
}
|
|
388
388
|
/** @internal Called by CharacterBody3D.moveAndSlide (during tree fixedUpdate). */
|
|
389
|
+
/**
|
|
390
|
+
* May `candidate` carry `rider`? — see the 2D twin.
|
|
391
|
+
*
|
|
392
|
+
* "Never a CharacterBody3D" is the whole CLASS, and a CharacterBody3D
|
|
393
|
+
* animated by position writes is the documented moving-platform path. What
|
|
394
|
+
* the guard was really for is a carry CYCLE, so that is what it tests.
|
|
395
|
+
*/
|
|
396
|
+
canCarry(rider, candidate) {
|
|
397
|
+
let at = candidate;
|
|
398
|
+
for (let depth = 0; depth < 8 && at; depth++) {
|
|
399
|
+
if (at === rider) return false;
|
|
400
|
+
at = this.entries.get(at)?.ground ?? null;
|
|
401
|
+
}
|
|
402
|
+
return true;
|
|
403
|
+
}
|
|
389
404
|
moveAndSlide(node) {
|
|
390
405
|
const e = this.ensureEntry(node);
|
|
391
406
|
if (!e) return;
|
|
@@ -421,6 +436,20 @@ var Physics3D = class {
|
|
|
421
436
|
z: nz
|
|
422
437
|
});
|
|
423
438
|
node._grounded = this.kcc.computedGrounded();
|
|
439
|
+
node._onWall = null;
|
|
440
|
+
node._onCeiling = false;
|
|
441
|
+
const touches = this.kcc.numComputedCollisions();
|
|
442
|
+
for (let i = 0; i < touches; i++) {
|
|
443
|
+
const normal = this.kcc.computedCollision(i)?.normal1;
|
|
444
|
+
if (!normal) continue;
|
|
445
|
+
if (normal.y < -.7) {
|
|
446
|
+
if (!node._grounded) node._onCeiling = true;
|
|
447
|
+
} else if (Math.hypot(normal.x, normal.z) > .7) node._onWall = [
|
|
448
|
+
normal.x,
|
|
449
|
+
normal.y,
|
|
450
|
+
normal.z
|
|
451
|
+
];
|
|
452
|
+
}
|
|
424
453
|
if (node._grounded) {
|
|
425
454
|
const half = characterHalfHeight(node);
|
|
426
455
|
const hit = this.castRay([
|
|
@@ -432,7 +461,7 @@ var Physics3D = class {
|
|
|
432
461
|
-1,
|
|
433
462
|
0
|
|
434
463
|
], .4, node);
|
|
435
|
-
if (hit?.node &&
|
|
464
|
+
if (hit?.node && this.canCarry(node, hit.node)) {
|
|
436
465
|
e.ground = hit.node;
|
|
437
466
|
const at = worldPosition3D(hit.node);
|
|
438
467
|
e.groundAt = [
|
|
@@ -499,9 +528,28 @@ var Physics3D = class {
|
|
|
499
528
|
* off ONE fixed body — they never move, so there is nothing to step — and the
|
|
500
529
|
* whole set is rebuilt only when the tree's shape changes.
|
|
501
530
|
*/
|
|
531
|
+
/** Where each live scatter field was when its colliders were built. */
|
|
532
|
+
scatterState = /* @__PURE__ */ new Map();
|
|
533
|
+
/**
|
|
534
|
+
* Has any scatter field moved, or had its rows replaced, since the build?
|
|
535
|
+
*
|
|
536
|
+
* `transforms` is compared by IDENTITY, which is the operation the skill
|
|
537
|
+
* teaches ("replace the whole array"); the world position catches the field
|
|
538
|
+
* itself or an ancestor being moved.
|
|
539
|
+
*/
|
|
540
|
+
scattersMoved() {
|
|
541
|
+
for (const [field] of this.scatterBodies) {
|
|
542
|
+
const was = this.scatterState.get(field);
|
|
543
|
+
const [x, y, z] = worldPosition3D(field);
|
|
544
|
+
if (!was) return true;
|
|
545
|
+
if (was.rows !== field.transforms || was.x !== x || was.y !== y || was.z !== z) return true;
|
|
546
|
+
}
|
|
547
|
+
return false;
|
|
548
|
+
}
|
|
502
549
|
syncScatters(root) {
|
|
503
550
|
for (const [, entry] of this.scatterBodies) this.world.removeRigidBody(entry);
|
|
504
551
|
this.scatterBodies.clear();
|
|
552
|
+
this.scatterState.clear();
|
|
505
553
|
const fields = /* @__PURE__ */ new Set();
|
|
506
554
|
collectScatters(root, fields);
|
|
507
555
|
for (const field of fields) {
|
|
@@ -543,6 +591,12 @@ var Physics3D = class {
|
|
|
543
591
|
this.world.createCollider(desc, body);
|
|
544
592
|
}
|
|
545
593
|
this.scatterBodies.set(field, body);
|
|
594
|
+
this.scatterState.set(field, {
|
|
595
|
+
rows: field.transforms,
|
|
596
|
+
x: ox,
|
|
597
|
+
y: oy,
|
|
598
|
+
z: oz
|
|
599
|
+
});
|
|
546
600
|
}
|
|
547
601
|
}
|
|
548
602
|
syncBodies() {
|
|
@@ -577,6 +631,7 @@ var Physics3D = class {
|
|
|
577
631
|
this.syncScatters(scene.root);
|
|
578
632
|
}
|
|
579
633
|
}
|
|
634
|
+
if (this.scatterBodies.size > 0 && this.scattersMoved()) this.syncScatters(scene.root);
|
|
580
635
|
const seen = this.bodySet;
|
|
581
636
|
for (const node of seen) {
|
|
582
637
|
if (node.collider.shape === void 0) {
|
|
@@ -644,12 +699,13 @@ var Physics3D = class {
|
|
|
644
699
|
if (node instanceof RigidBody3D) {
|
|
645
700
|
desc.setGravityScale(node.gravityScale);
|
|
646
701
|
desc.setLinvel(node.linearVelocity[0] ?? 0, node.linearVelocity[1] ?? 0, node.linearVelocity[2] ?? 0);
|
|
702
|
+
const locked = node.fixedRotation;
|
|
647
703
|
desc.setAngvel({
|
|
648
|
-
x: node.angularVelocity[0] ?? 0,
|
|
649
|
-
y: node.angularVelocity[1] ?? 0,
|
|
650
|
-
z: node.angularVelocity[2] ?? 0
|
|
704
|
+
x: locked ? 0 : node.angularVelocity[0] ?? 0,
|
|
705
|
+
y: locked ? 0 : node.angularVelocity[1] ?? 0,
|
|
706
|
+
z: locked ? 0 : node.angularVelocity[2] ?? 0
|
|
651
707
|
});
|
|
652
|
-
if (
|
|
708
|
+
if (locked) desc.lockRotations();
|
|
653
709
|
}
|
|
654
710
|
const body = this.world.createRigidBody(desc);
|
|
655
711
|
colDesc.setActiveEvents(R.ActiveEvents.COLLISION_EVENTS);
|
|
@@ -674,7 +730,10 @@ var Physics3D = class {
|
|
|
674
730
|
0,
|
|
675
731
|
0,
|
|
676
732
|
0
|
|
677
|
-
]
|
|
733
|
+
],
|
|
734
|
+
lastMass: node instanceof RigidBody3D ? node.mass : 1,
|
|
735
|
+
lastFriction: node instanceof RigidBody3D ? node.friction : .5,
|
|
736
|
+
lastRestitution: node instanceof RigidBody3D ? node.restitution : 0
|
|
678
737
|
};
|
|
679
738
|
this.entries.set(node, e);
|
|
680
739
|
this.byColliderHandle.set(collider.handle, node);
|
|
@@ -735,6 +794,18 @@ var Physics3D = class {
|
|
|
735
794
|
e.lastV = v;
|
|
736
795
|
}
|
|
737
796
|
if (node.gravityScale !== e.body.gravityScale()) e.body.setGravityScale(node.gravityScale, true);
|
|
797
|
+
if (node.mass !== e.lastMass) {
|
|
798
|
+
e.collider.setMass(node.mass);
|
|
799
|
+
e.lastMass = node.mass;
|
|
800
|
+
}
|
|
801
|
+
if (node.friction !== e.lastFriction) {
|
|
802
|
+
e.collider.setFriction(node.friction);
|
|
803
|
+
e.lastFriction = node.friction;
|
|
804
|
+
}
|
|
805
|
+
if (node.restitution !== e.lastRestitution) {
|
|
806
|
+
e.collider.setRestitution(node.restitution);
|
|
807
|
+
e.lastRestitution = node.restitution;
|
|
808
|
+
}
|
|
738
809
|
}
|
|
739
810
|
return e;
|
|
740
811
|
}
|
|
@@ -864,21 +935,24 @@ var Physics3D = class {
|
|
|
864
935
|
return [...set];
|
|
865
936
|
}
|
|
866
937
|
/**
|
|
867
|
-
* World-space raycast (meters). `
|
|
938
|
+
* World-space raycast (meters). `dir` may be any length (it is normalized),
|
|
939
|
+
* so `distance` and `maxLen` are metres. `exclude` skips that body's collider —
|
|
868
940
|
* ground probes from inside a capsule need it. `opts.staticOnly` hits ONLY the
|
|
869
941
|
* fixed/static world (excludes both dynamic AND kinematic bodies): the camera
|
|
870
942
|
* spring arm uses it so neither a dynamic projectile nor a KINEMATIC enemy
|
|
871
943
|
* (CharacterBody3D) passing behind the player yanks the camera in.
|
|
872
944
|
*/
|
|
873
945
|
castRay(origin, dir, maxLen, exclude, opts) {
|
|
946
|
+
const len = Math.hypot(dir[0], dir[1], dir[2]);
|
|
947
|
+
if (!(len > 0)) return null;
|
|
874
948
|
const ray = new this.R.Ray({
|
|
875
949
|
x: origin[0],
|
|
876
950
|
y: origin[1],
|
|
877
951
|
z: origin[2]
|
|
878
952
|
}, {
|
|
879
|
-
x: dir[0],
|
|
880
|
-
y: dir[1],
|
|
881
|
-
z: dir[2]
|
|
953
|
+
x: dir[0] / len,
|
|
954
|
+
y: dir[1] / len,
|
|
955
|
+
z: dir[2] / len
|
|
882
956
|
});
|
|
883
957
|
const excludeCollider = exclude ? this.entries.get(exclude)?.collider : void 0;
|
|
884
958
|
const flags = opts?.staticOnly ? this.R.QueryFilterFlags.EXCLUDE_DYNAMIC | this.R.QueryFilterFlags.EXCLUDE_KINEMATIC : void 0;
|
|
@@ -891,7 +965,12 @@ var Physics3D = class {
|
|
|
891
965
|
hit.normal.y,
|
|
892
966
|
hit.normal.z
|
|
893
967
|
],
|
|
894
|
-
node: this.byColliderHandle.get(hit.collider.handle) ?? null
|
|
968
|
+
node: this.byColliderHandle.get(hit.collider.handle) ?? null,
|
|
969
|
+
point: [
|
|
970
|
+
origin[0] + dir[0] / len * hit.timeOfImpact,
|
|
971
|
+
origin[1] + dir[1] / len * hit.timeOfImpact,
|
|
972
|
+
origin[2] + dir[2] / len * hit.timeOfImpact
|
|
973
|
+
]
|
|
895
974
|
};
|
|
896
975
|
}
|
|
897
976
|
/**
|
|
@@ -906,6 +985,8 @@ var Physics3D = class {
|
|
|
906
985
|
const R = this.R;
|
|
907
986
|
const excludeEntry = exclude ? this.entries.get(exclude) : void 0;
|
|
908
987
|
const flags = opts?.staticOnly ? R.QueryFilterFlags.EXCLUDE_DYNAMIC | R.QueryFilterFlags.EXCLUDE_KINEMATIC : void 0;
|
|
988
|
+
const len = Math.hypot(dir[0], dir[1], dir[2]);
|
|
989
|
+
if (!(len > 0)) return null;
|
|
909
990
|
const hit = this.world.castShape({
|
|
910
991
|
x: origin[0],
|
|
911
992
|
y: origin[1],
|
|
@@ -916,9 +997,9 @@ var Physics3D = class {
|
|
|
916
997
|
z: 0,
|
|
917
998
|
w: 1
|
|
918
999
|
}, {
|
|
919
|
-
x: dir[0],
|
|
920
|
-
y: dir[1],
|
|
921
|
-
z: dir[2]
|
|
1000
|
+
x: dir[0] / len,
|
|
1001
|
+
y: dir[1] / len,
|
|
1002
|
+
z: dir[2] / len
|
|
922
1003
|
}, new R.Ball(radius), 0, maxLen, true, flags, void 0, excludeEntry?.collider, excludeEntry?.body, (collider) => !collider.isSensor());
|
|
923
1004
|
if (!hit) return null;
|
|
924
1005
|
return {
|
|
@@ -950,7 +1031,7 @@ function collectBodies(node, out) {
|
|
|
950
1031
|
* Convex, always: a hull is a few planes to test against, where a triangle
|
|
951
1032
|
* mesh is a tree of them, and nothing scattered across a map needs concavity.
|
|
952
1033
|
*/
|
|
953
|
-
function meshShapedDesc(R, kind, size, scale, who) {
|
|
1034
|
+
function meshShapedDesc(R, kind, size, scale, who, engine) {
|
|
954
1035
|
const sx = scale[0] ?? 1;
|
|
955
1036
|
const sy = scale[1] ?? 1;
|
|
956
1037
|
const sz = scale[2] ?? 1;
|
|
@@ -958,7 +1039,7 @@ function meshShapedDesc(R, kind, size, scale, who) {
|
|
|
958
1039
|
const y = (size[1] ?? 1) * sy;
|
|
959
1040
|
const z = (size[2] ?? 1) * sz;
|
|
960
1041
|
const radial = Math.max(Math.abs(sx), Math.abs(sz));
|
|
961
|
-
if (who && kind !== "box" && kind !== "plane" && Math.abs(sx - sz) > 1e-6)
|
|
1042
|
+
if (who && kind !== "box" && kind !== "plane" && Math.abs(sx - sz) > 1e-6) diagnose(engine ?? null, "warn", `[incanto] Collider on '${who}': shape 'auto' fits a ${kind} to a mesh scaled [${sx}, ${sy}, ${sz}]. A ${kind} has one radius, so the widest of x/z (${radial}) is used. Use a box, or scale x and z the same.`);
|
|
962
1043
|
switch (kind) {
|
|
963
1044
|
case "box": return R.ColliderDesc.cuboid(x / 2, y / 2, z / 2);
|
|
964
1045
|
case "sphere": return R.ColliderDesc.ball((size[0] ?? 1) * Math.max(radial, Math.abs(sy)));
|
|
@@ -1233,9 +1314,9 @@ function buildColliderDesc3D(R, collider, nodeName, node) {
|
|
|
1233
1314
|
const model = models[0];
|
|
1234
1315
|
if (!mesh && !model) throw new IncantoError("BAD_FORMAT", `Collider on '${nodeName}': shape 'auto' needs a MeshInstance3D or ModelInstance3D on the body (or as a child) to take its shape from.`);
|
|
1235
1316
|
const parts = meshes.length + models.length;
|
|
1236
|
-
if (parts > 1)
|
|
1317
|
+
if (parts > 1) diagnose(node?.tree?.engine ?? null, "warn", `[incanto] Collider on '${nodeName}': shape 'auto' fits ONE mesh and took '${(mesh ?? model)?.name}'; ${parts - 1} other mesh(es) here are not solid. Give each part its own body.`);
|
|
1237
1318
|
if (mesh) {
|
|
1238
|
-
const desc = meshShapedDesc(R, mesh.mesh, mesh.size, mesh.scale, nodeName);
|
|
1319
|
+
const desc = meshShapedDesc(R, mesh.mesh, mesh.size, mesh.scale, nodeName, node?.tree?.engine);
|
|
1239
1320
|
if (desc) {
|
|
1240
1321
|
desc.setTranslation(mesh.at[0], mesh.at[1], mesh.at[2]);
|
|
1241
1322
|
if (mesh.turn[0] !== 0 || mesh.turn[1] !== 0 || mesh.turn[2] !== 0) {
|
|
@@ -1253,7 +1334,7 @@ function buildColliderDesc3D(R, collider, nodeName, node) {
|
|
|
1253
1334
|
const tri = modelTriangles(node, model);
|
|
1254
1335
|
if (!tri) return null;
|
|
1255
1336
|
if (isStaticShape(node)) {
|
|
1256
|
-
if (tri.triangles > TRIMESH_BUDGET)
|
|
1337
|
+
if (tri.triangles > TRIMESH_BUDGET) diagnose(node?.tree?.engine ?? null, "warn", `[incanto] Collider on '${nodeName}': shape 'auto' built a ${tri.triangles}-triangle mesh from '${model.name}'. Consider a low-poly collision model or an explicit box.`);
|
|
1257
1338
|
return R.ColliderDesc.trimesh(tri.vertices, tri.indices);
|
|
1258
1339
|
}
|
|
1259
1340
|
return R.ColliderDesc.convexHull(tri.vertices);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { _ as AudioPlayer } from "./register-
|
|
2
|
-
import { i as logReport, o as parseDrive } from "./webgl-unavailable-
|
|
3
|
-
import { a as frameSignature, i as frameImage, o as frameStats } from "./frame-report-
|
|
1
|
+
import { _ as AudioPlayer } from "./register-DuVdzxbq.js";
|
|
2
|
+
import { i as logReport, o as parseDrive } from "./webgl-unavailable-C8aDbGmR.js";
|
|
3
|
+
import { a as frameSignature, i as frameImage, o as frameStats } from "./frame-report-D-_7YF2G.js";
|
|
4
|
+
import { Q as worldPosition } from "./gameplay-CNULJvwh.js";
|
|
4
5
|
//#region src/core/audio-errors.ts
|
|
5
6
|
/** Every AudioPlayer under `root` whose source could not be played. */
|
|
6
7
|
function audioErrors(root) {
|
|
@@ -100,6 +101,74 @@ function wireDevChannel(opts) {
|
|
|
100
101
|
return cleanups;
|
|
101
102
|
}
|
|
102
103
|
//#endregion
|
|
104
|
+
//#region src/core/drive-apply.ts
|
|
105
|
+
/**
|
|
106
|
+
* Long enough for the page to draw a frame between the press and the release.
|
|
107
|
+
*
|
|
108
|
+
* Both edges in one tick would still FIRE — the input settles them together and
|
|
109
|
+
* `Clickable` reads press then release in the same update — but no real cursor
|
|
110
|
+
* does that, and everything a game shows for a held button (a pressed state, a
|
|
111
|
+
* hover tint, the sound) would never be on screen. Two frames at 60 Hz.
|
|
112
|
+
*/
|
|
113
|
+
const FRAME_MS = 34;
|
|
114
|
+
/** Apply one step of a drive script to a running game. */
|
|
115
|
+
async function applyDriveStep(target, step) {
|
|
116
|
+
const { engine } = target;
|
|
117
|
+
switch (step.kind) {
|
|
118
|
+
case "press":
|
|
119
|
+
engine.input.pressAction(step.action);
|
|
120
|
+
return;
|
|
121
|
+
case "release":
|
|
122
|
+
engine.input.releaseAction(step.action);
|
|
123
|
+
return;
|
|
124
|
+
case "vector":
|
|
125
|
+
engine.input.setActionVector(step.action, step.x, step.y);
|
|
126
|
+
return;
|
|
127
|
+
case "key":
|
|
128
|
+
engine.input.handleKey(step.code, step.down);
|
|
129
|
+
return;
|
|
130
|
+
case "pointer":
|
|
131
|
+
engine.input.handlePointerMove(step.dx, step.dy);
|
|
132
|
+
return;
|
|
133
|
+
case "at":
|
|
134
|
+
engine.input.setPointerPosition(step.x, step.y);
|
|
135
|
+
return;
|
|
136
|
+
case "atNode": {
|
|
137
|
+
const node = engine.scene?.root.getNodeOrNull(step.path);
|
|
138
|
+
const at = node ? worldPosition(node) : [];
|
|
139
|
+
if (at.length === 0) {
|
|
140
|
+
engine.log.error(`[incanto] drive: at '${step.path}' — no such node, so the cursor did not move.`);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const screen = target.project(at[0] ?? 0, at[1] ?? 0, at[2] ?? 0);
|
|
144
|
+
if (!screen) {
|
|
145
|
+
engine.log.error(`[incanto] drive: at '${step.path}' — this renderer cannot say where a world point is on screen, so the cursor did not move.`);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (screen.behind === true) {
|
|
149
|
+
engine.log.error(`[incanto] drive: at '${step.path}' — it is BEHIND the camera, so there is no pixel to put the cursor on.`);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
engine.input.setPointerPosition(screen.x, screen.y);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
case "click":
|
|
156
|
+
engine.input.handleMouseButton(step.button, true);
|
|
157
|
+
await target.wait(FRAME_MS);
|
|
158
|
+
engine.input.handleMouseButton(step.button, false);
|
|
159
|
+
await target.wait(FRAME_MS);
|
|
160
|
+
return;
|
|
161
|
+
case "mouse":
|
|
162
|
+
engine.input.handleMouseButton(step.button, step.down);
|
|
163
|
+
await target.wait(FRAME_MS);
|
|
164
|
+
return;
|
|
165
|
+
case "step":
|
|
166
|
+
await target.wait(step.ms);
|
|
167
|
+
return;
|
|
168
|
+
default: return;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
//#endregion
|
|
103
172
|
//#region src/core/editor-switch.ts
|
|
104
173
|
/**
|
|
105
174
|
* Cover the page while it changes what it is.
|
|
@@ -347,27 +416,20 @@ function pauseWhenHidden(engine, doc = typeof document === "undefined" ? null :
|
|
|
347
416
|
if (!doc) return () => {};
|
|
348
417
|
/** The scale to come back to — null when we did not pause anything. */
|
|
349
418
|
let heldScale = null;
|
|
350
|
-
let mutedByUs = false;
|
|
351
419
|
const onChange = () => {
|
|
352
420
|
if (doc.hidden) {
|
|
353
421
|
if (heldScale === null && engine.timeScale > 0) {
|
|
354
422
|
heldScale = engine.timeScale;
|
|
355
423
|
engine.timeScale = 0;
|
|
356
424
|
}
|
|
357
|
-
|
|
358
|
-
engine.audio.muted = true;
|
|
359
|
-
mutedByUs = true;
|
|
360
|
-
}
|
|
425
|
+
engine.audio.suspend(true);
|
|
361
426
|
return;
|
|
362
427
|
}
|
|
363
428
|
if (heldScale !== null) {
|
|
364
429
|
if (engine.timeScale === 0) engine.timeScale = heldScale;
|
|
365
430
|
heldScale = null;
|
|
366
431
|
}
|
|
367
|
-
|
|
368
|
-
engine.audio.muted = false;
|
|
369
|
-
mutedByUs = false;
|
|
370
|
-
}
|
|
432
|
+
engine.audio.suspend(false);
|
|
371
433
|
};
|
|
372
434
|
doc.addEventListener("visibilitychange", onChange);
|
|
373
435
|
return () => doc.removeEventListener("visibilitychange", onChange);
|
|
@@ -397,4 +459,33 @@ function defaultReport(what, error) {
|
|
|
397
459
|
console.error(`[incanto] teardown: ${what} failed (continuing)`, error);
|
|
398
460
|
}
|
|
399
461
|
//#endregion
|
|
400
|
-
|
|
462
|
+
//#region src/core/picking.ts
|
|
463
|
+
/** Is this object, and every ancestor up to the scene, drawn? */
|
|
464
|
+
function drawn(object) {
|
|
465
|
+
for (let at = object; at; at = at.parent) if (!at.visible) return false;
|
|
466
|
+
return true;
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* The `incantoNode` under a set of raycast hits, or null.
|
|
470
|
+
*
|
|
471
|
+
* Hits are expected in the order three produces them: nearest first.
|
|
472
|
+
*/
|
|
473
|
+
function nodeFromHits(hits, opts = {}) {
|
|
474
|
+
let best = null;
|
|
475
|
+
for (const hit of hits) {
|
|
476
|
+
if (!drawn(hit.object)) continue;
|
|
477
|
+
let obj = hit.object;
|
|
478
|
+
while (obj && !obj.userData?.incantoNode) obj = obj.parent;
|
|
479
|
+
const node = obj?.userData?.incantoNode;
|
|
480
|
+
if (node === void 0) continue;
|
|
481
|
+
if (!opts.byRenderOrder) return node;
|
|
482
|
+
const order = hit.object.renderOrder ?? 0;
|
|
483
|
+
if (!best || order >= best.order) best = {
|
|
484
|
+
node,
|
|
485
|
+
order
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
return best?.node ?? null;
|
|
489
|
+
}
|
|
490
|
+
//#endregion
|
|
491
|
+
export { devServerLibrary as a, applyDriveStep as c, audioErrors as d, crossFade as i, wireDevChannel as l, teardown as n, openBundledEditor as o, pauseWhenHidden as r, poseFromRenderer as s, nodeFromHits as t, claimCanvasGestures as u };
|
package/dist/react.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as Scene, b as Engine, n as BehaviorCtor } from "./behavior-
|
|
2
|
-
import { c as JsonValue } from "./
|
|
1
|
+
import { P as Scene, b as Engine, n as BehaviorCtor } from "./behavior-Dcz0fr1S.js";
|
|
2
|
+
import { c as JsonValue } from "./rng-Bb-IutXB.js";
|
|
3
3
|
import { CSSProperties, ReactNode } from "react";
|
|
4
4
|
|
|
5
5
|
//#region src/react/index.d.ts
|
package/dist/react.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as jsonEquals, t as jsonClone } from "./json-
|
|
1
|
+
import { n as jsonEquals, t as jsonClone } from "./json-CfTjpvW8.js";
|
|
2
2
|
import { createContext, useContext, useEffect, useRef, useState } from "react";
|
|
3
3
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
//#region src/react/index.tsx
|
|
@@ -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-LCHBLfA4.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-CH4kt78v.js").then((n) => n.n)).createGame2D(o)))(opts);
|
|
160
160
|
if (disposed) {
|
|
161
161
|
next.dispose();
|
|
162
162
|
return;
|