incanto 0.25.4 → 0.27.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-iYptFr-q.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";
@@ -1590,7 +1590,18 @@ interface PathSample {
1590
1590
  }
1591
1591
  /** A cross-section station: where the water is, how wide, how fast. */
1592
1592
  interface RiverRing extends PathSample {
1593
+ /** The width the author ASKED for (half of it) — nominal, drives current. */
1593
1594
  halfWidth: number;
1595
+ /** Wetted half-width to the left of the flow: where the ground rises to the
1596
+ * surface. This, not {@link halfWidth}, is where the water actually ends. */
1597
+ halfLeft: number;
1598
+ /** Wetted half-width to the right of the flow. */
1599
+ halfRight: number;
1600
+ /** No bank on that side: the ground never came up to the surface, so the
1601
+ * water is spilling out over it rather than being held. The ribbon thins to
1602
+ * a film there instead of ending in a wall of water standing on nothing. */
1603
+ spillLeft: boolean;
1604
+ spillRight: boolean;
1594
1605
  /** Water surface height (local Y). */
1595
1606
  surfaceY: number;
1596
1607
  /** Downhill grade of the surface (dimensionless, ≥ 0). */
@@ -1706,6 +1717,18 @@ declare class River3D extends Node3D implements SunConsumer3D {
1706
1717
  sunIntensity: number;
1707
1718
  /** Drape target path; empty = auto-find the first Terrain3D in the tree. */
1708
1719
  terrain: string;
1720
+ /**
1721
+ * Cut the bed. Real water arrives on a hillside and erodes itself a channel;
1722
+ * a ribbon laid on raw ground has nothing holding it and reads as a strip of
1723
+ * blue hovering over the grass. With this on, the river trenches its own
1724
+ * course into the terrain it drapes on — banks the player can walk into, a
1725
+ * collider and `heightAt` that agree with it, and splat that paints the cut.
1726
+ *
1727
+ * Off means you carve it yourself with `Terrain3D.channels` (or the reach is
1728
+ * genuinely a flat flood plain). Either way the surface is FITTED to
1729
+ * whatever ground it finds: it can never stand above its own banks.
1730
+ */
1731
+ carve: boolean;
1709
1732
  /** How hard the current sweeps bodies downstream (0 = visual only). */
1710
1733
  flowForce: number;
1711
1734
  /**
@@ -1721,13 +1744,22 @@ declare class River3D extends Node3D implements SunConsumer3D {
1721
1744
  private sprayKey;
1722
1745
  private courseKey;
1723
1746
  private bedAt;
1747
+ /** Whether the last course was fitted to real ground (see buildRiverRings). */
1748
+ private fitted;
1749
+ /** Everything standing in the current, rescanned on a slow timer. */
1750
+ private rocks;
1751
+ private rockScanAt;
1724
1752
  private world;
1725
1753
  private readonly _bodies;
1726
1754
  /** Loader hook: a malformed river fails at LOAD, not as an invisible ribbon. */
1727
1755
  static validateJson(node: Node): void;
1728
1756
  protected override _createObject3D(): Object3D;
1757
+ override onReady(): void;
1758
+ override onExitTree(): void;
1729
1759
  override update(dt: number): void;
1730
1760
  override _syncObject3D(): void;
1761
+ /** Rocks are picked per FRAME (nearest to the eye), scanned on a timer. */
1762
+ _onRender3D(ctx: RenderContext3D): void;
1731
1763
  /**
1732
1764
  * Where a WORLD point sits in the channel — the query gameplay scripts run
1733
1765
  * (steer a raft, drown a torch, decide a ford is too fast to cross). `null`
@@ -1755,6 +1787,14 @@ declare class River3D extends Node3D implements SunConsumer3D {
1755
1787
  _applySunDirection(dir: readonly [number, number, number]): void;
1756
1788
  override free(): void;
1757
1789
  private widthProfile;
1790
+ /**
1791
+ * Cut (or release) this river's trench in the terrain it drapes on.
1792
+ *
1793
+ * Runs at ready as well as on every course rebuild: the heightfield collider
1794
+ * is built before the first render sync, so a bed cut only at draw time
1795
+ * would leave the player walking on ground that is no longer there.
1796
+ */
1797
+ private applyCarve;
1758
1798
  /** Resolve the drape terrain; an explicit path that misses is a hard error. */
1759
1799
  private resolveTerrain;
1760
1800
  /**
@@ -1764,6 +1804,21 @@ declare class River3D extends Node3D implements SunConsumer3D {
1764
1804
  private ensureCourse;
1765
1805
  private rebuildIfNeeded;
1766
1806
  private buildMaterial;
1807
+ /**
1808
+ * Find everything standing in the current.
1809
+ *
1810
+ * A boulder in a creek is not a physics problem — it is a thing the water
1811
+ * has to be TOLD about, or the stream runs through it as if it were painted
1812
+ * on. Any mesh whose footprint lands inside the wetted channel and whose top
1813
+ * reaches near the surface counts, `InstancedMesh3D` rows included (a rock
1814
+ * scatter is one node with fifty stones in it).
1815
+ *
1816
+ * Scanned on a slow timer rather than per frame: rocks do not move often,
1817
+ * and a river with a hundred of them still writes only the nearest few.
1818
+ */
1819
+ private scanRocks;
1820
+ /** Write the nearest rocks into the shader, faded in by distance. */
1821
+ private syncRocks;
1767
1822
  private syncUniforms;
1768
1823
  /**
1769
1824
  * Hang a mist plume on every fall. Runtime children (`__spray*`), rebuilt
@@ -1965,6 +2020,14 @@ declare class Terrain3D extends Node3D {
1965
2020
  * bed to sit in, and the collider/heightAt/drape all see it. */
1966
2021
  channels: ChannelSpec[];
1967
2022
  /**
2023
+ * Trenches other NODES cut in this terrain — a `River3D` carving its own bed.
2024
+ * Keyed by the node that owns each, so a moving or re-pathed river replaces
2025
+ * its cut instead of stacking a new one. Runtime state, never serialized:
2026
+ * the scene file says "there is a river here", not "here is the ditch it
2027
+ * dug", and re-loading the scene digs it again.
2028
+ */
2029
+ private readonly externalChannels;
2030
+ /**
1968
2031
  * WET SAND band: `{ y, band? }` darkens + glosses the splat in a noisy,
1969
2032
  * breathing band just above height `y` (a waterline) — the trace of the
1970
2033
  * last swash runup. `band` is the max height above `y` that reads wet
@@ -1989,6 +2052,14 @@ declare class Terrain3D extends Node3D {
1989
2052
  * physics bodies follow).
1990
2053
  */
1991
2054
  heightAt(x: number, z: number): number;
2055
+ /**
2056
+ * @internal Cut (spec) or release (null) a trench another node owns. A
2057
+ * `River3D` calls this to dig its own bed; the heightmap, the collider,
2058
+ * `heightAt`, the drape and the mesh all pick it up on their next rebuild.
2059
+ */
2060
+ _setExternalChannel(owner: Node, specs: ChannelSpec[] | null): void;
2061
+ /** Authored trenches plus the ones other nodes cut. */
2062
+ private allChannels;
1992
2063
  /** @internal The pure heightfield (lazily built — physics pulls this). */
1993
2064
  _heightmap(): Heightmap;
1994
2065
  /** The splat layers in effect — theme preset, or `layers` for 'custom'. */
@@ -2387,6 +2458,10 @@ declare class Water3D extends Node3D implements RenderHook3D, SunConsumer3D {
2387
2458
  private readonly _scratchA;
2388
2459
  private readonly _scratchB;
2389
2460
  private readonly _bodies;
2461
+ private readonly _cutouts;
2462
+ /** Scratch for the cutout mask pass (no per-frame allocation). */
2463
+ private readonly _maskMeshes;
2464
+ private readonly _maskSaved;
2390
2465
  /** Scratch for the side-pass cull (no per-frame allocation). */
2391
2466
  private readonly _passHidden;
2392
2467
  private cubeTarget;
@@ -2428,6 +2503,16 @@ declare class Water3D extends Node3D implements RenderHook3D, SunConsumer3D {
2428
2503
  /** The cheap lake ShaderMaterial — fresnel sky tint + ripple normals + sun
2429
2504
  * glint + edge fade, NO scene re-renders (see lake.ts). */
2430
2505
  private buildLakeMaterial;
2506
+ /**
2507
+ * Stamp every {@link WaterCutout3D} owner into the depth pre-pass.
2508
+ *
2509
+ * The cutout does not describe a hole in the water — it names a piece of
2510
+ * geometry that holds the water back, and this pass re-draws exactly that
2511
+ * geometry (clipped to the cutout box) at the near plane. The water's
2512
+ * occlusion discard does the rest. The tree is walked live, so a cutout on a
2513
+ * bobbing hull tracks it with no wiring.
2514
+ */
2515
+ private renderCutoutMasks;
2431
2516
  private syncFancy;
2432
2517
  /**
2433
2518
  * @internal Environment-sky sun hand-off (see {@link SunConsumer3D}): the
@@ -2454,6 +2539,59 @@ declare class Water3D extends Node3D implements RenderHook3D, SunConsumer3D {
2454
2539
  private waveOffsetAt;
2455
2540
  }
2456
2541
  //#endregion
2542
+ //#region src/3d/nodes/water-cutout-3d.d.ts
2543
+ /**
2544
+ * A box the water does not enter.
2545
+ *
2546
+ * Water is ONE surface: it has no idea a boat has an inside. Float an open hull
2547
+ * and every wave crest that rises above its floor renders as sea sloshing
2548
+ * through the deck — correct for a plane cutting an open box, wrong for a boat.
2549
+ * Freeboard alone cannot fix it (a big enough swell always wins), so the fix is
2550
+ * to name the geometry that holds the water back. `Water3D` re-draws that
2551
+ * geometry — the part of it inside this box — into its depth pre-pass at the
2552
+ * near plane, and its ordinary occlusion discard then removes the water for
2553
+ * exactly the pixels the hull already covers.
2554
+ *
2555
+ * Park it as a CHILD of the thing it belongs to — a hull, a diving bell, a
2556
+ * dock's well, a cave mouth — and it follows along, rotation included.
2557
+ *
2558
+ * SAFE BY CONSTRUCTION: nothing is ever cut out of the water. The mask is the
2559
+ * owner's own silhouette, so water can only be hidden behind geometry the
2560
+ * player is already looking at. Size the box to the interior and err large —
2561
+ * an overlarge box just means more of this hull holds water back; it can never
2562
+ * punch a rectangle of bare ground into open water. (Needs the depth pre-pass:
2563
+ * `Water3D` in `fancy` quality. Skinned and instanced meshes are re-drawn at
2564
+ * their rest pose, so hang cutouts on ordinary geometry.)
2565
+ *
2566
+ * ```json
2567
+ * { "name": "Rowboat", "type": "RigidBody3D", "children": [
2568
+ * { "name": "DryInside", "type": "WaterCutout3D",
2569
+ * "props": { "size": [3.0, 1.2, 1.1], "position": [0, 0.4, 0] } } ] }
2570
+ * ```
2571
+ *
2572
+ * Cheap: a handful of these cost a few ALU per water pixel. `Water3D` reads the
2573
+ * nearest {@link WATER_CUTOUT_MAX} of them.
2574
+ */
2575
+ declare class WaterCutout3D extends Node3D {
2576
+ static override readonly typeName: string;
2577
+ static override readonly props: PropSchema;
2578
+ /** Box extent in meters, centered on the node ([x, y, z]). */
2579
+ size: number[];
2580
+ /** Live toggle — a scuttled hull can start letting the sea in. */
2581
+ enabled: boolean;
2582
+ static validateJson(node: Node): void;
2583
+ /**
2584
+ * A cutout works by re-drawing its PARENT, so it has to have one. At the
2585
+ * scene root it would hand the whole world a licence to erase water — the
2586
+ * one failure this design exists to rule out — so that arrangement is a hard
2587
+ * load error, not a silent no-op. (Checked here rather than in
2588
+ * `validateJson`: the loader validates a node before attaching it.)
2589
+ */
2590
+ override onReady(): void;
2591
+ }
2592
+ /** How many cutouts one Water3D reads (the nearest ones win). */
2593
+ declare const WATER_CUTOUT_MAX = 4;
2594
+ //#endregion
2457
2595
  //#region src/3d/register.d.ts
2458
2596
  /**
2459
2597
  * Register the 3D node taxonomy (and the core nodes). Call once in your game
@@ -2761,4 +2899,4 @@ declare function createNavDebugNode(nav: TerrainNav, opts?: {
2761
2899
  name?: string;
2762
2900
  }): InstancedMesh3D;
2763
2901
  //#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 };
2902
+ 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-BFzBVqhr.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-DyAPwm0j.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-CmtGqGSS.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-DUAtTqID.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-DcHXS1MA.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-ob23XE0Y.js";
6
+ import { n as enablePhysics3D, t as Physics3D } from "./physics-3d-4mGzOZ2J.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,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-BFzBVqhr.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";
@@ -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-BFzBVqhr.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-CmtGqGSS.js";
9
- import { n as enablePhysics3D } from "./physics-3d-ob23XE0Y.js";
8
+ import { d as DirectionalLight3D, l as ModelInstance3D, t as registerNodes3D, x as Camera3D } from "./register-DcHXS1MA.js";
9
+ import { n as enablePhysics3D } from "./physics-3d-4mGzOZ2J.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";
package/dist/env.d.ts CHANGED
@@ -376,6 +376,12 @@ interface TerrainOptions {
376
376
  * theme ALWAYS ships its sea — without it the edge-wrap skirt is bare cliff.
377
377
  */
378
378
  water?: boolean;
379
+ /**
380
+ * Grid segments per side (default 128). Raise it when the map carries
381
+ * features finer than a cell — a 2 m creek on a 260 m terrain has to be cut
382
+ * into ~1 m cells (256) or the trench smooths away to nothing.
383
+ */
384
+ resolution?: number;
379
385
  }
380
386
  /**
381
387
  * The environment header that matches a generator theme — sky, horizon fog
package/dist/env.js CHANGED
@@ -1419,7 +1419,7 @@ const TERRAIN_GENERATOR_THEMES = [
1419
1419
  "volcanic"
1420
1420
  ];
1421
1421
  /** Terrain3D's default grid segments — the probe MUST match the runtime grid. */
1422
- const TERRAIN_RESOLUTION = 128;
1422
+ const DEFAULT_TERRAIN_RESOLUTION = 128;
1423
1423
  /** Terrain3D edge wrap: the rim drops this many meters before the −50 m skirt. */
1424
1424
  const EDGE_WRAP_DROP = 20;
1425
1425
  /** Sea margin above the highest rim cliff (> the ~0.52 m wave-trough dip). */
@@ -1676,6 +1676,7 @@ function generateTerrain(opts) {
1676
1676
  prop: "theme",
1677
1677
  validOptions: [...TERRAIN_GENERATOR_THEMES]
1678
1678
  });
1679
+ const TERRAIN_RESOLUTION = Math.max(2, Math.round(opts.resolution ?? DEFAULT_TERRAIN_RESOLUTION));
1679
1680
  let maxHeight = opts.maxHeight || spec.maxHeight;
1680
1681
  const rng = new Rng(seed);
1681
1682
  const isIsland = theme === "island";
@@ -1714,7 +1715,8 @@ function generateTerrain(opts) {
1714
1715
  seed,
1715
1716
  theme: spec.splat,
1716
1717
  ...spec.roughness !== void 0 ? { roughness: spec.roughness } : {},
1717
- ...spec.detail !== void 0 ? { detail: spec.detail } : {}
1718
+ ...spec.detail !== void 0 ? { detail: spec.detail } : {},
1719
+ ...TERRAIN_RESOLUTION !== DEFAULT_TERRAIN_RESOLUTION ? { resolution: TERRAIN_RESOLUTION } : {}
1718
1720
  }
1719
1721
  }]
