incanto 0.13.0 → 0.15.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.
Files changed (29) hide show
  1. package/dist/3d.d.ts +40 -11
  2. package/dist/3d.js +3 -3
  3. package/dist/{create-game-J6g4QInv.js → create-game-BS-K-GY1.js} +2 -2
  4. package/dist/index.js +1 -1
  5. package/dist/{physics-3d-DhaAaKJo.js → physics-3d-TmOuCy7J.js} +1 -1
  6. package/dist/react.js +1 -1
  7. package/dist/{register-BFw4c83z.js → register-Bh_3Z39N.js} +164 -39
  8. package/dist/test.js +3 -3
  9. package/editor/assets/{agent8-BF8Jfbkl.js → agent8-DqQ2CQ5A.js} +1 -1
  10. package/editor/assets/{index-DSq0gdhE.js → index-CofH0yGY.js} +79 -7
  11. package/editor/index.html +1 -1
  12. package/package.json +1 -1
  13. package/schemas/scene.schema.json +19 -6
  14. package/skills/incanto-environment.md +22 -17
  15. package/skills/incanto-node-reference.md +7 -3
  16. package/templates-app/beacon-isle-3d/PROJECT/Status.md +1 -1
  17. package/templates-app/beacon-isle-3d/generate-world.ts +23 -20
  18. package/templates-app/beacon-isle-3d/package.json +1 -1
  19. package/templates-app/beacon-isle-3d/src/behaviors.ts +70 -4
  20. package/templates-app/beacon-isle-3d/src/game.scene.json +43 -67
  21. package/templates-app/tps-3d/package.json +1 -1
  22. package/templates-app/village-quest-3d/PROJECT/Context.md +1 -1
  23. package/templates-app/village-quest-3d/PROJECT/Requirements.md +1 -1
  24. package/templates-app/village-quest-3d/PROJECT/Structure.md +6 -1
  25. package/templates-app/village-quest-3d/generate-dressing.ts +769 -0
  26. package/templates-app/village-quest-3d/package.json +1 -1
  27. package/templates-app/village-quest-3d/src/behaviors.ts +74 -8
  28. package/templates-app/village-quest-3d/src/grove.scene.json +1 -13
  29. package/templates-app/village-quest-3d/src/village.scene.json +2989 -162
