@adatechnology/game-destruction-three 0.7.1 → 0.7.2

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,148 @@ 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 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
3576
  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])) };
3577
+ var GAME_EFFECTS = { ...WORKSHOP_EFFECTS, ...Object.fromEntries([...EVENT_EFFECTS, ...VIEWMODEL_EFFECTS].map((effect) => [effect.id, effect])) };
3208
3578
  var LEHMER_MULTIPLIER = 16807;
3209
3579
  var LEHMER_MODULUS = 2147483647;
3210
3580
  var HASH_MIX = 2654435761;
@@ -3300,7 +3670,7 @@ var GameDestructionThree = (() => {
3300
3670
  }
3301
3671
  var COLUMNS = 6;
3302
3672
  var ROWS = 5;
3303
- var ORIGIN2 = [-2.2, 0, 2.6];
3673
+ var ORIGIN3 = [-2.2, 0, 2.6];
3304
3674
  var BLOCK_SIZE = [0.5, 0.44, 0.25];
3305
3675
  var OBJECT_ID_DRAWS2 = 4;
3306
3676
  var FLOOR2 = 0.13;
@@ -3343,7 +3713,7 @@ var GameDestructionThree = (() => {
3343
3713
  const chipScale = random.between(0.8, 1.3) * scale;
3344
3714
  for (let draw = 0; draw < OBJECT_ID_DRAWS2; draw++) random.next();
3345
3715
  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);
3716
+ impact(host.engine, "concrete", [ORIGIN3[0] + x, y, ORIGIN3[2] + 0.13], [0, 0, 1], 0.8);
3347
3717
  } else if (action === "collapse") {
3348
3718
  const { x, y } = args;
3349
3719
  for (const block of blocks) {
@@ -3393,7 +3763,7 @@ var GameDestructionThree = (() => {
3393
3763
  }
3394
3764
  };
3395
3765
  }
3396
- var ORIGIN3 = [5.6, 0, 0.8];
3766
+ var ORIGIN4 = [5.6, 0, 0.8];
3397
3767
  var MAX_TILT = 0.25;
3398
3768
  function createCarBehavior() {
3399
3769
  let y = 0;
@@ -3425,8 +3795,13 @@ var GameDestructionThree = (() => {
3425
3795
  }
3426
3796
  }
3427
3797
  },
3428
- query(_host, name) {
3429
- if (name === "position") return [ORIGIN3[0], y, ORIGIN3[2]];
3798
+ query(_host, name, args) {
3799
+ if (name === "position") return [ORIGIN4[0], y, ORIGIN4[2]];
3800
+ if (name === "point") {
3801
+ const [x, py, z] = args;
3802
+ const point = carPoint(x, py, z);
3803
+ return [point[0], point[1] + y, point[2]];
3804
+ }
3430
3805
  if (name === "roll") return roll;
3431
3806
  throw new Error(`car: unknown query ${name}`);
3432
3807
  },
@@ -3626,6 +4001,29 @@ var GameDestructionThree = (() => {
3626
4001
  }
3627
4002
  };
3628
4003
  }
4004
+ function createRainBehavior() {
4005
+ return {
4006
+ reset() {
4007
+ },
4008
+ act(host, action, args) {
4009
+ const { engine, camera: camera2 } = host;
4010
+ if (action === "init") {
4011
+ const { count, wind } = args;
4012
+ engine.rain.init(count, wind);
4013
+ } else if (action === "update") {
4014
+ const { dt, splash } = args;
4015
+ engine.rain.update(dt, splash, camera2.position);
4016
+ }
4017
+ },
4018
+ query(host, name) {
4019
+ if (name === "count") return host.engine.rain.count;
4020
+ if (name === "on") return host.engine.rain.on;
4021
+ throw new Error(`rain: unknown query ${name}`);
4022
+ },
4023
+ update() {
4024
+ }
4025
+ };
4026
+ }
3629
4027
  var WALKER_COUNT = 10;
3630
4028
  var ARRIVAL_DISTANCE = 0.05;
3631
4029
  var SWAY_AMPLITUDE = 0.04;
@@ -3691,7 +4089,8 @@ var GameDestructionThree = (() => {
3691
4089
  blocks: () => createBlocksBehavior(),
3692
4090
  plate: () => createPlateBehavior(),
3693
4091
  cylinder: () => createCylinderBehavior(),
3694
- car: () => createCarBehavior()
4092
+ car: () => createCarBehavior(),
4093
+ rain: () => createRainBehavior()
3695
4094
  };
3696
4095
  var NOOP = () => {
3697
4096
  };
@@ -3762,6 +4161,8 @@ var GameDestructionThree = (() => {
3762
4161
  return Math.abs(asNumber(first));
3763
4162
  case "sqrt":
3764
4163
  return Math.sqrt(asNumber(first));
4164
+ case "exp":
4165
+ return Math.exp(asNumber(first));
3765
4166
  case "sin":
3766
4167
  return Math.sin(asNumber(first));
3767
4168
  case "cos":
@@ -3880,7 +4281,7 @@ var GameDestructionThree = (() => {
3880
4281
  return evaluateCall(value.$, args.map((entry) => resolve(entry, context)));
3881
4282
  }
3882
4283
  }
3883
- var UP11 = [0, 1, 0];
4284
+ var UP12 = [0, 1, 0];
3884
4285
  var ZERO = [0, 0, 0];
3885
4286
  var FULL_TURN_DRAWN = 6.283;
3886
4287
  function scaledSizes(range, factor) {
@@ -3893,7 +4294,7 @@ var GameDestructionThree = (() => {
3893
4294
  p: centre,
3894
4295
  r: radius,
3895
4296
  flat: true,
3896
- dir: options.dir ?? UP11,
4297
+ dir: options.dir ?? UP12,
3897
4298
  cone: 0.35,
3898
4299
  sp: [0.4, 1.4],
3899
4300
  v: wind,
@@ -3910,7 +4311,7 @@ var GameDestructionThree = (() => {
3910
4311
  p: centre,
3911
4312
  r: radius * 0.6,
3912
4313
  flat: true,
3913
- dir: UP11,
4314
+ dir: UP12,
3914
4315
  cone: 0.3,
3915
4316
  sp: [0.3, 0.8],
3916
4317
  life: [0.2, 0.4],
@@ -3925,14 +4326,14 @@ var GameDestructionThree = (() => {
3925
4326
  if (engine.quality < 2) {
3926
4327
  engine.emit(`${key}e`, "streak", 10 * factor, dt, () => {
3927
4328
  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 };
4329
+ 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
4330
  });
3930
4331
  }
3931
4332
  if (options.noSmoke) return;
3932
4333
  engine.emit(`${key}s`, "normal", (options.smoke || 14) * factor, dt, () => ({
3933
4334
  p: plus(centre, 0, 0.8 * sqrtFactor, 0),
3934
4335
  r: radius * 0.7,
3935
- dir: UP11,
4336
+ dir: UP12,
3936
4337
  cone: 0.35,
3937
4338
  sp: [0.6, 1.4],
3938
4339
  v: wind,
@@ -3972,15 +4373,15 @@ var GameDestructionThree = (() => {
3972
4373
  engine.screen.shake += 0.7 * scale;
3973
4374
  engine.screen.white += 0.18 * scale;
3974
4375
  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 });
4376
+ 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
4377
  engine.ring([plus(point, 0, 0.05, 0), 0.3, 7 * scale, 0.55, 16767136]);
3977
4378
  const toCamera = normalized([cameraPosition[0] - point[0], cameraPosition[1] - point[1], cameraPosition[2] - point[2]]);
3978
4379
  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 });
4380
+ 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
4381
  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 });
4382
+ 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
4383
  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 });
4384
+ 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
4385
  }
3985
4386
  engine.decal([point[0], engine.context.groundAt(point[0], point[2]), point[2], 3 * scale, 657930, { grow: 0.15, life: 60 }]);
3986
4387
  }
@@ -4123,6 +4524,7 @@ var GameDestructionThree = (() => {
4123
4524
  if (!def) return;
4124
4525
  this.schedule.clear();
4125
4526
  this.variables.clear();
4527
+ for (const [name, value] of Object.entries(this.setup.values ?? {})) this.variables.set(name, value);
4126
4528
  for (const behavior2 of this.deps.behaviors()) behavior2.reset(this.host);
4127
4529
  this.time = 0;
4128
4530
  this.within(() => {
@@ -4181,6 +4583,10 @@ var GameDestructionThree = (() => {
4181
4583
  return engine.spawn(step.system, this.value(step.spawn));
4182
4584
  case "emit":
4183
4585
  return engine.emit(step.key, step.system, this.value(step.rate), this.dt, () => this.value(step.spawn));
4586
+ case "mesh": {
4587
+ engine.mesh(this.value(step.spawn));
4588
+ return;
4589
+ }
4184
4590
  case "debris":
4185
4591
  return engine.debrisPiece(this.value(step.spawn));
4186
4592
  case "decal":
@@ -4262,7 +4668,7 @@ var GameDestructionThree = (() => {
4262
4668
  this.index = previous;
4263
4669
  }
4264
4670
  };
4265
- var ORIGIN4 = [0, 0, 0];
4671
+ var ORIGIN5 = [0, 0, 0];
4266
4672
  var DEFAULT_HEADING = [0, 0, -1];
4267
4673
  var DEFAULT_MAX_INSTANCES = 48;
4268
4674
  var ENDLESS_SECONDS = 60;
@@ -4307,7 +4713,7 @@ var GameDestructionThree = (() => {
4307
4713
  }
4308
4714
  function placementOf(entry) {
4309
4715
  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 };
4716
+ return { origin, anchor: entry.anchor, yaw: entry.yaw, ...entry.orientation ? { orientation: entry.orientation } : {} };
4311
4717
  }
4312
4718
  function drawing(entry, action) {
4313
4719
  const context = engine.context;
@@ -4331,8 +4737,9 @@ var GameDestructionThree = (() => {
4331
4737
  random: playOptions.seed !== void 0 ? streamOf(playOptions.seed) : void 0,
4332
4738
  follow: playOptions.follow,
4333
4739
  origin: point,
4334
- anchor: def.anchor ?? ORIGIN4,
4740
+ anchor: def.anchor ?? ORIGIN5,
4335
4741
  yaw: yawOf(heading, playOptions.direction),
4742
+ orientation: playOptions.orientation,
4336
4743
  seconds: playOptions.seconds ?? (def.loop && def.loop > 0 ? def.loop : ENDLESS_SECONDS)
4337
4744
  };
4338
4745
  playing.push(entry);
@@ -4341,7 +4748,8 @@ var GameDestructionThree = (() => {
4341
4748
  () => instance.begin(def, paramIndex(def, playOptions.param), {
4342
4749
  placement: placementOf(entry),
4343
4750
  ...playOptions.direction ? { direction: playOptions.direction } : {},
4344
- ...playOptions.normal ? { normal: playOptions.normal } : {}
4751
+ ...playOptions.normal ? { normal: playOptions.normal } : {},
4752
+ ...playOptions.values ? { values: playOptions.values } : {}
4345
4753
  })
4346
4754
  );
4347
4755
  return entry.handle;
@@ -4601,15 +5009,15 @@ var GameDestructionThree = (() => {
4601
5009
  return shock;
4602
5010
  }
4603
5011
  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;
5012
+ for (const puff3 of puffs) {
5013
+ if (puff3.ring || puff3.t > PUSH_AGE_LIMIT) continue;
5014
+ const dx = puff3.x - shock.x;
5015
+ const dz = puff3.z - shock.z;
4608
5016
  const distance = Math.hypot(dx, dz) + 1e-3;
4609
- if (Math.abs(distance - reach) < PUSH_BAND && puff2.y < PUSH_CEILING) {
5017
+ if (Math.abs(distance - reach) < PUSH_BAND && puff3.y < PUSH_CEILING) {
4610
5018
  const strength = 4 * dt * (1 - age);
4611
- puff2.vx += dx / distance * strength * 6;
4612
- puff2.vz += dz / distance * strength * 6;
5019
+ puff3.vx += dx / distance * strength * 6;
5020
+ puff3.vz += dz / distance * strength * 6;
4613
5021
  }
4614
5022
  }
4615
5023
  }
@@ -4909,10 +5317,10 @@ var GameDestructionThree = (() => {
4909
5317
  const life = random.between(ring2.life * 0.75, ring2.life);
4910
5318
  const h0 = random.between(1, 1.25);
4911
5319
  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;
5320
+ 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 });
5321
+ puff3.ring = ring2;
5322
+ puff3.ph = random.next() * Math.PI * 2;
5323
+ puff3.th = random.next() * Math.PI * 2;
4916
5324
  }
4917
5325
  const stemCount = Math.round((spec.stem || 18) * Math.max(lod, 0.4));
4918
5326
  for (let index = 0; index < stemCount; index++) {
@@ -5713,7 +6121,7 @@ var GameDestructionThree = (() => {
5713
6121
  };
5714
6122
  var TWO_PI_ROUNDED = 6.2832;
5715
6123
  var WIDE_CONE = 3.1;
5716
- var UP12 = [0, 1, 0];
6124
+ var UP13 = [0, 1, 0];
5717
6125
  function drawRange(random, range) {
5718
6126
  if (Array.isArray(range)) return random.between(range[0], range[1]);
5719
6127
  return range || 0;
@@ -5741,7 +6149,7 @@ var GameDestructionThree = (() => {
5741
6149
  }
5742
6150
  function coneDirection(random, direction, cone, out) {
5743
6151
  if (cone >= WIDE_CONE) return randomUnit(random, out);
5744
- const [dx, dy, dz] = direction ?? UP12;
6152
+ const [dx, dy, dz] = direction ?? UP13;
5745
6153
  const cosTilt = 1 - random.next() * (1 - Math.cos(cone));
5746
6154
  const sinTilt = Math.sqrt(1 - cosTilt * cosTilt);
5747
6155
  const phase = random.next() * TWO_PI_ROUNDED;
@@ -5861,12 +6269,12 @@ var GameDestructionThree = (() => {
5861
6269
  }
5862
6270
  };
5863
6271
  var CAPACITY2 = 400;
5864
- var UP13 = [0, 1, 0];
6272
+ var UP14 = [0, 1, 0];
5865
6273
  var DEFAULT_LIFE2 = 60;
5866
6274
  var DEFAULT_GROW = 1.2;
5867
6275
  var EVAPORATION = { fuel: [6, 5], water: [40, 20] };
5868
6276
  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 };
6277
+ 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
6278
  }
5871
6279
  var DecalPool = class {
5872
6280
  constructor(context) {
@@ -5879,7 +6287,7 @@ var GameDestructionThree = (() => {
5879
6287
  const random = this.context.random;
5880
6288
  const index = this.next++ % CAPACITY2;
5881
6289
  const decal2 = this.decals[index];
5882
- const normal = options.n ?? UP13;
6290
+ const normal = options.n ?? UP14;
5883
6291
  random.between(0, 6.28);
5884
6292
  const offset = 4e-3 + index % 40 * 4e-4;
5885
6293
  decal2.on = true;
@@ -5920,6 +6328,94 @@ var GameDestructionThree = (() => {
5920
6328
  }
5921
6329
  }
5922
6330
  };
6331
+ var CAPACITY3 = 400;
6332
+ var IDENTITY = [0, 0, 0, 1];
6333
+ function multiply(a, b) {
6334
+ const [ax, ay, az, aw] = a;
6335
+ const [bx, by, bz, bw] = b;
6336
+ 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];
6337
+ }
6338
+ var aboutX = (angle) => [Math.sin(angle / 2), 0, 0, Math.cos(angle / 2)];
6339
+ var MeshPool = class {
6340
+ constructor(capacity = CAPACITY3) {
6341
+ __publicField2(this, "capacity", capacity);
6342
+ __publicField2(this, "particles", []);
6343
+ __publicField2(this, "serial", 0);
6344
+ }
6345
+ get count() {
6346
+ return this.particles.length;
6347
+ }
6348
+ spawn(options, frame = IDENTITY) {
6349
+ const velocity = options.v ?? [0, 0, 0];
6350
+ const world = options.wv ?? [0, 0, 0];
6351
+ const particle = {
6352
+ id: ++this.serial,
6353
+ shape: options.shape,
6354
+ radius: options.r,
6355
+ height: options.h ?? 0,
6356
+ segments: options.seg ?? 0,
6357
+ segments2: options.seg2 ?? 0,
6358
+ color: options.c0,
6359
+ additive: options.add === true,
6360
+ lit: options.lit === true,
6361
+ x: options.p[0],
6362
+ y: options.p[1],
6363
+ z: options.p[2],
6364
+ frame: options.roll ? multiply(frame, aboutX(options.roll)) : frame,
6365
+ at: options.at ?? [0, 0, 0],
6366
+ rx: options.rot?.[0] ?? 0,
6367
+ ry: options.rot?.[1] ?? 0,
6368
+ rz: options.rot?.[2] ?? 0,
6369
+ scale: 1,
6370
+ opacity: options.a,
6371
+ age: 0,
6372
+ life: options.life,
6373
+ vx: velocity[0] + world[0],
6374
+ vy: velocity[1] + world[1],
6375
+ vz: velocity[2] + world[2],
6376
+ spin: options.spin ?? [0, 0, 0],
6377
+ gravity: options.g ?? 0,
6378
+ drag: options.drag ?? 0,
6379
+ grow: options.grow ?? 0,
6380
+ ramp: options.ramp,
6381
+ fade: options.fade === true,
6382
+ startOpacity: options.a
6383
+ };
6384
+ this.particles.push(particle);
6385
+ if (this.particles.length > this.capacity) this.particles.shift();
6386
+ return particle;
6387
+ }
6388
+ update(dt) {
6389
+ const live = this.particles;
6390
+ for (let index = 0; index < live.length; index++) {
6391
+ const particle = live[index];
6392
+ particle.age += dt;
6393
+ if (particle.ramp ? particle.age >= particle.life : particle.age > particle.life) {
6394
+ live.splice(index--, 1);
6395
+ continue;
6396
+ }
6397
+ if (particle.fade) particle.opacity = Math.max(0, particle.startOpacity * (1 - particle.age / particle.life));
6398
+ particle.vy -= particle.gravity * dt;
6399
+ if (particle.drag) {
6400
+ const keep = Math.max(0, 1 - particle.drag * dt);
6401
+ particle.vx *= keep;
6402
+ particle.vy *= keep;
6403
+ particle.vz *= keep;
6404
+ }
6405
+ particle.x += particle.vx * dt;
6406
+ particle.y += particle.vy * dt;
6407
+ particle.z += particle.vz * dt;
6408
+ particle.rx += particle.spin[0] * dt;
6409
+ particle.ry += particle.spin[1] * dt;
6410
+ particle.rz += particle.spin[2] * dt;
6411
+ if (particle.grow) particle.scale *= 1 + particle.grow * dt;
6412
+ if (particle.ramp) particle.scale = particle.ramp[0] + Math.min(1, particle.age / particle.life) * particle.ramp[1];
6413
+ }
6414
+ }
6415
+ clear() {
6416
+ this.particles.length = 0;
6417
+ }
6418
+ };
5923
6419
  var DEFAULT_FADE_IN = 0.1;
5924
6420
  var FLOATS_PER_VECTOR = 3;
5925
6421
  var BYTE_MAX2 = 255;
@@ -6209,11 +6705,11 @@ var GameDestructionThree = (() => {
6209
6705
  };
6210
6706
  var RING_CAPACITY = 120;
6211
6707
  var LIGHT_CAPACITY = 4;
6212
- var UP14 = [0, 1, 0];
6708
+ var UP15 = [0, 1, 0];
6213
6709
  var DEFAULT_LIGHT_DISTANCE = 14;
6214
6710
  var WaveRingPool = class {
6215
6711
  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 })));
6712
+ __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
6713
  __publicField2(this, "count", 0);
6218
6714
  __publicField2(this, "next", 0);
6219
6715
  }
@@ -6226,7 +6722,7 @@ var GameDestructionThree = (() => {
6226
6722
  ring2.t = 0;
6227
6723
  ring2.duration = duration;
6228
6724
  ring2.color = color;
6229
- ring2.normal = normal ?? UP14;
6725
+ ring2.normal = normal ?? UP15;
6230
6726
  ring2.hold = hold || 0;
6231
6727
  }
6232
6728
  clear() {
@@ -6246,7 +6742,7 @@ var GameDestructionThree = (() => {
6246
6742
  var FlashLightPool = class {
6247
6743
  constructor(context) {
6248
6744
  __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 })));
6745
+ __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
6746
  __publicField2(this, "fire", [
6251
6747
  { x: 0, y: 0, z: 0, intensity: 0 },
6252
6748
  { x: 0, y: 0, z: 0, intensity: 0 }
@@ -6256,7 +6752,7 @@ var GameDestructionThree = (() => {
6256
6752
  get lit() {
6257
6753
  return this.lights.filter((light) => light.intensity > 0).length;
6258
6754
  }
6259
- flash(point, color, intensity, duration, distance, force) {
6755
+ flash(point, color, intensity, duration, distance, force, steady) {
6260
6756
  const { level } = this.context.quality;
6261
6757
  if (level >= 1 && !force) return;
6262
6758
  if (level >= 2 && force !== "boom") return;
@@ -6269,6 +6765,7 @@ var GameDestructionThree = (() => {
6269
6765
  light.peak = intensity;
6270
6766
  light.duration = duration;
6271
6767
  light.t = 0;
6768
+ light.steady = steady === true;
6272
6769
  light.intensity = intensity;
6273
6770
  }
6274
6771
  clear() {
@@ -6280,7 +6777,7 @@ var GameDestructionThree = (() => {
6280
6777
  if (light.intensity <= 0) continue;
6281
6778
  light.t += dt;
6282
6779
  const age = light.t / light.duration;
6283
- light.intensity = age >= 1 ? 0 : light.peak * (1 - age) * (1 - age);
6780
+ light.intensity = age >= 1 ? 0 : light.steady ? light.peak : light.peak * (1 - age) * (1 - age);
6284
6781
  }
6285
6782
  }
6286
6783
  };
@@ -6288,6 +6785,14 @@ var GameDestructionThree = (() => {
6288
6785
  var FIRE_FLICKER_SWAY = 0.25;
6289
6786
  var FIRE_FLICKER_JITTER = 0.08;
6290
6787
  var LENS_FADE = 0.16;
6788
+ function rotate(q, v) {
6789
+ const [qx, qy, qz, qw] = q;
6790
+ const ix = qw * v[0] + qy * v[2] - qz * v[1];
6791
+ const iy = qw * v[1] + qz * v[0] - qx * v[2];
6792
+ const iz = qw * v[2] + qx * v[1] - qy * v[0];
6793
+ const iw = -qx * v[0] - qy * v[1] - qz * v[2];
6794
+ 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];
6795
+ }
6291
6796
  function workshopGround(x, z) {
6292
6797
  return z < -2.9 && z > -4.5 && Math.abs(x) < 13 ? 0.12 : 0;
6293
6798
  }
@@ -6301,6 +6806,8 @@ var GameDestructionThree = (() => {
6301
6806
  __publicField2(this, "decals");
6302
6807
  __publicField2(this, "rings", new WaveRingPool());
6303
6808
  __publicField2(this, "lights");
6809
+ __publicField2(this, "rain", new RainField(this));
6810
+ __publicField2(this, "meshes", new MeshPool());
6304
6811
  __publicField2(this, "screen", createScreenState());
6305
6812
  __publicField2(this, "lens", []);
6306
6813
  __publicField2(this, "post", {});
@@ -6333,7 +6840,11 @@ var GameDestructionThree = (() => {
6333
6840
  place(point) {
6334
6841
  const placement = this.placement;
6335
6842
  if (!placement) return point;
6336
- const { anchor, origin, yaw } = placement;
6843
+ const { anchor, origin, yaw, orientation } = placement;
6844
+ if (orientation) {
6845
+ const turned = rotate(orientation, [point[0] - anchor[0], point[1] - anchor[1], point[2] - anchor[2]]);
6846
+ return [origin[0] + turned[0], origin[1] + turned[1], origin[2] + turned[2]];
6847
+ }
6337
6848
  const x = point[0] - anchor[0];
6338
6849
  const z = point[2] - anchor[2];
6339
6850
  const cos2 = Math.cos(yaw);
@@ -6344,6 +6855,7 @@ var GameDestructionThree = (() => {
6344
6855
  turn(vector) {
6345
6856
  const placement = this.placement;
6346
6857
  if (!placement) return vector;
6858
+ if (placement.orientation) return rotate(placement.orientation, vector);
6347
6859
  const cos2 = Math.cos(placement.yaw);
6348
6860
  const sin2 = Math.sin(placement.yaw);
6349
6861
  return [cos2 * vector[0] + sin2 * vector[2], vector[1], -sin2 * vector[0] + cos2 * vector[2]];
@@ -6372,6 +6884,14 @@ var GameDestructionThree = (() => {
6372
6884
  }
6373
6885
  this.accumulators.set(key, carried);
6374
6886
  }
6887
+ /** 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. */
6888
+ mesh(recipe) {
6889
+ const placement = this.placement;
6890
+ const options = placement ? { ...recipe, p: this.place(recipe.p), ...recipe.v ? { v: this.turn(recipe.v) } : {} } : recipe;
6891
+ this.context.trace?.("mesh", [options]);
6892
+ const grouped = recipe.at !== void 0 || recipe.roll !== void 0;
6893
+ return this.meshes.spawn(options, placement && grouped ? placement.orientation ?? [0, Math.sin(placement.yaw / 2), 0, Math.cos(placement.yaw / 2)] : void 0);
6894
+ }
6375
6895
  debrisPiece(recipe) {
6376
6896
  const options = this.placed(recipe);
6377
6897
  this.context.trace?.("debris", [options]);
@@ -6392,10 +6912,11 @@ var GameDestructionThree = (() => {
6392
6912
  this.context.trace?.("ring", args);
6393
6913
  this.rings.add(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
6394
6914
  }
6915
+ /** `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
6916
  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]);
6917
+ const args = this.placement ? [this.place(recipe[0]), recipe[1], recipe[2], recipe[3], recipe[4], recipe[5], recipe[6]] : recipe;
6918
+ this.context.trace?.("flash", args.slice(0, 6));
6919
+ this.lights.flash(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
6399
6920
  }
6400
6921
  /** `fireLight` (W400): sways with the clock and flickers with one draw. */
6401
6922
  fireLight(slot, recipe, intensity, time) {
@@ -6420,6 +6941,7 @@ var GameDestructionThree = (() => {
6420
6941
  this.debris.update(dt);
6421
6942
  this.decals.update(dt);
6422
6943
  this.rings.update(dt);
6944
+ this.meshes.update(dt);
6423
6945
  this.lights.update(dt);
6424
6946
  updateScreen(this.screen, dt);
6425
6947
  for (let index = this.lens.length - 1; index >= 0; index--) {
@@ -6436,7 +6958,9 @@ var GameDestructionThree = (() => {
6436
6958
  this.debris.clear();
6437
6959
  this.decals.clear();
6438
6960
  this.rings.clear();
6961
+ this.meshes.clear();
6439
6962
  this.lights.clear();
6963
+ this.rain.clear();
6440
6964
  this.accumulators.clear();
6441
6965
  resetScreenState(this.screen);
6442
6966
  this.lens.length = 0;
@@ -6582,16 +7106,16 @@ var GameDestructionThree = (() => {
6582
7106
  },
6583
7107
  push(point, radius, speed) {
6584
7108
  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];
7109
+ for (const puff3 of core.puffs.items) {
7110
+ const dx = puff3.x - point[0];
7111
+ const dy = puff3.y - point[1];
7112
+ const dz = puff3.z - point[2];
6589
7113
  const distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
6590
7114
  if (distance >= radius) continue;
6591
7115
  const strength = speed * (1 - distance / radius) / (distance + 1e-3);
6592
- puff2.vx += dx * strength;
6593
- puff2.vy += dy * strength;
6594
- puff2.vz += dz * strength;
7116
+ puff3.vx += dx * strength;
7117
+ puff3.vy += dy * strength;
7118
+ puff3.vz += dz * strength;
6595
7119
  }
6596
7120
  },
6597
7121
  stop(handle) {
@@ -6608,6 +7132,12 @@ var GameDestructionThree = (() => {
6608
7132
  camera2.y = position[1];
6609
7133
  camera2.z = position[2];
6610
7134
  },
7135
+ setWind(next) {
7136
+ wind.x = next[0];
7137
+ wind.y = next[1];
7138
+ wind.z = next[2];
7139
+ },
7140
+ wind: () => [wind.x, wind.y, wind.z],
6611
7141
  forceLod(scale) {
6612
7142
  core.clock.forcedLod = scale;
6613
7143
  },
@@ -6805,11 +7335,11 @@ void main(){
6805
7335
  geometry.setAttribute("aC", attributes[2]);
6806
7336
  geometry.instanceCount = 0;
6807
7337
  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;
7338
+ const mesh2 = new THREE.Mesh(geometry, material);
7339
+ mesh2.name = "vfx-puffs";
7340
+ mesh2.frustumCulled = false;
6811
7341
  return {
6812
- mesh,
7342
+ mesh: mesh2,
6813
7343
  sync() {
6814
7344
  let n = 0;
6815
7345
  for (const p of pool.items) {
@@ -6850,12 +7380,12 @@ void main(){
6850
7380
  geometry.setAttribute("aE", attributes[2]);
6851
7381
  geometry.instanceCount = 0;
6852
7382
  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;
7383
+ const mesh2 = new THREE.Mesh(geometry, material);
7384
+ mesh2.name = "vfx-sparks";
7385
+ mesh2.frustumCulled = false;
7386
+ mesh2.renderOrder = 5;
6857
7387
  return {
6858
- mesh,
7388
+ mesh: mesh2,
6859
7389
  sync() {
6860
7390
  let n = 0;
6861
7391
  for (const s of pool.items) {
@@ -6893,12 +7423,12 @@ void main(){
6893
7423
  geometry.setAttribute("aC", attributes[1]);
6894
7424
  geometry.instanceCount = 0;
6895
7425
  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;
7426
+ const mesh2 = new THREE.Mesh(geometry, material);
7427
+ mesh2.name = "vfx-glows";
7428
+ mesh2.frustumCulled = false;
7429
+ mesh2.renderOrder = 6;
6900
7430
  return {
6901
- mesh,
7431
+ mesh: mesh2,
6902
7432
  sync() {
6903
7433
  let n = 0;
6904
7434
  for (const g of pool.items) {
@@ -6927,14 +7457,14 @@ void main(){
6927
7457
  const rockMaterial = new THREE.MeshStandardMaterial({ color: 16777215, roughness: 0.85, flatShading: true });
6928
7458
  const plateMaterial = rockMaterial.clone();
6929
7459
  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;
7460
+ const mesh2 = new THREE.InstancedMesh(geometry, material, capacity);
7461
+ mesh2.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
7462
+ mesh2.castShadow = true;
7463
+ mesh2.receiveShadow = true;
7464
+ mesh2.frustumCulled = false;
7465
+ mesh2.count = 0;
7466
+ mesh2.setColorAt(0, new THREE.Color(1, 1, 1));
7467
+ return mesh2;
6938
7468
  };
6939
7469
  const rocks = make(shapes.rock, rockMaterial);
6940
7470
  const plates = make(shapes.plate, plateMaterial);
@@ -6953,20 +7483,20 @@ void main(){
6953
7483
  euler.set(chunk.rx, chunk.ry, chunk.rz);
6954
7484
  quaternion.setFromEuler(euler);
6955
7485
  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;
7486
+ const mesh2 = chunk.plate ? plates : rocks;
6957
7487
  const index = chunk.plate ? plateCount++ : rockCount++;
6958
- mesh.setMatrixAt(index, matrix);
7488
+ mesh2.setMatrixAt(index, matrix);
6959
7489
  const color = colorOf2(chunk);
6960
- const colors = mesh.instanceColor.array;
7490
+ const colors = mesh2.instanceColor.array;
6961
7491
  colors[index * 3] = color[0];
6962
7492
  colors[index * 3 + 1] = color[1];
6963
7493
  colors[index * 3 + 2] = color[2];
6964
7494
  }
6965
7495
  rocks.count = rockCount;
6966
7496
  plates.count = plateCount;
6967
- for (const mesh of [rocks, plates]) {
6968
- mesh.instanceMatrix.needsUpdate = true;
6969
- mesh.instanceColor.needsUpdate = true;
7497
+ for (const mesh2 of [rocks, plates]) {
7498
+ mesh2.instanceMatrix.needsUpdate = true;
7499
+ mesh2.instanceColor.needsUpdate = true;
6970
7500
  }
6971
7501
  return rockCount + plateCount;
6972
7502
  },
@@ -7150,28 +7680,28 @@ void main(){
7150
7680
  position.setXYZ(index, worldX, (options.ground?.heightAt(worldX, worldZ) ?? 0) + mark.lift, worldZ);
7151
7681
  }
7152
7682
  geometry.computeVertexNormals();
7153
- const mesh = new THREE.Mesh(geometry, material);
7154
- mesh.receiveShadow = true;
7155
- return mesh;
7683
+ const mesh2 = new THREE.Mesh(geometry, material);
7684
+ mesh2.receiveShadow = true;
7685
+ return mesh2;
7156
7686
  }
7157
7687
  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;
7688
+ const mesh2 = new THREE.Mesh(mark.plate ? options.plateGeometry : options.rockGeometry, rockMaterial(mark.color));
7689
+ mesh2.position.set(mark.x, mark.y, mark.z);
7690
+ mesh2.scale.set(mark.scale[0], mark.scale[1], mark.scale[2]);
7691
+ mesh2.rotation.set(mark.rotation[0], mark.rotation[1], mark.rotation[2]);
7692
+ mesh2.castShadow = true;
7693
+ mesh2.receiveShadow = true;
7694
+ return mesh2;
7165
7695
  }
7166
7696
  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;
7697
+ const mesh2 = new THREE.Mesh(bloodGeometry, bloodMaterial);
7698
+ mesh2.position.set(mark.x, mark.y, mark.z);
7699
+ mesh2.scale.set(mark.scaleX, 1, mark.scaleZ);
7700
+ mesh2.rotation.y = mark.rotation;
7701
+ mesh2.receiveShadow = true;
7702
+ mesh2.scale.multiplyScalar(0.05);
7703
+ blood.push({ mesh: mesh2, scaleX: mark.scaleX, scaleZ: mark.scaleZ, age: 0 });
7704
+ return mesh2;
7175
7705
  }
7176
7706
  function build(mark) {
7177
7707
  if (mark.kind === "scorch") return conform(mark, scorchMaterials[mark.variant]);
@@ -7382,6 +7912,73 @@ void main() {
7382
7912
  }
7383
7913
  };
7384
7914
  }
7915
+ var SPHERE_DEFAULT_WIDTH = 8;
7916
+ var SPHERE_DEFAULT_HEIGHT = 6;
7917
+ function geometryOf(THREE, particle) {
7918
+ switch (particle.shape) {
7919
+ case "cone":
7920
+ return new THREE.ConeGeometry(particle.radius, particle.height, particle.segments || void 0, particle.segments2 || 1, false);
7921
+ case "cone_open":
7922
+ return new THREE.ConeGeometry(particle.radius, particle.height, particle.segments || void 0, particle.segments2 || 1, true);
7923
+ case "sphere":
7924
+ return new THREE.SphereGeometry(particle.radius, particle.segments || SPHERE_DEFAULT_WIDTH, particle.segments2 || SPHERE_DEFAULT_HEIGHT);
7925
+ case "icosahedron":
7926
+ return new THREE.IcosahedronGeometry(particle.radius, particle.segments);
7927
+ }
7928
+ }
7929
+ function materialOf(THREE, particle) {
7930
+ if (particle.lit) return new THREE.MeshStandardMaterial({ color: particle.color, roughness: 1, metalness: 0, transparent: true, opacity: particle.opacity, depthWrite: false });
7931
+ return new THREE.MeshBasicMaterial({ color: particle.color, transparent: true, opacity: particle.opacity, depthWrite: false, ...particle.additive ? { blending: THREE.AdditiveBlending } : {} });
7932
+ }
7933
+ function createMeshLayer(THREE, pool) {
7934
+ const group = new THREE.Group();
7935
+ group.name = "workshop-meshes";
7936
+ const drawn = /* @__PURE__ */ new Map();
7937
+ const frame = new THREE.Quaternion();
7938
+ const euler = new THREE.Euler();
7939
+ const turn = new THREE.Quaternion();
7940
+ const offset = new THREE.Vector3();
7941
+ function release(mesh2) {
7942
+ mesh2.removeFromParent();
7943
+ mesh2.geometry.dispose();
7944
+ mesh2.material.dispose();
7945
+ }
7946
+ return {
7947
+ group,
7948
+ sync() {
7949
+ const live = /* @__PURE__ */ new Set();
7950
+ for (const particle of pool.particles) {
7951
+ live.add(particle.id);
7952
+ let mesh2 = drawn.get(particle.id);
7953
+ if (!mesh2) {
7954
+ mesh2 = new THREE.Mesh(geometryOf(THREE, particle), materialOf(THREE, particle));
7955
+ mesh2.frustumCulled = false;
7956
+ mesh2.renderOrder = particle.additive ? 5 : 3;
7957
+ drawn.set(particle.id, mesh2);
7958
+ group.add(mesh2);
7959
+ }
7960
+ frame.set(particle.frame[0], particle.frame[1], particle.frame[2], particle.frame[3]);
7961
+ offset.set(particle.at[0], particle.at[1], particle.at[2]).multiplyScalar(particle.scale).applyQuaternion(frame);
7962
+ mesh2.position.set(particle.x + offset.x, particle.y + offset.y, particle.z + offset.z);
7963
+ turn.setFromEuler(euler.set(particle.rx, particle.ry, particle.rz));
7964
+ mesh2.quaternion.copy(frame).multiply(turn);
7965
+ mesh2.scale.setScalar(particle.scale);
7966
+ mesh2.material.opacity = particle.opacity;
7967
+ }
7968
+ for (const [id, mesh2] of drawn) {
7969
+ if (live.has(id)) continue;
7970
+ release(mesh2);
7971
+ drawn.delete(id);
7972
+ }
7973
+ return pool.count;
7974
+ },
7975
+ dispose() {
7976
+ for (const mesh2 of drawn.values()) release(mesh2);
7977
+ drawn.clear();
7978
+ group.removeFromParent();
7979
+ }
7980
+ };
7981
+ }
7385
7982
  var WORKSHOP_SPRITE_VERTEX = `attribute float size;attribute float alpha;attribute vec3 pcol;varying vec3 vC;varying float vA;uniform float uScale;
7386
7983
  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
7984
  var WORKSHOP_SPRITE_FRAGMENT = `uniform float uSoft;varying vec3 vC;varying float vA;
@@ -7457,13 +8054,37 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7457
8054
  }
7458
8055
  };
7459
8056
  }
8057
+ function rainLayer(THREE, rain) {
8058
+ const geometry = new THREE.BufferGeometry();
8059
+ const position = new THREE.BufferAttribute(rain.positions, 3).setUsage(THREE.DynamicDrawUsage);
8060
+ geometry.setAttribute("position", position);
8061
+ geometry.setDrawRange(0, 0);
8062
+ const material = new THREE.LineBasicMaterial({ color: 10466255, transparent: true, opacity: 0.35, depthWrite: false });
8063
+ const lines = new THREE.LineSegments(geometry, material);
8064
+ lines.frustumCulled = false;
8065
+ lines.name = "workshop-rain";
8066
+ return {
8067
+ object: lines,
8068
+ sync() {
8069
+ const drawn = rain.on && rain.count > 0;
8070
+ lines.visible = drawn;
8071
+ geometry.setDrawRange(0, drawn ? rain.count * 2 : 0);
8072
+ position.needsUpdate = true;
8073
+ return drawn;
8074
+ },
8075
+ dispose() {
8076
+ geometry.dispose();
8077
+ material.dispose();
8078
+ }
8079
+ };
8080
+ }
7460
8081
  function debrisLayer(THREE, pool) {
7461
8082
  const geometry = new THREE.BoxGeometry(1, 1, 1);
7462
8083
  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";
8084
+ const mesh2 = new THREE.InstancedMesh(geometry, material, DEBRIS_CAPACITY);
8085
+ mesh2.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
8086
+ mesh2.frustumCulled = false;
8087
+ mesh2.name = "workshop-debris";
7467
8088
  const euler = new THREE.Euler();
7468
8089
  const quaternion = new THREE.Quaternion();
7469
8090
  const position = new THREE.Vector3();
@@ -7471,16 +8092,16 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7471
8092
  const matrix = new THREE.Matrix4();
7472
8093
  const colour = new THREE.Color();
7473
8094
  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));
8095
+ for (let index = 0; index < DEBRIS_CAPACITY; index++) mesh2.setMatrixAt(index, hidden);
8096
+ mesh2.setColorAt(0, colour.setHex(16777215));
7476
8097
  return {
7477
- object: mesh,
8098
+ object: mesh2,
7478
8099
  sync() {
7479
8100
  let drawn = false;
7480
8101
  for (let index = 0; index < DEBRIS_CAPACITY; index++) {
7481
8102
  const piece = pool.pieces[index];
7482
8103
  if (!piece.on) {
7483
- mesh.setMatrixAt(index, hidden);
8104
+ mesh2.setMatrixAt(index, hidden);
7484
8105
  continue;
7485
8106
  }
7486
8107
  drawn = true;
@@ -7488,11 +8109,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7488
8109
  euler.set(piece.rx, piece.ry, piece.rz);
7489
8110
  quaternion.setFromEuler(euler);
7490
8111
  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));
8112
+ mesh2.setMatrixAt(index, matrix);
8113
+ mesh2.setColorAt(index, colour.setHex(piece.color));
7493
8114
  }
7494
- mesh.instanceMatrix.needsUpdate = true;
7495
- if (mesh.instanceColor) mesh.instanceColor.needsUpdate = true;
8115
+ mesh2.instanceMatrix.needsUpdate = true;
8116
+ if (mesh2.instanceColor) mesh2.instanceColor.needsUpdate = true;
7496
8117
  return drawn;
7497
8118
  },
7498
8119
  dispose() {
@@ -7549,11 +8170,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7549
8170
  const dryMesh = new THREE.InstancedMesh(geometry, dry, DECAL_CAPACITY);
7550
8171
  const group = new THREE.Group();
7551
8172
  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);
8173
+ for (const mesh2 of [wetMesh, dryMesh]) {
8174
+ mesh2.frustumCulled = false;
8175
+ mesh2.renderOrder = 1;
8176
+ mesh2.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
8177
+ group.add(mesh2);
7557
8178
  }
7558
8179
  const hidden = new THREE.Matrix4().makeScale(0, 0, 0);
7559
8180
  const colour = new THREE.Color();
@@ -7590,13 +8211,13 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7590
8211
  quaternion.multiply(turn.setFromAxisAngle(forward, index * 2.399963));
7591
8212
  const offset = 4e-3 + index % 40 * 4e-4;
7592
8213
  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);
8214
+ const mesh2 = look.dried ? dryMesh : wetMesh;
8215
+ mesh2.setMatrixAt(index, matrix);
8216
+ mesh2.setColorAt(index, colour);
7596
8217
  }
7597
- for (const mesh of [wetMesh, dryMesh]) {
7598
- mesh.instanceMatrix.needsUpdate = true;
7599
- if (mesh.instanceColor) mesh.instanceColor.needsUpdate = true;
8218
+ for (const mesh2 of [wetMesh, dryMesh]) {
8219
+ mesh2.instanceMatrix.needsUpdate = true;
8220
+ if (mesh2.instanceColor) mesh2.instanceColor.needsUpdate = true;
7600
8221
  }
7601
8222
  return drawn;
7602
8223
  },
@@ -7610,10 +8231,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7610
8231
  function ringLayer(THREE, pool) {
7611
8232
  const geometry = new THREE.RingGeometry(0.86, 1, 40);
7612
8233
  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";
8234
+ const mesh2 = new THREE.InstancedMesh(geometry, material, RING_CAPACITY3);
8235
+ mesh2.frustumCulled = false;
8236
+ mesh2.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
8237
+ mesh2.name = "workshop-rings";
7617
8238
  const hidden = new THREE.Matrix4().makeScale(0, 0, 0);
7618
8239
  const colour = new THREE.Color();
7619
8240
  const normal = new THREE.Vector3();
@@ -7622,16 +8243,16 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7622
8243
  const position = new THREE.Vector3();
7623
8244
  const scale = new THREE.Vector3();
7624
8245
  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);
8246
+ mesh2.setColorAt(0, colour.setHex(0));
8247
+ for (let index = 0; index < RING_CAPACITY3; index++) mesh2.setMatrixAt(index, hidden);
7627
8248
  return {
7628
- object: mesh,
8249
+ object: mesh2,
7629
8250
  sync() {
7630
8251
  let drawn = false;
7631
8252
  for (let index = 0; index < RING_CAPACITY3; index++) {
7632
8253
  const ring2 = pool.rings[index];
7633
8254
  if (!ring2.on) {
7634
- mesh.setMatrixAt(index, hidden);
8255
+ mesh2.setMatrixAt(index, hidden);
7635
8256
  continue;
7636
8257
  }
7637
8258
  drawn = true;
@@ -7639,11 +8260,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7639
8260
  const radius = ring2.from + (ring2.to - ring2.from) * (1 - Math.pow(1 - k, 2));
7640
8261
  quaternion.setFromUnitVectors(forward, normal.set(ring2.normal[0], ring2.normal[1], ring2.normal[2]));
7641
8262
  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));
8263
+ mesh2.setMatrixAt(index, matrix);
8264
+ mesh2.setColorAt(index, colour.setHex(ring2.color).multiplyScalar(ring2.hold ? ring2.hold : 1 - k));
7644
8265
  }
7645
- mesh.instanceMatrix.needsUpdate = true;
7646
- if (mesh.instanceColor) mesh.instanceColor.needsUpdate = true;
8266
+ mesh2.instanceMatrix.needsUpdate = true;
8267
+ if (mesh2.instanceColor) mesh2.instanceColor.needsUpdate = true;
7647
8268
  return drawn;
7648
8269
  },
7649
8270
  dispose() {
@@ -7659,11 +8280,14 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7659
8280
  spriteLayer(THREE, engine.add, true, 1, pointScale),
7660
8281
  spriteLayer(THREE, engine.normal, false, 0.55, pointScale),
7661
8282
  streakLayer(THREE, engine.streaks),
8283
+ rainLayer(THREE, engine.rain),
7662
8284
  debrisLayer(THREE, engine.debris),
7663
8285
  decalLayer(THREE, engine.decals),
7664
8286
  ringLayer(THREE, engine.rings)
7665
8287
  ];
7666
8288
  for (const layer2 of layers) group.add(layer2.object);
8289
+ const meshes = createMeshLayer(THREE, engine.meshes);
8290
+ group.add(meshes.group);
7667
8291
  const flashLights = engine.lights.lights.map(() => new THREE.PointLight(16760944, 0, 14));
7668
8292
  const fireLights = engine.lights.fire.map(() => new THREE.PointLight(FIRE_LIGHT_COLOR, 0, FIRE_LIGHT_DISTANCE));
7669
8293
  for (const light of [...flashLights, ...fireLights]) group.add(light);
@@ -7672,6 +8296,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7672
8296
  sync() {
7673
8297
  let drawCalls = 0;
7674
8298
  for (const layer2 of layers) if (layer2.sync()) drawCalls++;
8299
+ drawCalls += meshes.sync();
7675
8300
  engine.lights.lights.forEach((source, index) => {
7676
8301
  const light = flashLights[index];
7677
8302
  light.position.set(source.x, source.y, source.z);
@@ -7688,6 +8313,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7688
8313
  },
7689
8314
  dispose() {
7690
8315
  for (const layer2 of layers) layer2.dispose();
8316
+ meshes.dispose();
7691
8317
  group.clear();
7692
8318
  }
7693
8319
  };
@@ -7803,8 +8429,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7803
8429
  return world.play(request.effect, request.point, {
7804
8430
  ...request.direction ? { direction: request.direction } : {},
7805
8431
  ...request.normal ? { normal: request.normal } : {},
8432
+ ...request.orientation ? { orientation: request.orientation } : {},
7806
8433
  ...request.seed !== void 0 ? { seed: request.seed } : {},
7807
- ...request.param !== void 0 ? { param: request.param } : {}
8434
+ ...request.param !== void 0 ? { param: request.param } : {},
8435
+ ...request.values ? { values: request.values } : {}
7808
8436
  });
7809
8437
  },
7810
8438
  stop: (handle) => world.stop(handle),
@@ -7965,11 +8593,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7965
8593
  * 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
8594
  * 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
8595
  */
7968
- addMarks(mesh, f) {
8596
+ addMarks(mesh2, f) {
7969
8597
  if (!f.marks?.length) return;
7970
8598
  this.markGeometry ?? (this.markGeometry = new this.THREE.CircleGeometry(1, 10));
7971
8599
  this.markMaterial ?? (this.markMaterial = new this.THREE.MeshBasicMaterial({ color: 723465, depthWrite: false, polygonOffset: true, polygonOffsetFactor: -4, polygonOffsetUnits: -4 }));
7972
- const sc = mesh.scale;
8600
+ const sc = mesh2.scale;
7973
8601
  for (const mark of f.marks) {
7974
8602
  const disc = new this.THREE.Mesh(this.markGeometry, this.markMaterial);
7975
8603
  const radius = Math.max(4e-3, mark.diameter) * 0.6;
@@ -7977,22 +8605,22 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
7977
8605
  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
8606
  disc.quaternion.setFromUnitVectors(this.w.set(0, 0, 1), n);
7979
8607
  disc.scale.set(radius / sc.x, radius / sc.y, radius / sc.z);
7980
- mesh.add(disc);
8608
+ mesh2.add(disc);
7981
8609
  }
7982
8610
  }
7983
8611
  /** Create the mesh of a fragment and start its motion. `maxAllowed` is the live budget. */
7984
8612
  spawn(f, material, home, maxAllowed) {
7985
8613
  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();
8614
+ const mesh2 = new this.THREE.Mesh(geo, material);
8615
+ mesh2.position.set(f.center[0], f.center[1], f.center[2]);
8616
+ mesh2.quaternion.set(f.rotation[0], f.rotation[1], f.rotation[2], f.rotation[3]).normalize();
7989
8617
  const sz = f.size;
7990
8618
  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);
8619
+ mesh2.scale.copy(sc);
8620
+ mesh2.castShadow = true;
8621
+ this.o.scene.add(mesh2);
7994
8622
  const live = {
7995
- mesh,
8623
+ mesh: mesh2,
7996
8624
  home,
7997
8625
  vel: new this.THREE.Vector3(f.velocity[0], f.velocity[1], f.velocity[2]),
7998
8626
  ang: new this.THREE.Vector3(f.angularVelocity[0], f.angularVelocity[1], f.angularVelocity[2]),
@@ -8013,8 +8641,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8013
8641
  alive--;
8014
8642
  }
8015
8643
  }
8016
- this.addMarks(mesh, f);
8017
- return mesh;
8644
+ this.addMarks(mesh2, f);
8645
+ return mesh2;
8018
8646
  }
8019
8647
  /** Add velocity to every fragment within `radius` of a point (blasts). */
8020
8648
  blast(point, radius, speed) {
@@ -8053,24 +8681,24 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8053
8681
  }
8054
8682
  }
8055
8683
  step(f, dt, g) {
8056
- const mesh = f.mesh;
8684
+ const mesh2 = f.mesh;
8057
8685
  f.vel.y -= g * dt;
8058
- mesh.position.addScaledVector(f.vel, dt);
8686
+ mesh2.position.addScaledVector(f.vel, dt);
8059
8687
  const w = f.ang.length();
8060
8688
  if (w > 1e-5) {
8061
8689
  this.dq.setFromAxisAngle(this.v.copy(f.ang).multiplyScalar(1 / w), w * dt);
8062
- mesh.quaternion.premultiply(this.dq).normalize();
8690
+ mesh2.quaternion.premultiply(this.dq).normalize();
8063
8691
  }
8064
- this.m.makeRotationFromQuaternion(mesh.quaternion);
8692
+ this.m.makeRotationFromQuaternion(mesh2.quaternion);
8065
8693
  const e = this.m.elements;
8066
8694
  const h = f.half;
8067
8695
  const sc = f.dying ? f.fade : 1;
8068
8696
  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);
8697
+ const gy = this.o.groundAt(mesh2.position.x, mesh2.position.z);
8070
8698
  let contact = false;
8071
- if (mesh.position.y - ext < gy) {
8699
+ if (mesh2.position.y - ext < gy) {
8072
8700
  contact = true;
8073
- mesh.position.y = gy + ext;
8701
+ mesh2.position.y = gy + ext;
8074
8702
  if (f.vel.y < -0.6) {
8075
8703
  f.vel.y = -f.vel.y * 0.25;
8076
8704
  f.vel.x *= 0.7;
@@ -8103,8 +8731,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8103
8731
  }
8104
8732
  /** Tip the fragment towards lying on its flattest face. */
8105
8733
  settle(f, dt) {
8106
- const mesh = f.mesh;
8107
- this.m.makeRotationFromQuaternion(mesh.quaternion);
8734
+ const mesh2 = f.mesh;
8735
+ this.m.makeRotationFromQuaternion(mesh2.quaternion);
8108
8736
  const e = this.m.elements;
8109
8737
  const up = [e[1], e[5], e[9]];
8110
8738
  const half = [f.half.x, f.half.y, f.half.z];
@@ -8119,8 +8747,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8119
8747
  this.v.set(e[axis2 * 4], e[axis2 * 4 + 1], e[axis2 * 4 + 2]).multiplyScalar(sign);
8120
8748
  this.w.set(0, 1, 0);
8121
8749
  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));
8750
+ this.q.copy(this.dq).multiply(mesh2.quaternion);
8751
+ mesh2.quaternion.slerp(this.q, clamp6(dt * 7, 0, 1));
8124
8752
  }
8125
8753
  /** Remove the fragments of one object (after `restore`). */
8126
8754
  removeHome(home) {
@@ -8192,13 +8820,13 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8192
8820
  }
8193
8821
  }
8194
8822
  while (this.spots.length >= this.cap) this.remove(this.spots[0]);
8195
- const mesh = new this.THREE.Mesh(this.geometry, this.material(kind, color));
8823
+ const mesh2 = new this.THREE.Mesh(this.geometry, this.material(kind, color));
8196
8824
  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) });
8825
+ mesh2.position.set(x, y, z);
8826
+ mesh2.scale.setScalar(1e-3);
8827
+ mesh2.renderOrder = 1;
8828
+ this.scene.add(mesh2);
8829
+ this.spots.push({ mesh: mesh2, kind, home, x, z, radius: 1e-3, target: Math.min(MAX_RADIUS, radius) });
8202
8830
  }
8203
8831
  remove(s) {
8204
8832
  const i = this.spots.indexOf(s);
@@ -8578,7 +9206,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8578
9206
  /** Bars drawn now, over every part. */
8579
9207
  get count() {
8580
9208
  let n = 0;
8581
- for (const mesh of this.cages.values()) n += mesh.count;
9209
+ for (const mesh2 of this.cages.values()) n += mesh2.count;
8582
9210
  return n;
8583
9211
  }
8584
9212
  /** Meshes of the live cages (tests and picking). */
@@ -8589,25 +9217,25 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8589
9217
  set(key, bars) {
8590
9218
  this.remove(key);
8591
9219
  if (bars.length === 0) return;
8592
- const mesh = new this.THREE.InstancedMesh(this.geometry, this.material, bars.length);
9220
+ const mesh2 = new this.THREE.InstancedMesh(this.geometry, this.material, bars.length);
8593
9221
  bars.forEach(([a, b], i) => {
8594
9222
  this.dir.set(b[0] - a[0], b[1] - a[1], b[2] - a[2]);
8595
9223
  const length = this.dir.length();
8596
9224
  this.mid.set((a[0] + b[0]) / 2, (a[1] + b[1]) / 2, (a[2] + b[2]) / 2);
8597
9225
  this.q.setFromUnitVectors(this.up, length > 1e-9 ? this.dir.divideScalar(length) : this.up);
8598
9226
  this.m.compose(this.mid, this.q, this.scale.set(1, Math.max(length, 1e-4), 1));
8599
- mesh.setMatrixAt(i, this.m);
9227
+ mesh2.setMatrixAt(i, this.m);
8600
9228
  });
8601
- mesh.instanceMatrix.needsUpdate = true;
8602
- mesh.castShadow = true;
8603
- this.scene.add(mesh);
8604
- this.cages.set(key, mesh);
9229
+ mesh2.instanceMatrix.needsUpdate = true;
9230
+ mesh2.castShadow = true;
9231
+ this.scene.add(mesh2);
9232
+ this.cages.set(key, mesh2);
8605
9233
  }
8606
9234
  remove(key) {
8607
- const mesh = this.cages.get(key);
8608
- if (!mesh) return;
8609
- this.scene.remove(mesh);
8610
- mesh.dispose();
9235
+ const mesh2 = this.cages.get(key);
9236
+ if (!mesh2) return;
9237
+ this.scene.remove(mesh2);
9238
+ mesh2.dispose();
8611
9239
  this.cages.delete(key);
8612
9240
  }
8613
9241
  /** Remove the cages of every part of `object` (keys are `object|part`). */
@@ -8626,9 +9254,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8626
9254
 
8627
9255
  // ../game-destruction/dist/index.js
8628
9256
  var EPS = 1e-9;
8629
- var IDENTITY = [0, 0, 0, 1];
9257
+ var IDENTITY2 = [0, 0, 0, 1];
8630
9258
  var quat = {
8631
- identity: () => [IDENTITY[0], IDENTITY[1], IDENTITY[2], IDENTITY[3]],
9259
+ identity: () => [IDENTITY2[0], IDENTITY2[1], IDENTITY2[2], IDENTITY2[3]],
8632
9260
  /** Hamilton product `a * b` (apply `b` first, then `a`). */
8633
9261
  mul: (a, b) => [
8634
9262
  a[3] * b[0] + a[0] * b[3] + a[1] * b[2] - a[2] * b[1],
@@ -9951,25 +10579,25 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
9951
10579
  /**
9952
10580
  * @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
10581
  */
9954
- makeMeshVis(mesh, home, range, hosts) {
10582
+ makeMeshVis(mesh2, home, range, hosts) {
9955
10583
  let host = null;
9956
10584
  if (range) {
9957
- host = hosts?.get(mesh) ?? null;
10585
+ host = hosts?.get(mesh2) ?? null;
9958
10586
  if (!host) {
9959
- host = { mesh, origGeometry: mesh.geometry, origMaterial: mesh.material, ownGeo: false, work: null, ownMat: false, sharers: [] };
9960
- hosts?.set(mesh, host);
10587
+ host = { mesh: mesh2, origGeometry: mesh2.geometry, origMaterial: mesh2.material, ownGeo: false, work: null, ownMat: false, sharers: [] };
10588
+ hosts?.set(mesh2, host);
9961
10589
  }
9962
10590
  }
9963
10591
  const mv = {
9964
- mesh,
10592
+ mesh: mesh2,
9965
10593
  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(),
10594
+ origGeometry: mesh2.geometry,
10595
+ origMaterial: mesh2.material,
10596
+ origVisible: mesh2.visible,
10597
+ origParent: mesh2.parent,
10598
+ origPosition: mesh2.position.clone(),
10599
+ origQuaternion: mesh2.quaternion.clone(),
10600
+ origScale: mesh2.scale.clone(),
9973
10601
  ownMat: false,
9974
10602
  ownGeo: false,
9975
10603
  work: null,
@@ -10094,15 +10722,15 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
10094
10722
  * Returns false when nothing was changed (too far, low quality on a heavy mesh, or no editable positions).
10095
10723
  */
10096
10724
  applyDent(mv, worldPoint, worldNormal, dent) {
10097
- const mesh = mv.mesh;
10725
+ const mesh2 = mv.mesh;
10098
10726
  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();
10727
+ mesh2.updateWorldMatrix(true, false);
10728
+ const s = mesh2.matrixWorld.getMaxScaleOnAxis() || 1;
10729
+ const lp = mesh2.worldToLocal(this.a.copy(worldPoint));
10730
+ const ln = this.b.copy(worldNormal).transformDirection(this.m4.copy(mesh2.matrixWorld).invert()).normalize();
10103
10731
  const r = dent.radius / s;
10104
10732
  const depth = dent.depth / s;
10105
- const geo = mesh.geometry;
10733
+ const geo = mesh2.geometry;
10106
10734
  let bb;
10107
10735
  if (mv.host) bb = this.localBox(mv, this.lbox);
10108
10736
  else {
@@ -10112,7 +10740,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
10112
10740
  if (bb && !bb.isEmpty() && bb.distanceToPoint(lp) > r) return false;
10113
10741
  if (!this.ownGeometry(mv)) return false;
10114
10742
  const work = mv.host ? mv.host.work : mv.work;
10115
- const pos = positionAttr(mesh.geometry);
10743
+ const pos = positionAttr(mesh2.geometry);
10116
10744
  const r2 = r * r;
10117
10745
  let touched = false;
10118
10746
  const px = lp.x;
@@ -10178,11 +10806,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
10178
10806
  // Decals
10179
10807
  // ------------------------------------------------------------------------------------------------------------------------------------
10180
10808
  /** 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);
10809
+ decalPose(mesh2, worldPoint, worldNormal, lift) {
10810
+ mesh2.updateWorldMatrix(true, false);
10811
+ const s = mesh2.matrixWorld.getMaxScaleOnAxis() || 1;
10812
+ const ln = worldNormal.clone().transformDirection(this.m4.copy(mesh2.matrixWorld).invert()).normalize();
10813
+ const pos = mesh2.worldToLocal(worldPoint.clone()).addScaledVector(ln, lift / s);
10186
10814
  const quat2 = new this.THREE.Quaternion().setFromUnitVectors(new this.THREE.Vector3(0, 0, 1), ln);
10187
10815
  return { s, pos, quat: quat2 };
10188
10816
  }
@@ -10318,7 +10946,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
10318
10946
  if (Math.abs(m - pv.meltApplied) < 0.01) return;
10319
10947
  pv.meltApplied = m;
10320
10948
  for (const mv of pv.meshes) {
10321
- const mesh = mv.mesh;
10949
+ const mesh2 = mv.mesh;
10322
10950
  if (mv.host) {
10323
10951
  mv.meltApplied = m;
10324
10952
  mv.touched = true;
@@ -10326,27 +10954,27 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
10326
10954
  continue;
10327
10955
  }
10328
10956
  if (mv.meltApplied === 0 && m > 0) {
10329
- mesh.updateWorldMatrix(true, false);
10330
- mv.bottomY = this.box.setFromObject(mesh).min.y;
10957
+ mesh2.updateWorldMatrix(true, false);
10958
+ mv.bottomY = this.box.setFromObject(mesh2).min.y;
10331
10959
  }
10332
10960
  mv.meltApplied = m;
10333
10961
  mv.moved = true;
10334
10962
  const sy = lerp5(1, 0.25, m);
10335
10963
  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;
10964
+ mesh2.scale.set(mv.origScale.x * sxz, mv.origScale.y * sy, mv.origScale.z * sxz);
10965
+ mesh2.position.copy(mv.origPosition);
10966
+ mesh2.updateWorldMatrix(true, false);
10967
+ const bottom = this.box.setFromObject(mesh2).min.y;
10340
10968
  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();
10969
+ if (Math.abs(dy) > 1e-6 && mesh2.parent) {
10970
+ mesh2.parent.updateWorldMatrix(true, false);
10971
+ const inv = this.m4.copy(mesh2.parent.matrixWorld).invert();
10344
10972
  this.a.set(0, dy, 0).applyMatrix4(inv);
10345
10973
  this.b.set(0, 0, 0).applyMatrix4(inv);
10346
- mesh.position.copy(mv.origPosition).add(this.a.sub(this.b));
10974
+ mesh2.position.copy(mv.origPosition).add(this.a.sub(this.b));
10347
10975
  }
10348
10976
  if (!(this.lowQuality && this.vertexCount(mv) > 2e4) && this.ownGeometry(mv)) {
10349
- this.writePositions(mv, positionAttr(mesh.geometry));
10977
+ this.writePositions(mv, positionAttr(mesh2.geometry));
10350
10978
  }
10351
10979
  }
10352
10980
  }
@@ -10424,10 +11052,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
10424
11052
  const sy = lerp5(1, 0.25, melt);
10425
11053
  const sxz = 1 + 0.18 * melt;
10426
11054
  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;
11055
+ const noise = (Math.imul(v + 1, 2654435761) >>> 0) % 1e3 / 1e3;
10428
11056
  arr[i] = cx + (work[i] - cx) * sxz;
10429
11057
  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);
11058
+ arr[i + 1] = Math.max(minY, minY + (work[i + 1] - minY) * sy - noise * melt * h * 0.04);
10431
11059
  }
10432
11060
  }
10433
11061
  /** 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 +11247,30 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
10619
11247
  this.restoreRanged(mv);
10620
11248
  return;
10621
11249
  }
10622
- const mesh = mv.mesh;
11250
+ const mesh2 = mv.mesh;
10623
11251
  for (const d of mv.decals) this.disposeDecal(d);
10624
11252
  mv.decals.length = 0;
10625
11253
  if (mv.ownGeo) {
10626
- mesh.geometry.dispose();
10627
- mesh.geometry = mv.origGeometry;
11254
+ mesh2.geometry.dispose();
11255
+ mesh2.geometry = mv.origGeometry;
10628
11256
  mv.ownGeo = false;
10629
11257
  mv.work = null;
10630
11258
  }
10631
11259
  if (mv.ownMat) {
10632
- for (const m of materialsOf(mesh.material)) m.dispose();
10633
- mesh.material = mv.origMaterial;
11260
+ for (const m of materialsOf(mesh2.material)) m.dispose();
11261
+ mesh2.material = mv.origMaterial;
10634
11262
  mv.ownMat = false;
10635
11263
  mv.baseColors = null;
10636
11264
  }
10637
- mesh.visible = mv.origVisible;
10638
- if (mv.origParent && mesh.parent !== mv.origParent) {
10639
- mv.origParent.add(mesh);
11265
+ mesh2.visible = mv.origVisible;
11266
+ if (mv.origParent && mesh2.parent !== mv.origParent) {
11267
+ mv.origParent.add(mesh2);
10640
11268
  mv.moved = true;
10641
11269
  }
10642
11270
  if (mv.moved) {
10643
- mesh.position.copy(mv.origPosition);
10644
- mesh.quaternion.copy(mv.origQuaternion);
10645
- mesh.scale.copy(mv.origScale);
11271
+ mesh2.position.copy(mv.origPosition);
11272
+ mesh2.quaternion.copy(mv.origQuaternion);
11273
+ mesh2.scale.copy(mv.origScale);
10646
11274
  mv.moved = false;
10647
11275
  }
10648
11276
  mv.meltApplied = 0;
@@ -11780,6 +12408,56 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
11780
12408
  };
11781
12409
  }
11782
12410
  var GAME_SFX_BANK = { id: "game_sfx", sounds: [...GAME_SFX_CLASSES, GAME_SFX_FALLBACK].map((row) => noiseClassRecipe(row)) };
12411
+ var ATTACK = 2e-3;
12412
+ function lengthOf(d, attack) {
12413
+ if (typeof d === "number") return attack + d;
12414
+ if (Array.isArray(d)) return [attack + d[0], attack + d[1]];
12415
+ return { ...d, add: (d.add ?? 0) + attack };
12416
+ }
12417
+ var envelope = (attack) => [
12418
+ [0, 1e-4, "set", "fixed"],
12419
+ [attack, 1, "exp"],
12420
+ ["1d", 1e-4, "exp", "fixed"]
12421
+ ];
12422
+ function sceneNoise(o) {
12423
+ const a = o.a ?? ATTACK;
12424
+ const over = typeof o.d === "number" ? o.d : 0;
12425
+ return {
12426
+ filters: {
12427
+ mode: "serial",
12428
+ 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 }]
12429
+ },
12430
+ duration: lengthOf(o.d, a),
12431
+ gain: o.vol,
12432
+ ...o.delay === void 0 ? {} : { at: o.delay },
12433
+ ...o.wet === void 0 ? {} : { wet: o.wet },
12434
+ source: { kind: "noise", offset: "random", offsetScale: 1.5, loop: true },
12435
+ envelope: envelope(a)
12436
+ };
12437
+ }
12438
+ function sceneTone(o) {
12439
+ return {
12440
+ source: { kind: "osc", wave: o.wave ?? "sine", frequency: { from: o.f, to: o.f2 ?? "from", curve: "exp", over: "duration" } },
12441
+ duration: lengthOf(o.d, ATTACK),
12442
+ gain: o.vol,
12443
+ ...o.delay === void 0 ? {} : { at: o.delay },
12444
+ ...o.wet === void 0 ? {} : { wet: o.wet },
12445
+ envelope: envelope(ATTACK)
12446
+ };
12447
+ }
12448
+ var sceneMany = (count, make) => Array.from({ length: count }, make);
12449
+ function sceneRecipe(id, layers, line, options = {}) {
12450
+ return {
12451
+ id,
12452
+ bus: options.bus ?? "world",
12453
+ pan: true,
12454
+ layers,
12455
+ falloff: DISTANCE_CURVES.art_scene,
12456
+ ...options.vars ? { vars: options.vars } : {},
12457
+ ...options.group ? { group: options.group } : {},
12458
+ sources: [`cena:${line}`]
12459
+ };
12460
+ }
11783
12461
  var BENCH_BANK = {
11784
12462
  id: "audio_bench",
11785
12463
  sounds: [
@@ -11827,88 +12505,42 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
11827
12505
  };
11828
12506
 
11829
12507
  // 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
- });
12508
+ var scene = (id, layers, line, vars) => sceneRecipe(id, layers, line, vars ? { vars } : {});
11877
12509
  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] }))
12510
+ sceneNoise({ f: 1200, f2: 120, d: 1.8, vol: 2, wet: 1.2 }),
12511
+ sceneTone({ f: 65, f2: 24, d: 1, vol: 1.8 }),
12512
+ sceneNoise({ type: "highpass", f: 1500, d: 0.2, vol: 0.8 }),
12513
+ ...big ? [sceneNoise({ f: 500, f2: 90, a: 0.2, d: 2.4, vol: 1.2, wet: 1, delay: 0.15 })] : [],
12514
+ ...sceneMany(8, () => sceneTone({ f: [2600, 5600], d: 0.05, vol: 0.08, delay: [0.4, 1.6] }))
11883
12515
  ];
11884
12516
  var SCENE_SOUNDS = [
11885
12517
  scene(
11886
12518
  "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] }))],
12519
+ [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
12520
  "L1359 glass"
11889
12521
  ),
11890
12522
  scene(
11891
12523
  "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 })],
12524
+ [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
12525
  "L1360 wood"
11894
12526
  ),
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"),
12527
+ scene("thunk", [sceneNoise({ type: "bandpass", f: 700, q: 2, d: 0.07, vol: 0.4 })], "L1361 thunk"),
12528
+ scene("ping", [sceneTone({ f: [2600, 3500], f2: 1400, d: 0.14, vol: 0.18, wet: 0.3 })], "L1362 ping"),
12529
+ 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"),
12530
+ scene("chip", [sceneNoise({ type: "bandpass", f: 1600, q: 1.2, d: 0.06, vol: 0.5 })], "L1364 chip"),
11899
12531
  scene(
11900
12532
  "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] }))],
12533
+ [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
12534
  "L1365 crumble"
11903
12535
  ),
11904
12536
  // `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 }),
12537
+ 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
12538
  // `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 }),
12539
+ scene("hiss", [sceneNoise({ type: "highpass", f: 3500, a: 0.05, d: { param: "seconds" }, vol: 0.35 })], "L1366 hiss", { seconds: 1.2 }),
11908
12540
  scene("boom", boomLayers(false), "L1367 boom"),
11909
12541
  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")
12542
+ 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"),
12543
+ 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
12544
  ];
11913
12545
  var EXPLOSION_BOOM = {
11914
12546
  id: "explosion_boom",