1720
1722
  }];
@@ -1799,9 +1801,10 @@ function borderMaxHeight(hm) {
1799
1801
  let borderMax = Number.NEGATIVE_INFINITY;
1800
1802
  const hw = hm.width / 2;
1801
1803
  const hd = hm.depth / 2;
1802
- for (let i = 0; i <= TERRAIN_RESOLUTION; i++) {
1803
- const x = -hw + i / TERRAIN_RESOLUTION * hm.width;
1804
- const z = -hd + i / TERRAIN_RESOLUTION * hm.depth;
1804
+ const steps = hm.segsX;
1805
+ for (let i = 0; i <= steps; i++) {
1806
+ const x = -hw + i / steps * hm.width;
1807
+ const z = -hd + i / steps * hm.depth;
1805
1808
  borderMax = Math.max(borderMax, hm.baseHeight(x, -hd), hm.baseHeight(x, hd), hm.baseHeight(-hw, z), hm.baseHeight(hw, z));
1806
1809
  }
1807
1810
  return borderMax;
@@ -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,6 +3701,56 @@ 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. */
@@ -5640,4 +5867,4 @@ function registerGameplayBehaviors(opts) {
5640
5867
  for (const [name, ctor] of Object.entries(GAMEPLAY_BEHAVIORS)) registerBehavior(name, ctor, { replace });
5641
5868
  }
5642
5869
  //#endregion
5643
- 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-BFzBVqhr.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 };