@adatechnology/game-fluids-three 0.4.6 → 0.4.8

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.
@@ -38,6 +38,84 @@ var GameFluidsThree = (() => {
38
38
  tinted: () => tinted
39
39
  });
40
40
 
41
+ // ../game-contracts/dist/index.js
42
+ var DEFAULT_COST_ALLOCATION_BYTES = 1024 * 1024;
43
+ var DEFERRED_QUEUE_ID = {
44
+ DESTRUCTION_EVENTS: "destruction.events",
45
+ BREACH_PAINT: "floorplan.breach-paint",
46
+ AUDIO_VOICES: "audio.voices",
47
+ VEHICLE_CREASE: "vehicles.crease"
48
+ };
49
+ var DEFERRED_PRIORITY = {
50
+ [DEFERRED_QUEUE_ID.DESTRUCTION_EVENTS]: { priority: 300, phase: "present" },
51
+ [DEFERRED_QUEUE_ID.BREACH_PAINT]: { priority: 250, phase: "present" },
52
+ [DEFERRED_QUEUE_ID.AUDIO_VOICES]: { priority: 200, phase: "idle", reserveMs: 1 },
53
+ [DEFERRED_QUEUE_ID.VEHICLE_CREASE]: { priority: 100, phase: "idle" }
54
+ };
55
+ var WARNED_KEY = /* @__PURE__ */ Symbol.for("@adatechnology/game-contracts.warned");
56
+ var warnedKeys = () => {
57
+ const holder = globalThis;
58
+ return holder[WARNED_KEY] ?? (holder[WARNED_KEY] = /* @__PURE__ */ new Set());
59
+ };
60
+ var isProduction = () => {
61
+ try {
62
+ return process.env.NODE_ENV === "production";
63
+ } catch {
64
+ return false;
65
+ }
66
+ };
67
+ var isQuiet = () => globalThis.__GAME_KIT_QUIET__ === true;
68
+ var warnOnce = (key, message) => {
69
+ if (isQuiet() || isProduction()) return;
70
+ const seen = warnedKeys();
71
+ if (seen.has(key)) return;
72
+ seen.add(key);
73
+ console.warn(`[game-kit] ${message}`);
74
+ };
75
+ var KIT_ORIGIN_KEY = "kitOrigin";
76
+ function markKitOrigin(object, origin) {
77
+ const node = object;
78
+ node.userData = node.userData ?? {};
79
+ node.userData[KIT_ORIGIN_KEY] = { ...origin };
80
+ return object;
81
+ }
82
+ var SURFACE_IDS = ["concrete", "asphalt", "wood", "tile", "dirt", "grass", "gravel", "sand", "metal", "water", "glass", "carpet"];
83
+ var GAIT_IDS = ["walk", "run"];
84
+ var LANDING_IDS = ["light", "heavy"];
85
+ var BODY_SOUND_IDS = [
86
+ "jump",
87
+ "cloth_rustle",
88
+ "body_fall",
89
+ "fall_damage",
90
+ "player_hurt",
91
+ "player_breath_run",
92
+ "player_death",
93
+ "bite",
94
+ "grab",
95
+ "punch_swing",
96
+ "punch_hit_body",
97
+ "punch_hit_face",
98
+ "punch_hit_object",
99
+ "kick_swing",
100
+ "kick_hit",
101
+ "kick_hit_object",
102
+ "shove_two_hands",
103
+ "knife_swing",
104
+ "knife_stab",
105
+ "knife_slash",
106
+ "knife_hit_object",
107
+ "blunt_swing",
108
+ "blunt_hit",
109
+ "blunt_hit_object",
110
+ "axe_swing",
111
+ "axe_chop",
112
+ "axe_hit_object"
113
+ ];
114
+ var footstepSoundId = (surface, gait) => `footstep_${surface}_${gait}`;
115
+ var FOOTSTEP_SOUND_IDS = SURFACE_IDS.flatMap((surface) => GAIT_IDS.map((gait) => footstepSoundId(surface, gait)));
116
+ var LAND_SOUND_IDS = SURFACE_IDS.flatMap((surface) => LANDING_IDS.map((landing) => `land_${surface}_${landing}`));
117
+ var BODY_FOLEY_IDS = [...FOOTSTEP_SOUND_IDS, ...LAND_SOUND_IDS, ...BODY_SOUND_IDS];
118
+
41
119
  // ../game-vfx/dist/index.js
42
120
  var __defProp2 = Object.defineProperty;
43
121
  var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -1325,11 +1403,231 @@ var GameFluidsThree = (() => {
1325
1403
  glow.outIntensity = glow.i * Math.pow(1 - age, glow.curve) * flicker;
1326
1404
  }
1327
1405
  }
