incanto 0.25.3 → 0.26.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/dist/2d.js CHANGED
@@ -1,5 +1,5 @@
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-yGqGR-Ic.js";
2
+ import { a as AssetStore2D, i as syncTree2D, r as Renderer2D, t as createGame2D } from "./create-game-C0bPoURs.js";
3
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-CvpSUU3O.js";
4
4
  import { n as enablePhysics2D, t as Physics2D } from "./physics-2d-DiVFFlH3.js";
5
5
  //#region src/2d/library-sprite.ts
package/dist/3d.d.ts CHANGED
@@ -3,7 +3,7 @@ import { $ as Node, C as RendererStats, S as GameStats, b as Scheduler, l as Pro
3
3
  import { t as LoadSceneOptions } from "./loader-DILt9PGC.js";
4
4
  import { t as ParticleSim } from "./particle-sim-CwJ5rI_P.js";
5
5
  import { n as PathGrid, s as SpatialPose } from "./pathfinding-RWYkNKx9.js";
6
- import { AnimationClip, BufferGeometry, Color, DirectionalLight, InstancedMesh, MeshPhysicalMaterial, Object3D, PerspectiveCamera, Scene, Vector3, WebGLRenderer } from "three";
6
+ import { AnimationClip, BufferGeometry, Color, DirectionalLight, InstancedMesh, Mesh, MeshPhysicalMaterial, Object3D, PerspectiveCamera, Scene, ShaderMaterial, Vector3, WebGLRenderer } from "three";
7
7
  import { VRM } from "@pixiv/three-vrm";
8
8
  import { Sky } from "three/examples/jsm/objects/Sky.js";
9
9
  import * as RapierNs from "@dimforge/rapier3d-compat";
@@ -2387,6 +2387,10 @@ declare class Water3D extends Node3D implements RenderHook3D, SunConsumer3D {
2387
2387
  private readonly _scratchA;
2388
2388
  private readonly _scratchB;
2389
2389
  private readonly _bodies;
2390
+ private readonly _cutouts;
2391
+ /** Scratch for the cutout mask pass (no per-frame allocation). */
2392
+ private readonly _maskMeshes;
2393
+ private readonly _maskSaved;
2390
2394
  /** Scratch for the side-pass cull (no per-frame allocation). */
2391
2395
  private readonly _passHidden;
2392
2396
  private cubeTarget;
@@ -2428,6 +2432,16 @@ declare class Water3D extends Node3D implements RenderHook3D, SunConsumer3D {
2428
2432
  /** The cheap lake ShaderMaterial — fresnel sky tint + ripple normals + sun
2429
2433
  * glint + edge fade, NO scene re-renders (see lake.ts). */
2430
2434
  private buildLakeMaterial;
2435
+ /**
2436
+ * Stamp every {@link WaterCutout3D} owner into the depth pre-pass.
2437
+ *
2438
+ * The cutout does not describe a hole in the water — it names a piece of
2439
+ * geometry that holds the water back, and this pass re-draws exactly that
2440
+ * geometry (clipped to the cutout box) at the near plane. The water's
2441
+ * occlusion discard does the rest. The tree is walked live, so a cutout on a
2442
+ * bobbing hull tracks it with no wiring.
2443
+ */
2444
+ private renderCutoutMasks;
2431
2445
  private syncFancy;
2432
2446
  /**
2433
2447
  * @internal Environment-sky sun hand-off (see {@link SunConsumer3D}): the
@@ -2454,6 +2468,59 @@ declare class Water3D extends Node3D implements RenderHook3D, SunConsumer3D {
2454
2468
  private waveOffsetAt;
2455
2469
  }
2456
2470
  //#endregion
2471
+ //#region src/3d/nodes/water-cutout-3d.d.ts
2472
+ /**
2473
+ * A box the water does not enter.
2474
+ *
2475
+ * Water is ONE surface: it has no idea a boat has an inside. Float an open hull
2476
+ * and every wave crest that rises above its floor renders as sea sloshing
2477
+ * through the deck — correct for a plane cutting an open box, wrong for a boat.
2478
+ * Freeboard alone cannot fix it (a big enough swell always wins), so the fix is
2479
+ * to name the geometry that holds the water back. `Water3D` re-draws that
2480
+ * geometry — the part of it inside this box — into its depth pre-pass at the
2481
+ * near plane, and its ordinary occlusion discard then removes the water for
2482
+ * exactly the pixels the hull already covers.
2483
+ *
2484
+ * Park it as a CHILD of the thing it belongs to — a hull, a diving bell, a
2485
+ * dock's well, a cave mouth — and it follows along, rotation included.
2486
+ *
2487
+ * SAFE BY CONSTRUCTION: nothing is ever cut out of the water. The mask is the
2488
+ * owner's own silhouette, so water can only be hidden behind geometry the
2489
+ * player is already looking at. Size the box to the interior and err large —
2490
+ * an overlarge box just means more of this hull holds water back; it can never
2491
+ * punch a rectangle of bare ground into open water. (Needs the depth pre-pass:
2492
+ * `Water3D` in `fancy` quality. Skinned and instanced meshes are re-drawn at
2493
+ * their rest pose, so hang cutouts on ordinary geometry.)
2494
+ *
2495
+ * ```json
2496
+ * { "name": "Rowboat", "type": "RigidBody3D", "children": [
2497
+ * { "name": "DryInside", "type": "WaterCutout3D",
2498
+ * "props": { "size": [3.0, 1.2, 1.1], "position": [0, 0.4, 0] } } ] }
2499
+ * ```
2500
+ *
2501
+ * Cheap: a handful of these cost a few ALU per water pixel. `Water3D` reads the
2502
+ * nearest {@link WATER_CUTOUT_MAX} of them.
2503
+ */
2504
+ declare class WaterCutout3D extends Node3D {
2505
+ static override readonly typeName: string;
2506
+ static override readonly props: PropSchema;
2507
+ /** Box extent in meters, centered on the node ([x, y, z]). */
2508
+ size: number[];
2509
+ /** Live toggle — a scuttled hull can start letting the sea in. */
2510
+ enabled: boolean;
2511
+ static validateJson(node: Node): void;
2512
+ /**
2513
+ * A cutout works by re-drawing its PARENT, so it has to have one. At the
2514
+ * scene root it would hand the whole world a licence to erase water — the
2515
+ * one failure this design exists to rule out — so that arrangement is a hard
2516
+ * load error, not a silent no-op. (Checked here rather than in
2517
+ * `validateJson`: the loader validates a node before attaching it.)
2518
+ */
2519
+ override onReady(): void;
2520
+ }
2521
+ /** How many cutouts one Water3D reads (the nearest ones win). */
2522
+ declare const WATER_CUTOUT_MAX = 4;
2523
+ //#endregion
2457
2524
  //#region src/3d/register.d.ts
2458
2525
  /**
2459
2526
  * Register the 3D node taxonomy (and the core nodes). Call once in your game
@@ -2761,4 +2828,4 @@ declare function createNavDebugNode(nav: TerrainNav, opts?: {
2761
2828
  name?: string;
2762
2829
  }): InstancedMesh3D;
2763
2830
  //#endregion
2764
- export { Area3D, AssetStore3D, type BedSampler, Billboard3D, type BillboardGroupMode, BoneAttachment3D, BoneLookAt3D, Camera3D, CharacterBody3D, CharacterController3D, type CreateGame3DOptions, DEFAULT_TERRAIN_TEXTURE_BASE, DirectionalLight3D, type DownhillTraceOptions, Environment3D, type Environment3DConfig, DENSITY_PRESETS as FLOWER_DENSITY_PRESETS, FLOWER_VARIETIES, type FlowerVariety, Flowers3D, type FogEnvironment, Foliage3D, type FoliageKind, type FoliageStyle, type Game3D, type HeightSampler, type Heightmap, type HeightmapOptions, InstancedMesh3D, Joint3D, type JointType3D, LoftMesh3D, type LoftSection, MeshInstance3D, type MeshKind, type MeshMaterialProps, type ModelEntry, ModelInstance3D, Node3D, OmniLight3D, Particles3D, Physics3D, type Physics3DOptions, PhysicsBody3D, QUARTER_PITCH, type RenderContext3D, type RenderHook3D, Renderer3D, type Renderer3DOptions, type RigView, RigidBody3D, type Ripple, River3D, type RiverHit, type RiverRing, type ShadowsEnvironment, type SkyEnvironment, StaticBody3D, type SunConsumer3D, type SyncOptions, type SyncResult, TERRAIN_THEMES, Terrain3D, type TerrainLayer, type TerrainNav, type TerrainNavOptions, type TerrainTheme, Trail3D, Tree3D, type TreeTier, type TreeType, VOXEL_PALETTE, type VoxelBlock, VoxelGrid3D, WATER_MAX_RIPPLES, Water3D, buildHeightmap, buildTerrainNav, cameraRelative, createGame3D, createNavDebugNode, enablePhysics3D, horizonColorFromSky, keyboardIntensity, movementState, parseEnvironment3D, registerNodes3D, resolveFlowerDensity, rigPose, setEnvironment3D, splatWeights, sunDirectionFromElevationAzimuth, sunDirectionFromSky, syncTree, terrainThemeLayers, traceDownhillPath };
2831
+ export { Area3D, AssetStore3D, type BedSampler, Billboard3D, type BillboardGroupMode, BoneAttachment3D, BoneLookAt3D, Camera3D, CharacterBody3D, CharacterController3D, type CreateGame3DOptions, DEFAULT_TERRAIN_TEXTURE_BASE, DirectionalLight3D, type DownhillTraceOptions, Environment3D, type Environment3DConfig, DENSITY_PRESETS as FLOWER_DENSITY_PRESETS, FLOWER_VARIETIES, type FlowerVariety, Flowers3D, type FogEnvironment, Foliage3D, type FoliageKind, type FoliageStyle, type Game3D, type HeightSampler, type Heightmap, type HeightmapOptions, InstancedMesh3D, Joint3D, type JointType3D, LoftMesh3D, type LoftSection, MeshInstance3D, type MeshKind, type MeshMaterialProps, type ModelEntry, ModelInstance3D, Node3D, OmniLight3D, Particles3D, Physics3D, type Physics3DOptions, PhysicsBody3D, QUARTER_PITCH, type RenderContext3D, type RenderHook3D, Renderer3D, type Renderer3DOptions, type RigView, RigidBody3D, type Ripple, River3D, type RiverHit, type RiverRing, type ShadowsEnvironment, type SkyEnvironment, StaticBody3D, type SunConsumer3D, type SyncOptions, type SyncResult, TERRAIN_THEMES, Terrain3D, type TerrainLayer, type TerrainNav, type TerrainNavOptions, type TerrainTheme, Trail3D, Tree3D, type TreeTier, type TreeType, VOXEL_PALETTE, type VoxelBlock, VoxelGrid3D, WATER_CUTOUT_MAX, WATER_MAX_RIPPLES, Water3D, WaterCutout3D, buildHeightmap, buildTerrainNav, cameraRelative, createGame3D, createNavDebugNode, enablePhysics3D, horizonColorFromSky, keyboardIntensity, movementState, parseEnvironment3D, registerNodes3D, resolveFlowerDensity, rigPose, setEnvironment3D, splatWeights, sunDirectionFromElevationAzimuth, sunDirectionFromSky, syncTree, terrainThemeLayers, traceDownhillPath };
package/dist/3d.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
2
- import { A as Water3D, F as StaticBody3D, I as Node3D, M as CharacterBody3D, N as PhysicsBody3D, P as RigidBody3D, j as Area3D, z as WATER_MAX_RIPPLES } from "./gameplay-B8zVO3Dg.js";
3
- import { a as Environment3D, c as sunDirectionFromElevationAzimuth, i as syncTree, l as sunDirectionFromSky, o as horizonColorFromSky, r as Renderer3D, s as parseEnvironment3D, t as createGame3D, u as AssetStore3D } from "./create-game-K8A82JK5.js";
4
- import { A as QUARTER_PITCH, C as BoneAttachment3D, D as TERRAIN_THEMES, E as DEFAULT_TERRAIN_TEXTURE_BASE, M as keyboardIntensity, N as movementState, O as terrainThemeLayers, P as rigPose, S as BoneLookAt3D, T as Terrain3D, _ as Flowers3D, a as Trail3D, b as CharacterController3D, c as Particles3D, d as DirectionalLight3D, f as OmniLight3D, g as DENSITY_PRESETS, h as Foliage3D, i as Tree3D, j as cameraRelative, k as Joint3D, l as ModelInstance3D, m as MeshInstance3D, n as VOXEL_PALETTE, o as River3D, p as InstancedMesh3D, r as VoxelGrid3D, s as traceDownhillPath, t as registerNodes3D, u as LoftMesh3D, v as resolveFlowerDensity, w as Billboard3D, x as Camera3D, y as FLOWER_VARIETIES } from "./register-BgeMHtcG.js";
2
+ 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-Ddk13pQ6.js";
3
+ import { a as Environment3D, c as sunDirectionFromElevationAzimuth, i as syncTree, l as sunDirectionFromSky, o as horizonColorFromSky, r as Renderer3D, s as parseEnvironment3D, t as createGame3D, u as AssetStore3D } from "./create-game-B_qBR65J.js";
4
+ import { A as QUARTER_PITCH, C as BoneAttachment3D, D as TERRAIN_THEMES, E as DEFAULT_TERRAIN_TEXTURE_BASE, M as keyboardIntensity, N as movementState, O as terrainThemeLayers, P as rigPose, S as BoneLookAt3D, T as Terrain3D, _ as Flowers3D, a as Trail3D, b as CharacterController3D, c as Particles3D, d as DirectionalLight3D, f as OmniLight3D, g as DENSITY_PRESETS, h as Foliage3D, i as Tree3D, j as cameraRelative, k as Joint3D, l as ModelInstance3D, m as MeshInstance3D, n as VOXEL_PALETTE, o as River3D, p as InstancedMesh3D, r as VoxelGrid3D, s as traceDownhillPath, t as registerNodes3D, u as LoftMesh3D, v as resolveFlowerDensity, w as Billboard3D, x as Camera3D, y as FLOWER_VARIETIES } from "./register-CeWtdiCw.js";
5
5
  import { n as splatWeights, t as buildHeightmap } from "./heightmap-CRK0M4jT.js";
6
- import { n as enablePhysics3D, t as Physics3D } from "./physics-3d-DY0EtjfT.js";
6
+ import { n as enablePhysics3D, t as Physics3D } from "./physics-3d-Cd7Jl8w1.js";
7
7
  //#region src/3d/environment-runtime.ts
8
8
  /**
9
9
  * Live environment editing — the renderer re-applies `scene.environment`
@@ -142,4 +142,4 @@ function createNavDebugNode(nav, opts) {
142
142
  return node;
143
143
  }
144
144
  //#endregion
145
- export { Area3D, AssetStore3D, Billboard3D, BoneAttachment3D, BoneLookAt3D, Camera3D, CharacterBody3D, CharacterController3D, DEFAULT_TERRAIN_TEXTURE_BASE, DirectionalLight3D, Environment3D, DENSITY_PRESETS as FLOWER_DENSITY_PRESETS, FLOWER_VARIETIES, Flowers3D, Foliage3D, InstancedMesh3D, Joint3D, LoftMesh3D, MeshInstance3D, ModelInstance3D, Node3D, OmniLight3D, Particles3D, Physics3D, PhysicsBody3D, QUARTER_PITCH, Renderer3D, RigidBody3D, River3D, StaticBody3D, TERRAIN_THEMES, Terrain3D, Trail3D, Tree3D, VOXEL_PALETTE, VoxelGrid3D, WATER_MAX_RIPPLES, Water3D, buildHeightmap, buildTerrainNav, cameraRelative, createGame3D, createNavDebugNode, enablePhysics3D, horizonColorFromSky, keyboardIntensity, movementState, parseEnvironment3D, registerNodes3D, resolveFlowerDensity, rigPose, setEnvironment3D, splatWeights, sunDirectionFromElevationAzimuth, sunDirectionFromSky, syncTree, terrainThemeLayers, traceDownhillPath };
145
+ export { Area3D, AssetStore3D, Billboard3D, BoneAttachment3D, BoneLookAt3D, Camera3D, CharacterBody3D, CharacterController3D, DEFAULT_TERRAIN_TEXTURE_BASE, DirectionalLight3D, Environment3D, DENSITY_PRESETS as FLOWER_DENSITY_PRESETS, FLOWER_VARIETIES, Flowers3D, Foliage3D, InstancedMesh3D, Joint3D, LoftMesh3D, MeshInstance3D, ModelInstance3D, Node3D, OmniLight3D, Particles3D, Physics3D, PhysicsBody3D, QUARTER_PITCH, Renderer3D, RigidBody3D, River3D, StaticBody3D, TERRAIN_THEMES, Terrain3D, Trail3D, Tree3D, VOXEL_PALETTE, VoxelGrid3D, WATER_CUTOUT_MAX, WATER_MAX_RIPPLES, Water3D, WaterCutout3D, buildHeightmap, buildTerrainNav, cameraRelative, createGame3D, createNavDebugNode, enablePhysics3D, horizonColorFromSky, keyboardIntensity, movementState, parseEnvironment3D, registerNodes3D, resolveFlowerDensity, rigPose, setEnvironment3D, splatWeights, sunDirectionFromElevationAzimuth, sunDirectionFromSky, syncTree, terrainThemeLayers, traceDownhillPath };
@@ -3,10 +3,10 @@ import { _ as registerBehavior, n as loadScene } from "./loader-BZqOKfI2.js";
3
3
  import { l as AudioPlayer, u as Engine } from "./register-nObreUQR.js";
4
4
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
5
5
  import { i as resolveRendering, n as attachTouchControls } from "./touch-031PxtCR.js";
6
- import { I as Node3D, N as PhysicsBody3D, R as createCausticsQuad, n as registerGameplayBehaviors } from "./gameplay-B8zVO3Dg.js";
6
+ import { B as createCausticsQuad, F as PhysicsBody3D, R as Node3D, n as registerGameplayBehaviors } from "./gameplay-Ddk13pQ6.js";
7
7
  import { t as debugSources } from "./debug-draw-CZmOYjL2.js";
8
- import { d as DirectionalLight3D, l as ModelInstance3D, t as registerNodes3D, x as Camera3D } from "./register-BgeMHtcG.js";
9
- import { n as enablePhysics3D } from "./physics-3d-DY0EtjfT.js";
8
+ import { d as DirectionalLight3D, l as ModelInstance3D, t as registerNodes3D, x as Camera3D } from "./register-CeWtdiCw.js";
9
+ import { n as enablePhysics3D } from "./physics-3d-Cd7Jl8w1.js";
10
10
  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";
11
11
  import { VRMLoaderPlugin, VRMUtils } from "@pixiv/three-vrm";
12
12
  import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
@@ -3,7 +3,7 @@ import { _ as registerBehavior, n as loadScene, o as computeViewport, s as resol
3
3
  import { l as AudioPlayer, u as Engine } from "./register-nObreUQR.js";
4
4
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
5
5
  import { i as resolveRendering, n as attachTouchControls } from "./touch-031PxtCR.js";
6
- import { n as registerGameplayBehaviors } from "./gameplay-B8zVO3Dg.js";
6
+ import { n as registerGameplayBehaviors } from "./gameplay-Ddk13pQ6.js";
7
7
  import { g as PhysicsBody2D, n as UILayer, t as registerNodes2D, u as Camera2D, y as Node2D } from "./register-CvpSUU3O.js";
8
8
  import { t as debugSources } from "./debug-draw-CZmOYjL2.js";
9
9
  import { n as enablePhysics2D } from "./physics-2d-DiVFFlH3.js";
@@ -2,7 +2,7 @@ import { _ as registerBehavior, f as Node, m as Behavior, n as loadScene } from
2
2
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
3
3
  import { t as jsonClone } from "./json-BLk7H2Qa.js";
4
4
  import { r as effectiveOrder, t as duplicateNode } from "./duplicate-BgtFrFo4.js";
5
- import { Color, CubeCamera, DepthTexture, DoubleSide, FloatType, Frustum, HalfFloatType, LinearMipmapLinearFilter, MathUtils, Matrix4, Mesh, MeshDepthMaterial, Object3D, PerspectiveCamera, Plane, PlaneGeometry, Quaternion, ShaderMaterial, Sphere, Vector2, Vector3, Vector4, WebGLCubeRenderTarget, WebGLRenderTarget } from "three";
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();
8
8
  const scratchMatrix = new Matrix4();
@@ -1022,6 +1022,12 @@ uniform float uReflectivityMax;
1022
1022
  // Both cures are the same: filter the depth over its own texel footprint and
1023
1023
  // estimate gradients from those taps instead of from fwidth().
1024
1024
  uniform vec2 uScenePassTexel;
1025
+ // incanto CUTOUTS (a boat's inside, a diving bell, a well under a dock) are
1026
+ // NOT a fragment test — nothing here knows about them. A cutout stamps its
1027
+ // OWNER'S OWN geometry into the depth pre-pass at the near plane, so the
1028
+ // occlusion discard below removes the water for exactly the pixels that owner
1029
+ // already covers. That is what makes a hole in open sea impossible: this
1030
+ // shader can only ever hide water behind something the player is looking at.
1025
1031
  /** Shoreline dissolve width, in DEPTH TEXELS (≈2 screen px each). */
1026
1032
  const float SHORE_FADE_TEXELS = 11.0;
1027
1033
  /** Deepest water that can still be a shoreline (m) — past this the fade is off. */
@@ -1399,6 +1405,7 @@ void main() {
1399
1405
  depthMask = 1.0 - smoothstep(softDiscardStart, softDiscardEnd, depthDiff);
1400
1406
  // At border: 0 (fully transparent) ~ 1 (fully opaque)
1401
1407
  }
1408
+
1402
1409
  }
1403
1410
 
1404
1411
  // Calculate vector from camera to the vertex
@@ -2388,6 +2395,75 @@ var CharacterBody3D = class extends PhysicsBody3D {
2388
2395
  }
2389
2396
  };
2390
2397
  //#endregion
2398
+ //#region src/3d/nodes/water-cutout-3d.ts
2399
+ /**
2400
+ * A box the water does not enter.
2401
+ *
2402
+ * Water is ONE surface: it has no idea a boat has an inside. Float an open hull
2403
+ * and every wave crest that rises above its floor renders as sea sloshing
2404
+ * through the deck — correct for a plane cutting an open box, wrong for a boat.
2405
+ * Freeboard alone cannot fix it (a big enough swell always wins), so the fix is
2406
+ * to name the geometry that holds the water back. `Water3D` re-draws that
2407
+ * geometry — the part of it inside this box — into its depth pre-pass at the
2408
+ * near plane, and its ordinary occlusion discard then removes the water for
2409
+ * exactly the pixels the hull already covers.
2410
+ *
2411
+ * Park it as a CHILD of the thing it belongs to — a hull, a diving bell, a
2412
+ * dock's well, a cave mouth — and it follows along, rotation included.
2413
+ *
2414
+ * SAFE BY CONSTRUCTION: nothing is ever cut out of the water. The mask is the
2415
+ * owner's own silhouette, so water can only be hidden behind geometry the
2416
+ * player is already looking at. Size the box to the interior and err large —
2417
+ * an overlarge box just means more of this hull holds water back; it can never
2418
+ * punch a rectangle of bare ground into open water. (Needs the depth pre-pass:
2419
+ * `Water3D` in `fancy` quality. Skinned and instanced meshes are re-drawn at
2420
+ * their rest pose, so hang cutouts on ordinary geometry.)
2421
+ *
2422
+ * ```json
2423
+ * { "name": "Rowboat", "type": "RigidBody3D", "children": [
2424
+ * { "name": "DryInside", "type": "WaterCutout3D",
2425
+ * "props": { "size": [3.0, 1.2, 1.1], "position": [0, 0.4, 0] } } ] }
2426
+ * ```
2427
+ *
2428
+ * Cheap: a handful of these cost a few ALU per water pixel. `Water3D` reads the
2429
+ * nearest {@link WATER_CUTOUT_MAX} of them.
2430
+ */
2431
+ var WaterCutout3D = class extends Node3D {
2432
+ static typeName = "WaterCutout3D";
2433
+ static props = {
2434
+ size: { default: [
2435
+ 2,
2436
+ 1,
2437
+ 1
2438
+ ] },
2439
+ enabled: { default: true }
2440
+ };
2441
+ /** Box extent in meters, centered on the node ([x, y, z]). */
2442
+ size = [
2443
+ 2,
2444
+ 1,
2445
+ 1
2446
+ ];
2447
+ /** Live toggle — a scuttled hull can start letting the sea in. */
2448
+ enabled = true;
2449
+ static validateJson(node) {
2450
+ const c = node;
2451
+ if (!Array.isArray(c.size) || c.size.length !== 3 || c.size.some((v) => !(v > 0))) throw new IncantoError("BAD_FORMAT", `WaterCutout3D '${node.name}' size must be three positive meters [x, y, z], got ${JSON.stringify(c.size)}.`, { prop: "size" });
2452
+ }
2453
+ /**
2454
+ * A cutout works by re-drawing its PARENT, so it has to have one. At the
2455
+ * scene root it would hand the whole world a licence to erase water — the
2456
+ * one failure this design exists to rule out — so that arrangement is a hard
2457
+ * load error, not a silent no-op. (Checked here rather than in
2458
+ * `validateJson`: the loader validates a node before attaching it.)
2459
+ */
2460
+ onReady() {
2461
+ if (!this.parent?.parent) throw new IncantoError("BAD_FORMAT", `WaterCutout3D '${this.name}' must be a CHILD of the object that holds the water back (a hull, a diving bell, a dock's well) — it re-draws that object to keep water out of it, so at the scene root it has nothing to stand in for.`, { prop: "parent" });
2462
+ }
2463
+ };
2464
+ /** How many cutouts one Water3D reads (the nearest ones win). */
2465
+ const WATER_CUTOUT_MAX = 4;
2466
+ //#endregion
2391
2467
  //#region src/3d/nodes/water-3d.ts
2392
2468
  const WATER_QUALITIES = ["fancy", "simple"];
2393
2469
  const COLOR_KEYS = [
@@ -2869,6 +2945,10 @@ var Water3D = class Water3D extends Node3D {
2869
2945
  z: 0
2870
2946
  };
2871
2947
  _bodies = [];
2948
+ _cutouts = [];
2949
+ /** Scratch for the cutout mask pass (no per-frame allocation). */
2950
+ _maskMeshes = [];
2951
+ _maskSaved = [];
2872
2952
  /** Scratch for the side-pass cull (no per-frame allocation). */
2873
2953
  _passHidden = [];
2874
2954
  cubeTarget = null;
@@ -3052,6 +3132,7 @@ var Water3D = class Water3D extends Node3D {
3052
3132
  restoreForWaterPass(this._passHidden);
3053
3133
  ctx.scene.overrideMaterial = prevOverride;
3054
3134
  ctx.scene.background = prevBackground;
3135
+ this.renderCutoutMasks(ctx);
3055
3136
  ctx.gl.setRenderTarget(prevTarget);
3056
3137
  ctx.gl.autoClear = prevAutoClear;
3057
3138
  ctx.gl.clippingPlanes = prevClippingPlanes;
@@ -3232,6 +3313,61 @@ var Water3D = class Water3D extends Node3D {
3232
3313
  side: DoubleSide
3233
3314
  });
3234
3315
  }
3316
+ /**
3317
+ * Stamp every {@link WaterCutout3D} owner into the depth pre-pass.
3318
+ *
3319
+ * The cutout does not describe a hole in the water — it names a piece of
3320
+ * geometry that holds the water back, and this pass re-draws exactly that
3321
+ * geometry (clipped to the cutout box) at the near plane. The water's
3322
+ * occlusion discard does the rest. The tree is walked live, so a cutout on a
3323
+ * bobbing hull tracks it with no wiring.
3324
+ */
3325
+ renderCutoutMasks(ctx) {
3326
+ const list = this._cutouts;
3327
+ list.length = 0;
3328
+ let top = this;
3329
+ while (top.parent) top = top.parent;
3330
+ collectCutouts(top, list);
3331
+ if (list.length === 0) return;
3332
+ const u = cutoutMaskMaterial.uniforms;
3333
+ const saved = this._maskSaved;
3334
+ const meshes = this._maskMeshes;
3335
+ const prevClip = ctx.gl.clippingPlanes;
3336
+ ctx.gl.clippingPlanes = noClipPlanes;
3337
+ let n = 0;
3338
+ for (const cut of list) {
3339
+ if (n >= 4) break;
3340
+ const ownerObj = cutoutOwnerObject(cut);
3341
+ if (!ownerObj) continue;
3342
+ cut._syncObject3D();
3343
+ const world = cut._ensureObject3D();
3344
+ world.updateWorldMatrix(true, false);
3345
+ world.getWorldPosition(cutoutPosScratch);
3346
+ world.getWorldQuaternion(cutoutQuatScratch);
3347
+ (u.uCenter?.value)?.copy(cutoutPosScratch);
3348
+ (u.uHalf?.value)?.set((cut.size[0] ?? 1) / 2, (cut.size[1] ?? 1) / 2, (cut.size[2] ?? 1) / 2);
3349
+ cutoutEulerScratch.setFromQuaternion(cutoutQuatScratch, "YXZ");
3350
+ (u.uYaw?.value)?.set(Math.cos(cutoutEulerScratch.y), Math.sin(cutoutEulerScratch.y));
3351
+ meshes.length = 0;
3352
+ saved.length = 0;
3353
+ collectMaskMeshes(ownerObj, meshes);
3354
+ if (meshes.length === 0) continue;
3355
+ for (const m of meshes) {
3356
+ saved.push(m.material);
3357
+ m.material = cutoutMaskMaterial;
3358
+ }
3359
+ ctx.gl.render(ownerObj, ctx.camera);
3360
+ for (let i = 0; i < meshes.length; i++) {
3361
+ const m = meshes[i];
3362
+ const mat = saved[i];
3363
+ if (m && mat) m.material = mat;
3364
+ }
3365
+ n++;
3366
+ }
3367
+ meshes.length = 0;
3368
+ saved.length = 0;
3369
+ ctx.gl.clippingPlanes = prevClip;
3370
+ }
3235
3371
  syncFancy(mesh) {
3236
3372
  const u = mesh.material.uniforms;
3237
3373
  const w = this.size[0] ?? 1;
@@ -3444,6 +3580,45 @@ var Water3D = class Water3D extends Node3D {
3444
3580
  });
3445
3581
  }
