@adatechnology/game-fluids-three 0.4.6 → 0.4.7

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.
@@ -1325,11 +1325,231 @@ var GameFluidsThree = (() => {
1325
1325
  glow.outIntensity = glow.i * Math.pow(1 - age, glow.curve) * flicker;
1326
1326
  }
1327
1327
  }
1328
+ var LEHMER_MULTIPLIER = 16807;
1329
+ var LEHMER_MODULUS = 2147483647;
1330
+ var HASH_MIX = 2654435761;
1331
+ function normalizeLehmerSeed(seed) {
1332
+ const whole = Math.floor(Math.abs(seed)) % (LEHMER_MODULUS - 1);
1333
+ return whole === 0 ? 1 : whole;
1334
+ }
1335
+ function lehmer(seed) {
1336
+ let state = normalizeLehmerSeed(seed);
1337
+ const next = () => {
1338
+ state = state * LEHMER_MULTIPLIER % LEHMER_MODULUS;
1339
+ return (state - 1) / (LEHMER_MODULUS - 1);
1340
+ };
1341
+ return {
1342
+ next,
1343
+ between: (min2, max2) => min2 + (max2 - min2) * next(),
1344
+ direction(out) {
1345
+ const up = next() * 2 - 1;
1346
+ const angle = next() * Math.PI * 2;
1347
+ const ring2 = Math.sqrt(1 - up * up);
1348
+ out.x = ring2 * Math.cos(angle);
1349
+ out.y = up;
1350
+ out.z = ring2 * Math.sin(angle);
1351
+ return out;
1352
+ },
1353
+ get state() {
1354
+ return state;
1355
+ }
1356
+ };
1357
+ }
1358
+ function mixEventSeed(eventSeed) {
1359
+ let mixed = Math.imul((eventSeed | 0) ^ HASH_MIX, 2246822507);
1360
+ mixed ^= mixed >>> 13;
1361
+ mixed = Math.imul(mixed, 3266489909);
1362
+ mixed ^= mixed >>> 16;
1363
+ return (mixed >>> 0) % (LEHMER_MODULUS - 1) + 1;
1364
+ }
1365
+ function streamFor(eventSeed) {
1366
+ return lehmer(mixEventSeed(eventSeed));
1367
+ }
1368
+ var SKIN = 5e-3;
1369
+ var MAX_PAD = 0.45;
1370
+ function rotation(q) {
1371
+ const [x, y, z, w] = q ?? [0, 0, 0, 1];
1372
+ const n = Math.hypot(x, y, z, w) || 1;
1373
+ const qx = x / n;
1374
+ const qy = y / n;
1375
+ const qz = z / n;
1376
+ const qw = w / n;
1377
+ return new Float64Array([
1378
+ 1 - 2 * (qy * qy + qz * qz),
1379
+ 2 * (qx * qy - qz * qw),
1380
+ 2 * (qx * qz + qy * qw),
1381
+ 2 * (qx * qy + qz * qw),
1382
+ 1 - 2 * (qx * qx + qz * qz),
1383
+ 2 * (qy * qz - qx * qw),
1384
+ 2 * (qx * qz - qy * qw),
1385
+ 2 * (qy * qz + qx * qw),
1386
+ 1 - 2 * (qx * qx + qy * qy)
1387
+ ]);
1388
+ }
1389
+ var Enclosures = class {
1390
+ constructor(seed = 20973) {
1391
+ __publicField2(this, "spaces", /* @__PURE__ */ new Map());
1392
+ __publicField2(this, "list", []);
1393
+ __publicField2(this, "random");
1394
+ __publicField2(this, "out", { x: 0, y: 0, z: 0, nx: 0, ny: 0, nz: 0 });
1395
+ __publicField2(this, "l0", new Float64Array(3));
1396
+ __publicField2(this, "l1", new Float64Array(3));
1397
+ __publicField2(this, "q", new Float64Array(3));
1398
+ this.random = lehmer(seed);
1399
+ }
1400
+ get empty() {
1401
+ return this.list.length === 0;
1402
+ }
1403
+ get size() {
1404
+ return this.list.length;
1405
+ }
1406
+ /** Declares, moves or (with `null`) ends the space `id`. */
1407
+ set(id, enclosure) {
1408
+ if (!enclosure) {
1409
+ if (this.spaces.delete(id)) this.list = [...this.spaces.values()];
1410
+ return;
1411
+ }
1412
+ const [hx, hy, hz] = enclosure.halfSize;
1413
+ const space = {
1414
+ cx: enclosure.center[0],
1415
+ cy: enclosure.center[1],
1416
+ cz: enclosure.center[2],
1417
+ hx: Math.abs(hx),
1418
+ hy: Math.abs(hy),
1419
+ hz: Math.abs(hz),
1420
+ r: rotation(enclosure.orientation),
1421
+ radius: Math.hypot(hx, hy, hz),
1422
+ openings: enclosure.openings ?? [],
1423
+ leak: Math.max(0, enclosure.leak ?? 0)
1424
+ };
1425
+ const old = this.spaces.get(id);
1426
+ this.spaces.set(id, space);
1427
+ const at3 = old ? this.list.indexOf(old) : -1;
1428
+ if (at3 >= 0) this.list[at3] = space;
1429
+ else this.list.push(space);
1430
+ }
1431
+ get(id) {
1432
+ const s = this.spaces.get(id);
1433
+ if (!s) return void 0;
1434
+ return { center: [s.cx, s.cy, s.cz], halfSize: [s.hx, s.hy, s.hz], openings: s.openings, leak: s.leak };
1435
+ }
1436
+ ids() {
1437
+ return [...this.spaces.keys()];
1438
+ }
1439
+ clear() {
1440
+ this.spaces.clear();
1441
+ this.list = [];
1442
+ }
1443
+ /** Whether a point is inside any space (on the inside of its walls). */
1444
+ inside(x, y, z) {
1445
+ for (const s of this.list) {
1446
+ this.toLocal(s, x, y, z, this.l0);
1447
+ if (Math.abs(this.l0[0]) <= s.hx && Math.abs(this.l0[1]) <= s.hy && Math.abs(this.l0[2]) <= s.hz) return true;
1448
+ }
1449
+ return false;
1450
+ }
1451
+ toLocal(s, x, y, z, out) {
1452
+ const dx = x - s.cx;
1453
+ const dy = y - s.cy;
1454
+ const dz = z - s.cz;
1455
+ const r = s.r;
1456
+ out[0] = r[0] * dx + r[3] * dy + r[6] * dz;
1457
+ out[1] = r[1] * dx + r[4] * dy + r[7] * dz;
1458
+ out[2] = r[2] * dx + r[5] * dy + r[8] * dz;
1459
+ }
1460
+ /** Passes at `rate` per second. */
1461
+ chance(rate, dt) {
1462
+ return rate > 0 && this.random.next() < 1 - Math.exp(-rate * dt);
1463
+ }
1464
+ /** The opening the wall point `q` (local) is in lets it through now. */
1465
+ through(s, q, dt) {
1466
+ for (const o of s.openings) {
1467
+ 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;
1468
+ if (o.rate === void 0 || this.chance(o.rate, dt)) return true;
1469
+ }
1470
+ return this.chance(s.leak, dt);
1471
+ }
1472
+ confine(x0, y0, z0, x1, y1, z1, pad, dt) {
1473
+ const list = this.list;
1474
+ if (list.length === 0) return false;
1475
+ const p = Math.min(Math.max(0, pad), MAX_PAD);
1476
+ const l0 = this.l0;
1477
+ const l1 = this.l1;
1478
+ const q = this.q;
1479
+ for (const s of list) {
1480
+ const reach = s.radius + p + 0.05;
1481
+ const dx = x1 - s.cx;
1482
+ const dy = y1 - s.cy;
1483
+ const dz = z1 - s.cz;
1484
+ if (dx * dx + dy * dy + dz * dz > reach * reach) {
1485
+ const ex = x0 - s.cx;
1486
+ const ey = y0 - s.cy;
1487
+ const ez = z0 - s.cz;
1488
+ if (ex * ex + ey * ey + ez * ez > reach * reach) continue;
1489
+ }
1490
+ this.toLocal(s, x0, y0, z0, l0);
1491
+ this.toLocal(s, x1, y1, z1, l1);
1492
+ const h = [s.hx, s.hy, s.hz];
1493
+ const in0 = Math.abs(l0[0]) <= s.hx && Math.abs(l0[1]) <= s.hy && Math.abs(l0[2]) <= s.hz;
1494
+ let axis2 = 0;
1495
+ let sign = 1;
1496
+ if (in0) {
1497
+ if (Math.abs(l1[0]) <= s.hx && Math.abs(l1[1]) <= s.hy && Math.abs(l1[2]) <= s.hz) continue;
1498
+ for (let a = 0; a < 3; a++) q[a] = Math.min(h[a], Math.max(-h[a], l1[a]));
1499
+ if (this.through(s, q, dt)) continue;
1500
+ let worst = -Infinity;
1501
+ for (let a = 0; a < 3; a++) {
1502
+ const over = Math.abs(l1[a]) - h[a];
1503
+ if (over > worst) {
1504
+ worst = over;
1505
+ axis2 = a;
1506
+ }
1507
+ }
1508
+ sign = l1[axis2] < 0 ? -1 : 1;
1509
+ for (let a = 0; a < 3; a++) l1[a] = Math.min(h[a] - SKIN, Math.max(-(h[a] - SKIN), l1[a]));
1510
+ sign = -sign;
1511
+ } else {
1512
+ const padded0 = Math.abs(l0[0]) <= s.hx + p && Math.abs(l0[1]) <= s.hy + p && Math.abs(l0[2]) <= s.hz + p;
1513
+ const bound = padded0 ? 0 : p;
1514
+ if (!(Math.abs(l1[0]) <= s.hx + bound && Math.abs(l1[1]) <= s.hy + bound && Math.abs(l1[2]) <= s.hz + bound)) continue;
1515
+ for (let a = 0; a < 3; a++) q[a] = Math.min(h[a], Math.max(-h[a], l1[a]));
1516
+ if (this.through(s, q, dt)) continue;
1517
+ let worst = -Infinity;
1518
+ for (let a = 0; a < 3; a++) {
1519
+ const over = Math.abs(l0[a]) - (h[a] + bound);
1520
+ if (over > worst) {
1521
+ worst = over;
1522
+ axis2 = a;
1523
+ }
1524
+ }
1525
+ sign = l0[axis2] < 0 ? -1 : 1;
1526
+ l1[axis2] = sign * (h[axis2] + bound + SKIN);
1527
+ }
1528
+ const r = s.r;
1529
+ const out = this.out;
1530
+ out.x = s.cx + r[0] * l1[0] + r[1] * l1[1] + r[2] * l1[2];
1531
+ out.y = s.cy + r[3] * l1[0] + r[4] * l1[1] + r[5] * l1[2];
1532
+ out.z = s.cz + r[6] * l1[0] + r[7] * l1[1] + r[8] * l1[2];
1533
+ out.nx = r[axis2] * sign;
1534
+ out.ny = r[3 + axis2] * sign;
1535
+ out.nz = r[6 + axis2] * sign;
1536
+ return true;
1537
+ }
1538
+ return false;
1539
+ }
1540
+ };
1541
+ function slideAlong(out, vx2, vy2, vz2) {
1542
+ const into = vx2 * out.nx + vy2 * out.ny + vz2 * out.nz;
1543
+ if (into >= 0) return [vx2, vy2, vz2];
1544
+ return [vx2 - into * out.nx, vy2 - into * out.ny, vz2 - into * out.nz];
1545
+ }
1328
1546
  var FLOOR_FRACTION = 0.4;