1406
+ var LEHMER_MULTIPLIER = 16807;
1407
+ var LEHMER_MODULUS = 2147483647;
1408
+ var HASH_MIX = 2654435761;
1409
+ function normalizeLehmerSeed(seed) {
1410
+ const whole = Math.floor(Math.abs(seed)) % (LEHMER_MODULUS - 1);
1411
+ return whole === 0 ? 1 : whole;
1412
+ }
1413
+ function lehmer(seed) {
1414
+ let state = normalizeLehmerSeed(seed);
1415
+ const next = () => {
1416
+ state = state * LEHMER_MULTIPLIER % LEHMER_MODULUS;
1417
+ return (state - 1) / (LEHMER_MODULUS - 1);
1418
+ };
1419
+ return {
1420
+ next,
1421
+ between: (min2, max2) => min2 + (max2 - min2) * next(),
1422
+ direction(out) {
1423
+ const up = next() * 2 - 1;
1424
+ const angle = next() * Math.PI * 2;
1425
+ const ring2 = Math.sqrt(1 - up * up);
1426
+ out.x = ring2 * Math.cos(angle);
1427
+ out.y = up;
1428
+ out.z = ring2 * Math.sin(angle);
1429
+ return out;
1430
+ },
1431
+ get state() {
1432
+ return state;
1433
+ }
1434
+ };
1435
+ }
1436
+ function mixEventSeed(eventSeed) {
1437
+ let mixed = Math.imul((eventSeed | 0) ^ HASH_MIX, 2246822507);
1438
+ mixed ^= mixed >>> 13;
1439
+ mixed = Math.imul(mixed, 3266489909);
1440
+ mixed ^= mixed >>> 16;
1441
+ return (mixed >>> 0) % (LEHMER_MODULUS - 1) + 1;
1442
+ }
1443
+ function streamFor(eventSeed) {
1444
+ return lehmer(mixEventSeed(eventSeed));
1445
+ }
1446
+ var SKIN = 5e-3;
1447
+ var MAX_PAD = 0.45;
1448
+ function rotation(q) {
1449
+ const [x, y, z, w] = q ?? [0, 0, 0, 1];
1450
+ const n = Math.hypot(x, y, z, w) || 1;
1451
+ const qx = x / n;
1452
+ const qy = y / n;
1453
+ const qz = z / n;
1454
+ const qw = w / n;
1455
+ return new Float64Array([
1456
+ 1 - 2 * (qy * qy + qz * qz),
1457
+ 2 * (qx * qy - qz * qw),
1458
+ 2 * (qx * qz + qy * qw),
1459
+ 2 * (qx * qy + qz * qw),
1460
+ 1 - 2 * (qx * qx + qz * qz),
1461
+ 2 * (qy * qz - qx * qw),
1462
+ 2 * (qx * qz - qy * qw),
1463
+ 2 * (qy * qz + qx * qw),
1464
+ 1 - 2 * (qx * qx + qy * qy)
1465
+ ]);
1466
+ }
1467
+ var Enclosures = class {
1468
+ constructor(seed = 20973) {
1469
+ __publicField2(this, "spaces", /* @__PURE__ */ new Map());
1470
+ __publicField2(this, "list", []);
1471
+ __publicField2(this, "random");
1472
+ __publicField2(this, "out", { x: 0, y: 0, z: 0, nx: 0, ny: 0, nz: 0 });
1473
+ __publicField2(this, "l0", new Float64Array(3));
1474
+ __publicField2(this, "l1", new Float64Array(3));
1475
+ __publicField2(this, "q", new Float64Array(3));
1476
+ this.random = lehmer(seed);
1477
+ }
1478
+ get empty() {
1479
+ return this.list.length === 0;
1480
+ }
1481
+ get size() {
1482
+ return this.list.length;
1483
+ }
1484
+ /** Declares, moves or (with `null`) ends the space `id`. */
1485
+ set(id, enclosure) {
1486
+ if (!enclosure) {
1487
+ if (this.spaces.delete(id)) this.list = [...this.spaces.values()];
1488
+ return;
1489
+ }
1490
+ const [hx, hy, hz] = enclosure.halfSize;
1491
+ const space = {
1492
+ cx: enclosure.center[0],
1493
+ cy: enclosure.center[1],
1494
+ cz: enclosure.center[2],
1495
+ hx: Math.abs(hx),
1496
+ hy: Math.abs(hy),
1497
+ hz: Math.abs(hz),
1498
+ r: rotation(enclosure.orientation),
1499
+ radius: Math.hypot(hx, hy, hz),
1500
+ openings: enclosure.openings ?? [],
1501
+ leak: Math.max(0, enclosure.leak ?? 0)
1502
+ };
1503
+ const old = this.spaces.get(id);
1504
+ this.spaces.set(id, space);
1505
+ const at3 = old ? this.list.indexOf(old) : -1;
1506
+ if (at3 >= 0) this.list[at3] = space;
1507
+ else this.list.push(space);
1508
+ }
1509
+ get(id) {
1510
+ const s = this.spaces.get(id);
1511
+ if (!s) return void 0;
1512
+ return { center: [s.cx, s.cy, s.cz], halfSize: [s.hx, s.hy, s.hz], openings: s.openings, leak: s.leak };
1513
+ }
1514
+ ids() {
1515
+ return [...this.spaces.keys()];
1516
+ }
1517
+ clear() {
1518
+ this.spaces.clear();
1519
+ this.list = [];
1520
+ }
1521
+ /** Whether a point is inside any space (on the inside of its walls). */
1522
+ inside(x, y, z) {
1523
+ for (const s of this.list) {
1524
+ this.toLocal(s, x, y, z, this.l0);
1525
+ if (Math.abs(this.l0[0]) <= s.hx && Math.abs(this.l0[1]) <= s.hy && Math.abs(this.l0[2]) <= s.hz) return true;
1526
+ }
1527
+ return false;
1528
+ }
1529
+ toLocal(s, x, y, z, out) {
1530
+ const dx = x - s.cx;
1531
+ const dy = y - s.cy;
1532
+ const dz = z - s.cz;
1533
+ const r = s.r;
1534
+ out[0] = r[0] * dx + r[3] * dy + r[6] * dz;
1535
+ out[1] = r[1] * dx + r[4] * dy + r[7] * dz;
1536
+ out[2] = r[2] * dx + r[5] * dy + r[8] * dz;
1537
+ }
1538
+ /** Passes at `rate` per second. */
1539
+ chance(rate, dt) {
1540
+ return rate > 0 && this.random.next() < 1 - Math.exp(-rate * dt);
1541
+ }
1542
+ /** The opening the wall point `q` (local) is in lets it through now. */
1543
+ through(s, q, dt) {
1544
+ for (const o of s.openings) {
1545
+ 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;
1546
+ if (o.rate === void 0 || this.chance(o.rate, dt)) return true;
1547
+ }
1548
+ return this.chance(s.leak, dt);
1549
+ }
1550
+ confine(x0, y0, z0, x1, y1, z1, pad, dt) {
1551
+ const list = this.list;
1552
+ if (list.length === 0) return false;
1553
+ const p = Math.min(Math.max(0, pad), MAX_PAD);
1554
+ const l0 = this.l0;
1555
+ const l1 = this.l1;
1556
+ const q = this.q;
1557
+ for (const s of list) {
1558
+ const reach = s.radius + p + 0.05;
1559
+ const dx = x1 - s.cx;
1560
+ const dy = y1 - s.cy;
1561
+ const dz = z1 - s.cz;
1562
+ if (dx * dx + dy * dy + dz * dz > reach * reach) {
1563
+ const ex = x0 - s.cx;
1564
+ const ey = y0 - s.cy;
1565
+ const ez = z0 - s.cz;
1566
+ if (ex * ex + ey * ey + ez * ez > reach * reach) continue;
1567
+ }
1568
+ this.toLocal(s, x0, y0, z0, l0);
1569
+ this.toLocal(s, x1, y1, z1, l1);
1570
+ const h = [s.hx, s.hy, s.hz];
1571
+ const in0 = Math.abs(l0[0]) <= s.hx && Math.abs(l0[1]) <= s.hy && Math.abs(l0[2]) <= s.hz;
1572
+ let axis2 = 0;
1573
+ let sign = 1;
1574
+ if (in0) {
1575
+ if (Math.abs(l1[0]) <= s.hx && Math.abs(l1[1]) <= s.hy && Math.abs(l1[2]) <= s.hz) continue;
1576
+ for (let a = 0; a < 3; a++) q[a] = Math.min(h[a], Math.max(-h[a], l1[a]));
1577
+ if (this.through(s, q, dt)) continue;
1578
+ let worst = -Infinity;
1579
+ for (let a = 0; a < 3; a++) {
1580
+ const over = Math.abs(l1[a]) - h[a];
1581
+ if (over > worst) {
1582
+ worst = over;
1583
+ axis2 = a;
1584
+ }
1585
+ }
1586
+ sign = l1[axis2] < 0 ? -1 : 1;
1587
+ for (let a = 0; a < 3; a++) l1[a] = Math.min(h[a] - SKIN, Math.max(-(h[a] - SKIN), l1[a]));
1588
+ sign = -sign;
1589
+ } else {
1590
+ const padded0 = Math.abs(l0[0]) <= s.hx + p && Math.abs(l0[1]) <= s.hy + p && Math.abs(l0[2]) <= s.hz + p;
1591
+ const bound = padded0 ? 0 : p;
1592
+ if (!(Math.abs(l1[0]) <= s.hx + bound && Math.abs(l1[1]) <= s.hy + bound && Math.abs(l1[2]) <= s.hz + bound)) continue;
1593
+ for (let a = 0; a < 3; a++) q[a] = Math.min(h[a], Math.max(-h[a], l1[a]));
1594
+ if (this.through(s, q, dt)) continue;
1595
+ let worst = -Infinity;
1596
+ for (let a = 0; a < 3; a++) {
1597
+ const over = Math.abs(l0[a]) - (h[a] + bound);
1598
+ if (over > worst) {
1599
+ worst = over;
1600
+ axis2 = a;
1601
+ }
1602
+ }
1603
+ sign = l0[axis2] < 0 ? -1 : 1;
1604
+ l1[axis2] = sign * (h[axis2] + bound + SKIN);
1605
+ }
1606
+ const r = s.r;
1607
+ const out = this.out;
1608
+ out.x = s.cx + r[0] * l1[0] + r[1] * l1[1] + r[2] * l1[2];
1609
+ out.y = s.cy + r[3] * l1[0] + r[4] * l1[1] + r[5] * l1[2];
1610
+ out.z = s.cz + r[6] * l1[0] + r[7] * l1[1] + r[8] * l1[2];
1611
+ out.nx = r[axis2] * sign;
1612
+ out.ny = r[3 + axis2] * sign;
1613
+ out.nz = r[6 + axis2] * sign;
1614
+ return true;
1615
+ }
1616
+ return false;
1617
+ }
1618
+ };
1619
+ function slideAlong(out, vx2, vy2, vz2) {
1620
+ const into = vx2 * out.nx + vy2 * out.ny + vz2 * out.nz;
1621
+ if (into >= 0) return [vx2, vy2, vz2];
1622
+ return [vx2 - into * out.nx, vy2 - into * out.ny, vz2 - into * out.nz];
1623
+ }
1328
1624
  var FLOOR_FRACTION = 0.4;