package/dist/3d.d.ts CHANGED
@@ -888,9 +888,13 @@ declare class Flowers3D extends Node3D {
888
888
  clustering: number;
889
889
  /** Gentle head-bob wind strength — 0 holds still. */
890
890
  sway: number;
891
- /** Drape each plant onto a Terrain3D's surface so flowers root on the ground
892
- * instead of a flat plane (lets ONE bed cover rolling terrain). */
893
- drape: boolean;
891
+ /**
892
+ * null (the default) is AUTO: whenever the scene has a Terrain3D, instances
893
+ * root on it — flat ground behaves as before, rolling terrain just works.
894
+ * true forces it (keeps retrying until a terrain appears), false disables
895
+ * draping entirely.
896
+ */
897
+ drape: boolean | null;
894
898
  /** Drape target: a node path to the Terrain3D. Empty = auto-find the first
895
899
  * Terrain3D in the tree (the zero-config default when `drape` is on). */
896
900
  terrain: string;
@@ -1025,9 +1029,14 @@ declare class Foliage3D extends Node3D implements SunConsumer3D {
1025
1029
  flowers: number;
1026
1030
  /** Placement seed — the field is identical across runs and machines. */
1027
1031
  seed: number;
1028
- /** Drape each instance onto a Terrain3D's surface so blades root on the
1029
- * ground instead of a flat plane (lets ONE field cover rolling terrain). */
1030
- drape: boolean;
1032
+ /**
1033
+ * Drape each instance onto a Terrain3D's surface so blades root on the
1034
+ * ground instead of a flat plane. null (the default) is AUTO: whenever the
1035
+ * scene has a Terrain3D, instances root on it — flat ground behaves as
1036
+ * before, rolling terrain just works. true forces it (keeps retrying until
1037
+ * a terrain appears), false disables draping entirely.
1038
+ */
1039
+ drape: boolean | null;
1031
1040
  /** Drape target: a node path to the Terrain3D. Empty = auto-find the first
1032
1041
  * Terrain3D in the tree (the zero-config default when `drape` is on). */
1033
1042
  terrain: string;
@@ -1061,6 +1070,8 @@ declare class Foliage3D extends Node3D implements SunConsumer3D {
1061
1070
  /** @internal Every InstancedMesh of the field: [quads] or [quads, quads90°]. */
1062
1071
  _fields(): InstancedMesh[];
1063
1072
  override update(dt: number): void;
1073
+ /** Wind time/strength + the ≤4 character-bend slots (no-op before first build). */
1074
+ private writeAnimationUniforms;
1064
1075
  override _syncObject3D(): void;
1065
1076
  /**
1066
1077
  * @internal Environment-sky sun hand-off (see {@link SunConsumer3D}): the
@@ -1821,11 +1832,14 @@ declare class Tree3D extends Node3D {
1821
1832
  /** Let leaves cast shadows. false keeps trunk/branch shadows but drops the
1822
1833
  * expensive alpha-cutout leaf shadow pass (the dappled floor) for big FPS. */
1823
1834
  leafShadows: boolean;
1824
- /** Drape the grove over a Terrain3D: each scattered instance roots at the
1825
- * ground height under it (not the node's single Y), so a patch on rolling or
1826
- * carved terrain never floats/buries. No effect on a count:1 tree placed by
1827
- * hand unless it sits off the ground. */
1828
- drape: boolean;
1835
+ /**
1836
+ * Drape the grove over a Terrain3D: each scattered instance roots at the
1837
+ * ground height under it (not the node's single Y), so a patch on rolling
1838
+ * or carved terrain never floats/buries. null (the default) is AUTO:
1839
+ * whenever the scene has a Terrain3D the grove drapes onto it. true forces
1840
+ * it (keeps retrying until a terrain appears), false disables draping.
1841
+ */
1842
+ drape: boolean | null;
1829
1843
  /** Node path to the Terrain3D to drape onto ('' = auto-find the first one). */
1830
1844
  terrain: string;
1831
1845
  /** Resolved drape target + the grove's world position, set per rebuild. */
@@ -2029,6 +2043,21 @@ declare class Water3D extends Node3D implements RenderHook3D, SunConsumer3D {
2029
2043
  * `{ color?, visibility? }` overrides the fog hue / view distance (meters).
2030
2044
  */
2031
2045
  underwater: boolean | JsonObject;
2046
+ /**
2047
+ * Directional traveling SWELL amplitude in meters (fancy only; 0 = off, the
2048
+ * legacy isotropic look). Three peaked wave trains march across the surface
2049
+ * at real deep-water speeds — THE open-ocean dial: 0.15 = lively lake chop,
2050
+ * 0.4 = coastal sea, 0.8 = heavy weather. Crests feed the color ramp,
2051
+ * `whitecaps` and the shore foam automatically.
2052
+ */
2053
+ swell: number;
2054
+ /** Compass direction the swell TRAVELS toward, degrees (0 = +z, 90 = +x). */
2055
+ swellDirectionDeg: number;
2056
+ /** Primary swell wavelength in meters (the two crossing trains derive). */
2057
+ swellWavelength: number;
2058
+ /** Open-water whitecap foam on the tallest crests, 0–1 (fancy only). Works
2059
+ * best with `swell` — the caps ride the swell's crest lines. */
2060
+ whitecaps: number;
2032
2061
  /** Loader hook: degenerate planes and bad enums fail at LOAD, not at render. */
2033
2062
  static validateJson(node: Node): void;
2034
2063
  private time;
package/dist/3d.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
2
- 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-J6g4QInv.js";
3
- import { A as Area3D, B as movementState, C as BoneAttachment3D, D as TERRAIN_THEMES, E as DEFAULT_TERRAIN_TEXTURE_BASE, F as Node3D, L as QUARTER_PITCH, M as PhysicsBody3D, N as RigidBody3D, O as terrainThemeLayers, P as StaticBody3D, R as cameraRelative, S as BoneLookAt3D, T as Terrain3D, V as rigPose, _ as Flowers3D, a as VoxelGrid3D, b as CharacterController3D, c as Particles3D, d as DirectionalLight3D, f as OmniLight3D, g as DENSITY_PRESETS, h as Foliage3D, i as VOXEL_PALETTE, j as CharacterBody3D, k as Joint3D, l as ModelInstance3D, m as MeshInstance3D, n as Water3D, o as Tree3D, p as InstancedMesh3D, r as WATER_MAX_RIPPLES, s as Trail3D, t as registerNodes3D, u as LoftMesh3D, v as resolveFlowerDensity, w as Billboard3D, x as Camera3D, y as FLOWER_VARIETIES, z as keyboardIntensity } from "./register-BFw4c83z.js";
2
+ 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-BS-K-GY1.js";
3
+ import { A as Area3D, B as movementState, C as BoneAttachment3D, D as TERRAIN_THEMES, E as DEFAULT_TERRAIN_TEXTURE_BASE, F as Node3D, L as QUARTER_PITCH, M as PhysicsBody3D, N as RigidBody3D, O as terrainThemeLayers, P as StaticBody3D, R as cameraRelative, S as BoneLookAt3D, T as Terrain3D, V as rigPose, _ as Flowers3D, a as VoxelGrid3D, b as CharacterController3D, c as Particles3D, d as DirectionalLight3D, f as OmniLight3D, g as DENSITY_PRESETS, h as Foliage3D, i as VOXEL_PALETTE, j as CharacterBody3D, k as Joint3D, l as ModelInstance3D, m as MeshInstance3D, n as Water3D, o as Tree3D, p as InstancedMesh3D, r as WATER_MAX_RIPPLES, s as Trail3D, t as registerNodes3D, u as LoftMesh3D, v as resolveFlowerDensity, w as Billboard3D, x as Camera3D, y as FLOWER_VARIETIES, z as keyboardIntensity } from "./register-Bh_3Z39N.js";
4
4
  import { n as splatWeights, t as buildHeightmap } from "./heightmap-CroQPEER.js";
5
- import { n as enablePhysics3D, t as Physics3D } from "./physics-3d-DhaAaKJo.js";
5
+ import { n as enablePhysics3D, t as Physics3D } from "./physics-3d-TmOuCy7J.js";
6
6
  //#region src/3d/environment-runtime.ts
7
7
  /**
8
8
  * Live environment editing — the renderer re-applies `scene.environment`
@@ -5,8 +5,8 @@ import { t as IncantoError } from "./errors-BpWbnbb_.js";
5
5
  import { i as resolveRendering, n as attachTouchControls } from "./touch-031PxtCR.js";
6
6
  import { n as registerGameplayBehaviors } from "./gameplay-sSqrUcnz.js";
7
7
  import { t as debugSources } from "./debug-draw-CZmOYjL2.js";
8
- import { F as Node3D, M as PhysicsBody3D, d as DirectionalLight3D, l as ModelInstance3D, t as registerNodes3D, x as Camera3D } from "./register-BFw4c83z.js";
9
- import { n as enablePhysics3D } from "./physics-3d-DhaAaKJo.js";
8
+ import { F as Node3D, M as PhysicsBody3D, d as DirectionalLight3D, l as ModelInstance3D, t as registerNodes3D, x as Camera3D } from "./register-Bh_3Z39N.js";
9
+ import { n as enablePhysics3D } from "./physics-3d-TmOuCy7J.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/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.13.0";
298
+ const VERSION = "0.15.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,7 +1,7 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
2
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
3
3
  import { n as registerDebugSource } from "./debug-draw-CZmOYjL2.js";
4
- import { A as Area3D, F as Node3D, I as validateCollider3D, M as PhysicsBody3D, N as RigidBody3D, T as Terrain3D, j as CharacterBody3D, k as Joint3D } from "./register-BFw4c83z.js";
4
+ import { A as Area3D, F as Node3D, I as validateCollider3D, M as PhysicsBody3D, N as RigidBody3D, T as Terrain3D, j as CharacterBody3D, k as Joint3D } from "./register-Bh_3Z39N.js";
5
5
  import { Euler, Quaternion } from "three";
6
6
  //#region src/3d/physics/physics-3d.ts
7
7
  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-J6g4QInv.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-Ct86zczq.js").then((n) => n.n)).createGame2D(o)))(opts);
159
+ const next = await (_gameFactory ?? (mode === "3d" ? async (o) => (await import("./create-game-BS-K-GY1.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-Ct86zczq.js").then((n) => n.n)).createGame2D(o)))(opts);
160
160
  if (disposed) {
161
161
  next.dispose();
162
162
  return;
@@ -3066,7 +3066,7 @@ var Flowers3D = class extends Node3D {
3066
3066
  height: { default: .45 },
3067
3067
  clustering: { default: .6 },
3068
3068
  sway: { default: .5 },
3069
- drape: { default: false },
3069
+ drape: { default: null },
3070
3070
  terrain: { default: "" }
3071
3071
  };
3072
3072
  /** 'lush' | 'sparse' | 'none' | plants per m² (load-time check + budget). */
@@ -3087,9 +3087,13 @@ var Flowers3D = class extends Node3D {
3087
3087
  clustering = .6;
3088
3088
  /** Gentle head-bob wind strength — 0 holds still. */
3089
3089
  sway = .5;
3090
- /** Drape each plant onto a Terrain3D's surface so flowers root on the ground
3091
- * instead of a flat plane (lets ONE bed cover rolling terrain). */
3092
- drape = false;
3090
+ /**
3091
+ * null (the default) is AUTO: whenever the scene has a Terrain3D, instances
3092
+ * root on it — flat ground behaves as before, rolling terrain just works.
3093
+ * true forces it (keeps retrying until a terrain appears), false disables
3094
+ * draping entirely.
3095
+ */
3096
+ drape = null;
3093
3097
  /** Drape target: a node path to the Terrain3D. Empty = auto-find the first
3094
3098
  * Terrain3D in the tree (the zero-config default when `drape` is on). */
3095
3099
  terrain = "";
@@ -3119,7 +3123,7 @@ var Flowers3D = class extends Node3D {
3119
3123
  if (!(f.height > 0)) throw new IncantoError("BAD_FORMAT", `Flowers3D '${node.name}' height must be > 0 meters, got ${f.height}.`, { prop: "height" });
3120
3124
  if (!(f.clustering >= 0) || !(f.clustering <= 1)) throw new IncantoError("BAD_FORMAT", `Flowers3D '${node.name}' clustering must be in [0, 1] (0 uniform … 1 tight patches), got ${f.clustering}.`, { prop: "clustering" });
3121
3125
  if (!(f.sway >= 0)) throw new IncantoError("BAD_FORMAT", `Flowers3D '${node.name}' sway must be >= 0, got ${f.sway}.`, { prop: "sway" });
3122
- if (typeof f.drape !== "boolean") throw new IncantoError("BAD_FORMAT", `Flowers3D '${node.name}' drape must be a boolean, got ${JSON.stringify(f.drape)}.`, { prop: "drape" });
3126
+ if (f.drape !== null && typeof f.drape !== "boolean") throw new IncantoError("BAD_FORMAT", `Flowers3D '${node.name}' drape must be true, false or null (null = auto: drape whenever the scene has a Terrain3D), got ${JSON.stringify(f.drape)}.`, { prop: "drape" });
3123
3127
  if (typeof f.terrain !== "string") throw new IncantoError("BAD_FORMAT", `Flowers3D '${node.name}' terrain must be a node-path string (empty = auto-find), got ${JSON.stringify(f.terrain)}.`, { prop: "terrain" });
3124
3128
  }
3125
3129
  time = 0;
@@ -3172,7 +3176,7 @@ var Flowers3D = class extends Node3D {
3172
3176
  /** Resolve the drape target + cache the bed's world position for `bedY`. */
3173
3177
  resolveDrape() {
3174
3178
  this.drapeTerrain = null;
3175
- if (!this.drape) return;
3179
+ if (this.drape === false) return;
3176
3180
  const terrain = findDrapeTerrain(this, this.terrain);
3177
3181
  if (terrain) {
3178
3182
  this.drapeTerrain = terrain;
@@ -4449,7 +4453,7 @@ var Foliage3D = class Foliage3D extends Node3D {
4449
4453
  coverage: { default: .85 },
4450
4454
  flowers: { default: 0 },
4451
4455
  seed: { default: 1 },
4452
- drape: { default: false },
4456
+ drape: { default: null },
4453
4457
  terrain: { default: "" },
4454
4458
  renderOrder: { default: 2 }
4455
4459
  };
@@ -4508,9 +4512,14 @@ var Foliage3D = class Foliage3D extends Node3D {
4508
4512
  flowers = 0;
4509
4513
  /** Placement seed — the field is identical across runs and machines. */
4510
4514
  seed = 1;
4511
- /** Drape each instance onto a Terrain3D's surface so blades root on the
4512
- * ground instead of a flat plane (lets ONE field cover rolling terrain). */
4513
- drape = false;
4515
+ /**
4516
+ * Drape each instance onto a Terrain3D's surface so blades root on the
4517
+ * ground instead of a flat plane. null (the default) is AUTO: whenever the
4518
+ * scene has a Terrain3D, instances root on it — flat ground behaves as
4519
+ * before, rolling terrain just works. true forces it (keeps retrying until
4520
+ * a terrain appears), false disables draping entirely.
4521
+ */
4522
+ drape = null;
4514
4523
  /** Drape target: a node path to the Terrain3D. Empty = auto-find the first
4515
4524
  * Terrain3D in the tree (the zero-config default when `drape` is on). */
4516
4525
  terrain = "";
@@ -4544,7 +4553,7 @@ var Foliage3D = class Foliage3D extends Node3D {
4544
4553
  if (!Array.isArray(sun) || sun.length !== 3 || !sun.every((v) => Number.isFinite(v)) || Math.hypot(sun[0] ?? 0, sun[1] ?? 0, sun[2] ?? 0) === 0) throw new IncantoError("BAD_FORMAT", `Foliage3D '${node.name}' sunDirection must be a non-zero [x, y, z] vector, got ${JSON.stringify(sun)}.`, { prop: "sunDirection" });
4545
4554
  if (!(f.coverage > 0) || !(f.coverage <= 1)) throw new IncantoError("BAD_FORMAT", `Foliage3D '${node.name}' coverage must be in (0, 1] (carpet fill — 1 is solid, lower carves dirt patches), got ${f.coverage}.`, { prop: "coverage" });
4546
4555
  if (!(f.flowers >= 0) || !(f.flowers <= .2)) throw new IncantoError("BAD_FORMAT", `Foliage3D '${node.name}' flowers must be in [0, 0.2] (fraction of instances rendered as flower heads — for a whole flower FIELD use kind: 'flowers'), got ${f.flowers}.`, { prop: "flowers" });
4547
- if (typeof f.drape !== "boolean") throw new IncantoError("BAD_FORMAT", `Foliage3D '${node.name}' drape must be a boolean, got ${JSON.stringify(f.drape)}.`, { prop: "drape" });
4556
+ if (f.drape !== null && typeof f.drape !== "boolean") throw new IncantoError("BAD_FORMAT", `Foliage3D '${node.name}' drape must be true, false or null (null = auto: drape whenever the scene has a Terrain3D), got ${JSON.stringify(f.drape)}.`, { prop: "drape" });
4548
4557
  if (typeof f.terrain !== "string") throw new IncantoError("BAD_FORMAT", `Foliage3D '${node.name}' terrain must be a node-path string (empty = auto-find), got ${JSON.stringify(f.terrain)}.`, { prop: "terrain" });
4549
4558
  }
4550
4559
  time = 0;
@@ -4590,16 +4599,33 @@ var Foliage3D = class Foliage3D extends Node3D {
4590
4599
  update(dt) {
4591
4600
  this.time += dt;
4592
4601
  this.pickedBenders = this.computeBenders();
4602
+ this.writeAnimationUniforms();
4603
+ }
4604
+ /** Wind time/strength + the ≤4 character-bend slots (no-op before first build). */
4605
+ writeAnimationUniforms() {
4606
+ if (this.isShaderField && this.bladesMaterial) {
4607
+ const u = this.bladesMaterial.uniforms;
4608
+ if (u.time) u.time.value = this.time;
4609
+ if (u.windStrength) u.windStrength.value = this.sway * (this.isMesh ? MESH_SWAY_TO_WIND : SWAY_TO_WIND);
4610
+ const slots = this.benderSlots;
4611
+ if (slots) for (let i = 0; i < 4; i++) {
4612
+ const b = this.pickedBenders[i];
4613
+ if (b) slots[i]?.set(b.x, b.y, b.z, b.radius);
4614
+ else slots[i]?.set(0, 0, 0, 0);
4615
+ }
4616
+ } else if (this.isTufts) {
4617
+ if (this.tuftTime) this.tuftTime.value = this.time;
4618
+ if (this.tuftStrength) this.tuftStrength.value = this.sway * TUFT_SWAY_TO_WIND;
4619
+ }
4593
4620
  }
4594
4621
  _syncObject3D() {
4595
4622
  super._syncObject3D();
4596
4623
  this.rebuildIfNeeded();
4597
4624
  const pivot = this.pivot;
4625
+ this.writeAnimationUniforms();
4598
4626
  if (this.isShaderField) {
4599
4627
  pivot.matrix.identity();
4600
4628
  const u = this.bladesMaterial.uniforms;
4601
- if (u.time) u.time.value = this.time;
4602
- if (u.windStrength) u.windStrength.value = this.sway * (this.isMesh ? MESH_SWAY_TO_WIND : SWAY_TO_WIND);
4603
4629
  (u.bottomColor?.value).set(this.colorA);
4604
4630
  (u.topColor?.value).set(this.colorB);
4605
4631
  if (this.isMesh) {
@@ -4608,17 +4634,8 @@ var Foliage3D = class Foliage3D extends Node3D {
4608
4634
  if (u.fadeStart) u.fadeStart.value = this.fadeStart;
4609
4635
  if (u.fadeEnd) u.fadeEnd.value = this.fadeEnd;
4610
4636
  }
4611
- const slots = this.benderSlots;
4612
- for (let i = 0; i < 4; i++) {
4613
- const b = this.pickedBenders[i];
4614
- if (b) slots[i]?.set(b.x, b.y, b.z, b.radius);
4615
- else slots[i]?.set(0, 0, 0, 0);
4616
- }
4617
- } else if (this.isTufts) {
4618
- pivot.matrix.identity();
4619
- if (this.tuftTime) this.tuftTime.value = this.time;
4620
- if (this.tuftStrength) this.tuftStrength.value = this.sway * TUFT_SWAY_TO_WIND;
4621
- } else if (this.sway > 0) {
4637
+ } else if (this.isTufts) pivot.matrix.identity();
4638
+ else if (this.sway > 0) {
4622
4639
  const leanX = Math.sin(this.time * 1.7) * SWAY_LEAN * this.sway;
4623
4640
  const leanZ = Math.sin(this.time * 1.3 + 1.7) * SWAY_LEAN * this.sway * .6;
4624
4641
  pivot.matrix.makeShear(0, 0, leanX, leanZ, 0, 0);
@@ -4645,7 +4662,7 @@ var Foliage3D = class Foliage3D extends Node3D {
4645
4662
  computeBenders() {
4646
4663
  if (!this.interaction || !this.isShaderField) return [];
4647
4664
  const [fx, fy, fz] = worldXZY(this);
4648
- if (this.drape && !this.drapeTerrain) this.resolveDrape();
4665
+ if (this.drape === true && !this.drapeTerrain) this.resolveDrape();
4649
4666
  const terrain = this.drapeTerrain;
4650
4667
  const candidates = [];
4651
4668
  for (const b of movingBodiesFor(this)) {
@@ -4706,7 +4723,7 @@ var Foliage3D = class Foliage3D extends Node3D {
4706
4723
  /** Resolve the drape target + cache the field's world position for `bedY`. */
4707
4724
  resolveDrape() {
4708
4725
  this.drapeTerrain = null;
4709
- if (!this.drape) return;
4726
+ if (this.drape === false) return;
4710
4727
  const terrain = findDrapeTerrain(this, this.terrain);
4711
4728
  if (terrain) {
4712
4729
  this.drapeTerrain = terrain;
@@ -8411,7 +8428,7 @@ var Tree3D = class Tree3D extends Node3D {
8411
8428
  leafFadeStart: { default: 0 },
8412
8429
  leafFadeEnd: { default: 0 },
8413
8430
  leafShadows: { default: true },
8414
- drape: { default: false },
8431
+ drape: { default: null },
8415
8432
  terrain: { default: "" }
8416
8433
  };
8417
8434
  /** 'simple' (primitive low-poly) | 'medium' (light ez-trees) | 'high' (full). */
@@ -8440,11 +8457,14 @@ var Tree3D = class Tree3D extends Node3D {
8440
8457
  /** Let leaves cast shadows. false keeps trunk/branch shadows but drops the
8441
8458
  * expensive alpha-cutout leaf shadow pass (the dappled floor) for big FPS. */
8442
8459
  leafShadows = true;
8443
- /** Drape the grove over a Terrain3D: each scattered instance roots at the
8444
- * ground height under it (not the node's single Y), so a patch on rolling or
8445
- * carved terrain never floats/buries. No effect on a count:1 tree placed by
8446
- * hand unless it sits off the ground. */
8447
- drape = false;
8460
+ /**
8461
+ * Drape the grove over a Terrain3D: each scattered instance roots at the
8462
+ * ground height under it (not the node's single Y), so a patch on rolling
8463
+ * or carved terrain never floats/buries. null (the default) is AUTO:
8464
+ * whenever the scene has a Terrain3D the grove drapes onto it. true forces
8465
+ * it (keeps retrying until a terrain appears), false disables draping.
8466
+ */
8467
+ drape = null;
8448
8468
  /** Node path to the Terrain3D to drape onto ('' = auto-find the first one). */
8449
8469
  terrain = "";
8450
8470
  /** Resolved drape target + the grove's world position, set per rebuild. */
@@ -8463,7 +8483,7 @@ var Tree3D = class Tree3D extends Node3D {
8463
8483
  if (!((t.area[0] ?? 0) > 0) || !((t.area[1] ?? 0) > 0)) throw new IncantoError("BAD_FORMAT", `Tree3D '${node.name}' area must be positive [x, z] meters, got ${JSON.stringify(t.area)}.`, { prop: "area" });
8464
8484
  if (t.leafFadeEnd !== 0 && !(t.leafFadeEnd > t.leafFadeStart && t.leafFadeStart >= 0)) throw new IncantoError("BAD_FORMAT", `Tree3D '${node.name}' needs leafFadeEnd > leafFadeStart >= 0 (the leaf LOD window) or 0/0 to disable, got start ${t.leafFadeStart}, end ${t.leafFadeEnd}.`, { prop: "leafFadeStart" });
8465
8485
  if (typeof t.leafShadows !== "boolean") throw new IncantoError("BAD_FORMAT", `Tree3D '${node.name}' leafShadows must be a boolean, got ${JSON.stringify(t.leafShadows)}.`, { prop: "leafShadows" });
8466
- if (typeof t.drape !== "boolean") throw new IncantoError("BAD_FORMAT", `Tree3D '${node.name}' drape must be a boolean, got ${JSON.stringify(t.drape)}.`, { prop: "drape" });
8486
+ if (t.drape !== null && typeof t.drape !== "boolean") throw new IncantoError("BAD_FORMAT", `Tree3D '${node.name}' drape must be true, false or null (null = auto: drape whenever the scene has a Terrain3D), got ${JSON.stringify(t.drape)}.`, { prop: "drape" });
8467
8487
  if (typeof t.terrain !== "string") throw new IncantoError("BAD_FORMAT", `Tree3D '${node.name}' terrain must be a node-path string (empty = auto-find), got ${JSON.stringify(t.terrain)}.`, { prop: "terrain" });
8468
8488
  if (t.tier === "simple") return;
8469
8489
  const perTree = maxVariantTriangles(t.tier, t.type);
@@ -8540,7 +8560,7 @@ var Tree3D = class Tree3D extends Node3D {
8540
8560
  /** Resolve the drape target + cache the grove's world position for `bedY`. */
8541
8561
  resolveDrape() {
8542
8562
  this.drapeTerrain = null;
8543
- if (!this.drape) return;
8563
+ if (this.drape === false) return;
8544
8564
  const terrain = findDrapeTerrain(this, this.terrain);
8545
8565
  if (terrain) {
8546
8566
  this.drapeTerrain = terrain;
@@ -10324,6 +10344,11 @@ uniform vec3 uScale;
10324
10344
  // incanto: world-anchor + interaction ripples (x, z, age, amplitude)
10325
10345
  uniform vec3 uWaterCenter;
10326
10346
  uniform vec4 uRipples[8];
10347
+ // incanto swell: directional traveling wave trains (0 = off — legacy look).
10348
+ // uSwellDir is a unit XZ vector; amplitude in meters; wavelength in meters.
10349
+ uniform float uSwellAmplitude;
10350
+ uniform vec2 uSwellDir;
10351
+ uniform float uSwellWavelength;
10327
10352
 
10328
10353
  varying vec3 vNormal;
10329
10354
  varying vec3 vWorldPosition;
@@ -10406,10 +10431,34 @@ float getRippleHeight(vec2 p) {
10406
10431
  return h;
10407
10432
  }
10408
10433
 
10409
- // incanto: waves + ripples used for displacement AND the normal samples
10410
- // so interaction rings catch the light like real waves.
10434
+ // incanto swell: one peaked traveling wave train. Deep-water dispersion
10435
+ // (c = sqrt(g/k)) gives each wavelength its REAL speed, so short chop
10436
+ // overtakes long swell exactly like the open sea. The pow-shaped profile
10437
+ // sharpens crests and flattens troughs (a height-only Gerstner stand-in —
10438
+ // no XZ pinch, so the analytic finite-difference normals stay exact).
10439
+ float swellWave(vec2 p, vec2 dir, float lambda, float amp, float sharp) {
10440
+ float k = 6.2831853 / max(lambda, 1.0);
10441
+ float c = sqrt(9.8 / k);
10442
+ float s = 0.5 + 0.5 * sin(dot(p, dir) * k - uTime * c * k);
10443
+ return amp * (pow(s, sharp) * 2.0 - 1.0);
10444
+ }
10445
+
10446
+ // incanto swell: three trains — the primary + two shorter crossing sets
10447
+ // (+25° / −19°) so crest lines stay broken and organic, never a zebra field.
10448
+ float getSwell(vec2 p) {
10449
+ if (uSwellAmplitude <= 0.0) return 0.0;
10450
+ vec2 d1 = uSwellDir;
10451
+ vec2 d2 = normalize(vec2(d1.x * 0.906 - d1.y * 0.423, d1.x * 0.423 + d1.y * 0.906));
10452
+ vec2 d3 = normalize(vec2(d1.x * 0.946 + d1.y * 0.326, -d1.x * 0.326 + d1.y * 0.946));
10453
+ return swellWave(p, d1, uSwellWavelength, uSwellAmplitude * 0.60, 2.6)
10454
+ + swellWave(p, d2, uSwellWavelength * 0.53, uSwellAmplitude * 0.27, 2.2)
10455
+ + swellWave(p, d3, uSwellWavelength * 0.31, uSwellAmplitude * 0.16, 1.8);
10456
+ }
10457
+
10458
+ // incanto: waves + swell + ripples — used for displacement AND the normal
10459
+ // samples so every layer catches the light like real waves.
10411
10460
  float getHeight(float x, float z) {
10412
- return getElevation(x, z) + getRippleHeight(vec2(x, z));
10461
+ return getElevation(x, z) + getSwell(vec2(x, z)) + getRippleHeight(vec2(x, z));
10413
10462
  }
10414
10463
 
10415
10464
  void main() {
@@ -10518,6 +10567,15 @@ uniform vec4 uRipples[8];
10518
10567
  uniform float uRippleFoam[8];
10519
10568
  uniform float uSplashFoam;
10520
10569
 
10570
+ // incanto swell: open-water WHITECAPS — foam breaking on the highest crests,
10571
+ // independent of the shoreline depth mask. uWaveEnvelope is the JS-computed
10572
+ // total wave amplitude (FBM + swell) so the crest mask normalizes by the
10573
+ // actual sea state, not the surface's extent. uSwellAmplitude gates the
10574
+ // swell-only color work (crest banding + wave-face shading).
10575
+ uniform float uWhitecaps;
10576
+ uniform float uWaveEnvelope;
10577
+ uniform float uSwellAmplitude;
10578
+
10521
10579
  // 🎨 Contact surface color customization uniforms
10522
10580
  uniform vec3 uEdgeColor;
10523
10581
  uniform float uEdgeIntensity;
@@ -10904,6 +10962,24 @@ void main() {
10904
10962
  // Mix between surface and peak colors based on peak transition
10905
10963
  vec3 mixedColor2 = mix(mixedColor1, uPeakColor, peakFactor);
10906
10964
 
10965
+ // incanto swell: the legacy ramp normalizes elevation by the surface's
10966
+ // HALF-EXTENT — on a real ocean plane that's hundreds of meters, so the
10967
+ // ramp never sees a wave. When swell is on, re-read the crest height
10968
+ // against the actual sea state (uWaveEnvelope) and (a) lighten crest tops
10969
+ // toward the peak color, (b) shade the wave FACES with a gentle N·L off
10970
+ // the distance-smoothed geometry normal — the lit/shadow lanes are what
10971
+ // make open-ocean swell READ at any distance. Both gated: swell 0 keeps
10972
+ // the legacy look byte-identical.
10973
+ if (uSwellAmplitude > 0.0) {
10974
+ float crestBand = smoothstep(0.3, 0.9,
10975
+ (vWorldPosition.y - uWaterCenter.y) / max(uWaveEnvelope, 0.001));
10976
+ mixedColor2 = mix(mixedColor2, uPeakColor, crestBand * 0.45);
10977
+ float faceShade = clamp(dot(normalize(mix(vec3(0.0, 1.0, 0.0), normalize(vNormal),
10978
+ 1.0 - 0.9 * smoothstep(150.0, 600.0, length(vWorldPosition - cameraPosition)))),
10979
+ normalize(uSunDirection)), 0.0, 1.0);
10980
+ mixedColor2 *= 0.80 + 0.32 * faceShade;
10981
+ }
10982
+
10907
10983
  // incanto v2: Beer's-law depth absorption + screen-space refraction.
10908
10984
  // Shallow water transmits the (refracted) scene almost untouched; depth
10909
10985
  // exponentially absorbs it into the ramp color — clear turquoise edges,
@@ -11040,6 +11116,22 @@ void main() {
11040
11116
  if (uEnableFoam > 0.5) {
11041
11117
  foamAmount = calculateFoam();
11042
11118
  }
11119
+ // incanto swell: whitecaps — foam eats the TOPS of the tallest crests in
11120
+ // open water. The crest mask normalizes elevation by the real wave envelope
11121
+ // (uScale.y is the surface's half-extent — useless as a sea-state yardstick
11122
+ // on a big ocean plane), and a two-octave noise breaks the caps into
11123
+ // patches that live and die with the crests. Distance-faded: far whitecaps
11124
+ // subtend under a pixel and would alias into horizon speckle.
11125
+ if (uWhitecaps > 0.0 && uWaveEnvelope > 0.001) {
11126
+ float crestN = (vWorldPosition.y - uWaterCenter.y) / uWaveEnvelope;
11127
+ float cap = smoothstep(0.5, 0.85, crestN);
11128
+ vec2 capUV = vWorldPosition.xz * 0.3;
11129
+ float capNoise = noise(capUV * 2.2 + uTime * vec2(0.10, 0.06)) * 0.65
11130
+ + noise(capUV * 6.5 - uTime * vec2(0.05, 0.14)) * 0.35;
11131
+ float capFade = 1.0 - smoothstep(220.0, 640.0, camDist);
11132
+ float whitecap = uWhitecaps * cap * smoothstep(0.42, 0.78, capNoise) * capFade;
11133
+ foamAmount = max(foamAmount, whitecap);
11134
+ }
11043
11135
  // incanto: splash/wake whitewater rides the ripple buffer, INDEPENDENT of the
11044
11136
  // depth-foam gate so it works even on shallow ponds / before the first pre-pass
11045
11137
  float splashFoam = rippleFoam(vWorldPosition.xz);
@@ -11449,7 +11541,11 @@ var Water3D = class Water3D extends Node3D {
11449
11541
  detailStrength: { default: .26 },
11450
11542
  absorption: { default: .15 },
11451
11543
  refraction: { default: true },
11452
- underwater: { default: true }
11544
+ underwater: { default: true },
11545
+ swell: { default: 0 },
11546
+ swellDirectionDeg: { default: 0 },
11547
+ swellWavelength: { default: 30 },
11548
+ whitecaps: { default: 0 }
11453
11549
  };
11454
11550
  /** [width, depth] in meters (the surface lies on XZ). */
11455
11551
  size = [40, 40];
@@ -11504,6 +11600,21 @@ var Water3D = class Water3D extends Node3D {
11504
11600
  * `{ color?, visibility? }` overrides the fog hue / view distance (meters).
11505
11601
  */
11506
11602
  underwater = true;
11603
+ /**
11604
+ * Directional traveling SWELL amplitude in meters (fancy only; 0 = off, the
11605
+ * legacy isotropic look). Three peaked wave trains march across the surface
11606
+ * at real deep-water speeds — THE open-ocean dial: 0.15 = lively lake chop,
11607
+ * 0.4 = coastal sea, 0.8 = heavy weather. Crests feed the color ramp,
11608
+ * `whitecaps` and the shore foam automatically.
11609
+ */
11610
+ swell = 0;
11611
+ /** Compass direction the swell TRAVELS toward, degrees (0 = +z, 90 = +x). */
11612
+ swellDirectionDeg = 0;
11613
+ /** Primary swell wavelength in meters (the two crossing trains derive). */
11614
+ swellWavelength = 30;
11615
+ /** Open-water whitecap foam on the tallest crests, 0–1 (fancy only). Works
11616
+ * best with `swell` — the caps ride the swell's crest lines. */
11617
+ whitecaps = 0;
11507
11618
  /** Loader hook: degenerate planes and bad enums fail at LOAD, not at render. */
11508
11619
  static validateJson(node) {
11509
11620
  const w = node;
@@ -11525,6 +11636,9 @@ var Water3D = class Water3D extends Node3D {
11525
11636
  if (!(w.sunIntensity >= 0)) throw new IncantoError("BAD_FORMAT", `Water3D '${node.name}' sunIntensity must be >= 0, got ${w.sunIntensity}.`, { prop: "sunIntensity" });
11526
11637
  if (!(w.detailStrength >= 0)) throw new IncantoError("BAD_FORMAT", `Water3D '${node.name}' detailStrength must be >= 0, got ${w.detailStrength}.`, { prop: "detailStrength" });
11527
11638
  if (!(w.absorption >= 0)) throw new IncantoError("BAD_FORMAT", `Water3D '${node.name}' absorption must be >= 0 (per-meter Beer's-law constant), got ${w.absorption}.`, { prop: "absorption" });
11639
+ if (!(w.swell >= 0) || !Number.isFinite(w.swell)) throw new IncantoError("BAD_FORMAT", `Water3D '${node.name}' swell must be >= 0 meters (0 = off), got ${JSON.stringify(w.swell)}.`, { prop: "swell" });
11640
+ if (!(w.swellWavelength >= 2)) throw new IncantoError("BAD_FORMAT", `Water3D '${node.name}' swellWavelength must be >= 2 meters, got ${JSON.stringify(w.swellWavelength)}.`, { prop: "swellWavelength" });
11641
+ if (!(w.whitecaps >= 0) || !(w.whitecaps <= 1)) throw new IncantoError("BAD_FORMAT", `Water3D '${node.name}' whitecaps must be in [0, 1], got ${JSON.stringify(w.whitecaps)}.`, { prop: "whitecaps" });
11528
11642
  const uw = w.underwater;
11529
11643
  if (typeof uw !== "boolean") {
11530
11644
  if (typeof uw !== "object" || uw === null || Array.isArray(uw)) throw new IncantoError("BAD_FORMAT", `Water3D '${node.name}' underwater must be true, false or an object ({ color?, visibility? }), got ${JSON.stringify(uw)}.`, { prop: "underwater" });
@@ -11778,7 +11892,12 @@ var Water3D = class Water3D extends Node3D {
11778
11892
  uCameraFar: { value: 1e3 },
11779
11893
  uEdgeColor: { value: new Color(FOAM_LOOK.edgeColor) },
11780
11894
  uEdgeIntensity: { value: FOAM_LOOK.edgeIntensity },
11781
- uEdgeWidth: { value: FOAM_LOOK.edgeWidth }
11895
+ uEdgeWidth: { value: FOAM_LOOK.edgeWidth },
11896
+ uSwellAmplitude: { value: this.swell },
11897
+ uSwellDir: { value: new Vector2(0, 1) },
11898
+ uSwellWavelength: { value: this.swellWavelength },
11899
+ uWhitecaps: { value: this.whitecaps },
11900
+ uWaveEnvelope: { value: 0 }
11782
11901
  },
11783
11902
  transparent: true,
11784
11903
  depthTest: true,
@@ -11840,6 +11959,12 @@ var Water3D = class Water3D extends Node3D {
11840
11959
  if (u.uSunIntensity) u.uSunIntensity.value = this.sunIntensity;
11841
11960
  if (u.uDetailStrength) u.uDetailStrength.value = this.detailStrength;
11842
11961
  if (u.uAbsorption) u.uAbsorption.value = this.absorption;
11962
+ if (u.uSwellAmplitude) u.uSwellAmplitude.value = this.swell;
11963
+ if (u.uSwellWavelength) u.uSwellWavelength.value = this.swellWavelength;
11964
+ if (u.uWhitecaps) u.uWhitecaps.value = this.whitecaps;
11965
+ const swellRad = this.swellDirectionDeg * Math.PI / 180;
11966
+ (u.uSwellDir?.value)?.set(Math.sin(swellRad), Math.cos(swellRad));
11967
+ if (u.uWaveEnvelope) u.uWaveEnvelope.value = this.waveHeight * AMPLITUDE_PER_WAVE_HEIGHT * 1.5 + this.swell * 1.03;
11843
11968
  if (!this.reflection && u.uUseEnvironmentMap) u.uUseEnvironmentMap.value = 0;
11844
11969
  if (!this.foam && u.uEnableFoam) u.uEnableFoam.value = 0;
11845
11970
  if (!this.refraction && u.uUseRefraction) u.uUseRefraction.value = 0;
package/dist/test.js CHANGED
@@ -6,7 +6,7 @@ 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
7
  import { n as registerGameplayBehaviors } from "./gameplay-sSqrUcnz.js";
8
8
  import { t as registerNodes2D } from "./register-Djwckzgx.js";
9
- import { t as registerNodes3D } from "./register-BFw4c83z.js";
9
+ import { t as registerNodes3D } from "./register-Bh_3Z39N.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-BiIdl51r.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-DhaAaKJo.js").then((n) => n.r);
130
+ const { enablePhysics3D } = await import("./physics-3d-TmOuCy7J.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-BiIdl51r.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-DhaAaKJo.js").then((n) => n.r);
235
+ const { enablePhysics3D } = await import("./physics-3d-TmOuCy7J.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-DSq0gdhE.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-CofH0yGY.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};