@adatechnology/game-test-room 0.19.1 → 0.20.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.
@@ -605,9 +605,9 @@ var GameTestRoom = (() => {
605
605
  const tz = 2 * (qx * v[1] - qy * v[0]);
606
606
  return [v[0] + qw * tx + (qy * tz - qz * ty), v[1] + qw * ty + (qz * tx - qx * tz), v[2] + qw * tz + (qx * ty - qy * tx)];
607
607
  };
608
- var toLocal = (position, rotation, p) => rotateVec([-rotation[0], -rotation[1], -rotation[2], rotation[3]], [p[0] - position[0], p[1] - position[1], p[2] - position[2]]);
609
- var toWorld = (position, rotation, p) => {
610
- const r = rotateVec(rotation, p);
608
+ var toLocal = (position, rotation2, p) => rotateVec([-rotation2[0], -rotation2[1], -rotation2[2], rotation2[3]], [p[0] - position[0], p[1] - position[1], p[2] - position[2]]);
609
+ var toWorld = (position, rotation2, p) => {
610
+ const r = rotateVec(rotation2, p);
611
611
  return [r[0] + position[0], r[1] + position[1], r[2] + position[2]];
612
612
  };
613
613
  var shapeDims = (s) => {
@@ -1012,8 +1012,8 @@ var GameTestRoom = (() => {
1012
1012
  };
1013
1013
  var cellsOutside = (cells, thin, point, radius) => cells.filter((c) => planeDistance(c, thin, point) > radius);
1014
1014
  var MAX_MARKS_PER_FRAGMENT = 12;
1015
- var marksInBox = (center, size, rotation, holes) => {
1016
- const inverse = rotation ? quat.conjugate(rotation) : void 0;
1015
+ var marksInBox = (center, size, rotation2, holes) => {
1016
+ const inverse = rotation2 ? quat.conjugate(rotation2) : void 0;
1017
1017
  const marks = [];
1018
1018
  for (const hole of holes) {
1019
1019
  const offset = [hole.point[0] - center[0], hole.point[1] - center[1], hole.point[2] - center[2]];
@@ -3602,8 +3602,8 @@ var GameTestRoom = (() => {
3602
3602
  }
3603
3603
  solveStructure(o);
3604
3604
  if (peak <= sec.steel) return;
3605
- const rotation = (elasticEnergy(peak, L, EI, flexure.loadFactor) - elasticEnergy(sec.steel, L, EI, flexure.loadFactor)) / (2 * sec.steel);
3606
- p.rt.hinge = (p.rt.hinge ?? 0) + rotation;
3605
+ const rotation2 = (elasticEnergy(peak, L, EI, flexure.loadFactor) - elasticEnergy(sec.steel, L, EI, flexure.loadFactor)) / (2 * sec.steel);
3606
+ p.rt.hinge = (p.rt.hinge ?? 0) + rotation2;
3607
3607
  markDamaged(p);
3608
3608
  if (p.rt.hinge >= flexure.failRotationDeg * Math.PI / 180) hingeDrop(o, p, src);
3609
3609
  };
@@ -4305,8 +4305,8 @@ var GameTestRoom = (() => {
4305
4305
  const detachEvents = (o, opened, by, src) => {
4306
4306
  for (const j of opened) emit2({ ...base(o, src), type: "detach", joint: j.spec.id, parts: [j.spec.a, j.spec.b], by, groups: o.st.groups.map((g) => [...g]) });
4307
4307
  };
4308
- const slideOffResting = (o, rotation) => {
4309
- const upY = 1 - 2 * (rotation[0] * rotation[0] + rotation[2] * rotation[2]);
4308
+ const slideOffResting = (o, rotation2) => {
4309
+ const upY = 1 - 2 * (rotation2[0] * rotation2[0] + rotation2[2] * rotation2[2]);
4310
4310
  if (upY >= RESTING_HOLD_COS) return;
4311
4311
  const opened = o.joints.filter((j) => j.spec.kind === "resting" && !j.st.open);
4312
4312
  if (opened.length === 0) return;
@@ -5729,11 +5729,11 @@ var GameTestRoom = (() => {
5729
5729
  impls.set(id, { impl, position: opts.position ?? [0, 0, 0], rotation: opts.rotation ?? [0, 0, 0, 1], poses: /* @__PURE__ */ new Map() });
5730
5730
  return id;
5731
5731
  },
5732
- setPartPose(objectId, partId, position, rotation) {
5733
- const q = quatFromEuler(...rotation ?? [0, 0, 0]);
5732
+ setPartPose(objectId, partId, position, rotation2) {
5733
+ const q = quatFromEuler(...rotation2 ?? [0, 0, 0]);
5734
5734
  const im = impls.get(objectId);
5735
5735
  if (im) {
5736
- im.impl.setPartPose?.(partId, position, rotation);
5736
+ im.impl.setPartPose?.(partId, position, rotation2);
5737
5737
  im.poses.set(partId, { pos: [position[0], position[1], position[2]], q });
5738
5738
  return;
5739
5739
  }
@@ -5750,17 +5750,17 @@ var GameTestRoom = (() => {
5750
5750
  },
5751
5751
  has: (id) => taken(id),
5752
5752
  ids: () => [...objects.keys(), ...impls.keys()],
5753
- setTransform(id, position, rotation) {
5753
+ setTransform(id, position, rotation2) {
5754
5754
  const im = impls.get(id);
5755
5755
  if (im) {
5756
5756
  im.position = [position[0], position[1], position[2]];
5757
- im.rotation = [rotation[0], rotation[1], rotation[2], rotation[3]];
5757
+ im.rotation = [rotation2[0], rotation2[1], rotation2[2], rotation2[3]];
5758
5758
  return;
5759
5759
  }
5760
5760
  const o = getObj(id);
5761
- o.base = { pos: [position[0], position[1], position[2]], rot: [rotation[0], rotation[1], rotation[2], rotation[3]] };
5761
+ o.base = { pos: [position[0], position[1], position[2]], rot: [rotation2[0], rotation2[1], rotation2[2], rotation2[3]] };
5762
5762
  applySoilPose(o);
5763
- slideOffResting(o, rotation);
5763
+ slideOffResting(o, rotation2);
5764
5764
  },
5765
5765
  splitGroup(id, group, newId) {
5766
5766
  if (impls.has(id)) throw new Error(`object "${id}" is a registered DestructibleImpl and cannot be split; its module owns its pieces`);
@@ -9166,11 +9166,231 @@ var GameTestRoom = (() => {
9166
9166
  glow.outIntensity = glow.i * Math.pow(1 - age, glow.curve) * flicker;
9167
9167
  }
9168
9168
  }
9169
+ var LEHMER_MULTIPLIER = 16807;
9170
+ var LEHMER_MODULUS = 2147483647;
9171
+ var HASH_MIX = 2654435761;
9172
+ function normalizeLehmerSeed(seed) {
9173
+ const whole = Math.floor(Math.abs(seed)) % (LEHMER_MODULUS - 1);
9174
+ return whole === 0 ? 1 : whole;
9175
+ }
9176
+ function lehmer(seed) {
9177
+ let state = normalizeLehmerSeed(seed);
9178
+ const next = () => {
9179
+ state = state * LEHMER_MULTIPLIER % LEHMER_MODULUS;
9180
+ return (state - 1) / (LEHMER_MODULUS - 1);
9181
+ };
9182
+ return {
9183
+ next,
9184
+ between: (min2, max2) => min2 + (max2 - min2) * next(),
9185
+ direction(out) {
9186
+ const up = next() * 2 - 1;
9187
+ const angle = next() * Math.PI * 2;
9188
+ const ring2 = Math.sqrt(1 - up * up);
9189
+ out.x = ring2 * Math.cos(angle);
9190
+ out.y = up;
9191
+ out.z = ring2 * Math.sin(angle);
9192
+ return out;
9193
+ },
9194
+ get state() {
9195
+ return state;
9196
+ }
9197
+ };
9198
+ }
9199
+ function mixEventSeed(eventSeed) {
9200
+ let mixed = Math.imul((eventSeed | 0) ^ HASH_MIX, 2246822507);
9201
+ mixed ^= mixed >>> 13;
9202
+ mixed = Math.imul(mixed, 3266489909);
9203
+ mixed ^= mixed >>> 16;
9204
+ return (mixed >>> 0) % (LEHMER_MODULUS - 1) + 1;
9205
+ }
9206
+ function streamFor2(eventSeed) {
9207
+ return lehmer(mixEventSeed(eventSeed));
9208
+ }
9209
+ var SKIN = 5e-3;
9210
+ var MAX_PAD = 0.45;
9211
+ function rotation(q) {
9212
+ const [x, y, z, w] = q ?? [0, 0, 0, 1];
9213
+ const n = Math.hypot(x, y, z, w) || 1;
9214
+ const qx = x / n;
9215
+ const qy = y / n;
9216
+ const qz = z / n;
9217
+ const qw = w / n;
9218
+ return new Float64Array([
9219
+ 1 - 2 * (qy * qy + qz * qz),
9220
+ 2 * (qx * qy - qz * qw),
9221
+ 2 * (qx * qz + qy * qw),
9222
+ 2 * (qx * qy + qz * qw),
9223
+ 1 - 2 * (qx * qx + qz * qz),
9224
+ 2 * (qy * qz - qx * qw),
9225
+ 2 * (qx * qz - qy * qw),
9226
+ 2 * (qy * qz + qx * qw),
9227
+ 1 - 2 * (qx * qx + qy * qy)
9228
+ ]);
9229
+ }
9230
+ var Enclosures = class {
9231
+ constructor(seed = 20973) {
9232
+ __publicField(this, "spaces", /* @__PURE__ */ new Map());
9233
+ __publicField(this, "list", []);
9234
+ __publicField(this, "random");
9235
+ __publicField(this, "out", { x: 0, y: 0, z: 0, nx: 0, ny: 0, nz: 0 });
9236
+ __publicField(this, "l0", new Float64Array(3));
9237
+ __publicField(this, "l1", new Float64Array(3));
9238
+ __publicField(this, "q", new Float64Array(3));
9239
+ this.random = lehmer(seed);
9240
+ }
9241
+ get empty() {
9242
+ return this.list.length === 0;
9243
+ }
9244
+ get size() {
9245
+ return this.list.length;
9246
+ }
9247
+ /** Declares, moves or (with `null`) ends the space `id`. */
9248
+ set(id, enclosure) {
9249
+ if (!enclosure) {
9250
+ if (this.spaces.delete(id)) this.list = [...this.spaces.values()];
9251
+ return;
9252
+ }
9253
+ const [hx, hy, hz] = enclosure.halfSize;
9254
+ const space = {
9255
+ cx: enclosure.center[0],
9256
+ cy: enclosure.center[1],
9257
+ cz: enclosure.center[2],
9258
+ hx: Math.abs(hx),
9259
+ hy: Math.abs(hy),
9260
+ hz: Math.abs(hz),
9261
+ r: rotation(enclosure.orientation),
9262
+ radius: Math.hypot(hx, hy, hz),
9263
+ openings: enclosure.openings ?? [],
9264
+ leak: Math.max(0, enclosure.leak ?? 0)
9265
+ };
9266
+ const old = this.spaces.get(id);
9267
+ this.spaces.set(id, space);
9268
+ const at3 = old ? this.list.indexOf(old) : -1;
9269
+ if (at3 >= 0) this.list[at3] = space;
9270
+ else this.list.push(space);
9271
+ }
9272
+ get(id) {
9273
+ const s = this.spaces.get(id);
9274
+ if (!s) return void 0;
9275
+ return { center: [s.cx, s.cy, s.cz], halfSize: [s.hx, s.hy, s.hz], openings: s.openings, leak: s.leak };
9276
+ }
9277
+ ids() {
9278
+ return [...this.spaces.keys()];
9279
+ }
9280
+ clear() {
9281
+ this.spaces.clear();
9282
+ this.list = [];
9283
+ }
9284
+ /** Whether a point is inside any space (on the inside of its walls). */
9285
+ inside(x, y, z) {
9286
+ for (const s of this.list) {
9287
+ this.toLocal(s, x, y, z, this.l0);
9288
+ if (Math.abs(this.l0[0]) <= s.hx && Math.abs(this.l0[1]) <= s.hy && Math.abs(this.l0[2]) <= s.hz) return true;
9289
+ }
9290
+ return false;
9291
+ }
9292
+ toLocal(s, x, y, z, out) {
9293
+ const dx = x - s.cx;
9294
+ const dy = y - s.cy;
9295
+ const dz = z - s.cz;
9296
+ const r = s.r;
9297
+ out[0] = r[0] * dx + r[3] * dy + r[6] * dz;
9298
+ out[1] = r[1] * dx + r[4] * dy + r[7] * dz;
9299
+ out[2] = r[2] * dx + r[5] * dy + r[8] * dz;
9300
+ }
9301
+ /** Passes at `rate` per second. */
9302
+ chance(rate, dt) {
9303
+ return rate > 0 && this.random.next() < 1 - Math.exp(-rate * dt);
9304
+ }
9305
+ /** The opening the wall point `q` (local) is in lets it through now. */
9306
+ through(s, q, dt) {
9307
+ for (const o of s.openings) {
9308
+ if (Math.abs(q[0] - o.center[0]) > o.halfSize[0] || Math.abs(q[1] - o.center[1]) > o.halfSize[1] || Math.abs(q[2] - o.center[2]) > o.halfSize[2]) continue;
9309
+ if (o.rate === void 0 || this.chance(o.rate, dt)) return true;
9310
+ }
9311
+ return this.chance(s.leak, dt);
9312
+ }
9313
+ confine(x0, y0, z0, x1, y1, z1, pad2, dt) {
9314
+ const list = this.list;
9315
+ if (list.length === 0) return false;
9316
+ const p = Math.min(Math.max(0, pad2), MAX_PAD);
9317
+ const l0 = this.l0;
9318
+ const l1 = this.l1;
9319
+ const q = this.q;
9320
+ for (const s of list) {
9321
+ const reach = s.radius + p + 0.05;
9322
+ const dx = x1 - s.cx;
9323
+ const dy = y1 - s.cy;
9324
+ const dz = z1 - s.cz;
9325
+ if (dx * dx + dy * dy + dz * dz > reach * reach) {
9326
+ const ex = x0 - s.cx;
9327
+ const ey = y0 - s.cy;
9328
+ const ez = z0 - s.cz;
9329
+ if (ex * ex + ey * ey + ez * ez > reach * reach) continue;
9330
+ }
9331
+ this.toLocal(s, x0, y0, z0, l0);
9332
+ this.toLocal(s, x1, y1, z1, l1);
9333
+ const h = [s.hx, s.hy, s.hz];
9334
+ const in0 = Math.abs(l0[0]) <= s.hx && Math.abs(l0[1]) <= s.hy && Math.abs(l0[2]) <= s.hz;
9335
+ let axis2 = 0;
9336
+ let sign = 1;
9337
+ if (in0) {
9338
+ if (Math.abs(l1[0]) <= s.hx && Math.abs(l1[1]) <= s.hy && Math.abs(l1[2]) <= s.hz) continue;
9339
+ for (let a = 0; a < 3; a++) q[a] = Math.min(h[a], Math.max(-h[a], l1[a]));
9340
+ if (this.through(s, q, dt)) continue;
9341
+ let worst = -Infinity;
9342
+ for (let a = 0; a < 3; a++) {
9343
+ const over = Math.abs(l1[a]) - h[a];
9344
+ if (over > worst) {
9345
+ worst = over;
9346
+ axis2 = a;
9347
+ }
9348
+ }
9349
+ sign = l1[axis2] < 0 ? -1 : 1;
9350
+ for (let a = 0; a < 3; a++) l1[a] = Math.min(h[a] - SKIN, Math.max(-(h[a] - SKIN), l1[a]));
9351
+ sign = -sign;
9352
+ } else {
9353
+ const padded0 = Math.abs(l0[0]) <= s.hx + p && Math.abs(l0[1]) <= s.hy + p && Math.abs(l0[2]) <= s.hz + p;
9354
+ const bound = padded0 ? 0 : p;
9355
+ if (!(Math.abs(l1[0]) <= s.hx + bound && Math.abs(l1[1]) <= s.hy + bound && Math.abs(l1[2]) <= s.hz + bound)) continue;
9356
+ for (let a = 0; a < 3; a++) q[a] = Math.min(h[a], Math.max(-h[a], l1[a]));
9357
+ if (this.through(s, q, dt)) continue;
9358
+ let worst = -Infinity;
9359
+ for (let a = 0; a < 3; a++) {
9360
+ const over = Math.abs(l0[a]) - (h[a] + bound);
9361
+ if (over > worst) {
9362
+ worst = over;
9363
+ axis2 = a;
9364
+ }
9365
+ }
9366
+ sign = l0[axis2] < 0 ? -1 : 1;
9367
+ l1[axis2] = sign * (h[axis2] + bound + SKIN);
9368
+ }
9369
+ const r = s.r;
9370
+ const out = this.out;
9371
+ out.x = s.cx + r[0] * l1[0] + r[1] * l1[1] + r[2] * l1[2];
9372
+ out.y = s.cy + r[3] * l1[0] + r[4] * l1[1] + r[5] * l1[2];
9373
+ out.z = s.cz + r[6] * l1[0] + r[7] * l1[1] + r[8] * l1[2];
9374
+ out.nx = r[axis2] * sign;
9375
+ out.ny = r[3 + axis2] * sign;
9376
+ out.nz = r[6 + axis2] * sign;
9377
+ return true;
9378
+ }
9379
+ return false;
9380
+ }
9381
+ };
9382
+ function slideAlong(out, vx2, vy2, vz2) {
9383
+ const into = vx2 * out.nx + vy2 * out.ny + vz2 * out.nz;
9384
+ if (into >= 0) return [vx2, vy2, vz2];
9385
+ return [vx2 - into * out.nx, vy2 - into * out.ny, vz2 - into * out.nz];
9386
+ }
9169
9387
  var FLOOR_FRACTION = 0.4;
9170
9388
  var FLOOR_BOUNCE = -0.2;
9171
9389
  var ARRIVAL_FRAME = 30;
9172
9390
  var ARRIVAL_BASE = 0.3;
9173
9391
  var GROWTH_CAP_SECONDS = 3.5;
9392
+ var CARRIED_KINDS = [true, true, false, true, false];
9393
+ var PAD_OF_SIZE = 0.35;
9174
9394
  function createPuffPool(capacity) {
9175
9395
  const placeholder = { next: () => 0, between: (min2) => min2, direction: (out) => out, state: 0 };
9176
9396
  return new OrderedPool(capacity, () => ({
@@ -9265,13 +9485,24 @@ var GameTestRoom = (() => {
9265
9485
  if (puff3.vy < 0) puff3.vy *= FLOOR_BOUNCE;
9266
9486
  }
9267
9487
  }
9268
- function updatePuffs(pool, dt, wind) {
9488
+ function updatePuffs(pool, dt, wind, confiner) {
9269
9489
  pool.compact(ageOrKeep2, dt);
9490
+ const confining = confiner !== void 0 && !confiner.empty;
9270
9491
  for (const puff3 of pool.items) {
9271
9492
  const age = puff3.t / puff3.life;
9272
9493
  const heat = puff3.h0 > 0 ? puff3.h0 * Math.pow(Math.max(0, 1 - puff3.t / puff3.hl), 1.25) : 0;
9494
+ const x0 = puff3.x;
9495
+ const y0 = puff3.y;
9496
+ const z0 = puff3.z;
9273
9497
  if (puff3.ring) moveOnRing(puff3, puff3.ring, wind);
9274
9498
  else moveFree(puff3, dt, age, heat, wind);
9499
+ if (confining && CARRIED_KINDS[puff3.kind] && confiner.confine(x0, y0, z0, puff3.x, puff3.y, puff3.z, puff3.size * PAD_OF_SIZE, dt)) {
9500
+ const out = confiner.out;
9501
+ puff3.x = out.x;
9502
+ puff3.y = out.y;
9503
+ puff3.z = out.z;
9504
+ [puff3.vx, puff3.vy, puff3.vz] = slideAlong(out, puff3.vx, puff3.vy, puff3.vz);
9505
+ }
9275
9506
  const size = puff3.s0 + (puff3.s1 - puff3.s0) * (1 - Math.exp(-puff3.t * puff3.gk)) + puff3.sg * Math.min(puff3.t, GROWTH_CAP_SECONDS);
9276
9507
  puff3.size = size * Math.min(1, puff3.t * ARRIVAL_FRAME + ARRIVAL_BASE);
9277
9508
  puff3.age = age;
@@ -9797,10 +10028,10 @@ var GameTestRoom = (() => {
9797
10028
  ])
9798
10029
  ]
9799
10030
  };
9800
- function localToWorld(translation, rotation, local2) {
10031
+ function localToWorld(translation, rotation2, local2) {
9801
10032
  const [x, y, z] = local2;
9802
- const [a, c, e] = [Math.cos(rotation[0]), Math.cos(rotation[1]), Math.cos(rotation[2])];
9803
- const [b, d, f] = [Math.sin(rotation[0]), Math.sin(rotation[1]), Math.sin(rotation[2])];
10033
+ const [a, c, e] = [Math.cos(rotation2[0]), Math.cos(rotation2[1]), Math.cos(rotation2[2])];
10034
+ const [b, d, f] = [Math.sin(rotation2[0]), Math.sin(rotation2[1]), Math.sin(rotation2[2])];
9804
10035
  const [ae, af, be, bf] = [a * e, a * f, b * e, b * f];
9805
10036
  return [
9806
10037
  c * e * x + -c * f * y + d * z + translation[0],
@@ -11463,46 +11694,6 @@ var GameTestRoom = (() => {
11463
11694
  var ALL_EFFECTS = [BLOOD_HIT, HEADSHOT, DISMEMBER, BLOATER_CLOUD, SCREAMER_SHRIEK, CORPSE_DECAY, REANIMATION, EYES_IN_FOG, MUZZLE_FLASH, IMPACT_BY_MATERIAL, PENETRATION, CASINGS_AND_MAGAZINE, MOLOTOV, GRENADE, EXPLOSIVE_BARREL, SHOPFRONT_GLASS, DOOR_STAGES, BUILDING_COLLAPSE, BLOCK_WALL, METAL_DOOR_EFFECT, IRON_RAILING, WOODEN_FENCE, HIT_POINT_BREAKAGE, PRESSURE_CYLINDERS, BUILDING_FIRE, RAIN_AND_PUDDLES, FOG_AND_ASH, SCRAPING_SPARKS, ENGINE_SMOKE, FUEL_LEAK, CAR_EXPLOSION, WINDSHIELD, HEADLIGHTS_IN_RAIN, DAMAGE_AND_BITE, INFECTION_PHASES, PANIC, BLOATER_BLINDNESS, CAMPFIRE, COUGH_AND_BREATH];
11464
11695
  var WORKSHOP_EFFECTS = Object.fromEntries(ALL_EFFECTS.map((effect) => [effect.id, effect]));
11465
11696
  var GAME_EFFECTS = { ...WORKSHOP_EFFECTS, ...Object.fromEntries([...EVENT_EFFECTS, ...VIEWMODEL_EFFECTS, ...VEHICLE_EFFECTS].map((effect) => [effect.id, effect])) };
11466
- var LEHMER_MULTIPLIER = 16807;
11467
- var LEHMER_MODULUS = 2147483647;
11468
- var HASH_MIX = 2654435761;
11469
- function normalizeLehmerSeed(seed) {
11470
- const whole = Math.floor(Math.abs(seed)) % (LEHMER_MODULUS - 1);
11471
- return whole === 0 ? 1 : whole;
11472
- }
11473
- function lehmer(seed) {
11474
- let state = normalizeLehmerSeed(seed);
11475
- const next = () => {
11476
- state = state * LEHMER_MULTIPLIER % LEHMER_MODULUS;
11477
- return (state - 1) / (LEHMER_MODULUS - 1);
11478
- };
11479
- return {
11480
- next,
11481
- between: (min2, max2) => min2 + (max2 - min2) * next(),
11482
- direction(out) {
11483
- const up = next() * 2 - 1;
11484
- const angle = next() * Math.PI * 2;
11485
- const ring2 = Math.sqrt(1 - up * up);
11486
- out.x = ring2 * Math.cos(angle);
11487
- out.y = up;
11488
- out.z = ring2 * Math.sin(angle);
11489
- return out;
11490
- },
11491
- get state() {
11492
- return state;
11493
- }
11494
- };
11495
- }
11496
- function mixEventSeed(eventSeed) {
11497
- let mixed = Math.imul((eventSeed | 0) ^ HASH_MIX, 2246822507);
11498
- mixed ^= mixed >>> 13;
11499
- mixed = Math.imul(mixed, 3266489909);
11500
- mixed ^= mixed >>> 16;
11501
- return (mixed >>> 0) % (LEHMER_MODULUS - 1) + 1;
11502
- }
11503
- function streamFor2(eventSeed) {
11504
- return lehmer(mixEventSeed(eventSeed));
11505
- }
11506
11697
  var TABLE = {
11507
11698
  concrete: { dust: 9078138, chip: 7236195, spark: 2 },
11508
11699
  metal: { dust: 0, chip: 0, spark: 18 },
@@ -12806,8 +12997,8 @@ var GameTestRoom = (() => {
12806
12997
  function addScorch(store, random, x, z, radius) {
12807
12998
  const variant = Math.floor(random.next() * SCORCH_VARIANTS);
12808
12999
  const lift = 0.03 + nextLift(store) * 1e-3;
12809
- const rotation = random.next() * 6;
12810
- store.add({ kind: "scorch", x, z, radius, variant, lift, rotation });
13000
+ const rotation2 = random.next() * 6;
13001
+ store.add({ kind: "scorch", x, z, radius, variant, lift, rotation: rotation2 });
12811
13002
  }
12812
13003
  function addRimRocks(store, ground, random, x, z, radius) {
12813
13004
  const count = Math.round(6 + radius * 7);
@@ -12836,8 +13027,8 @@ var GameTestRoom = (() => {
12836
13027
  function addCrater(store, ground, random, spec) {
12837
13028
  ground?.dent(random, spec.x, spec.z, spec.radius, spec.depth);
12838
13029
  if (!spec.marks) return;
12839
- const rotation = random.next() * 6;
12840
- store.add({ kind: "dirt", x: spec.x, z: spec.z, radius: spec.radius * 1.02, lift: 0.045, rotation });
13030
+ const rotation2 = random.next() * 6;
13031
+ store.add({ kind: "dirt", x: spec.x, z: spec.z, radius: spec.radius * 1.02, lift: 0.045, rotation: rotation2 });
12841
13032
  addRimRocks(store, ground, random, spec.x, spec.z, spec.radius);
12842
13033
  }
12843
13034
  function addBloodSplat(store, ground, random, spec) {
@@ -13393,6 +13584,7 @@ var GameTestRoom = (() => {
13393
13584
  state.gas = Math.max(0, state.gas - dt / DECAY.gasSeconds);
13394
13585
  state.beat *= Math.exp(-DECAY.beat * dt);
13395
13586
  }
13587
+ var PAD_OF_SIZE2 = 0.35;
13396
13588
  var clamp4 = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
13397
13589
  var lerp4 = (a, b, t) => a + (b - a) * t;
13398
13590
  function rgb(hex) {
@@ -13552,9 +13744,20 @@ var GameTestRoom = (() => {
13552
13744
  vy2 *= k;
13553
13745
  vz2 *= k;
13554
13746
  }
13555
- const x = this.position[i3] + vx2 * dt;
13747
+ let x = this.position[i3] + vx2 * dt;
13556
13748
  let y = this.position[i3 + 1] + vy2 * dt;
13557
- const z = this.position[i3 + 2] + vz2 * dt;
13749
+ let z = this.position[i3 + 2] + vz2 * dt;
13750
+ const confiner = host.confiner;
13751
+ if (def.carried && confiner && !confiner.empty) {
13752
+ const pad2 = this.size0[i] * lerp4(1, def.grow, t) * PAD_OF_SIZE2;
13753
+ if (confiner.confine(this.position[i3], this.position[i3 + 1], this.position[i3 + 2], x, y, z, pad2, dt)) {
13754
+ const out = confiner.out;
13755
+ x = out.x;
13756
+ y = out.y;
13757
+ z = out.z;
13758
+ [vx2, vy2, vz2] = slideAlong(out, vx2, vy2, vz2);
13759
+ }
13760
+ }
13558
13761
  if (def.ground !== "none") {
13559
13762
  const own = this.floor[i];
13560
13763
  const gy = Number.isNaN(own) ? host.groundAt(x, z) + 5e-3 : own;
@@ -13640,7 +13843,8 @@ var GameTestRoom = (() => {
13640
13843
  alpha: 0.35,
13641
13844
  fadeIn: 0.15,
13642
13845
  cone: 0.6,
13643
- ground: "none"
13846
+ ground: "none",
13847
+ carried: true
13644
13848
  },
13645
13849
  sparks: {
13646
13850
  id: "sparks",
@@ -13684,7 +13888,8 @@ var GameTestRoom = (() => {
13684
13888
  alpha: 0.4,
13685
13889
  fadeIn: 0.1,
13686
13890
  cone: 0.4,
13687
- ground: "none"
13891
+ ground: "none",
13892
+ carried: true
13688
13893
  },
13689
13894
  shards: {
13690
13895
  id: "shards",
@@ -13842,7 +14047,8 @@ var GameTestRoom = (() => {
13842
14047
  fadeIn: 0.1,
13843
14048
  cone: 0.5,
13844
14049
  ground: "stop",
13845
- flutter: 0.6
14050
+ flutter: 0.6,
14051
+ carried: true
13846
14052
  },
13847
14053
  steam: {
13848
14054
  id: "steam",
@@ -13864,7 +14070,8 @@ var GameTestRoom = (() => {
13864
14070
  alpha: 0.35,
13865
14071
  fadeIn: 0.15,
13866
14072
  cone: 0.6,
13867
- ground: "none"
14073
+ ground: "none",
14074
+ carried: true
13868
14075
  },
13869
14076
  flash: {
13870
14077
  id: "flash",
@@ -14005,7 +14212,8 @@ var GameTestRoom = (() => {
14005
14212
  const host = {
14006
14213
  groundAt: this.options.groundAt,
14007
14214
  gravity: this.options.gravity,
14008
- land: (owner, x, y, z, color, tag) => this.listeners.get(owner)?.(x, y, z, color, tag)
14215
+ land: (owner, x, y, z, color, tag) => this.listeners.get(owner)?.(x, y, z, color, tag),
14216
+ ...this.options.confiner ? { confiner: this.options.confiner } : {}
14009
14217
  };
14010
14218
  if (this.additive.live > 0 || this.additive.drawCount > 0) this.additive.update(dt, host);
14011
14219
  if (this.normal.live > 0 || this.normal.drawCount > 0) this.normal.update(dt, host);
@@ -14211,8 +14419,8 @@ var GameTestRoom = (() => {
14211
14419
  * Takes away the marks inside a box: world `center`, `half` extents along the axes of the world `rotation` (a quaternion, x y z w; none means the
14212
14420
  * world axes). The surface they were on is gone (a brick came out of a wall with its bullet holes). Returns how many went.
14213
14421
  */
14214
- clearInside(center, half, rotation = [0, 0, 0, 1]) {
14215
- const qx = -rotation[0], qy = -rotation[1], qz = -rotation[2], qw = rotation[3];
14422
+ clearInside(center, half, rotation2 = [0, 0, 0, 1]) {
14423
+ const qx = -rotation2[0], qy = -rotation2[1], qz = -rotation2[2], qw = rotation2[3];
14216
14424
  let gone = 0;
14217
14425
  for (const decal2 of this.decals) {
14218
14426
  if (!decal2.on) continue;
@@ -14333,10 +14541,14 @@ var GameTestRoom = (() => {
14333
14541
  var DEFAULT_FADE_IN = 0.1;
14334
14542
  var FLOATS_PER_VECTOR = 3;
14335
14543
  var BYTE_MAX2 = 255;
14544
+ var CARRIED_GRAVITY = 3;
14545
+ var PAD_OF_SIZE3 = 0.35;
14336
14546
  var SpritePool = class {
14337
- constructor(capacity, context) {
14547
+ /** `carried`: the normal-blend pool (smoke, dust, steam), whose light sprites go round the closed spaces; the additive one (flames, sparks) does not. */
14548
+ constructor(capacity, context, carried = false) {
14338
14549
  __publicField(this, "capacity", capacity);
14339
14550
  __publicField(this, "context", context);
14551
+ __publicField(this, "carried", carried);
14340
14552
  __publicField(this, "count", 0);
14341
14553
  __publicField(this, "position");
14342
14554
  __publicField(this, "velocity");
@@ -14436,6 +14648,7 @@ var GameTestRoom = (() => {
14436
14648
  }
14437
14649
  update(dt, leaveDecal) {
14438
14650
  const { position, velocity } = this;
14651
+ const confiner = this.context.confiner;
14439
14652
  for (let index = this.count - 1; index >= 0; index--) {
14440
14653
  this.life[index] -= dt;
14441
14654
  if (this.life[index] <= 0) {
@@ -14449,9 +14662,25 @@ var GameTestRoom = (() => {
14449
14662
  velocity[offset] *= factor;
14450
14663
  velocity[offset + 1] *= factor;
14451
14664
  velocity[offset + 2] *= factor;
14665
+ const x0 = position[offset];
14666
+ const y0 = position[offset + 1];
14667
+ const z0 = position[offset + 2];
14452
14668
  position[offset] += velocity[offset] * dt;
14453
14669
  position[offset + 1] += velocity[offset + 1] * dt;
14454
14670
  position[offset + 2] += velocity[offset + 2] * dt;
14671
+ if (this.carried && confiner && !confiner.empty && Math.abs(this.gravity[index]) < CARRIED_GRAVITY) {
14672
+ const size = this.startSize[index] * (1 + (this.growth[index] - 1) * age);
14673
+ if (confiner.confine(x0, y0, z0, position[offset], position[offset + 1], position[offset + 2], size * PAD_OF_SIZE3, dt)) {
14674
+ const out = confiner.out;
14675
+ position[offset] = out.x;
14676
+ position[offset + 1] = out.y;
14677
+ position[offset + 2] = out.z;
14678
+ const [vx2, vy2, vz2] = slideAlong(out, velocity[offset], velocity[offset + 1], velocity[offset + 2]);
14679
+ velocity[offset] = vx2;
14680
+ velocity[offset + 1] = vy2;
14681
+ velocity[offset + 2] = vz2;
14682
+ }
14683
+ }
14455
14684
  const mode = this.groundMode[index];
14456
14685
  if (mode) {
14457
14686
  const groundHeight = this.context.groundAt(position[offset], position[offset + 2]);
@@ -14728,9 +14957,9 @@ var GameTestRoom = (() => {
14728
14957
  __publicField(this, "placement");
14729
14958
  __publicField(this, "accumulators", /* @__PURE__ */ new Map());
14730
14959
  const profile = options.profile ?? PROFILES.high;
14731
- this.context = { random: options.random, groundAt: options.groundAt ?? workshopGround, quality: { level: 0, mult: 1 }, trace: options.trace };
14960
+ this.context = { random: options.random, groundAt: options.groundAt ?? workshopGround, quality: { level: 0, mult: 1 }, trace: options.trace, ...options.confiner ? { confiner: options.confiner } : {} };
14732
14961
  this.add = new SpritePool(profile.sprites.add, this.context);
14733
- this.normal = new SpritePool(profile.sprites.normal, this.context);
14962
+ this.normal = new SpritePool(profile.sprites.normal, this.context, true);
14734
14963
  this.streaks = new StreakPool(profile.streaks, this.context);
14735
14964
  this.debris = new DebrisPool(this.context);
14736
14965
  this.decals = new DecalPool(this.context);
@@ -14918,12 +15147,14 @@ var GameTestRoom = (() => {
14918
15147
  const groundAt = options.groundAt ?? (ground ? (x, z) => ground.heightAt(x, z) : () => 0);
14919
15148
  const handles = { next: 0, events: 0 };
14920
15149
  const low = options.profile === "low";
15150
+ const enclosures = new Enclosures(20973 + (options.seed ?? 0));
14921
15151
  const spriteSystem = new SpriteKindSystem({
14922
15152
  capacity: options.sprites?.capacity ?? (low ? 1250 : 2500),
14923
15153
  density: options.sprites?.density ?? (low ? 0.6 : 1),
14924
15154
  seed: 2654435761 + (options.seed ?? 0),
14925
15155
  groundAt,
14926
15156
  gravity: options.gravity ?? DEFAULT_GRAVITY,
15157
+ confiner: enclosures,
14927
15158
  ...options.sprites?.kinds ? { kinds: options.sprites.kinds } : {}
14928
15159
  });
14929
15160
  const core = {
@@ -14973,7 +15204,7 @@ var GameTestRoom = (() => {
14973
15204
  return [camera2.x, camera2.y, camera2.z];
14974
15205
  }, forward: (local2) => [local2[0] ?? 0, local2[1] ?? 0, local2[2] ?? 0] };
14975
15206
  function workshop() {
14976
- return engine ?? (engine = new WorkshopEngine({ random: sharedRandom, profile, groundAt }));
15207
+ return engine ?? (engine = new WorkshopEngine({ random: sharedRandom, profile, groundAt, confiner: enclosures }));
14977
15208
  }
14978
15209
  function effectPlayer() {
14979
15210
  return player ?? (player = createEffectPlayer({ engine: workshop(), effects, view }));
@@ -15032,6 +15263,10 @@ var GameTestRoom = (() => {
15032
15263
  puff3.vz += dz * strength;
15033
15264
  }
15034
15265
  },
15266
+ enclose(id, enclosure) {
15267
+ enclosures.set(id, enclosure);
15268
+ },
15269
+ enclosures: () => enclosures,
15035
15270
  stop(handle) {
15036
15271
  player?.stop(handle);
15037
15272
  const fires = core.fires;
@@ -15059,7 +15294,7 @@ var GameTestRoom = (() => {
15059
15294
  core.clock.time += dt;
15060
15295
  core.schedule.update(core.clock.time);
15061
15296
  updateRings(core.rings, dt);
15062
- updatePuffs(core.puffs, dt, wind);
15297
+ updatePuffs(core.puffs, dt, wind, enclosures);
15063
15298
  updateSparks(core.sparks, { dt, groundAt });
15064
15299
  updateGlows(core.glows, dt);
15065
15300
  updateShocks(core.shocks, core.puffs.items, dt);
@@ -16459,6 +16694,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
16459
16694
  });
16460
16695
  },
16461
16696
  stop: (handle) => world.stop(handle),
16697
+ // a module's closed space (a shut car's cabin): the world's smoke, dust and steam go round it
16698
+ enclose: (id, enclosure) => world.enclose(id, enclosure),
16462
16699
  configure(settings) {
16463
16700
  const band = settings.lod_band;
16464
16701
  if (typeof band === "string" && band in LOD_BANDS) world.forceLod(LOD_BANDS[band] ?? null);
@@ -18389,8 +18626,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
18389
18626
  * Takes away the decals of a mesh that sit inside a box (world `center`, `half` extents along the axes of the world `rotation`): the surface they
18390
18627
  * marked is gone (a unit came out of the wall with its bullet holes and spalls).
18391
18628
  */
18392
- clearDecals(mv, center, half, rotation) {
18393
- const inv = rotation.clone().invert();
18629
+ clearDecals(mv, center, half, rotation2) {
18630
+ const inv = rotation2.clone().invert();
18394
18631
  const p = new this.THREE.Vector3();
18395
18632
  for (let i = mv.decals.length - 1; i >= 0; i--) {
18396
18633
  const d = mv.decals[i];
@@ -19820,8 +20057,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
19820
20057
  const T = this.THREE;
19821
20058
  const center = new T.Vector3(ev.center[0], ev.center[1], ev.center[2]);
19822
20059
  const half = new T.Vector3(ev.size[0] / 2 + CHIP_DECAL_EDGE, ev.size[1] / 2 + CHIP_DECAL_EDGE, ev.size[2] / 2 + CHIP_DECAL_EDGE);
19823
- const rotation = new T.Quaternion(ev.rotation[0], ev.rotation[1], ev.rotation[2], ev.rotation[3]);
19824
- for (const part of this.bounds.get(ev.object)?.parts.values() ?? []) for (const mv of part.meshes) this.visuals.clearDecals(mv, center, half, rotation);
20060
+ const rotation2 = new T.Quaternion(ev.rotation[0], ev.rotation[1], ev.rotation[2], ev.rotation[3]);
20061
+ for (const part of this.bounds.get(ev.object)?.parts.values() ?? []) for (const mv of part.meshes) this.visuals.clearDecals(mv, center, half, rotation2);
19825
20062
  this.vfx.peekWorkshop()?.decals.clearInside(ev.center, [half.x, half.y, half.z], ev.rotation);
19826
20063
  this.vfx.burst("dust", ev.center, [0, 1, 0], 8, 0.8, { color });
19827
20064
  this.sound(impactKey(def), ev.center, 0.7);
@@ -36139,11 +36376,11 @@ ${problems.map((problem) => ` - ${problem}`).join("\n")}`);
36139
36376
  for (const key2 of Object.keys(c.streams)) endStreamKey(c, key2);
36140
36377
  containers.delete(id);
36141
36378
  },
36142
- setTransform(id, position, rotation, velocity) {
36379
+ setTransform(id, position, rotation2, velocity) {
36143
36380
  const c = containers.get(id);
36144
36381
  if (!c) return;
36145
36382
  c.position = copy3(position);
36146
- c.rotation = [...rotation];
36383
+ c.rotation = [...rotation2];
36147
36384
  if (velocity) c.velocity = copy3(velocity);
36148
36385
  refreshLevel(c);
36149
36386
  },
@@ -37065,10 +37302,10 @@ ${problems.map((problem) => ` - ${problem}`).join("\n")}`);
37065
37302
  const hand = getHandTransform();
37066
37303
  const p = pose();
37067
37304
  const position = add2(hand.position, quatRotate(hand.rotation, p.position));
37068
- const rotation = quatMultiply(hand.rotation, quatFromEuler2(p.rotation[0], p.rotation[1], p.rotation[2]));
37305
+ const rotation2 = quatMultiply(hand.rotation, quatFromEuler2(p.rotation[0], p.rotation[1], p.rotation[2]));
37069
37306
  const velocity = lastPos && dt > 0 ? [(position[0] - lastPos[0]) / dt, (position[1] - lastPos[1]) / dt, (position[2] - lastPos[2]) / dt] : void 0;
37070
37307
  lastPos = position;
37071
- world.setTransform(containerId, position, rotation, velocity);
37308
+ world.setTransform(containerId, position, rotation2, velocity);
37072
37309
  };
37073
37310
  const player = {
37074
37311
  def,
@@ -39097,7 +39334,7 @@ ${problems.map((problem) => ` - ${problem}`).join("\n")}`);
39097
39334
  const direction = new THREE.Vector3();
39098
39335
  const location = new THREE.Vector3();
39099
39336
  const scale2 = new THREE.Vector3();
39100
- const rotation = new THREE.Quaternion();
39337
+ const rotation2 = new THREE.Quaternion();
39101
39338
  const matrix = new THREE.Matrix4();
39102
39339
  return {
39103
39340
  drops,
@@ -39117,9 +39354,9 @@ ${problems.map((problem) => ` - ${problem}`).join("\n")}`);
39117
39354
  const o = i * 3;
39118
39355
  const speed = Math.hypot(velocity[o], velocity[o + 1], velocity[o + 2]);
39119
39356
  direction.set(velocity[o], velocity[o + 1], velocity[o + 2]).normalize();
39120
- rotation.setFromUnitVectors(up, direction);
39357
+ rotation2.setFromUnitVectors(up, direction);
39121
39358
  const size = radius[i] * METRES_PER_MM;
39122
- matrix.compose(location.set(position[o], position[o + 1], position[o + 2]), rotation, scale2.set(size, size * (1 + Math.min(MAX_STRETCH, speed * STRETCH_PER_SPEED)), size));
39359
+ matrix.compose(location.set(position[o], position[o + 1], position[o + 2]), rotation2, scale2.set(size, size * (1 + Math.min(MAX_STRETCH, speed * STRETCH_PER_SPEED)), size));
39123
39360
  drops.setMatrixAt(i, matrix);
39124
39361
  drawn++;
39125
39362
  }
@@ -42661,7 +42898,7 @@ void main(){
42661
42898
  var NEAR_OPEN_LEAF = 0.9;
42662
42899
  var BODY_HEIGHT = 1.8;
42663
42900
  var HAND_REST = [0.22, -0.42, -0.32];
42664
- var SKIN = 0.01;
42901
+ var SKIN2 = 0.01;
42665
42902
  var HANDLE_INSET = 0.06;
42666
42903
  var STANDOFF2 = 0.55;
42667
42904
  var DRAWER_TRAVEL = 0.3;
@@ -42805,7 +43042,7 @@ void main(){
42805
43042
  const o = outs[0];
42806
43043
  return [o[1] * (o[0] + PLAYER_RADIUS), o[2] * (o[0] + PLAYER_RADIUS)];
42807
43044
  }
42808
- const push = PLAYER_RADIUS - d + SKIN;
43045
+ const push = PLAYER_RADIUS - d + SKIN2;
42809
43046
  return [dx / d * push, dz / d * push];
42810
43047
  };
42811
43048
  const isFree = (u, from, to) => {
@@ -42815,7 +43052,7 @@ void main(){
42815
43052
  options.pose(objectId, leaf2.part, leaf2.clip, to);
42816
43053
  obj.updateMatrixWorld(true);
42817
43054
  box2.setFromObject(obj);
42818
- box2.expandByScalar(-SKIN);
43055
+ box2.expandByScalar(-SKIN2);
42819
43056
  let by = null;
42820
43057
  if (Math.max(Math.abs(box2.min.x), Math.abs(box2.max.x), Math.abs(box2.min.z), Math.abs(box2.max.z)) > arena.limit) by = "wall";
42821
43058
  else
@@ -42833,7 +43070,7 @@ void main(){
42833
43070
  const dir = [tmp.x - centre.x, tmp.y - centre.y, tmp.z - centre.z];
42834
43071
  const len = Math.hypot(dir[0], dir[1], dir[2]);
42835
43072
  if (len > 1e-5) {
42836
- const hit = world.sweepBox({ min: [before.min.x + SKIN, before.min.y + FLOOR_GAP, before.min.z + SKIN], max: [before.max.x - SKIN, before.max.y - SKIN, before.max.z - SKIN] }, dir, len * 2, { ignore: objectId, spacing: 0.12 });
43073
+ const hit = world.sweepBox({ min: [before.min.x + SKIN2, before.min.y + FLOOR_GAP, before.min.z + SKIN2], max: [before.max.x - SKIN2, before.max.y - SKIN2, before.max.z - SKIN2] }, dir, len * 2, { ignore: objectId, spacing: 0.12 });
42837
43074
  if (hit && hit.normal[1] < 0.7) by = hit.object;
42838
43075
  }
42839
43076
  }
@@ -43121,6 +43358,7 @@ void main(){
43121
43358
  debris: "chips",
43122
43359
  color: [0.05, 0.05, 0.055],
43123
43360
  pattern: "asphalt",
43361
+ crack: { reach: 0.6, width: 8e-3, thresholdJoules: 800 },
43124
43362
  hits: {
43125
43363
  ballistic: { specificEnergy: 3e7, aspect: 0.2, minRadius: 0.03 },
43126
43364
  blunt: { specificEnergy: 1e8, aspect: 0.1, thresholdJoules: 400 },
@@ -43134,6 +43372,8 @@ void main(){
43134
43372
  debris: "chips",
43135
43373
  color: [0.33, 0.33, 0.31],
43136
43374
  pattern: "concrete",
43375
+ crack: { reach: 1, width: 6e-3, thresholdJoules: 300 },
43376
+ breach: { joulesPerCubedThickness: 24e8, radiusOverThickness: 1.5 },
43137
43377
  hits: {
43138
43378
  ballistic: { specificEnergy: 15e7, aspect: 0.14, minRadius: 0.018 },
43139
43379
  blunt: { specificEnergy: 3e8, aspect: 0.08, thresholdJoules: 800 },
@@ -43160,6 +43400,7 @@ void main(){
43160
43400
  debris: "shards",
43161
43401
  color: [0.72, 0.7, 0.66],
43162
43402
  pattern: "tile",
43403
+ crack: { reach: 0.5, width: 2e-3, thresholdJoules: 40 },
43163
43404
  hits: {
43164
43405
  ballistic: { specificEnergy: 8e6, aspect: 0.1, minRadius: 0.05 },
43165
43406
  blunt: { specificEnergy: 1e7, aspect: 0.08, thresholdJoules: 80, minRadius: 0.06 },
@@ -43186,6 +43427,7 @@ void main(){
43186
43427
  debris: "chips",
43187
43428
  color: [0.23, 0.22, 0.2],
43188
43429
  pattern: "stone",
43430
+ crack: { reach: 0.5, width: 4e-3, thresholdJoules: 1e3 },
43189
43431
  hits: {
43190
43432
  ballistic: { specificEnergy: 25e7, aspect: 0.12, minRadius: 0.015 },
43191
43433
  blunt: { specificEnergy: 5e8, aspect: 0.08, thresholdJoules: 1500 },
@@ -43208,6 +43450,91 @@ void main(){
43208
43450
  ];
43209
43451
  var DEFAULT_BAR_YIELD_JOULES = 24e5;
43210
43452
  var DEFAULT_PROFILE = "concrete_slab";
43453
+ function createRandom(seed) {
43454
+ let state = seed | 0;
43455
+ return () => {
43456
+ state = state + 1831565813 | 0;
43457
+ let mixed = Math.imul(state ^ state >>> 15, 1 | state);
43458
+ mixed = mixed + Math.imul(mixed ^ mixed >>> 7, 61 | mixed) ^ mixed;
43459
+ return ((mixed ^ mixed >>> 14) >>> 0) / 4294967296;
43460
+ };
43461
+ }
43462
+ var CRACK_REFERENCE_JOULES = 1e6;
43463
+ var MERGE_SHARE = 0.35;
43464
+ var HAIRLINE = 15e-4;
43465
+ var MIN_RAYS = 4;
43466
+ var MAX_RAYS = 11;
43467
+ var SEGMENTS2 = 4;
43468
+ var BRANCH_CHANCE = 0.45;
43469
+ function createCrackStore() {
43470
+ const list = [];
43471
+ let version = 0;
43472
+ let nextId = 1;
43473
+ let born = 0;
43474
+ return {
43475
+ list,
43476
+ get version() {
43477
+ return version;
43478
+ },
43479
+ add(spec, max2) {
43480
+ version++;
43481
+ const near = list.find((crack2) => Math.hypot(crack2.x - spec.x, crack2.z - spec.z) < crack2.reach * MERGE_SHARE);
43482
+ if (near) {
43483
+ near.reach = Math.max(near.reach, spec.reach) + Math.min(near.reach, spec.reach) * 0.25;
43484
+ near.width = Math.max(near.width, spec.width) * 1.1;
43485
+ near.rays = Math.min(MAX_RAYS, near.rays + 1);
43486
+ return near;
43487
+ }
43488
+ if (list.length >= max2) list.shift();
43489
+ const rays = MIN_RAYS + Math.floor(createRandom(spec.seed)() * (MAX_RAYS - MIN_RAYS - 2));
43490
+ const crack = { id: nextId++, x: spec.x, z: spec.z, reach: spec.reach, width: Math.max(HAIRLINE, spec.width), rays, seed: spec.seed, material: spec.material, born: born++ };
43491
+ list.push(crack);
43492
+ return crack;
43493
+ },
43494
+ clear() {
43495
+ list.length = 0;
43496
+ version++;
43497
+ },
43498
+ load(cracks) {
43499
+ list.length = 0;
43500
+ for (const crack of cracks) {
43501
+ list.push({ ...crack });
43502
+ nextId = Math.max(nextId, crack.id + 1);
43503
+ born = Math.max(born, crack.born + 1);
43504
+ }
43505
+ version++;
43506
+ }
43507
+ };
43508
+ }
43509
+ function crackLines(crack) {
43510
+ const random = createRandom(crack.seed);
43511
+ const segments = [];
43512
+ const widthAt = (distance4) => Math.max(HAIRLINE, crack.width * (1 - distance4 / crack.reach));
43513
+ for (let ray = 0; ray < crack.rays; ray++) {
43514
+ let angle = ray / crack.rays * Math.PI * 2 + (random() - 0.5) * 0.6;
43515
+ const length3 = crack.reach * (0.45 + 0.55 * random());
43516
+ let x = crack.x;
43517
+ let z = crack.z;
43518
+ let run = 0;
43519
+ for (let segment = 0; segment < SEGMENTS2; segment++) {
43520
+ const step = length3 / SEGMENTS2 * (0.75 + 0.5 * random());
43521
+ angle += (random() - 0.5) * 0.5;
43522
+ const nx = x + Math.cos(angle) * step;
43523
+ const nz = z + Math.sin(angle) * step;
43524
+ segments.push({ x0: x, z0: z, x1: nx, z1: nz, w0: widthAt(run), w1: widthAt(run + step) });
43525
+ if (segment > 0 && random() < BRANCH_CHANCE) {
43526
+ const side = angle + (random() < 0.5 ? -1 : 1) * (0.45 + random() * 0.5);
43527
+ const branch = step * (0.5 + 0.6 * random());
43528
+ const w = widthAt(run + step) * 0.6;
43529
+ segments.push({ x0: nx, z0: nz, x1: nx + Math.cos(side) * branch, z1: nz + Math.sin(side) * branch, w0: Math.max(HAIRLINE, w), w1: HAIRLINE });
43530
+ }
43531
+ run += step;
43532
+ x = nx;
43533
+ z = nz;
43534
+ }
43535
+ }
43536
+ return segments;
43537
+ }
43211
43538
  var REST_SPEED2 = 0.6;
43212
43539
  var RESTITUTION2 = 0.25;
43213
43540
  var FRICTION = 0.55;
@@ -43385,15 +43712,6 @@ void main(){
43385
43712
  }
43386
43713
  };
43387
43714
  }
43388
- function createRandom(seed) {
43389
- let state = seed | 0;
43390
- return () => {
43391
- state = state + 1831565813 | 0;
43392
- let mixed = Math.imul(state ^ state >>> 15, 1 | state);
43393
- mixed = mixed + Math.imul(mixed ^ mixed >>> 7, 61 | mixed) ^ mixed;
43394
- return ((mixed ^ mixed >>> 14) >>> 0) / 4294967296;
43395
- };
43396
- }
43397
43715
  var DEFAULT_TERRAIN_BUDGET = {
43398
43716
  markAreaSize: 2,
43399
43717
  maxMarksPerArea: 24,
@@ -43402,7 +43720,8 @@ void main(){
43402
43720
  maxDebris: 400,
43403
43721
  maxDebrisPerBlow: 36,
43404
43722
  maxSlideCellsPerFrame: 2500,
43405
- maxBurningCells: 300
43723
+ maxBurningCells: 300,
43724
+ maxCracks: 120
43406
43725
  };
43407
43726
  var GRAVITY5 = 9.81;
43408
43727
  var FINE_CELLS = 1.5;
@@ -43479,6 +43798,7 @@ void main(){
43479
43798
  const mixed = new Uint8Array(chunksX * chunksZ);
43480
43799
  const dirty = /* @__PURE__ */ new Set();
43481
43800
  const marks = createMarkStore2(budget);
43801
+ const cracks = createCrackStore();
43482
43802
  const debrisStore = createDebrisStore(() => budget.maxDebris);
43483
43803
  const sliding = /* @__PURE__ */ new Set();
43484
43804
  const burning = /* @__PURE__ */ new Map();
@@ -43655,6 +43975,7 @@ void main(){
43655
43975
  let depth = start.depth;
43656
43976
  let radius = 0;
43657
43977
  let carved = 0;
43978
+ let breached = false;
43658
43979
  const firstMaterial = materials.get(start.material);
43659
43980
  if (start.loose <= 0) {
43660
43981
  for (; ; ) {
@@ -43666,6 +43987,19 @@ void main(){
43666
43987
  const response = material.hits[blow3];
43667
43988
  if (!response || energy < (response.thresholdJoules ?? 0)) break;
43668
43989
  const bars = layer2.stratum.reinforcement;
43990
+ const breach = blow3 === "explosive" ? material.breach : void 0;
43991
+ const breachJoules = breach ? breach.joulesPerCubedThickness * layer2.stratum.thickness ** 3 : Infinity;
43992
+ if (breach && depth <= layer2.top + 1e-6 && energy >= breachJoules) {
43993
+ const takenRadius2 = layer2.stratum.thickness * breach.radiusOverThickness * Math.cbrt(energy / breachJoules);
43994
+ const takenVolume2 = Math.PI * takenRadius2 * takenRadius2 * room / 2;
43995
+ energy -= breachJoules;
43996
+ depth += room;
43997
+ carved += room;
43998
+ breached = true;
43999
+ radius = Math.max(radius, takenRadius2);
44000
+ removed.push({ material: material.id, volume: takenVolume2 });
44001
+ continue;
44002
+ }
43669
44003
  const pastCover = bars !== void 0 && depth - layer2.top >= bars.cover - 1e-6;
43670
44004
  const specificEnergy = response.specificEnergy * (pastCover ? bars.resistance : 1);
43671
44005
  const limit = bars && !pastCover ? Math.min(room, layer2.top + bars.cover - depth) : room;
@@ -43693,8 +44027,10 @@ void main(){
43693
44027
  removed.push({ material: firstMaterial.id, volume });
43694
44028
  }
43695
44029
  }
43696
- const collapsed = loadBars(blow3, spec.energy, x, z, profile);
43697
- if (radius <= 0) return { point, radius: 0, depth: 0, totalDepth: columnAt(x, z).depth, removed: [], exposed: start.material, atLimit: start.remaining <= 1e-6, rebarExposed: rebarShowing(profile, start.depth), collapsed, fine: true };
44030
+ let collapsed = loadBars(blow3, spec.energy, x, z, profile);
44031
+ const crack = crackAround(blow3, spec.energy, x, z, start.loose > 0 ? void 0 : firstMaterial);
44032
+ const extra = { ...crack ? { crack } : {}, ...breached ? { breached } : {} };
44033
+ if (radius <= 0) return { point, radius: 0, depth: 0, totalDepth: columnAt(x, z).depth, removed: [], exposed: start.material, atLimit: start.remaining <= 1e-6, rebarExposed: rebarShowing(profile, start.depth), collapsed, fine: true, ...extra };
43698
44034
  if (firstMaterial.reposeDegrees === void 0 && start.loose <= 0) radius *= 1 + RIGID_WIDENING * (start.depth / maxDepthOfProfile(profile));
43699
44035
  const volumeTotal = removed.reduce((sum2, entry) => sum2 + entry.volume, 0);
43700
44036
  const fine = radius < cellSize * FINE_CELLS;
@@ -43712,8 +44048,16 @@ void main(){
43712
44048
  throwPieces({ x, y: point[1], z, volume: thrown, material, energy: spec.energy * (entry.volume / volumeTotal), direction: spec.direction, quota: share, bowlRadius: radius, blow: blow3 });
43713
44049
  quota -= share;
43714
44050
  }
44051
+ if (breached) collapsed = loadBars(blow3, spec.energy, x, z, profile) || collapsed;
43715
44052
  const after2 = columnAt(x, z);
43716
- return { point, radius, depth: carved, totalDepth: after2.depth, removed, exposed: after2.material, atLimit: after2.remaining <= 1e-4, rebarExposed: rebarShowing(profile, after2.depth), collapsed, fine };
44053
+ return { point, radius, depth: carved, totalDepth: after2.depth, removed, exposed: after2.material, atLimit: after2.remaining <= 1e-4, rebarExposed: rebarShowing(profile, after2.depth), collapsed, fine, ...extra };
44054
+ }
44055
+ function crackAround(blow3, energy, x, z, material) {
44056
+ const response = material?.crack;
44057
+ if (!material || !response || energy < response.thresholdJoules) return void 0;
44058
+ if (blow3 === "slash") return void 0;
44059
+ const scale2 = Math.cbrt(energy / CRACK_REFERENCE_JOULES);
44060
+ return cracks.add({ x, z, reach: response.reach * scale2, width: response.width * scale2, material: material.id, seed: Math.floor(random() * 2 ** 31) }, budget.maxCracks);
43717
44061
  }
43718
44062
  function loadBars(blow3, energy, x, z, profile) {
43719
44063
  const bars = profile.strata.find((stratum) => stratum.reinforcement);
@@ -43954,6 +44298,7 @@ void main(){
43954
44298
  barBend.fill(0);
43955
44299
  touched.fill(0);
43956
44300
  marks.clear();
44301
+ cracks.clear();
43957
44302
  debrisStore.clear();
43958
44303
  sliding.clear();
43959
44304
  burning.clear();
@@ -44005,6 +44350,9 @@ void main(){
44005
44350
  updateSlides();
44006
44351
  updateFire(dt);
44007
44352
  },
44353
+ get dirtyChunkCount() {
44354
+ return dirty.size;
44355
+ },
44008
44356
  drainDirtyChunks(max2 = budget.maxDirtyChunksPerFrame) {
44009
44357
  const taken = [];
44010
44358
  for (const index of dirty) {
@@ -44016,6 +44364,10 @@ void main(){
44016
44364
  },
44017
44365
  chunk,
44018
44366
  marks: () => marks.list,
44367
+ cracks: () => cracks.list,
44368
+ get cracksVersion() {
44369
+ return cracks.version;
44370
+ },
44019
44371
  debris: () => debrisStore.list,
44020
44372
  burningPoints: () => [...burning.keys()].map((key2) => {
44021
44373
  const cellRow = Math.floor(key2 / 65536);
@@ -44028,7 +44380,7 @@ void main(){
44028
44380
  stats: () => ({ marks: marks.list.length, debris: debrisStore.list.length, debrisMoving: debrisStore.list.filter((piece) => !piece.atRest).length, dirtyChunks: dirty.size, burning: burning.size }),
44029
44381
  reset,
44030
44382
  serialize() {
44031
- return { layout: currentLayout, dug: sparse(dug), loose: sparse(loose), scorch: sparse(scorch), barBend: sparse(barBend), marks: marks.list.map((mark) => ({ ...mark })) };
44383
+ return { layout: currentLayout, dug: sparse(dug), loose: sparse(loose), scorch: sparse(scorch), barBend: sparse(barBend), marks: marks.list.map((mark) => ({ ...mark })), cracks: cracks.list.map((crack) => ({ ...crack })) };
44032
44384
  },
44033
44385
  load(snapshot) {
44034
44386
  currentLayout = snapshot.layout;
@@ -44038,6 +44390,7 @@ void main(){
44038
44390
  fillSparse(scorch, snapshot.scorch);
44039
44391
  fillSparse(barBend, snapshot.barBend ?? []);
44040
44392
  marks.load(snapshot.marks);
44393
+ cracks.load(snapshot.cracks ?? []);
44041
44394
  for (let chunkIndex = 0; chunkIndex < touched.length; chunkIndex++) markChunk(chunkIndex);
44042
44395
  }
44043
44396
  };
@@ -44382,7 +44735,7 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
44382
44735
  mesh2.renderOrder = 1;
44383
44736
  mesh2.setColorAt(0, new THREE.Color(1, 1, 1));
44384
44737
  const matrix = new THREE.Matrix4();
44385
- const rotation = new THREE.Matrix4();
44738
+ const rotation2 = new THREE.Matrix4();
44386
44739
  const scale2 = new THREE.Matrix4();
44387
44740
  const colour = new THREE.Color();
44388
44741
  let seen = "";
@@ -44403,9 +44756,9 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
44403
44756
  for (const mark of marks) {
44404
44757
  if (Math.hypot(mark.x - camera2.x, mark.z - camera2.z) > drawDistance) continue;
44405
44758
  const size = mark.radius * 2 * MARK_REACH;
44406
- rotation.makeRotationY(mark.id * 2.399);
44759
+ rotation2.makeRotationY(mark.id * 2.399);
44407
44760
  scale2.makeScale(size, 1, size);
44408
- matrix.multiplyMatrices(rotation, scale2);
44761
+ matrix.multiplyMatrices(rotation2, scale2);
44409
44762
  matrix.setPosition(mark.x, terrain.heightAt(mark.x, mark.z) + MARK_LIFT, mark.z);
44410
44763
  mesh2.setMatrixAt(count, matrix);
44411
44764
  const [r, g, b] = terrain.material(mark.material).color;
@@ -44469,6 +44822,92 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
44469
44822
  }
44470
44823
  };
44471
44824
  }
44825
+ var CRACK_LIFT = 4e-3;
44826
+ var MAX_CRACK_QUADS = 8192;
44827
+ var CRACK_SHADE = 0.22;
44828
+ var DARK_SURFACE = 0.1;
44829
+ var DUST_LIFT = 2;
44830
+ var DUST_GREY = 0.12;
44831
+ function createCrackLayer(THREE, terrain) {
44832
+ const positions = new Float32Array(MAX_CRACK_QUADS * 4 * 3);
44833
+ const colours = new Float32Array(MAX_CRACK_QUADS * 4 * 3);
44834
+ const indices = new Uint32Array(MAX_CRACK_QUADS * 6);
44835
+ for (let quad = 0; quad < MAX_CRACK_QUADS; quad++) {
44836
+ const first = quad * 4;
44837
+ indices.set([first, first + 2, first + 1, first + 1, first + 2, first + 3], quad * 6);
44838
+ }
44839
+ const geometry = new THREE.BufferGeometry();
44840
+ const position = new THREE.BufferAttribute(positions, 3);
44841
+ const colour = new THREE.BufferAttribute(colours, 3);
44842
+ position.setUsage(THREE.DynamicDrawUsage);
44843
+ colour.setUsage(THREE.DynamicDrawUsage);
44844
+ geometry.setAttribute("position", position);
44845
+ geometry.setAttribute("color", colour);
44846
+ geometry.setIndex(new THREE.BufferAttribute(indices, 1));
44847
+ geometry.setDrawRange(0, 0);
44848
+ const material = new THREE.MeshBasicMaterial({ vertexColors: true, transparent: true, opacity: 0.9, depthWrite: false, polygonOffset: true, polygonOffsetFactor: -3, polygonOffsetUnits: -3 });
44849
+ const mesh2 = new THREE.Mesh(geometry, material);
44850
+ mesh2.name = "terrain-cracks";
44851
+ mesh2.frustumCulled = false;
44852
+ mesh2.renderOrder = 2;
44853
+ let seen = -1;
44854
+ let groundMoved = false;
44855
+ const stopListening = terrain.onDeform(() => {
44856
+ groundMoved = true;
44857
+ });
44858
+ const lift = (x, z) => terrain.heightAt(x, z) + CRACK_LIFT;
44859
+ return {
44860
+ mesh: mesh2,
44861
+ sync() {
44862
+ const cracks = terrain.cracks();
44863
+ if (seen === terrain.cracksVersion && !(groundMoved && cracks.length > 0)) return;
44864
+ seen = terrain.cracksVersion;
44865
+ groundMoved = false;
44866
+ let quad = 0;
44867
+ for (let n = cracks.length - 1; n >= 0 && quad < MAX_CRACK_QUADS; n--) {
44868
+ const crack = cracks[n];
44869
+ const tone = terrain.material(crack.material).color;
44870
+ const dark = tone[0] * 0.3 + tone[1] * 0.59 + tone[2] * 0.11 < DARK_SURFACE;
44871
+ const k = dark ? DUST_LIFT : CRACK_SHADE;
44872
+ const r = dark ? Math.min(1, tone[0] * k + DUST_GREY) : tone[0] * k;
44873
+ const g = dark ? Math.min(1, tone[1] * k + DUST_GREY) : tone[1] * k;
44874
+ const b = dark ? Math.min(1, tone[2] * k + DUST_GREY) : tone[2] * k;
44875
+ for (const s of crackLines(crack)) {
44876
+ if (quad >= MAX_CRACK_QUADS) break;
44877
+ if (!terrain.contains(s.x0, s.z0) || !terrain.contains(s.x1, s.z1)) continue;
44878
+ const length3 = Math.hypot(s.x1 - s.x0, s.z1 - s.z0);
44879
+ if (length3 < 1e-5) continue;
44880
+ const ax = -(s.z1 - s.z0) / length3;
44881
+ const az = (s.x1 - s.x0) / length3;
44882
+ const corners = [
44883
+ [s.x0 + ax * s.w0 * 0.5, s.z0 + az * s.w0 * 0.5],
44884
+ [s.x0 - ax * s.w0 * 0.5, s.z0 - az * s.w0 * 0.5],
44885
+ [s.x1 + ax * s.w1 * 0.5, s.z1 + az * s.w1 * 0.5],
44886
+ [s.x1 - ax * s.w1 * 0.5, s.z1 - az * s.w1 * 0.5]
44887
+ ];
44888
+ corners.forEach(([x, z], corner) => {
44889
+ const at3 = (quad * 4 + corner) * 3;
44890
+ positions[at3] = x;
44891
+ positions[at3 + 1] = lift(x, z);
44892
+ positions[at3 + 2] = z;
44893
+ colours[at3] = r;
44894
+ colours[at3 + 1] = g;
44895
+ colours[at3 + 2] = b;
44896
+ });
44897
+ quad++;
44898
+ }
44899
+ }
44900
+ position.needsUpdate = true;
44901
+ colour.needsUpdate = true;
44902
+ geometry.setDrawRange(0, quad * 6);
44903
+ },
44904
+ dispose() {
44905
+ stopListening();
44906
+ geometry.dispose();
44907
+ material.dispose();
44908
+ }
44909
+ };
44910
+ }
44472
44911
  var BAR_RADIUS = 6e-3;
44473
44912
  var EXPOSED_MARGIN = 2e-3;
44474
44913
  var MAX_SAG = 0.07;
@@ -44583,7 +45022,8 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
44583
45022
  const drawn = /* @__PURE__ */ new Map();
44584
45023
  const marks = createMarkLayer2(THREE, terrain, options.markDistance ?? DEFAULT_MARK_DISTANCE);
44585
45024
  const debris2 = createDebrisLayer(THREE, terrain);
44586
- group.add(marks.mesh, debris2.mesh);
45025
+ const cracks = createCrackLayer(THREE, terrain);
45026
+ group.add(marks.mesh, cracks.mesh, debris2.mesh);
44587
45027
  let pristineStale = true;
44588
45028
  let pristineBuilt = false;
44589
45029
  let pristineCount = 0;
@@ -44669,7 +45109,8 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
44669
45109
  return {
44670
45110
  group,
44671
45111
  sync(camera2) {
44672
- const limit = firstSync ? Infinity : void 0;
45112
+ const laidAnew = terrain.dirtyChunkCount >= terrain.chunksX * terrain.chunksZ;
45113
+ const limit = firstSync || laidAnew ? Infinity : void 0;
44673
45114
  firstSync = false;
44674
45115
  for (const chunk of terrain.drainDirtyChunks(limit)) {
44675
45116
  if (chunk.pristine) {
@@ -44690,9 +45131,10 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
44690
45131
  }
44691
45132
  }
44692
45133
  marks.sync(camera2);
45134
+ cracks.sync();
44693
45135
  debris2.sync();
44694
45136
  },
44695
- stats: () => ({ chunkMeshes: drawn.size, pristineChunks: pristineCount, marksDrawn: marks.mesh.count, debrisDrawn: debris2.mesh.count }),
45137
+ stats: () => ({ chunkMeshes: drawn.size, pristineChunks: pristineCount, marksDrawn: marks.mesh.count, debrisDrawn: debris2.mesh.count, crackQuads: cracks.mesh.geometry.drawRange.count / 6 }),
44696
45138
  dispose() {
44697
45139
  for (const index of [...drawn.keys()]) drop(index);
44698
45140
  for (const geometry of spareGeometries) geometry.dispose();
@@ -44700,11 +45142,19 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
44700
45142
  surface.dispose();
44701
45143
  rebarMaterial.dispose();
44702
45144
  marks.dispose();
45145
+ cracks.dispose();
44703
45146
  debris2.dispose();
44704
45147
  group.removeFromParent();
44705
45148
  }
44706
45149
  };
44707
45150
  }
45151
+ var PROFILE_NAMES = {
45152
+ en: { lawn: "Lawn", dirt: "Dirt", sand: "Sand", road: "Asphalt road", concrete_slab: "Concrete slab", suspended_slab: "Slab over a void", tile_floor: "Tile floor", wood_floor: "Wood floor", stone_paving: "Stone paving" },
45153
+ "pt-BR": { lawn: "Grama", dirt: "Terra", sand: "Areia", road: "Asfalto", concrete_slab: "Laje de concreto", suspended_slab: "Laje sobre v\xE3o", tile_floor: "Piso cer\xE2mico", wood_floor: "Assoalho", stone_paving: "Cal\xE7amento de pedra" }
45154
+ };
45155
+ function terrainProfileName(profileId, localeId) {
45156
+ return (PROFILE_NAMES[localeId] ?? PROFILE_NAMES.en)[profileId] ?? profileId;
45157
+ }
44708
45158
  var TERRAIN_STRIPS = ["dirt", "lawn", "sand", "road", "concrete_slab", "suspended_slab", "wood_floor", "tile_floor", "stone_paving"];
44709
45159
  var STRIP_WIDTH = 3;
44710
45160
  var STRIP_Z0 = 4;
@@ -45087,6 +45537,8 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
45087
45537
  "ins.damage": "Marks",
45088
45538
  "ins.damage_counts": "{dents}, {holes}, {cracks}",
45089
45539
  "ins.mass": "Mass",
45540
+ "ins.dug": "Dug",
45541
+ "ins.dig_limit": "Ground left to dig",
45090
45542
  "ins.load": "Load",
45091
45543
  "ins.stress": "Stress",
45092
45544
  "ins.joints": "Joints",
@@ -45435,6 +45887,8 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
45435
45887
  "ins.damage": "Marcas",
45436
45888
  "ins.damage_counts": "{dents}, {holes}, {cracks}",
45437
45889
  "ins.mass": "Massa",
45890
+ "ins.dug": "Escavado",
45891
+ "ins.dig_limit": "Ch\xE3o at\xE9 o limite",
45438
45892
  "ins.load": "Carga",
45439
45893
  "ins.stress": "Tens\xE3o",
45440
45894
  "ins.joints": "Juntas",
@@ -46322,6 +46776,15 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
46322
46776
  insp.appendChild(mk3("div", void 0, t("ui.aim_at_something")));
46323
46777
  return;
46324
46778
  }
46779
+ if (i.ground) {
46780
+ const g = i.ground;
46781
+ insp.appendChild(mk3("div", "t", `${i.object} \u203A ${g.surface}`));
46782
+ insp.appendChild(row(t("ins.material"), g.material));
46783
+ insp.appendChild(row(t("ins.dug"), `${num(g.dug * 100, 0)} cm`));
46784
+ insp.appendChild(row(t("ins.dig_limit"), `${num(g.remaining, 2)} m`));
46785
+ if (g.scorch > 0.01) insp.appendChild(row(t("ins.char"), `${num(g.scorch * 100, 0)} %`));
46786
+ return;
46787
+ }
46325
46788
  if (i.thumbnail) insp.appendChild(thumb(i.thumbnail, i.thumbnailCredit));
46326
46789
  insp.appendChild(mk3("div", "t", `${i.object} \u203A ${i.part}`));
46327
46790
  insp.appendChild(row(t("ins.material"), i.material));
@@ -48680,9 +49143,36 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
48680
49143
  if (!leaf2) return void 0;
48681
49144
  return t(leaf2.locked ? "hud.door_hint_locked" : leaf2.open ? "hud.door_hint_close" : "hud.door_hint_open");
48682
49145
  };
49146
+ const groundInspect = (r) => {
49147
+ const onFloor = terrain.raycast(aimO, aimD, r.distance + 3);
49148
+ const [x, , z] = onFloor?.point ?? r.point;
49149
+ const column = terrain.columnAt(x, z);
49150
+ const materialName = S.text[`material.${column.material}`] ?? dStrings.materialNames[column.material] ?? column.material;
49151
+ return {
49152
+ object: t("ground.name"),
49153
+ objectId: GROUND_ID2,
49154
+ part: column.profile,
49155
+ material: materialName,
49156
+ status: "",
49157
+ hp: 0,
49158
+ maxHp: 0,
49159
+ temperature: 0,
49160
+ burning: false,
49161
+ char: column.scorch,
49162
+ melt: 0,
49163
+ dents: 0,
49164
+ holes: 0,
49165
+ cracks: 0,
49166
+ mass: 0,
49167
+ joints: [],
49168
+ distance: onFloor?.distance ?? r.distance,
49169
+ ground: { surface: terrainProfileName(column.profile, locale), material: materialName, dug: column.depth, remaining: column.remaining, scorch: column.scorch }
49170
+ };
49171
+ };
48683
49172
  const inspectInfo = () => {
48684
49173
  const r = inspectTarget();
48685
49174
  if (!r) return null;
49175
+ if (r.object === GROUND_ID2) return groundInspect(r);
48686
49176
  const st = world.state(r.object);
48687
49177
  const p = world.part(r.object, r.part);
48688
49178
  if (!st || !p) return null;