@adatechnology/game-weapons-three 0.12.0 → 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.
@@ -2164,6 +2164,75 @@ var reloadCueSteps = (id, profile = DEFAULT_SOUND_PROFILE, table = RELOAD_CUES)
2164
2164
  });
2165
2165
  };
2166
2166
 
2167
+ // src/audio/samples.ts
2168
+ import { sampleBankFromManifest } from "@adatechnology/game-audio";
2169
+
2170
+ // src/data/reload-sample-hits.json
2171
+ var reload_sample_hits_default = {
2172
+ $comment: "Seconds from the start of each recorded reload step to its hit (median of its takes), measured on the converted files by tools/sfx-sources/hits.mjs. The reload clip starts the sound that much early, so the hit lands on the frame the part reaches its stop.",
2173
+ hits: {
2174
+ bolt_lift: 0.072,
2175
+ bolt_lock: 0.078,
2176
+ bolt_pull: 0.128,
2177
+ bolt_push: 0.054,
2178
+ charging_handle_pull: 0.074,
2179
+ charging_handle_release: 0.048,
2180
+ cylinder_close: 0.024,
2181
+ cylinder_eject: 0.394,
2182
+ cylinder_open: 0.07,
2183
+ lever_close: 0.147,
2184
+ lever_open: 0.095,
2185
+ mag_out: 0.012,
2186
+ mag_seat: 0.094,
2187
+ pump_back: 0.094,
2188
+ pump_forward: 0.118,
2189
+ shell_insert: 0.21,
2190
+ slide_release: 0.031,
2191
+ speedloader: 0.089
2192
+ }
2193
+ };
2194
+
2195
+ // src/audio/samples.ts
2196
+ var GUNSHOT_SAMPLE_IDS = [...Object.keys(gunshot_profiles_default.profiles), "suppressed_pistol", "suppressed_rifle"];
2197
+ var GUNSHOT_SAMPLE = {
2198
+ bus: "weapons",
2199
+ gain: 1,
2200
+ pitch: 0.04,
2201
+ crossover: { from: 20, to: 90 },
2202
+ falloff: { model: "inverse_square", k: 5e-4, minGain: 6e-4 },
2203
+ cutoffK: 0.04,
2204
+ priority: 3
2205
+ };
2206
+ var SUPPRESSED_SAMPLE = { ...GUNSHOT_SAMPLE, gain: 0.55, crossover: { from: 10, to: 50 }, falloff: { model: "inverse_square", k: 4e-3, minGain: 8e-4 } };
2207
+ var RELOAD_SAMPLE = { bus: "weapons", gain: 0.32, pitch: 0.05, falloff: { model: "inverse_square", k: 0.05, minGain: 2e-3 }, cutoffK: 0.02, priority: 2 };
2208
+ var CASING_SAMPLE = { bus: "weapons", gain: 0.22, pitch: 0.06, falloff: { model: "inverse_square", k: 0.08, minGain: 3e-3 }, cutoffK: 0.03, priority: 1 };
2209
+ var casingSampleId = (sample) => sample.replace(/_(pistol|rifle|shell)$/, "");
2210
+ var RELOAD_SAMPLE_HITS = reload_sample_hits_default.hits;
2211
+ var WEIGHT_INTENSITY = { light: 0.1, medium: 0.5, heavy: 0.9 };
2212
+ var CUES_IN_SAMPLE = { mag_release: "mag_out", mag_insert: "mag_seat" };
2213
+ var isGunshot = (id) => GUNSHOT_SAMPLE_IDS.includes(id);
2214
+ function weaponSampleDefaults(sound3, overrides = {}) {
2215
+ if (sound3.category !== "armas") return void 0;
2216
+ const base = sound3.id.startsWith("suppressed_") ? SUPPRESSED_SAMPLE : isGunshot(sound3.id) ? GUNSHOT_SAMPLE : sound3.id.startsWith("casing_drop_") ? CASING_SAMPLE : RELOAD_SAMPLE;
2217
+ return { ...base, ...overrides[sound3.id] };
2218
+ }
2219
+ function weaponSampleBank(options) {
2220
+ const bank = sampleBankFromManifest(options.manifest, { id: "weapons_three_samples", ...options.url ? {} : { baseUrl: options.baseUrl ?? "" }, defaults: (sound3) => weaponSampleDefaults(sound3, options.sounds) });
2221
+ const url = options.url;
2222
+ const remap = (refs) => refs && url ? refs.map((ref) => ({ ...ref, url: url(ref.url) })) : refs ? [...refs] : void 0;
2223
+ const samples = (bank.samples ?? []).map((sample) => {
2224
+ const tiers = sample.tiers?.map((tier) => ({ ...tier, close: remap(tier.close) ?? [], ...tier.far ? { far: remap(tier.far) ?? [] } : {} }));
2225
+ return {
2226
+ ...sample,
2227
+ close: remap(sample.close) ?? [],
2228
+ ...sample.far ? { far: remap(sample.far) ?? [] } : {},
2229
+ ...tiers ? { tiers } : {},
2230
+ ...isGunshot(sample.id) ? { choke: { voices: 3, fade: 0.06 } } : {}
2231
+ };
2232
+ });
2233
+ return { id: bank.id, samples };
2234
+ }
2235
+
2167
2236
  // src/audio.ts
