@adatechnology/game-destruction-three 0.12.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -55,6 +55,39 @@ var GameDestructionThree = (() => {
55
55
  const length = Math.hypot(direction[0], direction[1], direction[2]);
56
56
  return { speed, direction: [direction[0] / length, direction[1] / length, direction[2] / length], distance, falloff };
57
57
  }
58
+ var DEFAULT_COST_ALLOCATION_BYTES = 1024 * 1024;
59
+ var DEFERRED_QUEUE_ID = {
60
+ DESTRUCTION_EVENTS: "destruction.events",
61
+ BREACH_PAINT: "floorplan.breach-paint",
62
+ AUDIO_VOICES: "audio.voices",
63
+ VEHICLE_CREASE: "vehicles.crease"
64
+ };
65
+ var DEFERRED_PRIORITY = {
66
+ [DEFERRED_QUEUE_ID.DESTRUCTION_EVENTS]: { priority: 300, phase: "present" },
67
+ [DEFERRED_QUEUE_ID.BREACH_PAINT]: { priority: 250, phase: "present" },
68
+ [DEFERRED_QUEUE_ID.AUDIO_VOICES]: { priority: 200, phase: "idle", reserveMs: 1 },
69
+ [DEFERRED_QUEUE_ID.VEHICLE_CREASE]: { priority: 100, phase: "idle" }
70
+ };
71
+ var WARNED_KEY = /* @__PURE__ */ Symbol.for("@adatechnology/game-contracts.warned");
72
+ var warnedKeys = () => {
73
+ const holder = globalThis;
74
+ return holder[WARNED_KEY] ?? (holder[WARNED_KEY] = /* @__PURE__ */ new Set());
75
+ };
76
+ var isProduction = () => {
77
+ try {
78
+ return process.env.NODE_ENV === "production";
79
+ } catch {
80
+ return false;
81
+ }
82
+ };
83
+ var isQuiet = () => globalThis.__GAME_KIT_QUIET__ === true;
84
+ var warnOnce = (key, message) => {
85
+ if (isQuiet() || isProduction()) return;
86
+ const seen = warnedKeys();
87
+ if (seen.has(key)) return;
88
+ seen.add(key);
89
+ console.warn(`[game-kit] ${message}`);
90
+ };
58
91
  var KIT_ORIGIN_KEY = "kitOrigin";
59
92
  function markKitOrigin(object, origin) {
60
93
  const node = object;
@@ -69,6 +102,43 @@ var GameDestructionThree = (() => {
69
102
  }
70
103
  return null;
71
104
  }
105
+ var SURFACE_IDS = ["concrete", "asphalt", "wood", "tile", "dirt", "grass", "gravel", "sand", "metal", "water", "glass", "carpet"];
106
+ var GAIT_IDS = ["walk", "run"];
107
+ var LANDING_IDS = ["light", "heavy"];
108
+ var BODY_SOUND_IDS = [
109
+ "jump",
110
+ "cloth_rustle",
111
+ "body_fall",
112
+ "fall_damage",
113
+ "player_hurt",
114
+ "player_breath_run",
115
+ "player_death",
116
+ "bite",
117
+ "grab",
118
+ "punch_swing",
119
+ "punch_hit_body",
120
+ "punch_hit_face",
121
+ "punch_hit_object",
122
+ "kick_swing",
123
+ "kick_hit",
124
+ "kick_hit_object",
125
+ "shove_two_hands",
126
+ "knife_swing",
127
+ "knife_stab",
128
+ "knife_slash",
129
+ "knife_hit_object",
130
+ "blunt_swing",
131
+ "blunt_hit",
132
+ "blunt_hit_object",
133
+ "axe_swing",
134
+ "axe_chop",
135
+ "axe_hit_object"
136
+ ];
137
+ var footstepSoundId = (surface, gait) => `footstep_${surface}_${gait}`;
138
+ var FOOTSTEP_SOUND_IDS = SURFACE_IDS.flatMap((surface) => GAIT_IDS.map((gait) => footstepSoundId(surface, gait)));
139
+ var LAND_SOUND_IDS = SURFACE_IDS.flatMap((surface) => LANDING_IDS.map((landing2) => `land_${surface}_${landing2}`));
140
+ var BODY_FOLEY_IDS = [...FOOTSTEP_SOUND_IDS, ...LAND_SOUND_IDS, ...BODY_SOUND_IDS];
141
+ var SWING_LEAD_SECONDS = { punch: 0.07, kick: 0.09, shove: 0, blade: 0.05, blunt: 0.1, chop: 0.11 };
72
142
 
73
143
  // ../game-vfx/dist/index.js
74
144
  var __defProp2 = Object.defineProperty;
@@ -2564,22 +2634,22 @@ var GameDestructionThree = (() => {
2564
2634
  )
2565
2635
  ]
2566
2636
  };
2567
- function normalized(vector) {
2568
- const length = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1] + vector[2] * vector[2]) || 1;
2637
+ function normalized(vector2) {
2638
+ const length = Math.sqrt(vector2[0] * vector2[0] + vector2[1] * vector2[1] + vector2[2] * vector2[2]) || 1;
2569
2639
  const inverse = 1 / length;
2570
- return [vector[0] * inverse, vector[1] * inverse, vector[2] * inverse];
2640
+ return [vector2[0] * inverse, vector2[1] * inverse, vector2[2] * inverse];
2571
2641
  }
