@adatechnology/game-destruction-three 0.7.1 → 0.7.3

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.
@@ -1361,77 +1361,77 @@ var GameDestructionThree = (() => {
1361
1361
  }));
1362
1362
  }
1363
1363
  function spawnPuff(pool, random, init) {
1364
- const puff2 = pool.acquire();
1365
- puff2.x = init.x ?? 0;
1366
- puff2.y = init.y ?? 0;
1367
- puff2.z = init.z ?? 0;
1368
- puff2.vx = init.vx ?? 0;
1369
- puff2.vy = init.vy ?? 0;
1370
- puff2.vz = init.vz ?? 0;
1371
- puff2.s0 = init.s0 ?? 0.2;
1372
- puff2.s1 = init.s1 ?? 1;
1373
- puff2.sg = init.sg ?? 0.1;
1374
- puff2.gk = init.gk ?? 6;
1375
- puff2.life = init.life ?? 3;
1376
- puff2.t = 0;
1377
- puff2.h0 = init.h0 ?? 0;
1378
- puff2.hl = init.hl ?? 0.4;
1379
- puff2.drag = init.drag ?? 3;
1380
- puff2.buoy = init.buoy ?? 0.5;
1381
- puff2.grav = init.grav ?? 0;
1382
- puff2.kind = init.kind ?? 0;
1383
- puff2.seed = random.next();
1384
- puff2.wind = init.wind ?? 1;
1385
- puff2.color = init.color ?? SMOKE_COLORS.grey;
1386
- puff2.ring = init.ring ?? null;
1387
- puff2.ph = init.ph ?? 0;
1388
- puff2.th = init.th ?? 0;
1389
- puff2.rng = random;
1390
- puff2.age = 0;
1391
- puff2.heat = 0;
1392
- puff2.size = 0;
1393
- return puff2;
1394
- }
1395
- function ageOrKeep2(puff2, dt) {
1396
- puff2.t += dt;
1397
- return puff2.t < puff2.life;
1398
- }
1399
- function moveOnRing(puff2, ring2, wind) {
1364
+ const puff3 = pool.acquire();
1365
+ puff3.x = init.x ?? 0;
1366
+ puff3.y = init.y ?? 0;
1367
+ puff3.z = init.z ?? 0;
1368
+ puff3.vx = init.vx ?? 0;
1369
+ puff3.vy = init.vy ?? 0;
1370
+ puff3.vz = init.vz ?? 0;
1371
+ puff3.s0 = init.s0 ?? 0.2;
1372
+ puff3.s1 = init.s1 ?? 1;
1373
+ puff3.sg = init.sg ?? 0.1;
1374
+ puff3.gk = init.gk ?? 6;
1375
+ puff3.life = init.life ?? 3;
1376
+ puff3.t = 0;
1377
+ puff3.h0 = init.h0 ?? 0;
1378
+ puff3.hl = init.hl ?? 0.4;
1379
+ puff3.drag = init.drag ?? 3;
1380
+ puff3.buoy = init.buoy ?? 0.5;
1381
+ puff3.grav = init.grav ?? 0;
1382
+ puff3.kind = init.kind ?? 0;
1383
+ puff3.seed = random.next();
1384
+ puff3.wind = init.wind ?? 1;
1385
+ puff3.color = init.color ?? SMOKE_COLORS.grey;
1386
+ puff3.ring = init.ring ?? null;
1387
+ puff3.ph = init.ph ?? 0;
1388
+ puff3.th = init.th ?? 0;
1389
+ puff3.rng = random;
1390
+ puff3.age = 0;
1391
+ puff3.heat = 0;
1392
+ puff3.size = 0;
1393
+ return puff3;
1394
+ }
1395
+ function ageOrKeep2(puff3, dt) {
1396
+ puff3.t += dt;
1397
+ return puff3.t < puff3.life;
1398
+ }
1399
+ function moveOnRing(puff3, ring2, wind) {
1400
1400
  const ringTime = ring2.t;
1401
1401
  const centerY = ring2.y0 + ring2.rise * (1 - Math.exp(-ringTime * ring2.rk));
1402
1402
  const majorRadius = ring2.R0 + (ring2.R1 - ring2.R0) * (1 - Math.exp(-ringTime * 1.6));
1403
- const theta = puff2.th + ringTime * ring2.roll;
1403
+ const theta = puff3.th + ringTime * ring2.roll;
1404
1404
  const minorRadius = ring2.r * (0.8 + 0.4 * Math.min(1, ringTime));
1405
1405
  const radius = majorRadius + minorRadius * Math.cos(theta);
1406
- puff2.x = ring2.x + Math.cos(puff2.ph) * radius + wind.x * ringTime * 0.6;
1407
- puff2.z = ring2.z + Math.sin(puff2.ph) * radius + wind.z * ringTime * 0.6;
1408
- puff2.y = centerY + minorRadius * Math.sin(theta);
1409
- }
1410
- function moveFree(puff2, dt, age, heat, wind) {
1411
- const damping = Math.exp(-puff2.drag * dt);
1412
- puff2.vx *= damping;
1413
- puff2.vy *= damping;
1414
- puff2.vz *= damping;
1415
- puff2.vy += (puff2.buoy * (heat > 0 ? 1.6 : 1) - puff2.grav) * dt;
1416
- puff2.x += (puff2.vx + wind.x * puff2.wind * age) * dt;
1417
- puff2.y += puff2.vy * dt;
1418
- puff2.z += (puff2.vz + wind.z * puff2.wind * age) * dt;
1419
- if (puff2.y < puff2.s0 * FLOOR_FRACTION) {
1420
- puff2.y = puff2.s0 * FLOOR_FRACTION;
1421
- if (puff2.vy < 0) puff2.vy *= FLOOR_BOUNCE;
1406
+ puff3.x = ring2.x + Math.cos(puff3.ph) * radius + wind.x * ringTime * 0.6;
1407
+ puff3.z = ring2.z + Math.sin(puff3.ph) * radius + wind.z * ringTime * 0.6;
1408
+ puff3.y = centerY + minorRadius * Math.sin(theta);
1409
+ }
1410
+ function moveFree(puff3, dt, age, heat, wind) {
1411
+ const damping = Math.exp(-puff3.drag * dt);
1412
+ puff3.vx *= damping;
1413
+ puff3.vy *= damping;
1414
+ puff3.vz *= damping;
1415
+ puff3.vy += (puff3.buoy * (heat > 0 ? 1.6 : 1) - puff3.grav) * dt;
1416
+ puff3.x += (puff3.vx + wind.x * puff3.wind * age) * dt;
1417
+ puff3.y += puff3.vy * dt;
1418
+ puff3.z += (puff3.vz + wind.z * puff3.wind * age) * dt;
1419
+ if (puff3.y < puff3.s0 * FLOOR_FRACTION) {
1420
+ puff3.y = puff3.s0 * FLOOR_FRACTION;
1421
+ if (puff3.vy < 0) puff3.vy *= FLOOR_BOUNCE;
1422
1422
  }
1423
1423
  }
1424
1424
  function updatePuffs(pool, dt, wind) {
1425
1425
  pool.compact(ageOrKeep2, dt);
1426
- for (const puff2 of pool.items) {
1427
- const age = puff2.t / puff2.life;
1428
- const heat = puff2.h0 > 0 ? puff2.h0 * Math.pow(Math.max(0, 1 - puff2.t / puff2.hl), 1.25) : 0;
1429
- if (puff2.ring) moveOnRing(puff2, puff2.ring, wind);
1430
- else moveFree(puff2, dt, age, heat, wind);
1431
- const size = puff2.s0 + (puff2.s1 - puff2.s0) * (1 - Math.exp(-puff2.t * puff2.gk)) + puff2.sg * Math.min(puff2.t, GROWTH_CAP_SECONDS);
1432
- puff2.size = size * Math.min(1, puff2.t * ARRIVAL_FRAME + ARRIVAL_BASE);
1433
- puff2.age = age;
1434
- puff2.heat = heat;
1426
+ for (const puff3 of pool.items) {
1427
+ const age = puff3.t / puff3.life;
1428
+ const heat = puff3.h0 > 0 ? puff3.h0 * Math.pow(Math.max(0, 1 - puff3.t / puff3.hl), 1.25) : 0;
1429
+ if (puff3.ring) moveOnRing(puff3, puff3.ring, wind);
1430
+ else moveFree(puff3, dt, age, heat, wind);
1431
+ const size = puff3.s0 + (puff3.s1 - puff3.s0) * (1 - Math.exp(-puff3.t * puff3.gk)) + puff3.sg * Math.min(puff3.t, GROWTH_CAP_SECONDS);
1432
+ puff3.size = size * Math.min(1, puff3.t * ARRIVAL_FRAME + ARRIVAL_BASE);
1433
+ puff3.age = age;
1434
+ puff3.heat = heat;
1435
1435
  }
1436
1436
  }
1437
1437
  var GROUND_LIFT = 0.02;
@@ -1488,9 +1488,9 @@ var GameDestructionThree = (() => {
1488
1488
  var HOT_GLOW_COLOR = [1, 0.45, 0.12];
1489
1489
  function addPuff(core, random, init) {
1490
1490
  core.spawned.puffs++;
1491
- const puff2 = spawnPuff(core.puffs, random, init);
1492
- core.trace?.puff?.(puff2);
1493
- return puff2;
1491
+ const puff3 = spawnPuff(core.puffs, random, init);
1492
+ core.trace?.puff?.(puff3);
1493
+ return puff3;
1494
1494
  }
1495
1495
  function addSpark(core, init) {
1496
1496
  core.spawned.sparks++;
@@ -1672,6 +1672,7 @@ var GameDestructionThree = (() => {
1672
1672
  var neg = call("neg");
1673
1673
  var abs = call("abs");
1674
1674
  var sqrt = call("sqrt");
1675
+ var exp = call("exp");
1675
1676
  var sin = call("sin");
1676
1677
  var cos = call("cos");
1677
1678
  var atan2 = call("atan2");
@@ -1717,10 +1718,12 @@ var GameDestructionThree = (() => {
1717
1718
  var INDEX = { $: "index" };
1718
1719
  var QUALITY = { $: "quality" };
1719
1720
  var FRESH = { $: "fresh" };
1721
+ var DIRECTION = { $: "direction" };
1720
1722
  var CAMERA_POSITION = { $: "cameraPosition" };
1721
1723
  var burst = (system, count, spawn2) => ({ op: "burst", system, count, spawn: spawn2 });
1722
1724
  var spawn = (system, options) => ({ op: "spawn", system, spawn: options });
1723
1725
  var emit = (key, system, rate, options) => ({ op: "emit", key, system, rate, spawn: options });
1726
+ var mesh = (options) => ({ op: "mesh", spawn: options });
1724
1727
  var debris = (options) => ({ op: "debris", spawn: options });
1725
1728
  var decal = (...args) => ({ op: "decal", args });
1726
1729
  var ring = (...args) => ({ op: "ring", args });
@@ -2732,6 +2735,241 @@ var GameDestructionThree = (() => {
2732
2735
  )
2733
2736
  ]
2734
2737
  };
2738
+ var SPOTS_X = [-3, 1, 5];
2739
+ var SPOTS_Z = [-1, -2.4, 0.4];
2740
+ var FOG_AND_ASH = {
2741
+ id: "fog_and_ash",
2742
+ pageId: "neblina",
2743
+ team: "world",
2744
+ budget: { particles: 120, lights: 1 },
2745
+ loop: 0,
2746
+ view: { eye: [3, 1.8, 7.5], target: [-3, 1.2, -1] },
2747
+ setup: [
2748
+ cue("fog", { density: 0.075 }),
2749
+ cue("lamp_cone", { visible: true }),
2750
+ cue("zombie_visible", { index: -1, visible: false }),
2751
+ repeat(
2752
+ SPOTS_X.length,
2753
+ set("turn", rnd(-0.6, 0.6)),
2754
+ behavior("walkers", "place", { index: INDEX, x: pick(SPOTS_X, INDEX), z: pick(SPOTS_Z, INDEX), facing: variable("turn") })
2755
+ )
2756
+ ],
2757
+ timeline: [],
2758
+ tick: [
2759
+ emit("fog", "normal", 5, { p: [rnd(-14, 10), rnd(0.2, 1.1), rnd(-3, 8)], v: [rnd(0.15, 0.35), 0, rnd(-0.05, 0.05)], sp: 0, life: [9, 13], size: [3, 5], grow: 1.4, c0: 8227488, a: 0.13, fi: 0.35 }),
2760
+ emit("ash", "normal", 14, { p: [rnd(-10, 8), rnd(4, 6), rnd(-3, 7)], v: [rnd(0.1, 0.4), -0.35, rnd(-0.1, 0.1)], sp: [0.05, 0.15], life: [8, 12], size: [0.025, 0.04], c0: 10525328, a: 0.75, fi: 0.1, gnd: 1 })
2761
+ ]
2762
+ };
2763
+ var RAIN_CAPACITY = 4e3;
2764
+ var FALL_SPEED = 15;
2765
+ var WIND_PER_FALL = 0.07;
2766
+ var STREAK_SLANT = 0.04;
2767
+ var STREAK_LENGTH = 0.6;
2768
+ var BOX_X = [-15, 15];
2769
+ var BOX_Z = [-6, 16];
2770
+ var START_HEIGHT = [0, 16];
2771
+ var REENTRY_HEIGHT = [14, 17];
2772
+ var SPLASH_CHANCE = 0.35;
2773
+ var SPLASH_RANGE = 12;
2774
+ var SPRITE_CHANCE = 0.5;
2775
+ var UP9 = [0, 1, 0];
2776
+ var WORKSHOP_PUDDLES = [
2777
+ [-1.5, 2.2, 1.6, 0.9],
2778
+ [3, 3.6, 1.1, 0.7],
2779
+ [-6, 3.2, 1.3, 0.8],
2780
+ [1.4, -2.2, 0.9, 0.5]
2781
+ ];
2782
+ var inPuddle = (puddles, x, z) => puddles.some((p) => ((x - p[0]) / p[2]) ** 2 + ((z - p[1]) / p[3]) ** 2 < 1);
2783
+ var RainField = class {
2784
+ constructor(engine) {
2785
+ __publicField2(this, "engine", engine);
2786
+ __publicField2(this, "positions", new Float32Array(RAIN_CAPACITY * 6));
2787
+ __publicField2(this, "count", 0);
2788
+ __publicField2(this, "wind", 1);
2789
+ __publicField2(this, "on", false);
2790
+ __publicField2(this, "centerX", 0);
2791
+ __publicField2(this, "centerZ", 0);
2792
+ __publicField2(this, "puddles", WORKSHOP_PUDDLES);
2793
+ __publicField2(this, "puddleAt");
2794
+ }
2795
+ /** `initRain` (W321): `count` streaks (thinned by the quality), each drawn at a random place in the box. */
2796
+ init(count, wind) {
2797
+ const { random } = this.engine;
2798
+ this.count = Math.min(RAIN_CAPACITY, Math.round(count * this.engine.context.quality.mult));
2799
+ this.wind = wind;
2800
+ this.on = true;
2801
+ for (let index = 0; index < this.count; index++) {
2802
+ const offset = index * 6;
2803
+ const x = random.between(BOX_X[0], BOX_X[1]);
2804
+ const y = random.between(START_HEIGHT[0], START_HEIGHT[1]);
2805
+ const z = random.between(BOX_Z[0], BOX_Z[1]);
2806
+ this.set(offset, x, y, z);
2807
+ }
2808
+ }
2809
+ /**
2810
+ * What a game does with the field every frame: the box follows the camera (`centerX`, `centerZ`), the streaks thin or thicken to `count` (a new streak
2811
+ * starts at a random place of the box, as `init` draws it) and the wind slants them. The golden never calls it.
2812
+ */
2813
+ drive(count, wind, centerX, centerZ) {
2814
+ const { random } = this.engine;
2815
+ const target = Math.max(0, Math.min(RAIN_CAPACITY, Math.round(count * this.engine.context.quality.mult)));
2816
+ this.wind = wind;
2817
+ this.centerX = centerX;
2818
+ this.centerZ = centerZ;
2819
+ this.on = target > 0;
2820
+ for (let index = this.count; index < target; index++) {
2821
+ this.set(index * 6, random.between(BOX_X[0], BOX_X[1]) + centerX, random.between(START_HEIGHT[0], START_HEIGHT[1]), random.between(BOX_Z[0], BOX_Z[1]) + centerZ);
2822
+ }
2823
+ this.count = target;
2824
+ }
2825
+ /** `updateRain` (W322-329). `camera` is where the eye is; `splash` lets the drops that land leave a splash. */
2826
+ update(dt, splash, camera2) {
2827
+ if (!this.on) return;
2828
+ const { random, context } = this.engine;
2829
+ const positions = this.positions;
2830
+ const fall = FALL_SPEED * dt;
2831
+ const slant = this.wind * fall * WIND_PER_FALL;
2832
+ const reach = SPLASH_RANGE / (1 + context.quality.level);
2833
+ for (let index = 0; index < this.count; index++) {
2834
+ const o = index * 6;
2835
+ positions[o] += slant;
2836
+ positions[o + 3] += slant;
2837
+ positions[o + 1] -= fall;
2838
+ positions[o + 4] -= fall;
2839
+ const ground = context.groundAt(positions[o], positions[o + 2]);
2840
+ if (positions[o + 4] < ground) {
2841
+ const x = positions[o];
2842
+ const z = positions[o + 2];
2843
+ if (splash && random.next() < SPLASH_CHANCE) {
2844
+ const distance = Math.hypot(x - camera2[0], z - camera2[2]);
2845
+ if (distance < reach) {
2846
+ if (random.next() < SPRITE_CHANCE) {
2847
+ this.engine.spawn("normal", { p: [x, ground + 0.01, z], dir: UP9, cone: 0.6, sp: [0.6, 1.3], life: [0.18, 0.3], size: 0.035, c0: 12110044, a: 0.6, g: 9.8, fi: 0 });
2848
+ }
2849
+ if (this.puddleAt ? this.puddleAt(x, z) : inPuddle(this.puddles, x, z)) {
2850
+ this.engine.ring([[x, ground + 0.012, z], 0.01, random.between(0.12, 0.25), random.between(0.4, 0.6), 6978192]);
2851
+ }
2852
+ }
2853
+ }
2854
+ const nx = random.between(BOX_X[0], BOX_X[1]) + this.centerX;
2855
+ const ny = random.between(REENTRY_HEIGHT[0], REENTRY_HEIGHT[1]);
2856
+ const nz = random.between(BOX_Z[0], BOX_Z[1]) + this.centerZ;
2857
+ this.set(o, nx, ny, nz);
2858
+ }
2859
+ }
2860
+ }
2861
+ /** `rain.visible = false; RN.on = false` (W440): the field stops; the streaks stay where they were. */
2862
+ clear() {
2863
+ this.on = false;
2864
+ }
2865
+ set(offset, x, y, z) {
2866
+ const positions = this.positions;
2867
+ positions[offset] = x;
2868
+ positions[offset + 1] = y;
2869
+ positions[offset + 2] = z;
2870
+ positions[offset + 3] = x + this.wind * STREAK_SLANT;
2871
+ positions[offset + 4] = y - STREAK_LENGTH;
2872
+ positions[offset + 5] = z;
2873
+ }
2874
+ };
2875
+ var DROPS = [900, 2200, 4e3];
2876
+ var SLANT = [0.3, 1.2, 4.5];
2877
+ var STORM = 2;
2878
+ var FLASH_STEPS = ite(lt(variable("flash"), 0.08), 1, ite(lt(variable("flash"), 0.16), 0.2, ite(lt(variable("flash"), 0.26), 0.8, 0)));
2879
+ var RAIN_AND_PUDDLES = {
2880
+ id: "rain_and_puddles",
2881
+ pageId: "chuva",
2882
+ team: "world",
2883
+ params: { id: "intensity", options: ["drizzle", "rain", "storm"], default: 1 },
2884
+ budget: { particles: 4e3, decals: 4 },
2885
+ loop: 0,
2886
+ view: { eye: [2, 2, 7], target: [-1, 0.6, 1] },
2887
+ setup: [
2888
+ cue("lamp_cone", { visible: true }),
2889
+ ...WORKSHOP_PUDDLES.map(([x, z, radiusX, radiusZ]) => decal(x, 0, z, radiusX * 2, 790550, { grow: 0, life: 1e6, ar: radiusZ / radiusX }))
2890
+ ],
2891
+ timeline: [
2892
+ behavior("rain", "init", { count: ite(eq(PARAM, 0), DROPS[0], ite(eq(PARAM, 1), DROPS[1], DROPS[2])), wind: ite(eq(PARAM, 0), SLANT[0], ite(eq(PARAM, 1), SLANT[1], SLANT[2])) }),
2893
+ set("nextStrike", rnd(2, 4)),
2894
+ set("flash", -1)
2895
+ ],
2896
+ tick: [
2897
+ behavior("rain", "update", { dt: DELTA, splash: gt(PARAM, 0) }),
2898
+ when(eq(PARAM, STORM), [
2899
+ when(gt(TIME, variable("nextStrike")), [set("nextStrike", add(TIME, rnd(3.5, 6))), set("flash", 0)]),
2900
+ when(ge(variable("flash"), 0), [
2901
+ set("flash", add(variable("flash"), DELTA)),
2902
+ set("light", FLASH_STEPS),
2903
+ cue("lightning", { intensity: variable("light") }),
2904
+ screen({ max: { white: mul(variable("light"), 0.12) } }),
2905
+ when(gt(variable("flash"), 0.4), [set("flash", -1), cue("lightning", { intensity: 0 })])
2906
+ ]),
2907
+ emit("sheet", "normal", 2.5, { p: [-12, 2.5, rnd(-1, 8)], box: [0, 2, 0], v: [7, -0.5, 0], sp: 0, life: [3, 4], size: [3, 5], grow: 1.3, c0: 9083568, a: 0.06, fi: 0.3 })
2908
+ ])
2909
+ ]
2910
+ };
2911
+ var CAR_ORIGIN2 = [5.6, 0, 0.8];
2912
+ var CAR_ROTATION2 = [0, -0.4, 0];
2913
+ var carPoint = (x, y, z) => localToWorld(CAR_ORIGIN2, CAR_ROTATION2, [x, y, z]);
2914
+ var carDirection = (from, to) => normalized([to[0] - from[0], to[1] - from[1], to[2] - from[2]]);
2915
+ var BEAM_FROM = carPoint(0, 0.74, 2.15);
2916
+ var BEAM_DIRECTION = carDirection(BEAM_FROM, carPoint(0, 0.74, 3.15));
2917
+ var BEAM_SIDE = [BEAM_DIRECTION[2], 0, -BEAM_DIRECTION[0]];
2918
+ var WALKERS = 6;
2919
+ var WALK_SPEED2 = 0.7;
2920
+ var DROPS2 = 1800;
2921
+ var SLANT2 = 0.8;
2922
+ var startWalking = [
2923
+ set("spot", query("car", "point", rnd(-2, 2), 0, rnd(8, 18))),
2924
+ set("goal", query("car", "point", rnd(-0.6, 0.6), 0, 2.6)),
2925
+ behavior("walkers", "place", { index: INDEX, x: pick(variable("spot"), 0), z: pick(variable("spot"), 2), facing: 0 }),
2926
+ behavior("walkers", "walk", { index: INDEX, to: variable("goal"), speed: WALK_SPEED2 })
2927
+ ];
2928
+ var HEADLIGHTS_IN_RAIN = {
2929
+ id: "headlights_in_rain",
2930
+ pageId: "farol",
2931
+ team: "vehicles",
2932
+ budget: { particles: 600, lights: 2 },
2933
+ loop: 0,
2934
+ view: { eye: [-2.5, 2, 5.5], target: [4, 1, 6] },
2935
+ setup: [
2936
+ cue("fog", { density: 0.05 }),
2937
+ cue("headlights", { on: true, intensity: 2.4, color: 16773584 }),
2938
+ behavior("rain", "init", { count: DROPS2, wind: SLANT2 }),
2939
+ repeat(WALKERS, ...startWalking)
2940
+ ],
2941
+ timeline: [],
2942
+ tick: [
2943
+ behavior("rain", "update", { dt: DELTA, splash: true }),
2944
+ emit(
2945
+ "lit",
2946
+ "streak",
2947
+ 600,
2948
+ local(
2949
+ "depth",
2950
+ rnd(1, 13),
2951
+ local(
2952
+ "spread",
2953
+ mul(variable("depth"), 0.33, sqrt(rand())),
2954
+ local(
2955
+ "turn",
2956
+ rnd(0, 6.283),
2957
+ local("base", vadd(BEAM_FROM, vscale(BEAM_DIRECTION, variable("depth")), vscale(BEAM_SIDE, mul(cos(variable("turn")), variable("spread")))), {
2958
+ p: vwith(variable("base"), 1, add(vy(variable("base")), mul(sin(variable("turn")), variable("spread"), 0.6))),
2959
+ v: [0.8, -14, 0],
2960
+ sp: 0,
2961
+ life: 0.06,
2962
+ len: 0.035,
2963
+ c0: 13622512,
2964
+ a: 0.9
2965
+ })
2966
+ )
2967
+ )
2968
+ )
2969
+ ),
2970
+ repeat(WALKERS, when(not(query("walkers", "is_walking", INDEX)), startWalking))
2971
+ ]
2972
+ };
2735
2973
  var YAW = -Math.PI / 2;
2736
2974
  var COS = Math.cos(YAW);
2737
2975
  var SIN = Math.sin(YAW);
@@ -2759,10 +2997,6 @@ var GameDestructionThree = (() => {
2759
2997
  fireLight(0, [vx(variable("contact")), 0.34, vz(variable("contact"))], 1.6, mul(TIME, 3))
2760
2998
  ]
2761
2999
  };
2762
- var CAR_ORIGIN2 = [5.6, 0, 0.8];
2763
- var CAR_ROTATION2 = [0, -0.4, 0];
2764
- var carPoint = (x, y, z) => localToWorld(CAR_ORIGIN2, CAR_ROTATION2, [x, y, z]);
2765
- var carDirection = (from, to) => normalized([to[0] - from[0], to[1] - from[1], to[2] - from[2]]);
2766
3000
  var HOOD = carPoint(0, 1.02, 1.4);
2767
3001
  var EXHAUST = carPoint(0.5, 0.3, -2.2);
2768
3002
  var EXHAUST_DIRECTION = carDirection(EXHAUST, carPoint(0.5, 0.3, -3));
@@ -2796,7 +3030,7 @@ var GameDestructionThree = (() => {
2796
3030
  ], [fireLightOff(0)])
2797
3031
  ]
2798
3032
  };
2799
- var UP9 = [0, 1, 0];
3033
+ var UP10 = [0, 1, 0];
2800
3034
  var doorVelocity = (side) => {
2801
3035
  const out = carPoint(side * 4, 3, 0.3);
2802
3036
  const centre = carPoint(0, 0, 0.3);
@@ -2806,7 +3040,7 @@ var GameDestructionThree = (() => {
2806
3040
  call2("explode", vadd(query("car", "position"), [0, 0.6, 0]), 1.8, 2759188),
2807
3041
  cue("car_burnt"),
2808
3042
  behavior("car", "launch", { rise: 4.5, spin: 0.9 }),
2809
- repeat(scaled(30), debris({ p: vadd(query("car", "position"), [0, 1.3, 0]), dir: UP9, cone: 1.3, sp: [2, 6], size: [0.06, 6e-3, 0.06], c: 11061464, life: 20 })),
3043
+ repeat(scaled(30), debris({ p: vadd(query("car", "position"), [0, 1.3, 0]), dir: UP10, cone: 1.3, sp: [2, 6], size: [0.06, 6e-3, 0.06], c: 11061464, life: 20 })),
2810
3044
  debris({ p: carPoint(0, 1, 1.4), v: [rnd(-1, 1), 9, rnd(-1, 1)], sp: 0, size: [1.6, 0.06, 1.2], c: 3808536, spin: 6, life: 20, bounce: 0.2 }),
2811
3045
  ...[-1, 1].map((side) => debris({ p: carPoint(side * 0.95, 0.7, 0.3), v: doorVelocity(side), sp: 0, size: [0.06, 0.7, 1.1], c: 3808536, spin: 5, life: 20, bounce: 0.2 })),
2812
3046
  debris({ p: carPoint(0.95, 0.38, 1.35), v: [3, 4, 2], sp: 0, size: [0.3, 0.76, 0.76], c: 1118481, spin: 8, life: 20, bounce: 0.4 })
@@ -2976,7 +3210,7 @@ var GameDestructionThree = (() => {
2976
3210
  ])
2977
3211
  ]
2978
3212
  };
2979
- var WALKERS = 6;
3213
+ var WALKERS2 = 6;
2980
3214
  var NEAR = 9;
2981
3215
  var RANGE = 7;
2982
3216
  var RESTING_BPM = 70;
@@ -2992,7 +3226,7 @@ var GameDestructionThree = (() => {
2992
3226
  setup: [
2993
3227
  cue("zombie_visible", { index: -1, visible: false }),
2994
3228
  repeat(
2995
- WALKERS,
3229
+ WALKERS2,
2996
3230
  set("startX", rnd(-4, 4)),
2997
3231
  set("startZ", sub(rnd(-4, -2), mul(INDEX, 0.6))),
2998
3232
  behavior("walkers", "place", { index: INDEX, x: variable("startX"), z: variable("startZ"), facing: 0 }),
@@ -3002,7 +3236,7 @@ var GameDestructionThree = (() => {
3002
3236
  timeline: [],
3003
3237
  tick: [
3004
3238
  set("nearest", 99),
3005
- repeat(WALKERS, set("nearest", min(variable("nearest"), vdist(walkerPoint, CAMERA_POSITION)))),
3239
+ repeat(WALKERS2, set("nearest", min(variable("nearest"), vdist(walkerPoint, CAMERA_POSITION)))),
3006
3240
  set("fear", clamp(div(sub(NEAR, variable("nearest")), RANGE), 0, 1)),
3007
3241
  set("bpm", add(RESTING_BPM, mul(BPM_RISE, variable("fear")))),
3008
3242
  when(lt(mod(div(mul(TIME, variable("bpm")), 60), 1), div(mul(DELTA, variable("bpm")), 60)), [screen({ set: { beat: add(0.4, mul(0.6, variable("fear"))) } })]),
@@ -3069,7 +3303,7 @@ var GameDestructionThree = (() => {
3069
3303
  ]
3070
3304
  };
3071
3305
  var FACING4 = [0, 0, 1];
3072
- var UP10 = [0, 1, 0];
3306
+ var UP11 = [0, 1, 0];
3073
3307
  var ORIGIN = [0, 0, 0];
3074
3308
  var FORWARD4 = [0, 0, -1];
3075
3309
  var IMPACT_MATERIALS = ["concrete", "metal", "wood", "glass", "flesh", "water"];
@@ -3199,12 +3433,157 @@ var GameDestructionThree = (() => {
3199
3433
  team: "destruction",
3200
3434
  budget: { particles: 20, debris: 8 },
3201
3435
  loop: 2,
3202
- timeline: [call2("chips", ORIGIN, UP10, 8, 0.9), call2("rust_at", ORIGIN, 6)]
3436
+ timeline: [call2("chips", ORIGIN, UP11, 8, 0.9), call2("rust_at", ORIGIN, 6)]
3203
3437
  }
3204
3438
  ];
3205
- 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, SCRAPING_SPARKS, ENGINE_SMOKE, FUEL_LEAK, CAR_EXPLOSION, WINDSHIELD, DAMAGE_AND_BITE, INFECTION_PHASES, PANIC, BLOATER_BLINDNESS, CAMPFIRE, COUGH_AND_BREATH];
3439
+ var PI = Math.PI;
3440
+ var SMOKE = { color: 9079430, width: 10, height: 8, gravity: -0.08, drag: 2.2, growth: 2.1 };
3441
+ var FLASH_SECONDS = 0.055;
3442
+ var FLASH_RAMP = [0.6, 0.9];
3443
+ var FLASH_OPACITY = 0.95;
3444
+ var FLAME_COLORS = [16742946, 16760896, 13121050, 16773296];
3445
+ var FLAME_ADDITIVE = 3;
3446
+ var ORIGIN2 = [0, 0, 0];
3447
+ var SIZE = variable("size");
3448
+ var wide = (width) => mul(sub(rand(), 0.5), width);
3449
+ var puff2 = (point, velocity, world, radius, life, opacity) => mesh({
3450
+ shape: "sphere",
3451
+ p: point,
3452
+ v: velocity,
3453
+ wv: world,
3454
+ r: radius,
3455
+ seg: SMOKE.width,
3456
+ seg2: SMOKE.height,
3457
+ life,
3458
+ c0: SMOKE.color,
3459
+ a: opacity,
3460
+ fade: true,
3461
+ lit: true,
3462
+ g: SMOKE.gravity,
3463
+ drag: SMOKE.drag,
3464
+ grow: SMOKE.growth
3465
+ });
3466
+ var flameBit = (point, velocity, world, radius, life) => [
3467
+ set("bitPoint", point),
3468
+ set("bitVelocity", velocity),
3469
+ set("bitWorld", world),
3470
+ set("bitRadius", radius),
3471
+ set("bitLife", life),
3472
+ set("bitColour", floor(mul(rand(), FLAME_COLORS.length))),
3473
+ mesh({
3474
+ shape: "icosahedron",
3475
+ p: variable("bitPoint"),
3476
+ v: variable("bitVelocity"),
3477
+ wv: variable("bitWorld"),
3478
+ r: variable("bitRadius"),
3479
+ seg: 1,
3480
+ rot: [mul(rand(), 3), mul(rand(), 3), 0],
3481
+ spin: [3, 4, 2],
3482
+ life: variable("bitLife"),
3483
+ c0: pick(FLAME_COLORS, variable("bitColour")),
3484
+ add: eq(variable("bitColour"), FLAME_ADDITIVE),
3485
+ a: 0.9,
3486
+ fade: true,
3487
+ g: -1.4,
3488
+ drag: 1.4,
3489
+ grow: 2.2
3490
+ })
3491
+ ];
3492
+ var flashPiece = (shape, radius, height, width, at3, rot, color) => mesh({ shape, r: radius, ...height === void 0 ? {} : { h: height }, seg: width, seg2: shape === "sphere" ? 8 : 1, p: ORIGIN2, at: at3, rot, roll: variable("roll"), ramp: FLASH_RAMP, life: FLASH_SECONDS, c0: color, a: FLASH_OPACITY, add: true });
3493
+ var MUZZLE_FLASH2 = {
3494
+ id: "viewmodel_muzzle_flash",
3495
+ pageId: "event:viewmodel_muzzle_flash",
3496
+ team: "weapons",
3497
+ budget: { particles: 12, lights: 1 },
3498
+ loop: 0.1,
3499
+ timeline: [
3500
+ set("angles", []),
3501
+ repeat(4, append("angles", add(div(mul(INDEX, PI), 2), mul(rand(), 0.6)))),
3502
+ set("roll", mul(rand(), PI)),
3503
+ flash(ORIGIN2, 16756832, mul(4, SIZE), FLASH_SECONDS, mul(2, SIZE), true, true),
3504
+ flashPiece("cone_open", mul(0.013, SIZE), mul(0.085, SIZE), 10, [mul(0.042, SIZE), 0, 0], [0, 0, -PI / 2], 16757575),
3505
+ repeat(
3506
+ 4,
3507
+ set("angle", pick(variable("angles"), INDEX)),
3508
+ flashPiece(
3509
+ "cone",
3510
+ mul(5e-3, SIZE),
3511
+ mul(0.04, SIZE),
3512
+ 6,
3513
+ [mul(0.012, SIZE), mul(cos(variable("angle")), 0.016, SIZE), mul(sin(variable("angle")), 0.016, SIZE)],
3514
+ [variable("angle"), 0, -PI / 2],
3515
+ 16757575
3516
+ )
3517
+ ),
3518
+ flashPiece("sphere", mul(0.016, SIZE), void 0, 12, [mul(8e-3, SIZE), 0, 0], [0, 0, 0], 16773568),
3519
+ repeat(
3520
+ 6,
3521
+ puff2(
3522
+ ORIGIN2,
3523
+ [add(0.15, mul(rand(), 0.35)), add(0.03, mul(rand(), 0.12)), mul(sub(rand(), 0.5), 0.14)],
3524
+ [0, 0, 0],
3525
+ mul(0.013, SIZE),
3526
+ add(1.3, mul(rand(), 0.6)),
3527
+ 0.45
3528
+ )
3529
+ )
3530
+ ]
3531
+ };
3532
+ var SMOKE_PUFF = {
3533
+ id: "weapon_smoke_puff",
3534
+ pageId: "event:weapon_smoke_puff",
3535
+ team: "weapons",
3536
+ budget: { particles: 1 },
3537
+ loop: 0.1,
3538
+ timeline: [puff2(ORIGIN2, void 0, [variable("vx"), variable("vy"), variable("vz")], variable("radius"), variable("life"), variable("opacity"))]
3539
+ };
3540
+ var BACKBLAST = {
3541
+ id: "launcher_backblast",
3542
+ pageId: "event:launcher_backblast",
3543
+ team: "weapons",
3544
+ budget: { particles: 40 },
3545
+ loop: 0.1,
3546
+ timeline: [
3547
+ repeat(26, puff2(ORIGIN2, [neg(add(1.5, mul(rand(), 3))), 0, 0], [wide(1.4), wide(0.8), wide(1.4)], add(0.03, mul(rand(), 0.03)), add(1.8, mul(rand(), 1.4)), 0.5)),
3548
+ repeat(14, ...flameBit(ORIGIN2, [neg(add(2, mul(rand(), 2))), 0, 0], [wide(1), wide(0.6), wide(1)], 0.02, 0.25))
3549
+ ]
3550
+ };
3551
+ var FLAME_JET = {
3552
+ id: "flame_jet",
3553
+ pageId: "event:flame_jet",
3554
+ team: "weapons",
3555
+ budget: { particles: 30 },
3556
+ loop: 0.1,
3557
+ timeline: [
3558
+ repeat(
3559
+ floor(mul(420, variable("dt"), variable("scale"))),
3560
+ set("speed", mul(add(2.2, mul(rand(), 1.1)), variable("scale"))),
3561
+ ...flameBit(
3562
+ [mul(rand(), variable("speed"), min(variable("dt"), 0.3)), 0, 0],
3563
+ [variable("speed"), 0, 0],
3564
+ [wide(0.35), mul(sub(rand(), 0.3), 0.3), wide(0.35)],
3565
+ add(0.02, mul(rand(), 0.022)),
3566
+ add(0.4, mul(rand(), 0.35))
3567
+ )
3568
+ ),
3569
+ when(lt(rand(), 0.5), [
3570
+ puff2([add(mul(1.1, variable("scale")), mul(rand(), 0.4)), 0, 0], void 0, [mul(vx(DIRECTION), 0.6), 0.35, mul(vz(DIRECTION), 0.6)], 0.03, 1.6, 0.4)
3571
+ ])
3572
+ ]
3573
+ };
3574
+ var VIEWMODEL_EFFECTS = [MUZZLE_FLASH2, SMOKE_PUFF, BACKBLAST, FLAME_JET];
3575
+ var aliasOf = (base, id, changes) => ({ ...base, id, pageId: `vehicle:${id}`, anchor: CAR_ORIGIN2, ...changes });
3576
+ var VEHICLE_EFFECTS = [
3577
+ /** The engine on fire: `engine_smoke` at its worst condition (black smoke, flames in the hood, a light). */
3578
+ aliasOf(ENGINE_SMOKE, "vehicle_fire", { params: { id: "condition", options: ["100", "60", "30", "10"], default: 3 }, loop: 12 }),
3579
+ /** The exhaust of a car under way: `engine_smoke` in the best condition, for half a second so a moving car asks again. */
3580
+ aliasOf(ENGINE_SMOKE, "vehicle_exhaust", { params: { id: "condition", options: ["100", "60", "30", "10"], default: 0 }, loop: 0.5 }),
3581
+ /** A hole in the tank: the page's own petrol leak (drips, pool, sparks, fire running to the tank). */
3582
+ aliasOf(FUEL_LEAK, "vehicle_leak", {})
3583
+ ];
3584
+ 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];
3206
3585
  var WORKSHOP_EFFECTS = Object.fromEntries(ALL_EFFECTS.map((effect) => [effect.id, effect]));
3207
- var GAME_EFFECTS = { ...WORKSHOP_EFFECTS, ...Object.fromEntries(EVENT_EFFECTS.map((effect) => [effect.id, effect])) };
3586
+ var GAME_EFFECTS = { ...WORKSHOP_EFFECTS, ...Object.fromEntries([...EVENT_EFFECTS, ...VIEWMODEL_EFFECTS, ...VEHICLE_EFFECTS].map((effect) => [effect.id, effect])) };
3208
3587
  var LEHMER_MULTIPLIER = 16807;
3209
3588
  var LEHMER_MODULUS = 2147483647;
3210
3589
  var HASH_MIX = 2654435761;
@@ -3300,7 +3679,7 @@ var GameDestructionThree = (() => {
3300
3679
  }
3301
3680
  var COLUMNS = 6;
3302
3681
  var ROWS = 5;
3303
- var ORIGIN2 = [-2.2, 0, 2.6];
3682
+ var ORIGIN3 = [-2.2, 0, 2.6];
3304
3683
  var BLOCK_SIZE = [0.5, 0.44, 0.25];
3305
3684
  var OBJECT_ID_DRAWS2 = 4;
3306
3685
  var FLOOR2 = 0.13;
@@ -3343,7 +3722,7 @@ var GameDestructionThree = (() => {
3343
3722
  const chipScale = random.between(0.8, 1.3) * scale;
3344
3723
  for (let draw = 0; draw < OBJECT_ID_DRAWS2; draw++) random.next();
3345
3724
  best.holes.push({ x: offsetX, y: offsetY, scale, chipTurn, chipScale });
3346
- impact(host.engine, "concrete", [ORIGIN2[0] + x, y, ORIGIN2[2] + 0.13], [0, 0, 1], 0.8);
3725
+ impact(host.engine, "concrete", [ORIGIN3[0] + x, y, ORIGIN3[2] + 0.13], [0, 0, 1], 0.8);
3347
3726
  } else if (action === "collapse") {
3348
3727
  const { x, y } = args;
3349
3728
  for (const block of blocks) {
@@ -3393,7 +3772,7 @@ var GameDestructionThree = (() => {
3393
3772
  }
3394
3773
  };
3395
3774
  }
3396
- var ORIGIN3 = [5.6, 0, 0.8];
3775
+ var ORIGIN4 = [5.6, 0, 0.8];
3397
3776
  var MAX_TILT = 0.25;
3398
3777
  function createCarBehavior() {
3399
3778
  let y = 0;
@@ -3425,8 +3804,13 @@ var GameDestructionThree = (() => {
3425
3804
  }
3426
3805
  }
3427
3806
  },
3428
- query(_host, name) {
3429
- if (name === "position") return [ORIGIN3[0], y, ORIGIN3[2]];
3807
+ query(_host, name, args) {
3808
+ if (name === "position") return [ORIGIN4[0], y, ORIGIN4[2]];
3809
+ if (name === "point") {
3810
+ const [x, py, z] = args;
3811
+ const point = carPoint(x, py, z);
3812
+ return [point[0], point[1] + y, point[2]];
3813
+ }
3430
3814
  if (name === "roll") return roll;
3431
3815
  throw new Error(`car: unknown query ${name}`);
3432
3816
  },
@@ -3626,6 +4010,29 @@ var GameDestructionThree = (() => {
3626
4010
  }
3627
4011
  };
3628
4012
  }
4013
+ function createRainBehavior() {
4014
+ return {
4015
+ reset() {
4016
+ },
4017
+ act(host, action, args) {
4018
+ const { engine, camera: camera2 } = host;
4019
+ if (action === "init") {
4020
+ const { count, wind } = args;
4021
+ engine.rain.init(count, wind);
4022
+ } else if (action === "update") {
4023
+ const { dt, splash } = args;
4024
+ engine.rain.update(dt, splash, camera2.position);
4025
+ }
4026
+ },
4027
+ query(host, name) {
4028
+ if (name === "count") return host.engine.rain.count;
4029
+ if (name === "on") return host.engine.rain.on;
4030
+ throw new Error(`rain: unknown query ${name}`);
4031
+ },
4032
+ update() {
4033
+ }
4034
+ };
4035
+ }
3629
4036
  var WALKER_COUNT = 10;
3630
4037
  var ARRIVAL_DISTANCE = 0.05;
3631
4038
  var SWAY_AMPLITUDE = 0.04;
@@ -3691,7 +4098,8 @@ var GameDestructionThree = (() => {
3691
4098
  blocks: () => createBlocksBehavior(),
3692
4099
  plate: () => createPlateBehavior(),
3693
4100
  cylinder: () => createCylinderBehavior(),
3694
- car: () => createCarBehavior()
4101
+ car: () => createCarBehavior(),
4102
+ rain: () => createRainBehavior()
3695
4103
  };
3696
4104
  var NOOP = () => {
3697
4105
  };
@@ -3762,6 +4170,8 @@ var GameDestructionThree = (() => {
3762
4170
  return Math.abs(asNumber(first));
3763
4171
  case "sqrt":
3764
4172
  return Math.sqrt(asNumber(first));
4173
+ case "exp":
4174
+ return Math.exp(asNumber(first));
3765
4175
  case "sin":
3766
4176
  return Math.sin(asNumber(first));
3767
4177
  case "cos":
@@ -3880,7 +4290,7 @@ var GameDestructionThree = (() => {
3880
4290
  return evaluateCall(value.$, args.map((entry) => resolve(entry, context)));
3881
4291
  }
3882
4292
  }
3883
- var UP11 = [0, 1, 0];
4293
+ var UP12 = [0, 1, 0];
3884
4294
  var ZERO = [0, 0, 0];
3885
4295
  var FULL_TURN_DRAWN = 6.283;
3886
4296
  function scaledSizes(range, factor) {
@@ -3893,7 +4303,7 @@ var GameDestructionThree = (() => {
3893
4303
  p: centre,
3894
4304
  r: radius,
3895
4305
  flat: true,
3896
- dir: options.dir ?? UP11,
4306
+ dir: options.dir ?? UP12,
3897
4307
  cone: 0.35,
3898
4308
  sp: [0.4, 1.4],
3899
4309
  v: wind,
@@ -3910,7 +4320,7 @@ var GameDestructionThree = (() => {
3910
4320
  p: centre,
3911
4321
  r: radius * 0.6,
3912
4322
  flat: true,
3913
- dir: UP11,
4323
+ dir: UP12,
3914
4324
  cone: 0.3,
3915
4325
  sp: [0.3, 0.8],
3916
4326
  life: [0.2, 0.4],
@@ -3925,14 +4335,14 @@ var GameDestructionThree = (() => {
3925
4335
  if (engine.quality < 2) {
3926
4336
  engine.emit(`${key}e`, "streak", 10 * factor, dt, () => {
3927
4337
  const jitter = plus(wind, engine.random.between(-0.4, 0.4), 0, engine.random.between(-0.4, 0.4));
3928
- return { p: centre, box: [radius, 0, radius], dir: UP11, cone: 0.5, sp: [1.5, 3.5], v: jitter, life: [0.8, 1.8], len: 0.03, c0: 16756816, c1: 6295552, g: -0.6, drag: 0.4 };
4338
+ return { p: centre, box: [radius, 0, radius], dir: UP12, cone: 0.5, sp: [1.5, 3.5], v: jitter, life: [0.8, 1.8], len: 0.03, c0: 16756816, c1: 6295552, g: -0.6, drag: 0.4 };
3929
4339
  });
3930
4340
  }
3931
4341
  if (options.noSmoke) return;
3932
4342
  engine.emit(`${key}s`, "normal", (options.smoke || 14) * factor, dt, () => ({
3933
4343
  p: plus(centre, 0, 0.8 * sqrtFactor, 0),
3934
4344
  r: radius * 0.7,
3935
- dir: UP11,
4345
+ dir: UP12,
3936
4346
  cone: 0.35,
3937
4347
  sp: [0.6, 1.4],
3938
4348
  v: wind,
@@ -3972,15 +4382,15 @@ var GameDestructionThree = (() => {
3972
4382
  engine.screen.shake += 0.7 * scale;
3973
4383
  engine.screen.white += 0.18 * scale;
3974
4384
  engine.burst("add", 55 * scale, { p: point, r: 0.3 * scale, sp: [2, 7 * scale], drag: 4, life: [0.25, 0.6], size: [0.7 * scale, 1.4 * scale], grow: 1.7, c0: 16773296, c1: 10103304, fi: 0.04 });
3975
- engine.burst("add", 28 * scale, { p: plus(point, 0, 0.3, 0), r: 0.3, dir: UP11, cone: 0.7, sp: [2, 5 * scale], drag: 1.5, life: [0.6, 1.1], size: [0.6 * scale, 1 * scale], grow: 1.3, c0: 16760928, c1: 5902853, g: -2, fi: 0.05 });
4385
+ engine.burst("add", 28 * scale, { p: plus(point, 0, 0.3, 0), r: 0.3, dir: UP12, cone: 0.7, sp: [2, 5 * scale], drag: 1.5, life: [0.6, 1.1], size: [0.6 * scale, 1 * scale], grow: 1.3, c0: 16760928, c1: 5902853, g: -2, fi: 0.05 });
3976
4386
  engine.ring([plus(point, 0, 0.05, 0), 0.3, 7 * scale, 0.55, 16767136]);
3977
4387
  const toCamera = normalized([cameraPosition[0] - point[0], cameraPosition[1] - point[1], cameraPosition[2] - point[2]]);
3978
4388
  engine.ring([plus(point, 0, 1, 0), 0.3, 5 * scale, 0.35, 16773328, toCamera]);
3979
- engine.burst("normal", 32 * scale, { p: plus(point, 0, 0.6, 0), r: 0.8 * scale, dir: UP11, cone: 1.1, sp: [1, 3], drag: 1.2, life: [3, 6], size: [1 * scale, 1.8 * scale], grow: 2.6, c0: 2828068, c1: 987155, a: 0.6, fi: 0.18, g: -0.35 });
4389
+ engine.burst("normal", 32 * scale, { p: plus(point, 0, 0.6, 0), r: 0.8 * scale, dir: UP12, cone: 1.1, sp: [1, 3], drag: 1.2, life: [3, 6], size: [1 * scale, 1.8 * scale], grow: 2.6, c0: 2828068, c1: 987155, a: 0.6, fi: 0.18, g: -0.35 });
3980
4390
  ringOfDust(engine, point, scale);
3981
- if (engine.quality < 2) engine.burst("streak", 44 * scale, { p: point, dir: UP11, cone: 1.45, sp: [10, 26], life: [0.3, 0.7], len: 0.03, c0: 16760944, c1: 8396800, g: 9.8, gnd: 2 });
4391
+ if (engine.quality < 2) engine.burst("streak", 44 * scale, { p: point, dir: UP12, cone: 1.45, sp: [10, 26], life: [0.3, 0.7], len: 0.03, c0: 16760944, c1: 8396800, g: 9.8, gnd: 2 });
3982
4392
  for (let index = 0; index < engine.scaled(16 * scale); index++) {
3983
- engine.debrisPiece({ p: point, dir: UP11, cone: 1.2, sp: [3, 9], size: [0.12, 0.06, 0.1], k: [0.6, 1.6], c: chipColor || 2762274, life: 9 });
4393
+ engine.debrisPiece({ p: point, dir: UP12, cone: 1.2, sp: [3, 9], size: [0.12, 0.06, 0.1], k: [0.6, 1.6], c: chipColor || 2762274, life: 9 });
3984
4394
  }
3985
4395
  engine.decal([point[0], engine.context.groundAt(point[0], point[2]), point[2], 3 * scale, 657930, { grow: 0.15, life: 60 }]);
3986
4396
  }
@@ -4123,6 +4533,7 @@ var GameDestructionThree = (() => {
4123
4533
  if (!def) return;
4124
4534
  this.schedule.clear();
4125
4535
  this.variables.clear();
4536
+ for (const [name, value] of Object.entries(this.setup.values ?? {})) this.variables.set(name, value);
4126
4537
  for (const behavior2 of this.deps.behaviors()) behavior2.reset(this.host);
4127
4538
  this.time = 0;
4128
4539
  this.within(() => {
@@ -4181,6 +4592,10 @@ var GameDestructionThree = (() => {
4181
4592
  return engine.spawn(step.system, this.value(step.spawn));
4182
4593
  case "emit":
4183
4594
  return engine.emit(step.key, step.system, this.value(step.rate), this.dt, () => this.value(step.spawn));
4595
+ case "mesh": {
4596
+ engine.mesh(this.value(step.spawn));
4597
+ return;
4598
+ }
4184
4599
  case "debris":
4185
4600
  return engine.debrisPiece(this.value(step.spawn));
4186
4601
  case "decal":
@@ -4262,7 +4677,7 @@ var GameDestructionThree = (() => {
4262
4677
  this.index = previous;
4263
4678
  }
4264
4679
  };
4265
- var ORIGIN4 = [0, 0, 0];
4680
+ var ORIGIN5 = [0, 0, 0];
4266
4681
  var DEFAULT_HEADING = [0, 0, -1];
4267
4682
  var DEFAULT_MAX_INSTANCES = 48;
4268
4683
  var ENDLESS_SECONDS = 60;
@@ -4307,7 +4722,7 @@ var GameDestructionThree = (() => {
4307
4722
  }
4308
4723
  function placementOf(entry) {
4309
4724
  const origin = entry.follow ? [entry.origin[0] + positionOf2(entry.follow)[0], entry.origin[1] + positionOf2(entry.follow)[1], entry.origin[2] + positionOf2(entry.follow)[2]] : entry.origin;
4310
- return { origin, anchor: entry.anchor, yaw: entry.yaw };
4725
+ return { origin, anchor: entry.anchor, yaw: entry.yaw, ...entry.orientation ? { orientation: entry.orientation } : {} };
4311
4726
  }
4312
4727
  function drawing(entry, action) {
4313
4728
  const context = engine.context;
@@ -4331,8 +4746,9 @@ var GameDestructionThree = (() => {
4331
4746
  random: playOptions.seed !== void 0 ? streamOf(playOptions.seed) : void 0,
4332
4747
  follow: playOptions.follow,
4333
4748
  origin: point,
4334
- anchor: def.anchor ?? ORIGIN4,
4749
+ anchor: def.anchor ?? ORIGIN5,
4335
4750
  yaw: yawOf(heading, playOptions.direction),
4751
+ orientation: playOptions.orientation,
4336
4752
  seconds: playOptions.seconds ?? (def.loop && def.loop > 0 ? def.loop : ENDLESS_SECONDS)
4337
4753
  };
4338
4754
  playing.push(entry);
@@ -4341,7 +4757,8 @@ var GameDestructionThree = (() => {
4341
4757
  () => instance.begin(def, paramIndex(def, playOptions.param), {
4342
4758
  placement: placementOf(entry),
4343
4759
  ...playOptions.direction ? { direction: playOptions.direction } : {},
4344
- ...playOptions.normal ? { normal: playOptions.normal } : {}
4760
+ ...playOptions.normal ? { normal: playOptions.normal } : {},
4761
+ ...playOptions.values ? { values: playOptions.values } : {}
4345
4762
  })
4346
4763
  );
4347
4764
  return entry.handle;
@@ -4601,15 +5018,15 @@ var GameDestructionThree = (() => {
4601
5018
  return shock;
4602
5019
  }
4603
5020
  function pushPuffs(puffs, shock, reach, dt, age) {
4604
- for (const puff2 of puffs) {
4605
- if (puff2.ring || puff2.t > PUSH_AGE_LIMIT) continue;
4606
- const dx = puff2.x - shock.x;
4607
- const dz = puff2.z - shock.z;
5021
+ for (const puff3 of puffs) {
5022
+ if (puff3.ring || puff3.t > PUSH_AGE_LIMIT) continue;
5023
+ const dx = puff3.x - shock.x;
5024
+ const dz = puff3.z - shock.z;
4608
5025
  const distance = Math.hypot(dx, dz) + 1e-3;
4609
- if (Math.abs(distance - reach) < PUSH_BAND && puff2.y < PUSH_CEILING) {
5026
+ if (Math.abs(distance - reach) < PUSH_BAND && puff3.y < PUSH_CEILING) {
4610
5027
  const strength = 4 * dt * (1 - age);
4611
- puff2.vx += dx / distance * strength * 6;
4612
- puff2.vz += dz / distance * strength * 6;
5028
+ puff3.vx += dx / distance * strength * 6;
5029
+ puff3.vz += dz / distance * strength * 6;
4613
5030
  }
4614
5031
  }
4615
5032
  }
@@ -4909,10 +5326,10 @@ var GameDestructionThree = (() => {
4909
5326
  const life = random.between(ring2.life * 0.75, ring2.life);
4910
5327
  const h0 = random.between(1, 1.25);
4911
5328
  const hl = random.between(0.9, 1.6);
4912
- const puff2 = addPuff(core, random, { x, y, z, s0: spec.radius * 0.2, s1, sg: spec.radius * 0.06, gk: 3, life, h0, hl, color, kind: 0 });
4913
- puff2.ring = ring2;
4914
- puff2.ph = random.next() * Math.PI * 2;
4915
- puff2.th = random.next() * Math.PI * 2;
5329
+ const puff3 = addPuff(core, random, { x, y, z, s0: spec.radius * 0.2, s1, sg: spec.radius * 0.06, gk: 3, life, h0, hl, color, kind: 0 });
5330
+ puff3.ring = ring2;
5331
+ puff3.ph = random.next() * Math.PI * 2;
5332
+ puff3.th = random.next() * Math.PI * 2;
4916
5333
  }
4917
5334
  const stemCount = Math.round((spec.stem || 18) * Math.max(lod, 0.4));
4918
5335
  for (let index = 0; index < stemCount; index++) {
@@ -5713,7 +6130,7 @@ var GameDestructionThree = (() => {
5713
6130
  };
5714
6131
  var TWO_PI_ROUNDED = 6.2832;
5715
6132
  var WIDE_CONE = 3.1;
5716
- var UP12 = [0, 1, 0];
6133
+ var UP13 = [0, 1, 0];
5717
6134
  function drawRange(random, range) {
5718
6135
  if (Array.isArray(range)) return random.between(range[0], range[1]);
5719
6136
  return range || 0;
@@ -5741,7 +6158,7 @@ var GameDestructionThree = (() => {
5741
6158
  }
5742
6159
  function coneDirection(random, direction, cone, out) {
5743
6160
  if (cone >= WIDE_CONE) return randomUnit(random, out);
5744
- const [dx, dy, dz] = direction ?? UP12;
6161
+ const [dx, dy, dz] = direction ?? UP13;
5745
6162
  const cosTilt = 1 - random.next() * (1 - Math.cos(cone));
5746
6163
  const sinTilt = Math.sqrt(1 - cosTilt * cosTilt);
5747
6164
  const phase = random.next() * TWO_PI_ROUNDED;
@@ -5861,12 +6278,12 @@ var GameDestructionThree = (() => {
5861
6278
  }
5862
6279
  };
5863
6280
  var CAPACITY2 = 400;
5864
- var UP13 = [0, 1, 0];
6281
+ var UP14 = [0, 1, 0];
5865
6282
  var DEFAULT_LIFE2 = 60;
5866
6283
  var DEFAULT_GROW = 1.2;
5867
6284
  var EVAPORATION = { fuel: [6, 5], water: [40, 20] };
5868
6285
  function createDecal() {
5869
- return { on: false, x: 0, y: 0, z: 0, sx: 0, sy: 0, t: 0, life: 0, grow: 0, color: 0, normal: UP13, fluid: void 0, dryColor: -1, dryAt: 0, dryTime: 0 };
6286
+ return { on: false, x: 0, y: 0, z: 0, sx: 0, sy: 0, t: 0, life: 0, grow: 0, color: 0, normal: UP14, fluid: void 0, dryColor: -1, dryAt: 0, dryTime: 0 };
5870
6287
  }
5871
6288
  var DecalPool = class {
5872
6289
  constructor(context) {
@@ -5879,7 +6296,7 @@ var GameDestructionThree = (() => {
5879
6296
  const random = this.context.random;
5880
6297
  const index = this.next++ % CAPACITY2;
5881
6298
  const decal2 = this.decals[index];
5882
- const normal = options.n ?? UP13;
6299
+ const normal = options.n ?? UP14;
5883
6300
  random.between(0, 6.28);
5884
6301
  const offset = 4e-3 + index % 40 * 4e-4;
5885
6302
  decal2.on = true;
@@ -5920,6 +6337,94 @@ var GameDestructionThree = (() => {
5920
6337
  }
5921
6338
  }
5922
6339
  };
6340
+ var CAPACITY3 = 400;
6341
+ var IDENTITY = [0, 0, 0, 1];
6342
+ function multiply(a, b) {
6343
+ const [ax, ay, az, aw] = a;
6344
+ const [bx, by, bz, bw] = b;
6345
+ return [aw * bx + ax * bw + ay * bz - az * by, aw * by - ax * bz + ay * bw + az * bx, aw * bz + ax * by - ay * bx + az * bw, aw * bw - ax * bx - ay * by - az * bz];
6346
+ }
6347
+ var aboutX = (angle) => [Math.sin(angle / 2), 0, 0, Math.cos(angle / 2)];
6348
+ var MeshPool = class {
6349
+ constructor(capacity = CAPACITY3) {
6350
+ __publicField2(this, "capacity", capacity);
6351
+ __publicField2(this, "particles", []);
6352
+ __publicField2(this, "serial", 0);
6353
+ }
6354
+ get count() {
6355
+ return this.particles.length;
6356
+ }
6357
+ spawn(options, frame = IDENTITY) {
6358
+ const velocity = options.v ?? [0, 0, 0];
6359
+ const world = options.wv ?? [0, 0, 0];
6360
+ const particle = {
6361
+ id: ++this.serial,
6362
+ shape: options.shape,
6363
+ radius: options.r,
6364
+ height: options.h ?? 0,
6365
+ segments: options.seg ?? 0,
6366
+ segments2: options.seg2 ?? 0,
6367
+ color: options.c0,
6368
+ additive: options.add === true,
6369
+ lit: options.lit === true,
6370
+ x: options.p[0],
6371
+ y: options.p[1],
6372
+ z: options.p[2],
6373
+ frame: options.roll ? multiply(frame, aboutX(options.roll)) : frame,
6374
+ at: options.at ?? [0, 0, 0],
6375
+ rx: options.rot?.[0] ?? 0,
6376
+ ry: options.rot?.[1] ?? 0,
6377
+ rz: options.rot?.[2] ?? 0,
6378
+ scale: 1,
6379
+ opacity: options.a,
6380
+ age: 0,
6381
+ life: options.life,
6382
+ vx: velocity[0] + world[0],
6383
+ vy: velocity[1] + world[1],
6384
+ vz: velocity[2] + world[2],
6385
+ spin: options.spin ?? [0, 0, 0],
6386
+ gravity: options.g ?? 0,
6387
+ drag: options.drag ?? 0,
6388
+ grow: options.grow ?? 0,
6389
+ ramp: options.ramp,
6390
+ fade: options.fade === true,
6391
+ startOpacity: options.a
6392
+ };
6393
+ this.particles.push(particle);
6394
+ if (this.particles.length > this.capacity) this.particles.shift();
6395
+ return particle;
6396
+ }
6397
+ update(dt) {
6398
+ const live = this.particles;
6399
+ for (let index = 0; index < live.length; index++) {
6400
+ const particle = live[index];
6401
+ particle.age += dt;
6402
+ if (particle.ramp ? particle.age >= particle.life : particle.age > particle.life) {
6403
+ live.splice(index--, 1);
6404
+ continue;
6405
+ }
6406
+ if (particle.fade) particle.opacity = Math.max(0, particle.startOpacity * (1 - particle.age / particle.life));
6407
+ particle.vy -= particle.gravity * dt;
6408
+ if (particle.drag) {
6409
+ const keep = Math.max(0, 1 - particle.drag * dt);
6410
+ particle.vx *= keep;
6411
+ particle.vy *= keep;
6412
+ particle.vz *= keep;
6413
+ }
6414
+ particle.x += particle.vx * dt;
6415
+ particle.y += particle.vy * dt;
6416
+ particle.z += particle.vz * dt;
6417
+ particle.rx += particle.spin[0] * dt;
6418
+ particle.ry += particle.spin[1] * dt;
6419
+ particle.rz += particle.spin[2] * dt;
6420
+ if (particle.grow) particle.scale *= 1 + particle.grow * dt;
6421
+ if (particle.ramp) particle.scale = particle.ramp[0] + Math.min(1, particle.age / particle.life) * particle.ramp[1];
6422
+ }
6423
+ }
6424
+ clear() {
6425
+ this.particles.length = 0;
6426
+ }
6427
+ };
5923
6428
  var DEFAULT_FADE_IN = 0.1;
5924
6429
  var FLOATS_PER_VECTOR = 3;
5925
6430
  var BYTE_MAX2 = 255;
@@ -6209,11 +6714,11 @@ var GameDestructionThree = (() => {
6209
6714
  };
6210
6715
  var RING_CAPACITY = 120;
6211
6716
  var LIGHT_CAPACITY = 4;
6212
- var UP14 = [0, 1, 0];
6717
+ var UP15 = [0, 1, 0];
6213
6718
  var DEFAULT_LIGHT_DISTANCE = 14;
6214
6719
  var WaveRingPool = class {
6215
6720
  constructor() {
6216
- __publicField2(this, "rings", Array.from({ length: RING_CAPACITY }, () => ({ on: false, point: UP14, from: 0, to: 0, t: 0, duration: 1, color: 0, normal: UP14, hold: 0 })));
6721
+ __publicField2(this, "rings", Array.from({ length: RING_CAPACITY }, () => ({ on: false, point: UP15, from: 0, to: 0, t: 0, duration: 1, color: 0, normal: UP15, hold: 0 })));
6217
6722
  __publicField2(this, "count", 0);
6218
6723
  __publicField2(this, "next", 0);
6219
6724
  }
@@ -6226,7 +6731,7 @@ var GameDestructionThree = (() => {
6226
6731
  ring2.t = 0;
6227
6732
  ring2.duration = duration;
6228
6733
  ring2.color = color;
6229
- ring2.normal = normal ?? UP14;
6734
+ ring2.normal = normal ?? UP15;
6230
6735
  ring2.hold = hold || 0;
6231
6736
  }
6232
6737
  clear() {
@@ -6246,7 +6751,7 @@ var GameDestructionThree = (() => {
6246
6751
  var FlashLightPool = class {
6247
6752
  constructor(context) {
6248
6753
  __publicField2(this, "context", context);
6249
- __publicField2(this, "lights", Array.from({ length: LIGHT_CAPACITY }, () => ({ x: 0, y: 0, z: 0, color: 16760944, distance: DEFAULT_LIGHT_DISTANCE, peak: 0, duration: 1, t: 0, intensity: 0 })));
6754
+ __publicField2(this, "lights", Array.from({ length: LIGHT_CAPACITY }, () => ({ x: 0, y: 0, z: 0, color: 16760944, distance: DEFAULT_LIGHT_DISTANCE, peak: 0, duration: 1, t: 0, intensity: 0, steady: false })));
6250
6755
  __publicField2(this, "fire", [
6251
6756
  { x: 0, y: 0, z: 0, intensity: 0 },
6252
6757
  { x: 0, y: 0, z: 0, intensity: 0 }
@@ -6256,7 +6761,7 @@ var GameDestructionThree = (() => {
6256
6761
  get lit() {
6257
6762
  return this.lights.filter((light) => light.intensity > 0).length;
6258
6763
  }
6259
- flash(point, color, intensity, duration, distance, force) {
6764
+ flash(point, color, intensity, duration, distance, force, steady) {
6260
6765
  const { level } = this.context.quality;
6261
6766
  if (level >= 1 && !force) return;
6262
6767
  if (level >= 2 && force !== "boom") return;
@@ -6269,6 +6774,7 @@ var GameDestructionThree = (() => {
6269
6774
  light.peak = intensity;
6270
6775
  light.duration = duration;
6271
6776
  light.t = 0;
6777
+ light.steady = steady === true;
6272
6778
  light.intensity = intensity;
6273
6779
  }
6274
6780
  clear() {
@@ -6280,7 +6786,7 @@ var GameDestructionThree = (() => {
6280
6786
  if (light.intensity <= 0) continue;
6281
6787
  light.t += dt;
6282
6788
  const age = light.t / light.duration;
6283
- light.intensity = age >= 1 ? 0 : light.peak * (1 - age) * (1 - age);
6789
+ light.intensity = age >= 1 ? 0 : light.steady ? light.peak : light.peak * (1 - age) * (1 - age);
6284
6790
  }
6285
6791
  }
6286
6792
  };
@@ -6288,6 +6794,14 @@ var GameDestructionThree = (() => {
6288
6794
  var FIRE_FLICKER_SWAY = 0.25;
6289
6795
  var FIRE_FLICKER_JITTER = 0.08;
6290
6796
  var LENS_FADE = 0.16;
6797
+ function rotate(q, v) {
6798
+ const [qx, qy, qz, qw] = q;
6799
+ const ix = qw * v[0] + qy * v[2] - qz * v[1];
6800
+ const iy = qw * v[1] + qz * v[0] - qx * v[2];
6801
+ const iz = qw * v[2] + qx * v[1] - qy * v[0];
6802
+ const iw = -qx * v[0] - qy * v[1] - qz * v[2];
6803
+ return [ix * qw + iw * -qx + iy * -qz - iz * -qy, iy * qw + iw * -qy + iz * -qx - ix * -qz, iz * qw + iw * -qz + ix * -qy - iy * -qx];
6804
+ }
6291
6805
  function workshopGround(x, z) {
6292
6806
  return z < -2.9 && z > -4.5 && Math.abs(x) < 13 ? 0.12 : 0;
6293
6807
  }
@@ -6301,6 +6815,8 @@ var GameDestructionThree = (() => {
6301
6815
  __publicField2(this, "decals");
6302
6816
  __publicField2(this, "rings", new WaveRingPool());
6303
6817
  __publicField2(this, "lights");
6818
+ __publicField2(this, "rain", new RainField(this));
6819
+ __publicField2(this, "meshes", new MeshPool());
6304
6820
  __publicField2(this, "screen", createScreenState());
6305
6821
  __publicField2(this, "lens", []);
6306
6822
  __publicField2(this, "post", {});
@@ -6333,7 +6849,11 @@ var GameDestructionThree = (() => {
6333
6849
  place(point) {
6334
6850
  const placement = this.placement;
6335
6851
  if (!placement) return point;
6336
- const { anchor, origin, yaw } = placement;
6852
+ const { anchor, origin, yaw, orientation } = placement;
6853
+ if (orientation) {
6854
+ const turned = rotate(orientation, [point[0] - anchor[0], point[1] - anchor[1], point[2] - anchor[2]]);
6855
+ return [origin[0] + turned[0], origin[1] + turned[1], origin[2] + turned[2]];
6856
+ }
6337
6857
  const x = point[0] - anchor[0];
6338
6858
  const z = point[2] - anchor[2];
6339
6859
  const cos2 = Math.cos(yaw);
@@ -6344,6 +6864,7 @@ var GameDestructionThree = (() => {
6344
6864
  turn(vector) {
6345
6865
  const placement = this.placement;
6346
6866
  if (!placement) return vector;
6867
+ if (placement.orientation) return rotate(placement.orientation, vector);
6347
6868
  const cos2 = Math.cos(placement.yaw);
6348
6869
  const sin2 = Math.sin(placement.yaw);
6349
6870
  return [cos2 * vector[0] + sin2 * vector[2], vector[1], -sin2 * vector[0] + cos2 * vector[2]];
@@ -6372,6 +6893,14 @@ var GameDestructionThree = (() => {
6372
6893
  }
6373
6894
  this.accumulators.set(key, carried);
6374
6895
  }
6896
+ /** A solid particle (a cone, a sphere): the group sits at `p` turned by the placement, `v` is turned with it and `wv` is not. */
6897
+ mesh(recipe) {
6898
+ const placement = this.placement;
6899
+ const options = placement ? { ...recipe, p: this.place(recipe.p), ...recipe.v ? { v: this.turn(recipe.v) } : {} } : recipe;
6900
+ this.context.trace?.("mesh", [options]);
6901
+ const grouped = recipe.at !== void 0 || recipe.roll !== void 0;
6902
+ return this.meshes.spawn(options, placement && grouped ? placement.orientation ?? [0, Math.sin(placement.yaw / 2), 0, Math.cos(placement.yaw / 2)] : void 0);
6903
+ }
6375
6904
  debrisPiece(recipe) {
6376
6905
  const options = this.placed(recipe);
6377
6906
  this.context.trace?.("debris", [options]);
@@ -6392,10 +6921,11 @@ var GameDestructionThree = (() => {
6392
6921
  this.context.trace?.("ring", args);
6393
6922
  this.rings.add(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
6394
6923
  }
6924
+ /** `steady` (a seventh number): the light holds its intensity for its duration and goes out, where the Workshop's fades with the square of its age. */
6395
6925
  flash(recipe) {
6396
- const args = this.placement ? [this.place(recipe[0]), recipe[1], recipe[2], recipe[3], recipe[4], recipe[5]] : recipe;
6397
- this.context.trace?.("flash", args);
6398
- this.lights.flash(args[0], args[1], args[2], args[3], args[4], args[5]);
6926
+ const args = this.placement ? [this.place(recipe[0]), recipe[1], recipe[2], recipe[3], recipe[4], recipe[5], recipe[6]] : recipe;
6927
+ this.context.trace?.("flash", args.slice(0, 6));
6928
+ this.lights.flash(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
6399
6929
  }
6400
6930
  /** `fireLight` (W400): sways with the clock and flickers with one draw. */
6401
6931
  fireLight(slot, recipe, intensity, time) {
@@ -6420,6 +6950,7 @@ var GameDestructionThree = (() => {
6420
6950
  this.debris.update(dt);
6421
6951
  this.decals.update(dt);
6422
6952
  this.rings.update(dt);
6953
+ this.meshes.update(dt);
6423
6954
  this.lights.update(dt);
6424
6955
  updateScreen(this.screen, dt);
6425
6956
  for (let index = this.lens.length - 1; index >= 0; index--) {
@@ -6436,7 +6967,9 @@ var GameDestructionThree = (() => {
6436
6967
  this.debris.clear();
6437
6968
  this.decals.clear();
6438
6969
  this.rings.clear();
6970
+ this.meshes.clear();
6439
6971
  this.lights.clear();
6972
+ this.rain.clear();
6440
6973
  this.accumulators.clear();
6441
6974
  resetScreenState(this.screen);
6442
6975
  this.lens.length = 0;
@@ -6582,16 +7115,16 @@ var GameDestructionThree = (() => {
6582
7115
  },
6583
7116
  push(point, radius, speed) {
6584
7117
  spriteSystem.push(point, radius, speed);
6585
- for (const puff2 of core.puffs.items) {
6586
- const dx = puff2.x - point[0];
6587
- const dy = puff2.y - point[1];
6588
- const dz = puff2.z - point[2];
7118
+ for (const puff3 of core.puffs.items) {
7119
+ const dx = puff3.x - point[0];
7120
+ const dy = puff3.y - point[1];
7121
+ const dz = puff3.z - point[2];
6589
7122
  const distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
6590
7123
  if (distance >= radius) continue;
6591
7124
  const strength = speed * (1 - distance / radius) / (distance + 1e-3);
6592
- puff2.vx += dx * strength;
6593
- puff2.vy += dy * strength;
6594
- puff2.vz += dz * strength;
7125
+ puff3.vx += dx * strength;
7126
+ puff3.vy += dy * strength;
7127
+ puff3.vz += dz * strength;
6595
7128
  }
6596
7129
  },
6597
7130
  stop(handle) {
@@ -6608,6 +7141,12 @@ var GameDestructionThree = (() => {
6608
7141
  camera2.y = position[1];
6609
7142
  camera2.z = position[2];
6610
7143
  },
7144
+ setWind(next) {
7145
+ wind.x = next[0];
7146
+ wind.y = next[1];
7147
+ wind.z = next[2];
7148
+ },
7149
+ wind: () => [wind.x, wind.y, wind.z],
6611
7150
  forceLod(scale) {
6612
7151
  core.clock.forcedLod = scale;
6613
7152
  },
@@ -6805,11 +7344,11 @@ void main(){
6805
7344
  geometry.setAttribute("aC", attributes[2]);
6806
7345
  geometry.instanceCount = 0;
6807
7346
  const material = new THREE.ShaderMaterial({ uniforms, vertexShader: PUFF_VERTEX, fragmentShader: PUFF_FRAGMENT });
6808
- const mesh = new THREE.Mesh(geometry, material);
6809
- mesh.name = "vfx-puffs";
6810
- mesh.frustumCulled = false;
7347
+ const mesh2 = new THREE.Mesh(geometry, material);
7348
+ mesh2.name = "vfx-puffs";
7349
+ mesh2.frustumCulled = false;
6811
7350
  return {
6812
- mesh,
7351
+ mesh: mesh2,
6813
7352
  sync() {
6814
7353
  let n = 0;
6815
7354
  for (const p of pool.items) {
@@ -6850,12 +7389,12 @@ void main(){
6850
7389
  geometry.setAttribute("aE", attributes[2]);
6851
7390
  geometry.instanceCount = 0;
6852
7391
  const material = new THREE.ShaderMaterial({ transparent: true, depthWrite: false, blending: THREE.AdditiveBlending, vertexShader: SPARK_VERTEX, fragmentShader: SPARK_FRAGMENT });
6853
- const mesh = new THREE.Mesh(geometry, material);
6854
- mesh.name = "vfx-sparks";
6855
- mesh.frustumCulled = false;
6856
- mesh.renderOrder = 5;
7392
+ const mesh2 = new THREE.Mesh(geometry, material);
7393
+ mesh2.name = "vfx-sparks";
7394
+ mesh2.frustumCulled = false;
7395
+ mesh2.renderOrder = 5;
6857
7396
  return {
6858
- mesh,
7397
+ mesh: mesh2,
6859
7398
  sync() {
6860
7399
  let n = 0;
6861
7400
  for (const s of pool.items) {
@@ -6893,12 +7432,12 @@ void main(){
6893
7432
  geometry.setAttribute("aC", attributes[1]);
6894
7433
  geometry.instanceCount = 0;
6895
7434
  const material = new THREE.ShaderMaterial({ transparent: true, depthWrite: false, blending: THREE.AdditiveBlending, vertexShader: GLOW_VERTEX, fragmentShader: GLOW_FRAGMENT });
6896
- const mesh = new THREE.Mesh(geometry, material);
6897
- mesh.name = "vfx-glows";
6898
- mesh.frustumCulled = false;
6899
- mesh.renderOrder = 6;
7435
+ const mesh2 = new THREE.Mesh(geometry, material);
7436
+ mesh2.name = "vfx-glows";
7437
+ mesh2.frustumCulled = false;
7438
+ mesh2.renderOrder = 6;
6900
7439
  return {
6901
- mesh,
7440
+ mesh: mesh2,
6902
7441
  sync() {
6903
7442
  let n = 0;
6904
7443
  for (const g of pool.items) {
@@ -6927,14 +7466,14 @@ void main(){
6927
7466
  const rockMaterial = new THREE.MeshStandardMaterial({ color: 16777215, roughness: 0.85, flatShading: true });
6928
7467
  const plateMaterial = rockMaterial.clone();
6929
7468
  const make = (geometry, material) => {
6930
- const mesh = new THREE.InstancedMesh(geometry, material, capacity);
6931
- mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
6932
- mesh.castShadow = true;
6933
- mesh.receiveShadow = true;
6934
- mesh.frustumCulled = false;
6935
- mesh.count = 0;
6936
- mesh.setColorAt(0, new THREE.Color(1, 1, 1));
6937
- return mesh;
7469
+ const mesh2 = new THREE.InstancedMesh(geometry, material, capacity);
7470
+ mesh2.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
7471
+ mesh2.castShadow = true;
7472
+ mesh2.receiveShadow = true;
7473
+ mesh2.frustumCulled = false;
7474
+ mesh2.count = 0;
7475
+ mesh2.setColorAt(0, new THREE.Color(1, 1, 1));
7476
+ return mesh2;
6938
7477
  };
6939
7478
  const rocks = make(shapes.rock, rockMaterial);
6940
7479
  const plates = make(shapes.plate, plateMaterial);
@@ -6953,20 +7492,20 @@ void main(){
6953
7492
  euler.set(chunk.rx, chunk.ry, chunk.rz);
6954
7493
  quaternion.setFromEuler(euler);
6955
7494
  matrix.compose(position.set(chunk.x, chunk.y, chunk.z), quaternion, scale.set(chunk.sx, chunk.sy, chunk.sz));
6956
- const mesh = chunk.plate ? plates : rocks;
7495
+ const mesh2 = chunk.plate ? plates : rocks;
6957
7496
  const index = chunk.plate ? plateCount++ : rockCount++;
6958
- mesh.setMatrixAt(index, matrix);
7497
+ mesh2.setMatrixAt(index, matrix);
6959
7498
  const color = colorOf2(chunk);
6960
- const colors = mesh.instanceColor.array;
7499
+ const colors = mesh2.instanceColor.array;
6961
7500
  colors[index * 3] = color[0];
6962
7501
  colors[index * 3 + 1] = color[1];
6963
7502
  colors[index * 3 + 2] = color[2];
6964
7503
  }
6965
7504
  rocks.count = rockCount;
6966
7505
  plates.count = plateCount;
6967
- for (const mesh of [rocks, plates]) {
6968
- mesh.instanceMatrix.needsUpdate = true;
6969
- mesh.instanceColor.needsUpdate = true;
7506
+ for (const mesh2 of [rocks, plates]) {
7507
+ mesh2.instanceMatrix.needsUpdate = true;
7508
+ mesh2.instanceColor.needsUpdate = true;
6970
7509
  }
6971
7510
  return rockCount + plateCount;
6972
7511
  },
@@ -7150,28 +7689,28 @@ void main(){
7150
7689
  position.setXYZ(index, worldX, (options.ground?.heightAt(worldX, worldZ) ?? 0) + mark.lift, worldZ);
7151
7690
  }
7152
7691
  geometry.computeVertexNormals();
7153
- const mesh = new THREE.Mesh(geometry, material);
7154
- mesh.receiveShadow = true;
7155
- return mesh;
7692
+ const mesh2 = new THREE.Mesh(geometry, material);
7693
+ mesh2.receiveShadow = true;
7694
+ return mesh2;
7156
7695
  }
7157
7696
  function addRock(mark) {
7158
- const mesh = new THREE.Mesh(mark.plate ? options.plateGeometry : options.rockGeometry, rockMaterial(mark.color));
7159
- mesh.position.set(mark.x, mark.y, mark.z);
7160
- mesh.scale.set(mark.scale[0], mark.scale[1], mark.scale[2]);
7161
- mesh.rotation.set(mark.rotation[0], mark.rotation[1], mark.rotation[2]);
7162
- mesh.castShadow = true;
7163
- mesh.receiveShadow = true;
7164
- return mesh;
7697
+ const mesh2 = new THREE.Mesh(mark.plate ? options.plateGeometry : options.rockGeometry, rockMaterial(mark.color));
7698
+ mesh2.position.set(mark.x, mark.y, mark.z);
7699
+ mesh2.scale.set(mark.scale[0], mark.scale[1], mark.scale[2]);
7700
+ mesh2.rotation.set(mark.rotation[0], mark.rotation[1], mark.rotation[2]);
7701
+ mesh2.castShadow = true;
7702
+ mesh2.receiveShadow = true;
7703
+ return mesh2;
7165
7704
  }
7166
7705
  function addBlood(mark) {
7167
- const mesh = new THREE.Mesh(bloodGeometry, bloodMaterial);
7168
- mesh.position.set(mark.x, mark.y, mark.z);
7169
- mesh.scale.set(mark.scaleX, 1, mark.scaleZ);
7170
- mesh.rotation.y = mark.rotation;
7171
- mesh.receiveShadow = true;
7172
- mesh.scale.multiplyScalar(0.05);
7173
- blood.push({ mesh, scaleX: mark.scaleX, scaleZ: mark.scaleZ, age: 0 });
7174
- return mesh;
7706
+ const mesh2 = new THREE.Mesh(bloodGeometry, bloodMaterial);
7707
+ mesh2.position.set(mark.x, mark.y, mark.z);
7708
+ mesh2.scale.set(mark.scaleX, 1, mark.scaleZ);
7709
+ mesh2.rotation.y = mark.rotation;
7710
+ mesh2.receiveShadow = true;
7711
+ mesh2.scale.multiplyScalar(0.05);
7712
+ blood.push({ mesh: mesh2, scaleX: mark.scaleX, scaleZ: mark.scaleZ, age: 0 });
7713
+ return mesh2;
7175
7714
  }
7176
7715
  function build(mark) {
7177
7716
  if (mark.kind === "scorch") return conform(mark, scorchMaterials[mark.variant]);
@@ -7382,6 +7921,73 @@ void main() {
7382
7921
  }
7383
7922
  };
7384
7923
  }
7924
+ var SPHERE_DEFAULT_WIDTH = 8;
7925
+ var SPHERE_DEFAULT_HEIGHT = 6;
7926
+ function geometryOf(THREE, particle) {
7927
+ switch (particle.shape) {
7928
+ case "cone":
7929
+ return new THREE.ConeGeometry(particle.radius, particle.height, particle.segments || void 0, particle.segments2 || 1, false);
7930
+ case "cone_open":
7931
+ return new THREE.ConeGeometry(particle.radius, particle.height, particle.segments || void 0, particle.segments2 || 1, true);
7932
+ case "sphere":
7933
+ return new THREE.SphereGeometry(particle.radius, particle.segments || SPHERE_DEFAULT_WIDTH, particle.segments2 || SPHERE_DEFAULT_HEIGHT);
7934
+ case "icosahedron":
7935
+ return new THREE.IcosahedronGeometry(particle.radius, particle.segments);
7936
+ }
7937
+ }
7938
+ function materialOf(THREE, particle) {
7939
+ if (particle.lit) return new THREE.MeshStandardMaterial({ color: particle.color, roughness: 1, metalness: 0, transparent: true, opacity: particle.opacity, depthWrite: false });
7940
+ return new THREE.MeshBasicMaterial({ color: particle.color, transparent: true, opacity: particle.opacity, depthWrite: false, ...particle.additive ? { blending: THREE.AdditiveBlending } : {} });
7941
+ }
7942
+ function createMeshLayer(THREE, pool) {
7943
+ const group = new THREE.Group();
7944
+ group.name = "workshop-meshes";
7945
+ const drawn = /* @__PURE__ */ new Map();
7946
+ const frame = new THREE.Quaternion();
7947
+ const euler = new THREE.Euler();
7948
+ const turn = new THREE.Quaternion();
7949
+ const offset = new THREE.Vector3();
7950
+ function release(mesh2) {
7951
+ mesh2.removeFromParent();
7952
+ mesh2.geometry.dispose();
7953
+ mesh2.material.dispose();
7954
+ }
7955
+ return {
7956
+ group,
7957
+ sync() {
7958
+ const live = /* @__PURE__ */ new Set();
7959
+ for (const particle of pool.particles) {
7960
+ live.add(particle.id);
7961
+ let mesh2 = drawn.get(particle.id);
7962
+ if (!mesh2) {
7963
+ mesh2 = new THREE.Mesh(geometryOf(THREE, particle), materialOf(THREE, particle));
7964
+ mesh2.frustumCulled = false;
7965
+ mesh2.renderOrder = particle.additive ? 5 : 3;
7966
+ drawn.set(particle.id, mesh2);
7967
+ group.add(mesh2);
7968
+ }
7969
+ frame.set(particle.frame[0], particle.frame[1], particle.frame[2], particle.frame[3]);
7970
+ offset.set(particle.at[0], particle.at[1], particle.at[2]).multiplyScalar(particle.scale).applyQuaternion(frame);
7971
+ mesh2.position.set(particle.x + offset.x, particle.y + offset.y, particle.z + offset.z);
7972
+ turn.setFromEuler(euler.set(particle.rx, particle.ry, particle.rz));
7973
+ mesh2.quaternion.copy(frame).multiply(turn);
7974
+ mesh2.scale.setScalar(particle.scale);
7975
+ mesh2.material.opacity = particle.opacity;
7976
+ }
7977
+ for (const [id, mesh2] of drawn) {
7978
+ if (live.has(id)) continue;
7979
+ release(mesh2);
7980
+ drawn.delete(id);
7981
+ }
7982
+ return pool.count;
7983
+ },
7984
+ dispose() {
7985
+ for (const mesh2 of drawn.values()) release(mesh2);
7986
+ drawn.clear();
7987
+ group.removeFromParent();
7988
+ }
7989
+ };
7990
+ }
7385
7991
  var WORKSHOP_SPRITE_VERTEX = `attribute float size;attribute float alpha;attribute vec3 pcol;varying vec3 vC;varying float vA;uniform float uScale;
7386
7992
  void main(){vC=pcol;vA=alpha;vec4 mv=modelViewMatrix*vec4(position,1.);gl_PointSize=clamp(size*uScale/-mv.z,1.,512.);gl_Position=projectionMatrix*mv;}`;
7387
7993
  var WORKSHOP_SPRITE_FRAGMENT = `uniform float uSoft;varying vec3 vC;varying float vA;
@@ -7457,13 +8063,37 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7457
8063
  }
7458
8064
  };
7459
8065
  }
8066
+ function rainLayer(THREE, rain) {
8067
+ const geometry = new THREE.BufferGeometry();
8068
+ const position = new THREE.BufferAttribute(rain.positions, 3).setUsage(THREE.DynamicDrawUsage);
8069
+ geometry.setAttribute("position", position);
8070
+ geometry.setDrawRange(0, 0);
8071
+ const material = new THREE.LineBasicMaterial({ color: 10466255, transparent: true, opacity: 0.35, depthWrite: false });
8072
+ const lines = new THREE.LineSegments(geometry, material);
8073
+ lines.frustumCulled = false;
8074
+ lines.name = "workshop-rain";
8075
+ return {
8076
+ object: lines,
8077
+ sync() {
8078
+ const drawn = rain.on && rain.count > 0;
8079
+ lines.visible = drawn;
8080
+ geometry.setDrawRange(0, drawn ? rain.count * 2 : 0);
8081
+ position.needsUpdate = true;
8082
+ return drawn;
8083
+ },
8084
+ dispose() {
8085
+ geometry.dispose();
8086
+ material.dispose();
8087
+ }
8088
+ };
8089
+ }
7460
8090
  function debrisLayer(THREE, pool) {
7461
8091
  const geometry = new THREE.BoxGeometry(1, 1, 1);
7462
8092
  const material = new THREE.MeshStandardMaterial({ color: 16777215, roughness: 0.45, metalness: 0.25, flatShading: true });
7463
- const mesh = new THREE.InstancedMesh(geometry, material, DEBRIS_CAPACITY);
7464
- mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
7465
- mesh.frustumCulled = false;
7466
- mesh.name = "workshop-debris";
8093
+ const mesh2 = new THREE.InstancedMesh(geometry, material, DEBRIS_CAPACITY);
8094
+ mesh2.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
8095
+ mesh2.frustumCulled = false;
8096
+ mesh2.name = "workshop-debris";
7467
8097
  const euler = new THREE.Euler();
7468
8098
  const quaternion = new THREE.Quaternion();
7469
8099
  const position = new THREE.Vector3();
@@ -7471,16 +8101,16 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7471
8101
  const matrix = new THREE.Matrix4();
7472
8102
  const colour = new THREE.Color();
7473
8103
  const hidden = new THREE.Matrix4().makeScale(0, 0, 0);
7474
- for (let index = 0; index < DEBRIS_CAPACITY; index++) mesh.setMatrixAt(index, hidden);
7475
- mesh.setColorAt(0, colour.setHex(16777215));
8104
+ for (let index = 0; index < DEBRIS_CAPACITY; index++) mesh2.setMatrixAt(index, hidden);
8105
+ mesh2.setColorAt(0, colour.setHex(16777215));
7476
8106
  return {
7477
- object: mesh,
8107
+ object: mesh2,
7478
8108
  sync() {
7479
8109
  let drawn = false;
7480
8110
  for (let index = 0; index < DEBRIS_CAPACITY; index++) {
7481
8111
  const piece = pool.pieces[index];
7482
8112
  if (!piece.on) {
7483
- mesh.setMatrixAt(index, hidden);
8113
+ mesh2.setMatrixAt(index, hidden);
7484
8114
  continue;
7485
8115
  }
7486
8116
  drawn = true;
@@ -7488,11 +8118,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7488
8118
  euler.set(piece.rx, piece.ry, piece.rz);
7489
8119
  quaternion.setFromEuler(euler);
7490
8120
  matrix.compose(position.set(piece.x, piece.y, piece.z), quaternion, scale.set(piece.sx * shrink, piece.sy * shrink, piece.sz * shrink));
7491
- mesh.setMatrixAt(index, matrix);
7492
- mesh.setColorAt(index, colour.setHex(piece.color));
8121
+ mesh2.setMatrixAt(index, matrix);
8122
+ mesh2.setColorAt(index, colour.setHex(piece.color));
7493
8123
  }
7494
- mesh.instanceMatrix.needsUpdate = true;
7495
- if (mesh.instanceColor) mesh.instanceColor.needsUpdate = true;
8124
+ mesh2.instanceMatrix.needsUpdate = true;
8125
+ if (mesh2.instanceColor) mesh2.instanceColor.needsUpdate = true;
7496
8126
  return drawn;
7497
8127
  },
7498
8128
  dispose() {
@@ -7549,11 +8179,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7549
8179
  const dryMesh = new THREE.InstancedMesh(geometry, dry, DECAL_CAPACITY);
7550
8180
  const group = new THREE.Group();
7551
8181
  group.name = "workshop-decals";
7552
- for (const mesh of [wetMesh, dryMesh]) {
7553
- mesh.frustumCulled = false;
7554
- mesh.renderOrder = 1;
7555
- mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
7556
- group.add(mesh);
8182
+ for (const mesh2 of [wetMesh, dryMesh]) {
8183
+ mesh2.frustumCulled = false;
8184
+ mesh2.renderOrder = 1;
8185
+ mesh2.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
8186
+ group.add(mesh2);
7557
8187
  }
7558
8188
  const hidden = new THREE.Matrix4().makeScale(0, 0, 0);
7559
8189
  const colour = new THREE.Color();
@@ -7590,13 +8220,13 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7590
8220
  quaternion.multiply(turn.setFromAxisAngle(forward, index * 2.399963));
7591
8221
  const offset = 4e-3 + index % 40 * 4e-4;
7592
8222
  matrix.compose(position.set(decal2.x + normal.x * offset, decal2.y + normal.y * offset, decal2.z + normal.z * offset), quaternion, scale.set(look.sx, look.sy, 1));
7593
- const mesh = look.dried ? dryMesh : wetMesh;
7594
- mesh.setMatrixAt(index, matrix);
7595
- mesh.setColorAt(index, colour);
8223
+ const mesh2 = look.dried ? dryMesh : wetMesh;
8224
+ mesh2.setMatrixAt(index, matrix);
8225
+ mesh2.setColorAt(index, colour);
7596
8226
  }
7597
- for (const mesh of [wetMesh, dryMesh]) {
7598
- mesh.instanceMatrix.needsUpdate = true;
7599
- if (mesh.instanceColor) mesh.instanceColor.needsUpdate = true;
8227
+ for (const mesh2 of [wetMesh, dryMesh]) {
8228
+ mesh2.instanceMatrix.needsUpdate = true;
8229
+ if (mesh2.instanceColor) mesh2.instanceColor.needsUpdate = true;
7600
8230
  }
7601
8231
  return drawn;
7602
8232
  },
@@ -7610,10 +8240,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7610
8240
  function ringLayer(THREE, pool) {
7611
8241
  const geometry = new THREE.RingGeometry(0.86, 1, 40);
7612
8242
  const material = new THREE.MeshBasicMaterial({ color: 16777215, transparent: true, blending: THREE.AdditiveBlending, depthWrite: false, side: THREE.DoubleSide });
7613
- const mesh = new THREE.InstancedMesh(geometry, material, RING_CAPACITY3);
7614
- mesh.frustumCulled = false;
7615
- mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
7616
- mesh.name = "workshop-rings";
8243
+ const mesh2 = new THREE.InstancedMesh(geometry, material, RING_CAPACITY3);
8244
+ mesh2.frustumCulled = false;
8245
+ mesh2.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
8246
+ mesh2.name = "workshop-rings";
7617
8247
  const hidden = new THREE.Matrix4().makeScale(0, 0, 0);
7618
8248
  const colour = new THREE.Color();
7619
8249
  const normal = new THREE.Vector3();
@@ -7622,16 +8252,16 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7622
8252
  const position = new THREE.Vector3();
7623
8253
  const scale = new THREE.Vector3();
7624
8254
  const matrix = new THREE.Matrix4();
7625
- mesh.setColorAt(0, colour.setHex(0));
7626
- for (let index = 0; index < RING_CAPACITY3; index++) mesh.setMatrixAt(index, hidden);
8255
+ mesh2.setColorAt(0, colour.setHex(0));
8256
+ for (let index = 0; index < RING_CAPACITY3; index++) mesh2.setMatrixAt(index, hidden);
7627
8257
  return {
7628
- object: mesh,
8258
+ object: mesh2,
7629
8259
  sync() {
7630
8260
  let drawn = false;
7631
8261
  for (let index = 0; index < RING_CAPACITY3; index++) {
7632
8262
  const ring2 = pool.rings[index];
7633
8263
  if (!ring2.on) {
7634
- mesh.setMatrixAt(index, hidden);
8264
+ mesh2.setMatrixAt(index, hidden);
7635
8265
  continue;
7636
8266
  }
7637
8267
  drawn = true;
@@ -7639,11 +8269,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7639
8269
  const radius = ring2.from + (ring2.to - ring2.from) * (1 - Math.pow(1 - k, 2));
7640
8270
  quaternion.setFromUnitVectors(forward, normal.set(ring2.normal[0], ring2.normal[1], ring2.normal[2]));
7641
8271
  matrix.compose(position.set(ring2.point[0], ring2.point[1], ring2.point[2]), quaternion, scale.set(radius, radius, 1));
7642
- mesh.setMatrixAt(index, matrix);
7643
- mesh.setColorAt(index, colour.setHex(ring2.color).multiplyScalar(ring2.hold ? ring2.hold : 1 - k));
8272
+ mesh2.setMatrixAt(index, matrix);
8273
+ mesh2.setColorAt(index, colour.setHex(ring2.color).multiplyScalar(ring2.hold ? ring2.hold : 1 - k));
7644
8274
  }
7645
- mesh.instanceMatrix.needsUpdate = true;
7646
- if (mesh.instanceColor) mesh.instanceColor.needsUpdate = true;
8275
+ mesh2.instanceMatrix.needsUpdate = true;
8276
+ if (mesh2.instanceColor) mesh2.instanceColor.needsUpdate = true;
7647
8277
  return drawn;
7648
8278
  },
7649
8279
  dispose() {
@@ -7659,11 +8289,14 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7659
8289
  spriteLayer(THREE, engine.add, true, 1, pointScale),
7660
8290
  spriteLayer(THREE, engine.normal, false, 0.55, pointScale),
7661
8291
  streakLayer(THREE, engine.streaks),
8292
+ rainLayer(THREE, engine.rain),
7662
8293
  debrisLayer(THREE, engine.debris),
7663
8294
  decalLayer(THREE, engine.decals),
7664
8295
  ringLayer(THREE, engine.rings)
7665
8296
  ];
7666
8297
  for (const layer2 of layers) group.add(layer2.object);
8298
+ const meshes = createMeshLayer(THREE, engine.meshes);
8299
+ group.add(meshes.group);
7667
8300
  const flashLights = engine.lights.lights.map(() => new THREE.PointLight(16760944, 0, 14));
7668
8301
  const fireLights = engine.lights.fire.map(() => new THREE.PointLight(FIRE_LIGHT_COLOR, 0, FIRE_LIGHT_DISTANCE));
7669
8302
  for (const light of [...flashLights, ...fireLights]) group.add(light);
@@ -7672,6 +8305,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7672
8305
  sync() {
7673
8306
  let drawCalls = 0;
7674
8307
  for (const layer2 of layers) if (layer2.sync()) drawCalls++;
8308
+ drawCalls += meshes.sync();
7675
8309
  engine.lights.lights.forEach((source, index) => {
7676
8310
  const light = flashLights[index];
7677
8311
  light.position.set(source.x, source.y, source.z);
@@ -7688,6 +8322,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7688
8322
  },
7689
8323
  dispose() {
7690
8324
  for (const layer2 of layers) layer2.dispose();
8325
+ meshes.dispose();
7691
8326
  group.clear();
7692
8327
  }
7693
8328
  };
@@ -7803,8 +8438,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7803
8438
  return world.play(request.effect, request.point, {
7804
8439
  ...request.direction ? { direction: request.direction } : {},
7805
8440
  ...request.normal ? { normal: request.normal } : {},
8441
+ ...request.orientation ? { orientation: request.orientation } : {},
7806
8442
  ...request.seed !== void 0 ? { seed: request.seed } : {},
7807
- ...request.param !== void 0 ? { param: request.param } : {}
8443
+ ...request.param !== void 0 ? { param: request.param } : {},
8444
+ ...request.values ? { values: request.values } : {}
7808
8445
  });
7809
8446
  },
7810
8447
  stop: (handle) => world.stop(handle),
@@ -7965,11 +8602,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7965
8602
  * Bullet holes that stay on a fragment (`Fragment.marks`): dark discs that are children of its mesh, so they fall, spin and settle with it. The mesh is
7966
8603
  * a unit box scaled to the fragment, so each disc is scaled back; exact for a mark on a face, which is where a bullet hole is.
7967
8604
  */
7968
- addMarks(mesh, f) {
8605
+ addMarks(mesh2, f) {
7969
8606
  if (!f.marks?.length) return;
7970
8607
  this.markGeometry ?? (this.markGeometry = new this.THREE.CircleGeometry(1, 10));
7971
8608
  this.markMaterial ?? (this.markMaterial = new this.THREE.MeshBasicMaterial({ color: 723465, depthWrite: false, polygonOffset: true, polygonOffsetFactor: -4, polygonOffsetUnits: -4 }));
7972
- const sc = mesh.scale;
8609
+ const sc = mesh2.scale;
7973
8610
  for (const mark of f.marks) {
7974
8611
  const disc = new this.THREE.Mesh(this.markGeometry, this.markMaterial);
7975
8612
  const radius = Math.max(4e-3, mark.diameter) * 0.6;
@@ -7977,22 +8614,22 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7977
8614
  disc.position.set(mark.point[0] / sc.x + n.x * 2e-3 / sc.x, mark.point[1] / sc.y + n.y * 2e-3 / sc.y, mark.point[2] / sc.z + n.z * 2e-3 / sc.z);
7978
8615
  disc.quaternion.setFromUnitVectors(this.w.set(0, 0, 1), n);
7979
8616
  disc.scale.set(radius / sc.x, radius / sc.y, radius / sc.z);
7980
- mesh.add(disc);
8617
+ mesh2.add(disc);
7981
8618
  }
7982
8619
  }
7983
8620
  /** Create the mesh of a fragment and start its motion. `maxAllowed` is the live budget. */
7984
8621
  spawn(f, material, home, maxAllowed) {
7985
8622
  const geo = f.shape === "shard" ? this.shardGeometry(hashString(f.id)) : this.box;
7986
- const mesh = new this.THREE.Mesh(geo, material);
7987
- mesh.position.set(f.center[0], f.center[1], f.center[2]);
7988
- mesh.quaternion.set(f.rotation[0], f.rotation[1], f.rotation[2], f.rotation[3]).normalize();
8623
+ const mesh2 = new this.THREE.Mesh(geo, material);
8624
+ mesh2.position.set(f.center[0], f.center[1], f.center[2]);
8625
+ mesh2.quaternion.set(f.rotation[0], f.rotation[1], f.rotation[2], f.rotation[3]).normalize();
7989
8626
  const sz = f.size;
7990
8627
  const sc = new this.THREE.Vector3(Math.max(5e-3, sz[0]), Math.max(5e-3, sz[1]), Math.max(3e-3, sz[2]));
7991
- mesh.scale.copy(sc);
7992
- mesh.castShadow = true;
7993
- this.o.scene.add(mesh);
8628
+ mesh2.scale.copy(sc);
8629
+ mesh2.castShadow = true;
8630
+ this.o.scene.add(mesh2);
7994
8631
  const live = {
7995
- mesh,
8632
+ mesh: mesh2,
7996
8633
  home,
7997
8634
  vel: new this.THREE.Vector3(f.velocity[0], f.velocity[1], f.velocity[2]),
7998
8635
  ang: new this.THREE.Vector3(f.angularVelocity[0], f.angularVelocity[1], f.angularVelocity[2]),
@@ -8013,8 +8650,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8013
8650
  alive--;
8014
8651
  }
8015
8652
  }
8016
- this.addMarks(mesh, f);
8017
- return mesh;
8653
+ this.addMarks(mesh2, f);
8654
+ return mesh2;
8018
8655
  }
8019
8656
  /** Add velocity to every fragment within `radius` of a point (blasts). */
8020
8657
  blast(point, radius, speed) {
@@ -8053,24 +8690,24 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8053
8690
  }
8054
8691
  }
8055
8692
  step(f, dt, g) {
8056
- const mesh = f.mesh;
8693
+ const mesh2 = f.mesh;
8057
8694
  f.vel.y -= g * dt;
8058
- mesh.position.addScaledVector(f.vel, dt);
8695
+ mesh2.position.addScaledVector(f.vel, dt);
8059
8696
  const w = f.ang.length();
8060
8697
  if (w > 1e-5) {
8061
8698
  this.dq.setFromAxisAngle(this.v.copy(f.ang).multiplyScalar(1 / w), w * dt);
8062
- mesh.quaternion.premultiply(this.dq).normalize();
8699
+ mesh2.quaternion.premultiply(this.dq).normalize();
8063
8700
  }
8064
- this.m.makeRotationFromQuaternion(mesh.quaternion);
8701
+ this.m.makeRotationFromQuaternion(mesh2.quaternion);
8065
8702
  const e = this.m.elements;
8066
8703
  const h = f.half;
8067
8704
  const sc = f.dying ? f.fade : 1;
8068
8705
  const ext = (Math.abs(e[1]) * h.x + Math.abs(e[5]) * h.y + Math.abs(e[9]) * h.z) * sc;
8069
- const gy = this.o.groundAt(mesh.position.x, mesh.position.z);
8706
+ const gy = this.o.groundAt(mesh2.position.x, mesh2.position.z);
8070
8707
  let contact = false;
8071
- if (mesh.position.y - ext < gy) {
8708
+ if (mesh2.position.y - ext < gy) {
8072
8709
  contact = true;
8073
- mesh.position.y = gy + ext;
8710
+ mesh2.position.y = gy + ext;
8074
8711
  if (f.vel.y < -0.6) {
8075
8712
  f.vel.y = -f.vel.y * 0.25;
8076
8713
  f.vel.x *= 0.7;
@@ -8103,8 +8740,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8103
8740
  }
8104
8741
  /** Tip the fragment towards lying on its flattest face. */
8105
8742
  settle(f, dt) {
8106
- const mesh = f.mesh;
8107
- this.m.makeRotationFromQuaternion(mesh.quaternion);
8743
+ const mesh2 = f.mesh;
8744
+ this.m.makeRotationFromQuaternion(mesh2.quaternion);
8108
8745
  const e = this.m.elements;
8109
8746
  const up = [e[1], e[5], e[9]];
8110
8747
  const half = [f.half.x, f.half.y, f.half.z];
@@ -8119,8 +8756,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8119
8756
  this.v.set(e[axis2 * 4], e[axis2 * 4 + 1], e[axis2 * 4 + 2]).multiplyScalar(sign);
8120
8757
  this.w.set(0, 1, 0);
8121
8758
  this.dq.setFromUnitVectors(this.v, this.w);
8122
- this.q.copy(this.dq).multiply(mesh.quaternion);
8123
- mesh.quaternion.slerp(this.q, clamp6(dt * 7, 0, 1));
8759
+ this.q.copy(this.dq).multiply(mesh2.quaternion);
8760
+ mesh2.quaternion.slerp(this.q, clamp6(dt * 7, 0, 1));
8124
8761
  }
8125
8762
  /** Remove the fragments of one object (after `restore`). */
8126
8763
  removeHome(home) {
@@ -8192,13 +8829,13 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8192
8829
  }
8193
8830
  }
8194
8831
  while (this.spots.length >= this.cap) this.remove(this.spots[0]);
8195
- const mesh = new this.THREE.Mesh(this.geometry, this.material(kind, color));
8832
+ const mesh2 = new this.THREE.Mesh(this.geometry, this.material(kind, color));
8196
8833
  const y = this.groundAt(x, z) + 4e-3 + this.spots.length % 8 * 4e-4;
8197
- mesh.position.set(x, y, z);
8198
- mesh.scale.setScalar(1e-3);
8199
- mesh.renderOrder = 1;
8200
- this.scene.add(mesh);
8201
- this.spots.push({ mesh, kind, home, x, z, radius: 1e-3, target: Math.min(MAX_RADIUS, radius) });
8834
+ mesh2.position.set(x, y, z);
8835
+ mesh2.scale.setScalar(1e-3);
8836
+ mesh2.renderOrder = 1;
8837
+ this.scene.add(mesh2);
8838
+ this.spots.push({ mesh: mesh2, kind, home, x, z, radius: 1e-3, target: Math.min(MAX_RADIUS, radius) });
8202
8839
  }
8203
8840
  remove(s) {
8204
8841
  const i = this.spots.indexOf(s);
@@ -8578,7 +9215,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8578
9215
  /** Bars drawn now, over every part. */
8579
9216
  get count() {
8580
9217
  let n = 0;
8581
- for (const mesh of this.cages.values()) n += mesh.count;
9218
+ for (const mesh2 of this.cages.values()) n += mesh2.count;
8582
9219
  return n;
8583
9220
  }
8584
9221
  /** Meshes of the live cages (tests and picking). */
@@ -8589,25 +9226,25 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8589
9226
  set(key, bars) {
8590
9227
  this.remove(key);
8591
9228
  if (bars.length === 0) return;
8592
- const mesh = new this.THREE.InstancedMesh(this.geometry, this.material, bars.length);
9229
+ const mesh2 = new this.THREE.InstancedMesh(this.geometry, this.material, bars.length);
8593
9230
  bars.forEach(([a, b], i) => {
8594
9231
  this.dir.set(b[0] - a[0], b[1] - a[1], b[2] - a[2]);
8595
9232
  const length = this.dir.length();
8596
9233
  this.mid.set((a[0] + b[0]) / 2, (a[1] + b[1]) / 2, (a[2] + b[2]) / 2);
8597
9234
  this.q.setFromUnitVectors(this.up, length > 1e-9 ? this.dir.divideScalar(length) : this.up);
8598
9235
  this.m.compose(this.mid, this.q, this.scale.set(1, Math.max(length, 1e-4), 1));
8599
- mesh.setMatrixAt(i, this.m);
9236
+ mesh2.setMatrixAt(i, this.m);
8600
9237
  });
8601
- mesh.instanceMatrix.needsUpdate = true;
8602
- mesh.castShadow = true;
8603
- this.scene.add(mesh);
8604
- this.cages.set(key, mesh);
9238
+ mesh2.instanceMatrix.needsUpdate = true;
9239
+ mesh2.castShadow = true;
9240
+ this.scene.add(mesh2);
9241
+ this.cages.set(key, mesh2);
8605
9242
  }
8606
9243
  remove(key) {
8607
- const mesh = this.cages.get(key);
8608
- if (!mesh) return;
8609
- this.scene.remove(mesh);
8610
- mesh.dispose();
9244
+ const mesh2 = this.cages.get(key);
9245
+ if (!mesh2) return;
9246
+ this.scene.remove(mesh2);
9247
+ mesh2.dispose();
8611
9248
  this.cages.delete(key);
8612
9249
  }
8613
9250
  /** Remove the cages of every part of `object` (keys are `object|part`). */
@@ -8626,9 +9263,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8626
9263
 
8627
9264
  // ../game-destruction/dist/index.js
8628
9265
  var EPS = 1e-9;
8629
- var IDENTITY = [0, 0, 0, 1];
9266
+ var IDENTITY2 = [0, 0, 0, 1];
8630
9267
  var quat = {
8631
- identity: () => [IDENTITY[0], IDENTITY[1], IDENTITY[2], IDENTITY[3]],
9268
+ identity: () => [IDENTITY2[0], IDENTITY2[1], IDENTITY2[2], IDENTITY2[3]],
8632
9269
  /** Hamilton product `a * b` (apply `b` first, then `a`). */
8633
9270
  mul: (a, b) => [
8634
9271
  a[3] * b[0] + a[0] * b[3] + a[1] * b[2] - a[2] * b[1],
@@ -9951,25 +10588,25 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
9951
10588
  /**
9952
10589
  * @param range vertex range `[v0, v1)` of a merged mesh this part is drawn by (see `MeshRange`); `hosts` caches the shared state per mesh for one `bind`.
9953
10590
  */
9954
- makeMeshVis(mesh, home, range, hosts) {
10591
+ makeMeshVis(mesh2, home, range, hosts) {
9955
10592
  let host = null;
9956
10593
  if (range) {
9957
- host = hosts?.get(mesh) ?? null;
10594
+ host = hosts?.get(mesh2) ?? null;
9958
10595
  if (!host) {
9959
- host = { mesh, origGeometry: mesh.geometry, origMaterial: mesh.material, ownGeo: false, work: null, ownMat: false, sharers: [] };
9960
- hosts?.set(mesh, host);
10596
+ host = { mesh: mesh2, origGeometry: mesh2.geometry, origMaterial: mesh2.material, ownGeo: false, work: null, ownMat: false, sharers: [] };
10597
+ hosts?.set(mesh2, host);
9961
10598
  }
9962
10599
  }
9963
10600
  const mv = {
9964
- mesh,
10601
+ mesh: mesh2,
9965
10602
  home,
9966
- origGeometry: mesh.geometry,
9967
- origMaterial: mesh.material,
9968
- origVisible: mesh.visible,
9969
- origParent: mesh.parent,
9970
- origPosition: mesh.position.clone(),
9971
- origQuaternion: mesh.quaternion.clone(),
9972
- origScale: mesh.scale.clone(),
10603
+ origGeometry: mesh2.geometry,
10604
+ origMaterial: mesh2.material,
10605
+ origVisible: mesh2.visible,
10606
+ origParent: mesh2.parent,
10607
+ origPosition: mesh2.position.clone(),
10608
+ origQuaternion: mesh2.quaternion.clone(),
10609
+ origScale: mesh2.scale.clone(),
9973
10610
  ownMat: false,
9974
10611
  ownGeo: false,
9975
10612
  work: null,
@@ -10094,15 +10731,15 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
10094
10731
  * Returns false when nothing was changed (too far, low quality on a heavy mesh, or no editable positions).
10095
10732
  */
10096
10733
  applyDent(mv, worldPoint, worldNormal, dent) {
10097
- const mesh = mv.mesh;
10734
+ const mesh2 = mv.mesh;
10098
10735
  if (this.lowQuality && this.vertexCount(mv) > 2e4) return false;
10099
- mesh.updateWorldMatrix(true, false);
10100
- const s = mesh.matrixWorld.getMaxScaleOnAxis() || 1;
10101
- const lp = mesh.worldToLocal(this.a.copy(worldPoint));
10102
- const ln = this.b.copy(worldNormal).transformDirection(this.m4.copy(mesh.matrixWorld).invert()).normalize();
10736
+ mesh2.updateWorldMatrix(true, false);
10737
+ const s = mesh2.matrixWorld.getMaxScaleOnAxis() || 1;
10738
+ const lp = mesh2.worldToLocal(this.a.copy(worldPoint));
10739
+ const ln = this.b.copy(worldNormal).transformDirection(this.m4.copy(mesh2.matrixWorld).invert()).normalize();
10103
10740
  const r = dent.radius / s;
10104
10741
  const depth = dent.depth / s;
10105
- const geo = mesh.geometry;
10742
+ const geo = mesh2.geometry;
10106
10743
  let bb;
10107
10744
  if (mv.host) bb = this.localBox(mv, this.lbox);
10108
10745
  else {
@@ -10112,7 +10749,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
10112
10749
  if (bb && !bb.isEmpty() && bb.distanceToPoint(lp) > r) return false;
10113
10750
  if (!this.ownGeometry(mv)) return false;
10114
10751
  const work = mv.host ? mv.host.work : mv.work;
10115
- const pos = positionAttr(mesh.geometry);
10752
+ const pos = positionAttr(mesh2.geometry);
10116
10753
  const r2 = r * r;
10117
10754
  let touched = false;
10118
10755
  const px = lp.x;
@@ -10178,11 +10815,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
10178
10815
  // Decals
10179
10816
  // ------------------------------------------------------------------------------------------------------------------------------------
10180
10817
  /** Decal pose in mesh space: position and a rotation taking +Z to the surface normal. */
10181
- decalPose(mesh, worldPoint, worldNormal, lift) {
10182
- mesh.updateWorldMatrix(true, false);
10183
- const s = mesh.matrixWorld.getMaxScaleOnAxis() || 1;
10184
- const ln = worldNormal.clone().transformDirection(this.m4.copy(mesh.matrixWorld).invert()).normalize();
10185
- const pos = mesh.worldToLocal(worldPoint.clone()).addScaledVector(ln, lift / s);
10818
+ decalPose(mesh2, worldPoint, worldNormal, lift) {
10819
+ mesh2.updateWorldMatrix(true, false);
10820
+ const s = mesh2.matrixWorld.getMaxScaleOnAxis() || 1;
10821
+ const ln = worldNormal.clone().transformDirection(this.m4.copy(mesh2.matrixWorld).invert()).normalize();
10822
+ const pos = mesh2.worldToLocal(worldPoint.clone()).addScaledVector(ln, lift / s);
10186
10823
  const quat2 = new this.THREE.Quaternion().setFromUnitVectors(new this.THREE.Vector3(0, 0, 1), ln);
10187
10824
  return { s, pos, quat: quat2 };
10188
10825
  }
@@ -10318,7 +10955,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
10318
10955
  if (Math.abs(m - pv.meltApplied) < 0.01) return;
10319
10956
  pv.meltApplied = m;
10320
10957
  for (const mv of pv.meshes) {
10321
- const mesh = mv.mesh;
10958
+ const mesh2 = mv.mesh;
10322
10959
  if (mv.host) {
10323
10960
  mv.meltApplied = m;
10324
10961
  mv.touched = true;
@@ -10326,27 +10963,27 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
10326
10963
  continue;
10327
10964
  }
10328
10965
  if (mv.meltApplied === 0 && m > 0) {
10329
- mesh.updateWorldMatrix(true, false);
10330
- mv.bottomY = this.box.setFromObject(mesh).min.y;
10966
+ mesh2.updateWorldMatrix(true, false);
10967
+ mv.bottomY = this.box.setFromObject(mesh2).min.y;
10331
10968
  }
10332
10969
  mv.meltApplied = m;
10333
10970
  mv.moved = true;
10334
10971
  const sy = lerp5(1, 0.25, m);
10335
10972
  const sxz = 1 + 0.18 * m;
10336
- mesh.scale.set(mv.origScale.x * sxz, mv.origScale.y * sy, mv.origScale.z * sxz);
10337
- mesh.position.copy(mv.origPosition);
10338
- mesh.updateWorldMatrix(true, false);
10339
- const bottom = this.box.setFromObject(mesh).min.y;
10973
+ mesh2.scale.set(mv.origScale.x * sxz, mv.origScale.y * sy, mv.origScale.z * sxz);
10974
+ mesh2.position.copy(mv.origPosition);
10975
+ mesh2.updateWorldMatrix(true, false);
10976
+ const bottom = this.box.setFromObject(mesh2).min.y;
10340
10977
  const dy = mv.bottomY - bottom;
10341
- if (Math.abs(dy) > 1e-6 && mesh.parent) {
10342
- mesh.parent.updateWorldMatrix(true, false);
10343
- const inv = this.m4.copy(mesh.parent.matrixWorld).invert();
10978
+ if (Math.abs(dy) > 1e-6 && mesh2.parent) {
10979
+ mesh2.parent.updateWorldMatrix(true, false);
10980
+ const inv = this.m4.copy(mesh2.parent.matrixWorld).invert();
10344
10981
  this.a.set(0, dy, 0).applyMatrix4(inv);
10345
10982
  this.b.set(0, 0, 0).applyMatrix4(inv);
10346
- mesh.position.copy(mv.origPosition).add(this.a.sub(this.b));
10983
+ mesh2.position.copy(mv.origPosition).add(this.a.sub(this.b));
10347
10984
  }
10348
10985
  if (!(this.lowQuality && this.vertexCount(mv) > 2e4) && this.ownGeometry(mv)) {
10349
- this.writePositions(mv, positionAttr(mesh.geometry));
10986
+ this.writePositions(mv, positionAttr(mesh2.geometry));
10350
10987
  }
10351
10988
  }
10352
10989
  }
@@ -10424,10 +11061,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
10424
11061
  const sy = lerp5(1, 0.25, melt);
10425
11062
  const sxz = 1 + 0.18 * melt;
10426
11063
  for (let i = range[0] * 3, v = 0; i < range[1] * 3; i += 3, v++) {
10427
- const noise2 = (Math.imul(v + 1, 2654435761) >>> 0) % 1e3 / 1e3;
11064
+ const noise = (Math.imul(v + 1, 2654435761) >>> 0) % 1e3 / 1e3;
10428
11065
  arr[i] = cx + (work[i] - cx) * sxz;
10429
11066
  arr[i + 2] = cz + (work[i + 2] - cz) * sxz;
10430
- arr[i + 1] = Math.max(minY, minY + (work[i + 1] - minY) * sy - noise2 * melt * h * 0.04);
11067
+ arr[i + 1] = Math.max(minY, minY + (work[i + 1] - minY) * sy - noise * melt * h * 0.04);
10431
11068
  }
10432
11069
  }
10433
11070
  /** The colour attribute of the host's current geometry (created white when there is none) with vertex colours switched on in its material. */
@@ -10619,30 +11256,30 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
10619
11256
  this.restoreRanged(mv);
10620
11257
  return;
10621
11258
  }
10622
- const mesh = mv.mesh;
11259
+ const mesh2 = mv.mesh;
10623
11260
  for (const d of mv.decals) this.disposeDecal(d);
10624
11261
  mv.decals.length = 0;
10625
11262
  if (mv.ownGeo) {
10626
- mesh.geometry.dispose();
10627
- mesh.geometry = mv.origGeometry;
11263
+ mesh2.geometry.dispose();
11264
+ mesh2.geometry = mv.origGeometry;
10628
11265
  mv.ownGeo = false;
10629
11266
  mv.work = null;
10630
11267
  }
10631
11268
  if (mv.ownMat) {
10632
- for (const m of materialsOf(mesh.material)) m.dispose();
10633
- mesh.material = mv.origMaterial;
11269
+ for (const m of materialsOf(mesh2.material)) m.dispose();
11270
+ mesh2.material = mv.origMaterial;
10634
11271
  mv.ownMat = false;
10635
11272
  mv.baseColors = null;
10636
11273
  }
10637
- mesh.visible = mv.origVisible;
10638
- if (mv.origParent && mesh.parent !== mv.origParent) {
10639
- mv.origParent.add(mesh);
11274
+ mesh2.visible = mv.origVisible;
11275
+ if (mv.origParent && mesh2.parent !== mv.origParent) {
11276
+ mv.origParent.add(mesh2);
10640
11277
  mv.moved = true;
10641
11278
  }
10642
11279
  if (mv.moved) {
10643
- mesh.position.copy(mv.origPosition);
10644
- mesh.quaternion.copy(mv.origQuaternion);
10645
- mesh.scale.copy(mv.origScale);
11280
+ mesh2.position.copy(mv.origPosition);
11281
+ mesh2.quaternion.copy(mv.origQuaternion);
11282
+ mesh2.scale.copy(mv.origScale);
10646
11283
  mv.moved = false;
10647
11284
  }
10648
11285
  mv.meltApplied = 0;
@@ -11780,6 +12417,56 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
11780
12417
  };
11781
12418
  }
11782
12419
  var GAME_SFX_BANK = { id: "game_sfx", sounds: [...GAME_SFX_CLASSES, GAME_SFX_FALLBACK].map((row) => noiseClassRecipe(row)) };
12420
+ var ATTACK = 2e-3;
12421
+ function lengthOf(d, attack) {
12422
+ if (typeof d === "number") return attack + d;
12423
+ if (Array.isArray(d)) return [attack + d[0], attack + d[1]];
12424
+ return { ...d, add: (d.add ?? 0) + attack };
12425
+ }
12426
+ var envelope = (attack) => [
12427
+ [0, 1e-4, "set", "fixed"],
12428
+ [attack, 1, "exp"],
12429
+ ["1d", 1e-4, "exp", "fixed"]
12430
+ ];
12431
+ function sceneNoise(o) {
12432
+ const a = o.a ?? ATTACK;
12433
+ const over = typeof o.d === "number" ? o.d : 0;
12434
+ return {
12435
+ filters: {
12436
+ mode: "serial",
12437
+ bank: [{ type: o.type ?? "lowpass", frequency: o.f2 === void 0 ? { set: o.f } : { set: o.f, to: o.f2, curve: "exp", over }, q: o.q ?? 0.7 }]
12438
+ },
12439
+ duration: lengthOf(o.d, a),
12440
+ gain: o.vol,
12441
+ ...o.delay === void 0 ? {} : { at: o.delay },
12442
+ ...o.wet === void 0 ? {} : { wet: o.wet },
12443
+ source: { kind: "noise", offset: "random", offsetScale: 1.5, loop: true },
12444
+ envelope: envelope(a)
12445
+ };
12446
+ }
12447
+ function sceneTone(o) {
12448
+ return {
12449
+ source: { kind: "osc", wave: o.wave ?? "sine", frequency: { from: o.f, to: o.f2 ?? "from", curve: "exp", over: "duration" } },
12450
+ duration: lengthOf(o.d, ATTACK),
12451
+ gain: o.vol,
12452
+ ...o.delay === void 0 ? {} : { at: o.delay },
12453
+ ...o.wet === void 0 ? {} : { wet: o.wet },
12454
+ envelope: envelope(ATTACK)
12455
+ };
12456
+ }
12457
+ var sceneMany = (count, make) => Array.from({ length: count }, make);
12458
+ function sceneRecipe(id, layers, line, options = {}) {
12459
+ return {
12460
+ id,
12461
+ bus: options.bus ?? "world",
12462
+ pan: true,
12463
+ layers,
12464
+ falloff: DISTANCE_CURVES.art_scene,
12465
+ ...options.vars ? { vars: options.vars } : {},
12466
+ ...options.group ? { group: options.group } : {},
12467
+ sources: [`cena:${line}`]
12468
+ };
12469
+ }
11783
12470
  var BENCH_BANK = {
11784
12471
  id: "audio_bench",
11785
12472
  sounds: [
@@ -11827,88 +12514,42 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
11827
12514
  };
11828
12515
 
11829
12516
  // src/audio/recipes.ts
11830
- var ATTACK = 2e-3;
11831
- var lengthOf = (d, attack) => {
11832
- if (typeof d === "number") return attack + d;
11833
- if (Array.isArray(d)) return [attack + d[0], attack + d[1]];
11834
- return { ...d, add: (d.add ?? 0) + attack };
11835
- };
11836
- var envelope = (attack) => [
11837
- [0, 1e-4, "set", "fixed"],
11838
- [attack, 1, "exp"],
11839
- ["1d", 1e-4, "exp", "fixed"]
11840
- ];
11841
- function noise(o) {
11842
- const a = o.a ?? ATTACK;
11843
- const over = typeof o.d === "number" ? o.d : 0;
11844
- return {
11845
- filters: {
11846
- mode: "serial",
11847
- bank: [{ type: o.type ?? "lowpass", frequency: o.f2 === void 0 ? { set: o.f } : { set: o.f, to: o.f2, curve: "exp", over }, q: o.q ?? 0.7 }]
11848
- },
11849
- duration: lengthOf(o.d, a),
11850
- gain: o.vol,
11851
- ...o.delay === void 0 ? {} : { at: o.delay },
11852
- ...o.wet === void 0 ? {} : { wet: o.wet },
11853
- source: { kind: "noise", offset: "random", offsetScale: 1.5, loop: true },
11854
- envelope: envelope(a)
11855
- };
11856
- }
11857
- function tone(o) {
11858
- return {
11859
- source: { kind: "osc", wave: o.wave ?? "sine", frequency: { from: o.f, to: o.f2 ?? "from", curve: "exp", over: "duration" } },
11860
- duration: lengthOf(o.d, ATTACK),
11861
- gain: o.vol,
11862
- ...o.delay === void 0 ? {} : { at: o.delay },
11863
- ...o.wet === void 0 ? {} : { wet: o.wet },
11864
- envelope: envelope(ATTACK)
11865
- };
11866
- }
11867
- var many = (count, make) => Array.from({ length: count }, make);
11868
- var scene = (id, layers, line, vars) => ({
11869
- id,
11870
- bus: "world",
11871
- pan: true,
11872
- layers,
11873
- falloff: DISTANCE_CURVES.art_scene,
11874
- ...vars ? { vars } : {},
11875
- sources: [`cena:${line}`]
11876
- });
12517
+ var scene = (id, layers, line, vars) => sceneRecipe(id, layers, line, vars ? { vars } : {});
11877
12518
  var boomLayers = (big) => [
11878
- noise({ f: 1200, f2: 120, d: 1.8, vol: 2, wet: 1.2 }),
11879
- tone({ f: 65, f2: 24, d: 1, vol: 1.8 }),
11880
- noise({ type: "highpass", f: 1500, d: 0.2, vol: 0.8 }),
11881
- ...big ? [noise({ f: 500, f2: 90, a: 0.2, d: 2.4, vol: 1.2, wet: 1, delay: 0.15 })] : [],
11882
- ...many(8, () => tone({ f: [2600, 5600], d: 0.05, vol: 0.08, delay: [0.4, 1.6] }))
12519
+ sceneNoise({ f: 1200, f2: 120, d: 1.8, vol: 2, wet: 1.2 }),
12520
+ sceneTone({ f: 65, f2: 24, d: 1, vol: 1.8 }),
12521
+ sceneNoise({ type: "highpass", f: 1500, d: 0.2, vol: 0.8 }),
12522
+ ...big ? [sceneNoise({ f: 500, f2: 90, a: 0.2, d: 2.4, vol: 1.2, wet: 1, delay: 0.15 })] : [],
12523
+ ...sceneMany(8, () => sceneTone({ f: [2600, 5600], d: 0.05, vol: 0.08, delay: [0.4, 1.6] }))
11883
12524
  ];
11884
12525
  var SCENE_SOUNDS = [
11885
12526
  scene(
11886
12527
  "glass_shatter",
11887
- [noise({ type: "highpass", f: 2800, d: 0.45, vol: 0.9, wet: 0.5 }), ...many(12, () => tone({ f: [3e3, 7500], d: [0.06, 0.14], vol: 0.18, delay: [0, 0.55] }))],
12528
+ [sceneNoise({ type: "highpass", f: 2800, d: 0.45, vol: 0.9, wet: 0.5 }), ...sceneMany(12, () => sceneTone({ f: [3e3, 7500], d: [0.06, 0.14], vol: 0.18, delay: [0, 0.55] }))],
11888
12529
  "L1359 glass"
11889
12530
  ),
11890
12531
  scene(
11891
12532
  "wood_hit",
11892
- [noise({ type: "bandpass", f: 850, q: 1, d: 0.25, vol: 0.9, wet: 0.3 }), tone({ f: 210, f2: 90, d: 0.1, vol: 0.6 }), noise({ type: "highpass", f: 2e3, d: 0.04, vol: 0.5 })],
12533
+ [sceneNoise({ type: "bandpass", f: 850, q: 1, d: 0.25, vol: 0.9, wet: 0.3 }), sceneTone({ f: 210, f2: 90, d: 0.1, vol: 0.6 }), sceneNoise({ type: "highpass", f: 2e3, d: 0.04, vol: 0.5 })],
11893
12534
  "L1360 wood"
11894
12535
  ),
11895
- scene("thunk", [noise({ type: "bandpass", f: 700, q: 2, d: 0.07, vol: 0.4 })], "L1361 thunk"),
11896
- scene("ping", [tone({ f: [2600, 3500], f2: 1400, d: 0.14, vol: 0.18, wet: 0.3 })], "L1362 ping"),
11897
- scene("clang", [tone({ f: [520, 720], f2: 480, d: 0.5, vol: 0.35, wet: 0.4 }), tone({ f: 1370, f2: 1300, d: 0.3, vol: 0.15 }), noise({ type: "highpass", f: 2e3, d: 0.05, vol: 0.5 })], "L1363 clang"),
11898
- scene("chip", [noise({ type: "bandpass", f: 1600, q: 1.2, d: 0.06, vol: 0.5 })], "L1364 chip"),
12536
+ scene("thunk", [sceneNoise({ type: "bandpass", f: 700, q: 2, d: 0.07, vol: 0.4 })], "L1361 thunk"),
12537
+ scene("ping", [sceneTone({ f: [2600, 3500], f2: 1400, d: 0.14, vol: 0.18, wet: 0.3 })], "L1362 ping"),
12538
+ scene("clang", [sceneTone({ f: [520, 720], f2: 480, d: 0.5, vol: 0.35, wet: 0.4 }), sceneTone({ f: 1370, f2: 1300, d: 0.3, vol: 0.15 }), sceneNoise({ type: "highpass", f: 2e3, d: 0.05, vol: 0.5 })], "L1363 clang"),
12539
+ scene("chip", [sceneNoise({ type: "bandpass", f: 1600, q: 1.2, d: 0.06, vol: 0.5 })], "L1364 chip"),
11899
12540
  scene(
11900
12541
  "crumble",
11901
- [noise({ f: 900, f2: 200, d: 0.6, vol: 0.9, wet: 0.3 }), ...many(5, () => noise({ type: "bandpass", f: [500, 1400], q: 2, d: 0.05, vol: 0.4, delay: [0.1, 0.5] }))],
12542
+ [sceneNoise({ f: 900, f2: 200, d: 0.6, vol: 0.9, wet: 0.3 }), ...sceneMany(5, () => sceneNoise({ type: "bandpass", f: [500, 1400], q: 2, d: 0.05, vol: 0.4, delay: [0.1, 0.5] }))],
11902
12543
  "L1365 crumble"
11903
12544
  ),
11904
12545
  // `thud(x, z, v)`: `v` is the `scale` param (default 1)
11905
- scene("thud", [tone({ f: 75, f2: 38, d: 0.22, vol: { param: "scale", scale: 0.6 } }), noise({ f: 420, d: 0.16, vol: { param: "scale", scale: 0.45 } })], "L1358 thud", { scale: 1 }),
12546
+ scene("thud", [sceneTone({ f: 75, f2: 38, d: 0.22, vol: { param: "scale", scale: 0.6 } }), sceneNoise({ f: 420, d: 0.16, vol: { param: "scale", scale: 0.45 } })], "L1358 thud", { scale: 1 }),
11906
12547
  // `hiss(x, z, d)`: `seconds` is the param `d` (default 1.2)
11907
- scene("hiss", [noise({ type: "highpass", f: 3500, a: 0.05, d: { param: "seconds" }, vol: 0.35 })], "L1366 hiss", { seconds: 1.2 }),
12548
+ scene("hiss", [sceneNoise({ type: "highpass", f: 3500, a: 0.05, d: { param: "seconds" }, vol: 0.35 })], "L1366 hiss", { seconds: 1.2 }),
11908
12549
  scene("boom", boomLayers(false), "L1367 boom"),
11909
12550
  scene("boom_big", boomLayers(true), "L1367 boom (big)"),
11910
- scene("burst", [noise({ f: 700, f2: 120, d: 0.7, vol: 1.4, wet: 0.5 }), noise({ type: "highpass", f: 3e3, a: 0.05, d: 1.2, vol: 0.4, delay: 0.1 })], "L1357 burst"),
11911
- scene("crack", [noise({ type: "highpass", f: 1800, d: 0.05, vol: 0.8 }), tone({ f: 420, f2: 180, d: 0.06, vol: 0.4 })], "L1354 crack")
12551
+ scene("burst", [sceneNoise({ f: 700, f2: 120, d: 0.7, vol: 1.4, wet: 0.5 }), sceneNoise({ type: "highpass", f: 3e3, a: 0.05, d: 1.2, vol: 0.4, delay: 0.1 })], "L1357 burst"),
12552
+ scene("crack", [sceneNoise({ type: "highpass", f: 1800, d: 0.05, vol: 0.8 }), sceneTone({ f: 420, f2: 180, d: 0.06, vol: 0.4 })], "L1354 crack")
11912
12553
  ];
11913
12554
  var EXPLOSION_BOOM = {
11914
12555
  id: "explosion_boom",