3446
3582
  };
3583
+ const cutoutPosScratch = new Vector3();
3584
+ const cutoutQuatScratch = new Quaternion();
3585
+ const cutoutEulerScratch = new Euler();
3586
+ /** Every enabled water cutout in the tree, nearest-first is not needed: the
3587
+ * cap is small and scenes rarely exceed it. */
3588
+ function collectCutouts(node, out) {
3589
+ if (node instanceof WaterCutout3D && node.enabled && node.visible) out.push(node);
3590
+ for (const child of node.children) collectCutouts(child, out);
3591
+ }
3592
+ /**
3593
+ * The three object of the thing a cutout hangs on — its PARENT, the hull that
3594
+ * holds the water back. A cutout parented to the scene root has no owner and
3595
+ * does nothing (the loader rejects that arrangement outright): masking a whole
3596
+ * scene's geometry is how you get a hole in open water, which is the one thing
3597
+ * this design must never allow.
3598
+ */
3599
+ function cutoutOwnerObject(cut) {
3600
+ const owner = cut.parent;
3601
+ if (!(owner instanceof Node3D) || !owner.parent) return null;
3602
+ const obj = owner._ensureObject3D();
3603
+ return typeof obj?.traverse === "function" ? obj : null;
3604
+ }
3605
+ /** Node types that never stand in for water, however they are parented. */
3606
+ const NEVER_MASKS = new Set([
3607
+ "Terrain3D",
3608
+ "Water3D",
3609
+ "River3D"
3610
+ ]);
3611
+ /** Visible meshes of a cutout owner, the ones the mask pass re-draws. */
3612
+ function collectMaskMeshes(root, out) {
3613
+ root.traverse((obj) => {
3614
+ const mesh = obj;
3615
+ if (!mesh.isMesh || !mesh.visible || !mesh.material) return;
3616
+ const type = (obj.userData?.incantoNode)?.constructor?.typeName;
3617
+ if (type && NEVER_MASKS.has(type)) return;
3618
+ out.push(mesh);
3619
+ });
3620
+ return out;
3621
+ }
3447
3622
  /** Bodies that can splash: kinematic characters and dynamic rigid bodies. */