2572
- function plus(vector, x, y, z) {
2573
- return [vector[0] + x, vector[1] + y, vector[2] + z];
2642
+ function plus(vector2, x, y, z) {
2643
+ return [vector2[0] + x, vector2[1] + y, vector2[2] + z];
2574
2644
  }
2575
- function scaledBy(vector, factor) {
2576
- return [vector[0] * factor, vector[1] * factor, vector[2] * factor];
2645
+ function scaledBy(vector2, factor) {
2646
+ return [vector2[0] * factor, vector2[1] * factor, vector2[2] * factor];
2577
2647
  }
2578
- function plusScaled(vector, direction, factor) {
2579
- return [vector[0] + direction[0] * factor, vector[1] + direction[1] * factor, vector[2] + direction[2] * factor];
2648
+ function plusScaled(vector2, direction, factor) {
2649
+ return [vector2[0] + direction[0] * factor, vector2[1] + direction[1] * factor, vector2[2] + direction[2] * factor];
2580
2650
  }
2581
- function negated(vector) {
2582
- return [-vector[0], -vector[1], -vector[2]];
2651
+ function negated(vector2) {
2652
+ return [-vector2[0], -vector2[1], -vector2[2]];
2583
2653
  }
2584
2654
  var FRAME_DUST = normalized([0, -0.3, 1]);
2585
2655
  var SPLINTER_DIRECTION = normalized([0, 0.3, 1]);
@@ -3882,9 +3952,32 @@ var GameDestructionThree = (() => {
3882
3952
  /** The leak caught fire (an incendiary or tracer round, a spark, a fire near): the page's whole sequence, the fire running along the pool to the tank and the blast. */
3883
3953
  aliasOf(FUEL_LEAK, "vehicle_leak_ignited", {})
3884
3954
  ];
3955
+ var vector = [variable("vx"), variable("vy"), variable("vz")];
3956
+ var range = (name) => [variable(name), variable(name)];
3957
+ var singleParticle = (id, system) => ({
3958
+ id,
3959
+ pageId: `particle:${id}`,
3960
+ team: "world",
3961
+ budget: { particles: 1, lights: 0 },
3962
+ loop: 1e-3,
3963
+ timeline: [
3964
+ spawn(system, {
3965
+ p: [0, 0, 0],
3966
+ v: vector,
3967
+ life: range("life"),
3968
+ size: range("size"),
3969
+ grow: variable("grow"),
3970
+ c0: variable("color"),
3971
+ a: variable("alpha"),
3972
+ g: variable("gravity"),
3973
+ drag: variable("drag")
3974
+ })
3975
+ ]
3976
+ });
3977
+ var SINGLE_PARTICLE_EFFECTS = [singleParticle("particle_puff", "normal"), singleParticle("particle_glow", "add"), singleParticle("particle_streak", "streak")];
3885
3978
  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];
3886
3979
  var WORKSHOP_EFFECTS = Object.fromEntries(ALL_EFFECTS.map((effect) => [effect.id, effect]));
3887
- var GAME_EFFECTS = { ...WORKSHOP_EFFECTS, ...Object.fromEntries([...EVENT_EFFECTS, ...VIEWMODEL_EFFECTS, ...VEHICLE_EFFECTS].map((effect) => [effect.id, effect])) };
3980
+ var GAME_EFFECTS = { ...WORKSHOP_EFFECTS, ...Object.fromEntries([...EVENT_EFFECTS, ...VIEWMODEL_EFFECTS, ...VEHICLE_EFFECTS, ...SINGLE_PARTICLE_EFFECTS].map((effect) => [effect.id, effect])) };
3888
3981
  var TABLE = {
3889
3982
  concrete: { dust: 9078138, chip: 7236195, spark: 2 },
3890
3983
  metal: { dust: 0, chip: 0, spark: 18 },
@@ -4554,8 +4647,8 @@ var GameDestructionThree = (() => {
4554
4647
  var UP12 = [0, 1, 0];
4555
4648
  var ZERO = [0, 0, 0];
4556
4649
  var FULL_TURN_DRAWN = 6.283;
4557
- function scaledSizes(range, factor) {
4558
- return [range[0] * Math.sqrt(factor), range[1] * Math.sqrt(factor)];
4650
+ function scaledSizes(range2, factor) {
4651
+ return [range2[0] * Math.sqrt(factor), range2[1] * Math.sqrt(factor)];
4559
4652
  }
4560
4653
  function fireEmit(engine, key, dt, centre, radius, factor, options = {}) {
4561
4654
  const wind = options.wind ?? ZERO;
@@ -6409,9 +6502,9 @@ var GameDestructionThree = (() => {
6409
6502
  var TWO_PI_ROUNDED = 6.2832;
6410
6503
  var WIDE_CONE = 3.1;
6411
6504
  var UP13 = [0, 1, 0];
6412
- function drawRange(random, range) {
6413
- if (Array.isArray(range)) return random.between(range[0], range[1]);
6414
- return range || 0;
6505
+ function drawRange(random, range2) {
6506
+ if (Array.isArray(range2)) return random.between(range2[0], range2[1]);
6507
+ return range2 || 0;
6415
6508
  }
6416
6509
  function randomUnit(random, out) {
6417
6510
  const up = random.next() * 2 - 1;
@@ -7178,20 +7271,20 @@ var GameDestructionThree = (() => {
7178
7271
  return [origin[0] + cos2 * x + sin2 * z, origin[1] + point[1] - anchor[1], origin[2] - sin2 * x + cos2 * z];
7179
7272
  }
7180
7273
  /** A direction or velocity of the recipe's space in the world. */
7181
- turn(vector) {
7274
+ turn(vector2) {
7182
7275
  const placement = this.placement;
7183
- if (!placement) return vector;
7184
- if (placement.orientation) return rotate(placement.orientation, vector);
7276
+ if (!placement) return vector2;
7277
+ if (placement.orientation) return rotate(placement.orientation, vector2);
7185
7278
  const cos2 = Math.cos(placement.yaw);
7186
7279
  const sin2 = Math.sin(placement.yaw);
7187
- return [cos2 * vector[0] + sin2 * vector[2], vector[1], -sin2 * vector[0] + cos2 * vector[2]];
7280
+ return [cos2 * vector2[0] + sin2 * vector2[2], vector2[1], -sin2 * vector2[0] + cos2 * vector2[2]];
7188
7281
  }
7189
7282
  placed(options) {
7190
7283
  if (!this.placement) return options;
7191
7284
  return { ...options, p: this.place(options.p), ...options.dir ? { dir: this.turn(options.dir) } : {}, ...options.v ? { v: this.turn(options.v) } : {} };
7192
7285
  }
7193
- spawn(system, recipe) {
7194
- const options = this.placed(recipe);
7286
+ spawn(system, recipe2) {
7287
+ const options = this.placed(recipe2);
7195
7288
  this.context.trace?.(system, [options]);
7196
7289
  if (system === "add") this.add.spawn(options);
7197
7290
  else if (system === "normal") this.normal.spawn(options);
@@ -7211,43 +7304,43 @@ var GameDestructionThree = (() => {
7211
7304
  this.accumulators.set(key, carried);
7212
7305
  }
7213
7306
  /** 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. */
7214
- mesh(recipe) {
7307
+ mesh(recipe2) {
7215
7308
  const placement = this.placement;
7216
- const options = placement ? { ...recipe, p: this.place(recipe.p), ...recipe.v ? { v: this.turn(recipe.v) } : {} } : recipe;
7309
+ const options = placement ? { ...recipe2, p: this.place(recipe2.p), ...recipe2.v ? { v: this.turn(recipe2.v) } : {} } : recipe2;
7217
7310
  this.context.trace?.("mesh", [options]);
7218
- const grouped = recipe.at !== void 0 || recipe.roll !== void 0;
7311
+ const grouped = recipe2.at !== void 0 || recipe2.roll !== void 0;
7219
7312
  return this.meshes.spawn(options, placement && grouped ? placement.orientation ?? [0, Math.sin(placement.yaw / 2), 0, Math.cos(placement.yaw / 2)] : void 0);
7220
7313
  }
7221
- debrisPiece(recipe) {
7222
- const options = this.placed(recipe);
7314
+ debrisPiece(recipe2) {
7315
+ const options = this.placed(recipe2);
7223
7316
  this.context.trace?.("debris", [options]);
7224
7317
  this.debris.spawn(options);
7225
7318
  }
7226
- decal(recipe) {
7227
- let args = recipe;
7319
+ decal(recipe2) {
7320
+ let args = recipe2;
7228
7321
  if (this.placement) {
7229
- const [x, y, z] = this.place([recipe[0], recipe[1], recipe[2]]);
7230
- const options = recipe[5];
7231
- args = [x, y, z, recipe[3], recipe[4], options?.n ? { ...options, n: this.turn(options.n) } : options];
7322
+ const [x, y, z] = this.place([recipe2[0], recipe2[1], recipe2[2]]);
7323
+ const options = recipe2[5];
7324
+ args = [x, y, z, recipe2[3], recipe2[4], options?.n ? { ...options, n: this.turn(options.n) } : options];
7232
7325
  }
7233
7326
  this.context.trace?.("decal", args);
7234
7327
  this.decals.add(args[0], args[1], args[2], args[3], args[4], args[5]);
7235
7328
  }
7236
- ring(recipe) {
7237
- const args = this.placement ? [this.place(recipe[0]), recipe[1], recipe[2], recipe[3], recipe[4], recipe[5] && this.turn(recipe[5]), recipe[6]] : recipe;
7329
+ ring(recipe2) {
7330
+ const args = this.placement ? [this.place(recipe2[0]), recipe2[1], recipe2[2], recipe2[3], recipe2[4], recipe2[5] && this.turn(recipe2[5]), recipe2[6]] : recipe2;
7238
7331
  this.context.trace?.("ring", args);
7239
7332
  this.rings.add(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
7240
7333
  }
7241
7334
  /** `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. */
7242
- flash(recipe) {
7243
- const args = this.placement ? [this.place(recipe[0]), recipe[1], recipe[2], recipe[3], recipe[4], recipe[5], recipe[6]] : recipe;
7335
+ flash(recipe2) {
7336
+ const args = this.placement ? [this.place(recipe2[0]), recipe2[1], recipe2[2], recipe2[3], recipe2[4], recipe2[5], recipe2[6]] : recipe2;
7244
7337
  this.context.trace?.("flash", args.slice(0, 6));
7245
7338
  this.lights.flash(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
7246
7339
  }
7247
7340
  /** `fireLight` (W400): sways with the clock and flickers with one draw. */
7248
- fireLight(slot, recipe, intensity, time) {
7341
+ fireLight(slot, recipe2, intensity, time) {
7249
7342
  const light = this.lights.fire[slot];
7250
- const point = this.place(recipe);
7343
+ const point = this.place(recipe2);
7251
7344
  light.x = point[0];
7252
7345
  light.y = point[1];
7253
7346
  light.z = point[2];
@@ -7561,7 +7654,22 @@ var GameDestructionThree = (() => {
7561
7654
  return key === void 0 ? void 0 : entry[key];
7562
7655
  }
7563
7656
 
7564
- // ../game-vfx-three/dist/index.js
7657
+ // ../game-render-three/dist/index.js
7658
+ var DEFAULT_ANGLE = Math.PI / 3;
7659
+ var LIGHT_PRIORITY = {
7660
+ blast: 900,
7661
+ rocket: 800,
7662
+ muzzle: 750,
7663
+ fire: 600,
7664
+ flare: 500,
7665
+ headlight: 400,
7666
+ weaponLight: 400,
7667
+ lamp: 200,
7668
+ street: 150
7669
+ };
7670
+ var EFFECT_STEP_SECONDS = 1 / 50;
7671
+
7672
+ // ../game-vfx-three/dist/chunk-ALDCQ5RH.js
7565
7673
  var GLOW_VERTEX = `attribute vec4 aP;attribute vec4 aC;varying vec2 vU;varying vec4 vC;
7566
7674
  void main(){vec3 R=vec3(viewMatrix[0][0],viewMatrix[1][0],viewMatrix[2][0]);vec3 Up=vec3(viewMatrix[0][1],viewMatrix[1][1],viewMatrix[2][1]);
7567
7675
  vec3 wp=aP.xyz+(R*position.x+Up*position.y)*aP.w;vU=position.xy;vC=aC;gl_Position=projectionMatrix*viewMatrix*vec4(wp,1.);}`;
@@ -7840,8 +7948,26 @@ void main(){
7840
7948
  }
7841
7949
  };
7842
7950
  }
7843
- function createLightLayer(THREE, pool, scale) {
7951
+ var LIGHT_ON_THRESHOLD = 0.05;
7952
+ var FIRE_COLOR = 16742954;
7953
+ var FIRE_DECAY = 2;
7954
+ var BLAST_DECAY = 2;
7955
+ function followEntry(budget, handles, index, entry, request) {
7956
+ const handle = handles[index] ?? null;
7957
+ if (entry.intensity <= LIGHT_ON_THRESHOLD) {
7958
+ handle?.release({ fadeMs: 0 });
7959
+ handles[index] = null;
7960
+ return;
7961
+ }
7962
+ if (handle?.alive) {
7963
+ handle.update({ color: request.color, intensity: request.intensity, distance: request.distance, position: request.position });
7964
+ return;
7965
+ }
7966
+ handles[index] = budget.acquire(request);
7967
+ }
7968
+ function createLightLayer(THREE, pool, scale, budget) {
7844
7969
  const group = new THREE.Group();
7970
+ if (budget) return createBudgetedLightLayer(group, pool, scale, budget);
7845
7971
  const blast = pool.blast.map(() => {
7846
7972
  const light = new THREE.PointLight(16752720, 0, 30, 2);
7847
7973
  light.position.set(0, -50, 0);
@@ -7880,6 +8006,30 @@ void main(){
7880
8006
  }
7881
8007
  };
7882
8008
  }
8009
+ function createBudgetedLightLayer(group, pool, scale, budget) {
8010
+ const blast = pool.blast.map(() => null);
8011
+ const fire = pool.fire.map(() => null);
8012
+ return {
8013
+ group,
8014
+ sync() {
8015
+ let lit = 0;
8016
+ pool.blast.forEach((entry, index) => {
8017
+ const request = { owner: "vfx.blast", priority: LIGHT_PRIORITY.blast, urgent: true, color: entry.color, intensity: entry.intensity * scale, distance: entry.distance, decay: BLAST_DECAY, position: [entry.x, entry.y, entry.z] };
8018
+ followEntry(budget, blast, index, entry, request);
8019
+ if (entry.intensity > LIGHT_ON_THRESHOLD) lit++;
8020
+ });
8021
+ pool.fire.forEach((entry, index) => {
8022
+ const request = { owner: "vfx.fire", priority: LIGHT_PRIORITY.fire, color: FIRE_COLOR, intensity: entry.intensity * scale, distance: entry.distance, decay: FIRE_DECAY, position: [entry.x, entry.y, entry.z] };
8023
+ followEntry(budget, fire, index, entry, request);
8024
+ if (entry.intensity > LIGHT_ON_THRESHOLD) lit++;
8025
+ });
8026
+ return lit;
8027
+ },
8028
+ dispose() {
8029
+ for (const handle of [...blast, ...fire]) handle?.release({ fadeMs: 0 });
8030
+ }
8031
+ };
8032
+ }
7883
8033
  var SCORCH_SEED = 5;
7884
8034
  var TEXTURE_SIZE = 256;
7885
8035
  var FULL_TURN = Math.PI * 2;
@@ -8013,6 +8163,7 @@ void main(){
8013
8163
  group.add(mesh2);
8014
8164
  return mesh2;
8015
8165
  }
8166
+ let probeGeometry;
8016
8167
  const groundMarks = [];
8017
8168
  const spareGeometries = [];
8018
8169
  const blood = [];
@@ -8118,6 +8269,15 @@ void main(){
8118
8269
  rockMesh();
8119
8270
  plateMesh();
8120
8271
  },
8272
+ probes() {
8273
+ probeGeometry ?? (probeGeometry = new THREE.PlaneGeometry(1e-3, 1e-3).rotateX(-Math.PI / 2));
8274
+ return [...scorchMaterials, dirtMaterial].map((material) => {
8275
+ const mesh2 = new THREE.Mesh(probeGeometry, material);
8276
+ mesh2.receiveShadow = true;
8277
+ mesh2.frustumCulled = false;
8278
+ return mesh2;
8279
+ });
8280
+ },
8121
8281
  update(dt) {
8122
8282
  for (let index = 0; index < blood.length; index++) {
8123
8283
  const drawn = blood[index];
@@ -8130,6 +8290,7 @@ void main(){
8130
8290
  dispose() {
8131
8291
  clearMarks();
8132
8292
  for (const mesh2 of [instanced.blood, instanced.rock, instanced.plate]) mesh2?.dispose();
8293
+ probeGeometry?.dispose();
8133
8294
  for (const item of owned) item.dispose();
8134
8295
  }
8135
8296
  };
@@ -8705,7 +8866,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8705
8866
  }
8706
8867
  };
8707
8868
  }
8708
- function createWorkshopLayers(THREE, engine, pointScale, lightScale) {
8869
+ function createWorkshopLayers(THREE, engine, pointScale, lightScale, budget) {
8709
8870
  const group = new THREE.Group();
8710
8871
  group.name = "workshop";
8711
8872
  const layers = [
@@ -8720,15 +8881,30 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8720
8881
  for (const layer2 of layers) group.add(layer2.object);
8721
8882
  const meshes = createMeshLayer(THREE, engine.meshes);
8722
8883
  group.add(meshes.group);
8723
- const flashLights = engine.lights.lights.map(() => new THREE.PointLight(16760944, 0, 14));
8724
- const fireLights = engine.lights.fire.map(() => new THREE.PointLight(FIRE_LIGHT_COLOR, 0, FIRE_LIGHT_DISTANCE));
8725
- for (const light of [...flashLights, ...fireLights]) group.add(light);
8884
+ const lights = budget ? budgetedLights(engine, lightScale, budget) : legacyLights(THREE, engine, lightScale, group);
8726
8885
  return {
8727
8886
  group,
8728
8887
  sync() {
8729
8888
  let drawCalls = 0;
8730
8889
  for (const layer2 of layers) if (layer2.sync()) drawCalls++;
8731
8890
  drawCalls += meshes.sync();
8891
+ lights.sync();
8892
+ return drawCalls;
8893
+ },
8894
+ dispose() {
8895
+ for (const layer2 of layers) layer2.dispose();
8896
+ meshes.dispose();
8897
+ lights.dispose();
8898
+ group.clear();
8899
+ }
8900
+ };
8901
+ }
8902
+ function legacyLights(THREE, engine, lightScale, group) {
8903
+ const flashLights = engine.lights.lights.map(() => new THREE.PointLight(16760944, 0, 14));
8904
+ const fireLights = engine.lights.fire.map(() => new THREE.PointLight(FIRE_LIGHT_COLOR, 0, FIRE_LIGHT_DISTANCE));
8905
+ for (const light of [...flashLights, ...fireLights]) group.add(light);
8906
+ return {
8907
+ sync() {
8732
8908
  engine.lights.lights.forEach((source, index) => {
8733
8909
  const light = flashLights[index];
8734
8910
  light.position.set(source.x, source.y, source.z);
@@ -8741,12 +8917,27 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8741
8917
  light.position.set(source.x, source.y, source.z);
8742
8918
  light.intensity = source.intensity * lightScale;
8743
8919
  });
8744
- return drawCalls;
8745
8920
  },
8746
8921
  dispose() {
8747
- for (const layer2 of layers) layer2.dispose();
8748
- meshes.dispose();
8749
- group.clear();
8922
+ }
8923
+ };
8924
+ }
8925
+ function budgetedLights(engine, lightScale, budget) {
8926
+ const flash2 = engine.lights.lights.map(() => null);
8927
+ const fire = engine.lights.fire.map(() => null);
8928
+ return {
8929
+ sync() {
8930
+ engine.lights.lights.forEach((source, index) => {
8931
+ const request = { owner: "vfx.workshop", priority: LIGHT_PRIORITY.blast, urgent: true, color: source.color, intensity: source.intensity * lightScale, distance: source.distance, position: [source.x, source.y, source.z] };
8932
+ followEntry(budget, flash2, index, source, request);
8933
+ });
8934
+ engine.lights.fire.forEach((source, index) => {
8935
+ const request = { owner: "vfx.workshop", priority: LIGHT_PRIORITY.fire, color: FIRE_LIGHT_COLOR, intensity: source.intensity * lightScale, distance: FIRE_LIGHT_DISTANCE, position: [source.x, source.y, source.z] };
8936
+ followEntry(budget, fire, index, source, request);
8937
+ });
8938
+ },
8939
+ dispose() {
8940
+ for (const handle of [...flash2, ...fire]) handle?.release({ fadeMs: 0 });
8750
8941
  }
8751
8942
  };
8752
8943
  }
@@ -8804,7 +8995,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8804
8995
  const chunks = createChunkLayer(THREE, pools.chunks, { rock: rockGeometry, plate: plateGeometry }, colorOfChunk);
8805
8996
  const shocks = createShockLayer(THREE, pools.shocks);
8806
8997
  const sprites = createSpriteLayer(THREE, world.sprites(), options.pointScale ?? 700);
8807
- const lights = createLightLayer(THREE, pools.lights, options.lightScale ?? Math.PI);
8998
+ const lights = createLightLayer(THREE, pools.lights, options.lightScale ?? Math.PI, options.lights);
8808
8999
  const marks = createMarkLayer(THREE, { ground: pools.ground, createCanvas: options.createCanvas, rockGeometry, plateGeometry, ...options.markLimits });
8809
9000
  root.add(puffs.mesh, sparks.mesh, glows.mesh, chunks.rocks, chunks.plates, shocks.group, lights.group, sprites.additive, sprites.normal, marks.group);
8810
9001
  const stopMarks = world.onMarks((event) => marks.handle(event));
@@ -8837,7 +9028,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8837
9028
  workshopLayers?.dispose();
8838
9029
  if (workshopLayers) root.remove(workshopLayers.group);
8839
9030
  workshopEngine = engine;
8840
- workshopLayers = createWorkshopLayers(THREE, engine, options.pointScale ?? 700, 1);
9031
+ workshopLayers = createWorkshopLayers(THREE, engine, options.pointScale ?? 700, 1, options.lights);
8841
9032
  root.add(workshopLayers.group);
8842
9033
  applyLayers();
8843
9034
  }
@@ -8910,7 +9101,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
8910
9101
  const meshes = createMeshLayerProbes(THREE);
8911
9102
  for (const mesh2 of meshes) mesh2.frustumCulled = false;
8912
9103
  const group = new THREE.Group();
8913
- group.add(...meshes);
9104
+ group.add(...meshes, ...marks.probes());
8914
9105
  anchors = { meshes, group };
8915
9106
  return { group, dispose: () => {
8916
9107
  } };
@@ -9028,6 +9219,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
9028
9219
  var BIG_LENGTH = 0.2;
9029
9220
  var BIG_WIDTH = 0.03;
9030
9221
  var FRAGMENT_SEED = 1374496523;
9222
+ var SHARD_SHAPES = 6;
9031
9223
  var isBig = (size) => {
9032
9224
  const [, middle, longest] = [...size].sort((a, b) => a - b);
9033
9225
  return longest >= BIG_LENGTH && middle >= BIG_WIDTH;
@@ -9077,7 +9269,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
9077
9269
  return m;
9078
9270
  }
9079
9271
  shardGeometry(seed) {
9080
- const idx = seed % 6;
9272
+ const idx = seed % SHARD_SHAPES;
9081
9273
  let g = this.shardGeos[idx];
9082
9274
  if (!g) {
9083
9275
  const r = createRng(1e3 + idx);
@@ -9098,17 +9290,32 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
9098
9290
  }
9099
9291
  return g;
9100
9292
  }
9293
+ markParts() {
9294
+ this.markGeometry ?? (this.markGeometry = new this.THREE.CircleGeometry(1, 10));
9295
+ this.markMaterial ?? (this.markMaterial = new this.THREE.MeshBasicMaterial({ color: 723465, depthWrite: false, polygonOffset: true, polygonOffsetFactor: -4, polygonOffsetUnits: -4 }));
9296
+ return { geometry: this.markGeometry, material: this.markMaterial };
9297
+ }
9298
+ /**
9299
+ * Meshes, never put in a scene by this class, that draw every shared piece of a fragment once: the box, the six shards, the bullet-hole disc, and the opaque and
9300
+ * glass materials. A host draws them at load so the first blast does not upload these buffers in the middle of a frame.
9301
+ */
9302
+ warmMeshes() {
9303
+ const opaque = this.materialFor("warm", 8421504, false);
9304
+ const glass = this.materialFor("warm", 8421504, true);
9305
+ const geometries = [this.box, ...Array.from({ length: SHARD_SHAPES }, (_, index) => this.shardGeometry(index))];
9306
+ const { geometry, material } = this.markParts();
9307
+ return [...geometries.flatMap((shared) => [new this.THREE.Mesh(shared, opaque), new this.THREE.Mesh(shared, glass)]), new this.THREE.Mesh(geometry, material)];
9308
+ }
9101
9309
  /**
9102
9310
  * 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
9103
9311
  * 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.
9104
9312
  */
9105
9313
  addMarks(mesh2, f) {
9106
9314
  if (!f.marks?.length) return;
9107
- this.markGeometry ?? (this.markGeometry = new this.THREE.CircleGeometry(1, 10));
9108
- this.markMaterial ?? (this.markMaterial = new this.THREE.MeshBasicMaterial({ color: 723465, depthWrite: false, polygonOffset: true, polygonOffsetFactor: -4, polygonOffsetUnits: -4 }));
9315
+ const { geometry, material } = this.markParts();
9109
9316
  const sc = mesh2.scale;
9110
9317
  for (const mark of f.marks) {
9111
- const disc = new this.THREE.Mesh(this.markGeometry, this.markMaterial);
9318
+ const disc = new this.THREE.Mesh(geometry, material);
9112
9319
  const radius = Math.max(4e-3, mark.diameter) * 0.6;
9113
9320
  const n = this.v.set(mark.normal[0], mark.normal[1], mark.normal[2]).normalize();
9114
9321
  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);
@@ -9691,7 +9898,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
9691
9898
  const invM = 1 / b.mass;
9692
9899
  const steps = clamp6(Math.ceil(dt / SUB_DT), 1, 8);
9693
9900
  const h = dt / steps;
9694
- let landing = 0;
9901
+ let landing2 = 0;
9695
9902
  let landPoint = [pos[0], pos[1], pos[2]];
9696
9903
  let contactedLast = false;
9697
9904
  let touched = false;
@@ -9797,8 +10004,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
9797
10004
  let impact2 = 0;
9798
10005
  for (const r of rs) impact2 = Math.max(impact2, -(v[1] + w[2] * r[0] - w[0] * r[2]));
9799
10006
  const restitution = impact2 > 2 ? 0.25 : 0;
9800
- if (impact2 > landing) {
9801
- landing = impact2;
10007
+ if (impact2 > landing2) {
10008
+ landing2 = impact2;
9802
10009
  let sx = 0;
9803
10010
  let sz = 0;
9804
10011
  for (const r of rs) {
@@ -9934,9 +10141,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
9934
10141
  b.root.position.set(pos[0] - off.x, pos[1] - off.y, pos[2] - off.z);
9935
10142
  b.root.updateMatrixWorld(true);
9936
10143
  b.onGround = contactedLast;
9937
- if (landing > HARD_LANDING && this.time - b.lastHit > 0.15) {
10144
+ if (landing2 > HARD_LANDING && this.time - b.lastHit > 0.15) {
9938
10145
  b.lastHit = this.time;
9939
- this.o.onHardLanding(b, landPoint, landing);
10146
+ this.o.onHardLanding(b, landPoint, landing2);
9940
10147
  }
9941
10148
  const still = touched && !unstable && Math.hypot(v[0], v[1], v[2]) < 0.12 && Math.hypot(w[0], w[1], w[2]) < 0.45;
9942
10149
  if (still) b.still += dt;
@@ -11416,6 +11623,69 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
11416
11623
  return out;
11417
11624
  };
11418
11625
  var RESTING_HOLD_COS = Math.cos(Math.PI / 6);
11626
+ var assemblies_default = [
11627
+ {
11628
+ id: "brick_wall_plastered",
11629
+ name: "Plastered brick wall",
11630
+ core: { material: "brick", thickness: 0.09 },
11631
+ layers: [{ material: "plaster", thickness: 0.02, faces: "both" }],
11632
+ units: { size: [0.23, 0.075, 0.11], bond: "running", mortar: "plaster" },
11633
+ breach: { area: 0.25 }
11634
+ },
11635
+ {
11636
+ id: "concrete_block_wall",
11637
+ name: "Concrete block wall",
11638
+ core: { material: "concrete", thickness: 0.14 },
11639
+ fill: 0.55,
11640
+ units: { size: [0.39, 0.19, 0.14], bond: "running", mortar: "plaster" },
11641
+ breach: { area: 0.25 }
11642
+ },
11643
+ {
11644
+ id: "reinforced_concrete",
11645
+ name: "Reinforced concrete wall",
11646
+ core: { material: "concrete", thickness: 0.2 },
11647
+ reinforcement: { material: "steel", spacing: 0.18, diameter: 8e-3, residualCapacity: 0.15 },
11648
+ units: { size: [0.6, 0.42, 0.2], bond: "stack", mortar: "concrete" },
11649
+ breach: { area: 0.25 }
11650
+ },
11651
+ {
11652
+ id: "drywall",
11653
+ name: "Drywall partition on steel studs",
11654
+ core: { material: "plaster", thickness: 0.0125 },
11655
+ layers: [{ material: "plaster", thickness: 0.0125, faces: "+" }],
11656
+ depth: 0.095,
11657
+ breach: { area: 0.25 }
11658
+ },
11659
+ {
11660
+ id: "wood_frame_wall",
11661
+ name: "Wood frame wall",
11662
+ core: { material: "plywood", thickness: 0.012 },
11663
+ layers: [{ material: "plaster", thickness: 0.0125, faces: "-" }],
11664
+ depth: 0.1135,
11665
+ breach: { area: 0.25 }
11666
+ },
11667
+ {
11668
+ id: "log_wall",
11669
+ name: "Log wall",
11670
+ core: { material: "wood_soft", thickness: 0.2 },
11671
+ fill: 0.785,
11672
+ breach: { area: 0.25 }
11673
+ },
11674
+ {
11675
+ id: "sheet_metal_shack",
11676
+ name: "Corrugated sheet metal wall",
11677
+ core: { material: "steel", thickness: 6e-4 },
11678
+ depth: 0.02,
11679
+ breach: { area: 0.25 }
11680
+ },
11681
+ {
11682
+ id: "glass_pane",
11683
+ name: "Glass pane",
11684
+ core: { material: "glass", thickness: 6e-3 },
11685
+ breach: { area: 0.1 }
11686
+ }
11687
+ ];
11688
+ var SHIPPED = structuredClone(assemblies_default);
11419
11689
 
11420
11690
  // src/spec.ts
11421
11691
  function containsOf(value) {
@@ -11781,6 +12051,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
11781
12051
  this.b = tmpV(THREE);
11782
12052
  this.m4 = new THREE.Matrix4();
11783
12053
  this.ray = new THREE.Raycaster();
12054
+ this.ray.layers.enableAll();
11784
12055
  this.box = new THREE.Box3();
11785
12056
  this.t = tmpV(THREE);
11786
12057
  this.lbox = new THREE.Box3();
@@ -11788,9 +12059,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
11788
12059
  /**
11789
12060
  * @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`.
11790
12061
  */
11791
- makeMeshVis(mesh2, home, range, hosts) {
12062
+ makeMeshVis(mesh2, home, range2, hosts) {
11792
12063
  let host = null;
11793
- if (range) {
12064
+ if (range2) {
11794
12065
  host = hosts?.get(mesh2) ?? null;
11795
12066
  if (!host) {
11796
12067
  host = { mesh: mesh2, origGeometry: mesh2.geometry, origMaterial: mesh2.material, ownGeo: false, work: null, ownMat: false, sharers: [] };
@@ -11816,8 +12087,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
11816
12087
  bottomY: 0,
11817
12088
  moved: false,
11818
12089
  host,
11819
- range: range ? [range[0], range[1]] : null,
11820
- merged: host && range ? { host, range: [range[0], range[1]] } : null,
12090
+ range: range2 ? [range2[0], range2[1]] : null,
12091
+ merged: host && range2 ? { host, range: [range2[0], range2[1]] } : null,
11821
12092
  loose: false,
11822
12093
  collapsed: false,
11823
12094
  hidden: false,
@@ -12386,24 +12657,24 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
12386
12657
  geo.boundingBox = null;
12387
12658
  geo.boundingSphere = null;
12388
12659
  }
12389
- collapseRange(arr, range) {
12390
- const x = arr[range[0] * 3];
12391
- const y = arr[range[0] * 3 + 1];
12392
- const z = arr[range[0] * 3 + 2];
12393
- for (let i = range[0] * 3; i < range[1] * 3; i += 3) {
12660
+ collapseRange(arr, range2) {
12661
+ const x = arr[range2[0] * 3];
12662
+ const y = arr[range2[0] * 3 + 1];
12663
+ const z = arr[range2[0] * 3 + 2];
12664
+ for (let i = range2[0] * 3; i < range2[1] * 3; i += 3) {
12394
12665
  arr[i] = x;
12395
12666
  arr[i + 1] = y;
12396
12667
  arr[i + 2] = z;
12397
12668
  }
12398
12669
  }
12399
12670
  /** Melt of one range: shorter in Y (down to 25 %) with the bottom fixed, a bit wider around its centre, with the same noise as a whole mesh. */
12400
- meltRange(arr, work, range, melt) {
12671
+ meltRange(arr, work, range2, melt) {
12401
12672
  let minY = Infinity;
12402
12673
  let maxY = -Infinity;
12403
12674
  let cx = 0;
12404
12675
  let cz = 0;
12405
- const n = Math.max(1, range[1] - range[0]);
12406
- for (let i = range[0] * 3; i < range[1] * 3; i += 3) {
12676
+ const n = Math.max(1, range2[1] - range2[0]);
12677
+ for (let i = range2[0] * 3; i < range2[1] * 3; i += 3) {
12407
12678
  minY = Math.min(minY, work[i + 1]);
12408
12679
  maxY = Math.max(maxY, work[i + 1]);
12409
12680
  cx += work[i];
@@ -12414,7 +12685,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
12414
12685
  const h = Math.max(1e-4, maxY - minY);
12415
12686
  const sy = lerp5(1, 0.25, melt);
12416
12687
  const sxz = 1 + 0.18 * melt;
12417
- for (let i = range[0] * 3, v = 0; i < range[1] * 3; i += 3, v++) {
12688
+ for (let i = range2[0] * 3, v = 0; i < range2[1] * 3; i += 3, v++) {
12418
12689
  const noise = (Math.imul(v + 1, 2654435761) >>> 0) % 1e3 / 1e3;
12419
12690
  arr[i] = cx + (work[i] - cx) * sxz;
12420
12691
  arr[i + 2] = cz + (work[i + 2] - cz) * sxz;
@@ -12771,8 +13042,26 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
12771
13042
  __publicField(this, "queue", []);
12772
13043
  /** Presentation work an event left for later frames (fragments still to spawn, loose groups still to split), run in order within the frame budget. */
12773
13044
  __publicField(this, "slices", []);
12774
- /** When the work of this frame must stop; `Infinity` outside `drainQueue`, where a call finishes what it started. */
13045
+ /** When the work of this frame must stop; `Infinity` outside a drain, where a call finishes what it started. */
12775
13046
  __publicField(this, "sliceDeadline", Infinity);
13047
+ /** The clock the drain reads; a budget lends its own while it runs the queue. */
13048
+ __publicField(this, "drainClock", () => performance.now());
13049
+ /** true once a frame budget owns the draining: `update` no longer drains by itself. */
13050
+ __publicField(this, "attached", false);
13051
+ __publicField(this, "eventQueue", {
13052
+ id: DEFERRED_QUEUE_ID.DESTRUCTION_EVENTS,
13053
+ pending: () => this.queue.length + this.slices.length,
13054
+ run: (deadline, clock) => this.runBudgeted(deadline, clock),
13055
+ flush: () => {
13056
+ for (let guard = 0; guard < 16 && this.queue.length + this.slices.length > 0; guard++) this.runBudgeted(Infinity, () => 0);
13057
+ },
13058
+ attach: () => {
13059
+ this.attached = true;
13060
+ },
13061
+ detach: () => {
13062
+ this.attached = false;
13063
+ }
13064
+ });
12776
13065
  __publicField(this, "unsubscribe");
12777
13066
  __publicField(this, "visuals");
12778
13067
  __publicField(this, "fragments");
@@ -12818,6 +13107,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
12818
13107
  const maxParticles = Math.max(8, Math.floor((o.maxParticles ?? 2500) * (this.low ? 0.5 : 1)));
12819
13108
  this.visuals = new Visuals(THREE, this.low, 600);
12820
13109
  this.ground = new GroundSpots(THREE, o.scene, this.groundAt, 48);
13110
+ if (!o.vfx) warnOnce("game-destruction-three.own-effects-world", "game-destruction-three made an effects world of its own because the host lent none (options.vfx); pass the host effects so the effects share one budget");
12821
13111
  this.vfx = o.vfx ?? createVfx({ profile: this.quality, gravity: this.gravity, groundAt: this.groundAt, sprites: { capacity: maxParticles, density: this.low ? 0.6 : 1 } });
12822
13112
  this.ownVfx = o.vfx ? void 0 : createVfxRenderer(THREE, { scene: o.scene, camera: o.camera ?? new THREE.PerspectiveCamera(), vfx: this.vfx, pointScale: o.pointScale ?? 700 });
12823
13113
  this.stopLanding = this.vfx.onLand(this.owner, (x, _y, z, color, tag) => {
@@ -13055,7 +13345,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
13055
13345
  if (this.disposed) return;
13056
13346
  dt = clamp6(dt, 0, 0.1);
13057
13347
  this.time += dt;
13058
- this.drainQueue();
13348
+ if (!this.attached) this.drainQueue();
13059
13349
  for (const b of this.bounds.values()) b.onFrame?.(dt);
13060
13350
  for (const b of this.bounds.values()) if (b.soilNode && this.world.state(b.id)?.soil?.status === "yielding") this.onSettle(b.id);
13061
13351
  this.updateActive(dt);
@@ -13066,36 +13356,50 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
13066
13356
  this.ownVfx?.update(dt);
13067
13357
  this.syncTransforms();
13068
13358
  }
13359
+ deferredQueue() {
13360
+ return this.eventQueue;
13361
+ }
13069
13362
  drainQueue() {
13363
+ this.runBudgeted(performance.now() + this.eventBudgetMs, () => performance.now());
13364
+ }
13365
+ /** Applies the waiting events in order until the deadline, after the slices earlier events left; always at least one unit when something waits. Returns the units run. */
13366
+ runBudgeted(deadline, clock) {
13070
13367
  const batch = this.queue.splice(0, this.queue.length);
13071
- const deadline = performance.now() + this.eventBudgetMs;
13368
+ const outerClock = this.drainClock;
13369
+ this.drainClock = clock;
13072
13370
  this.sliceDeadline = deadline;
13371
+ let ran = 0;
13073
13372
  try {
13074
- this.runSlices(deadline);
13373
+ ran = this.runSlices(deadline);
13075
13374
  for (let i = 0; i < batch.length; i++) {
13076
- if (i > 0 && performance.now() >= deadline) {
13375
+ if ((i > 0 || ran > 0 && this.attached) && clock() >= deadline) {
13077
13376
  this.queue.unshift(...batch.slice(i));
13078
- return;
13377
+ return ran;
13079
13378
  }
13080
13379
  try {
13081
13380
  this.apply(batch[i]);
13082
13381
  } catch (err) {
13083
13382
  if (typeof console !== "undefined") console.error("game-destruction-three: failed to apply", batch[i].type, err);
13084
13383
  }
13384
+ ran++;
13085
13385
  }
13086
13386
  } finally {
13087
13387
  this.sliceDeadline = Infinity;
13388
+ this.drainClock = outerClock;
13088
13389
  }
13390
+ return ran;
13089
13391
  }
13090
13392
  /** Runs the work left by earlier frames until the deadline; at least one piece per frame, so it always finishes. */
13091
13393
  runSlices(deadline) {
13092
- for (let ran = 0; this.slices.length > 0 && (ran === 0 || performance.now() < deadline); ran++) {
13394
+ let ran = 0;
13395
+ for (; this.slices.length > 0 && (ran === 0 || this.drainClock() < deadline); ran++) {
13093
13396
  try {
13094
13397
  this.slices.shift()();
13095
13398
  } catch (err) {
13096
13399
  if (typeof console !== "undefined") console.error("game-destruction-three: failed to run deferred work", err);
13097
13400
  }
13098
13401
  }
13402
+ return ran;
13099
13403
  }
13100
13404
  /**
13101
13405
  * The ground under an object settled or tilted: its parts are drawn under a node with the soil's offset (`ObjectState.soil.offset`), so the host's
@@ -13653,7 +13957,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
13653
13957
  const maxAlive = this.low ? Math.floor(this.maxFragments / 2) : this.maxFragments;
13654
13958
  const step = this.low ? 2 : 1;
13655
13959
  for (let i = from; i < ev.fragments.length; i += step) {
13656
- if (i - from >= MIN_FRAGMENTS_PER_SLICE * step && performance.now() >= this.sliceDeadline) {
13960
+ if (i - from >= MIN_FRAGMENTS_PER_SLICE * step && this.drainClock() >= this.sliceDeadline) {
13657
13961
  this.slices.push(() => this.spawnFragments(ev, i, look));
13658
13962
  return;
13659
13963
  }
@@ -13677,7 +13981,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
13677
13981
  if (!st || st.groups.length < 2) return;
13678
13982
  const loose = st.groups.length - 1;
13679
13983
  for (let i = 0; i < loose; i++) {
13680
- if (i > 0 && performance.now() >= this.sliceDeadline) {
13984
+ if (i > 0 && this.drainClock() >= this.sliceDeadline) {
13681
13985
  this.slices.push(() => {
13682
13986
  this.staticDirty.add(objectId);
13683
13987
  this.splitLoose(objectId);
@@ -14056,6 +14360,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
14056
14360
  const materials = [kit.discMaterial().clone(), kit.patchMaterial(8421504).clone(), kit.lineMaterial(0).clone()];
14057
14361
  const group = new T.Group();
14058
14362
  group.add(new T.Mesh(kit.discGeometry(), materials[0]), new T.Mesh(kit.discGeometry(), materials[1]), new T.LineSegments(geometry, materials[2]));
14363
+ group.add(...this.fragments.warmMeshes());
14059
14364
  for (const probe of group.children) probe.frustumCulled = false;
14060
14365
  this.warmProbes = { group, geometry, materials };
14061
14366
  return { group, dispose: () => {
@@ -14086,7 +14391,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
14086
14391
  }
14087
14392
  };
14088
14393
 
14089
- // ../game-audio/dist/index.js
14394
+ // ../game-audio/dist/chunk-DZITVFMJ.js
14090
14395
  var DISTANCE_CURVES = {
14091
14396
  /** The game's `sfx`: `vol / (1 + d² × 0.004)`, cutoff `/ (1 + d × 0.01)`, silent below 0.01 (jogo L62091–62092, L62101). */
14092
14397
  game_sfx: { model: "inverse_square", k: 4e-3, cutoffK: 0.01, minGain: 0.01 },
@@ -14101,31 +14406,6 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
14101
14406
  /** The game's animal calls: `max(0, 1 − d / 70) × 0.25`, silent at or below 0.005 (jogo L66627–66628). */
14102
14407
  animals: { model: "linear", range: 70, scale: 0.25, minGain: 5e-3 }
14103
14408
  };
14104
- var mix_profiles_default = [
14105
- {
14106
- id: "game",
14107
- master: 0.5,
14108
- noise: { seconds: 1.5, create: "lazy" },
14109
- sources: ["jogo:L62077-62081 master 0.5 to the output", "jogo:L62082-62088 noise 1.5 s made by the first sound that needs it"]
14110
- },
14111
- {
14112
- id: "art_scene",
14113
- master: 0.85,
14114
- compressor: { threshold: -12, ratio: 5 },
14115
- reverb: { seconds: 2.2, decay: 3.4, wet: 0.4 },
14116
- noise: { seconds: 2, create: "eager" },
14117
- sources: [
14118
- "cena:L1308 compressor -12 dB, ratio 5",
14119
- "cena:L1309 master 0.85",
14120
- "cena:L1310 convolver, impulse 2.2 s of (1 - i/len)^3.4",
14121
- "cena:L1311 wet 0.4",
14122
- "cena:L1312 noise 2 s made with the chain"
14123
- ]
14124
- }
14125
- ];
14126
- var MIX_PROFILES = Object.fromEntries(
14127
- mix_profiles_default.map((profile) => [profile.id, profile])
14128
- );
14129
14409
  var game_sfx_default = {
14130
14410
  sources: ["jogo:L62089-62107"],
14131
14411
  note: "The game's sfx table P: [lowpass cutoff Hz, peak gain, seconds]. Played as a noise burst with the game's distance curve (falloff game_sfx) and a 0.05 s tail. Team names in `team`.",
@@ -14170,6 +14450,78 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
14170
14450
  };
14171
14451
  }
14172
14452
  var GAME_SFX_BANK = { id: "game_sfx", sounds: [...GAME_SFX_CLASSES, GAME_SFX_FALLBACK].map((row) => noiseClassRecipe(row)) };
14453
+ var mix_profiles_default = [
14454
+ {
14455
+ id: "game",
14456
+ master: 0.5,
14457
+ noise: { seconds: 1.5, create: "lazy" },
14458
+ sources: ["jogo:L62077-62081 master 0.5 to the output", "jogo:L62082-62088 noise 1.5 s made by the first sound that needs it"]
14459
+ },
14460
+ {
14461
+ id: "art_scene",
14462
+ master: 0.85,
14463
+ compressor: { threshold: -12, ratio: 5 },
14464
+ reverb: { seconds: 2.2, decay: 3.4, wet: 0.4 },
14465
+ noise: { seconds: 2, create: "eager" },
14466
+ sources: [
14467
+ "cena:L1308 compressor -12 dB, ratio 5",
14468
+ "cena:L1309 master 0.85",
14469
+ "cena:L1310 convolver, impulse 2.2 s of (1 - i/len)^3.4",
14470
+ "cena:L1311 wet 0.4",
14471
+ "cena:L1312 noise 2 s made with the chain"
14472
+ ]
14473
+ }
14474
+ ];
14475
+ var MIX_PROFILES = Object.fromEntries(
14476
+ mix_profiles_default.map((profile) => [profile.id, profile])
14477
+ );
14478
+ var BENCH_BANK = {
14479
+ id: "audio_bench",
14480
+ sounds: [
14481
+ {
14482
+ id: "bench_ping",
14483
+ bus: "ui",
14484
+ pan: true,
14485
+ duration: 0.25,
14486
+ layers: [
14487
+ {
14488
+ gain: 0.5,
14489
+ wet: 0.3,
14490
+ source: { kind: "osc", wave: "sine", frequency: { from: 880 } },
14491
+ envelope: [
14492
+ [0, 1e-4, "set", "fixed"],
14493
+ [2e-3, 1, "exp"],
14494
+ ["1d", 1e-4, "exp", "fixed"]
14495
+ ]
14496
+ }
14497
+ ],
14498
+ falloff: DISTANCE_CURVES.art_scene,
14499
+ sources: ["sdk: bench probe, a 880 Hz ping through the art scene output stage"]
14500
+ },
14501
+ {
14502
+ id: "bench_hiss",
14503
+ bus: "ui",
14504
+ pan: true,
14505
+ duration: [0.3, 0.9],
14506
+ layers: [
14507
+ {
14508
+ gain: 0.4,
14509
+ source: { kind: "noise", offset: "random", offsetScale: 1.5, loop: true },
14510
+ filters: { mode: "serial", bank: [{ type: "highpass", frequency: { set: 3e3 }, q: 0.7 }] },
14511
+ envelope: [
14512
+ [0, 1e-4, "set", "fixed"],
14513
+ [2e-3, 1, "exp"],
14514
+ ["1d", 1e-4, "exp", "fixed"]
14515
+ ]
14516
+ }
14517
+ ],
14518
+ falloff: DISTANCE_CURVES.art_scene,
14519
+ sources: ["sdk: bench probe, a highpass hiss of random length"]
14520
+ }
14521
+ ]
14522
+ };
14523
+
14524
+ // ../game-audio/dist/index.js
14173
14525
  var ATTACK = 2e-3;
14174
14526
  function lengthOf(d, attack) {
14175
14527
  if (typeof d === "number") return attack + d;
@@ -14207,6 +14559,31 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
14207
14559
  envelope: envelope(ATTACK)
14208
14560
  };
14209
14561
  }
14562
+ function sceneVoice(o) {
14563
+ return {
14564
+ duration: lengthOf(o.d, 0),
14565
+ gain: o.vol,
14566
+ wet: 0.45,
14567
+ source: {
14568
+ kind: "formant_voice",
14569
+ wave: o.wave ?? "sawtooth",
14570
+ frequency: { from: o.f0, to: o.f1, curve: "linear", over: "duration" },
14571
+ vibrato: { rate: [4, 10], depth: 0.08 },
14572
+ formants: [
14573
+ { frequency: o.fA, q: 4 },
14574
+ { frequency: o.fB, q: 6 }
14575
+ ],
14576
+ breath: { frequency: o.fB, q: 1.1, gain: o.noise },
14577
+ mix: 3.2
14578
+ },
14579
+ envelope: [
14580
+ [0, 1e-4, "set", "fixed"],
14581
+ [0.1, 1, "exp"],
14582
+ ["0.6d", 1, "set"],
14583
+ ["1d", 1e-4, "exp", "fixed"]
14584
+ ]
14585
+ };
14586
+ }
14210
14587
  var sceneMany = (count, make) => Array.from({ length: count }, make);
14211
14588
  function sceneRecipe(id, layers, line, options = {}) {
14212
14589
  return {
@@ -14220,51 +14597,172 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
14220
14597
  sources: [`cena:${line}`]
14221
14598
  };
14222
14599
  }
14223
- var BENCH_BANK = {
14224
- id: "audio_bench",
14225
- sounds: [
14226
- {
14227
- id: "bench_ping",
14228
- bus: "ui",
14229
- pan: true,
14230
- duration: 0.25,
14231
- layers: [
14232
- {
14233
- gain: 0.5,
14234
- wet: 0.3,
14235
- source: { kind: "osc", wave: "sine", frequency: { from: 880 } },
14236
- envelope: [
14237
- [0, 1e-4, "set", "fixed"],
14238
- [2e-3, 1, "exp"],
14239
- ["1d", 1e-4, "exp", "fixed"]
14240
- ]
14241
- }
14242
- ],
14243
- falloff: DISTANCE_CURVES.art_scene,
14244
- sources: ["sdk: bench probe, a 880 Hz ping through the art scene output stage"]
14245
- },
14246
- {
14247
- id: "bench_hiss",
14248
- bus: "ui",
14249
- pan: true,
14250
- duration: [0.3, 0.9],
14251
- layers: [
14252
- {
14253
- gain: 0.4,
14254
- source: { kind: "noise", offset: "random", offsetScale: 1.5, loop: true },
14255
- filters: { mode: "serial", bank: [{ type: "highpass", frequency: { set: 3e3 }, q: 0.7 }] },
14256
- envelope: [
14257
- [0, 1e-4, "set", "fixed"],
14258
- [2e-3, 1, "exp"],
14259
- ["1d", 1e-4, "exp", "fixed"]
14260
- ]
14261
- }
14262
- ],
14263
- falloff: DISTANCE_CURVES.art_scene,
14264
- sources: ["sdk: bench probe, a highpass hiss of random length"]
14265
- }
14266
- ]
14600
+ var TAKES = [
14601
+ { f: 1, t: 1, v: 1 },
14602
+ { f: 1.08, t: 0.9, v: 0.92 },
14603
+ { f: 0.93, t: 1.12, v: 1.05 },
14604
+ { f: 1.03, t: 1.05, v: 0.88 }
14605
+ ];
14606
+ var FOOTSTEP_FALLOFF = { model: "inverse_square", k: 0.03, cutoffK: 0.03, minGain: 4e-3 };
14607
+ var BODY_FALLOFF = { model: "inverse_square", k: 0.012, cutoffK: 0.015, minGain: 4e-3 };
14608
+ var SURFACES = {
14609
+ concrete: { heel: { f: 95, d: 0.05, vol: 0.55 }, roll: { type: "bandpass", f: 2300, q: 1.1, d: 0.05, vol: 0.38 }, grains: { count: 2, f: 4200, q: 2, spread: 0.04, vol: 0.08 } },
14610
+ asphalt: { heel: { f: 90, d: 0.05, vol: 0.5 }, roll: { type: "bandpass", f: 1700, q: 0.9, d: 0.06, vol: 0.36 }, grains: { count: 4, f: 3600, q: 2.5, spread: 0.06, vol: 0.1 } },
14611
+ wood: { heel: { f: 130, d: 0.08, vol: 0.55 }, roll: { type: "lowpass", f: 1300, q: 0.8, d: 0.06, vol: 0.3 }, ring: [{ f: 210, d: 0.1, vol: 0.18 }, { f: 470, d: 0.06, vol: 0.06 }] },
14612
+ tile: { heel: { f: 110, d: 0.04, vol: 0.45 }, roll: { type: "bandpass", f: 3600, q: 2, d: 0.03, vol: 0.42 }, ring: [{ f: 1900, d: 0.03, vol: 0.05 }] },
14613
+ dirt: { heel: { f: 75, d: 0.06, vol: 0.45 }, roll: { type: "lowpass", f: 750, q: 0.7, d: 0.09, vol: 0.4 }, grains: { count: 3, f: 1800, q: 1.5, spread: 0.07, vol: 0.06 } },
14614
+ grass: { heel: { f: 70, d: 0.05, vol: 0.32 }, roll: { type: "highpass", f: 2600, q: 0.7, d: 0.13, vol: 0.28 }, grains: { count: 4, f: 5200, q: 1.2, spread: 0.1, vol: 0.07 } },
14615
+ gravel: { heel: { f: 80, d: 0.05, vol: 0.4 }, roll: { type: "lowpass", f: 950, q: 0.8, d: 0.1, vol: 0.3 }, grains: { count: 8, f: 2600, q: 3, spread: 0.13, vol: 0.2 } },
14616
+ sand: { heel: { f: 65, d: 0.06, vol: 0.3 }, roll: { type: "bandpass", f: 1500, q: 0.6, d: 0.16, vol: 0.3 }, grains: { count: 5, f: 4800, q: 1.5, spread: 0.12, vol: 0.05 } },
14617
+ metal: { heel: { f: 120, d: 0.05, vol: 0.5 }, roll: { type: "bandpass", f: 2800, q: 1.5, d: 0.03, vol: 0.25 }, ring: [{ f: 420, d: 0.25, vol: 0.16 }, { f: 1130, d: 0.18, vol: 0.09 }, { f: 2470, d: 0.1, vol: 0.04 }] },
14618
+ water: { heel: { f: 70, d: 0.04, vol: 0.25 }, roll: { type: "bandpass", f: 3e3, q: 1.2, d: 0.1, vol: 0.18 }, splash: { f: 1100, f2: 380, d: 0.22, vol: 0.55 }, grains: { count: 4, f: 2400, q: 6, spread: 0.18, vol: 0.12 } },
14619
+ glass: { heel: { f: 90, d: 0.04, vol: 0.4 }, roll: { type: "highpass", f: 4500, q: 0.8, d: 0.06, vol: 0.2 }, grains: { count: 9, f: 6200, q: 5, spread: 0.12, vol: 0.22 }, ring: [{ f: 3900, d: 0.04, vol: 0.04 }, { f: 5300, d: 0.03, vol: 0.03 }] },
14620
+ carpet: { heel: { f: 70, d: 0.05, vol: 0.32 }, roll: { type: "lowpass", f: 420, q: 0.7, d: 0.07, vol: 0.3 } }
14621
+ };
14622
+ var GAITS = {
14623
+ walk: { vol: 1, bright: 1, toe: 0.075, short: 1 },
14624
+ run: { vol: 1.35, bright: 1.25, toe: 0.035, short: 0.75 }
14625
+ };
14626
+ var grainLayers = (g, take, start, scale) => Array.from(
14627
+ { length: g.count },
14628
+ (_, i) => sceneNoise({ type: "bandpass", f: [g.f * take.f * 0.85, g.f * take.f * 1.15], q: g.q, d: [8e-3, 0.02], vol: g.vol * scale * take.v, delay: start + (i + 0.5) / g.count * g.spread * take.t })
14629
+ );
14630
+ var ringLayers = (rings, take, scale, at3 = 0) => rings.map((r) => sceneTone({ f: r.f * take.f, d: r.d * take.t, vol: r.vol * scale * take.v, delay: at3 }));
14631
+ function footLayers(surface, take, gait, weight = 1) {
14632
+ const s = SURFACES[surface];
14633
+ const toe = gait.toe * take.t;
14634
+ return [
14635
+ sceneTone({ f: s.heel.f * take.f * weight ** -0.3, f2: s.heel.f * take.f * 0.6, d: s.heel.d * gait.short * take.t * weight ** 0.5, vol: s.heel.vol * gait.vol * take.v * weight ** 0.5 }),
14636
+ sceneNoise({ type: "lowpass", f: 500 * gait.bright, d: 0.02 * take.t, vol: 0.18 * gait.vol * take.v * weight ** 0.5 }),
14637
+ sceneNoise({ type: s.roll.type, f: [s.roll.f * take.f * gait.bright * 0.92, s.roll.f * take.f * gait.bright * 1.08], q: s.roll.q, d: s.roll.d * gait.short * take.t * weight ** 0.3, vol: s.roll.vol * gait.vol * take.v, delay: toe }),
14638
+ ...s.grains ? grainLayers(s.grains, take, toe * 0.5, gait.vol * weight ** 0.5) : [],
14639
+ ...s.ring ? ringLayers(s.ring, take, gait.vol * weight ** 0.5) : [],
14640
+ ...s.splash ? [sceneNoise({ type: "bandpass", f: s.splash.f * take.f, f2: s.splash.f2 * take.f, q: 1.4, d: s.splash.d * take.t * weight ** 0.4, vol: s.splash.vol * gait.vol * take.v * weight ** 0.5 })] : []
14641
+ ];
14642
+ }
14643
+ var quieter = (level, layers) => layers.map((layer2) => {
14644
+ const gain = layer2.gain ?? 1;
14645
+ if (typeof gain === "number") return { ...layer2, gain: gain * level };
14646
+ if (Array.isArray(gain)) return { ...layer2, gain: [gain[0] * level, gain[1] * level] };
14647
+ return layer2;
14648
+ });
14649
+ var recipe = (id, layers, falloff, bus = "world") => ({ id, bus, pan: true, layers, falloff, sources: ["sdk"] });
14650
+ var takeId = (id, index) => `${id}_${String(index + 1).padStart(2, "0")}`;
14651
+ function takes(id, layersOf, falloff, rate = [0.96, 1.04], bus) {
14652
+ const sounds = TAKES.map((take, index) => recipe(takeId(id, index), layersOf(take, index), falloff, bus));
14653
+ return { sounds, set: { id, variants: sounds.map((sound) => sound.id), rate } };
14654
+ }
14655
+ var footstep = (surface, gait) => takes(footstepSoundId(surface, gait), (take) => footLayers(surface, take, GAITS[gait]), FOOTSTEP_FALLOFF);
14656
+ var LANDINGS = { light: { weight: 1.4, cloth: 0.12 }, heavy: { weight: 2.4, cloth: 0.22 } };
14657
+ var LANDING_LEVEL = 0.65;
14658
+ var landing = (surface, landing2) => takes(
14659
+ `land_${surface}_${landing2}`,
14660
+ (take) => quieter(LANDING_LEVEL, [
14661
+ // both feet, a hair apart, then the knees taking the weight (a low body thump) and the clothes settling
14662
+ ...footLayers(surface, take, GAITS.run, LANDINGS[landing2].weight),
14663
+ ...footLayers(surface, { ...take, v: take.v * 0.6 }, GAITS.run, LANDINGS[landing2].weight * 0.6).map((layer2) => ({ ...layer2, at: (typeof layer2.at === "number" ? layer2.at : 0) + 0.018 * take.t })),
14664
+ sceneTone({ f: 60 * take.f, f2: 38, d: 0.14 * LANDINGS[landing2].weight * take.t, vol: 0.22 * LANDINGS[landing2].weight ** 0.7 * take.v, delay: 0.02 }),
14665
+ sceneNoise({ type: "bandpass", f: [1800, 2600], q: 0.8, d: 0.18, vol: LANDINGS[landing2].cloth * take.v, delay: 0.04 })
14666
+ ]),
14667
+ FOOTSTEP_FALLOFF
14668
+ );
14669
+ var whoosh = (blow3, from, to, d, vol, q = 1.6) => (take) => [
14670
+ // the air: a band sweeping up as the limb speeds up; it peaks `SWING_LEAD_SECONDS` in, the moment before the contact, then dies away
14671
+ sceneNoise({ type: "bandpass", f: from * take.f, f2: to * take.f, q, a: SWING_LEAD_SECONDS[blow3], d: d * take.t, vol: vol * take.v }),
14672
+ sceneNoise({ type: "lowpass", f: 600 * take.f, a: SWING_LEAD_SECONDS[blow3], d: d * take.t * 0.8, vol: vol * 0.35 * take.v })
14673
+ ];
14674
+ var fleshHit = (o) => (take) => [
14675
+ sceneNoise({ type: "bandpass", f: o.slapF * take.f, q: 1.2, d: 0.03 * take.t, vol: o.slap * take.v }),
14676
+ sceneNoise({ type: "highpass", f: 3500 * take.f, d: 0.012, vol: o.slap * 0.4 * take.v }),
14677
+ sceneTone({ f: o.thudF * take.f, f2: o.thudF * 0.5, d: o.thudD * take.t, vol: o.thud * take.v }),
14678
+ sceneNoise({ type: "lowpass", f: 600 * take.f, d: o.thudD * 1.2 * take.t, vol: o.thud * 0.5 * take.v }),
14679
+ ...o.crack ? [sceneNoise({ type: "highpass", f: 2500, d: 0.015, vol: o.crack * take.v, delay: 6e-3 }), sceneTone({ wave: "square", f: 900 * take.f, f2: 300, d: 0.02, vol: o.crack * 0.15 * take.v, delay: 6e-3 })] : [],
14680
+ ...o.wet ? [sceneNoise({ type: "bandpass", f: 1400 * take.f, f2: 500, q: 2.5, d: 0.12 * take.t, vol: o.wet * take.v, delay: 0.01 })] : []
14681
+ ];
14682
+ var knock = (o) => (take) => [
14683
+ sceneNoise({ type: "bandpass", f: o.clickF * take.f, q: 1.5, d: 0.018, vol: o.click * take.v }),
14684
+ ...o.tones.map(([f, d, vol]) => sceneTone({ f: f * take.f, d: d * take.t, vol: vol * take.v }))
14685
+ ];
14686
+ var voice = (o) => (take) => [
14687
+ sceneVoice({ f0: o.f0 * take.f, f1: o.f1 * take.f, d: o.d * take.t, fA: o.fA * take.f, fB: o.fB * take.f, noise: o.noise, vol: o.vol * take.v })
14688
+ ];
14689
+ var BODY = {
14690
+ jump: (take) => [
14691
+ sceneNoise({ type: "bandpass", f: 900 * take.f, f2: 1700 * take.f, q: 1, a: 0.03, d: 0.12, vol: 0.18 * take.v }),
14692
+ sceneNoise({ type: "bandpass", f: 2200 * take.f, q: 0.8, d: 0.04, vol: 0.12 * take.v }),
14693
+ sceneTone({ f: 85 * take.f, f2: 60, d: 0.04, vol: 0.2 * take.v })
14694
+ ],
14695
+ cloth_rustle: (take) => [
14696
+ { ...sceneNoise({ type: "bandpass", f: 2400 * take.f, q: 0.8, a: 0.06, d: 0.22 * take.t, vol: 0.12 * take.v }), modulators: [{ target: "gain", wave: "triangle", rate: [9, 14], depth: 0.5 }] }
14697
+ ],
14698
+ body_fall: (take) => [
14699
+ sceneTone({ f: 58 * take.f, f2: 35, d: 0.28 * take.t, vol: 0.9 * take.v }),
14700
+ sceneNoise({ type: "lowpass", f: 480 * take.f, d: 0.3 * take.t, vol: 0.55 * take.v }),
14701
+ sceneNoise({ type: "bandpass", f: 1300 * take.f, q: 1, d: 0.05, vol: 0.3 * take.v }),
14702
+ // the limbs a moment later, and the clothes
14703
+ sceneTone({ f: 75 * take.f, f2: 45, d: 0.12, vol: 0.4 * take.v, delay: 0.13 * take.t }),
14704
+ sceneNoise({ type: "bandpass", f: 2200, q: 0.8, d: 0.2, vol: 0.12 * take.v, delay: 0.05 })
14705
+ ],
14706
+ fall_damage: (take) => [
14707
+ ...BODY.body_fall(take),
14708
+ sceneNoise({ type: "highpass", f: 2800, d: 0.02, vol: 0.35 * take.v, delay: 0.01 }),
14709
+ ...voice({ f0: 170, f1: 110, d: 0.32, fA: 650, fB: 1150, noise: 0.25, vol: 0.32 })(take).map((layer2) => ({ ...layer2, at: 0.08 }))
14710
+ ],
14711
+ player_hurt: voice({ f0: 150, f1: 105, d: 0.26, fA: 620, fB: 1100, noise: 0.3, vol: 0.35 }),
14712
+ player_breath_run: (take) => [
14713
+ sceneNoise({ type: "bandpass", f: 1200 * take.f, q: 1, a: 0.08, d: 0.22 * take.t, vol: 0.25 * take.v }),
14714
+ sceneNoise({ type: "bandpass", f: 900 * take.f, q: 1, a: 0.06, d: 0.26 * take.t, vol: 0.3 * take.v, delay: 0.3 * take.t })
14715
+ ],
14716
+ player_death: voice({ f0: 135, f1: 68, d: 1, fA: 560, fB: 980, noise: 0.35, vol: 0.4 }),
14717
+ bite: (take) => [
14718
+ sceneNoise({ type: "highpass", f: 2600, d: 0.015, vol: 0.4 * take.v }),
14719
+ sceneNoise({ type: "bandpass", f: 1600 * take.f, f2: 450, q: 2.2, d: 0.16 * take.t, vol: 0.55 * take.v, delay: 0.01 }),
14720
+ sceneNoise({ type: "bandpass", f: 3200 * take.f, f2: 1800, q: 1.2, d: 0.22 * take.t, vol: 0.25 * take.v, delay: 0.09 }),
14721
+ sceneTone({ f: 120 * take.f, f2: 70, d: 0.06, vol: 0.3 * take.v })
14722
+ ],
14723
+ grab: (take) => [
14724
+ sceneNoise({ type: "bandpass", f: 1900 * take.f, q: 0.9, a: 4e-3, d: 0.13 * take.t, vol: 0.32 * take.v }),
14725
+ sceneTone({ f: 95 * take.f, f2: 60, d: 0.07, vol: 0.3 * take.v }),
14726
+ sceneNoise({ type: "lowpass", f: 700, d: 0.08, vol: 0.2 * take.v })
14727
+ ],
14728
+ punch_swing: whoosh("punch", 450, 1500, 0.16, 0.9),
14729
+ punch_hit_body: fleshHit({ slap: 0.55, slapF: 1500, thud: 0.6, thudF: 115, thudD: 0.09 }),
14730
+ punch_hit_face: fleshHit({ slap: 0.75, slapF: 2300, thud: 0.45, thudF: 150, thudD: 0.07, crack: 0.25 }),
14731
+ punch_hit_object: knock({ clickF: 1800, click: 0.3, tones: [[210, 0.04, 0.25]] }),
14732
+ kick_swing: whoosh("kick", 280, 950, 0.22, 1, 1.3),
14733
+ kick_hit: fleshHit({ slap: 0.45, slapF: 1100, thud: 0.85, thudF: 82, thudD: 0.14 }),
14734
+ kick_hit_object: knock({ clickF: 1e3, click: 0.3, tones: [[92, 0.08, 0.5]] }),
14735
+ shove_two_hands: (take) => [
14736
+ sceneNoise({ type: "bandpass", f: 950 * take.f, q: 0.9, a: 0.01, d: 0.13 * take.t, vol: 0.38 * take.v }),
14737
+ sceneTone({ f: 100 * take.f, f2: 60, d: 0.1 * take.t, vol: 0.45 * take.v }),
14738
+ sceneNoise({ type: "bandpass", f: 2300, q: 0.8, d: 0.16, vol: 0.12 * take.v, delay: 0.03 })
14739
+ ],
14740
+ knife_swing: whoosh("blade", 1900, 4600, 0.12, 1.1, 2.2),
14741
+ knife_stab: fleshHit({ slap: 0.2, slapF: 1200, thud: 0.4, thudF: 160, thudD: 0.06, wet: 0.5 }),
14742
+ knife_slash: (take) => [
14743
+ sceneNoise({ type: "highpass", f: 3200 * take.f, f2: 1800, d: 0.13 * take.t, vol: 0.35 * take.v }),
14744
+ sceneNoise({ type: "bandpass", f: 1300 * take.f, f2: 600, q: 2.5, d: 0.12 * take.t, vol: 0.35 * take.v, delay: 0.02 })
14745
+ ],
14746
+ knife_hit_object: knock({ clickF: 3200, click: 0.25, tones: [[2800, 0.08, 0.07], [4150, 0.06, 0.05]] }),
14747
+ blunt_swing: whoosh("blunt", 220, 760, 0.28, 1.1, 1.5),
14748
+ blunt_hit: fleshHit({ slap: 0.35, slapF: 900, thud: 1, thudF: 70, thudD: 0.18, crack: 0.3 }),
14749
+ blunt_hit_object: knock({ clickF: 1400, click: 0.35, tones: [[300, 0.12, 0.25], [760, 0.08, 0.12]] }),
14750
+ axe_swing: whoosh("chop", 190, 620, 0.3, 1.2, 1.4),
14751
+ axe_chop: fleshHit({ slap: 0.3, slapF: 1400, thud: 0.75, thudF: 140, thudD: 0.1, crack: 0.35, wet: 0.45 }),
14752
+ axe_hit_object: knock({ clickF: 2200, click: 0.4, tones: [[1500, 0.15, 0.1], [2300, 0.1, 0.06], [140, 0.1, 0.35]] })
14753
+ };
14754
+ var BODY_BUS = { player_hurt: "voices", player_death: "voices", player_breath_run: "voices", fall_damage: "voices" };
14755
+ var built = [
14756
+ ...SURFACE_IDS.flatMap((surface) => GAIT_IDS.map((gait) => footstep(surface, gait))),
14757
+ ...SURFACE_IDS.flatMap((surface) => LANDING_IDS.map((l) => landing(surface, l))),
14758
+ ...BODY_SOUND_IDS.map((id) => takes(id, BODY[id], BODY_FALLOFF, id.endsWith("_swing") ? [0.94, 1.06] : [0.96, 1.04], BODY_BUS[id]))
14759
+ ];
14760
+ var BODY_FOLEY_BANK = {
14761
+ id: "body_foley",
14762
+ sounds: built.flatMap((entry) => entry.sounds),
14763
+ sets: built.map((entry) => entry.set)
14267
14764
  };
14765
+ var BODY_FOLEY_SET_IDS = [...FOOTSTEP_SOUND_IDS, ...LAND_SOUND_IDS, ...BODY_SOUND_IDS];
14268
14766
 
14269
14767
  // src/audio/recipes.ts
14270
14768
  var scene = (id, layers, line, vars) => sceneRecipe(id, layers, line, vars ? { vars } : {});