incanto 0.39.0 → 0.40.1

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.
Files changed (41) hide show
  1. package/dist/2d.d.ts +2 -2
  2. package/dist/2d.js +3 -3
  3. package/dist/3d.d.ts +31 -3
  4. package/dist/3d.js +4 -4
  5. package/dist/{behavior-BNNSIXS5.d.ts → behavior-TA8nySqb.d.ts} +21 -0
  6. package/dist/{create-game-DEtU2PRg.js → create-game-B7c5icho.js} +4 -4
  7. package/dist/{create-game-R_8_gTCU.js → create-game-p-O66Y_U.js} +4 -4
  8. package/dist/debug.d.ts +1 -1
  9. package/dist/{environment-presets-bokhdw_w.js → environment-presets-CIZXGKnS.js} +9 -5
  10. package/dist/{gameplay-BPGomkHR.js → gameplay-By8mslMc.js} +83 -17
  11. package/dist/gameplay.d.ts +1 -1
  12. package/dist/gameplay.js +1 -1
  13. package/dist/index.d.ts +4 -4
  14. package/dist/index.js +3 -3
  15. package/dist/{loader-mSL65Djg.d.ts → loader-CvSDKC3v.d.ts} +1 -1
  16. package/dist/net.d.ts +1 -1
  17. package/dist/net.js +2 -2
  18. package/dist/{pathfinding-D7W8uqm1.d.ts → pathfinding-_UlKUoox.d.ts} +1 -1
  19. package/dist/{physics-2d-B9ktSWrr.js → physics-2d-CxIqR1cW.js} +1 -1
  20. package/dist/{physics-3d-Cpgc5ZM0.js → physics-3d-CgIHivyL.js} +2 -2
  21. package/dist/react.d.ts +1 -1
  22. package/dist/react.js +1 -1
  23. package/dist/{register-C2iO_yLv.js → register-CgMO5JBY.js} +1 -1
  24. package/dist/{register-CiXP3-GB.js → register-D5v8iqjZ.js} +1 -1
  25. package/dist/{register-CGf5NKrc.js → register-DMFJI-c_.js} +15 -3
  26. package/dist/{replay-CwdWgz0f.d.ts → replay-DN-kG4va.d.ts} +1 -1
  27. package/dist/{replay-YQ9FW7Yh.js → replay-D_QY5Xm3.js} +1 -1
  28. package/dist/{test-Bf-lUxr2.js → test-DUrGse_6.js} +10 -10
  29. package/dist/test.d.ts +3 -3
  30. package/dist/test.js +2 -2
  31. package/dist/vite.js +1 -1
  32. package/editor/assets/{agent8-DfIcNKb4.js → agent8-CFq3LMrx.js} +1 -1
  33. package/editor/assets/{debug-ex82CxIM.js → debug-C2aomumK.js} +1 -1
  34. package/editor/assets/{index-eVTVArHd.js → index-CRwy6mq9.js} +83 -67
  35. package/editor/index.html +1 -1
  36. package/package.json +1 -1
  37. package/skills/incanto-environment.md +12 -1
  38. package/skills/incanto-performance.md +22 -6
  39. package/templates-app/beacon-isle-3d/package.json +1 -1
  40. package/templates-app/tps-3d/package.json +1 -1
  41. package/templates-app/village-quest-3d/package.json +1 -1
package/dist/2d.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { Et as Node, P as Scene$1, S as Scheduler, T as RendererStats, b as Engine, d as PropSchema, n as BehaviorCtor, w as GameStats } from "./behavior-BNNSIXS5.js";
1
+ import { Et as Node, P as Scene$1, S as Scheduler, T as RendererStats, b as Engine, d as PropSchema, n as BehaviorCtor, w as GameStats } from "./behavior-TA8nySqb.js";
2
2
  import { n as DiagnosticSink, t as EditorSwitchOptions } from "./editor-switch-DVwIGZdK.js";
3
3
  import { i as SceneJson, s as JsonObject } from "./schema-CFeioQRE.js";
4
- import { t as LoadSceneOptions } from "./loader-mSL65Djg.js";
4
+ import { t as LoadSceneOptions } from "./loader-CvSDKC3v.js";
5
5
  import { t as ParticleSim } from "./particle-sim-BzJ1yxoE.js";
6
6
  import { Group, Mesh, Object3D, Scene, Texture } from "three";
7
7
  import * as RapierNs from "@dimforge/rapier2d-compat";
package/dist/2d.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
2
- import { a as AssetStore2D, i as syncTree2D, r as Renderer2D, t as createGame2D } from "./create-game-DEtU2PRg.js";
3
- import { _ as RigidBody2D, a as parseCells, c as ColorRect2D, d as AnimatedSprite2D, f as Sprite2D, g as PhysicsBody2D, h as CharacterBody2D, i as mergeSolidRects, l as CharacterController2D, m as Area2D, n as UILayer, o as Particles2D, p as Joint2D, r as TileMap2D, s as Label, t as registerNodes2D, u as Camera2D, v as StaticBody2D, y as Node2D } from "./register-C2iO_yLv.js";
4
- import { n as enablePhysics2D, t as Physics2D } from "./physics-2d-B9ktSWrr.js";
2
+ import { a as AssetStore2D, i as syncTree2D, r as Renderer2D, t as createGame2D } from "./create-game-B7c5icho.js";
3
+ import { _ as RigidBody2D, a as parseCells, c as ColorRect2D, d as AnimatedSprite2D, f as Sprite2D, g as PhysicsBody2D, h as CharacterBody2D, i as mergeSolidRects, l as CharacterController2D, m as Area2D, n as UILayer, o as Particles2D, p as Joint2D, r as TileMap2D, s as Label, t as registerNodes2D, u as Camera2D, v as StaticBody2D, y as Node2D } from "./register-CgMO5JBY.js";
4
+ import { n as enablePhysics2D, t as Physics2D } from "./physics-2d-CxIqR1cW.js";
5
5
  //#region src/2d/library-sprite.ts
6
6
  /**
7
7
  * Turn a library sprite-animation JSON into a scene-ready spritesheet asset
package/dist/3d.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { D as QualityCaps, Et as Node, P as Scene$1, S as Scheduler, T as RendererStats, b as Engine, d as PropSchema, n as BehaviorCtor, w as GameStats } from "./behavior-BNNSIXS5.js";
1
+ import { D as QualityCaps, Et as Node, P as Scene$1, S as Scheduler, T as RendererStats, b as Engine, d as PropSchema, n as BehaviorCtor, w as GameStats } from "./behavior-TA8nySqb.js";
2
2
  import { n as DiagnosticSink, t as EditorSwitchOptions } from "./editor-switch-DVwIGZdK.js";
3
3
  import { i as SceneJson, s as JsonObject } from "./schema-CFeioQRE.js";
4
- import { t as LoadSceneOptions } from "./loader-mSL65Djg.js";
4
+ import { t as LoadSceneOptions } from "./loader-CvSDKC3v.js";
5
5
  import { t as ParticleSim } from "./particle-sim-BzJ1yxoE.js";
6
- import { n as PathGrid, s as SpatialPose } from "./pathfinding-D7W8uqm1.js";
6
+ import { n as PathGrid, s as SpatialPose } from "./pathfinding-_UlKUoox.js";
7
7
  import { AnimationClip, AnimationMixer, BufferGeometry, Color, DirectionalLight, Group, InstancedMesh, Mesh, MeshPhysicalMaterial, Object3D, PerspectiveCamera, Scene, ShaderMaterial, Texture, Vector3, WebGLRenderer } from "three";
8
8
  import { VRM } from "@pixiv/three-vrm";
9
9
  import { Sky } from "three/examples/jsm/objects/Sky.js";
@@ -1104,6 +1104,8 @@ declare class CharacterController3D extends Node3D {
1104
1104
  private bufferLeft;
1105
1105
  private jumpsUsed;
1106
1106
  private rising;
1107
+ /** Between the jump impulse and the landing — the animation's clock. */
1108
+ private jumping;
1107
1109
  private get body();