1329
1547
  var FLOOR_BOUNCE = -0.2;
1330
1548
  var ARRIVAL_FRAME = 30;
1331
1549
  var ARRIVAL_BASE = 0.3;
1332
1550
  var GROWTH_CAP_SECONDS = 3.5;
1551
+ var CARRIED_KINDS = [true, true, false, true, false];
1552
+ var PAD_OF_SIZE = 0.35;
1333
1553
  function createPuffPool(capacity) {
1334
1554
  const placeholder = { next: () => 0, between: (min2) => min2, direction: (out) => out, state: 0 };
1335
1555
  return new OrderedPool(capacity, () => ({
@@ -1424,13 +1644,24 @@ var GameFluidsThree = (() => {
1424
1644
  if (puff3.vy < 0) puff3.vy *= FLOOR_BOUNCE;
1425
1645
  }
1426
1646
  }
1427
- function updatePuffs(pool, dt, wind) {
1647
+ function updatePuffs(pool, dt, wind, confiner) {
1428
1648
  pool.compact(ageOrKeep2, dt);
1649
+ const confining = confiner !== void 0 && !confiner.empty;
1429
1650
  for (const puff3 of pool.items) {
1430
1651
  const age = puff3.t / puff3.life;
1431
1652
  const heat = puff3.h0 > 0 ? puff3.h0 * Math.pow(Math.max(0, 1 - puff3.t / puff3.hl), 1.25) : 0;
1653
+ const x0 = puff3.x;
1654
+ const y0 = puff3.y;
1655
+ const z0 = puff3.z;
1432
1656
  if (puff3.ring) moveOnRing(puff3, puff3.ring, wind);
1433
1657
  else moveFree(puff3, dt, age, heat, wind);
1658
+ if (confining && CARRIED_KINDS[puff3.kind] && confiner.confine(x0, y0, z0, puff3.x, puff3.y, puff3.z, puff3.size * PAD_OF_SIZE, dt)) {
1659
+ const out = confiner.out;
1660
+ puff3.x = out.x;
1661
+ puff3.y = out.y;
1662
+ puff3.z = out.z;
1663
+ [puff3.vx, puff3.vy, puff3.vz] = slideAlong(out, puff3.vx, puff3.vy, puff3.vz);
1664
+ }
1434
1665
  const size = puff3.s0 + (puff3.s1 - puff3.s0) * (1 - Math.exp(-puff3.t * puff3.gk)) + puff3.sg * Math.min(puff3.t, GROWTH_CAP_SECONDS);
1435
1666
  puff3.size = size * Math.min(1, puff3.t * ARRIVAL_FRAME + ARRIVAL_BASE);
1436
1667
  puff3.age = age;
@@ -1956,10 +2187,10 @@ var GameFluidsThree = (() => {
1956
2187
  ])
1957
2188
  ]
1958
2189
  };
1959
- function localToWorld(translation, rotation, local2) {
2190
+ function localToWorld(translation, rotation2, local2) {
1960
2191
  const [x, y, z] = local2;
1961
- const [a, c, e] = [Math.cos(rotation[0]), Math.cos(rotation[1]), Math.cos(rotation[2])];
1962
- const [b, d, f] = [Math.sin(rotation[0]), Math.sin(rotation[1]), Math.sin(rotation[2])];
2192
+ const [a, c, e] = [Math.cos(rotation2[0]), Math.cos(rotation2[1]), Math.cos(rotation2[2])];
2193
+ const [b, d, f] = [Math.sin(rotation2[0]), Math.sin(rotation2[1]), Math.sin(rotation2[2])];
1963
2194
  const [ae, af, be, bf] = [a * e, a * f, b * e, b * f];
1964
2195
  return [
1965
2196
  c * e * x + -c * f * y + d * z + translation[0],
@@ -3622,46 +3853,6 @@ var GameFluidsThree = (() => {
3622
3853
  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];
3623
3854
  var WORKSHOP_EFFECTS = Object.fromEntries(ALL_EFFECTS.map((effect) => [effect.id, effect]));
3624
3855
  var GAME_EFFECTS = { ...WORKSHOP_EFFECTS, ...Object.fromEntries([...EVENT_EFFECTS, ...VIEWMODEL_EFFECTS, ...VEHICLE_EFFECTS].map((effect) => [effect.id, effect])) };
3625
- var LEHMER_MULTIPLIER = 16807;
3626
- var LEHMER_MODULUS = 2147483647;
3627
- var HASH_MIX = 2654435761;
3628
- function normalizeLehmerSeed(seed) {
3629
- const whole = Math.floor(Math.abs(seed)) % (LEHMER_MODULUS - 1);
3630
- return whole === 0 ? 1 : whole;
3631
- }
3632
- function lehmer(seed) {
3633
- let state = normalizeLehmerSeed(seed);
3634
- const next = () => {
3635
- state = state * LEHMER_MULTIPLIER % LEHMER_MODULUS;
3636
- return (state - 1) / (LEHMER_MODULUS - 1);
3637
- };
3638
- return {
3639
- next,
3640
- between: (min2, max2) => min2 + (max2 - min2) * next(),
3641
- direction(out) {
3642
- const up = next() * 2 - 1;
3643
- const angle = next() * Math.PI * 2;
3644
- const ring2 = Math.sqrt(1 - up * up);
3645
- out.x = ring2 * Math.cos(angle);
3646
- out.y = up;
3647
- out.z = ring2 * Math.sin(angle);
3648
- return out;
3649
- },
3650
- get state() {
3651
- return state;
3652
- }
3653
- };
3654
- }
3655
- function mixEventSeed(eventSeed) {
3656
- let mixed = Math.imul((eventSeed | 0) ^ HASH_MIX, 2246822507);
3657
- mixed ^= mixed >>> 13;
3658
- mixed = Math.imul(mixed, 3266489909);
3659
- mixed ^= mixed >>> 16;
3660
- return (mixed >>> 0) % (LEHMER_MODULUS - 1) + 1;
3661
- }
3662
- function streamFor(eventSeed) {
3663
- return lehmer(mixEventSeed(eventSeed));
3664
- }
3665
3856
  var TABLE = {
3666
3857
  concrete: { dust: 9078138, chip: 7236195, spark: 2 },
3667
3858
  metal: { dust: 0, chip: 0, spark: 18 },
@@ -4965,8 +5156,8 @@ var GameFluidsThree = (() => {
4965
5156
  function addScorch(store, random, x, z, radius) {
4966
5157
  const variant = Math.floor(random.next() * SCORCH_VARIANTS);
4967
5158
  const lift = 0.03 + nextLift(store) * 1e-3;
4968
- const rotation = random.next() * 6;
4969
- store.add({ kind: "scorch", x, z, radius, variant, lift, rotation });
5159
+ const rotation2 = random.next() * 6;
5160
+ store.add({ kind: "scorch", x, z, radius, variant, lift, rotation: rotation2 });
4970
5161
  }
4971
5162
  function addRimRocks(store, ground, random, x, z, radius) {
4972
5163
  const count = Math.round(6 + radius * 7);
@@ -4995,8 +5186,8 @@ var GameFluidsThree = (() => {
4995
5186
  function addCrater(store, ground, random, spec) {
4996
5187
  ground?.dent(random, spec.x, spec.z, spec.radius, spec.depth);
4997
5188
  if (!spec.marks) return;
4998
- const rotation = random.next() * 6;
4999
- store.add({ kind: "dirt", x: spec.x, z: spec.z, radius: spec.radius * 1.02, lift: 0.045, rotation });
5189
+ const rotation2 = random.next() * 6;
5190
+ store.add({ kind: "dirt", x: spec.x, z: spec.z, radius: spec.radius * 1.02, lift: 0.045, rotation: rotation2 });
5000
5191
  addRimRocks(store, ground, random, spec.x, spec.z, spec.radius);
5001
5192
  }
5002
5193
  function addBloodSplat(store, ground, random, spec) {
@@ -5552,6 +5743,7 @@ var GameFluidsThree = (() => {
5552
5743
  state.gas = Math.max(0, state.gas - dt / DECAY.gasSeconds);
5553
5744
  state.beat *= Math.exp(-DECAY.beat * dt);
5554
5745
  }
5746
+ var PAD_OF_SIZE2 = 0.35;
5555
5747
  var clamp4 = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
5556
5748
  var lerp4 = (a, b, t) => a + (b - a) * t;
5557
5749
  function rgb(hex) {
@@ -5711,9 +5903,20 @@ var GameFluidsThree = (() => {
5711
5903
  vy2 *= k;
5712
5904
  vz2 *= k;
5713
5905
  }
5714
- const x = this.position[i3] + vx2 * dt;
5906
+ let x = this.position[i3] + vx2 * dt;
5715
5907
  let y = this.position[i3 + 1] + vy2 * dt;
5716
- const z = this.position[i3 + 2] + vz2 * dt;
5908
+ let z = this.position[i3 + 2] + vz2 * dt;
5909
+ const confiner = host.confiner;
5910
+ if (def.carried && confiner && !confiner.empty) {
5911
+ const pad = this.size0[i] * lerp4(1, def.grow, t) * PAD_OF_SIZE2;
5912
+ if (confiner.confine(this.position[i3], this.position[i3 + 1], this.position[i3 + 2], x, y, z, pad, dt)) {
5913
+ const out = confiner.out;
5914
+ x = out.x;
5915
+ y = out.y;
5916
+ z = out.z;
5917
+ [vx2, vy2, vz2] = slideAlong(out, vx2, vy2, vz2);
5918
+ }
5919
+ }
5717
5920
  if (def.ground !== "none") {
5718
5921
  const own = this.floor[i];
5719
5922
  const gy = Number.isNaN(own) ? host.groundAt(x, z) + 5e-3 : own;
@@ -5799,7 +6002,8 @@ var GameFluidsThree = (() => {
5799
6002
  alpha: 0.35,
5800
6003
  fadeIn: 0.15,
5801
6004
  cone: 0.6,
5802
- ground: "none"
6005
+ ground: "none",
6006
+ carried: true
5803
6007
  },
5804
6008
  sparks: {
5805
6009
  id: "sparks",
@@ -5843,7 +6047,8 @@ var GameFluidsThree = (() => {
5843
6047
  alpha: 0.4,
5844
6048
  fadeIn: 0.1,
5845
6049
  cone: 0.4,
5846
- ground: "none"
6050
+ ground: "none",
6051
+ carried: true
5847
6052
  },
5848
6053
  shards: {
5849
6054
  id: "shards",
@@ -6001,7 +6206,8 @@ var GameFluidsThree = (() => {
6001
6206
  fadeIn: 0.1,
6002
6207
  cone: 0.5,
6003
6208
  ground: "stop",
6004
- flutter: 0.6
6209
+ flutter: 0.6,
6210
+ carried: true
6005
6211
  },
6006
6212
  steam: {
6007
6213
  id: "steam",
@@ -6023,7 +6229,8 @@ var GameFluidsThree = (() => {
6023
6229
  alpha: 0.35,
6024
6230
  fadeIn: 0.15,
6025
6231
  cone: 0.6,
6026
- ground: "none"
6232
+ ground: "none",
6233
+ carried: true
6027
6234
  },
6028
6235
  flash: {
6029
6236
  id: "flash",
@@ -6156,7 +6363,8 @@ var GameFluidsThree = (() => {
6156
6363
  const host = {
6157
6364
  groundAt: this.options.groundAt,
6158
6365
  gravity: this.options.gravity,
6159
- land: (owner, x, y, z, color, tag) => this.listeners.get(owner)?.(x, y, z, color, tag)
6366
+ land: (owner, x, y, z, color, tag) => this.listeners.get(owner)?.(x, y, z, color, tag),
6367
+ ...this.options.confiner ? { confiner: this.options.confiner } : {}
6160
6368
  };
6161
6369
  if (this.additive.live > 0 || this.additive.drawCount > 0) this.additive.update(dt, host);
6162
6370
  if (this.normal.live > 0 || this.normal.drawCount > 0) this.normal.update(dt, host);
@@ -6362,8 +6570,8 @@ var GameFluidsThree = (() => {
6362
6570
  * 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
6363
6571
  * world axes). The surface they were on is gone (a brick came out of a wall with its bullet holes). Returns how many went.
6364
6572
  */
6365
- clearInside(center, half, rotation = [0, 0, 0, 1]) {
6366
- const qx = -rotation[0], qy = -rotation[1], qz = -rotation[2], qw = rotation[3];
6573
+ clearInside(center, half, rotation2 = [0, 0, 0, 1]) {
6574
+ const qx = -rotation2[0], qy = -rotation2[1], qz = -rotation2[2], qw = rotation2[3];
6367
6575
  let gone = 0;
6368
6576
  for (const decal2 of this.decals) {
6369
6577
  if (!decal2.on) continue;
@@ -6484,10 +6692,14 @@ var GameFluidsThree = (() => {
6484
6692
  var DEFAULT_FADE_IN = 0.1;
6485
6693
  var FLOATS_PER_VECTOR = 3;
6486
6694
  var BYTE_MAX2 = 255;
6695
+ var CARRIED_GRAVITY = 3;
6696
+ var PAD_OF_SIZE3 = 0.35;
6487
6697
  var SpritePool = class {
6488
- constructor(capacity, context) {
6698
+ /** `carried`: the normal-blend pool (smoke, dust, steam), whose light sprites go round the closed spaces; the additive one (flames, sparks) does not. */
6699
+ constructor(capacity, context, carried = false) {
6489
6700
  __publicField2(this, "capacity", capacity);
6490
6701
  __publicField2(this, "context", context);
6702
+ __publicField2(this, "carried", carried);
6491
6703
  __publicField2(this, "count", 0);
6492
6704
  __publicField2(this, "position");
6493
6705
  __publicField2(this, "velocity");
@@ -6587,6 +6799,7 @@ var GameFluidsThree = (() => {
6587
6799
  }
6588
6800
  update(dt, leaveDecal) {
6589
6801
  const { position, velocity } = this;
6802
+ const confiner = this.context.confiner;
6590
6803
  for (let index = this.count - 1; index >= 0; index--) {
6591
6804
  this.life[index] -= dt;
6592
6805
  if (this.life[index] <= 0) {
@@ -6600,9 +6813,25 @@ var GameFluidsThree = (() => {
6600
6813
  velocity[offset] *= factor;
6601
6814
  velocity[offset + 1] *= factor;
6602
6815
  velocity[offset + 2] *= factor;
6816
+ const x0 = position[offset];
6817
+ const y0 = position[offset + 1];
6818
+ const z0 = position[offset + 2];
6603
6819
  position[offset] += velocity[offset] * dt;
6604
6820
  position[offset + 1] += velocity[offset + 1] * dt;
6605
6821
  position[offset + 2] += velocity[offset + 2] * dt;
6822
+ if (this.carried && confiner && !confiner.empty && Math.abs(this.gravity[index]) < CARRIED_GRAVITY) {
6823
+ const size = this.startSize[index] * (1 + (this.growth[index] - 1) * age);
6824
+ if (confiner.confine(x0, y0, z0, position[offset], position[offset + 1], position[offset + 2], size * PAD_OF_SIZE3, dt)) {
6825
+ const out = confiner.out;
6826
+ position[offset] = out.x;
6827
+ position[offset + 1] = out.y;
6828
+ position[offset + 2] = out.z;
6829
+ const [vx2, vy2, vz2] = slideAlong(out, velocity[offset], velocity[offset + 1], velocity[offset + 2]);
6830
+ velocity[offset] = vx2;
6831
+ velocity[offset + 1] = vy2;
6832
+ velocity[offset + 2] = vz2;
6833
+ }
6834
+ }
6606
6835
  const mode = this.groundMode[index];
6607
6836
  if (mode) {
6608
6837
  const groundHeight = this.context.groundAt(position[offset], position[offset + 2]);
@@ -6879,9 +7108,9 @@ var GameFluidsThree = (() => {
6879
7108
  __publicField2(this, "placement");
6880
7109
  __publicField2(this, "accumulators", /* @__PURE__ */ new Map());
6881
7110
  const profile = options.profile ?? PROFILES.high;
6882
- this.context = { random: options.random, groundAt: options.groundAt ?? workshopGround, quality: { level: 0, mult: 1 }, trace: options.trace };
7111
+ this.context = { random: options.random, groundAt: options.groundAt ?? workshopGround, quality: { level: 0, mult: 1 }, trace: options.trace, ...options.confiner ? { confiner: options.confiner } : {} };
6883
7112
  this.add = new SpritePool(profile.sprites.add, this.context);
6884
- this.normal = new SpritePool(profile.sprites.normal, this.context);
7113
+ this.normal = new SpritePool(profile.sprites.normal, this.context, true);
6885
7114
  this.streaks = new StreakPool(profile.streaks, this.context);
6886
7115
  this.debris = new DebrisPool(this.context);
6887
7116
  this.decals = new DecalPool(this.context);
@@ -7069,12 +7298,14 @@ var GameFluidsThree = (() => {
7069
7298
  const groundAt = options.groundAt ?? (ground ? (x, z) => ground.heightAt(x, z) : () => 0);
7070
7299
  const handles = { next: 0, events: 0 };
7071
7300
  const low = options.profile === "low";
7301
+ const enclosures = new Enclosures(20973 + (options.seed ?? 0));
7072
7302
  const spriteSystem = new SpriteKindSystem({
7073
7303
  capacity: options.sprites?.capacity ?? (low ? 1250 : 2500),
7074
7304
  density: options.sprites?.density ?? (low ? 0.6 : 1),
7075
7305
  seed: 2654435761 + (options.seed ?? 0),
7076
7306
  groundAt,
7077
7307
  gravity: options.gravity ?? DEFAULT_GRAVITY,
7308
+ confiner: enclosures,
7078
7309
  ...options.sprites?.kinds ? { kinds: options.sprites.kinds } : {}
7079
7310
  });
7080
7311
  const core = {
@@ -7124,7 +7355,7 @@ var GameFluidsThree = (() => {
7124
7355
  return [camera2.x, camera2.y, camera2.z];
7125
7356
  }, forward: (local2) => [local2[0] ?? 0, local2[1] ?? 0, local2[2] ?? 0] };
7126
7357
  function workshop() {
7127
- return engine ?? (engine = new WorkshopEngine({ random: sharedRandom, profile, groundAt }));
7358
+ return engine ?? (engine = new WorkshopEngine({ random: sharedRandom, profile, groundAt, confiner: enclosures }));
7128
7359
  }
7129
7360
  function effectPlayer() {
7130
7361
  return player ?? (player = createEffectPlayer({ engine: workshop(), effects, view }));
@@ -7183,6 +7414,10 @@ var GameFluidsThree = (() => {
7183
7414
  puff3.vz += dz * strength;
7184
7415
  }
7185
7416
  },
7417
+ enclose(id, enclosure) {
7418
+ enclosures.set(id, enclosure);
7419
+ },
7420
+ enclosures: () => enclosures,
7186
7421
  stop(handle) {
7187
7422
  player?.stop(handle);
7188
7423
  const fires = core.fires;
@@ -7210,7 +7445,7 @@ var GameFluidsThree = (() => {
7210
7445
  core.clock.time += dt;
7211
7446
  core.schedule.update(core.clock.time);
7212
7447
  updateRings(core.rings, dt);
7213
- updatePuffs(core.puffs, dt, wind);
7448
+ updatePuffs(core.puffs, dt, wind, enclosures);
7214
7449
  updateSparks(core.sparks, { dt, groundAt });
7215
7450
  updateGlows(core.glows, dt);
7216
7451
  updateShocks(core.shocks, core.puffs.items, dt);
@@ -8603,6 +8838,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8603
8838
  });
8604
8839
  },
8605
8840
  stop: (handle) => world.stop(handle),
8841
+ // a module's closed space (a shut car's cabin): the world's smoke, dust and steam go round it
8842
+ enclose: (id, enclosure) => world.enclose(id, enclosure),
8606
8843
  configure(settings) {
8607
8844
  const band = settings.lod_band;
8608
8845
  if (typeof band === "string" && band in LOD_BANDS) world.forceLod(LOD_BANDS[band] ?? null);
@@ -10673,7 +10910,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
10673
10910
  const direction = new THREE.Vector3();
10674
10911
  const location = new THREE.Vector3();
10675
10912
  const scale2 = new THREE.Vector3();
10676
- const rotation = new THREE.Quaternion();
10913
+ const rotation2 = new THREE.Quaternion();
10677
10914
  const matrix = new THREE.Matrix4();
10678
10915
  return {
10679
10916
  drops,
@@ -10693,9 +10930,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
10693
10930
  const o = i * 3;
10694
10931
  const speed = Math.hypot(velocity[o], velocity[o + 1], velocity[o + 2]);
10695
10932
  direction.set(velocity[o], velocity[o + 1], velocity[o + 2]).normalize();
10696
- rotation.setFromUnitVectors(up, direction);
10933
+ rotation2.setFromUnitVectors(up, direction);
10697
10934
  const size = radius[i] * METRES_PER_MM;
10698
- 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));
10935
+ 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));
10699
10936
  drops.setMatrixAt(i, matrix);
10700
10937
  drawn++;
10701
10938
  }