incanto 0.27.0 → 0.28.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 +1 -1
- package/dist/3d.d.ts +177 -135
- package/dist/3d.js +5 -5
- package/dist/{create-game-C0bPoURs.js → create-game-71TJjW1T.js} +1 -1
- package/dist/{create-game-DUAtTqID.js → create-game-BRgWpNsa.js} +3 -3
- package/dist/{gameplay-Ddk13pQ6.js → gameplay-DEG-TP7D.js} +13 -0
- package/dist/gameplay.js +1 -1
- package/dist/index.js +1 -1
- package/dist/{physics-3d-4mGzOZ2J.js → physics-3d--y5clE2j.js} +2 -2
- package/dist/react.js +1 -1
- package/dist/{register-DcHXS1MA.js → register-6R75AC7-.js} +183 -74
- package/dist/test.js +4 -4
- package/editor/assets/{agent8-DElIPozC.js → agent8-CojUfCXN.js} +1 -1
- package/editor/assets/{index-BrneVqN0.js → index-D6RQgROR.js} +83 -83
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/schemas/scene.schema.json +8 -0
- package/skills/incanto-environment.md +23 -0
- package/skills/incanto-node-reference.md +2 -0
- package/templates-app/beacon-isle-3d/package.json +1 -1
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/village-quest-3d/package.json +1 -1
|
@@ -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, 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-
|
|
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-DEG-TP7D.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";
|
|
@@ -2832,6 +2832,37 @@ function firstTerrain(node) {
|
|
|
2832
2832
|
return null;
|
|
2833
2833
|
}
|
|
2834
2834
|
//#endregion
|
|
2835
|
+
//#region src/3d/water/wet-ground.ts
|
|
2836
|
+
function isWetSurface(node) {
|
|
2837
|
+
return typeof node?._waterYAt === "function";
|
|
2838
|
+
}
|
|
2839
|
+
/** Every water surface in the tree the node belongs to. */
|
|
2840
|
+
function collectWetSurfaces(from) {
|
|
2841
|
+
let root = from;
|
|
2842
|
+
while (root.parent) root = root.parent;
|
|
2843
|
+
const out = [];
|
|
2844
|
+
const walk = (node) => {
|
|
2845
|
+
if (node.visible !== false && isWetSurface(node)) out.push(node);
|
|
2846
|
+
for (const child of node.children) walk(child);
|
|
2847
|
+
};
|
|
2848
|
+
walk(root);
|
|
2849
|
+
return out;
|
|
2850
|
+
}
|
|
2851
|
+
/**
|
|
2852
|
+
* Is this spot under water deep enough to drown a plant?
|
|
2853
|
+
*
|
|
2854
|
+
* `margin` is the depth a blade tolerates — a wet edge is still a bank, and
|
|
2855
|
+
* culling everything the water so much as touches leaves a bald halo around
|
|
2856
|
+
* every stream.
|
|
2857
|
+
*/
|
|
2858
|
+
function isUnderWater(surfaces, x, z, groundY, margin = .06) {
|
|
2859
|
+
for (let i = 0; i < surfaces.length; i++) {
|
|
2860
|
+
const y = surfaces[i]?._waterYAt(x, z);
|
|
2861
|
+
if (y !== null && y !== void 0 && y - groundY > margin) return true;
|
|
2862
|
+
}
|
|
2863
|
+
return false;
|
|
2864
|
+
}
|
|
2865
|
+
//#endregion
|
|
2835
2866
|
//#region src/3d/nodes/flowers-3d.ts
|
|
2836
2867
|
/** Named density presets, plants per m² — the vibe-coding dial (풍성하게 /
|
|
2837
2868
|
* 듬성듬성 / 없게). Numbers are accepted too. */
|
|
@@ -2900,6 +2931,7 @@ var Flowers3D = class extends Node3D {
|
|
|
2900
2931
|
clustering: { default: .6 },
|
|
2901
2932
|
sway: { default: .5 },
|
|
2902
2933
|
drape: { default: null },
|
|
2934
|
+
avoidWater: { default: true },
|
|
2903
2935
|
terrain: { default: "" }
|
|
2904
2936
|
};
|
|
2905
2937
|
/** 'lush' | 'sparse' | 'none' | plants per m² (load-time check + budget). */
|
|
@@ -2927,6 +2959,12 @@ var Flowers3D = class extends Node3D {
|
|
|
2927
2959
|
* draping entirely.
|
|
2928
2960
|
*/
|
|
2929
2961
|
drape = null;
|
|
2962
|
+
/**
|
|
2963
|
+
* Keep plants out of standing and running water (default true) — a meadow a
|
|
2964
|
+
* stream runs through has a bank of flowers, not flowers in the current.
|
|
2965
|
+
* Set false for water lilies and marsh planting.
|
|
2966
|
+
*/
|
|
2967
|
+
avoidWater = true;
|
|
2930
2968
|
/** Drape target: a node path to the Terrain3D. Empty = auto-find the first
|
|
2931
2969
|
* Terrain3D in the tree (the zero-config default when `drape` is on). */
|
|
2932
2970
|
terrain = "";
|
|
@@ -3026,13 +3064,20 @@ var Flowers3D = class extends Node3D {
|
|
|
3026
3064
|
const density = resolveFlowerDensity(this.density);
|
|
3027
3065
|
const count = Math.min(Math.floor((this.area[0] ?? 0) * (this.area[1] ?? 0) * density), MAX_PLANTS);
|
|
3028
3066
|
if (count <= 0) return;
|
|
3029
|
-
|
|
3067
|
+
let placements = placeFlowerPlants({
|
|
3030
3068
|
seed: this.seed,
|
|
3031
3069
|
areaX: this.area[0] ?? 0,
|
|
3032
3070
|
areaZ: this.area[1] ?? 0,
|
|
3033
3071
|
count,
|
|
3034
3072
|
clustering: this.clustering
|
|
3035
3073
|
});
|
|
3074
|
+
if (this.avoidWater) {
|
|
3075
|
+
const surfaces = collectWetSurfaces(this);
|
|
3076
|
+
if (surfaces.length > 0) {
|
|
3077
|
+
const [ox, oy, oz] = worldXZY(this);
|
|
3078
|
+
placements = placements.filter((p) => !isUnderWater(surfaces, p.x + ox, p.z + oz, this.bedY(p.x, p.z) + oy));
|
|
3079
|
+
}
|
|
3080
|
+
}
|
|
3036
3081
|
const varieties = this.varieties.length > 0 ? this.varieties : FLOWER_VARIETIES;
|
|
3037
3082
|
const buckets = varieties.map(() => []);
|
|
3038
3083
|
for (const p of placements) buckets[Math.min(Math.floor(p.variety01 * varieties.length), varieties.length - 1)].push(p);
|
|
@@ -4287,6 +4332,7 @@ var Foliage3D = class Foliage3D extends Node3D {
|
|
|
4287
4332
|
flowers: { default: 0 },
|
|
4288
4333
|
seed: { default: 1 },
|
|
4289
4334
|
drape: { default: null },
|
|
4335
|
+
avoidWater: { default: true },
|
|
4290
4336
|
terrain: { default: "" },
|
|
4291
4337
|
renderOrder: { default: 2 }
|
|
4292
4338
|
};
|
|
@@ -4353,6 +4399,14 @@ var Foliage3D = class Foliage3D extends Node3D {
|
|
|
4353
4399
|
* a terrain appears), false disables draping entirely.
|
|
4354
4400
|
*/
|
|
4355
4401
|
drape = null;
|
|
4402
|
+
/**
|
|
4403
|
+
* Keep blades out of standing and running water (default true). Grass does
|
|
4404
|
+
* not grow in a stream: any instance whose ground sits under a `Water3D` or
|
|
4405
|
+
* `River3D` surface is dropped, which is what stops a creek that cut its bed
|
|
4406
|
+
* through a meadow from filling with waving blades. Set false for reeds and
|
|
4407
|
+
* paddy fields, where the plant IS in the water.
|
|
4408
|
+
*/
|
|
4409
|
+
avoidWater = true;
|
|
4356
4410
|
/** Drape target: a node path to the Terrain3D. Empty = auto-find the first
|
|
4357
4411
|
* Terrain3D in the tree (the zero-config default when `drape` is on). */
|
|
4358
4412
|
terrain = "";
|
|
@@ -4582,6 +4636,22 @@ var Foliage3D = class Foliage3D extends Node3D {
|
|
|
4582
4636
|
* fraction of instances into a SECOND InstancedMesh of 5-vertex flower
|
|
4583
4637
|
* heads (white/yellow/violet per instance).
|
|
4584
4638
|
*/
|
|
4639
|
+
/**
|
|
4640
|
+
* Drop the instances standing in water. The ground each blade drapes onto is
|
|
4641
|
+
* already known here, so the test is one question per candidate — and only
|
|
4642
|
+
* the ones a water surface actually covers pay for it.
|
|
4643
|
+
*/
|
|
4644
|
+
dryOnly(placements) {
|
|
4645
|
+
if (!this.avoidWater) return placements;
|
|
4646
|
+
const surfaces = collectWetSurfaces(this);
|
|
4647
|
+
if (surfaces.length === 0) return placements;
|
|
4648
|
+
const [ox, , oz] = worldXZY(this);
|
|
4649
|
+
return placements.filter((p) => !isUnderWater(surfaces, p.x + ox, p.z + oz, this.bedY(p.x, p.z) + this.worldY()));
|
|
4650
|
+
}
|
|
4651
|
+
/** This field's own world height (the bed sampler works in local Y). */
|
|
4652
|
+
worldY() {
|
|
4653
|
+
return worldXZY(this)[1];
|
|
4654
|
+
}
|
|
4585
4655
|
buildMeshField(count) {
|
|
4586
4656
|
const placements = placeGrass({
|
|
4587
4657
|
seed: this.seed,
|
|
@@ -4590,7 +4660,7 @@ var Foliage3D = class Foliage3D extends Node3D {
|
|
|
4590
4660
|
count,
|
|
4591
4661
|
density: this.density * MESH_DENSITY_MULT
|
|
4592
4662
|
});
|
|
4593
|
-
const kept = this.coverage >= 1 ? placements : placements.filter((p) => coverageNoiseAt(p.x, p.z, this.seed) <= this.coverage);
|
|
4663
|
+
const kept = this.dryOnly(this.coverage >= 1 ? placements : placements.filter((p) => coverageNoiseAt(p.x, p.z, this.seed) <= this.coverage));
|
|
4594
4664
|
const flowerCount = this.flowers > 0 ? Math.floor(kept.length * this.flowers) : 0;
|
|
4595
4665
|
const flowerSlots = /* @__PURE__ */ new Set();
|
|
4596
4666
|
for (let k = 0; k < flowerCount; k++) flowerSlots.add(Math.floor((k + .5) * kept.length / flowerCount));
|
|
@@ -4700,7 +4770,7 @@ var Foliage3D = class Foliage3D extends Node3D {
|
|
|
4700
4770
|
count,
|
|
4701
4771
|
density: this.density * TUFT_DENSITY_MULT
|
|
4702
4772
|
});
|
|
4703
|
-
const kept = this.coverage >= 1 ? placements : placements.filter((p) => coverageNoiseAt(p.x, p.z, this.seed) <= this.coverage);
|
|
4773
|
+
const kept = this.dryOnly(this.coverage >= 1 ? placements : placements.filter((p) => coverageNoiseAt(p.x, p.z, this.seed) <= this.coverage));
|
|
4704
4774
|
this.tuftTime = { value: this.time };
|
|
4705
4775
|
this.tuftStrength = { value: this.sway * TUFT_SWAY_TO_WIND };
|
|
4706
4776
|
const material = new MeshStandardMaterial({
|
|
@@ -4803,23 +4873,32 @@ var Foliage3D = class Foliage3D extends Node3D {
|
|
|
4803
4873
|
roughness: 1
|
|
4804
4874
|
});
|
|
4805
4875
|
const mesh = new InstancedMesh(buildBladeGeometry(BLADE_WIDTH[this.kind] ?? .08), material, Math.max(count, 1));
|
|
4806
|
-
mesh.count = count;
|
|
4807
4876
|
mesh.receiveShadow = true;
|
|
4808
4877
|
const rng = new Rng(this.seed);
|
|
4809
4878
|
const halfX = (this.area[0] ?? 0) / 2;
|
|
4810
4879
|
const halfZ = (this.area[1] ?? 0) / 2;
|
|
4811
4880
|
const colorA = colorScratchA$1.set(this.colorA);
|
|
4812
4881
|
const colorB = colorScratchB$1.set(this.colorB);
|
|
4882
|
+
const wet = this.avoidWater ? collectWetSurfaces(this) : [];
|
|
4883
|
+
const [ox, oy, oz] = worldXZY(this);
|
|
4884
|
+
let placed = 0;
|
|
4813
4885
|
for (let i = 0; i < count; i++) {
|
|
4814
4886
|
const sx = rng.range(-halfX, halfX);
|
|
4815
4887
|
const sz = rng.range(-halfZ, halfZ);
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4888
|
+
const rotY = rng.range(0, Math.PI * 2);
|
|
4889
|
+
const jitter = rng.range(HEIGHT_JITTER$1[0], HEIGHT_JITTER$1[1]);
|
|
4890
|
+
const tint = rng.next();
|
|
4891
|
+
const bed = this.bedY(sx, sz);
|
|
4892
|
+
if (wet.length > 0 && isUnderWater(wet, sx + ox, sz + oz, bed + oy)) continue;
|
|
4893
|
+
positionScratch$1.set(sx, bed, sz);
|
|
4894
|
+
quaternionScratch$1.setFromAxisAngle(Y_AXIS$1, rotY);
|
|
4895
|
+
scaleScratch$2.set(1, this.height * jitter, 1);
|
|
4819
4896
|
matrixScratch$1.compose(positionScratch$1, quaternionScratch$1, scaleScratch$2);
|
|
4820
|
-
mesh.setMatrixAt(
|
|
4821
|
-
mesh.setColorAt(
|
|
4897
|
+
mesh.setMatrixAt(placed, matrixScratch$1);
|
|
4898
|
+
mesh.setColorAt(placed, colorScratchC$1.copy(colorA).lerp(colorB, tint));
|
|
4899
|
+
placed++;
|
|
4822
4900
|
}
|
|
4901
|
+
mesh.count = placed;
|
|
4823
4902
|
mesh.instanceMatrix.needsUpdate = true;
|
|
4824
4903
|
if (mesh.instanceColor) mesh.instanceColor.needsUpdate = true;
|
|
4825
4904
|
this.meshes = [mesh];
|
|
@@ -6452,6 +6531,81 @@ function softSprite() {
|
|
|
6452
6531
|
}
|
|
6453
6532
|
//#endregion
|
|
6454
6533
|
//#region src/3d/water/river.ts
|
|
6534
|
+
/**
|
|
6535
|
+
* Self-carved bed. Sized off the WATER COLUMN, never off the width: a channel's
|
|
6536
|
+
* banks are as steep as banks are, and a taper scaled to width turns a wide
|
|
6537
|
+
* river into a shallow saucer the water floods far past its edge.
|
|
6538
|
+
*/
|
|
6539
|
+
/** Trench depth, in water columns. */
|
|
6540
|
+
const CARVE_DEPTH = 2.2;
|
|
6541
|
+
/** Bank blend beyond the flat bed, in water columns. */
|
|
6542
|
+
const CARVE_BANK = 2;
|
|
6543
|
+
/** Where the waterline crosses that blend — smoothstep(t) = 1 − 1/CARVE_DEPTH.
|
|
6544
|
+
* Knowing it is what lets the floor be narrowed so the WATER ends up the
|
|
6545
|
+
* authored width instead of the bed doing so. */
|
|
6546
|
+
const CARVE_WATERLINE_T = .52;
|
|
6547
|
+
/** Never cut the floor away entirely, however steep the bank works out. */
|
|
6548
|
+
const CARVE_FLOOR_MIN = .3;
|
|
6549
|
+
/** A trench thinner than this many terrain cells cannot survive the grid it is
|
|
6550
|
+
* sampled on — it smooths away to nothing and the creek is back on raw ground.
|
|
6551
|
+
* Widening it is the honest failure: better a bed too wide than no bed. */
|
|
6552
|
+
const CARVE_MIN_CELLS = 1.5;
|
|
6553
|
+
/** Deepest the bed may be cut, in water columns — the budget for digging
|
|
6554
|
+
* THROUGH a rise so the water keeps flowing, before it reads as a canyon. */
|
|
6555
|
+
const CARVE_MAX_DEPTH = 7;
|
|
6556
|
+
/** The fall the carved floor keeps even where the ground does not (m per m). */
|
|
6557
|
+
const CARVE_MIN_GRADE = .004;
|
|
6558
|
+
/**
|
|
6559
|
+
* The trench a river digs for itself, as {@link ChannelSpec}s for Terrain3D.
|
|
6560
|
+
*
|
|
6561
|
+
* Shared on purpose: `River3D` cuts this at load, and anything that has to
|
|
6562
|
+
* PLACE things against the result — a scene generator putting a player, a
|
|
6563
|
+
* bridge and a boulder field on the ground — computes the identical cut
|
|
6564
|
+
* instead of approximating it. A generator that guesses differently spawns the
|
|
6565
|
+
* player inside a hill.
|
|
6566
|
+
*
|
|
6567
|
+
* The floor only ever FALLS. Water does not climb, so a bed that climbs is a
|
|
6568
|
+
* river that stops and buries itself; carrying the running minimum downstream
|
|
6569
|
+
* digs through a rise instead, and the cap keeps that from turning a hill into
|
|
6570
|
+
* a canyon.
|
|
6571
|
+
*/
|
|
6572
|
+
function riverCarveChannels(opts) {
|
|
6573
|
+
if (opts.path.length < 2) return [];
|
|
6574
|
+
const line = resamplePath(opts.path, Math.max(opts.cell, opts.depth));
|
|
6575
|
+
if (line.length < 2) return [];
|
|
6576
|
+
const total = line[line.length - 1].along || 1;
|
|
6577
|
+
const ground = line.map((s) => opts.groundAt(s.x, s.z));
|
|
6578
|
+
const baseCut = opts.depth * CARVE_DEPTH;
|
|
6579
|
+
const maxCut = opts.depth * CARVE_MAX_DEPTH;
|
|
6580
|
+
const floor = new Array(line.length);
|
|
6581
|
+
for (let i = 0; i < line.length; i++) {
|
|
6582
|
+
const wanted = ground[i] - baseCut;
|
|
6583
|
+
if (i === 0) {
|
|
6584
|
+
floor[i] = wanted;
|
|
6585
|
+
continue;
|
|
6586
|
+
}
|
|
6587
|
+
const run = line[i].along - line[i - 1].along;
|
|
6588
|
+
const carried = floor[i - 1] - run * CARVE_MIN_GRADE;
|
|
6589
|
+
floor[i] = Math.max(Math.min(wanted, carried), ground[i] - maxCut);
|
|
6590
|
+
}
|
|
6591
|
+
const out = [];
|
|
6592
|
+
for (let i = 1; i < line.length; i++) {
|
|
6593
|
+
const a = line[i - 1];
|
|
6594
|
+
const b = line[i];
|
|
6595
|
+
const cut = Math.max((ground[i - 1] + ground[i]) / 2 - (floor[i - 1] + floor[i]) / 2, 0);
|
|
6596
|
+
if (cut <= .001) continue;
|
|
6597
|
+
const half = widthAlong(opts.widths, a.along / total) / 2;
|
|
6598
|
+
const bank = Math.max(opts.depth * CARVE_BANK, opts.cell * CARVE_MIN_CELLS);
|
|
6599
|
+
const floorHalf = Math.max(half - bank * CARVE_WATERLINE_T, half * CARVE_FLOOR_MIN, opts.cell * .5);
|
|
6600
|
+
out.push({
|
|
6601
|
+
path: [[a.x, a.z], [b.x, b.z]],
|
|
6602
|
+
width: floorHalf * 2,
|
|
6603
|
+
depth: cut,
|
|
6604
|
+
taper: bank
|
|
6605
|
+
});
|
|
6606
|
+
}
|
|
6607
|
+
return out;
|
|
6608
|
+
}
|
|
6455
6609
|
/** Dense Catmull-Rom taps per control segment before arc-length resampling. */
|
|
6456
6610
|
const SPLINE_TAPS = 24;
|
|
6457
6611
|
/** Box-filter half-window (samples) that irons kinks out of the bed profile. */
|
|
@@ -7327,30 +7481,6 @@ const METERS_PER_ACROSS_SEGMENT = 1.5;
|
|
|
7327
7481
|
* flow over it, wide enough that terrain grain does not fizz the whole river. */
|
|
7328
7482
|
const COLUMN_BLUR_PER_HALF_WIDTH = .35;
|
|
7329
7483
|
const COLUMN_BLUR_MAX = 1.2;
|
|
7330
|
-
/**
|
|
7331
|
-
* Self-carved bed. The cut is sized off the WATER COLUMN, not off the width:
|
|
7332
|
-
* a channel's banks are as steep as its banks are, and a taper scaled to width
|
|
7333
|
-
* turns a wide river into a shallow saucer the water floods far past its edge.
|
|
7334
|
-
*
|
|
7335
|
-
* `depth` is the trench in water columns; `bank` the smoothstep blend beyond
|
|
7336
|
-
* the flat bed, also in columns. The waterline crosses that blend at
|
|
7337
|
-
* {@link CARVE_WATERLINE_T} of its width (smoothstep(t) = 1 − 1/depth), which
|
|
7338
|
-
* is what lets the floor be narrowed so the WATER ends up the authored width.
|
|
7339
|
-
*/
|
|
7340
|
-
const CARVE_DEPTH = 2.2;
|
|
7341
|
-
const CARVE_BANK = 2;
|
|
7342
|
-
const CARVE_WATERLINE_T = .52;
|
|
7343
|
-
/** Never cut the floor away entirely, however steep the bank works out. */
|
|
7344
|
-
const CARVE_FLOOR_MIN = .3;
|
|
7345
|
-
/** A trench thinner than this many terrain cells cannot survive the grid it is
|
|
7346
|
-
* sampled on — it smooths away to nothing and the creek is back on raw ground.
|
|
7347
|
-
* Widening it is the honest failure: better a bed too wide than no bed. */
|
|
7348
|
-
const CARVE_MIN_CELLS = 1.5;
|
|
7349
|
-
/** Deepest the bed may be cut, in water columns — the budget for digging
|
|
7350
|
-
* THROUGH a rise so the water keeps flowing, before it would read as a canyon. */
|
|
7351
|
-
const CARVE_MAX_DEPTH = 7;
|
|
7352
|
-
/** The fall the carved floor keeps even where the ground does not (m per m). */
|
|
7353
|
-
const CARVE_MIN_GRADE = .004;
|
|
7354
7484
|
/** Rocks the shader can carry at once (the nearest ones win). */
|
|
7355
7485
|
const ROCK_SLOTS = 8;
|
|
7356
7486
|
/** How often the tree is re-scanned for things standing in the water (s). */
|
|
@@ -7571,6 +7701,17 @@ var River3D = class River3D extends Node3D {
|
|
|
7571
7701
|
this.syncRocks(u, ctx.camera.getWorldPosition(rockCamScratch));
|
|
7572
7702
|
}
|
|
7573
7703
|
/**
|
|
7704
|
+
* @internal Water surface height over a world point, or null where this
|
|
7705
|
+
* river does not run — the question scattered vegetation asks before it
|
|
7706
|
+
* plants a blade (see {@link WetSurface}).
|
|
7707
|
+
*/
|
|
7708
|
+
_waterYAt(x, z) {
|
|
7709
|
+
if (this.rings.length === 0) return null;
|
|
7710
|
+
const [wx, wy, wz] = this.world;
|
|
7711
|
+
const hit = projectToRiver(this.rings, x - wx, z - wz);
|
|
7712
|
+
return hit?.inside ? hit.surfaceY + wy : null;
|
|
7713
|
+
}
|
|
7714
|
+
/**
|
|
7574
7715
|
* Where a WORLD point sits in the channel — the query gameplay scripts run
|
|
7575
7716
|
* (steer a raft, drown a torch, decide a ford is too fast to cross). `null`
|
|
7576
7717
|
* until the river has been built.
|
|
@@ -7638,46 +7779,14 @@ var River3D = class River3D extends Node3D {
|
|
|
7638
7779
|
terrain._setExternalChannel(this, null);
|
|
7639
7780
|
const [wx, , wz] = worldXZY(this);
|
|
7640
7781
|
const [tx, , tz] = worldXZY(terrain);
|
|
7641
|
-
const widths = this.widthProfile();
|
|
7642
7782
|
const hm = terrain._heightmap();
|
|
7643
|
-
|
|
7644
|
-
|
|
7645
|
-
|
|
7646
|
-
|
|
7647
|
-
|
|
7648
|
-
|
|
7649
|
-
|
|
7650
|
-
const ground = line.map((s) => terrain._heightmap().heightAt(s.x, s.z));
|
|
7651
|
-
const baseCut = this.depth * CARVE_DEPTH;
|
|
7652
|
-
const maxCut = this.depth * CARVE_MAX_DEPTH;
|
|
7653
|
-
const floor = new Array(line.length);
|
|
7654
|
-
for (let i = 0; i < line.length; i++) {
|
|
7655
|
-
const wanted = ground[i] - baseCut;
|
|
7656
|
-
if (i === 0) {
|
|
7657
|
-
floor[i] = wanted;
|
|
7658
|
-
continue;
|
|
7659
|
-
}
|
|
7660
|
-
const run = line[i].along - line[i - 1].along;
|
|
7661
|
-
const carried = floor[i - 1] - run * CARVE_MIN_GRADE;
|
|
7662
|
-
floor[i] = Math.max(Math.min(wanted, carried), ground[i] - maxCut);
|
|
7663
|
-
}
|
|
7664
|
-
const specs = [];
|
|
7665
|
-
for (let i = 1; i < line.length; i++) {
|
|
7666
|
-
const a = line[i - 1];
|
|
7667
|
-
const b = line[i];
|
|
7668
|
-
const cut = Math.max((ground[i - 1] + ground[i]) / 2 - (floor[i - 1] + floor[i]) / 2, 0);
|
|
7669
|
-
if (cut <= .001) continue;
|
|
7670
|
-
const half = widthAlong(widths, a.along / total) / 2;
|
|
7671
|
-
const bank = Math.max(this.depth * CARVE_BANK, cell * CARVE_MIN_CELLS);
|
|
7672
|
-
const floorHalf = Math.max(half - bank * CARVE_WATERLINE_T, half * CARVE_FLOOR_MIN, cell * .5);
|
|
7673
|
-
specs.push({
|
|
7674
|
-
path: [[a.x, a.z], [b.x, b.z]],
|
|
7675
|
-
width: floorHalf * 2,
|
|
7676
|
-
depth: cut,
|
|
7677
|
-
taper: bank
|
|
7678
|
-
});
|
|
7679
|
-
}
|
|
7680
|
-
terrain._setExternalChannel(this, specs);
|
|
7783
|
+
terrain._setExternalChannel(this, riverCarveChannels({
|
|
7784
|
+
path: this.path.map((p) => [(p[0] ?? 0) + wx - tx, (p[1] ?? 0) + wz - tz]),
|
|
7785
|
+
widths: this.widthProfile(),
|
|
7786
|
+
depth: this.depth,
|
|
7787
|
+
cell: (terrain.size[0] ?? 1) / Math.max(1, hm.segsX),
|
|
7788
|
+
groundAt: (x, z) => hm.heightAt(x, z)
|
|
7789
|
+
}));
|
|
7681
7790
|
}
|
|
7682
7791
|
/** Resolve the drape terrain; an explicit path that misses is a hard error. */
|
|
7683
7792
|
resolveTerrain() {
|
|
@@ -11292,4 +11401,4 @@ function registerNodes3D() {
|
|
|
11292
11401
|
registerNode(CharacterBody3D);
|
|
11293
11402
|
}
|
|
11294
11403
|
//#endregion
|
|
11295
|
-
export {
|
|
11404
|
+
export { Joint3D as A, BoneLookAt3D as C, DEFAULT_TERRAIN_TEXTURE_BASE as D, Terrain3D as E, rigPose as F, cameraRelative as M, keyboardIntensity as N, TERRAIN_THEMES as O, movementState as P, Camera3D as S, Billboard3D as T, DENSITY_PRESETS as _, Trail3D as a, FLOWER_VARIETIES as b, traceDownhillPath as c, LoftMesh3D as d, DirectionalLight3D as f, Foliage3D as g, MeshInstance3D as h, Tree3D as i, QUARTER_PITCH as j, terrainThemeLayers as k, Particles3D as l, InstancedMesh3D as m, VOXEL_PALETTE as n, River3D as o, OmniLight3D as p, VoxelGrid3D as r, riverCarveChannels as s, registerNodes3D as t, ModelInstance3D as u, Flowers3D as v, BoneAttachment3D as w, CharacterController3D as x, resolveFlowerDensity as y };
|
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-
|
|
7
|
+
import { n as registerGameplayBehaviors } from "./gameplay-DEG-TP7D.js";
|
|
8
8
|
import { t as registerNodes2D } from "./register-CvpSUU3O.js";
|
|
9
|
-
import { t as registerNodes3D } from "./register-
|
|
9
|
+
import { t as registerNodes3D } from "./register-6R75AC7-.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
|
|
130
|
+
const { enablePhysics3D } = await import("./physics-3d--y5clE2j.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
|
|
235
|
+
const { enablePhysics3D } = await import("./physics-3d--y5clE2j.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-
|
|
1
|
+
import{t as e}from"./index-D6RQgROR.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};
|