3448
3623
  function collectBodies(node, out) {
3449
3624
  if (node instanceof CharacterBody3D || node instanceof RigidBody3D) out.push(node);
@@ -3517,6 +3692,8 @@ const mirrorClipPlane = new Plane(new Vector3(0, 1, 0), 0);
3517
3692
  const mirrorClipPlanes = [mirrorClipPlane];
3518
3693
  const grabClipPlane = new Plane(new Vector3(0, -1, 0), 0);
3519
3694
  const grabClipPlanes = [grabClipPlane];
3695
+ /** No clipping — the cutout mask pass wants the submerged geometry back. */
3696
+ const noClipPlanes = [];
3520
3697
  /** Keep-above twin for the depth-completion pass: normal (0,1,0) keeps
3521
3698
  * y > −constant — set per render to waterY − slack (overlapping the grab). */
3522
3699
  const depthOnlyClipPlane = new Plane(new Vector3(0, 1, 0), 0);
@@ -3524,12 +3701,64 @@ const depthOnlyClipPlanes = [depthOnlyClipPlane];
3524
3701
  /** Shared override for the depth-completion pass — writes depth, never color. */
3525
3702
  const depthOnlyMaterial = new MeshDepthMaterial();
3526
3703
  depthOnlyMaterial.colorWrite = false;
3704
+ /**
3705
+ * The cutout mask material — the whole of {@link WaterCutout3D}'s mechanism.
3706
+ *
3707
+ * It is drawn over the cutout OWNER'S OWN meshes into the water's depth
3708
+ * pre-pass, and does exactly two things: throw away everything outside the
3709
+ * cutout box, and stamp what is left at the NEAR PLANE. The water's existing
3710
+ * occlusion discard then reads "something solid is in front of me here" and
3711
+ * removes itself — for exactly the pixels the owner's geometry already covers.
3712
+ *
3713
+ * Screen position is untouched (only clip-space z is rewritten), so the mask
3714
+ * is pixel-identical to the owner's silhouette. THAT is the safety property:
3715
+ * water can only ever be hidden behind the hull the player is looking at, so
3716
+ * an oversized box degrades into "more of this boat holds water back" and can
3717
+ * never carve a rectangle of bare seabed out of open water.
3718
+ */
3719
+ const cutoutMaskMaterial = new ShaderMaterial({
3720
+ uniforms: {
3721
+ uCenter: { value: new Vector3() },
3722
+ uHalf: { value: new Vector3(1, 1, 1) },
3723
+ uYaw: { value: new Vector2(1, 0) }
3724
+ },
3725
+ vertexShader: `
3726
+ uniform vec3 uCenter;
3727
+ uniform vec2 uYaw;
3728
+ varying vec3 vCutLocal;
3729
+ void main() {
3730
+ vec3 world = (modelMatrix * vec4(position, 1.0)).xyz;
3731
+ vec3 rel = world - uCenter;
3732
+ vCutLocal = vec3(rel.x * uYaw.x + rel.z * uYaw.y, rel.y, -rel.x * uYaw.y + rel.z * uYaw.x);
3733
+ vec4 clip = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
3734
+ // pull the DEPTH to the near plane and nothing else — scaling the view
3735
+ // position would slide the silhouette under perspective, and a silhouette
3736
+ // that grows past its owner is exactly the hole this design forbids
3737
+ clip.z = -clip.w * 0.99;
3738
+ gl_Position = clip;
3739
+ }
3740
+ `,
3741
+ fragmentShader: `
3742
+ uniform vec3 uHalf;
3743
+ varying vec3 vCutLocal;
3744
+ void main() {
3745
+ if (any(greaterThan(abs(vCutLocal), uHalf))) discard;
3746
+ gl_FragColor = vec4(0.0);
3747
+ }
3748
+ `,
3749
+ colorWrite: false,
3750
+ depthWrite: true,
3751
+ depthTest: false,
3752
+ side: DoubleSide
3753
+ });
3527
3754
  //#endregion
3528
3755
  //#region src/gameplay/buoyancy.ts
3529
3756
  /** Gravity the float is sized against (m/s²) — the cap is expressed in g. */
3530
3757
  const G = 9.81;
3531
3758
  /** Hard ceiling on the lift, in g. Water pushes back; it does not launch. */
3532
- const MAX_LIFT_G = 3.5;
3759
+ const MAX_LIFT_G = 6;
3760
+ /** Depth scale the restoring force is measured against when `draft` is tiny. */
3761
+ const MIN_DRAFT_REF = .15;
3533
3762
  /**
3534
3763
  * Float on water.
3535
3764
  *
@@ -3553,10 +3782,13 @@ var Buoyancy = class extends Behavior {
3553
3782
  static props = {
3554
3783
  /** Water node path; empty = the first Water3D in the tree. */
3555
3784
  water: { default: "" },
3556
- /** How deep the hull sits at rest, in meters (its waterline). */
3785
+ /** Where the resting WATERLINE sits below the body origin, in meters. A
3786
+ * hull whose origin is its interior floor wants a draft of ~0.1: the water
3787
+ * then rests that far under the floor and the boat is dry inside. */
3557
3788
  draft: { default: .35 },
3558
- /** Float stiffness (1/s²) higher snaps to the surface, lower wallows. */
3559
- stiffness: { default: 14 },
3789
+ /** How stiffly the hull returns to its waterline (extra g per draft of
3790
+ * submersion). 1 wallows, 3 is a firm dinghy, 8 is a cork. */
3791
+ stiffness: { default: 3 },
3560
3792
  /** Vertical damping (1/s) — how fast the bobbing settles. */
3561
3793
  damping: { default: 3.5 },
3562
3794
  /** How hard wave slopes carry the body along (0 = bob in place). */
@@ -3570,8 +3802,10 @@ var Buoyancy = class extends Behavior {
3570
3802
  };
3571
3803
  static signals = ["submerged", "surfaced"];
3572
3804
  water = "";
3805
+ /** Resting waterline, in meters BELOW the body origin. */
3573
3806
  draft = .35;
3574
- stiffness = 14;
3807
+ /** Extra g of lift per draft of submersion below the waterline. */
3808
+ stiffness = 3;
3575
3809
  damping = 3.5;
3576
3810
  drift = .6;
3577
3811
  drag = .9;
@@ -3606,7 +3840,7 @@ var Buoyancy = class extends Behavior {
3606
3840
  const depth = water.heightAt(px, pz) - (y - this.draft);
3607
3841
  if (depth <= 0) continue;
3608
3842
  wetPoints++;
3609
- liftAccel += this.stiffness * depth;
3843
+ liftAccel += G * (1 + this.stiffness * (depth / Math.max(this.draft, MIN_DRAFT_REF)));
3610
3844
  const e = .6;
3611
3845
  slopeX += water.heightAt(px + e, pz) - water.heightAt(px - e, pz);
3612
3846
  slopeZ += water.heightAt(px, pz + e) - water.heightAt(px, pz - e);
@@ -5633,4 +5867,4 @@ function registerGameplayBehaviors(opts) {
5633
5867
  for (const [name, ctor] of Object.entries(GAMEPLAY_BEHAVIORS)) registerBehavior(name, ctor, { replace });
5634
5868
  }
5635
5869
  //#endregion
5636
- export { Water3D as A, colliderFootDrop as B, FollowCamera as C, Health as D, DamageOnContact as E, StaticBody3D as F, Node3D as I, validateCollider3D as L, CharacterBody3D as M, PhysicsBody3D as N, Collector as O, RigidBody3D as P, createCausticsQuad as R, restartScene as S, phaseOf as T, hitStop as _, Wander as a, GameFlow as b, Projectile as c, PathFollow as d, Oscillate as f, Cooldown as g, CameraShake as h, WaveSpawner as i, Area3D as j, Chase as k, Pickup as l, Lifetime as m, registerGameplayBehaviors as n, Spawner as o, MoveTo as p, ZombieAI as r, ScoreKeeper as s, GAMEPLAY_BEHAVIORS as t, Patrol as u, screenFlash as v, DayNight as w, goToScene as x, Interactable as y, WATER_MAX_RIPPLES as z };
5870
+ export { Water3D as A, createCausticsQuad as B, FollowCamera as C, Health as D, DamageOnContact as E, PhysicsBody3D as F, colliderFootDrop as H, RigidBody3D as I, StaticBody3D as L, WaterCutout3D as M, Area3D as N, Collector as O, CharacterBody3D as P, Node3D as R, restartScene as S, phaseOf as T, WATER_MAX_RIPPLES as V, hitStop as _, Wander as a, GameFlow as b, Projectile as c, PathFollow as d, Oscillate as f, Cooldown as g, CameraShake as h, WaveSpawner as i, WATER_CUTOUT_MAX as j, Chase as k, Pickup as l, Lifetime as m, registerGameplayBehaviors as n, Spawner as o, MoveTo as p, ZombieAI as r, ScoreKeeper as s, GAMEPLAY_BEHAVIORS as t, Patrol as u, screenFlash as v, DayNight as w, goToScene as x, Interactable as y, validateCollider3D as z };
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-B8zVO3Dg.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-Ddk13pQ6.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.js CHANGED
@@ -295,6 +295,6 @@ function newUid() {
295
295
  //#endregion
296
296
  //#region src/index.ts
297
297
  /** Engine version. Kept in sync with package.json by the release pipeline. */
298
- const VERSION = "0.25.3";
298
+ const VERSION = "0.26.0";
299
299
  //#endregion
300
300
  export { AudioBuses, AudioPlayer, Behavior, CONST_REF_KEY, Engine, HudLayer, IncantoError, InputMap, LogManager, MusicManager, Node, ORDER_GROUP_BASE, PARTICLE_PRESETS, PARTICLE_PRESET_NAMES, ParticleSim, ROLLOFF_MODELS, Rng, SCENE_FORMAT, SFX_PRESETS, SFX_PRESET_NAMES, Scene, SceneTree, SfxEngine, Signal, Timer, TouchControls, UiBanner, UiBar, UiButton, UiDialogue, UiText, VERSION, WebAudioMusicBackend, applyParticlePreset, assetUrls, attachTouchControls, auditScene, 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, registerBehavior, registerCoreNodes, registerNode, registeredBehaviors, registeredTypes, replay, resolveConstants, resolveRendering, resolveViewport, serializeNode, spatialGain, spatialPan, startRecording, synthSfx };
@@ -1,8 +1,8 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
2
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
3
- import { I as Node3D, L as validateCollider3D, M as CharacterBody3D, N as PhysicsBody3D, P as RigidBody3D, j as Area3D } from "./gameplay-B8zVO3Dg.js";
3
+ import { F as PhysicsBody3D, I as RigidBody3D, N as Area3D, P as CharacterBody3D, R as Node3D, z as validateCollider3D } from "./gameplay-Ddk13pQ6.js";
4
4
  import { n as registerDebugSource } from "./debug-draw-CZmOYjL2.js";
5
- import { T as Terrain3D, k as Joint3D } from "./register-BgeMHtcG.js";
5
+ import { T as Terrain3D, k as Joint3D } from "./register-CeWtdiCw.js";
6
6
  import { Euler, Quaternion } from "three";
7
7
  //#region src/3d/physics/physics-3d.ts
8
8
  var physics_3d_exports = /* @__PURE__ */ __exportAll({
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-K8A82JK5.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-yGqGR-Ic.js").then((n) => n.n)).createGame2D(o)))(opts);
159
+ const next = await (_gameFactory ?? (mode === "3d" ? async (o) => (await import("./create-game-B_qBR65J.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-C0bPoURs.js").then((n) => n.n)).createGame2D(o)))(opts);
160
160
  if (disposed) {
161
161
  next.dispose();
162
162
  return;
@@ -4,7 +4,7 @@ import { t as Rng } from "./rng-DP-SR7eg.js";
4
4
  import { i as getNodeSchema, l as registerNode } from "./registry-BVJ2HbCn.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, B as colliderFootDrop, F as StaticBody3D, I as Node3D, M as CharacterBody3D, N as PhysicsBody3D, P as RigidBody3D, j as Area3D } from "./gameplay-B8zVO3Dg.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-Ddk13pQ6.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, Color, ConeGeometry, CylinderGeometry, DataTexture, DirectionalLight, DoubleSide, DynamicDrawUsage, Euler, Group, IcosahedronGeometry, ImageBitmapLoader, InstancedBufferAttribute, InstancedMesh, LinearFilter, LoopOnce, LoopRepeat, Matrix4, Mesh, MeshBasicMaterial, MeshDepthMaterial, MeshPhysicalMaterial, MeshStandardMaterial, NearestFilter, NoBlending, 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";
@@ -10870,6 +10870,7 @@ function registerNodes3D() {
10870
10870
  registerNode(River3D);
10871
10871
  registerNode(Terrain3D);
10872
10872
  registerNode(Water3D);
10873
+ registerNode(WaterCutout3D);
10873
10874
  registerNode(Foliage3D);
10874
10875
  registerNode(Flowers3D);
10875
10876
  registerNode(Tree3D);
package/dist/test.js CHANGED
@@ -4,9 +4,9 @@ import { t as IncantoError } from "./errors-BpWbnbb_.js";
4
4
  import { t as auditScene } from "./audit-C6rMyict.js";
5
5
  import { n as jsonEquals, t as jsonClone } from "./json-BLk7H2Qa.js";
6
6
  import { i as getNodeSchema, s as mergeStaticProps } from "./registry-BVJ2HbCn.js";
7
- import { n as registerGameplayBehaviors } from "./gameplay-B8zVO3Dg.js";
7
+ import { n as registerGameplayBehaviors } from "./gameplay-Ddk13pQ6.js";
8
8
  import { t as registerNodes2D } from "./register-CvpSUU3O.js";
9
- import { t as registerNodes3D } from "./register-BgeMHtcG.js";
9
+ import { t as registerNodes3D } from "./register-CeWtdiCw.js";
10
10
  import { t as registerNodesNet } from "./register-BFFE1Mh1.js";
11
11
  //#region src/test/index.ts
12
12
  /**
@@ -127,7 +127,7 @@ async function runScript(json, opts) {
127
127
  const { enablePhysics2D } = await import("./physics-2d-DiVFFlH3.js").then((n) => n.r);
128
128
  await enablePhysics2D(engine);
129
129
  } else if (physics === "3d" || physics === "auto" && scene.dimension === "3d") {
130
- const { enablePhysics3D } = await import("./physics-3d-DY0EtjfT.js").then((n) => n.r);
130
+ const { enablePhysics3D } = await import("./physics-3d-Cd7Jl8w1.js").then((n) => n.r);
131
131
  await enablePhysics3D(engine);
132
132
  }
133
133
  const failures = [];
@@ -232,7 +232,7 @@ async function createPlaySession(json, opts = {}) {
232
232
  const { enablePhysics2D } = await import("./physics-2d-DiVFFlH3.js").then((n) => n.r);
233
233
  await enablePhysics2D(engine);
234
234
  } else if (physics === "3d" || physics === "auto" && scene.dimension === "3d") {
235
- const { enablePhysics3D } = await import("./physics-3d-DY0EtjfT.js").then((n) => n.r);
235
+ const { enablePhysics3D } = await import("./physics-3d-Cd7Jl8w1.js").then((n) => n.r);
236
236
  await enablePhysics3D(engine);
237
237
  }
238
238
  const stepMs = 1e3 / (opts.fixedHz ?? 60);
@@ -1 +1 @@
1
- import{t as e}from"./index-BGtXxzJn.js";async function t(t){return new n((await e(()=>import(`./GameServer-C56iOUgF.js`),[],import.meta.url)).GameServer,t)}var n=class{raw;active=new Map;reconnecting=!1;disposed=!1;constructor(e,t){this.raw=new e({...t})}get account(){return this.raw.account}get connected(){return this.raw.connected}connect(){return this.raw.connected?Promise.resolve(!0):(this.disposed=!1,this.rawConnect())}rawConnect(){return this.raw.connect({onDisconnect:()=>void this.reconnect()})}disconnect(){this.disposed=!0;for(let e of this.active.values())e.off();return this.active.clear(),this.raw.disconnect()}remoteFunction(e,t,n){return this.raw.remoteFunction(e,t,n)}track(e){let t=Symbol(`sub`),n={make:e,off:e()};return this.active.set(t,n),()=>{n.off(),this.active.delete(t)}}async reconnect(){if(!this.disposed&&!this.reconnecting){this.reconnecting=!0;try{await this.rawConnect();for(let e of this.active.values())e.off(),e.off=e.make()}finally{this.reconnecting=!1}}}subscribeRoomState(e,t){return this.track(()=>this.raw.subscribeRoomState(e,t))}subscribeRoomMyState(e,t){return this.track(()=>this.raw.subscribeRoomMyState(e,t))}subscribeRoomAllUserStates(e,t){return this.track(()=>this.raw.subscribeRoomAllUserStates(e,e=>{let n={};for(let t of e??[]){if(!t||typeof t.account!=`string`||t.__leaved)continue;let{account:e,__updated:r,__leaved:i,...a}=t;n[e]=a}t(n)}))}subscribeRoomCollection(e,t,n){return this.track(()=>this.raw.subscribeRoomCollection(e,t,({items:e})=>{let t={};for(let n of e??[])n&&typeof n.__id==`string`&&(t[n.__id]=n);n(t)}))}onRoomMessage(e,t,n){return this.track(()=>this.raw.onRoomMessage(e,t,n))}onRoomUserJoin(e,t){return this.track(()=>this.raw.onRoomUserJoin(e,t))}onRoomUserLeave(e,t){return this.track(()=>this.raw.onRoomUserLeave(e,t))}subscribeGlobalState(e){return this.track(()=>this.raw.subscribeGlobalState(e))}subscribeGlobalMyState(e){return this.track(()=>this.raw.subscribeGlobalMyState(e))}subscribeGlobalUserState(e,t){return this.track(()=>this.raw.subscribeGlobalUserState(e,t))}subscribeGlobalCollection(e,t){return this.track(()=>this.raw.subscribeGlobalCollection(e,({items:e})=>{let n={};for(let t of e??[])t&&typeof t.__id==`string`&&(n[t.__id]=t);t(n)}))}subscribeAsset(e,t){return this.track(()=>this.raw.subscribeAsset(e,t))}onGlobalMessage(e,t){return this.track(()=>this.raw.onGlobalMessage(e,t))}};export{t as createAgent8Server};
1
+ import{t as e}from"./index-D4_I76v2.js";async function t(t){return new n((await e(()=>import(`./GameServer-C56iOUgF.js`),[],import.meta.url)).GameServer,t)}var n=class{raw;active=new Map;reconnecting=!1;disposed=!1;constructor(e,t){this.raw=new e({...t})}get account(){return this.raw.account}get connected(){return this.raw.connected}connect(){return this.raw.connected?Promise.resolve(!0):(this.disposed=!1,this.rawConnect())}rawConnect(){return this.raw.connect({onDisconnect:()=>void this.reconnect()})}disconnect(){this.disposed=!0;for(let e of this.active.values())e.off();return this.active.clear(),this.raw.disconnect()}remoteFunction(e,t,n){return this.raw.remoteFunction(e,t,n)}track(e){let t=Symbol(`sub`),n={make:e,off:e()};return this.active.set(t,n),()=>{n.off(),this.active.delete(t)}}async reconnect(){if(!this.disposed&&!this.reconnecting){this.reconnecting=!0;try{await this.rawConnect();for(let e of this.active.values())e.off(),e.off=e.make()}finally{this.reconnecting=!1}}}subscribeRoomState(e,t){return this.track(()=>this.raw.subscribeRoomState(e,t))}subscribeRoomMyState(e,t){return this.track(()=>this.raw.subscribeRoomMyState(e,t))}subscribeRoomAllUserStates(e,t){return this.track(()=>this.raw.subscribeRoomAllUserStates(e,e=>{let n={};for(let t of e??[]){if(!t||typeof t.account!=`string`||t.__leaved)continue;let{account:e,__updated:r,__leaved:i,...a}=t;n[e]=a}t(n)}))}subscribeRoomCollection(e,t,n){return this.track(()=>this.raw.subscribeRoomCollection(e,t,({items:e})=>{let t={};for(let n of e??[])n&&typeof n.__id==`string`&&(t[n.__id]=n);n(t)}))}onRoomMessage(e,t,n){return this.track(()=>this.raw.onRoomMessage(e,t,n))}onRoomUserJoin(e,t){return this.track(()=>this.raw.onRoomUserJoin(e,t))}onRoomUserLeave(e,t){return this.track(()=>this.raw.onRoomUserLeave(e,t))}subscribeGlobalState(e){return this.track(()=>this.raw.subscribeGlobalState(e))}subscribeGlobalMyState(e){return this.track(()=>this.raw.subscribeGlobalMyState(e))}subscribeGlobalUserState(e,t){return this.track(()=>this.raw.subscribeGlobalUserState(e,t))}subscribeGlobalCollection(e,t){return this.track(()=>this.raw.subscribeGlobalCollection(e,({items:e})=>{let n={};for(let t of e??[])t&&typeof t.__id==`string`&&(n[t.__id]=t);t(n)}))}subscribeAsset(e,t){return this.track(()=>this.raw.subscribeAsset(e,t))}onGlobalMessage(e,t){return this.track(()=>this.raw.onGlobalMessage(e,t))}};export{t as createAgent8Server};