2168
2237
  var NOISE_SECONDS = 1.5;
2169
2238
  var DEFAULT_SEED = 1;
@@ -2184,6 +2253,17 @@ function createWeaponAudio(volume = 0.6, options = {}) {
2184
2253
  const channel = shared && options.seed !== void 0 ? shared.channel({ seed }) : engine.channel({});
2185
2254
  engine.register({ id: `weapons_three@${classBurstId(distanceCurve)}`, sounds: weaponRecipes(distanceCurve) });
2186
2255
  const classBurst = classBurstId(distanceCurve);
2256
+ const sampleBank = options.samples ? weaponSampleBank(options.samples) : null;
2257
+ if (sampleBank) engine.register(sampleBank);
2258
+ const sampleIds = new Set((sampleBank?.samples ?? []).map((sample) => sample.id));
2259
+ let sampleLoad = null;
2260
+ const loadSamples = () => sampleLoad ?? (sampleLoad = sampleIds.size > 0 ? engine.loadSamples([...sampleIds]).then(() => void 0, () => void 0) : Promise.resolve());
2261
+ const sampleReady = (id) => {
2262
+ if (!sampleIds.has(id)) return false;
2263
+ if (engine.sampleStatus(id) === "ready") return true;
2264
+ void loadSamples();
2265
+ return false;
2266
+ };
2187
2267
  const burst = (config) => {
2188
2268
  const id = config.filter === "highpass" ? BURST_IDS.highpass : BURST_IDS.lowpass;
2189
2269
  const params = { frequency: config.frequency, q: config.q ?? 0.8, gain: Math.max(config.gain, 2e-4), seconds: config.seconds };
@@ -2232,8 +2312,14 @@ function createWeaponAudio(volume = 0.6, options = {}) {
2232
2312
  };
2233
2313
  const casing = (input) => {
2234
2314
  const sound3 = computeCasingSound({ ...input, pitch: input.pitch ?? 1 + (channel.random() * 2 - 1) * 0.06 });
2315
+ const recorded = sound3.kind === "shell" ? "" : casingSampleId(sound3.sample);
2316
+ if (recorded && sampleReady(recorded)) {
2317
+ if ((input.bounce ?? 0) === 0) channel.play(recorded, { volume: Math.min(1, Math.max(0.2, (input.speed ?? 1.5) / 2.5)) });
2318
+ options.onCasing?.(sound3, "sample");
2319
+ return;
2320
+ }
2235
2321
  for (const voice of sound3.voices) playCasingVoice(voice);
2236
- options.onCasing?.(sound3);
2322
+ options.onCasing?.(sound3, "synth");
2237
2323
  };
2238
2324
  const lastTake = /* @__PURE__ */ new Map();
2239
2325
  const pitchSpread = options.pitchSpread ?? 0.04;
@@ -2255,8 +2341,25 @@ function createWeaponAudio(volume = 0.6, options = {}) {
2255
2341
  lastTake.set(profile, take);
2256
2342
  const pitch = 1 + (channel.random() * 2 - 1) * pitchSpread;
2257
2343
  const shot = computeGunshot({ ...base, take, pitch }, options.gunshot);
2344
+ const recorded = shot.samples.close;
2345
+ if (shot.audible && sampleReady(recorded)) {
2346
+ const occlusion = Math.min(1, Math.max(0, extras.occlusion ?? 0));
2347
+ const handle = channel.play(recorded, {
2348
+ volume: (extras.volume ?? 1) * (1 - 0.5 * occlusion),
2349
+ environment: base.space === "indoor" ? "indoor" : "outdoor",
2350
+ ...extras.position ? { position: [extras.position[0], extras.position[1], extras.position[2]] } : {},
2351
+ ...extras.distance === void 0 ? {} : { distance: extras.distance },
2352
+ ...shot.delay > 0 ? { delay: shot.delay } : {}
2353
+ });
2354
+ if (base.space === "indoor") {
2355
+ for (const voice of shot.voices) if (voice.group === "tail") playGunshotVoice(voice, extras);
2356
+ }
2357
+ const layers = handle?.kind === "sample" ? handle.plan.layers.map((layer) => ({ role: layer.role, take: layer.take, rate: layer.rate })) : [];
2358
+ options.onGunshot?.(shot, { ...base, take, pitch }, { kind: "sample", id: recorded, layers });
2359
+ return;
2360
+ }
2258
2361
  for (const voice of shot.voices) playGunshotVoice(voice, extras);
2259
- if (shot.audible) options.onGunshot?.(shot, { ...base, take, pitch });
2362
+ if (shot.audible) options.onGunshot?.(shot, { ...base, take, pitch }, { kind: "synth" });
2260
2363
  return;
2261
2364
  }
2262
2365
  const sound3 = shotSound({ ...extras, energyJ, category });
@@ -2271,9 +2374,17 @@ function createWeaponAudio(volume = 0.6, options = {}) {
2271
2374
  mechanical("dry_fire");
2272
2375
  },
2273
2376
  mechanical,
2274
- cue(id, profile) {
2275
- for (const entry of reloadCueSteps(id, profile, cueTable)) step(entry);
2377
+ cue(id, profile, lead = 0) {
2378
+ const carriedBy = CUES_IN_SAMPLE[id];
2379
+ if (carriedBy && sampleReady(carriedBy)) return "carried";
2380
+ if (sampleReady(id)) {
2381
+ channel.play(id, { intensity: WEIGHT_INTENSITY[(profile ?? DEFAULT_SOUND_PROFILE).weight] });
2382
+ return "sample";
2383
+ }
2384
+ for (const entry of reloadCueSteps(id, profile, cueTable)) step(lead > 0 ? { ...entry, at: entry.at + lead } : entry);
2385
+ return "synth";
2276
2386
  },
2387
+ loadSamples,
2277
2388
  event(event) {
2278
2389
  const id = mechanicalSoundOf(event);
2279
2390
  if (id) mechanical(id);
@@ -6048,6 +6159,7 @@ import { createRng as createRng2, muzzleEnergy as muzzleEnergy4 } from "@adatech
6048
6159
 
6049
6160
  // src/animation/clip-builder.ts
6050
6161
  import { STAGE_EVENT } from "@adatechnology/game-contracts";
6162
+ var RELOAD_SAMPLE_HITS2 = reload_sample_hits_default.hits;
6051
6163
  var ROOT = "root";
6052
6164
  var AXIS_INDEX = { x: 0, y: 1, z: 2 };
6053
6165
  var EASE_STEPS = 10;
@@ -6055,7 +6167,10 @@ var ease = (progress) => progress < 0.5 ? 2 * progress * progress : 1 - (-2 * pr
6055
6167
  var move = (part, axis, start, duration, from, to) => ({ part, channel: "position", axis, start, duration, from, to });
6056
6168
  var turn = (part, axis, start, duration, from, to) => ({ part, channel: "rotation", axis, start, duration, from, to });
6057
6169
  var cue = (t, type, data) => ({ t, type, ...data ? { data } : {} });
6058
- var sound2 = (t, id) => ({ t, type: "sound", data: { id } });
6170
+ var sound2 = (t, id) => {
6171
+ const lead = RELOAD_SAMPLE_HITS2[id];
6172
+ return lead ? { t, type: "sound", data: { id }, lead } : { t, type: "sound", data: { id } };
6173
+ };
6059
6174
  var stage = (id, t) => ({ t, type: STAGE_EVENT, data: { stage: id } });
6060
6175
  var valueAt = (tweens, t, includeInstants) => {
6061
6176
  let value = 0;
@@ -7561,7 +7676,7 @@ function createClipEventHandler(world, session) {
7561
7676
  breechRound();
7562
7677
  break;
7563
7678
  case "sound":
7564
- world.sound(session.id, String(data.id), session.soundProfile);
7679
+ world.sound(session.id, String(data.id), session.soundProfile, typeof data.lead === "number" ? data.lead : void 0);
7565
7680
  break;
7566
7681
  default:
7567
7682
  break;
@@ -8137,9 +8252,9 @@ function createViewmodel(options) {
8137
8252
  audio,
8138
8253
  layers,
8139
8254
  groundAt,
8140
- sound: (weapon, cue2, profile) => {
8141
- audio?.cue(cue2, profile);
8142
- options.onSound?.({ weapon, cue: cue2, profile });
8255
+ sound: (weapon, cue2, profile, lead) => {
8256
+ const played = audio?.cue(cue2, profile, lead);
8257
+ options.onSound?.({ weapon, cue: cue2, profile, ...played ? { played } : {} });
8143
8258
  },
8144
8259
  schedule: (delay, seconds, step, done) => void tasks.push({ delay, seconds, elapsed: 0, step: (progress) => step(ease(progress)), done })
8145
8260
  };
@@ -9536,7 +9651,10 @@ var TEXT = {
9536
9651
  reloadStart: "Reload started: {weapon} ({ammo}/{capacity})",
9537
9652
  reloadClip: "Reload of {weapon}: {kind}, clip {clip} ({seconds} s)",
9538
9653
  soundCue: "Sound: {cue} ({weapon})",
9654
+ cueRecorded: " \xB7 recorded",
9655
+ cueCarried: " \xB7 inside the next recording",
9539
9656
  gunshotSound: "Shot sound: {profile}, take {take}/4, pitch {pitch}, {space}, {distance} m{far}",
9657
+ gunshotRecorded: " \xB7 recorded take {takes}",
9540
9658
  casingSound: "Case lands: {case} on {surface}",
9541
9659
  spaces: { indoor: "indoors", outdoor: "outdoors" },
9542
9660
  surfaces: { hard: "concrete", wood: "wood", metal: "metal", soft: "dirt" },
@@ -9568,7 +9686,10 @@ var TEXT = {
9568
9686
  reloadStart: "Recarga iniciada: {weapon} ({ammo}/{capacity})",
9569
9687
  reloadClip: "Recarga de {weapon}: {kind}, clipe {clip} ({seconds} s)",
9570
9688
  soundCue: "Som: {cue} ({weapon})",
9689
+ cueRecorded: " \xB7 grava\xE7\xE3o",
9690
+ cueCarried: " \xB7 dentro da grava\xE7\xE3o seguinte",
9571
9691
  gunshotSound: "Som do tiro: {profile}, tomada {take}/4, tom {pitch}, {space}, {distance} m{far}",
9692
+ gunshotRecorded: " \xB7 grava\xE7\xE3o, tomada {takes}",
9572
9693
  casingSound: "C\xE1psula caiu: {case} em {surface}",
9573
9694
  spaces: { indoor: "ambiente fechado", outdoor: "ao ar livre" },
9574
9695
  surfaces: { hard: "concreto", wood: "madeira", metal: "metal", soft: "terra" },
@@ -9690,7 +9811,7 @@ function createWeaponsTestModule(options = {}) {
9690
9811
  const accessoryText = accessoryTextOf(locale);
9691
9812
  const seedOption = options.seed === void 0 ? {} : { seed: options.seed };
9692
9813
  let audio;
9693
- const ownAudio = () => audio === void 0 ? audio = options.sound === false ? null : createWeaponAudio(void 0, seedOption) : audio;
9814
+ const ownAudio = () => audio === void 0 ? audio = options.sound === false ? null : createWeaponAudio(void 0, { ...seedOption, ...options.samples ? { samples: options.samples } : {} }) : audio;
9694
9815
  const weaponDefs = [...arsenal.weapons.values()].filter((def) => !BODY_ACTIONS.has(def.id));
9695
9816
  const thumbnailBase = options.thumbnailBase ?? DEFAULT_THUMBNAIL_BASE;
9696
9817
  const withThumbnail = (item, key) => {
@@ -9777,18 +9898,22 @@ function createWeaponsTestModule(options = {}) {
9777
9898
  audio = createWeaponAudio(void 0, {
9778
9899
  ...seedOption,
9779
9900
  engine: lentEngine,
9780
- onGunshot: (shot, input) => lent.log(
9781
- fill(text.gunshotSound, { profile: shot.profile, take: String(shot.take + 1), pitch: `${shot.pitch >= 1 ? "+" : ""}${((shot.pitch - 1) * 100).toFixed(1)} %`, space: text.spaces[input.space ?? "outdoor"], distance: String(Math.round(input.distance ?? 0)), far: shot.closeMix < 1 ? ` (${Math.round((1 - shot.closeMix) * 100)} % far)` : "" }),
9782
- { profile: shot.profile, take: shot.take, pitch: shot.pitch, space: input.space ?? "outdoor", distance: input.distance ?? 0, closeMix: shot.closeMix, delay: shot.delay, samples: shot.samples },
9783
- "sound"
9784
- ),
9785
- onCasing: (sound3) => lent.log(fill(text.casingSound, { case: sound3.kind, surface: text.surfaces[sound3.surface] }), { case: sound3.kind, surface: sound3.surface, sample: sound3.sample }, "sound")
9901
+ ...options.samples ? { samples: options.samples } : {},
9902
+ onGunshot: (shot, input, played) => {
9903
+ const recorded = played.kind === "sample" && played.layers.length > 0 ? fill(text.gunshotRecorded, { takes: played.layers.map((layer) => `${layer.role} ${layer.take + 1}`).join(", ") }) : "";
9904
+ lent.log(
9905
+ fill(text.gunshotSound, { profile: shot.profile, take: String(shot.take + 1), pitch: `${shot.pitch >= 1 ? "+" : ""}${((shot.pitch - 1) * 100).toFixed(1)} %`, space: text.spaces[input.space ?? "outdoor"], distance: String(Math.round(input.distance ?? 0)), far: shot.closeMix < 1 ? ` (${Math.round((1 - shot.closeMix) * 100)} % far)` : "" }) + recorded,
9906
+ { profile: shot.profile, take: shot.take, pitch: shot.pitch, space: input.space ?? "outdoor", distance: input.distance ?? 0, closeMix: shot.closeMix, delay: shot.delay, samples: shot.samples, played },
9907
+ "sound"
9908
+ );
9909
+ },
9910
+ onCasing: (sound3, played) => lent.log(fill(text.casingSound, { case: sound3.kind, surface: text.surfaces[sound3.surface] }) + (played === "sample" ? text.cueRecorded : ""), { case: sound3.kind, surface: sound3.surface, sample: sound3.sample, played }, "sound")
9786
9911
  });
9787
9912
  }
9788
9913
  let simulatedMs = 0;
9789
9914
  const offClock = lent.onFrame((dt) => simulatedMs += dt * 1e3);
9790
9915
  const created = createWeaponFx({
9791
- onSound: ({ weapon, cue: cue2, profile }) => lent.log(fill(text.soundCue, { weapon: arsenal.name(weapon), cue: text.soundCues[cue2] ?? cue2 }), { weapon, cue: cue2, weight: profile.weight, material: profile.material }, "sound"),
9916
+ onSound: ({ weapon, cue: cue2, profile, played }) => lent.log(fill(text.soundCue, { weapon: arsenal.name(weapon), cue: text.soundCues[cue2] ?? cue2 }) + (played === "sample" ? text.cueRecorded : played === "carried" ? text.cueCarried : ""), { weapon, cue: cue2, weight: profile.weight, material: profile.material, ...played ? { played } : {} }, "sound"),
9792
9917
  THREE: lent.THREE,
9793
9918
  scene: lent.scene,
9794
9919
  camera: lent.camera,
@@ -9963,6 +10088,7 @@ var EXPLOSIVES_TEXT = {
9963
10088
  push_outward: "Every body inside the reach moved away from the blast ({moved} of {n}, {least} m at the least)",
9964
10089
  push_falloff: "{what}: the nearer one moved more than the farther one ({near} m at {nearDistance} m, {far} m at {farDistance} m)",
9965
10090
  push_person: "{who} at {distance} m was pushed {moved} m away",
10091
+ push_sheltered: "{what} at {distance} m stood behind cover ({cover}% of the blast got through) and moved only {moved} m",
9966
10092
  push_heavy: "{what} (too heavy to grab, not fixed) was moved {moved} m by the blast"
9967
10093
  }
9968
10094
  },
@@ -10048,6 +10174,7 @@ var EXPLOSIVES_TEXT = {
10048
10174
  push_outward: "Todo corpo dentro do alcance se afastou da explos\xE3o ({moved} de {n}, no m\xEDnimo {least} m)",
10049
10175
  push_falloff: "{what}: o mais perto andou mais que o mais longe ({near} m a {nearDistance} m, {far} m a {farDistance} m)",
10050
10176
  push_person: "{who} a {distance} m foi empurrado {moved} m para longe",
10177
+ push_sheltered: "{what} a {distance} m estava atr\xE1s de cobertura ({cover}% da explos\xE3o passou) e andou s\xF3 {moved} m",
10051
10178
  push_heavy: "{what} (pesado demais para pegar, mas solto) foi movido {moved} m pela explos\xE3o"
10052
10179
  }
10053
10180
  }
@@ -10593,6 +10720,10 @@ var PUSH_KINDS = [
10593
10720
  ];
10594
10721
  var PUSH_READ_AT = 0.8;
10595
10722
  var MOVED = 0.15;
10723
+ var SHELTERED = 0.5;
10724
+ var SHELTERED_MOST = 1;
10725
+ var CHEST = 1.1;
10726
+ var MIDDLE = 0.3;
10596
10727
  var HEAVY_ITEMS = ["camp_table"];
10597
10728
  var flat = (a, b) => Math.hypot(a[0] - b[0], a[2] - b[2]);
10598
10729
  var bearing = (centre, distance, degrees) => [centre[0] + Math.sin(degrees * Math.PI / 180) * distance, 0, centre[2] + Math.cos(degrees * Math.PI / 180) * distance];
@@ -10609,9 +10740,11 @@ function pushScenario(all, bench, mode) {
10609
10740
  name: mode === "p13" ? text.push_p13 : text.push_grenade,
10610
10741
  run: ({ room, step, expect }) => {
10611
10742
  let centre;
10743
+ let source;
10612
10744
  if (mode === "p13") {
10613
10745
  room.openArea(EXPLOSIVES_AREA_ID);
10614
10746
  const cylinder = room.spawned().find((spawned) => spawned.itemId === CYLINDER_ITEM);
10747
+ source = cylinder?.objectId;
10615
10748
  const at = cylinder?.root?.position;
10616
10749
  centre = [at?.x ?? 0, 0, at?.z ?? -10.66];
10617
10750
  } else {
@@ -10662,6 +10795,14 @@ function pushScenario(all, bench, mode) {
10662
10795
  const blast = bench.lastBlast();
10663
10796
  expect(!!blast, text.push_told, { waited });
10664
10797
  if (!blast) return;
10798
+ const coverOf = new Map(
10799
+ placed.map((body) => {
10800
+ const from = origin.get(body.objectId) ?? body.from;
10801
+ const person = body.kind === "zombie" || body.kind === "survivor";
10802
+ const ignore = source ? [source, body.objectId] : [body.objectId];
10803
+ return [body.objectId, room.world.blastReach?.(blast.point, [from[0], from[1] + (person ? CHEST : MIDDLE), from[2]], { ignore }) ?? 1];
10804
+ })
10805
+ );
10665
10806
  step(PUSH_READ_AT);
10666
10807
  const rows = placed.map((body) => {
10667
10808
  const from = origin.get(body.objectId) ?? body.from;
@@ -10669,10 +10810,15 @@ function pushScenario(all, bench, mode) {
10669
10810
  const distance = flat(from, blast.point);
10670
10811
  const away = Math.max(1e-6, distance);
10671
10812
  const outward = ((now[0] - from[0]) * (from[0] - blast.point[0]) + (now[2] - from[2]) * (from[2] - blast.point[2])) / away;
10672
- return { ...body, distance, outward, moved: flat(now, from) };
10813
+ return { ...body, distance, outward, moved: flat(now, from), cover: coverOf.get(body.objectId) ?? 1 };
10673
10814
  });
10674
10815
  const torn = (row) => (PUSH_KINDS.some((entry) => entry.kind === row.kind) || HEAVY_ITEMS.includes(row.kind)) && partsOf(row.objectId) < (partsBefore.get(row.objectId) ?? 0);
10675
- const reached = rows.filter((row) => row.distance < blast.radius * 0.85 && !torn(row));
10816
+ const inReach = rows.filter((row) => row.distance < blast.radius * 0.85 && !torn(row));
10817
+ const sheltered = inReach.filter((row) => row.cover < SHELTERED);
10818
+ for (const row of sheltered) {
10819
+ expect(row.outward < SHELTERED_MOST, fillText2(text.push_sheltered, { what: things[row.kind] ?? row.kind, distance: row.distance.toFixed(1), cover: (row.cover * 100).toFixed(0), moved: Math.max(0, row.outward).toFixed(2) }), { row: { kind: row.kind, distance: row.distance, outward: row.outward, cover: row.cover } });
10820
+ }
10821
+ const reached = inReach.filter((row) => row.cover >= SHELTERED);
10676
10822
  const movedAway = reached.filter((row) => row.outward >= MOVED);
10677
10823
  const least = reached.length === 0 ? 0 : Math.min(...reached.map((row) => row.outward));
10678
10824
  expect(reached.length > 0 && movedAway.length === reached.length, fillText2(text.push_outward, { moved: movedAway.length, n: reached.length, least: least.toFixed(2) }), { rows: reached.map((row) => ({ kind: row.kind, distance: row.distance, outward: row.outward })), torn: rows.filter(torn).map((row) => row.objectId) });
@@ -10842,6 +10988,18 @@ export {
10842
10988
  DEFAULT_ENERGY_CURVE,
10843
10989
  CLASS_REFERENCE_ENERGY,
10844
10990
  computeShotSound,
10991
+ GUNSHOT_PROFILES,
10992
+ computeGunshot,
10993
+ computeCasingSound,
10994
+ computeFlyby,
10995
+ GUNSHOT_SAMPLE_IDS,
10996
+ GUNSHOT_SAMPLE,
10997
+ RELOAD_SAMPLE,
10998
+ RELOAD_SAMPLE_HITS,
10999
+ WEIGHT_INTENSITY,
11000
+ CUES_IN_SAMPLE,
11001
+ weaponSampleDefaults,
11002
+ weaponSampleBank,
10845
11003
  createWeaponAudio,
10846
11004
  BODY_ACTIONS,
10847
11005
  weaponProp,
@@ -10899,4 +11057,4 @@ export {
10899
11057
  DEFAULT_EXPLOSIVES_SCENE,
10900
11058
  createExplosivesTestModule
10901
11059
  };
10902
- //# sourceMappingURL=chunk-ZVLG34JN.js.map
11060
+ //# sourceMappingURL=chunk-NQELXT56.js.map