1329
1625
  var FLOOR_BOUNCE = -0.2;
1330
1626
  var ARRIVAL_FRAME = 30;
1331
1627
  var ARRIVAL_BASE = 0.3;
1332
1628
  var GROWTH_CAP_SECONDS = 3.5;
1629
+ var CARRIED_KINDS = [true, true, false, true, false];
1630
+ var PAD_OF_SIZE = 0.35;
1333
1631
  function createPuffPool(capacity) {
1334
1632
  const placeholder = { next: () => 0, between: (min2) => min2, direction: (out) => out, state: 0 };
1335
1633
  return new OrderedPool(capacity, () => ({
@@ -1424,13 +1722,24 @@ var GameFluidsThree = (() => {
1424
1722
  if (puff3.vy < 0) puff3.vy *= FLOOR_BOUNCE;
1425
1723
  }
1426
1724
  }
1427
- function updatePuffs(pool, dt, wind) {
1725
+ function updatePuffs(pool, dt, wind, confiner) {
1428
1726
  pool.compact(ageOrKeep2, dt);
1727
+ const confining = confiner !== void 0 && !confiner.empty;
1429
1728
  for (const puff3 of pool.items) {
1430
1729
  const age = puff3.t / puff3.life;
1431
1730
  const heat = puff3.h0 > 0 ? puff3.h0 * Math.pow(Math.max(0, 1 - puff3.t / puff3.hl), 1.25) : 0;
1731
+ const x0 = puff3.x;
1732
+ const y0 = puff3.y;
1733
+ const z0 = puff3.z;
1432
1734
  if (puff3.ring) moveOnRing(puff3, puff3.ring, wind);
1433
1735
  else moveFree(puff3, dt, age, heat, wind);
1736
+ if (confining && CARRIED_KINDS[puff3.kind] && confiner.confine(x0, y0, z0, puff3.x, puff3.y, puff3.z, puff3.size * PAD_OF_SIZE, dt)) {
1737
+ const out = confiner.out;
1738
+ puff3.x = out.x;
1739
+ puff3.y = out.y;
1740
+ puff3.z = out.z;
1741
+ [puff3.vx, puff3.vy, puff3.vz] = slideAlong(out, puff3.vx, puff3.vy, puff3.vz);
1742
+ }
1434
1743
  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
1744
  puff3.size = size * Math.min(1, puff3.t * ARRIVAL_FRAME + ARRIVAL_BASE);
1436
1745
  puff3.age = age;
@@ -1956,10 +2265,10 @@ var GameFluidsThree = (() => {
1956
2265
  ])
1957
2266
  ]
1958
2267
  };
1959
- function localToWorld(translation, rotation, local2) {
2268
+ function localToWorld(translation, rotation2, local2) {
1960
2269
  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])];
2270
+ const [a, c, e] = [Math.cos(rotation2[0]), Math.cos(rotation2[1]), Math.cos(rotation2[2])];
2271
+ const [b, d, f] = [Math.sin(rotation2[0]), Math.sin(rotation2[1]), Math.sin(rotation2[2])];
1963
2272
  const [ae, af, be, bf] = [a * e, a * f, b * e, b * f];
1964
2273
  return [
1965
2274
  c * e * x + -c * f * y + d * z + translation[0],
@@ -2301,22 +2610,22 @@ var GameFluidsThree = (() => {
2301
2610
  )
2302
2611
  ]
2303
2612
  };
2304
- function normalized(vector) {
2305
- const length2 = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1] + vector[2] * vector[2]) || 1;
2613
+ function normalized(vector2) {
2614
+ const length2 = Math.sqrt(vector2[0] * vector2[0] + vector2[1] * vector2[1] + vector2[2] * vector2[2]) || 1;
2306
2615
  const inverse = 1 / length2;
2307
- return [vector[0] * inverse, vector[1] * inverse, vector[2] * inverse];
2616
+ return [vector2[0] * inverse, vector2[1] * inverse, vector2[2] * inverse];
2308
2617
  }
2309
- function plus(vector, x, y, z) {
2310
- return [vector[0] + x, vector[1] + y, vector[2] + z];
2618
+ function plus(vector2, x, y, z) {
2619
+ return [vector2[0] + x, vector2[1] + y, vector2[2] + z];
2311
2620
  }
2312
- function scaledBy(vector, factor) {
2313
- return [vector[0] * factor, vector[1] * factor, vector[2] * factor];
2621
+ function scaledBy(vector2, factor) {
2622
+ return [vector2[0] * factor, vector2[1] * factor, vector2[2] * factor];
2314
2623
  }
2315
- function plusScaled(vector, direction, factor) {
2316
- return [vector[0] + direction[0] * factor, vector[1] + direction[1] * factor, vector[2] + direction[2] * factor];
2624
+ function plusScaled(vector2, direction, factor) {
2625
+ return [vector2[0] + direction[0] * factor, vector2[1] + direction[1] * factor, vector2[2] + direction[2] * factor];
2317
2626
  }
2318
- function negated(vector) {
2319
- return [-vector[0], -vector[1], -vector[2]];
2627
+ function negated(vector2) {
2628
+ return [-vector2[0], -vector2[1], -vector2[2]];
2320
2629
  }
2321
2630
  var FRAME_DUST = normalized([0, -0.3, 1]);
2322
2631
  var SPLINTER_DIRECTION = normalized([0, 0.3, 1]);
@@ -3619,49 +3928,32 @@ var GameFluidsThree = (() => {
3619
3928
  /** The leak caught fire (an incendiary or tracer round, a spark, a fire near): the page's whole sequence, the fire running along the pool to the tank and the blast. */
3620
3929
  aliasOf(FUEL_LEAK, "vehicle_leak_ignited", {})
3621
3930
  ];
3931
+ var vector = [variable("vx"), variable("vy"), variable("vz")];
3932
+ var range = (name) => [variable(name), variable(name)];
3933
+ var singleParticle = (id, system) => ({
3934
+ id,
3935
+ pageId: `particle:${id}`,
3936
+ team: "world",
3937
+ budget: { particles: 1, lights: 0 },
3938
+ loop: 1e-3,
3939
+ timeline: [
3940
+ spawn(system, {
3941
+ p: [0, 0, 0],
3942
+ v: vector,
3943
+ life: range("life"),
3944
+ size: range("size"),
3945
+ grow: variable("grow"),
3946
+ c0: variable("color"),
3947
+ a: variable("alpha"),
3948
+ g: variable("gravity"),
3949
+ drag: variable("drag")
3950
+ })
3951
+ ]
3952
+ });
3953
+ var SINGLE_PARTICLE_EFFECTS = [singleParticle("particle_puff", "normal"), singleParticle("particle_glow", "add"), singleParticle("particle_streak", "streak")];
3622
3954
  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
3955
  var WORKSHOP_EFFECTS = Object.fromEntries(ALL_EFFECTS.map((effect) => [effect.id, effect]));
3624
- 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
- }
3956
+ var GAME_EFFECTS = { ...WORKSHOP_EFFECTS, ...Object.fromEntries([...EVENT_EFFECTS, ...VIEWMODEL_EFFECTS, ...VEHICLE_EFFECTS, ...SINGLE_PARTICLE_EFFECTS].map((effect) => [effect.id, effect])) };
3665
3957
  var TABLE = {
3666
3958
  concrete: { dust: 9078138, chip: 7236195, spark: 2 },
3667
3959
  metal: { dust: 0, chip: 0, spark: 18 },
@@ -4331,8 +4623,8 @@ var GameFluidsThree = (() => {
4331
4623
  var UP12 = [0, 1, 0];
4332
4624
  var ZERO = [0, 0, 0];
4333
4625
  var FULL_TURN_DRAWN = 6.283;
4334
- function scaledSizes(range2, factor) {
4335
- return [range2[0] * Math.sqrt(factor), range2[1] * Math.sqrt(factor)];
4626
+ function scaledSizes(range22, factor) {
4627
+ return [range22[0] * Math.sqrt(factor), range22[1] * Math.sqrt(factor)];
4336
4628
  }
4337
4629
  function fireEmit(engine, key, dt, centre, radius, factor, options = {}) {
4338
4630
  const wind = options.wind ?? ZERO;
@@ -4965,8 +5257,8 @@ var GameFluidsThree = (() => {
4965
5257
  function addScorch(store, random, x, z, radius) {
4966
5258
  const variant = Math.floor(random.next() * SCORCH_VARIANTS);
4967
5259
  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 });
5260
+ const rotation2 = random.next() * 6;
5261
+ store.add({ kind: "scorch", x, z, radius, variant, lift, rotation: rotation2 });
4970
5262
  }
4971
5263
  function addRimRocks(store, ground, random, x, z, radius) {
4972
5264
  const count = Math.round(6 + radius * 7);
@@ -4995,8 +5287,8 @@ var GameFluidsThree = (() => {
4995
5287
  function addCrater(store, ground, random, spec) {
4996
5288
  ground?.dent(random, spec.x, spec.z, spec.radius, spec.depth);
4997
5289
  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 });
5290
+ const rotation2 = random.next() * 6;
5291
+ store.add({ kind: "dirt", x: spec.x, z: spec.z, radius: spec.radius * 1.02, lift: 0.045, rotation: rotation2 });
5000
5292
  addRimRocks(store, ground, random, spec.x, spec.z, spec.radius);
5001
5293
  }
5002
5294
  function addBloodSplat(store, ground, random, spec) {
@@ -5552,6 +5844,7 @@ var GameFluidsThree = (() => {
5552
5844
  state.gas = Math.max(0, state.gas - dt / DECAY.gasSeconds);
5553
5845
  state.beat *= Math.exp(-DECAY.beat * dt);
5554
5846
  }
5847
+ var PAD_OF_SIZE2 = 0.35;
5555
5848
  var clamp4 = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
5556
5849
  var lerp4 = (a, b, t) => a + (b - a) * t;
5557
5850
  function rgb(hex) {
@@ -5711,9 +6004,20 @@ var GameFluidsThree = (() => {
5711
6004
  vy2 *= k;
5712
6005
  vz2 *= k;
5713
6006
  }
5714
- const x = this.position[i3] + vx2 * dt;
6007
+ let x = this.position[i3] + vx2 * dt;
5715
6008
  let y = this.position[i3 + 1] + vy2 * dt;
5716
- const z = this.position[i3 + 2] + vz2 * dt;
6009
+ let z = this.position[i3 + 2] + vz2 * dt;
6010
+ const confiner = host.confiner;
6011
+ if (def.carried && confiner && !confiner.empty) {
6012
+ const pad = this.size0[i] * lerp4(1, def.grow, t) * PAD_OF_SIZE2;
6013
+ if (confiner.confine(this.position[i3], this.position[i3 + 1], this.position[i3 + 2], x, y, z, pad, dt)) {
6014
+ const out = confiner.out;
6015
+ x = out.x;
6016
+ y = out.y;
6017
+ z = out.z;
6018
+ [vx2, vy2, vz2] = slideAlong(out, vx2, vy2, vz2);
6019
+ }
6020
+ }
5717
6021
  if (def.ground !== "none") {
5718
6022
  const own = this.floor[i];
5719
6023
  const gy = Number.isNaN(own) ? host.groundAt(x, z) + 5e-3 : own;
@@ -5799,7 +6103,8 @@ var GameFluidsThree = (() => {
5799
6103
  alpha: 0.35,
5800
6104
  fadeIn: 0.15,
5801
6105
  cone: 0.6,
5802
- ground: "none"
6106
+ ground: "none",
6107
+ carried: true
5803
6108
  },
5804
6109
  sparks: {
5805
6110
  id: "sparks",
@@ -5843,7 +6148,8 @@ var GameFluidsThree = (() => {
5843
6148
  alpha: 0.4,
5844
6149
  fadeIn: 0.1,
5845
6150
  cone: 0.4,
5846
- ground: "none"
6151
+ ground: "none",
6152
+ carried: true
5847
6153
  },
5848
6154
  shards: {
5849
6155
  id: "shards",
@@ -6001,7 +6307,8 @@ var GameFluidsThree = (() => {
6001
6307
  fadeIn: 0.1,
6002
6308
  cone: 0.5,
6003
6309
  ground: "stop",
6004
- flutter: 0.6
6310
+ flutter: 0.6,
6311
+ carried: true
6005
6312
  },
6006
6313
  steam: {
6007
6314
  id: "steam",
@@ -6023,7 +6330,8 @@ var GameFluidsThree = (() => {
6023
6330
  alpha: 0.35,
6024
6331
  fadeIn: 0.15,
6025
6332
  cone: 0.6,
6026
- ground: "none"
6333
+ ground: "none",
6334
+ carried: true
6027
6335
  },
6028
6336
  flash: {
6029
6337
  id: "flash",
@@ -6156,7 +6464,8 @@ var GameFluidsThree = (() => {
6156
6464
  const host = {
6157
6465
  groundAt: this.options.groundAt,
6158
6466
  gravity: this.options.gravity,
6159
- land: (owner, x, y, z, color, tag) => this.listeners.get(owner)?.(x, y, z, color, tag)
6467
+ land: (owner, x, y, z, color, tag) => this.listeners.get(owner)?.(x, y, z, color, tag),
6468
+ ...this.options.confiner ? { confiner: this.options.confiner } : {}
6160
6469
  };
6161
6470
  if (this.additive.live > 0 || this.additive.drawCount > 0) this.additive.update(dt, host);
6162
6471
  if (this.normal.live > 0 || this.normal.drawCount > 0) this.normal.update(dt, host);
@@ -6169,9 +6478,9 @@ var GameFluidsThree = (() => {
6169
6478
  var TWO_PI_ROUNDED = 6.2832;
6170
6479
  var WIDE_CONE = 3.1;
6171
6480
  var UP13 = [0, 1, 0];
6172
- function drawRange(random, range2) {
6173
- if (Array.isArray(range2)) return random.between(range2[0], range2[1]);
6174
- return range2 || 0;
6481
+ function drawRange(random, range22) {
6482
+ if (Array.isArray(range22)) return random.between(range22[0], range22[1]);
6483
+ return range22 || 0;
6175
6484
  }
6176
6485
  function randomUnit(random, out) {
6177
6486
  const up = random.next() * 2 - 1;
@@ -6362,8 +6671,8 @@ var GameFluidsThree = (() => {
6362
6671
  * 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
6672
  * 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
6673
  */
6365
- clearInside(center, half, rotation = [0, 0, 0, 1]) {
6366
- const qx = -rotation[0], qy = -rotation[1], qz = -rotation[2], qw = rotation[3];
6674
+ clearInside(center, half, rotation2 = [0, 0, 0, 1]) {
6675
+ const qx = -rotation2[0], qy = -rotation2[1], qz = -rotation2[2], qw = rotation2[3];
6367
6676
  let gone = 0;
6368
6677
  for (const decal2 of this.decals) {
6369
6678
  if (!decal2.on) continue;
@@ -6484,10 +6793,14 @@ var GameFluidsThree = (() => {
6484
6793
  var DEFAULT_FADE_IN = 0.1;
6485
6794
  var FLOATS_PER_VECTOR = 3;
6486
6795
  var BYTE_MAX2 = 255;
6796
+ var CARRIED_GRAVITY = 3;
6797
+ var PAD_OF_SIZE3 = 0.35;
6487
6798
  var SpritePool = class {
6488
- constructor(capacity, context) {
6799
+ /** `carried`: the normal-blend pool (smoke, dust, steam), whose light sprites go round the closed spaces; the additive one (flames, sparks) does not. */
6800
+ constructor(capacity, context, carried = false) {
6489
6801
  __publicField2(this, "capacity", capacity);
6490
6802
  __publicField2(this, "context", context);
6803
+ __publicField2(this, "carried", carried);
6491
6804
  __publicField2(this, "count", 0);
6492
6805
  __publicField2(this, "position");
6493
6806
  __publicField2(this, "velocity");
@@ -6587,6 +6900,7 @@ var GameFluidsThree = (() => {
6587
6900
  }
6588
6901
  update(dt, leaveDecal) {
6589
6902
  const { position, velocity } = this;
6903
+ const confiner = this.context.confiner;
6590
6904
  for (let index = this.count - 1; index >= 0; index--) {
6591
6905
  this.life[index] -= dt;
6592
6906
  if (this.life[index] <= 0) {
@@ -6600,9 +6914,25 @@ var GameFluidsThree = (() => {
6600
6914
  velocity[offset] *= factor;
6601
6915
  velocity[offset + 1] *= factor;
6602
6916
  velocity[offset + 2] *= factor;
6917
+ const x0 = position[offset];
6918
+ const y0 = position[offset + 1];
6919
+ const z0 = position[offset + 2];
6603
6920
  position[offset] += velocity[offset] * dt;
6604
6921
  position[offset + 1] += velocity[offset + 1] * dt;
6605
6922
  position[offset + 2] += velocity[offset + 2] * dt;
6923
+ if (this.carried && confiner && !confiner.empty && Math.abs(this.gravity[index]) < CARRIED_GRAVITY) {
6924
+ const size = this.startSize[index] * (1 + (this.growth[index] - 1) * age);
6925
+ if (confiner.confine(x0, y0, z0, position[offset], position[offset + 1], position[offset + 2], size * PAD_OF_SIZE3, dt)) {
6926
+ const out = confiner.out;
6927
+ position[offset] = out.x;
6928
+ position[offset + 1] = out.y;
6929
+ position[offset + 2] = out.z;
6930
+ const [vx2, vy2, vz2] = slideAlong(out, velocity[offset], velocity[offset + 1], velocity[offset + 2]);
6931
+ velocity[offset] = vx2;
6932
+ velocity[offset + 1] = vy2;
6933
+ velocity[offset + 2] = vz2;
6934
+ }
6935
+ }
6606
6936
  const mode = this.groundMode[index];
6607
6937
  if (mode) {
6608
6938
  const groundHeight = this.context.groundAt(position[offset], position[offset + 2]);
@@ -6879,9 +7209,9 @@ var GameFluidsThree = (() => {
6879
7209
  __publicField2(this, "placement");
6880
7210
  __publicField2(this, "accumulators", /* @__PURE__ */ new Map());
6881
7211
  const profile = options.profile ?? PROFILES.high;
6882
- this.context = { random: options.random, groundAt: options.groundAt ?? workshopGround, quality: { level: 0, mult: 1 }, trace: options.trace };
7212
+ this.context = { random: options.random, groundAt: options.groundAt ?? workshopGround, quality: { level: 0, mult: 1 }, trace: options.trace, ...options.confiner ? { confiner: options.confiner } : {} };
6883
7213
  this.add = new SpritePool(profile.sprites.add, this.context);
6884
- this.normal = new SpritePool(profile.sprites.normal, this.context);
7214
+ this.normal = new SpritePool(profile.sprites.normal, this.context, true);
6885
7215
  this.streaks = new StreakPool(profile.streaks, this.context);
6886
7216
  this.debris = new DebrisPool(this.context);
6887
7217
  this.decals = new DecalPool(this.context);
@@ -6917,13 +7247,13 @@ var GameFluidsThree = (() => {
6917
7247
  return [origin[0] + cos2 * x + sin2 * z, origin[1] + point[1] - anchor[1], origin[2] - sin2 * x + cos2 * z];
6918
7248
  }
6919
7249
  /** A direction or velocity of the recipe's space in the world. */
6920
- turn(vector) {
7250
+ turn(vector2) {
6921
7251
  const placement = this.placement;
6922
- if (!placement) return vector;
6923
- if (placement.orientation) return rotate(placement.orientation, vector);
7252
+ if (!placement) return vector2;
7253
+ if (placement.orientation) return rotate(placement.orientation, vector2);
6924
7254
  const cos2 = Math.cos(placement.yaw);
6925
7255
  const sin2 = Math.sin(placement.yaw);
6926
- return [cos2 * vector[0] + sin2 * vector[2], vector[1], -sin2 * vector[0] + cos2 * vector[2]];
7256
+ return [cos2 * vector2[0] + sin2 * vector2[2], vector2[1], -sin2 * vector2[0] + cos2 * vector2[2]];
6927
7257
  }
6928
7258
  placed(options) {
6929
7259
  if (!this.placement) return options;
@@ -7069,12 +7399,14 @@ var GameFluidsThree = (() => {
7069
7399
  const groundAt = options.groundAt ?? (ground ? (x, z) => ground.heightAt(x, z) : () => 0);
7070
7400
  const handles = { next: 0, events: 0 };
7071
7401
  const low = options.profile === "low";
7402
+ const enclosures = new Enclosures(20973 + (options.seed ?? 0));
7072
7403
  const spriteSystem = new SpriteKindSystem({
7073
7404
  capacity: options.sprites?.capacity ?? (low ? 1250 : 2500),
7074
7405
  density: options.sprites?.density ?? (low ? 0.6 : 1),
7075
7406
  seed: 2654435761 + (options.seed ?? 0),
7076
7407
  groundAt,
7077
7408
  gravity: options.gravity ?? DEFAULT_GRAVITY,
7409
+ confiner: enclosures,
7078
7410
  ...options.sprites?.kinds ? { kinds: options.sprites.kinds } : {}
7079
7411
  });
7080
7412
  const core = {
@@ -7124,7 +7456,7 @@ var GameFluidsThree = (() => {
7124
7456
  return [camera2.x, camera2.y, camera2.z];
7125
7457
  }, forward: (local2) => [local2[0] ?? 0, local2[1] ?? 0, local2[2] ?? 0] };
7126
7458
  function workshop() {
7127
- return engine ?? (engine = new WorkshopEngine({ random: sharedRandom, profile, groundAt }));
7459
+ return engine ?? (engine = new WorkshopEngine({ random: sharedRandom, profile, groundAt, confiner: enclosures }));
7128
7460
  }
7129
7461
  function effectPlayer() {
7130
7462
  return player ?? (player = createEffectPlayer({ engine: workshop(), effects, view }));
@@ -7183,6 +7515,10 @@ var GameFluidsThree = (() => {
7183
7515
  puff3.vz += dz * strength;
7184
7516
  }
7185
7517
  },
7518
+ enclose(id, enclosure) {
7519
+ enclosures.set(id, enclosure);
7520
+ },
7521
+ enclosures: () => enclosures,
7186
7522
  stop(handle) {
7187
7523
  player?.stop(handle);
7188
7524
  const fires = core.fires;
@@ -7210,7 +7546,7 @@ var GameFluidsThree = (() => {
7210
7546
  core.clock.time += dt;
7211
7547
  core.schedule.update(core.clock.time);
7212
7548
  updateRings(core.rings, dt);
7213
- updatePuffs(core.puffs, dt, wind);
7549
+ updatePuffs(core.puffs, dt, wind, enclosures);
7214
7550
  updateSparks(core.sparks, { dt, groundAt });
7215
7551
  updateGlows(core.glows, dt);
7216
7552
  updateShocks(core.shocks, core.puffs.items, dt);
@@ -7278,16 +7614,22 @@ var GameFluidsThree = (() => {
7278
7614
  };
7279
7615
  }
7280
7616
 
7281
- // ../game-contracts/dist/index.js
7282
- var KIT_ORIGIN_KEY = "kitOrigin";
7283
- function markKitOrigin(object, origin) {
7284
- const node = object;
7285
- node.userData = node.userData ?? {};
7286
- node.userData[KIT_ORIGIN_KEY] = { ...origin };
7287
- return object;
7288
- }
7617
+ // ../game-render-three/dist/index.js
7618
+ var DEFAULT_ANGLE = Math.PI / 3;
7619
+ var LIGHT_PRIORITY = {
7620
+ blast: 900,
7621
+ rocket: 800,
7622
+ muzzle: 750,
7623
+ fire: 600,
7624
+ flare: 500,
7625
+ headlight: 400,
7626
+ weaponLight: 400,
7627
+ lamp: 200,
7628
+ street: 150
7629
+ };
7630
+ var EFFECT_STEP_SECONDS = 1 / 50;
7289
7631
 
7290
- // ../game-vfx-three/dist/index.js
7632
+ // ../game-vfx-three/dist/chunk-ALDCQ5RH.js
7291
7633
  var GLOW_VERTEX = `attribute vec4 aP;attribute vec4 aC;varying vec2 vU;varying vec4 vC;
7292
7634
  void main(){vec3 R=vec3(viewMatrix[0][0],viewMatrix[1][0],viewMatrix[2][0]);vec3 Up=vec3(viewMatrix[0][1],viewMatrix[1][1],viewMatrix[2][1]);
7293
7635
  vec3 wp=aP.xyz+(R*position.x+Up*position.y)*aP.w;vU=position.xy;vC=aC;gl_Position=projectionMatrix*viewMatrix*vec4(wp,1.);}`;
@@ -7566,8 +7908,26 @@ void main(){
7566
7908
  }
7567
7909
  };
7568
7910
  }
7569
- function createLightLayer(THREE, pool, scale2) {
7911
+ var LIGHT_ON_THRESHOLD = 0.05;
7912
+ var FIRE_COLOR = 16742954;
7913
+ var FIRE_DECAY = 2;
7914
+ var BLAST_DECAY = 2;
7915
+ function followEntry(budget, handles, index, entry, request) {
7916
+ const handle = handles[index] ?? null;
7917
+ if (entry.intensity <= LIGHT_ON_THRESHOLD) {
7918
+ handle?.release({ fadeMs: 0 });
7919
+ handles[index] = null;
7920
+ return;
7921
+ }
7922
+ if (handle?.alive) {
7923
+ handle.update({ color: request.color, intensity: request.intensity, distance: request.distance, position: request.position });
7924
+ return;
7925
+ }
7926
+ handles[index] = budget.acquire(request);
7927
+ }
7928
+ function createLightLayer(THREE, pool, scale2, budget) {
7570
7929
  const group = new THREE.Group();
7930
+ if (budget) return createBudgetedLightLayer(group, pool, scale2, budget);
7571
7931
  const blast = pool.blast.map(() => {
7572
7932
  const light = new THREE.PointLight(16752720, 0, 30, 2);
7573
7933
  light.position.set(0, -50, 0);
@@ -7606,6 +7966,30 @@ void main(){
7606
7966
  }
7607
7967
  };
7608
7968
  }
7969
+ function createBudgetedLightLayer(group, pool, scale2, budget) {
7970
+ const blast = pool.blast.map(() => null);
7971
+ const fire = pool.fire.map(() => null);
7972
+ return {
7973
+ group,
7974
+ sync() {
7975
+ let lit = 0;
7976
+ pool.blast.forEach((entry, index) => {
7977
+ const request = { owner: "vfx.blast", priority: LIGHT_PRIORITY.blast, urgent: true, color: entry.color, intensity: entry.intensity * scale2, distance: entry.distance, decay: BLAST_DECAY, position: [entry.x, entry.y, entry.z] };
7978
+ followEntry(budget, blast, index, entry, request);
7979
+ if (entry.intensity > LIGHT_ON_THRESHOLD) lit++;
7980
+ });
7981
+ pool.fire.forEach((entry, index) => {
7982
+ const request = { owner: "vfx.fire", priority: LIGHT_PRIORITY.fire, color: FIRE_COLOR, intensity: entry.intensity * scale2, distance: entry.distance, decay: FIRE_DECAY, position: [entry.x, entry.y, entry.z] };
7983
+ followEntry(budget, fire, index, entry, request);
7984
+ if (entry.intensity > LIGHT_ON_THRESHOLD) lit++;
7985
+ });
7986
+ return lit;
7987
+ },
7988
+ dispose() {
7989
+ for (const handle of [...blast, ...fire]) handle?.release({ fadeMs: 0 });
7990
+ }
7991
+ };
7992
+ }
7609
7993
  var SCORCH_SEED = 5;
7610
7994
  var TEXTURE_SIZE = 256;
7611
7995
  var FULL_TURN = Math.PI * 2;
@@ -7739,6 +8123,7 @@ void main(){
7739
8123
  group.add(mesh2);
7740
8124
  return mesh2;
7741
8125
  }
8126
+ let probeGeometry;
7742
8127
  const groundMarks = [];
7743
8128
  const spareGeometries = [];
7744
8129
  const blood = [];
@@ -7844,6 +8229,15 @@ void main(){
7844
8229
  rockMesh();
7845
8230
  plateMesh();
7846
8231
  },
8232
+ probes() {
8233
+ probeGeometry ?? (probeGeometry = new THREE.PlaneGeometry(1e-3, 1e-3).rotateX(-Math.PI / 2));
8234
+ return [...scorchMaterials, dirtMaterial].map((material) => {
8235
+ const mesh2 = new THREE.Mesh(probeGeometry, material);
8236
+ mesh2.receiveShadow = true;
8237
+ mesh2.frustumCulled = false;
8238
+ return mesh2;
8239
+ });
8240
+ },
7847
8241
  update(dt) {
7848
8242
  for (let index = 0; index < blood.length; index++) {
7849
8243
  const drawn = blood[index];
@@ -7856,6 +8250,7 @@ void main(){
7856
8250
  dispose() {
7857
8251
  clearMarks();
7858
8252
  for (const mesh2 of [instanced.blood, instanced.rock, instanced.plate]) mesh2?.dispose();
8253
+ probeGeometry?.dispose();
7859
8254
  for (const item of owned) item.dispose();
7860
8255
  }
7861
8256
  };
@@ -8431,7 +8826,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8431
8826
  }
8432
8827
  };
8433
8828
  }
8434
- function createWorkshopLayers(THREE, engine, pointScale, lightScale) {
8829
+ function createWorkshopLayers(THREE, engine, pointScale, lightScale, budget) {
8435
8830
  const group = new THREE.Group();
8436
8831
  group.name = "workshop";
8437
8832
  const layers = [
@@ -8446,15 +8841,30 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8446
8841
  for (const layer2 of layers) group.add(layer2.object);
8447
8842
  const meshes = createMeshLayer(THREE, engine.meshes);
8448
8843
  group.add(meshes.group);
8449
- const flashLights = engine.lights.lights.map(() => new THREE.PointLight(16760944, 0, 14));
8450
- const fireLights = engine.lights.fire.map(() => new THREE.PointLight(FIRE_LIGHT_COLOR, 0, FIRE_LIGHT_DISTANCE));
8451
- for (const light of [...flashLights, ...fireLights]) group.add(light);
8844
+ const lights = budget ? budgetedLights(engine, lightScale, budget) : legacyLights(THREE, engine, lightScale, group);
8452
8845
  return {
8453
8846
  group,
8454
8847
  sync() {
8455
8848
  let drawCalls = 0;
8456
8849
  for (const layer2 of layers) if (layer2.sync()) drawCalls++;
8457
8850
  drawCalls += meshes.sync();
8851
+ lights.sync();
8852
+ return drawCalls;
8853
+ },
8854
+ dispose() {
8855
+ for (const layer2 of layers) layer2.dispose();
8856
+ meshes.dispose();
8857
+ lights.dispose();
8858
+ group.clear();
8859
+ }
8860
+ };
8861
+ }
8862
+ function legacyLights(THREE, engine, lightScale, group) {
8863
+ const flashLights = engine.lights.lights.map(() => new THREE.PointLight(16760944, 0, 14));
8864
+ const fireLights = engine.lights.fire.map(() => new THREE.PointLight(FIRE_LIGHT_COLOR, 0, FIRE_LIGHT_DISTANCE));
8865
+ for (const light of [...flashLights, ...fireLights]) group.add(light);
8866
+ return {
8867
+ sync() {
8458
8868
  engine.lights.lights.forEach((source, index) => {
8459
8869
  const light = flashLights[index];
8460
8870
  light.position.set(source.x, source.y, source.z);
@@ -8467,12 +8877,27 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8467
8877
  light.position.set(source.x, source.y, source.z);
8468
8878
  light.intensity = source.intensity * lightScale;
8469
8879
  });
8470
- return drawCalls;
8471
8880
  },
8472
8881
  dispose() {
8473
- for (const layer2 of layers) layer2.dispose();
8474
- meshes.dispose();
8475
- group.clear();
8882
+ }
8883
+ };
8884
+ }
8885
+ function budgetedLights(engine, lightScale, budget) {
8886
+ const flash2 = engine.lights.lights.map(() => null);
8887
+ const fire = engine.lights.fire.map(() => null);
8888
+ return {
8889
+ sync() {
8890
+ engine.lights.lights.forEach((source, index) => {
8891
+ const request = { owner: "vfx.workshop", priority: LIGHT_PRIORITY.blast, urgent: true, color: source.color, intensity: source.intensity * lightScale, distance: source.distance, position: [source.x, source.y, source.z] };
8892
+ followEntry(budget, flash2, index, source, request);
8893
+ });
8894
+ engine.lights.fire.forEach((source, index) => {
8895
+ const request = { owner: "vfx.workshop", priority: LIGHT_PRIORITY.fire, color: FIRE_LIGHT_COLOR, intensity: source.intensity * lightScale, distance: FIRE_LIGHT_DISTANCE, position: [source.x, source.y, source.z] };
8896
+ followEntry(budget, fire, index, source, request);
8897
+ });
8898
+ },
8899
+ dispose() {
8900
+ for (const handle of [...flash2, ...fire]) handle?.release({ fadeMs: 0 });
8476
8901
  }
8477
8902
  };
8478
8903
  }
@@ -8530,7 +8955,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8530
8955
  const chunks = createChunkLayer(THREE, pools.chunks, { rock: rockGeometry, plate: plateGeometry }, colorOfChunk);
8531
8956
  const shocks = createShockLayer(THREE, pools.shocks);
8532
8957
  const sprites = createSpriteLayer(THREE, world.sprites(), options.pointScale ?? 700);
8533
- const lights = createLightLayer(THREE, pools.lights, options.lightScale ?? Math.PI);
8958
+ const lights = createLightLayer(THREE, pools.lights, options.lightScale ?? Math.PI, options.lights);
8534
8959
  const marks = createMarkLayer(THREE, { ground: pools.ground, createCanvas: options.createCanvas, rockGeometry, plateGeometry, ...options.markLimits });
8535
8960
  root.add(puffs.mesh, sparks.mesh, glows.mesh, chunks.rocks, chunks.plates, shocks.group, lights.group, sprites.additive, sprites.normal, marks.group);
8536
8961
  const stopMarks = world.onMarks((event) => marks.handle(event));
@@ -8563,7 +8988,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8563
8988
  workshopLayers?.dispose();
8564
8989
  if (workshopLayers) root.remove(workshopLayers.group);
8565
8990
  workshopEngine = engine;
8566
- workshopLayers = createWorkshopLayers(THREE, engine, options.pointScale ?? 700, 1);
8991
+ workshopLayers = createWorkshopLayers(THREE, engine, options.pointScale ?? 700, 1, options.lights);
8567
8992
  root.add(workshopLayers.group);
8568
8993
  applyLayers();
8569
8994
  }
@@ -8603,6 +9028,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8603
9028
  });
8604
9029
  },
8605
9030
  stop: (handle) => world.stop(handle),
9031
+ // a module's closed space (a shut car's cabin): the world's smoke, dust and steam go round it
9032
+ enclose: (id, enclosure) => world.enclose(id, enclosure),
8606
9033
  configure(settings) {
8607
9034
  const band = settings.lod_band;
8608
9035
  if (typeof band === "string" && band in LOD_BANDS) world.forceLod(LOD_BANDS[band] ?? null);
@@ -8634,7 +9061,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8634
9061
  const meshes = createMeshLayerProbes(THREE);
8635
9062
  for (const mesh2 of meshes) mesh2.frustumCulled = false;
8636
9063
  const group = new THREE.Group();
8637
- group.add(...meshes);
9064
+ group.add(...meshes, ...marks.probes());
8638
9065
  anchors = { meshes, group };
8639
9066
  return { group, dispose: () => {
8640
9067
  } };
@@ -9502,7 +9929,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
9502
9929
  }
9503
9930
  };
9504
9931
  }
9505
- var range = (random, from, to) => from + random() * (to - from);
9932
+ var range2 = (random, from, to) => from + random() * (to - from);
9506
9933
  function updateDrip(drip, bead, layout, random, launch, dt) {
9507
9934
  if (!drip) {
9508
9935
  bead.visible = false;
@@ -9521,11 +9948,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
9521
9948
  if (drip.elapsed < drip.every) return;
9522
9949
  const origin = [bead.position[0], bead.position[1] + -radius * 1e-3, bead.position[2]];
9523
9950
  if (isWorkshop) {
9524
- const sideX2 = range(random, -0.025, 0.025);
9525
- const sideZ2 = range(random, -0.025, 0.025);
9526
- launch(origin, [sideX2, -0.2, sideZ2], drip.radius * range(random, 0.85, 1.15));
9951
+ const sideX2 = range2(random, -0.025, 0.025);
9952
+ const sideZ2 = range2(random, -0.025, 0.025);
9953
+ launch(origin, [sideX2, -0.2, sideZ2], drip.radius * range2(random, 0.85, 1.15));
9527
9954
  drip.elapsed = 0;
9528
- drip.every = drip.base * range(random, 0.75, 1.25);
9955
+ drip.every = drip.base * range2(random, 0.75, 1.25);
9529
9956
  return;
9530
9957
  }
9531
9958
  const sideX = (random() - 0.5) * 0.05;
@@ -9858,7 +10285,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
9858
10285
  var DEFAULT_CURB_ROWS = 12;
9859
10286
  var RULES = { dropCapacity: 160, curbCatch: false, guardBehindWall: false, clipRoadEnd: false, dripStyle: "padrao" };
9860
10287
  var along3 = (origin, axis2, distance2) => add2(origin, scale(axis2, distance2));
9861
- var reject = (vector, axis2) => sub3(vector, scale(axis2, dot2(vector, axis2)));
10288
+ var reject = (vector2, axis2) => sub3(vector2, scale(axis2, dot2(vector2, axis2)));
9862
10289
  function layoutFromSurfaces(options) {
9863
10290
  const columns = options.columns ?? DEFAULT_COLUMNS;
9864
10291
  const totalRows = options.rows ?? DEFAULT_ROWS;
@@ -10463,6 +10890,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
10463
10890
  const root = new THREE.Group();
10464
10891
  root.name = "fluid_renderer";
10465
10892
  scene.add(root);
10893
+ if (!options.vfx) warnOnce("game-fluids-three.own-effects-world", "game-fluids-three made an effects world of its own because the host lent none (options.vfx); pass the host effects so the effects share one budget");
10466
10894
  const vfx = options.vfx ?? createVfx({ profile: quality, gravity: fluids.gravity, sprites: { capacity: profile.maxParticles } });
10467
10895
  const ownVfx = options.vfx ? void 0 : createVfxRenderer(THREE, { scene, camera: options.camera ?? new THREE.PerspectiveCamera(), vfx, pointScale: options.pointScale ?? 600 });
10468
10896
  const particles = new FluidSprites(vfx, `fluids-${++rendererCount}`);
@@ -10673,7 +11101,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
10673
11101
  const direction = new THREE.Vector3();
10674
11102
  const location = new THREE.Vector3();
10675
11103
  const scale2 = new THREE.Vector3();
10676
- const rotation = new THREE.Quaternion();
11104
+ const rotation2 = new THREE.Quaternion();
10677
11105
  const matrix = new THREE.Matrix4();
10678
11106
  return {
10679
11107
  drops,
@@ -10693,9 +11121,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
10693
11121
  const o = i * 3;
10694
11122
  const speed = Math.hypot(velocity[o], velocity[o + 1], velocity[o + 2]);
10695
11123
  direction.set(velocity[o], velocity[o + 1], velocity[o + 2]).normalize();
10696
- rotation.setFromUnitVectors(up, direction);
11124
+ rotation2.setFromUnitVectors(up, direction);
10697
11125
  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));
11126
+ 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
11127
  drops.setMatrixAt(i, matrix);
10700
11128
  drawn++;
10701
11129
  }
@@ -11168,7 +11596,7 @@ void main(){
11168
11596
  update(dt) {
11169
11597
  this.time += dt;
11170
11598
  const viewer = this.viewer();
11171
- const range2 = this.options.range ?? DEFAULT_RANGE;
11599
+ const range3 = this.options.range ?? DEFAULT_RANGE;
11172
11600
  const budget = this.options.budgetMs ?? DEFAULT_BUDGET_MS;
11173
11601
  const drying = dt * (this.options.dryingSpeed ?? 1);
11174
11602
  const order = [...this.grids].sort((a, b) => viewer ? distance(viewer.position, a.center) - distance(viewer.position, b.center) : 0);
@@ -11176,7 +11604,7 @@ void main(){
11176
11604
  let starved = 0;
11177
11605
  let uploaded = 0;
11178
11606
  for (const grid of order) {
11179
- if (viewer && distance(viewer.position, grid.center) > range2 * BAKE_RANGE_FACTOR) {
11607
+ if (viewer && distance(viewer.position, grid.center) > range3 * BAKE_RANGE_FACTOR) {
11180
11608
  this.release(grid, "range");
11181
11609
  continue;
11182
11610
  }