1108
1110
  override onEnterTree(): void;
1109
1111
  override fixedUpdate(dt: number): void;
@@ -3046,6 +3048,32 @@ declare class Water3D extends Node3D implements RenderHook3D, SunConsumer3D {
3046
3048
  * chose.
3047
3049
  */
3048
3050
  private effectiveQuality;
3051
+ /**
3052
+ * Whether collapsing to the simple shader would still produce a waterline.
3053
+ *
3054
+ * A tier may make a game cheaper; it may not make it look broken. Found by
3055
+ * looking at the four water templates at every tier in a browser: the ocean
3056
+ * at low met the beach in a razor-straight line, because the simple shader's
3057
+ * only shoreline is a fade of its own rectangle and that rectangle's edge was
3058
+ * 800 m out to sea. Big water keeps the fancy shader at low — and still loses
3059
+ * its mirror, so the saving moves rather than disappears.
3060
+ *
3061
+ * This is NOT a veto on the author: a scene that asked for `quality: 'simple'`
3062
+ * gets it at any size. The guardrail only stops a TIER from choosing it.
3063
+ */
3064
+ private canFadeItsOwnRim;
3065
+ /** Whether the planar mirror runs — the tier can take it away, never add it. */
3066
+ private effectiveMirror;
3067
+ /**
3068
+ * How often the reflection cube may refresh, in ms.
3069
+ *
3070
+ * The floor is the whole reason a tier can drop the mirror and keep the
3071
+ * saving: the cube slows to `MIRRORED_CUBE_INTERVAL` only BECAUSE a mirror is
3072
+ * carrying the near reflection, so a capped-away mirror without a floor sends
3073
+ * the cube back to the scene's own interval — six faces of the scene, several
3074
+ * times a second.
3075
+ */
3076
+ private effectiveCubeInterval;
3049
3077
  /** One frame that took no pass; release everything once it has been a while. */
3050
3078
  private noteIdleFrame;
3051
3079
  override free(): void;
package/dist/3d.js CHANGED
@@ -1,8 +1,8 @@
1
- import { A as Water3D, F as PhysicsBody3D, I as RigidBody3D, L as StaticBody3D, M as WaterCutout3D, N as Area3D, P as CharacterBody3D, R as Node3D, V as WATER_MAX_RIPPLES, j as WATER_CUTOUT_MAX } from "./gameplay-BPGomkHR.js";
2
- import { A as Terrain3D, B as keyboardIntensity, C as resolveFlowerDensity, D as BoneLookAt3D, E as Camera3D, F as InstancedMesh3D, G as acquireTexture, H as rigPose, I as MeshInstance3D, M as TERRAIN_THEMES, N as terrainThemeLayers, O as BoneAttachment3D, P as Joint3D, R as QUARTER_PITCH, S as Flowers3D, T as CharacterController3D, U as TextureCache3D, V as movementState, W as acquireOwnTexture, _ as LoftMesh3D, a as Tree3D, b as Foliage3D, c as buildRiverRings, d as riverCarveChannels, f as riverStepFor, g as ModelInstance3D, h as Particles3D, i as VoxelGrid3D, j as DEFAULT_TERRAIN_TEXTURE_BASE, k as Billboard3D, l as findRiverCoverageGaps, m as traceDownhillPath, n as registerNodes3D, o as Trail3D, p as smoothCourse, r as VOXEL_PALETTE, s as River3D, u as projectToRiver, v as DirectionalLight3D, w as FLOWER_VARIETIES, x as DENSITY_PRESETS, y as OmniLight3D, z as cameraRelative } from "./environment-presets-bokhdw_w.js";
3
- import { a as Environment3D, c as parseEnvironment3D, d as AssetStore3D, i as syncTree, l as sunDirectionFromElevationAzimuth, o as setEnvironment3D, r as Renderer3D, s as horizonColorFromSky, t as createGame3D, u as sunDirectionFromSky } from "./create-game-R_8_gTCU.js";
1
+ import { A as Water3D, F as PhysicsBody3D, I as RigidBody3D, L as StaticBody3D, M as WaterCutout3D, N as Area3D, P as CharacterBody3D, R as Node3D, V as WATER_MAX_RIPPLES, j as WATER_CUTOUT_MAX } from "./gameplay-By8mslMc.js";
2
+ import { A as Terrain3D, B as keyboardIntensity, C as resolveFlowerDensity, D as BoneLookAt3D, E as Camera3D, F as InstancedMesh3D, G as acquireTexture, H as rigPose, I as MeshInstance3D, M as TERRAIN_THEMES, N as terrainThemeLayers, O as BoneAttachment3D, P as Joint3D, R as QUARTER_PITCH, S as Flowers3D, T as CharacterController3D, U as TextureCache3D, V as movementState, W as acquireOwnTexture, _ as LoftMesh3D, a as Tree3D, b as Foliage3D, c as buildRiverRings, d as riverCarveChannels, f as riverStepFor, g as ModelInstance3D, h as Particles3D, i as VoxelGrid3D, j as DEFAULT_TERRAIN_TEXTURE_BASE, k as Billboard3D, l as findRiverCoverageGaps, m as traceDownhillPath, n as registerNodes3D, o as Trail3D, p as smoothCourse, r as VOXEL_PALETTE, s as River3D, u as projectToRiver, v as DirectionalLight3D, w as FLOWER_VARIETIES, x as DENSITY_PRESETS, y as OmniLight3D, z as cameraRelative } from "./environment-presets-CIZXGKnS.js";
3
+ import { a as Environment3D, c as parseEnvironment3D, d as AssetStore3D, i as syncTree, l as sunDirectionFromElevationAzimuth, o as setEnvironment3D, r as Renderer3D, s as horizonColorFromSky, t as createGame3D, u as sunDirectionFromSky } from "./create-game-p-O66Y_U.js";
4
4
  import { n as splatWeights, t as buildHeightmap } from "./heightmap-CRK0M4jT.js";
5
- import { n as enablePhysics3D, t as Physics3D } from "./physics-3d-Cpgc5ZM0.js";
5
+ import { n as enablePhysics3D, t as Physics3D } from "./physics-3d-CgIHivyL.js";
6
6
  //#region src/3d/terrain-nav.ts
7
7
  function buildTerrainNav(terrain, opts) {
8
8
  const cellSize = opts?.cellSize ?? 2;
@@ -1247,6 +1247,27 @@ interface QualityCaps {
1247
1247
  * so the shoreline dissolve, contact foam and depth absorption go with it.
1248
1248
  */
1249
1249
  fancyWater: boolean;
1250
+ /**
1251
+ * Whether a water surface may run its planar MIRROR pass — a whole extra
1252
+ * render of the scene, per water node, per frame.
1253
+ *
1254
+ * The most expensive single thing water does, and the first to go on a phone
1255
+ * that can still afford the rest of the fancy shader. The surface keeps its
1256
+ * cube reflection, so it still mirrors the sky and the far shore; what it
1257
+ * loses is the sharp reflection of nearby geometry.
1258
+ */
1259
+ waterMirror: boolean;
1260
+ /**
1261
+ * Floor (ms) on how often a reflection may be re-rendered. 0 = the scene
1262
+ * decides.
1263
+ *
1264
+ * This exists because of a trap: the reflection cube runs on a SLOW schedule
1265
+ * precisely because a mirror is present. Suppress the mirror without holding
1266
+ * that schedule and the cube starts refreshing at the scene's own interval —
1267
+ * six faces of the scene, several times a second — which hands most of the
1268
+ * saving straight back.
1269
+ */
1270
+ minReflectionIntervalMs: number;
1250
1271
  }
1251
1272
  //#endregion
1252
1273
  //#region src/core/stats.d.ts
@@ -1,13 +1,13 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
2
  import { n as loadScene, o as computeViewport, s as resolveViewport, v as diagnose, w as registerBehavior } from "./loader-r49nDwB4.js";
3
- import { h as Engine, m as AudioPlayer } from "./register-CGf5NKrc.js";
3
+ import { h as Engine, m as AudioPlayer } from "./register-DMFJI-c_.js";
4
4
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
5
5
  import { i as resolveRendering, n as attachTouchControls } from "./touch-BoNg_MnF.js";
6
6
  import { i as openBundledEditor, n as crossFade, r as devServerLibrary, t as teardown } from "./teardown-CCtAMDLB.js";
7
- import { n as registerGameplayBehaviors } from "./gameplay-BPGomkHR.js";
8
- import { g as PhysicsBody2D, n as UILayer, t as registerNodes2D, u as Camera2D, y as Node2D } from "./register-C2iO_yLv.js";
7
+ import { n as registerGameplayBehaviors } from "./gameplay-By8mslMc.js";
8
+ import { g as PhysicsBody2D, n as UILayer, t as registerNodes2D, u as Camera2D, y as Node2D } from "./register-CgMO5JBY.js";
9
9
  import { t as debugSources } from "./debug-draw-BM3DsvtT.js";
10
- import { n as enablePhysics2D } from "./physics-2d-B9ktSWrr.js";
10
+ import { n as enablePhysics2D } from "./physics-2d-CxIqR1cW.js";
11
11
  import { Box3, BufferAttribute, BufferGeometry, Color, LineBasicMaterial, LineSegments, LinearFilter, NearestFilter, OrthographicCamera, Raycaster, SRGBColorSpace, Scene, TextureLoader, Vector2, Vector3, WebGLRenderer } from "three";
12
12
  //#region src/2d/assets.ts
13
13
  /**
@@ -1,13 +1,13 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
2
  import { n as loadScene, v as diagnose, w as registerBehavior } from "./loader-r49nDwB4.js";
3
- import { _ as qualityCaps, h as Engine, m as AudioPlayer, y as qualityRendering } from "./register-CGf5NKrc.js";
3
+ import { _ as qualityCaps, h as Engine, m as AudioPlayer, y as qualityRendering } from "./register-DMFJI-c_.js";
4
4
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
5
5
  import { i as resolveRendering, n as attachTouchControls } from "./touch-BoNg_MnF.js";
6
6
  import { a as poseFromRenderer, i as openBundledEditor, n as crossFade, r as devServerLibrary, t as teardown } from "./teardown-CCtAMDLB.js";
7
- import { B as createCausticsQuad, F as PhysicsBody3D, R as Node3D, n as registerGameplayBehaviors } from "./gameplay-BPGomkHR.js";
7
+ import { B as createCausticsQuad, F as PhysicsBody3D, R as Node3D, n as registerGameplayBehaviors } from "./gameplay-By8mslMc.js";
8
8
  import { t as debugSources } from "./debug-draw-BM3DsvtT.js";
9
- import { E as Camera3D, U as TextureCache3D, g as ModelInstance3D, n as registerNodes3D, t as resolveEnvironmentHdri, v as DirectionalLight3D } from "./environment-presets-bokhdw_w.js";
10
- import { n as enablePhysics3D } from "./physics-3d-Cpgc5ZM0.js";
9
+ import { E as Camera3D, U as TextureCache3D, g as ModelInstance3D, n as registerNodes3D, t as resolveEnvironmentHdri, v as DirectionalLight3D } from "./environment-presets-CIZXGKnS.js";
10
+ import { n as enablePhysics3D } from "./physics-3d-CgIHivyL.js";
11
11
  import { ACESFilmicToneMapping, AmbientLight, Box3, BufferAttribute, BufferGeometry, Color, DepthTexture, EquirectangularReflectionMapping, FloatType, Fog, HalfFloatType, LineBasicMaterial, LineSegments, Matrix4, Mesh, PCFShadowMap, PMREMGenerator, PerspectiveCamera, PlaneGeometry, Quaternion, Raycaster, Scene, ShaderMaterial, Vector2, Vector3, WebGLRenderTarget, WebGLRenderer } from "three";
12
12
  import { VRMLoaderPlugin, VRMUtils } from "@pixiv/three-vrm";
13
13
  import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
package/dist/debug.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { At as LogLevel, T as RendererStats, b as Engine } from "./behavior-BNNSIXS5.js";
1
+ import { At as LogLevel, T as RendererStats, b as Engine } from "./behavior-TA8nySqb.js";
2
2
 
3
3
  //#region src/debug/panel.d.ts
4
4
  /** Minimal document surface the overlay needs (injectable for tests). */
@@ -1,10 +1,10 @@
1
- import { t as registerCoreNodes } from "./register-CGf5NKrc.js";
1
+ import { t as registerCoreNodes } from "./register-DMFJI-c_.js";
2
2
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
3
3
  import { t as Rng } from "./rng-DP-SR7eg.js";
4
4
  import { i as getNodeSchema, l as registerNode } from "./registry-IyWCGe4q.js";
5
5
  import { t as createNoise2D } from "./noise-CGUMx44x.js";
6
6
  import { i as applyParticlePreset, r as PARTICLE_PRESET_NAMES, t as ParticleSim } from "./particle-sim-Bw7hB93B.js";
7
- import { A as Water3D, F as PhysicsBody3D, H as colliderFootDrop, I as RigidBody3D, L as StaticBody3D, M as WaterCutout3D, N as Area3D, P as CharacterBody3D, R as Node3D } from "./gameplay-BPGomkHR.js";
7
+ import { A as Water3D, F as PhysicsBody3D, H as colliderFootDrop, I as RigidBody3D, L as StaticBody3D, M as WaterCutout3D, N as Area3D, P as CharacterBody3D, R as Node3D } from "./gameplay-By8mslMc.js";
8
8
  import { n as splatWeights, t as buildHeightmap } from "./heightmap-CRK0M4jT.js";
9
9
  import { AdditiveBlending, AnimationClip, AnimationMixer, Box3, BoxGeometry, BufferAttribute, BufferGeometry, CanvasTexture, CapsuleGeometry, ClampToEdgeWrapping, Color, ConeGeometry, CylinderGeometry, DataTexture, DirectionalLight, DoubleSide, DynamicDrawUsage, Euler, Group, IcosahedronGeometry, ImageBitmapLoader, InstancedBufferAttribute, InstancedMesh, LinearFilter, LinearMipmapLinearFilter, LoopOnce, LoopRepeat, Matrix4, Mesh, MeshBasicMaterial, MeshDepthMaterial, MeshPhysicalMaterial, MeshStandardMaterial, NearestFilter, NoBlending, NoColorSpace, NormalBlending, PerspectiveCamera, PlaneGeometry, PointLight, Points, PointsMaterial, Quaternion, QuaternionKeyframeTrack, RGBADepthPacking, RGBAFormat, RepeatWrapping, SRGBColorSpace, ShaderChunk, ShaderMaterial, SphereGeometry, Texture, TextureLoader, UniformsLib, UniformsUtils, Vector3, Vector4, VectorKeyframeTrack } from "three";
10
10
  import { clone } from "three/addons/utils/SkeletonUtils.js";
@@ -2621,6 +2621,8 @@ var CharacterController3D = class extends Node3D {
2621
2621
  bufferLeft = 0;
2622
2622
  jumpsUsed = 0;
2623
2623
  rising = false;
2624
+ /** Between the jump impulse and the landing — the animation's clock. */
2625
+ jumping = false;
2624
2626
  get body() {
2625
2627
  const parent = this.parent;
2626
2628
  if (!(parent instanceof RigidBody3D)) throw new IncantoError("TREE_VIOLATION", `CharacterController3D '${this.name}' must be a child of a RigidBody3D (parent is '${parent?.name ?? "none"}').`);
@@ -2708,7 +2710,7 @@ var CharacterController3D = class extends Node3D {
2708
2710
  0,
2709
2711
  -vel[2] * DRAG_DAMPING * mass
2710
2712
  ]);
2711
- if (toi !== null && toi < floatingDis * 2) {
2713
+ if (toi !== null && toi < floatingDis * 2 && !this.jumping) {
2712
2714
  const spring = SPRING_K * (floatingDis - toi) - vel[1] * SPRING_DAMPING;
2713
2715
  body.applyImpulse([
2714
2716
  0,
@@ -2720,9 +2722,10 @@ var CharacterController3D = class extends Node3D {
2720
2722
  this.coyoteLeft = this.coyoteSeconds;
2721
2723
  this.jumpsUsed = 0;
2722
2724
  this.rising = false;
2725
+ this.jumping = false;
2723
2726
  } else {
2724
2727
  this.coyoteLeft = Math.max(0, this.coyoteLeft - dt);
2725
- if (this.coyoteLeft <= 0 && this.jumpsUsed === 0) this.jumpsUsed = 1;
2728
+ if (!this.grounded && this.coyoteLeft <= 0 && this.jumpsUsed === 0) this.jumpsUsed = 1;
2726
2729
  }
2727
2730
  const pressedJump = safeJustPressed(input, this.jumpAction);
2728
2731
  if (pressedJump) this.bufferLeft = Math.max(this.jumpBufferSeconds, 0);
@@ -2739,6 +2742,7 @@ var CharacterController3D = class extends Node3D {
2739
2742
  this.bufferLeft = 0;
2740
2743
  this.coyoteLeft = 0;
2741
2744
  this.rising = true;
2745
+ this.jumping = true;
2742
2746
  vel[1] = jumpVel;
2743
2747
  }
2744
2748
  if (this.rising) {
@@ -2762,7 +2766,7 @@ var CharacterController3D = class extends Node3D {
2762
2766
  body.position[1] ?? 0,
2763
2767
  0
2764
2768
  ];
2765
- const next = movementState(footed, moving ? intensity : 0);
2769
+ const next = movementState(!(this.jumping || !this.grounded), moving ? intensity : 0);
2766
2770
  if (next !== this.state) {
2767
2771
  this.state = next;
2768
2772
  const clip = this.animations[next];
@@ -1409,20 +1409,36 @@ void main() {
1409
1409
 
1410
1410
  // The depth texture DELIBERATELY holds above-water geometry too (the
1411
1411
  // depth-completion pass, for cutouts) — so a sample NEARER than this
1412
- // fragment cannot be a bed, and treating it as "column 0" rendered the
1413
- // water as clear glass over the bare bed. Behind a tree the half-res
1414
- // pre-pass smears the canopy across the gaps between its own leaves, and
1415
- // whole stretches of pond turned to glass — "the water isn't drawn". The
1416
- // column reads the DEEPEST of the 5-tap neighborhood instead; when even
1417
- // that is contaminated, depth is UNKNOWN (-1) and the flat-ramp fallback
1418
- // paints honest murk.
1419
- float seamL = linearizeDepth(texture2D(uDepthTexture, screenUV - vec2(uScenePassTexel.x, 0.0)).r, uCameraNear, uCameraFar);
1420
- float seamR = linearizeDepth(texture2D(uDepthTexture, screenUV + vec2(uScenePassTexel.x, 0.0)).r, uCameraNear, uCameraFar);
1421
- float seamD = linearizeDepth(texture2D(uDepthTexture, screenUV - vec2(0.0, uScenePassTexel.y)).r, uCameraNear, uCameraFar);
1422
- float seamU = linearizeDepth(texture2D(uDepthTexture, screenUV + vec2(0.0, uScenePassTexel.y)).r, uCameraNear, uCameraFar);
1423
- float seamFarthest = max(max(seamL, seamR), max(seamD, max(seamU, linearSceneDepth)));
1424
- if (seamFarthest > linearWaterDepth + 0.02) {
1425
- waterViewDepth = seamFarthest - linearWaterDepth;
1412
+ // fragment can be a lie: behind a tree the half-res pre-pass smears the
1413
+ // canopy across the gaps between its own leaves, and whole stretches of
1414
+ // pond turned to glass ("the water isn't drawn"). Reading the DEEPEST of
1415
+ // the 5-tap neighborhood repairs that.
1416
+ //
1417
+ // But ONLY where there is something to repair. Applied everywhere, it draws
1418
+ // a dark hairline along every shoreline: in the thin swash the true column
1419
+ // is millimetres while one texel further out the bed has already dropped
1420
+ // away, so the deepest-of-5 hands a shallow fragment a deep-water column.
1421
+ // Reported as "a black line on the beach", and it is gone the moment this
1422
+ // reaches for the neighbourhood only when the centre is genuinely occluded.
1423
+ //
1424
+ // The two cases are told apart by HOW FAR IN FRONT the near sample is:
1425
+ // a canopy smeared over the water sits well in front → repair
1426
+ // a beach meeting the surface sits AT it (a few cm) → the column really
1427
+ // is zero; leave it
1428
+ float centreColumn = -depthDiff;
1429
+ if (centreColumn > 0.02) {
1430
+ waterViewDepth = centreColumn;
1431
+ } else if (depthDiff > 0.25) {
1432
+ float seamL = linearizeDepth(texture2D(uDepthTexture, screenUV - vec2(uScenePassTexel.x, 0.0)).r, uCameraNear, uCameraFar);
1433
+ float seamR = linearizeDepth(texture2D(uDepthTexture, screenUV + vec2(uScenePassTexel.x, 0.0)).r, uCameraNear, uCameraFar);
1434
+ float seamD = linearizeDepth(texture2D(uDepthTexture, screenUV - vec2(0.0, uScenePassTexel.y)).r, uCameraNear, uCameraFar);
1435
+ float seamU = linearizeDepth(texture2D(uDepthTexture, screenUV + vec2(0.0, uScenePassTexel.y)).r, uCameraNear, uCameraFar);
1436
+ float seamFarthest = max(max(seamL, seamR), max(seamD, max(seamU, linearSceneDepth)));
1437
+ if (seamFarthest > linearWaterDepth + 0.02) {
1438
+ waterViewDepth = seamFarthest - linearWaterDepth;
1439
+ }
1440
+ } else {
1441
+ waterViewDepth = 0.0;
1426
1442
  }
1427
1443
 
1428
1444
  // waterline dither: on a near-flat beach one half-res depth texel spans a
@@ -2833,6 +2849,22 @@ const FANCY_SEGMENTS_MIN = 24;
2833
2849
  const FANCY_SEGMENTS_MAX = 160;
2834
2850
  /** Simple quality: coarse enough to animate every vertex on the CPU. */
2835
2851
  const SIMPLE_SEGMENTS = 32;
2852
+ /**
2853
+ * The fraction of its half-extent over which the simple shader dissolves its
2854
+ * own rim — `smoothstep(0.82, 1.0, …)` in lake.ts, so the outer 18%.
2855
+ */
2856
+ const SIMPLE_EDGE_FADE_FRACTION = .18;
2857
+ /**
2858
+ * How wide that dissolve may be and still read as a SHORE, in metres.
2859
+ *
2860
+ * The simple shader has no depth texture, so fading its own rectangle is the
2861
+ * only waterline it can draw — it dissolves the plane's EDGE, not the place
2862
+ * where the ground rises. Fine when the water fills its basin (a 12 m pond
2863
+ * fades over 1.1 m, a 70 m lake over 6.3 m); useless when it does not (a
2864
+ * 1920 m ocean fades over 173 m, 800 m out to sea from the island it surrounds,
2865
+ * and meets the beach in a razor-straight line with no surf).
2866
+ */
2867
+ const SIMPLE_SHORE_BAND_MAX = 10;
2836
2868
  /** Bodies must rise this far above the waterline to 'exit' (no surface flicker). */
2837
2869
  const SURFACE_HYSTERESIS = .05;
2838
2870
  /** Entry splash amplitude: base + per-(m/s of |vy|), capped. */
@@ -3209,7 +3241,7 @@ var Water3D = class Water3D extends Node3D {
3209
3241
  this.cubeCamera = new CubeCamera(near, 1e3, this.cubeTarget);
3210
3242
  this.lastReflectionAt = 0;
3211
3243
  }
3212
- const cubeInterval = this.mirror ? Math.max(this.rp("reflectionInterval"), MIRRORED_CUBE_INTERVAL) : this.rp("reflectionInterval");
3244
+ const cubeInterval = this.effectiveCubeInterval();
3213
3245
  if (this.lastReflectionAt === 0 || now - this.lastReflectionAt >= cubeInterval) {
3214
3246
  this.lastReflectionAt = now;
3215
3247
  const center = worldTranslationOf(this);
@@ -3225,7 +3257,7 @@ var Water3D = class Water3D extends Node3D {
3225
3257
  u.uUseEnvironmentMap.value = 1;
3226
3258
  }
3227
3259
  }
3228
- if (this.mirror && u.uMirrorMap && u.uMirrorMatrix && u.uUseMirror) {
3260
+ if (this.effectiveMirror() && u.uMirrorMap && u.uMirrorMatrix && u.uUseMirror) {
3229
3261
  ctx.gl.getDrawingBufferSize(drawingBufferScratch);
3230
3262
  const mirrorW = Math.max(1, Math.min(Math.round(drawingBufferScratch.x / MIRROR_PASS_SCALE), MIRROR_PASS_MAX));
3231
3263
  const mirrorH = Math.max(1, Math.min(Math.round(drawingBufferScratch.y / MIRROR_PASS_SCALE), MIRROR_PASS_MAX));
@@ -3423,9 +3455,43 @@ var Water3D = class Water3D extends Node3D {
3423
3455
  * chose.
3424
3456
  */
3425
3457
  effectiveQuality() {
3426
- if (this.caps && !this.caps.fancyWater) return "simple";
3458
+ if (this.caps && !this.caps.fancyWater && this.canFadeItsOwnRim()) return "simple";
3427
3459
  return this.quality;
3428
3460
  }
3461
+ /**
3462
+ * Whether collapsing to the simple shader would still produce a waterline.
3463
+ *
3464
+ * A tier may make a game cheaper; it may not make it look broken. Found by
3465
+ * looking at the four water templates at every tier in a browser: the ocean
3466
+ * at low met the beach in a razor-straight line, because the simple shader's
3467
+ * only shoreline is a fade of its own rectangle and that rectangle's edge was
3468
+ * 800 m out to sea. Big water keeps the fancy shader at low — and still loses
3469
+ * its mirror, so the saving moves rather than disappears.
3470
+ *
3471
+ * This is NOT a veto on the author: a scene that asked for `quality: 'simple'`
3472
+ * gets it at any size. The guardrail only stops a TIER from choosing it.
3473
+ */
3474
+ canFadeItsOwnRim() {
3475
+ return Math.max(this.size[0] ?? 1, this.size[1] ?? 1) / 2 * SIMPLE_EDGE_FADE_FRACTION <= SIMPLE_SHORE_BAND_MAX;
3476
+ }
3477
+ /** Whether the planar mirror runs — the tier can take it away, never add it. */
3478
+ effectiveMirror() {
3479
+ return this.mirror && (this.caps ? this.caps.waterMirror : true);
3480
+ }
3481
+ /**
3482
+ * How often the reflection cube may refresh, in ms.
3483
+ *
3484
+ * The floor is the whole reason a tier can drop the mirror and keep the
3485
+ * saving: the cube slows to `MIRRORED_CUBE_INTERVAL` only BECAUSE a mirror is
3486
+ * carrying the near reflection, so a capped-away mirror without a floor sends
3487
+ * the cube back to the scene's own interval — six faces of the scene, several
3488
+ * times a second.
3489
+ */
3490
+ effectiveCubeInterval() {
3491
+ const authored = this.rp("reflectionInterval");
3492
+ const base = this.effectiveMirror() ? Math.max(authored, MIRRORED_CUBE_INTERVAL) : authored;
3493
+ return Math.max(base, this.caps?.minReflectionIntervalMs ?? 0);
3494
+ }
3429
3495
  /** One frame that took no pass; release everything once it has been a while. */
3430
3496
  noteIdleFrame() {
3431
3497
  this.idleFrames += 1;
@@ -1,4 +1,4 @@
1
- import { Et as Node, b as Engine, d as PropSchema, n as BehaviorCtor, t as Behavior } from "./behavior-BNNSIXS5.js";
1
+ import { Et as Node, b as Engine, d as PropSchema, n as BehaviorCtor, t as Behavior } from "./behavior-TA8nySqb.js";
2
2
  import { c as JsonValue, s as JsonObject } from "./schema-CFeioQRE.js";
3
3
 
4
4
  //#region src/gameplay/chase.d.ts
package/dist/gameplay.js CHANGED
@@ -1,2 +1,2 @@
1
- import { C as FollowCamera, D as Health, E as DamageOnContact, O as Collector, S as restartScene, T as phaseOf, _ as hitStop, a as Wander, b as GameFlow, c as Projectile, d as PathFollow, f as Oscillate, g as Cooldown, h as CameraShake, i as WaveSpawner, k as Chase, l as Pickup, m as Lifetime, n as registerGameplayBehaviors, o as Spawner, p as MoveTo, r as ZombieAI, s as ScoreKeeper, t as GAMEPLAY_BEHAVIORS, u as Patrol, v as screenFlash, w as DayNight, x as goToScene, y as Interactable } from "./gameplay-BPGomkHR.js";
1
+ import { C as FollowCamera, D as Health, E as DamageOnContact, O as Collector, S as restartScene, T as phaseOf, _ as hitStop, a as Wander, b as GameFlow, c as Projectile, d as PathFollow, f as Oscillate, g as Cooldown, h as CameraShake, i as WaveSpawner, k as Chase, l as Pickup, m as Lifetime, n as registerGameplayBehaviors, o as Spawner, p as MoveTo, r as ZombieAI, s as ScoreKeeper, t as GAMEPLAY_BEHAVIORS, u as Patrol, v as screenFlash, w as DayNight, x as goToScene, y as Interactable } from "./gameplay-By8mslMc.js";
2
2
  export { CameraShake, Chase, Collector, Cooldown, DamageOnContact, DayNight, FollowCamera, GAMEPLAY_BEHAVIORS, GameFlow, Health, Interactable, Lifetime, MoveTo, Oscillate, PathFollow, Patrol, Pickup, Projectile, ScoreKeeper, Spawner, Wander, WaveSpawner, ZombieAI, goToScene, hitStop, phaseOf, registerGameplayBehaviors, restartScene, screenFlash };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { $ as suggestLocale, A as SettingsValues, At as LogLevel, B as captureBehaviors, C as EngineStats, Ct as PlayMusicOptions, Dt as NodeLifecycle, E as DeviceHints, Et as Node, F as BehaviorState, G as OrderGroup, H as SaveStore, I as RestoreReport, J as resolveOrderGroups, K as OrderGroupTable, L as SaveSlot, M as readDeviceHints, Mt as Signal, N as suggestQuality, Nt as SignalListener, O as QualityTier, Ot as SceneTree, P as Scene, Q as T_PREFIX, R as SaveSlots, S as Scheduler, St as MusicTrack, T as RendererStats, Tt as BusName, U as createSaveStore, V as restoreBehaviors, W as ORDER_GROUP_BASE, X as LocaleTables, Y as BASE_LOCALE, Z as Localization, _ as mergeStaticSignals, _t as SfxWave, a as clearBehaviors, at as Voice, b as Engine, bt as MusicBackend, c as registeredBehaviors, ct as ROLLOFF_MODELS, d as PropSchema, dt as Vec3, et as translationKey, f as clearRegistry, ft as spatialGain, g as getNodeType, gt as SfxParams, h as getNodeSignals, ht as SFX_PRESET_NAMES, i as behaviorSignals, it as isAudioContextAvailable, j as qualityEnvironment, jt as LogManager, k as Settings, kt as LogEntry, l as NodeCtor, lt as RolloffModel, m as getNodeSchema, mt as SFX_PRESETS, n as BehaviorCtor, nt as SfxEngine, o as getBehavior, ot as VoicePreset, p as createNode, pt as spatialPan, q as effectiveOrder, r as behaviorSchema, rt as SfxPlayOptions, s as registerBehavior, st as Listener, t as Behavior, tt as InputMap, u as PropDef, ut as SpatialParams, v as registerNode, vt as SynthOptions, w as GameStats, wt as AudioBuses, x as EngineOptions, xt as MusicManager, y as registeredTypes, yt as synthSfx, z as behaviorsWithoutSave } from "./behavior-BNNSIXS5.js";
1
+ import { $ as suggestLocale, A as SettingsValues, At as LogLevel, B as captureBehaviors, C as EngineStats, Ct as PlayMusicOptions, Dt as NodeLifecycle, E as DeviceHints, Et as Node, F as BehaviorState, G as OrderGroup, H as SaveStore, I as RestoreReport, J as resolveOrderGroups, K as OrderGroupTable, L as SaveSlot, M as readDeviceHints, Mt as Signal, N as suggestQuality, Nt as SignalListener, O as QualityTier, Ot as SceneTree, P as Scene, Q as T_PREFIX, R as SaveSlots, S as Scheduler, St as MusicTrack, T as RendererStats, Tt as BusName, U as createSaveStore, V as restoreBehaviors, W as ORDER_GROUP_BASE, X as LocaleTables, Y as BASE_LOCALE, Z as Localization, _ as mergeStaticSignals, _t as SfxWave, a as clearBehaviors, at as Voice, b as Engine, bt as MusicBackend, c as registeredBehaviors, ct as ROLLOFF_MODELS, d as PropSchema, dt as Vec3, et as translationKey, f as clearRegistry, ft as spatialGain, g as getNodeType, gt as SfxParams, h as getNodeSignals, ht as SFX_PRESET_NAMES, i as behaviorSignals, it as isAudioContextAvailable, j as qualityEnvironment, jt as LogManager, k as Settings, kt as LogEntry, l as NodeCtor, lt as RolloffModel, m as getNodeSchema, mt as SFX_PRESETS, n as BehaviorCtor, nt as SfxEngine, o as getBehavior, ot as VoicePreset, p as createNode, pt as spatialPan, q as effectiveOrder, r as behaviorSchema, rt as SfxPlayOptions, s as registerBehavior, st as Listener, t as Behavior, tt as InputMap, u as PropDef, ut as SpatialParams, v as registerNode, vt as SynthOptions, w as GameStats, wt as AudioBuses, x as EngineOptions, xt as MusicManager, y as registeredTypes, yt as synthSfx, z as behaviorsWithoutSave } from "./behavior-TA8nySqb.js";
2
2
  import { a as Rng, c as JsonValue, d as jsonKind, i as SceneJson, l as jsonClone, n as NodeJson, o as JsonKind, r as SCENE_FORMAT, s as JsonObject, t as ConnectionJson, u as jsonEquals } from "./schema-CFeioQRE.js";
3
- import { n as loadScene, t as LoadSceneOptions } from "./loader-mSL65Djg.js";
3
+ import { n as loadScene, t as LoadSceneOptions } from "./loader-CvSDKC3v.js";
4
4
  import { n as ParticleSimConfig, r as ParticleView, t as ParticleSim } from "./particle-sim-BzJ1yxoE.js";
5
- import { a as AudioElementLike, i as gridFromRows, n as PathGrid, o as AudioPlayer, r as findPath, t as FindPathOptions } from "./pathfinding-D7W8uqm1.js";
6
- import { a as startRecording, c as IncantoErrorDetails, i as replay, l as auditScene, n as ReplayEvent, o as IncantoError, r as ReplayJson, s as IncantoErrorCode, t as Recorder } from "./replay-CwdWgz0f.js";
5
+ import { a as AudioElementLike, i as gridFromRows, n as PathGrid, o as AudioPlayer, r as findPath, t as FindPathOptions } from "./pathfinding-_UlKUoox.js";
6
+ import { a as startRecording, c as IncantoErrorDetails, i as replay, l as auditScene, n as ReplayEvent, o as IncantoError, r as ReplayJson, s as IncantoErrorCode, t as Recorder } from "./replay-DN-kG4va.js";
7
7
 
8
8
  //#region src/core/audio/crossfade.d.ts
9
9
  /**
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { C as getBehavior, E as Signal, S as clearBehaviors, T as registeredBehaviors, _ as InputMap, a as SCENE_FORMAT, b as behaviorSchema, c as SceneTree, d as resolveConstants, f as Node, g as resolveOrderGroups, h as effectiveOrder, i as serializeNode, l as CONST_REF_KEY, m as ORDER_GROUP_BASE, n as loadScene, o as computeViewport, p as parseNodePath, r as Scene, s as resolveViewport, u as isConstRef, w as registerBehavior, x as behaviorSignals, y as Behavior } from "./loader-r49nDwB4.js";
2
- import { A as T_PREFIX, B as synthSfx, C as behaviorsWithoutSave, D as LogManager, E as createSaveStore, F as ROLLOFF_MODELS, G as AudioBuses, H as WebAudioMusicBackend, I as spatialGain, L as spatialPan, M as translationKey, N as SfxEngine, O as BASE_LOCALE, P as isAudioContextAvailable, R as SFX_PRESETS, S as SaveSlots, T as restoreBehaviors, U as crossfadeGains, V as MusicManager, W as fadeGain, a as UiSlider, b as readDeviceHints, c as UiButton, d as UiBanner, f as UiBar, g as Settings, h as Engine, i as UiSelect, j as suggestLocale, k as Localization, l as UiDialogue, m as AudioPlayer, n as UiImage, o as UiToggle, p as UiText, r as UiPanel, s as Timer, t as registerCoreNodes, u as HudLayer, v as qualityEnvironment, w as captureBehaviors, x as suggestQuality, z as SFX_PRESET_NAMES } from "./register-CGf5NKrc.js";
2
+ import { A as T_PREFIX, B as synthSfx, C as behaviorsWithoutSave, D as LogManager, E as createSaveStore, F as ROLLOFF_MODELS, G as AudioBuses, H as WebAudioMusicBackend, I as spatialGain, L as spatialPan, M as translationKey, N as SfxEngine, O as BASE_LOCALE, P as isAudioContextAvailable, R as SFX_PRESETS, S as SaveSlots, T as restoreBehaviors, U as crossfadeGains, V as MusicManager, W as fadeGain, a as UiSlider, b as readDeviceHints, c as UiButton, d as UiBanner, f as UiBar, g as Settings, h as Engine, i as UiSelect, j as suggestLocale, k as Localization, l as UiDialogue, m as AudioPlayer, n as UiImage, o as UiToggle, p as UiText, r as UiPanel, s as Timer, t as registerCoreNodes, u as HudLayer, v as qualityEnvironment, w as captureBehaviors, x as suggestQuality, z as SFX_PRESET_NAMES } from "./register-DMFJI-c_.js";
3
3
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
4
4
  import { t as Rng } from "./rng-DP-SR7eg.js";
5
- import { n as startRecording, r as auditScene, t as replay } from "./replay-YQ9FW7Yh.js";
5
+ import { n as startRecording, r as auditScene, t as replay } from "./replay-D_QY5Xm3.js";
6
6
  import { n as jsonEquals, r as jsonKind, t as jsonClone } from "./json-BLk7H2Qa.js";
7
7
  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-IyWCGe4q.js";
8
8
  import { t as createNoise2D } from "./noise-CGUMx44x.js";
@@ -172,6 +172,6 @@ function newUid() {
172
172
  //#endregion
173
173
  //#region src/index.ts
174
174
  /** Engine version. Kept in sync with package.json by the release pipeline. */
175
- const VERSION = "0.39.0";
175
+ const VERSION = "0.40.1";
176
176
  //#endregion
177
177
  export { AudioBuses, AudioPlayer, BASE_LOCALE, Behavior, CONST_REF_KEY, 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, UiImage, UiPanel, UiSelect, UiSlider, UiText, UiToggle, VERSION, WebAudioMusicBackend, applyParticlePreset, assetUrls, attachTouchControls, auditScene, behaviorSchema, behaviorSignals, behaviorsWithoutSave, captureBehaviors, clearBehaviors, clearRegistry, computeViewport, createNode, createNoise2D, createSaveStore, crossfadeGains, duplicateNode, effectiveOrder, fadeGain, findPath, getBehavior, getNodeSchema, getNodeSignals, getNodeType, gridFromRows, isAudioContextAvailable, isConstRef, joystickVector, jsonClone, jsonEquals, jsonKind, loadScene, mergeStaticSignals, newUid, parseNodePath, preloadUrls, qualityEnvironment, readDeviceHints, registerBehavior, registerCoreNodes, registerNode, registeredBehaviors, registeredTypes, replay, resolveConstants, resolveOrderGroups, resolveRendering, resolveViewport, restoreBehaviors, serializeNode, spatialGain, spatialPan, startRecording, suggestLocale, suggestQuality, synthSfx, translationKey };
@@ -1,4 +1,4 @@
1
- import { P as Scene, b as Engine } from "./behavior-BNNSIXS5.js";
1
+ import { P as Scene, b as Engine } from "./behavior-TA8nySqb.js";
2
2
  import { i as SceneJson } from "./schema-CFeioQRE.js";
3
3
 
4
4
  //#region src/core/scene/loader.d.ts
package/dist/net.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Et as Node, Mt as Signal, b as Engine, d as PropSchema } from "./behavior-BNNSIXS5.js";
1
+ import { Et as Node, Mt as Signal, b as Engine, d as PropSchema } from "./behavior-TA8nySqb.js";
2
2
  import { c as JsonValue, i as SceneJson, s as JsonObject } from "./schema-CFeioQRE.js";
3
3
 
4
4
  //#region src/net/types.d.ts
package/dist/net.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { n as loadScene } from "./loader-r49nDwB4.js";
2
- import { h as Engine } from "./register-CGf5NKrc.js";
2
+ import { h as Engine } from "./register-DMFJI-c_.js";
3
3
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
4
4
  import { t as jsonClone } from "./json-BLk7H2Qa.js";
5
5
  import { n as createAgent8Server } from "./agent8-CvsfVskX.js";
6
- import { i as applySyncPatch, n as NetworkSpawner, r as NetworkManager, t as registerNodesNet } from "./register-CiXP3-GB.js";
6
+ import { i as applySyncPatch, n as NetworkSpawner, r as NetworkManager, t as registerNodesNet } from "./register-D5v8iqjZ.js";
7
7
  //#region src/net/loopback.ts
8
8
  /**
9
9
  * In-memory multiplayer hub implementing the SAME kernel contract as
@@ -1,4 +1,4 @@
1
- import { Et as Node, d as PropSchema, st as Listener } from "./behavior-BNNSIXS5.js";
1
+ import { Et as Node, d as PropSchema, st as Listener } from "./behavior-TA8nySqb.js";
2
2
 
3
3
  //#region src/core/nodes/audio-player.d.ts
4
4
  /**
@@ -1,7 +1,7 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
2
  import { v as diagnose } from "./loader-r49nDwB4.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-C2iO_yLv.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-CgMO5JBY.js";
5
5
  import { n as registerDebugSource } from "./debug-draw-BM3DsvtT.js";
6
6
  //#region src/2d/physics/physics-2d.ts
7
7
  var physics_2d_exports = /* @__PURE__ */ __exportAll({
@@ -1,9 +1,9 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
2
  import { v as diagnose } from "./loader-r49nDwB4.js";
3
3
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
4
- import { F as PhysicsBody3D, I as RigidBody3D, N as Area3D, P as CharacterBody3D, R as Node3D, z as validateCollider3D } from "./gameplay-BPGomkHR.js";
4
+ import { F as PhysicsBody3D, I as RigidBody3D, N as Area3D, P as CharacterBody3D, R as Node3D, z as validateCollider3D } from "./gameplay-By8mslMc.js";
5
5
  import { n as registerDebugSource } from "./debug-draw-BM3DsvtT.js";
6
- import { A as Terrain3D, F as InstancedMesh3D, L as buildMeshGeometry, P as Joint3D } from "./environment-presets-bokhdw_w.js";
6
+ import { A as Terrain3D, F as InstancedMesh3D, L as buildMeshGeometry, P as Joint3D } from "./environment-presets-CIZXGKnS.js";
7
7
  import { Euler, Matrix4, Quaternion, Vector3 } from "three";
8
8
  //#region src/3d/physics/collider-lines.ts
9
9
  /**
package/dist/react.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { P as Scene, b as Engine, n as BehaviorCtor } from "./behavior-BNNSIXS5.js";
1
+ import { P as Scene, b as Engine, n as BehaviorCtor } from "./behavior-TA8nySqb.js";
2
2
  import { c as JsonValue } from "./schema-CFeioQRE.js";
3
3
  import { CSSProperties, ReactNode } from "react";
4
4
 
package/dist/react.js CHANGED
@@ -156,7 +156,7 @@ function IncantoCanvas(props) {
156
156
  pointer: latest.pointer,
157
157
  ...keyboard !== void 0 ? { keyboard } : {}
158
158
  };
159
- const next = await (_gameFactory ?? (mode === "3d" ? async (o) => (await import("./create-game-R_8_gTCU.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-DEtU2PRg.js").then((n) => n.n)).createGame2D(o)))(opts);
159
+ const next = await (_gameFactory ?? (mode === "3d" ? async (o) => (await import("./create-game-p-O66Y_U.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-B7c5icho.js").then((n) => n.n)).createGame2D(o)))(opts);
160
160
  if (disposed) {
161
161
  next.dispose();
162
162
  return;
@@ -1,5 +1,5 @@
1
1
  import { f as Node, h as effectiveOrder } from "./loader-r49nDwB4.js";
2
- import { t as registerCoreNodes } from "./register-CGf5NKrc.js";
2
+ import { t as registerCoreNodes } from "./register-DMFJI-c_.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 { i as getNodeSchema, l as registerNode } from "./registry-IyWCGe4q.js";
@@ -1,5 +1,5 @@
1
1
  import { E as Signal, f as Node, t as buildNodeJson } from "./loader-r49nDwB4.js";
2
- import { t as registerCoreNodes } from "./register-CGf5NKrc.js";
2
+ import { t as registerCoreNodes } from "./register-DMFJI-c_.js";
3
3
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
4
4
  import { n as jsonEquals, t as jsonClone } from "./json-BLk7H2Qa.js";
5
5
  import { l as registerNode } from "./registry-IyWCGe4q.js";
@@ -1599,9 +1599,21 @@ function qualityEnvironment(tier) {
1599
1599
  };
1600
1600
  }
1601
1601
  const QUALITY_CAPS = Object.freeze({
1602
- low: Object.freeze({ fancyWater: false }),
1603
- medium: Object.freeze({ fancyWater: true }),
1604
- high: Object.freeze({ fancyWater: true })
1602
+ low: Object.freeze({
1603
+ fancyWater: false,
1604
+ waterMirror: false,
1605
+ minReflectionIntervalMs: 2e3
1606
+ }),
1607
+ medium: Object.freeze({
1608
+ fancyWater: true,
1609
+ waterMirror: false,
1610
+ minReflectionIntervalMs: 2e3
1611
+ }),
1612
+ high: Object.freeze({
1613
+ fancyWater: true,
1614
+ waterMirror: true,
1615
+ minReflectionIntervalMs: 0
1616
+ })
1605
1617
  });
1606
1618
  /**
1607
1619
  * The caps for a tier — the same frozen object every call.
@@ -1,4 +1,4 @@
1
- import { b as Engine } from "./behavior-BNNSIXS5.js";
1
+ import { b as Engine } from "./behavior-TA8nySqb.js";
2
2
  import { s as JsonObject } from "./schema-CFeioQRE.js";
3
3
 
4
4
  //#region src/core/audit.d.ts
@@ -1,4 +1,4 @@
1
- import { M as translationKey } from "./register-CGf5NKrc.js";
1
+ import { M as translationKey } from "./register-DMFJI-c_.js";
2
2
  //#region src/core/audit.ts
3
3
  const ANIMATED_TYPES = new Set([
4
4
  "Particles2D",