incanto 0.61.0 → 0.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/incanto-verify.mjs +134 -55
- package/dist/2d.d.ts +42 -0
- package/dist/2d.js +3 -3
- package/dist/3d.d.ts +22 -0
- package/dist/3d.js +5 -5
- package/dist/{create-game-DH7JI5xx.js → create-game-lLeITaZ5.js} +8 -8
- package/dist/{create-game-IX5lEH0P.js → create-game-viBqXUoZ.js} +6 -6
- package/dist/{duplicate-CGqAmK2h.js → duplicate-BOOKmkQ7.js} +1 -1
- package/dist/editor.js +39 -18
- package/dist/{environment-presets-CNxCuhZF.js → environment-presets-QR7_75KJ.js} +5 -3
- package/dist/{gameplay-Dtzd2itW.js → gameplay-CuqoHHUB.js} +153 -16
- package/dist/gameplay.js +1 -1
- package/dist/index.d.ts +92 -1
- package/dist/index.js +6 -6
- package/dist/{loader-D7jTvDQv.js → loader-zDynoew_.js} +160 -2
- package/dist/net.js +1 -1
- package/dist/{physics-2d-CBnor8Zf.js → physics-2d-CllJXlic.js} +11 -3
- package/dist/{physics-3d-BTUfUSWO.js → physics-3d-BL_pFJ19.js} +123 -17
- package/dist/react.js +1 -1
- package/dist/{register-DL3izw8j.js → register-1cKM8DEj.js} +60 -9
- package/dist/{register-xuSRyD6b.js → register-Btm7_Emq.js} +158 -2
- package/dist/{replay-D7-yle3s.js → replay-Bhdntkvq.js} +15 -2
- package/dist/{split-screen-5Ban4q4n.js → split-screen-Dx0LvzqS.js} +2 -2
- package/dist/{src-DozXvyZS.js → src-BlV9Sv4m.js} +1 -1
- package/dist/{teardown-B6rwJOyS.js → teardown-BwhkcNt8.js} +1 -1
- package/dist/{test-Dch_7VQD.js → test-Ct1_zf5S.js} +64 -20
- package/dist/test.d.ts +41 -0
- package/dist/test.js +2 -2
- package/dist/vite.js +2 -2
- package/editor/assets/{agent8-CF1JL2tR.js → agent8-CLZXBRoM.js} +1 -1
- package/editor/assets/{debug-3QzYhOPA.js → debug-eaSKFAKW.js} +1 -1
- package/editor/assets/{index-CAD2c5ug.js → index-D66AuRwG.js} +91 -91
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/schemas/scene.schema.json +91 -0
- package/skills/README.md +4 -0
- package/skills/incanto-behaviors-and-scripts.md +37 -0
- package/skills/incanto-building-2d-games.md +20 -1
- package/skills/incanto-building-3d-games.md +1 -1
- package/skills/incanto-game-feel.md +3 -2
- package/skills/incanto-gameplay-behaviors.md +45 -6
- package/skills/incanto-node-reference.md +12 -0
- package/skills/incanto-physics-and-input.md +53 -2
- package/skills/incanto-scene-json-authoring.md +15 -1
- package/skills/incanto-verifying-your-game.md +31 -1
- package/skills/incanto-your-first-game.md +237 -0
- package/templates-app/beacon-isle-3d/package.json +1 -1
- package/templates-app/platformer-2d/package.json +1 -1
- package/templates-app/star-survivor/package.json +1 -1
- package/templates-app/star-survivor/src/game.scene.json +4 -2
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/tps-3d/src/game.scene.json +3 -2
- package/templates-app/tps-3d/verify.ts +38 -0
- package/templates-app/village-quest-3d/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { O as Node, P as Behavior, _ as SaveSlots, k as diagnose, n as loadScene, w as effectiveOrder, z as registerBehavior } from "./loader-
|
|
1
|
+
import { O as Node, P as Behavior, _ as SaveSlots, k as diagnose, n as loadScene, w as effectiveOrder, z as registerBehavior } from "./loader-zDynoew_.js";
|
|
2
2
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
3
3
|
import { t as jsonClone } from "./json-CwwhxQgb.js";
|
|
4
|
-
import { t as duplicateNode } from "./duplicate-
|
|
4
|
+
import { t as duplicateNode } from "./duplicate-BOOKmkQ7.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();
|
|
@@ -2582,7 +2582,11 @@ var CharacterBody3D = class extends PhysicsBody3D {
|
|
|
2582
2582
|
0
|
|
2583
2583
|
] },
|
|
2584
2584
|
stickToGround: { default: true },
|
|
2585
|
-
slopeLimitDeg: { default: 45 }
|
|
2585
|
+
slopeLimitDeg: { default: 45 },
|
|
2586
|
+
stepHeight: {
|
|
2587
|
+
default: .35,
|
|
2588
|
+
range: { min: 0 }
|
|
2589
|
+
}
|
|
2586
2590
|
};
|
|
2587
2591
|
/** m/s, y-up (up = +y). */
|
|
2588
2592
|
velocity = [
|
|
@@ -2604,6 +2608,28 @@ var CharacterBody3D = class extends PhysicsBody3D {
|
|
|
2604
2608
|
/** Max climbable slope angle. */
|
|
2605
2609
|
slopeLimitDeg = 45;
|
|
2606
2610
|
/**
|
|
2611
|
+
* How high a step this body walks UP without jumping (0 = none).
|
|
2612
|
+
*
|
|
2613
|
+
* Rapier's character controller does not autostep unless asked, and nothing
|
|
2614
|
+
* asked — so every `CharacterBody3D` was stopped dead by any ledge at all.
|
|
2615
|
+
* The player never showed it, because `CharacterController3D` rides a hover
|
|
2616
|
+
* spring rather than the KCC; enemies are the ones that walk.
|
|
2617
|
+
*
|
|
2618
|
+
* Measured on the shipped `tps-3d` template, whose arena has a 0.6 m `Ramp`
|
|
2619
|
+
* between the spawn and the player. Standing still for 45 seconds:
|
|
2620
|
+
*
|
|
2621
|
+
* ```
|
|
2622
|
+
* hits=0 hp=100 closest an enemy ever got = 33.91m
|
|
2623
|
+
* ```
|
|
2624
|
+
*
|
|
2625
|
+
* They chase at the right speed the whole time and pile up against the near
|
|
2626
|
+
* face of a knee-high box, because a chaser walks a straight line and cannot
|
|
2627
|
+
* climb. The template's own playtest had been printing
|
|
2628
|
+
* `danger: the player took damage 0 times in 4 runs — nothing here can hurt
|
|
2629
|
+
* you` all along, and no rung failed on it.
|
|
2630
|
+
*/
|
|
2631
|
+
stepHeight = .35;
|
|
2632
|
+
/**
|
|
2607
2633
|
* `snapToGround` on a character body is ambiguous, so it is refused.
|
|
2608
2634
|
*
|
|
2609
2635
|
* A boolean here used to mean the ground-stick, and as placement it means
|
|
@@ -2625,6 +2651,51 @@ var CharacterBody3D = class extends PhysicsBody3D {
|
|
|
2625
2651
|
}
|
|
2626
2652
|
};
|
|
2627
2653
|
//#endregion
|
|
2654
|
+
//#region src/3d/nodes/unscalable.ts
|
|
2655
|
+
/**
|
|
2656
|
+
* Refuse `scale` on a node that keeps a CPU twin of itself.
|
|
2657
|
+
*
|
|
2658
|
+
* `scale` is a plain `Node3D` prop and the 3D skill actively recommends it —
|
|
2659
|
+
* *"resize with `scale`, not `size` — resizing a mesh rebuilds its geometry
|
|
2660
|
+
* every call, scaling is free"*. On a mesh that is true. On `Terrain3D`,
|
|
2661
|
+
* `Water3D` and `River3D` it resizes only the PICTURE: everything the gameplay
|
|
2662
|
+
* layer reads is scale-blind, because it reads `size`.
|
|
2663
|
+
*
|
|
2664
|
+
* - `Terrain3D.heightAt` indexes a heightmap built from `size` and composes
|
|
2665
|
+
* ancestor POSITIONS only
|
|
2666
|
+
* - `buildHeightfieldDesc3D` passes `{x: hm.width, y: 1, z: hm.depth}` with no
|
|
2667
|
+
* scale term
|
|
2668
|
+
* - `Water3D._waterYAt` rejects anything outside `size / 2` of the node
|
|
2669
|
+
*
|
|
2670
|
+
* Measured on `size: [100, 100]` at `scale: [2, 1, 2]`:
|
|
2671
|
+
*
|
|
2672
|
+
* ```
|
|
2673
|
+
* terrain rendered X extent = [-100.0, 100.0] (was [-50, 50])
|
|
2674
|
+
* terrain heightAt(70, 0) = -50.000 (unchanged: the island floor)
|
|
2675
|
+
* water rendered X extent = [-40.0, 40.0]
|
|
2676
|
+
* water _waterYAt(30, 0) = null (unchanged)
|
|
2677
|
+
* ```
|
|
2678
|
+
*
|
|
2679
|
+
* So the player walks off an invisible cliff at ±50 m onto ground that is
|
|
2680
|
+
* visibly there for another 50; drape, spawn placement and terrain-nav all
|
|
2681
|
+
* agree with the collider and disagree with the picture; a scaled water's
|
|
2682
|
+
* underwater fog, splash and `avoidWater` culling stop at half the visible
|
|
2683
|
+
* shoreline. `incanto check` called the scene valid and `auditScene` said
|
|
2684
|
+
* nothing.
|
|
2685
|
+
*
|
|
2686
|
+
* A hard load error rather than a warning, per the engine's own guardrail: the
|
|
2687
|
+
* dial the author wanted exists and is one word away.
|
|
2688
|
+
*/
|
|
2689
|
+
function rejectScale(node, reads) {
|
|
2690
|
+
const scale = node.scale;
|
|
2691
|
+
if (!Array.isArray(scale)) return;
|
|
2692
|
+
if (scale.every((v) => Math.abs((v ?? 1) - 1) < 1e-4)) return;
|
|
2693
|
+
const type = node.constructor.typeName ?? "node";
|
|
2694
|
+
const size = node.size;
|
|
2695
|
+
const suggestion = Array.isArray(size) ? `size: [${size.map((v, i) => (v ?? 0) * (scale[i === 1 ? 2 : i] ?? 1)).join(", ")}]` : "size";
|
|
2696
|
+
throw new IncantoError("BAD_FORMAT", `${type} '${node.name}' has scale ${JSON.stringify(scale)}. On this node scale resizes only the MESH — ${reads} read "size" — so the ground you can see would reach past the ground you can stand on. Resize with "${suggestion}" instead.`, { prop: "scale" });
|
|
2697
|
+
}
|
|
2698
|
+
//#endregion
|
|
2628
2699
|
//#region src/3d/nodes/water-cutout-3d.ts
|
|
2629
2700
|
/**
|
|
2630
2701
|
* A box the water does not enter.
|
|
@@ -3155,6 +3226,7 @@ var Water3D = class Water3D extends Node3D {
|
|
|
3155
3226
|
}
|
|
3156
3227
|
/** Loader hook: degenerate planes and bad enums fail at LOAD, not at render. */
|
|
3157
3228
|
static validateJson(node) {
|
|
3229
|
+
rejectScale(node, "the water surface query, underwater fog, splashes and buoyancy all");
|
|
3158
3230
|
const w = node;
|
|
3159
3231
|
if (!((w.size[0] ?? 0) > 0) || !((w.size[1] ?? 0) > 0)) throw new IncantoError("BAD_FORMAT", `Water3D '${node.name}' size must be positive [width, depth] meters, got ${JSON.stringify(w.size)}.`, { prop: "size" });
|
|
3160
3232
|
if (!WATER_QUALITIES.includes(w.quality)) throw new IncantoError("BAD_FORMAT", `Water3D '${node.name}' quality must be one of [${WATER_QUALITIES.join(", ")}], got '${w.quality}'.`, {
|
|
@@ -4388,6 +4460,56 @@ function smoothingFactor(smoothing, dt) {
|
|
|
4388
4460
|
const s = Math.min(Math.max(smoothing, 0), .99);
|
|
4389
4461
|
return s <= 0 ? 1 : 1 - s ** (dt * 60);
|
|
4390
4462
|
}
|
|
4463
|
+
/**
|
|
4464
|
+
* A node's position with every spatial ancestor's added in.
|
|
4465
|
+
*
|
|
4466
|
+
* The tree composes transforms for DRAWING — three does it for free — and every
|
|
4467
|
+
* piece of engine logic that reads `node.position` gets the LOCAL number
|
|
4468
|
+
* instead. Physics already knew (`worldPosition2D`/`worldPosition3D` in the
|
|
4469
|
+
* adapters); the camera did not, and neither did anything that chases a target.
|
|
4470
|
+
*
|
|
4471
|
+
* A camera parented to the player is the Godot and Phaser idiom, and the
|
|
4472
|
+
* authoring skill explicitly permits it — *"EVERY node type can hold children …
|
|
4473
|
+
* any node works as a grouping container"*. Measured on exactly that scene:
|
|
4474
|
+
*
|
|
4475
|
+
* ```
|
|
4476
|
+
* player world position : [1400, 900]
|
|
4477
|
+
* renderer view centre : {"x":0,"y":0}
|
|
4478
|
+
* framing : camera /Level/Player/Cam centred [1400, 900]
|
|
4479
|
+
* 1 in view, 0 outside it
|
|
4480
|
+
* ```
|
|
4481
|
+
*
|
|
4482
|
+
* The renderer drew empty world 1600 px from the player while `framing` — the
|
|
4483
|
+
* instrument the skills tell you to trust before handing a game back —
|
|
4484
|
+
* certified the player on screen.
|
|
4485
|
+
*
|
|
4486
|
+
* Arity comes from the node itself: a 2D node has two components, a 3D node
|
|
4487
|
+
* three, and an ancestor that is neither contributes nothing.
|
|
4488
|
+
*/
|
|
4489
|
+
function worldPosition(node) {
|
|
4490
|
+
const own = node.position;
|
|
4491
|
+
if (!Array.isArray(own)) return [];
|
|
4492
|
+
const out = own.slice();
|
|
4493
|
+
for (let at = node.parent; at; at = at.parent) {
|
|
4494
|
+
const p = at.position;
|
|
4495
|
+
if (!Array.isArray(p)) continue;
|
|
4496
|
+
for (let i = 0; i < out.length; i++) out[i] = (out[i] ?? 0) + (p[i] ?? 0);
|
|
4497
|
+
}
|
|
4498
|
+
return out;
|
|
4499
|
+
}
|
|
4500
|
+
/**
|
|
4501
|
+
* Turn a world position into the local one `node.position` has to hold for the
|
|
4502
|
+
* node to END UP there — the inverse of `worldPosition` for its parent chain.
|
|
4503
|
+
*/
|
|
4504
|
+
function localFromWorld(node, world) {
|
|
4505
|
+
const out = world.slice();
|
|
4506
|
+
for (let at = node.parent; at; at = at.parent) {
|
|
4507
|
+
const p = at.position;
|
|
4508
|
+
if (!Array.isArray(p)) continue;
|
|
4509
|
+
for (let i = 0; i < out.length; i++) out[i] = (out[i] ?? 0) - (p[i] ?? 0);
|
|
4510
|
+
}
|
|
4511
|
+
return out;
|
|
4512
|
+
}
|
|
4391
4513
|
//#endregion
|
|
4392
4514
|
//#region src/gameplay/chase.ts
|
|
4393
4515
|
/**
|
|
@@ -5062,11 +5184,12 @@ var FollowCamera = class extends Behavior {
|
|
|
5062
5184
|
if (!hasPosition$1(cam)) return;
|
|
5063
5185
|
const target = cam.getNodeOrNull(this.target);
|
|
5064
5186
|
if (!target || !hasPosition$1(target)) return;
|
|
5065
|
-
const
|
|
5066
|
-
|
|
5187
|
+
const here = worldPosition(cam);
|
|
5188
|
+
const desired = add(worldPosition(target), this.offset);
|
|
5189
|
+
if (!(this.deadzone > 0 && distance$1(here, desired) <= this.deadzone)) {
|
|
5067
5190
|
const f = smoothingFactor(this.smoothing, dt);
|
|
5068
|
-
if (f >= 1) cam.position =
|
|
5069
|
-
else cam.position = cam.
|
|
5191
|
+
if (f >= 1) cam.position = localFromWorld(cam, desired);
|
|
5192
|
+
else cam.position = localFromWorld(cam, here.map((c, i) => c + ((desired[i] ?? c) - c) * f));
|
|
5070
5193
|
}
|
|
5071
5194
|
if (this.shakeT > 0) {
|
|
5072
5195
|
this.shakeT -= dt;
|
|
@@ -6069,12 +6192,12 @@ var Projectile = class extends Behavior {
|
|
|
6069
6192
|
return [Math.cos(rad), Math.sin(rad)];
|
|
6070
6193
|
}
|
|
6071
6194
|
if (Array.isArray(rot)) {
|
|
6072
|
-
const
|
|
6073
|
-
const
|
|
6195
|
+
const ry = (rot[1] ?? 0) * Math.PI / 180;
|
|
6196
|
+
const rx = (rot[0] ?? 0) * Math.PI / 180;
|
|
6074
6197
|
return normalize([
|
|
6075
|
-
|
|
6076
|
-
Math.sin(
|
|
6077
|
-
|
|
6198
|
+
Math.sin(ry),
|
|
6199
|
+
-Math.sin(rx) * Math.cos(ry),
|
|
6200
|
+
Math.cos(rx) * Math.cos(ry)
|
|
6078
6201
|
]);
|
|
6079
6202
|
}
|
|
6080
6203
|
return [1, 0];
|
|
@@ -6360,13 +6483,27 @@ var SpawnSource = class {
|
|
|
6360
6483
|
* spawner's node), and return the clone.
|
|
6361
6484
|
*
|
|
6362
6485
|
* Placement: with no `at`, the clone keeps the template's authored position;
|
|
6363
|
-
* with `at`, it
|
|
6364
|
-
*
|
|
6486
|
+
* with `at`, it ends up at the spawner's position plus `at`.
|
|
6487
|
+
*
|
|
6488
|
+
* `at` is written as the clone's LOCAL position, because the clone is added
|
|
6489
|
+
* as a CHILD of the spawner's node and that parent's transform already
|
|
6490
|
+
* contributes the spawner's own position. This used to add the two, so the
|
|
6491
|
+
* spawner's position was applied twice and the further from the origin you
|
|
6492
|
+
* put the nest, the further away its enemies appeared. Measured on a spawner
|
|
6493
|
+
* at [820, 240] with `"at": [0, -50]`:
|
|
6494
|
+
*
|
|
6495
|
+
* ```
|
|
6496
|
+
* expected world = [820, 190]
|
|
6497
|
+
* clone WORLD position = [1640, 430] error = [820, 240]
|
|
6498
|
+
* ```
|
|
6499
|
+
*
|
|
6500
|
+
* The scene loads, the spawner spawns, the clone is alive and visible — it is
|
|
6501
|
+
* just somewhere else in the level, and nothing has anything to report.
|
|
6365
6502
|
*/
|
|
6366
6503
|
spawn(template, opts) {
|
|
6367
6504
|
const clone = duplicateNode(template);
|
|
6368
6505
|
const at = opts?.at;
|
|
6369
|
-
if (at && at.length > 0 && hasPosition$1(clone)) clone.position = add(hasPosition$1(this.behavior.node) ? this.behavior.node.position
|
|
6506
|
+
if (at && at.length > 0 && hasPosition$1(clone)) clone.position = add((opts?.parent ?? this.behavior.node) === this.behavior.node || !hasPosition$1(this.behavior.node) ? [] : this.behavior.node.position, at);
|
|
6370
6507
|
if (typeof clone.visible === "boolean") clone.visible = true;
|
|
6371
6508
|
(opts?.parent ?? this.behavior.node).addChild(clone);
|
|
6372
6509
|
return clone;
|
|
@@ -6867,4 +7004,4 @@ function registerGameplayBehaviors(opts) {
|
|
|
6867
7004
|
for (const [name, ctor] of Object.entries(GAMEPLAY_BEHAVIORS)) registerBehavior(name, ctor, { replace });
|
|
6868
7005
|
}
|
|
6869
7006
|
//#endregion
|
|
6870
|
-
export { Health as A,
|
|
7007
|
+
export { Health as A, CharacterBody3D as B, restartScene as C, DayNight as D, FloatAway as E, Water3D as F, validateCollider3D as G, RigidBody3D as H, WATER_CUTOUT_MAX as I, colliderFootDrop as J, createCausticsQuad as K, WaterCutout3D as L, Chase as M, localFromWorld as N, phaseOf as O, worldPosition as P, rejectScale as R, goToScene as S, FollowCamera as T, StaticBody3D as U, PhysicsBody3D as V, Node3D as W, Cooldown as _, Wander as a, Interactable as b, SavePoint as c, Patrol as d, PathFollow as f, CameraShake as g, Lifetime as h, WaveSpawner as i, Collector as j, DamageOnContact as k, Projectile as l, MoveTo as m, registerGameplayBehaviors as n, Spawner as o, Oscillate as p, WATER_MAX_RIPPLES as q, ZombieAI as r, ScoreKeeper as s, GAMEPLAY_BEHAVIORS as t, Pickup as u, hitStop as v, tolerateUnknownAction as w, GameFlow as x, screenFlash as y, Area3D as z };
|
package/dist/gameplay.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as Health, C as restartScene, D as DayNight, E as FloatAway, M as Chase, O as phaseOf, S as goToScene, T as FollowCamera, _ as Cooldown, a as Wander, b as Interactable, c as SavePoint, d as Patrol, f as PathFollow, g as CameraShake, h as Lifetime, i as WaveSpawner, j as Collector, k as DamageOnContact, 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, x as GameFlow, y as screenFlash } from "./gameplay-
|
|
1
|
+
import { A as Health, C as restartScene, D as DayNight, E as FloatAway, M as Chase, O as phaseOf, S as goToScene, T as FollowCamera, _ as Cooldown, a as Wander, b as Interactable, c as SavePoint, d as Patrol, f as PathFollow, g as CameraShake, h as Lifetime, i as WaveSpawner, j as Collector, k as DamageOnContact, 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, x as GameFlow, y as screenFlash } from "./gameplay-CuqoHHUB.js";
|
|
2
2
|
export { CameraShake, Chase, Collector, Cooldown, DamageOnContact, DayNight, FloatAway, FollowCamera, GAMEPLAY_BEHAVIORS, GameFlow, Health, Interactable, Lifetime, MoveTo, Oscillate, PathFollow, Patrol, Pickup, Projectile, SavePoint, ScoreKeeper, Spawner, Wander, WaveSpawner, ZombieAI, goToScene, hitStop, phaseOf, registerGameplayBehaviors, restartScene, screenFlash };
|
package/dist/index.d.ts
CHANGED
|
@@ -624,6 +624,97 @@ declare class UiDialogue extends HudWidgetBase {
|
|
|
624
624
|
override update(dt: number): void;
|
|
625
625
|
}
|
|
626
626
|
//#endregion
|
|
627
|
+
//#region src/core/nodes/respawn.d.ts
|
|
628
|
+
/**
|
|
629
|
+
* Catch whatever this node hangs off when it leaves the world, and put it back.
|
|
630
|
+
*
|
|
631
|
+
* Nothing in the engine did this. Not a node, not a behavior, not a scene
|
|
632
|
+
* header — and the shipped examples prove what that costs: with the `plays`
|
|
633
|
+
* rung finally counting defects, `basic-3d-sideview` fails 8 seeded runs out of
|
|
634
|
+
* 8, `water-lake-3d` 7, `water-ocean-3d` 5, `water-pool-3d` 4,
|
|
635
|
+
* `water-river-3d` 1. Every one of them the same way — the player walks off the
|
|
636
|
+
* edge of the terrain and falls forever, and the only fix available was a
|
|
637
|
+
* per-frame `y` check hand-written in TypeScript, which is exactly the kind of
|
|
638
|
+
* thing this engine exists to keep out of TypeScript.
|
|
639
|
+
*
|
|
640
|
+
* A CHILD NODE rather than a Behavior, deliberately: a node holds one behavior
|
|
641
|
+
* and the player's is already spoken for (`PlayerControl`, `Health`, the game's
|
|
642
|
+
* own script). Children stack, the way `CharacterController3D` already hangs
|
|
643
|
+
* off its body.
|
|
644
|
+
*
|
|
645
|
+
* ```jsonc
|
|
646
|
+
* { "name": "Player", "type": "RigidBody3D", "props": { … },
|
|
647
|
+
* "children": [
|
|
648
|
+
* { "name": "Ctl", "type": "CharacterController3D" },
|
|
649
|
+
* { "name": "Catch", "type": "Respawn" } // that is the whole thing
|
|
650
|
+
* ] }
|
|
651
|
+
* ```
|
|
652
|
+
*
|
|
653
|
+
* With no props it guards its parent, uses the same line the playtest's fall
|
|
654
|
+
* detector uses (50 m under the spawn in 3D, 1000 px in 2D), and puts the node
|
|
655
|
+
* back where it started. `respawned` fires each time, so a life can be taken
|
|
656
|
+
* off a `ScoreKeeper` or a `Health` with an ordinary connection — this node
|
|
657
|
+
* does not decide what falling COSTS, which is a game-design question and not
|
|
658
|
+
* a physics one.
|
|
659
|
+
*/
|
|
660
|
+
declare class Respawn extends Node {
|
|
661
|
+
static override readonly typeName: string;
|
|
662
|
+
static override readonly signals: readonly string[];
|
|
663
|
+
static readonly props: PropSchema;
|
|
664
|
+
/**
|
|
665
|
+
* Who is being caught. Defaults to the parent, which is the whole point of
|
|
666
|
+
* hanging this off the thing it guards.
|
|
667
|
+
*/
|
|
668
|
+
target: string;
|
|
669
|
+
/**
|
|
670
|
+
* The line past which the target has left the world, in the scene's own
|
|
671
|
+
* down: 3D counts down (−y), 2D counts down the screen (+y).
|
|
672
|
+
*
|
|
673
|
+
* `null` is AUTO — the spawn position offset by the same distance the
|
|
674
|
+
* playtest's own fall detector uses, so "the level says it fell" and "the
|
|
675
|
+
* ladder says it fell" cannot disagree. Author a number when the level has a
|
|
676
|
+
* real floor to name; leave it alone otherwise.
|
|
677
|
+
*/
|
|
678
|
+
below: number | null;
|
|
679
|
+
/** Where to put it back. Empty = wherever it started. */
|
|
680
|
+
to: number[];
|
|
681
|
+
/**
|
|
682
|
+
* Zero the body's velocity on the way back.
|
|
683
|
+
*
|
|
684
|
+
* On by default because the alternative is invisible: a body respawned with
|
|
685
|
+
* 40 m/s of accumulated fall still in it drops straight back through the
|
|
686
|
+
* line on the next frame, and the level flickers instead of resuming.
|
|
687
|
+
*/
|
|
688
|
+
resetVelocity: boolean;
|
|
689
|
+
/** How many times this has caught the target — for HUDs and tests. */
|
|
690
|
+
falls: number;
|
|
691
|
+
private home;
|
|
692
|
+
private line;
|
|
693
|
+
/** +1 when "out" means y greater (2D screen-down), −1 when smaller (3D). */
|
|
694
|
+
private down;
|
|
695
|
+
override onReady(): void;
|
|
696
|
+
private spawnY;
|
|
697
|
+
/**
|
|
698
|
+
* Which way is out, and where the line is.
|
|
699
|
+
*
|
|
700
|
+
* Resolved on the first `update` rather than in `onReady`, because
|
|
701
|
+
* `onReady` runs inside `loadScene` — BEFORE `setScene` attaches the engine —
|
|
702
|
+
* so the scene's `dimension` is not readable there yet. Reading it there
|
|
703
|
+
* silently gives every 2D scene the 3D answer, and a line above a falling
|
|
704
|
+
* player fires on frame one, forever.
|
|
705
|
+
*
|
|
706
|
+
* Not called `resolve`: `Node` has a private method by that name and
|
|
707
|
+
* `getNodeOrNull` calls it, so a subclass method with the same name
|
|
708
|
+
* overrides it on the prototype — `private` is a TypeScript fiction that
|
|
709
|
+
* costs nothing at runtime — and every path lookup this node made returned a
|
|
710
|
+
* number. `node-method-shadowing.test.ts` refuses the whole family now.
|
|
711
|
+
*/
|
|
712
|
+
private fallLine;
|
|
713
|
+
/** The node this guards, or itself if the path no longer resolves. */
|
|
714
|
+
private guarded;
|
|
715
|
+
override update(_dt: number): void;
|
|
716
|
+
}
|
|
717
|
+
//#endregion
|
|
627
718
|
//#region src/core/nodes/timer.d.ts
|
|
628
719
|
/**
|
|
629
720
|
* The canonical serializable game clock — never `setTimeout` in game logic.
|
|
@@ -1174,4 +1265,4 @@ declare function computeViewport(canvasW: number, canvasH: number, viewport: {
|
|
|
1174
1265
|
/** Engine version. Kept in sync with package.json by the release pipeline. */
|
|
1175
1266
|
declare const VERSION: string;
|
|
1176
1267
|
//#endregion
|
|
1177
|
-
export { type AnimationDef, type AnimationEntry, type AssetFailure, AudioBuses, type AudioElementLike, AudioPlayer, BASE_LOCALE, Behavior, type BehaviorCtor, type BehaviorState, type BusName, CONST_REF_KEY, type ComputedViewport, type ConnectionJson, type CrossfadeGains, type DebugLineSource, type DeviceHints, type DriveScript, type DriveStep, type EffectEvent, type EffectKind, EffectLog, Engine, type EngineOptions, type EngineStats, type FindPathOptions, type GameStats, type GroupedLog, type HudAnchor, HudLayer, IncantoError, type IncantoErrorCode, type IncantoErrorDetails, InputMap, type JsonKind, type JsonObject, type JsonValue, type Listener, type LiveInput, type LiveReport, type LoadSceneOptions, type LocaleTables, Localization, type LogEntry, type LogLevel, LogManager, type MusicBackend, MusicManager, type MusicTrack, Node, type NodeCtor, type NodeJson, type NodeLifecycle, ORDER_GROUP_BASE, type OrderGroup, type OrderGroupTable, PARTICLE_PRESETS, PARTICLE_PRESET_NAMES, type ParsedNodePath, type ParticlePresetValues, ParticleSim, type ParticleSimConfig, type ParticleView, type PathGrid, type PlayMusicOptions, type PreloadResult, type PropDef, type PropSchema, type QualityTier, ROLLOFF_MODELS, type Recorder, type RefNode, type RefProblem, type RefResult, type RendererStats, type ReplayEvent, type ReplayJson, type ResolvedRendering, type ResolvedViewport, type RestoreReport, Rng, type RolloffModel, SCENE_FORMAT, SFX_PRESETS, SFX_PRESET_NAMES, type SaveSlot, SaveSlots, type SaveStore, Scene, type SceneJson, SceneTree, type Scheduler, Settings, type SettingsValues, SfxEngine, type SfxParams, type SfxPlayOptions, type SfxWave, Signal, type SignalListener, type SpatialParams, type SynthOptions, T_PREFIX, Timer, TouchControls, type TouchControlsOptions, UiBanner, UiBar, UiButton, UiDialogue, UiFrameCapSelect, UiImage, UiLanguageSelect, UiMuteToggle, UiPanel, UiQualitySelect, UiRenderScaleSelect, UiSelect, UiSlider, UiText, UiToggle, UiVolumeSlider, VERSION, type Vec3, type ViewportFit, type Voice, type VoicePreset, WebAudioMusicBackend, applyParticlePreset, assetUrls, attachTouchControls, auditScene, behaviorSchema, behaviorSignals, behaviorsWithoutSave, captureBehaviors, clearBehaviors, clearRegistry, computeViewport, createNode, createNoise2D, createSaveStore, crossfadeGains, describeRefProblem, duplicateNode, effectiveOrder, fadeGain, findPath, getBehavior, getNodeSchema, getNodeSignals, getNodeType, gridFromRows, isAudioContextAvailable, isConstRef, joystickVector, jsonClone, jsonEquals, jsonKind, loadScene, logReport, logText, mergeStaticSignals, newUid, nodeRefWarnings, parseDrive, parseNodePath, preloadSceneAssets, preloadUrls, qualityEnvironment, readDeviceHints, registerBehavior, registerCoreNodes, registerNode, registeredBehaviors, registeredTypes, replay, resolveAnimation, resolveConstants, resolveFrames, resolveOrderGroups, resolveRefInJson, resolveRendering, resolveViewport, restoreBehaviors, savesWithoutUid, serializeNode, sfxDuration, spatialGain, spatialPan, startRecording, suggestLocale, suggestQuality, synthSfx, translationKey };
|
|
1268
|
+
export { type AnimationDef, type AnimationEntry, type AssetFailure, AudioBuses, type AudioElementLike, AudioPlayer, BASE_LOCALE, Behavior, type BehaviorCtor, type BehaviorState, type BusName, CONST_REF_KEY, type ComputedViewport, type ConnectionJson, type CrossfadeGains, type DebugLineSource, type DeviceHints, type DriveScript, type DriveStep, type EffectEvent, type EffectKind, EffectLog, Engine, type EngineOptions, type EngineStats, type FindPathOptions, type GameStats, type GroupedLog, type HudAnchor, HudLayer, IncantoError, type IncantoErrorCode, type IncantoErrorDetails, InputMap, type JsonKind, type JsonObject, type JsonValue, type Listener, type LiveInput, type LiveReport, type LoadSceneOptions, type LocaleTables, Localization, type LogEntry, type LogLevel, LogManager, type MusicBackend, MusicManager, type MusicTrack, Node, type NodeCtor, type NodeJson, type NodeLifecycle, ORDER_GROUP_BASE, type OrderGroup, type OrderGroupTable, PARTICLE_PRESETS, PARTICLE_PRESET_NAMES, type ParsedNodePath, type ParticlePresetValues, ParticleSim, type ParticleSimConfig, type ParticleView, type PathGrid, type PlayMusicOptions, type PreloadResult, type PropDef, type PropSchema, type QualityTier, ROLLOFF_MODELS, type Recorder, type RefNode, type RefProblem, type RefResult, type RendererStats, type ReplayEvent, type ReplayJson, type ResolvedRendering, type ResolvedViewport, Respawn, type RestoreReport, Rng, type RolloffModel, SCENE_FORMAT, SFX_PRESETS, SFX_PRESET_NAMES, type SaveSlot, SaveSlots, type SaveStore, Scene, type SceneJson, SceneTree, type Scheduler, Settings, type SettingsValues, SfxEngine, type SfxParams, type SfxPlayOptions, type SfxWave, Signal, type SignalListener, type SpatialParams, type SynthOptions, T_PREFIX, Timer, TouchControls, type TouchControlsOptions, UiBanner, UiBar, UiButton, UiDialogue, UiFrameCapSelect, UiImage, UiLanguageSelect, UiMuteToggle, UiPanel, UiQualitySelect, UiRenderScaleSelect, UiSelect, UiSlider, UiText, UiToggle, UiVolumeSlider, VERSION, type Vec3, type ViewportFit, type Voice, type VoicePreset, WebAudioMusicBackend, applyParticlePreset, assetUrls, attachTouchControls, auditScene, behaviorSchema, behaviorSignals, behaviorsWithoutSave, captureBehaviors, clearBehaviors, clearRegistry, computeViewport, createNode, createNoise2D, createSaveStore, crossfadeGains, describeRefProblem, duplicateNode, effectiveOrder, fadeGain, findPath, getBehavior, getNodeSchema, getNodeSignals, getNodeType, gridFromRows, isAudioContextAvailable, isConstRef, joystickVector, jsonClone, jsonEquals, jsonKind, loadScene, logReport, logText, mergeStaticSignals, newUid, nodeRefWarnings, parseDrive, parseNodePath, preloadSceneAssets, preloadUrls, qualityEnvironment, readDeviceHints, registerBehavior, registerCoreNodes, registerNode, registeredBehaviors, registeredTypes, replay, resolveAnimation, resolveConstants, resolveFrames, resolveOrderGroups, resolveRefInJson, resolveRendering, resolveViewport, restoreBehaviors, savesWithoutUid, serializeNode, sfxDuration, spatialGain, spatialPan, startRecording, suggestLocale, suggestQuality, synthSfx, translationKey };
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { A as describeRefProblem, B as registeredBehaviors, C as ORDER_GROUP_BASE, E as InputMap, F as behaviorSchema, I as behaviorSignals, L as clearBehaviors, M as resolveRefInJson, N as parseNodePath, O as Node, P as Behavior, R as getBehavior, S as createSaveStore, T as resolveOrderGroups, V as Signal, _ as SaveSlots, a as serializeNode, b as restoreBehaviors, d as computeViewport, f as resolveViewport, g as resolveConstants, h as isConstRef, i as Scene, j as nodeRefWarnings, m as CONST_REF_KEY, n as loadScene, o as SCENE_FORMAT, p as SceneTree, v as behaviorsWithoutSave, w as effectiveOrder, x as savesWithoutUid, y as captureBehaviors, z as registerBehavior } from "./loader-
|
|
2
|
-
import { A as
|
|
1
|
+
import { A as describeRefProblem, B as registeredBehaviors, C as ORDER_GROUP_BASE, E as InputMap, F as behaviorSchema, I as behaviorSignals, L as clearBehaviors, M as resolveRefInJson, N as parseNodePath, O as Node, P as Behavior, R as getBehavior, S as createSaveStore, T as resolveOrderGroups, V as Signal, _ as SaveSlots, a as serializeNode, b as restoreBehaviors, d as computeViewport, f as resolveViewport, g as resolveConstants, h as isConstRef, i as Scene, j as nodeRefWarnings, m as CONST_REF_KEY, n as loadScene, o as SCENE_FORMAT, p as SceneTree, v as behaviorsWithoutSave, w as effectiveOrder, x as savesWithoutUid, y as captureBehaviors, z as registerBehavior } from "./loader-zDynoew_.js";
|
|
2
|
+
import { A as UiBanner, B as isAudioContextAvailable, D as EffectLog, E as LogManager, F as T_PREFIX, G as SFX_PRESET_NAMES, H as spatialGain, I as suggestLocale, J as MusicManager, K as sfxDuration, M as UiText, N as BASE_LOCALE, O as HudLayer, P as Localization, Q as AudioBuses, R as translationKey, S as qualityEnvironment, T as suggestQuality, U as spatialPan, V as ROLLOFF_MODELS, W as SFX_PRESETS, X as crossfadeGains, Y as WebAudioMusicBackend, Z as fadeGain, _ as AudioPlayer, a as UiMuteToggle, b as Settings, c as UiRenderScaleSelect, d as UiToggle, f as UiVolumeSlider, g as UiDialogue, h as UiButton, i as UiLanguageSelect, j as UiBar, l as UiSelect, m as Respawn, n as UiFrameCapSelect, o as UiPanel, p as Timer, q as synthSfx, r as UiImage, s as UiQualitySelect, t as registerCoreNodes, u as UiSlider, v as Engine, w as readDeviceHints, z as SfxEngine } from "./register-Btm7_Emq.js";
|
|
3
3
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
4
4
|
import { t as Rng } from "./rng-DP-SR7eg.js";
|
|
5
5
|
import { n as jsonEquals, r as jsonKind, t as jsonClone } from "./json-CwwhxQgb.js";
|
|
6
6
|
import { a as getNodeSignals, c as mergeStaticSignals, i as getNodeSchema, l as registerNode, n as clearRegistry, o as getNodeType, r as createNode, u as registeredTypes } from "./registry-CF70EArN.js";
|
|
7
|
-
import { n as startRecording, r as auditScene, t as replay } from "./replay-
|
|
7
|
+
import { n as startRecording, r as auditScene, t as replay } from "./replay-Bhdntkvq.js";
|
|
8
8
|
import { a as logReport, i as resolveRendering, n as attachTouchControls, o as logText, r as joystickVector, s as parseDrive, t as TouchControls } from "./touch-DESwnpOc.js";
|
|
9
9
|
import { t as createNoise2D } from "./noise-CGUMx44x.js";
|
|
10
10
|
import { a as PARTICLE_PRESETS, i as ParticleSim, n as resolveFrames, o as PARTICLE_PRESET_NAMES, s as applyParticlePreset, t as resolveAnimation } from "./sprite-animation-CY-mrr1L.js";
|
|
11
|
-
import { a as preloadUrls, i as preloadSceneAssets, n as newUid, o as findPath, r as assetUrls, s as gridFromRows, t as VERSION } from "./src-
|
|
12
|
-
import { t as duplicateNode } from "./duplicate-
|
|
13
|
-
export { AudioBuses, AudioPlayer, BASE_LOCALE, Behavior, CONST_REF_KEY, EffectLog, Engine, HudLayer, IncantoError, InputMap, Localization, LogManager, MusicManager, Node, ORDER_GROUP_BASE, PARTICLE_PRESETS, PARTICLE_PRESET_NAMES, ParticleSim, ROLLOFF_MODELS, Rng, SCENE_FORMAT, SFX_PRESETS, SFX_PRESET_NAMES, SaveSlots, Scene, SceneTree, Settings, SfxEngine, Signal, T_PREFIX, Timer, TouchControls, UiBanner, UiBar, UiButton, UiDialogue, UiFrameCapSelect, UiImage, UiLanguageSelect, UiMuteToggle, UiPanel, UiQualitySelect, UiRenderScaleSelect, UiSelect, UiSlider, UiText, UiToggle, UiVolumeSlider, VERSION, WebAudioMusicBackend, applyParticlePreset, assetUrls, attachTouchControls, auditScene, behaviorSchema, behaviorSignals, behaviorsWithoutSave, captureBehaviors, clearBehaviors, clearRegistry, computeViewport, createNode, createNoise2D, createSaveStore, crossfadeGains, describeRefProblem, duplicateNode, effectiveOrder, fadeGain, findPath, getBehavior, getNodeSchema, getNodeSignals, getNodeType, gridFromRows, isAudioContextAvailable, isConstRef, joystickVector, jsonClone, jsonEquals, jsonKind, loadScene, logReport, logText, mergeStaticSignals, newUid, nodeRefWarnings, parseDrive, parseNodePath, preloadSceneAssets, preloadUrls, qualityEnvironment, readDeviceHints, registerBehavior, registerCoreNodes, registerNode, registeredBehaviors, registeredTypes, replay, resolveAnimation, resolveConstants, resolveFrames, resolveOrderGroups, resolveRefInJson, resolveRendering, resolveViewport, restoreBehaviors, savesWithoutUid, serializeNode, sfxDuration, spatialGain, spatialPan, startRecording, suggestLocale, suggestQuality, synthSfx, translationKey };
|
|
11
|
+
import { a as preloadUrls, i as preloadSceneAssets, n as newUid, o as findPath, r as assetUrls, s as gridFromRows, t as VERSION } from "./src-BlV9Sv4m.js";
|
|
12
|
+
import { t as duplicateNode } from "./duplicate-BOOKmkQ7.js";
|
|
13
|
+
export { AudioBuses, AudioPlayer, BASE_LOCALE, Behavior, CONST_REF_KEY, EffectLog, Engine, HudLayer, IncantoError, InputMap, Localization, LogManager, MusicManager, Node, ORDER_GROUP_BASE, PARTICLE_PRESETS, PARTICLE_PRESET_NAMES, ParticleSim, ROLLOFF_MODELS, Respawn, Rng, SCENE_FORMAT, SFX_PRESETS, SFX_PRESET_NAMES, SaveSlots, Scene, SceneTree, Settings, SfxEngine, Signal, T_PREFIX, Timer, TouchControls, UiBanner, UiBar, UiButton, UiDialogue, UiFrameCapSelect, UiImage, UiLanguageSelect, UiMuteToggle, UiPanel, UiQualitySelect, UiRenderScaleSelect, UiSelect, UiSlider, UiText, UiToggle, UiVolumeSlider, VERSION, WebAudioMusicBackend, applyParticlePreset, assetUrls, attachTouchControls, auditScene, behaviorSchema, behaviorSignals, behaviorsWithoutSave, captureBehaviors, clearBehaviors, clearRegistry, computeViewport, createNode, createNoise2D, createSaveStore, crossfadeGains, describeRefProblem, duplicateNode, effectiveOrder, fadeGain, findPath, getBehavior, getNodeSchema, getNodeSignals, getNodeType, gridFromRows, isAudioContextAvailable, isConstRef, joystickVector, jsonClone, jsonEquals, jsonKind, loadScene, logReport, logText, mergeStaticSignals, newUid, nodeRefWarnings, parseDrive, parseNodePath, preloadSceneAssets, preloadUrls, qualityEnvironment, readDeviceHints, registerBehavior, registerCoreNodes, registerNode, registeredBehaviors, registeredTypes, replay, resolveAnimation, resolveConstants, resolveFrames, resolveOrderGroups, resolveRefInJson, resolveRendering, resolveViewport, restoreBehaviors, savesWithoutUid, serializeNode, sfxDuration, spatialGain, spatialPan, startRecording, suggestLocale, suggestQuality, synthSfx, translationKey };
|
|
@@ -854,6 +854,92 @@ function nodeTypeMethods(type) {
|
|
|
854
854
|
return out;
|
|
855
855
|
}
|
|
856
856
|
const methodCache = /* @__PURE__ */ new Map();
|
|
857
|
+
/** Every code the engine can ever receive, keyboard + mouse + gamepad. */
|
|
858
|
+
const INPUT_CODES = new Set([
|
|
859
|
+
...[..."ABCDEFGHIJKLMNOPQRSTUVWXYZ"].map((c) => `Key${c}`),
|
|
860
|
+
...Array.from({ length: 10 }, (_, i) => `Digit${i}`),
|
|
861
|
+
...Array.from({ length: 10 }, (_, i) => `Numpad${i}`),
|
|
862
|
+
"NumpadAdd",
|
|
863
|
+
"NumpadSubtract",
|
|
864
|
+
"NumpadMultiply",
|
|
865
|
+
"NumpadDivide",
|
|
866
|
+
"NumpadDecimal",
|
|
867
|
+
"NumpadEnter",
|
|
868
|
+
...Array.from({ length: 12 }, (_, i) => `F${i + 1}`),
|
|
869
|
+
"ArrowUp",
|
|
870
|
+
"ArrowDown",
|
|
871
|
+
"ArrowLeft",
|
|
872
|
+
"ArrowRight",
|
|
873
|
+
"Space",
|
|
874
|
+
"Enter",
|
|
875
|
+
"Escape",
|
|
876
|
+
"Tab",
|
|
877
|
+
"Backspace",
|
|
878
|
+
"Delete",
|
|
879
|
+
"Insert",
|
|
880
|
+
"Home",
|
|
881
|
+
"End",
|
|
882
|
+
"PageUp",
|
|
883
|
+
"PageDown",
|
|
884
|
+
"CapsLock",
|
|
885
|
+
"ShiftLeft",
|
|
886
|
+
"ShiftRight",
|
|
887
|
+
"ControlLeft",
|
|
888
|
+
"ControlRight",
|
|
889
|
+
"AltLeft",
|
|
890
|
+
"AltRight",
|
|
891
|
+
"MetaLeft",
|
|
892
|
+
"MetaRight",
|
|
893
|
+
"ContextMenu",
|
|
894
|
+
"Backquote",
|
|
895
|
+
"Minus",
|
|
896
|
+
"Equal",
|
|
897
|
+
"BracketLeft",
|
|
898
|
+
"BracketRight",
|
|
899
|
+
"Backslash",
|
|
900
|
+
"Semicolon",
|
|
901
|
+
"Quote",
|
|
902
|
+
"Comma",
|
|
903
|
+
"Period",
|
|
904
|
+
"Slash",
|
|
905
|
+
"IntlBackslash",
|
|
906
|
+
"IntlRo",
|
|
907
|
+
"IntlYen",
|
|
908
|
+
...Array.from({ length: 5 }, (_, i) => `Mouse${i}`),
|
|
909
|
+
...Array.from({ length: 17 }, (_, i) => `Pad${i}`)
|
|
910
|
+
]);
|
|
911
|
+
/** The one a typo probably meant, or null when nothing is close enough. */
|
|
912
|
+
function nearestCode(written) {
|
|
913
|
+
const lower = written.toLowerCase();
|
|
914
|
+
if (/^[a-z]$/.test(lower)) return `Key${lower.toUpperCase()}`;
|
|
915
|
+
if (/^[0-9]$/.test(written)) return `Digit${written}`;
|
|
916
|
+
if (lower === "shift" || lower === "ctrl" || lower === "control" || lower === "alt") {
|
|
917
|
+
const side = lower === "ctrl" ? "Control" : lower[0]?.toUpperCase() + lower.slice(1);
|
|
918
|
+
return `${side}Left" or "${side}Right`;
|
|
919
|
+
}
|
|
920
|
+
if (lower === "esc") return "Escape";
|
|
921
|
+
if (lower === "return") return "Enter";
|
|
922
|
+
if (lower === "up" || lower === "down" || lower === "left" || lower === "right") return `Arrow${lower[0]?.toUpperCase()}${lower.slice(1)}`;
|
|
923
|
+
let best = null;
|
|
924
|
+
let bestScore = 3;
|
|
925
|
+
for (const code of INPUT_CODES) {
|
|
926
|
+
const d = distance$1(lower, code.toLowerCase());
|
|
927
|
+
if (d < bestScore) {
|
|
928
|
+
bestScore = d;
|
|
929
|
+
best = code;
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
return best;
|
|
933
|
+
}
|
|
934
|
+
function distance$1(a, b) {
|
|
935
|
+
const rows = Array.from({ length: a.length + 1 }, (_, i) => [i, ...Array(b.length).fill(0)]);
|
|
936
|
+
for (let j = 0; j <= b.length; j++) rows[0][j] = j;
|
|
937
|
+
for (let i = 1; i <= a.length; i++) for (let j = 1; j <= b.length; j++) {
|
|
938
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
939
|
+
rows[i][j] = Math.min(rows[i - 1][j] + 1, rows[i][j - 1] + 1, rows[i - 1][j - 1] + cost);
|
|
940
|
+
}
|
|
941
|
+
return rows[a.length][b.length];
|
|
942
|
+
}
|
|
857
943
|
//#endregion
|
|
858
944
|
//#region src/core/input.ts
|
|
859
945
|
/** Keys typed into editable elements belong to the page UI, not the game. */
|
|
@@ -1322,6 +1408,21 @@ const DIRECTIONS = [
|
|
|
1322
1408
|
"left",
|
|
1323
1409
|
"right"
|
|
1324
1410
|
];
|
|
1411
|
+
/**
|
|
1412
|
+
* Refuse a key code the engine can never receive.
|
|
1413
|
+
*
|
|
1414
|
+
* The map is looked up by EXACT string against what the browser hands us, so
|
|
1415
|
+
* `"W"`, `"space"` and `"Shift"` — the three most natural things to write — bind
|
|
1416
|
+
* to nothing at all. They loaded clean, passed `incanto check`, and left the
|
|
1417
|
+
* player without that control, with nothing anywhere to say so.
|
|
1418
|
+
*/
|
|
1419
|
+
function checkCodes(action, where, keys) {
|
|
1420
|
+
for (const key of keys) {
|
|
1421
|
+
if (typeof key !== "string" || INPUT_CODES.has(key)) continue;
|
|
1422
|
+
const guess = nearestCode(key);
|
|
1423
|
+
throw new IncantoError("BAD_FORMAT", `Input action '${action}'${where}: '${key}' is not a key code, so nothing can ever press it${guess ? ` — did you mean "${guess}"?` : ""}. Codes are KeyboardEvent.code values (KeyW, Space, ArrowLeft, ShiftLeft), plus Mouse0..4 and Pad0..16.`, { prop: "keys" });
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1325
1426
|
function parseInputDecls(decls) {
|
|
1326
1427
|
const out = /* @__PURE__ */ new Map();
|
|
1327
1428
|
for (const [name, raw] of Object.entries(decls)) {
|
|
@@ -1330,6 +1431,7 @@ function parseInputDecls(decls) {
|
|
|
1330
1431
|
const keys = decl.keys;
|
|
1331
1432
|
if (!Array.isArray(keys) || keys.some((k) => typeof k !== "string")) throw new IncantoError("BAD_FORMAT", `Input action '${name}': button "keys" must be an array of KeyboardEvent.code strings.`);
|
|
1332
1433
|
if (decl.touch !== void 0 && decl.touch !== "button") throw new IncantoError("BAD_FORMAT", `Input action '${name}': button "touch" must be 'button', got ${JSON.stringify(decl.touch)}.`);
|
|
1434
|
+
checkCodes(name, "", keys);
|
|
1333
1435
|
out.set(name, {
|
|
1334
1436
|
type: "button",
|
|
1335
1437
|
keys: [...keys],
|
|
@@ -1345,6 +1447,7 @@ function parseInputDecls(decls) {
|
|
|
1345
1447
|
}
|
|
1346
1448
|
if (decl.touch !== void 0 && decl.touch !== "joystick") throw new IncantoError("BAD_FORMAT", `Input action '${name}': vector2 "touch" must be 'joystick', got ${JSON.stringify(decl.touch)}.`);
|
|
1347
1449
|
const d = dirs;
|
|
1450
|
+
for (const dir of DIRECTIONS) checkCodes(name, ` (${dir})`, d[dir]);
|
|
1348
1451
|
out.set(name, {
|
|
1349
1452
|
type: "vector2",
|
|
1350
1453
|
keys: {
|
|
@@ -2712,6 +2815,53 @@ function validateStrings(strings) {
|
|
|
2712
2815
|
}
|
|
2713
2816
|
}
|
|
2714
2817
|
//#endregion
|
|
2818
|
+
//#region src/core/scene/wire-arity.ts
|
|
2819
|
+
/**
|
|
2820
|
+
* A wire that hands a handler fewer arguments than it needs.
|
|
2821
|
+
*
|
|
2822
|
+
* The engine hard-validates node types, prop kinds, every prop enum, node
|
|
2823
|
+
* paths, handler existence and signal declaration — and never that the signal's
|
|
2824
|
+
* ARGUMENTS fit the handler it is wired to. `died` carries none and
|
|
2825
|
+
* `ScoreKeeper.addScore(n)` wants one, so the most natural way to score a kill:
|
|
2826
|
+
*
|
|
2827
|
+
* ```json
|
|
2828
|
+
* { "signal": "died", "from": "Enemy", "to": "Keeper", "handler": "addScore" }
|
|
2829
|
+
* ```
|
|
2830
|
+
*
|
|
2831
|
+
* sets the score to `NaN` on the first kill and the win condition becomes
|
|
2832
|
+
* permanently unreachable. Measured:
|
|
2833
|
+
*
|
|
2834
|
+
* ```
|
|
2835
|
+
* score = NaN | Number.isNaN: true
|
|
2836
|
+
* HUD text = "Kills 0"
|
|
2837
|
+
* won emitted = false
|
|
2838
|
+
* engine errors = 0
|
|
2839
|
+
* incanto-check → 1/1 scene(s) valid
|
|
2840
|
+
* ```
|
|
2841
|
+
*
|
|
2842
|
+
* Two agents building different games from the published package hit it
|
|
2843
|
+
* independently, which is what makes it worth a rule rather than a footnote.
|
|
2844
|
+
*
|
|
2845
|
+
* **Why this is not a load error.** Nothing declares how many arguments a signal
|
|
2846
|
+
* carries — `static signals` is a list of names — so at load the pair cannot be
|
|
2847
|
+
* compared. At the moment the wire FIRES both halves are known exactly, and
|
|
2848
|
+
* that is where this lives.
|
|
2849
|
+
*
|
|
2850
|
+
* **Why `fn.length` is the right measure.** It counts leading parameters
|
|
2851
|
+
* without defaults, so it makes precisely the distinction the engine needs:
|
|
2852
|
+
* `addScore(n)` is 1 and gets reported; `GameFlow.win(text = 'YOU WIN')` is 0
|
|
2853
|
+
* and is silent, because that wire genuinely works — the banner defaults. The
|
|
2854
|
+
* same arg-less signal into a same-arity method is right in one case and wrong
|
|
2855
|
+
* in the other, and only the defaults tell them apart.
|
|
2856
|
+
*/
|
|
2857
|
+
function reportShortWire(from, conn, fn, args) {
|
|
2858
|
+
diagnose(from.tree?.engine ?? null, "error", `[incanto] '${conn.signal}' from '${conn.from}' carries ${args.length} argument(s) and '${conn.to}.${conn.handler}' needs ${fn.length}. The missing one(s) arrive as undefined — a number handler gets NaN and never recovers. Give the handler a default, or wire a signal that carries what it needs.`);
|
|
2859
|
+
}
|
|
2860
|
+
/** Does this wire under-feed its handler? */
|
|
2861
|
+
function isShortWire(fn, args) {
|
|
2862
|
+
return args.length < fn.length;
|
|
2863
|
+
}
|
|
2864
|
+
//#endregion
|
|
2715
2865
|
//#region src/core/scene/loader.ts
|
|
2716
2866
|
var loader_exports = /* @__PURE__ */ __exportAll({
|
|
2717
2867
|
buildNodeJson: () => buildNodeJson,
|
|
@@ -2920,17 +3070,25 @@ function wireConnections(root, connections, declareSignals = false) {
|
|
|
2920
3070
|
const behaviorFn = toNode.behavior ? toNode.behavior[conn.handler] : void 0;
|
|
2921
3071
|
if (typeof nodeFn !== "function" && typeof behaviorFn !== "function" && !(toNode.behavior instanceof StubBehavior)) throw new IncantoError("UNKNOWN_HANDLER", `Connection '${conn.signal}' → '${conn.to}' names handler '${conn.handler}', but '${toNode.name}' has no such method` + (toNode.behavior ? ` (nor does its behavior '${toNode.script?.name}')` : "") + `.`);
|
|
2922
3072
|
if (ambiguousHandler(toNode, conn.handler)) throw ambiguousHandlerError(toNode, conn.handler, toNode.script?.name, conn.to);
|
|
3073
|
+
let saidShort = false;
|
|
3074
|
+
const feed = (fn, self, args) => {
|
|
3075
|
+
if (!saidShort && isShortWire(fn, args)) {
|
|
3076
|
+
saidShort = true;
|
|
3077
|
+
reportShortWire(fromNode, conn, fn, args);
|
|
3078
|
+
}
|
|
3079
|
+
fn.apply(self, args);
|
|
3080
|
+
};
|
|
2923
3081
|
const listener = (...args) => {
|
|
2924
3082
|
if (conn.filter && !matchesFilter(args[0], conn.filter)) return;
|
|
2925
3083
|
if (conn.once) fromNode.off(conn.signal, listener);
|
|
2926
3084
|
try {
|
|
2927
3085
|
const fn = toNode[conn.handler];
|
|
2928
3086
|
if (typeof fn === "function") {
|
|
2929
|
-
fn
|
|
3087
|
+
feed(fn, toNode, args);
|
|
2930
3088
|
return;
|
|
2931
3089
|
}
|
|
2932
3090
|
const bfn = toNode.behavior ? toNode.behavior[conn.handler] : void 0;
|
|
2933
|
-
if (typeof bfn === "function") bfn
|
|
3091
|
+
if (typeof bfn === "function") feed(bfn, toNode.behavior, args);
|
|
2934
3092
|
} catch (error) {
|
|
2935
3093
|
quarantineWire(fromNode, toNode, conn, listener, error);
|
|
2936
3094
|
}
|
package/dist/net.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { n as createAgent8Server } from "./agent8-CvsfVskX.js";
|
|
2
|
-
import { a as NetworkManager, d as createLocalGameServer, f as LoopbackHub, l as LocalGameServer, n as registerNodesNet, o as applySyncPatch, p as LoopbackTransport, r as NetworkSpawner, t as createSplitScreen, u as LocalGameServerTransport } from "./split-screen-
|
|
2
|
+
import { a as NetworkManager, d as createLocalGameServer, f as LoopbackHub, l as LocalGameServer, n as registerNodesNet, o as applySyncPatch, p as LoopbackTransport, r as NetworkSpawner, t as createSplitScreen, u as LocalGameServerTransport } from "./split-screen-Dx0LvzqS.js";
|
|
3
3
|
export { LocalGameServer, LocalGameServerTransport, LoopbackHub, LoopbackTransport, NetworkManager, NetworkSpawner, applySyncPatch, createAgent8Server, createLocalGameServer, createSplitScreen, registerNodesNet };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import { k as diagnose } from "./loader-
|
|
2
|
+
import { k as diagnose } from "./loader-zDynoew_.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-1cKM8DEj.js";
|
|
5
5
|
import { n as registerDebugSource } from "./debug-draw-BM3DsvtT.js";
|
|
6
6
|
import { t as withoutRapierInitNoise } from "./quiet-rapier-BAJ4K94N.js";
|
|
7
7
|
//#region src/2d/physics/physics-2d.ts
|
|
@@ -100,7 +100,10 @@ var Physics2D = class {
|
|
|
100
100
|
node.position = [t.x * SCALE - ox, t.y * SCALE - oy];
|
|
101
101
|
}
|
|
102
102
|
if (node instanceof RigidBody2D) {
|
|
103
|
-
if (!node.fixedRotation)
|
|
103
|
+
if (!node.fixedRotation) {
|
|
104
|
+
node.rotation = e.body.rotation() / DEG;
|
|
105
|
+
e.lastRot = node.rotation;
|
|
106
|
+
}
|
|
104
107
|
const lv = e.body.linvel();
|
|
105
108
|
node.linearVelocity = [lv.x * SCALE, lv.y * SCALE];
|
|
106
109
|
e.lastVX = node.linearVelocity[0] ?? 0;
|
|
@@ -428,6 +431,7 @@ var Physics2D = class {
|
|
|
428
431
|
collider,
|
|
429
432
|
lastX: wx,
|
|
430
433
|
lastY: wy,
|
|
434
|
+
lastRot: node.rotation,
|
|
431
435
|
lastVX: 0,
|
|
432
436
|
lastVY: 0
|
|
433
437
|
};
|
|
@@ -456,6 +460,10 @@ var Physics2D = class {
|
|
|
456
460
|
e.lastX = px;
|
|
457
461
|
e.lastY = py;
|
|
458
462
|
}
|
|
463
|
+
if (node.rotation !== e.lastRot) {
|
|
464
|
+
e.body.setRotation(node.rotation * DEG, true);
|
|
465
|
+
e.lastRot = node.rotation;
|
|
466
|
+
}
|
|
459
467
|
if (node instanceof RigidBody2D) {
|
|
460
468
|
const vx = node.linearVelocity[0] ?? 0;
|
|
461
469
|
const vy = node.linearVelocity[1] ?? 0;
|