@adatechnology/game-destruction-three 0.12.1 → 0.14.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.
- package/README.md +1 -1
- package/dist/index.d.ts +161 -14
- package/dist/index.global.js +2371 -320
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +728 -110
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.global.js
CHANGED
|
@@ -23,38 +23,131 @@ var GameDestructionThree = (() => {
|
|
|
23
23
|
// src/index.ts
|
|
24
24
|
var index_exports = {};
|
|
25
25
|
__export(index_exports, {
|
|
26
|
+
BIG_BLAST_RADIUS: () => BIG_BLAST_RADIUS,
|
|
27
|
+
BLAST_FALLOFF: () => BLAST_FALLOFF,
|
|
28
|
+
BLAST_FAR_DISTANCE: () => BLAST_FAR_DISTANCE,
|
|
29
|
+
BLAST_FAR_FALLOFF: () => BLAST_FAR_FALLOFF,
|
|
30
|
+
BLAST_FIRE_SAMPLES: () => BLAST_FIRE_SAMPLES,
|
|
31
|
+
BLAST_KINDS: () => BLAST_KINDS,
|
|
32
|
+
BLAST_KIND_OF: () => BLAST_KIND_OF,
|
|
33
|
+
BLAST_SOUNDS: () => BLAST_SOUNDS,
|
|
34
|
+
BLAST_SOUND_OF: () => BLAST_SOUND_OF,
|
|
26
35
|
BURN_LOOP: () => BURN_LOOP,
|
|
36
|
+
DEFAULT_CRACKLES: () => DEFAULT_CRACKLES,
|
|
27
37
|
DEFAULT_FIRE: () => DEFAULT_FIRE,
|
|
38
|
+
DEFAULT_INDOOR_CONFINEMENT: () => DEFAULT_INDOOR_CONFINEMENT,
|
|
28
39
|
DESTRUCTION_SOUND_BANK: () => DESTRUCTION_SOUND_BANK,
|
|
29
40
|
DESTRUCTION_SOUND_KEYS: () => DESTRUCTION_SOUND_KEYS,
|
|
30
41
|
DRAG_SOUNDS: () => DRAG_SOUNDS,
|
|
31
42
|
EXPLOSION_BOOM: () => EXPLOSION_BOOM,
|
|
43
|
+
FIRE_FALLOFF: () => FIRE_FALLOFF,
|
|
44
|
+
FIRE_LOOP: () => FIRE_LOOP,
|
|
45
|
+
FIRE_SOUNDS: () => FIRE_SOUNDS,
|
|
46
|
+
IMPACT_FAMILY_TAGS: () => IMPACT_FAMILY_TAGS,
|
|
47
|
+
IMPACT_SOUNDS: () => IMPACT_SOUNDS,
|
|
48
|
+
IMPACT_SOUND_KEYS: () => IMPACT_SOUND_KEYS,
|
|
32
49
|
SCENE_SOUNDS: () => SCENE_SOUNDS,
|
|
50
|
+
blastFireSampleBank: () => blastFireSampleBank,
|
|
51
|
+
breakSoundKeys: () => breakSoundKeys,
|
|
33
52
|
createDestructionRenderer: () => createDestructionRenderer,
|
|
34
53
|
createDestructionSound: () => createDestructionSound,
|
|
35
54
|
dragSoundKey: () => dragSoundKey,
|
|
36
55
|
heatColor: () => heatColor,
|
|
56
|
+
impactFamily: () => impactFamily,
|
|
37
57
|
impactKey: () => impactKey,
|
|
58
|
+
impactSoundKeys: () => impactSoundKeys,
|
|
59
|
+
ricochetSoundKeys: () => ricochetSoundKeys,
|
|
60
|
+
shotKey: () => shotKey,
|
|
38
61
|
specFromObject3D: () => specFromObject3D
|
|
39
62
|
});
|
|
40
63
|
|
|
64
|
+
// src/audio/impact-keys.ts
|
|
65
|
+
var IMPACT_FAMILY_TAGS = [
|
|
66
|
+
["metal", "metal"],
|
|
67
|
+
["glass", "glass"],
|
|
68
|
+
["ceramic", "ceramic"],
|
|
69
|
+
["soil", "soil"],
|
|
70
|
+
["masonry", "masonry"],
|
|
71
|
+
["wood", "wood"],
|
|
72
|
+
["plant", "plant"],
|
|
73
|
+
["plastic", "plastic"],
|
|
74
|
+
["rubber", "rubber"],
|
|
75
|
+
["wax", "wax"],
|
|
76
|
+
["fabric", "soft"]
|
|
77
|
+
];
|
|
78
|
+
function impactFamily(def) {
|
|
79
|
+
const tags = def?.tags ?? [];
|
|
80
|
+
for (const [tag, family] of IMPACT_FAMILY_TAGS) if (tags.includes(tag)) return family;
|
|
81
|
+
return void 0;
|
|
82
|
+
}
|
|
83
|
+
var ordered = (prefix, material, family, blow3) => {
|
|
84
|
+
const keys = [];
|
|
85
|
+
if (material && blow3) keys.push(`${prefix}_${material}_${blow3}`);
|
|
86
|
+
if (material) keys.push(`${prefix}_${material}`);
|
|
87
|
+
if (family && blow3) keys.push(`${prefix}_${family}_${blow3}`);
|
|
88
|
+
if (family) keys.push(`${prefix}_${family}`);
|
|
89
|
+
return keys;
|
|
90
|
+
};
|
|
91
|
+
function impactSoundKeys(material, def, blow3) {
|
|
92
|
+
return { keys: ordered("impact", material, impactFamily(def), blow3) };
|
|
93
|
+
}
|
|
94
|
+
function breakSoundKeys(material, def, blow3) {
|
|
95
|
+
return { keys: ordered("break", material, impactFamily(def), blow3) };
|
|
96
|
+
}
|
|
97
|
+
function ricochetSoundKeys(material, def) {
|
|
98
|
+
return { keys: ordered("ricochet", material, impactFamily(def), void 0) };
|
|
99
|
+
}
|
|
100
|
+
|
|
41
101
|
// ../game-contracts/dist/index.js
|
|
42
102
|
var DEFAULT_BLAST_SHOVE = { coupling: 0.02, maxSpeed: 20, lift: 0.3, minSpeed: 0.3, falloffPower: 2 };
|
|
43
|
-
function blastShove(
|
|
103
|
+
function blastShove(blast3, target, options = {}) {
|
|
44
104
|
const o = { ...DEFAULT_BLAST_SHOVE, ...options };
|
|
45
|
-
const dx = target.position[0] -
|
|
46
|
-
const dy = target.position[1] -
|
|
47
|
-
const dz = target.position[2] -
|
|
105
|
+
const dx = target.position[0] - blast3.point[0];
|
|
106
|
+
const dy = target.position[1] - blast3.point[1];
|
|
107
|
+
const dz = target.position[2] - blast3.point[2];
|
|
48
108
|
const distance = Math.hypot(dx, dy, dz);
|
|
49
|
-
if (!(
|
|
50
|
-
const falloff = (1 - distance /
|
|
109
|
+
if (!(blast3.radius > 0) || distance > blast3.radius) return void 0;
|
|
110
|
+
const falloff = (1 - distance / blast3.radius) ** o.falloffPower;
|
|
51
111
|
const mass = Math.max(target.mass, 0.05);
|
|
52
|
-
const speed = Math.min(o.maxSpeed, Math.sqrt(2 *
|
|
112
|
+
const speed = Math.min(o.maxSpeed, Math.sqrt(2 * blast3.energy * (blast3.reach ?? 1) * o.coupling / mass)) * falloff;
|
|
53
113
|
if (!(speed >= o.minSpeed)) return void 0;
|
|
54
114
|
const direction = distance > 1e-4 ? [dx / distance, dy / distance + o.lift, dz / distance] : [0, 1, 0];
|
|
55
115
|
const length = Math.hypot(direction[0], direction[1], direction[2]);
|
|
56
116
|
return { speed, direction: [direction[0] / length, direction[1] / length, direction[2] / length], distance, falloff };
|
|
57
117
|
}
|
|
118
|
+
var DEFAULT_COST_ALLOCATION_BYTES = 1024 * 1024;
|
|
119
|
+
var DEFERRED_QUEUE_ID = {
|
|
120
|
+
DESTRUCTION_EVENTS: "destruction.events",
|
|
121
|
+
BREACH_PAINT: "floorplan.breach-paint",
|
|
122
|
+
AUDIO_VOICES: "audio.voices",
|
|
123
|
+
VEHICLE_CREASE: "vehicles.crease"
|
|
124
|
+
};
|
|
125
|
+
var DEFERRED_PRIORITY = {
|
|
126
|
+
[DEFERRED_QUEUE_ID.DESTRUCTION_EVENTS]: { priority: 300, phase: "present" },
|
|
127
|
+
[DEFERRED_QUEUE_ID.BREACH_PAINT]: { priority: 250, phase: "present" },
|
|
128
|
+
[DEFERRED_QUEUE_ID.AUDIO_VOICES]: { priority: 200, phase: "idle", reserveMs: 1 },
|
|
129
|
+
[DEFERRED_QUEUE_ID.VEHICLE_CREASE]: { priority: 100, phase: "idle" }
|
|
130
|
+
};
|
|
131
|
+
var WARNED_KEY = /* @__PURE__ */ Symbol.for("@adatechnology/game-contracts.warned");
|
|
132
|
+
var warnedKeys = () => {
|
|
133
|
+
const holder = globalThis;
|
|
134
|
+
return holder[WARNED_KEY] ?? (holder[WARNED_KEY] = /* @__PURE__ */ new Set());
|
|
135
|
+
};
|
|
136
|
+
var isProduction = () => {
|
|
137
|
+
try {
|
|
138
|
+
return process.env.NODE_ENV === "production";
|
|
139
|
+
} catch {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
var isQuiet = () => globalThis.__GAME_KIT_QUIET__ === true;
|
|
144
|
+
var warnOnce = (key, message) => {
|
|
145
|
+
if (isQuiet() || isProduction()) return;
|
|
146
|
+
const seen = warnedKeys();
|
|
147
|
+
if (seen.has(key)) return;
|
|
148
|
+
seen.add(key);
|
|
149
|
+
console.warn(`[game-kit] ${message}`);
|
|
150
|
+
};
|
|
58
151
|
var KIT_ORIGIN_KEY = "kitOrigin";
|
|
59
152
|
function markKitOrigin(object, origin) {
|
|
60
153
|
const node = object;
|
|
@@ -69,6 +162,43 @@ var GameDestructionThree = (() => {
|
|
|
69
162
|
}
|
|
70
163
|
return null;
|
|
71
164
|
}
|
|
165
|
+
var SURFACE_IDS = ["concrete", "asphalt", "wood", "tile", "dirt", "grass", "gravel", "sand", "metal", "water", "glass", "carpet"];
|
|
166
|
+
var GAIT_IDS = ["walk", "run"];
|
|
167
|
+
var LANDING_IDS = ["light", "heavy"];
|
|
168
|
+
var BODY_SOUND_IDS = [
|
|
169
|
+
"jump",
|
|
170
|
+
"cloth_rustle",
|
|
171
|
+
"body_fall",
|
|
172
|
+
"fall_damage",
|
|
173
|
+
"player_hurt",
|
|
174
|
+
"player_breath_run",
|
|
175
|
+
"player_death",
|
|
176
|
+
"bite",
|
|
177
|
+
"grab",
|
|
178
|
+
"punch_swing",
|
|
179
|
+
"punch_hit_body",
|
|
180
|
+
"punch_hit_face",
|
|
181
|
+
"punch_hit_object",
|
|
182
|
+
"kick_swing",
|
|
183
|
+
"kick_hit",
|
|
184
|
+
"kick_hit_object",
|
|
185
|
+
"shove_two_hands",
|
|
186
|
+
"knife_swing",
|
|
187
|
+
"knife_stab",
|
|
188
|
+
"knife_slash",
|
|
189
|
+
"knife_hit_object",
|
|
190
|
+
"blunt_swing",
|
|
191
|
+
"blunt_hit",
|
|
192
|
+
"blunt_hit_object",
|
|
193
|
+
"axe_swing",
|
|
194
|
+
"axe_chop",
|
|
195
|
+
"axe_hit_object"
|
|
196
|
+
];
|
|
197
|
+
var footstepSoundId = (surface, gait) => `footstep_${surface}_${gait}`;
|
|
198
|
+
var FOOTSTEP_SOUND_IDS = SURFACE_IDS.flatMap((surface) => GAIT_IDS.map((gait) => footstepSoundId(surface, gait)));
|
|
199
|
+
var LAND_SOUND_IDS = SURFACE_IDS.flatMap((surface) => LANDING_IDS.map((landing2) => `land_${surface}_${landing2}`));
|
|
200
|
+
var BODY_FOLEY_IDS = [...FOOTSTEP_SOUND_IDS, ...LAND_SOUND_IDS, ...BODY_SOUND_IDS];
|
|
201
|
+
var SWING_LEAD_SECONDS = { punch: 0.07, kick: 0.09, shove: 0, blade: 0.05, blunt: 0.1, chop: 0.11 };
|
|
72
202
|
|
|
73
203
|
// ../game-vfx/dist/index.js
|
|
74
204
|
var __defProp2 = Object.defineProperty;
|
|
@@ -1803,48 +1933,48 @@ var GameDestructionThree = (() => {
|
|
|
1803
1933
|
}));
|
|
1804
1934
|
}
|
|
1805
1935
|
function spawnFire(pool, random, init, handle) {
|
|
1806
|
-
const
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
return
|
|
1936
|
+
const fire2 = pool.acquire();
|
|
1937
|
+
fire2.x = init.x ?? 0;
|
|
1938
|
+
fire2.y = init.y ?? 0;
|
|
1939
|
+
fire2.z = init.z ?? 0;
|
|
1940
|
+
fire2.r = init.r ?? 0.6;
|
|
1941
|
+
fire2.r0 = init.startRadius ?? fire2.r;
|
|
1942
|
+
fire2.grow = init.grow ?? 0;
|
|
1943
|
+
fire2.life = init.life ?? 6;
|
|
1944
|
+
fire2.t = 0;
|
|
1945
|
+
fire2.rate = init.rate ?? 22;
|
|
1946
|
+
fire2.acc = 0;
|
|
1947
|
+
fire2.smoke = init.smoke ?? 0.6;
|
|
1948
|
+
fire2.smokeAcc = 0;
|
|
1949
|
+
fire2.fade = 1;
|
|
1950
|
+
fire2.follow = init.follow ?? null;
|
|
1951
|
+
fire2.fx = init.fx ?? 0;
|
|
1952
|
+
fire2.fy = init.fy ?? 0;
|
|
1953
|
+
fire2.fz = init.fz ?? 0;
|
|
1954
|
+
fire2.color = init.color ?? SMOKE_COLORS.black;
|
|
1955
|
+
fire2.rng = random;
|
|
1956
|
+
fire2.handle = handle;
|
|
1957
|
+
return fire2;
|
|
1828
1958
|
}
|
|
1829
1959
|
function addFire(core, random, init, handle) {
|
|
1830
|
-
const
|
|
1831
|
-
core.trace?.fire?.(
|
|
1832
|
-
return
|
|
1960
|
+
const fire2 = spawnFire(core.fires, random, init, handle);
|
|
1961
|
+
core.trace?.fire?.(fire2);
|
|
1962
|
+
return fire2;
|
|
1833
1963
|
}
|
|
1834
1964
|
function positionOf(follow) {
|
|
1835
1965
|
return "position" in follow ? follow.position : follow;
|
|
1836
1966
|
}
|
|
1837
|
-
function emitFlames(core,
|
|
1838
|
-
const random =
|
|
1839
|
-
|
|
1840
|
-
while (
|
|
1841
|
-
|
|
1967
|
+
function emitFlames(core, fire2, radius, dt) {
|
|
1968
|
+
const random = fire2.rng;
|
|
1969
|
+
fire2.acc += dt * fire2.rate * (0.5 + radius * 0.9) * fire2.fade * core.clock.lod;
|
|
1970
|
+
while (fire2.acc >= 1) {
|
|
1971
|
+
fire2.acc -= 1;
|
|
1842
1972
|
const angle = random.next() * Math.PI * 2;
|
|
1843
1973
|
const distance = Math.sqrt(random.next()) * radius;
|
|
1844
1974
|
addPuff(core, random, {
|
|
1845
|
-
x:
|
|
1846
|
-
y:
|
|
1847
|
-
z:
|
|
1975
|
+
x: fire2.x + Math.cos(angle) * distance,
|
|
1976
|
+
y: fire2.y + 0.05,
|
|
1977
|
+
z: fire2.z + Math.sin(angle) * distance,
|
|
1848
1978
|
vx: random.between(-0.2, 0.2),
|
|
1849
1979
|
vy: random.between(0.6, 1.4),
|
|
1850
1980
|
vz: random.between(-0.2, 0.2),
|
|
@@ -1856,21 +1986,21 @@ var GameDestructionThree = (() => {
|
|
|
1856
1986
|
hl: random.between(0.35, 0.6),
|
|
1857
1987
|
drag: 1.5,
|
|
1858
1988
|
buoy: 3.2,
|
|
1859
|
-
color:
|
|
1989
|
+
color: fire2.color,
|
|
1860
1990
|
kind: 0,
|
|
1861
1991
|
wind: 0.4
|
|
1862
1992
|
});
|
|
1863
1993
|
}
|
|
1864
1994
|
}
|
|
1865
|
-
function emitSmoke(core,
|
|
1866
|
-
const random =
|
|
1867
|
-
|
|
1868
|
-
while (
|
|
1869
|
-
|
|
1995
|
+
function emitSmoke(core, fire2, radius, dt) {
|
|
1996
|
+
const random = fire2.rng;
|
|
1997
|
+
fire2.smokeAcc += dt * fire2.smoke * (1 + radius * 0.8) * fire2.fade * core.clock.lod;
|
|
1998
|
+
while (fire2.smokeAcc >= 1 && core.layers.smoke) {
|
|
1999
|
+
fire2.smokeAcc -= 1;
|
|
1870
2000
|
addPuff(core, random, {
|
|
1871
|
-
x:
|
|
1872
|
-
y:
|
|
1873
|
-
z:
|
|
2001
|
+
x: fire2.x + random.between(-radius, radius) * 0.5,
|
|
2002
|
+
y: fire2.y + 0.6 + radius * 0.3,
|
|
2003
|
+
z: fire2.z + random.between(-radius, radius) * 0.5,
|
|
1874
2004
|
vy: random.between(0.8, 1.5),
|
|
1875
2005
|
s0: 0.25,
|
|
1876
2006
|
s1: 0.6 + Math.min(radius, 2) * 0.3,
|
|
@@ -1880,18 +2010,18 @@ var GameDestructionThree = (() => {
|
|
|
1880
2010
|
hl: 0.25,
|
|
1881
2011
|
drag: 0.6,
|
|
1882
2012
|
buoy: 0.55,
|
|
1883
|
-
color:
|
|
2013
|
+
color: fire2.color,
|
|
1884
2014
|
kind: 0
|
|
1885
2015
|
});
|
|
1886
2016
|
}
|
|
1887
2017
|
}
|
|
1888
|
-
function emitEmber(core,
|
|
1889
|
-
const random =
|
|
1890
|
-
if (!(random.next() < dt * 6 *
|
|
2018
|
+
function emitEmber(core, fire2, radius, dt) {
|
|
2019
|
+
const random = fire2.rng;
|
|
2020
|
+
if (!(random.next() < dt * 6 * fire2.fade && core.layers.shrapnel)) return;
|
|
1891
2021
|
addSpark(core, {
|
|
1892
|
-
x:
|
|
1893
|
-
y:
|
|
1894
|
-
z:
|
|
2022
|
+
x: fire2.x + random.between(-radius, radius) * 0.6,
|
|
2023
|
+
y: fire2.y + 0.4,
|
|
2024
|
+
z: fire2.z + random.between(-radius, radius) * 0.6,
|
|
1895
2025
|
vx: random.between(-0.4, 0.4),
|
|
1896
2026
|
vy: random.between(1.5, 3.2),
|
|
1897
2027
|
vz: random.between(-0.4, 0.4),
|
|
@@ -1906,24 +2036,24 @@ var GameDestructionThree = (() => {
|
|
|
1906
2036
|
function updateFires(core, dt) {
|
|
1907
2037
|
const pool = core.fires;
|
|
1908
2038
|
for (let index = pool.items.length - 1; index >= 0; index--) {
|
|
1909
|
-
const
|
|
1910
|
-
|
|
1911
|
-
if (
|
|
1912
|
-
const point = positionOf(
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
}
|
|
1917
|
-
const radius =
|
|
1918
|
-
|
|
1919
|
-
if (
|
|
2039
|
+
const fire2 = pool.items[index];
|
|
2040
|
+
fire2.t += dt;
|
|
2041
|
+
if (fire2.follow) {
|
|
2042
|
+
const point = positionOf(fire2.follow);
|
|
2043
|
+
fire2.x = point.x + fire2.fx;
|
|
2044
|
+
fire2.y = point.y + fire2.fy;
|
|
2045
|
+
fire2.z = point.z + fire2.fz;
|
|
2046
|
+
}
|
|
2047
|
+
const radius = fire2.grow ? lerp(fire2.r0, fire2.r, Math.min(1, fire2.t / fire2.grow)) : fire2.r;
|
|
2048
|
+
fire2.fade = fire2.t < fire2.life - 1.5 ? Math.min(1, fire2.t * 3) : Math.max(0, (fire2.life - fire2.t) / 1.5);
|
|
2049
|
+
if (fire2.t >= fire2.life) {
|
|
1920
2050
|
pool.removeAt(index);
|
|
1921
2051
|
continue;
|
|
1922
2052
|
}
|
|
1923
2053
|
if (!core.layers.fire) continue;
|
|
1924
|
-
emitFlames(core,
|
|
1925
|
-
emitSmoke(core,
|
|
1926
|
-
emitEmber(core,
|
|
2054
|
+
emitFlames(core, fire2, radius, dt);
|
|
2055
|
+
emitSmoke(core, fire2, radius, dt);
|
|
2056
|
+
emitEmber(core, fire2, radius, dt);
|
|
1927
2057
|
}
|
|
1928
2058
|
}
|
|
1929
2059
|
var call = (name) => (...args) => ({ $: name, a: args });
|
|
@@ -2564,22 +2694,22 @@ var GameDestructionThree = (() => {
|
|
|
2564
2694
|
)
|
|
2565
2695
|
]
|
|
2566
2696
|
};
|
|
2567
|
-
function normalized(
|
|
2568
|
-
const length = Math.sqrt(
|
|
2697
|
+
function normalized(vector2) {
|
|
2698
|
+
const length = Math.sqrt(vector2[0] * vector2[0] + vector2[1] * vector2[1] + vector2[2] * vector2[2]) || 1;
|
|
2569
2699
|
const inverse = 1 / length;
|
|
2570
|
-
return [
|
|
2700
|
+
return [vector2[0] * inverse, vector2[1] * inverse, vector2[2] * inverse];
|
|
2571
2701
|
}
|
|
2572
|
-
function plus(
|
|
2573
|
-
return [
|
|
2702
|
+
function plus(vector2, x, y, z) {
|
|
2703
|
+
return [vector2[0] + x, vector2[1] + y, vector2[2] + z];
|
|
2574
2704
|
}
|
|
2575
|
-
function scaledBy(
|
|
2576
|
-
return [
|
|
2705
|
+
function scaledBy(vector2, factor) {
|
|
2706
|
+
return [vector2[0] * factor, vector2[1] * factor, vector2[2] * factor];
|
|
2577
2707
|
}
|
|
2578
|
-
function plusScaled(
|
|
2579
|
-
return [
|
|
2708
|
+
function plusScaled(vector2, direction, factor) {
|
|
2709
|
+
return [vector2[0] + direction[0] * factor, vector2[1] + direction[1] * factor, vector2[2] + direction[2] * factor];
|
|
2580
2710
|
}
|
|
2581
|
-
function negated(
|
|
2582
|
-
return [-
|
|
2711
|
+
function negated(vector2) {
|
|
2712
|
+
return [-vector2[0], -vector2[1], -vector2[2]];
|
|
2583
2713
|
}
|
|
2584
2714
|
var FRAME_DUST = normalized([0, -0.3, 1]);
|
|
2585
2715
|
var SPLINTER_DIRECTION = normalized([0, 0.3, 1]);
|
|
@@ -3882,9 +4012,32 @@ var GameDestructionThree = (() => {
|
|
|
3882
4012
|
/** 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
4013
|
aliasOf(FUEL_LEAK, "vehicle_leak_ignited", {})
|
|
3884
4014
|
];
|
|
4015
|
+
var vector = [variable("vx"), variable("vy"), variable("vz")];
|
|
4016
|
+
var range = (name) => [variable(name), variable(name)];
|
|
4017
|
+
var singleParticle = (id, system) => ({
|
|
4018
|
+
id,
|
|
4019
|
+
pageId: `particle:${id}`,
|
|
4020
|
+
team: "world",
|
|
4021
|
+
budget: { particles: 1, lights: 0 },
|
|
4022
|
+
loop: 1e-3,
|
|
4023
|
+
timeline: [
|
|
4024
|
+
spawn(system, {
|
|
4025
|
+
p: [0, 0, 0],
|
|
4026
|
+
v: vector,
|
|
4027
|
+
life: range("life"),
|
|
4028
|
+
size: range("size"),
|
|
4029
|
+
grow: variable("grow"),
|
|
4030
|
+
c0: variable("color"),
|
|
4031
|
+
a: variable("alpha"),
|
|
4032
|
+
g: variable("gravity"),
|
|
4033
|
+
drag: variable("drag")
|
|
4034
|
+
})
|
|
4035
|
+
]
|
|
4036
|
+
});
|
|
4037
|
+
var SINGLE_PARTICLE_EFFECTS = [singleParticle("particle_puff", "normal"), singleParticle("particle_glow", "add"), singleParticle("particle_streak", "streak")];
|
|
3885
4038
|
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
4039
|
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])) };
|
|
4040
|
+
var GAME_EFFECTS = { ...WORKSHOP_EFFECTS, ...Object.fromEntries([...EVENT_EFFECTS, ...VIEWMODEL_EFFECTS, ...VEHICLE_EFFECTS, ...SINGLE_PARTICLE_EFFECTS].map((effect) => [effect.id, effect])) };
|
|
3888
4041
|
var TABLE = {
|
|
3889
4042
|
concrete: { dust: 9078138, chip: 7236195, spark: 2 },
|
|
3890
4043
|
metal: { dust: 0, chip: 0, spark: 18 },
|
|
@@ -4365,35 +4518,35 @@ var GameDestructionThree = (() => {
|
|
|
4365
4518
|
var NOOP = () => {
|
|
4366
4519
|
};
|
|
4367
4520
|
function createSchedule() {
|
|
4368
|
-
const
|
|
4521
|
+
const entries2 = [];
|
|
4369
4522
|
const spare = [];
|
|
4370
4523
|
return {
|
|
4371
4524
|
at(time, action) {
|
|
4372
4525
|
const entry = spare.pop() ?? { time: 0, action: NOOP };
|
|
4373
4526
|
entry.time = time;
|
|
4374
4527
|
entry.action = action;
|
|
4375
|
-
|
|
4528
|
+
entries2.push(entry);
|
|
4376
4529
|
},
|
|
4377
4530
|
update(now) {
|
|
4378
|
-
for (let index =
|
|
4379
|
-
const entry =
|
|
4531
|
+
for (let index = entries2.length - 1; index >= 0; index--) {
|
|
4532
|
+
const entry = entries2[index];
|
|
4380
4533
|
if (!entry || entry.time > now) continue;
|
|
4381
4534
|
const action = entry.action;
|
|
4382
|
-
|
|
4535
|
+
entries2.splice(index, 1);
|
|
4383
4536
|
entry.action = NOOP;
|
|
4384
4537
|
spare.push(entry);
|
|
4385
4538
|
action();
|
|
4386
4539
|
}
|
|
4387
4540
|
},
|
|
4388
4541
|
clear() {
|
|
4389
|
-
for (const entry of
|
|
4542
|
+
for (const entry of entries2) {
|
|
4390
4543
|
entry.action = NOOP;
|
|
4391
4544
|
spare.push(entry);
|
|
4392
4545
|
}
|
|
4393
|
-
|
|
4546
|
+
entries2.length = 0;
|
|
4394
4547
|
},
|
|
4395
4548
|
get pending() {
|
|
4396
|
-
return
|
|
4549
|
+
return entries2.length;
|
|
4397
4550
|
}
|
|
4398
4551
|
};
|
|
4399
4552
|
}
|
|
@@ -4554,8 +4707,8 @@ var GameDestructionThree = (() => {
|
|
|
4554
4707
|
var UP12 = [0, 1, 0];
|
|
4555
4708
|
var ZERO = [0, 0, 0];
|
|
4556
4709
|
var FULL_TURN_DRAWN = 6.283;
|
|
4557
|
-
function scaledSizes(
|
|
4558
|
-
return [
|
|
4710
|
+
function scaledSizes(range2, factor) {
|
|
4711
|
+
return [range2[0] * Math.sqrt(factor), range2[1] * Math.sqrt(factor)];
|
|
4559
4712
|
}
|
|
4560
4713
|
function fireEmit(engine, key, dt, centre, radius, factor, options = {}) {
|
|
4561
4714
|
const wind = options.wind ?? ZERO;
|
|
@@ -5081,9 +5234,9 @@ var GameDestructionThree = (() => {
|
|
|
5081
5234
|
}
|
|
5082
5235
|
function largestFire(fires, skip) {
|
|
5083
5236
|
let best = null;
|
|
5084
|
-
for (const
|
|
5085
|
-
if (
|
|
5086
|
-
if (best === null ||
|
|
5237
|
+
for (const fire2 of fires) {
|
|
5238
|
+
if (fire2 === skip) continue;
|
|
5239
|
+
if (best === null || fire2.r > best.r) best = fire2;
|
|
5087
5240
|
}
|
|
5088
5241
|
return best;
|
|
5089
5242
|
}
|
|
@@ -5106,17 +5259,17 @@ var GameDestructionThree = (() => {
|
|
|
5106
5259
|
const second = largestFire(step.fires, first);
|
|
5107
5260
|
const largest = [first, second];
|
|
5108
5261
|
for (let index = 0; index < FIRE_LIGHTS; index++) {
|
|
5109
|
-
const
|
|
5262
|
+
const fire2 = largest[index];
|
|
5110
5263
|
const light = pool.fire[index];
|
|
5111
|
-
if (!
|
|
5264
|
+
if (!fire2) {
|
|
5112
5265
|
light.intensity = 0;
|
|
5113
5266
|
continue;
|
|
5114
5267
|
}
|
|
5115
|
-
light.x =
|
|
5116
|
-
light.y =
|
|
5117
|
-
light.z =
|
|
5118
|
-
light.intensity = (1.4 +
|
|
5119
|
-
light.distance = 10 +
|
|
5268
|
+
light.x = fire2.x;
|
|
5269
|
+
light.y = fire2.y + Math.max(0.8, fire2.r * 0.6);
|
|
5270
|
+
light.z = fire2.z;
|
|
5271
|
+
light.intensity = (1.4 + fire2.r * 0.6) * (0.75 + 0.25 * Math.sin(step.clock * 17 + index * 3) + 0.1 * Math.sin(step.clock * 41)) * fire2.fade;
|
|
5272
|
+
light.distance = 10 + fire2.r * 4;
|
|
5120
5273
|
}
|
|
5121
5274
|
if (brightest && brightness > 0.05) {
|
|
5122
5275
|
pool.glow.x = brightest.x;
|
|
@@ -5148,12 +5301,12 @@ var GameDestructionThree = (() => {
|
|
|
5148
5301
|
var SCORCH_VARIANTS = 3;
|
|
5149
5302
|
var RIM_COLORS = [2829359, 3815478];
|
|
5150
5303
|
function createMarkStore() {
|
|
5151
|
-
const
|
|
5304
|
+
const entries2 = [];
|
|
5152
5305
|
let version = 0;
|
|
5153
5306
|
let bloodCount = 0;
|
|
5154
5307
|
const listeners = /* @__PURE__ */ new Set();
|
|
5155
5308
|
return {
|
|
5156
|
-
entries,
|
|
5309
|
+
entries: entries2,
|
|
5157
5310
|
get version() {
|
|
5158
5311
|
return version;
|
|
5159
5312
|
},
|
|
@@ -5162,13 +5315,13 @@ var GameDestructionThree = (() => {
|
|
|
5162
5315
|
},
|
|
5163
5316
|
lift: 0,
|
|
5164
5317
|
add(mark) {
|
|
5165
|
-
|
|
5318
|
+
entries2.push(mark);
|
|
5166
5319
|
if (mark.kind === "blood") bloodCount++;
|
|
5167
5320
|
version++;
|
|
5168
5321
|
for (const listener of listeners) listener({ type: "add", mark });
|
|
5169
5322
|
},
|
|
5170
5323
|
clear() {
|
|
5171
|
-
|
|
5324
|
+
entries2.length = 0;
|
|
5172
5325
|
bloodCount = 0;
|
|
5173
5326
|
this.lift = 0;
|
|
5174
5327
|
version++;
|
|
@@ -5622,18 +5775,18 @@ var GameDestructionThree = (() => {
|
|
|
5622
5775
|
}
|
|
5623
5776
|
if (def.mushroom && core.layers.fire) composeMushroom(context, def.mushroom);
|
|
5624
5777
|
if (def.residualFires && core.layers.fire) {
|
|
5625
|
-
for (const
|
|
5778
|
+
for (const fire2 of def.residualFires) {
|
|
5626
5779
|
addFire(core, random, {
|
|
5627
|
-
x: x + (
|
|
5780
|
+
x: x + (fire2.dx ?? 0),
|
|
5628
5781
|
y: 0,
|
|
5629
|
-
z: z + (
|
|
5630
|
-
r:
|
|
5631
|
-
life:
|
|
5632
|
-
rate:
|
|
5633
|
-
smoke:
|
|
5634
|
-
startRadius:
|
|
5635
|
-
grow:
|
|
5636
|
-
color:
|
|
5782
|
+
z: z + (fire2.dz ?? 0),
|
|
5783
|
+
r: fire2.radius,
|
|
5784
|
+
life: fire2.life,
|
|
5785
|
+
rate: fire2.rate,
|
|
5786
|
+
smoke: fire2.smoke,
|
|
5787
|
+
startRadius: fire2.startRadius,
|
|
5788
|
+
grow: fire2.grow,
|
|
5789
|
+
color: fire2.smokeColor !== void 0 ? hexToLinearRgb(fire2.smokeColor) : void 0
|
|
5637
5790
|
}, context.call.handle);
|
|
5638
5791
|
}
|
|
5639
5792
|
}
|
|
@@ -6409,9 +6562,9 @@ var GameDestructionThree = (() => {
|
|
|
6409
6562
|
var TWO_PI_ROUNDED = 6.2832;
|
|
6410
6563
|
var WIDE_CONE = 3.1;
|
|
6411
6564
|
var UP13 = [0, 1, 0];
|
|
6412
|
-
function drawRange(random,
|
|
6413
|
-
if (Array.isArray(
|
|
6414
|
-
return
|
|
6565
|
+
function drawRange(random, range2) {
|
|
6566
|
+
if (Array.isArray(range2)) return random.between(range2[0], range2[1]);
|
|
6567
|
+
return range2 || 0;
|
|
6415
6568
|
}
|
|
6416
6569
|
function randomUnit(random, out) {
|
|
6417
6570
|
const up = random.next() * 2 - 1;
|
|
@@ -7178,20 +7331,20 @@ var GameDestructionThree = (() => {
|
|
|
7178
7331
|
return [origin[0] + cos2 * x + sin2 * z, origin[1] + point[1] - anchor[1], origin[2] - sin2 * x + cos2 * z];
|
|
7179
7332
|
}
|
|
7180
7333
|
/** A direction or velocity of the recipe's space in the world. */
|
|
7181
|
-
turn(
|
|
7334
|
+
turn(vector2) {
|
|
7182
7335
|
const placement = this.placement;
|
|
7183
|
-
if (!placement) return
|
|
7184
|
-
if (placement.orientation) return rotate(placement.orientation,
|
|
7336
|
+
if (!placement) return vector2;
|
|
7337
|
+
if (placement.orientation) return rotate(placement.orientation, vector2);
|
|
7185
7338
|
const cos2 = Math.cos(placement.yaw);
|
|
7186
7339
|
const sin2 = Math.sin(placement.yaw);
|
|
7187
|
-
return [cos2 *
|
|
7340
|
+
return [cos2 * vector2[0] + sin2 * vector2[2], vector2[1], -sin2 * vector2[0] + cos2 * vector2[2]];
|
|
7188
7341
|
}
|
|
7189
7342
|
placed(options) {
|
|
7190
7343
|
if (!this.placement) return options;
|
|
7191
7344
|
return { ...options, p: this.place(options.p), ...options.dir ? { dir: this.turn(options.dir) } : {}, ...options.v ? { v: this.turn(options.v) } : {} };
|
|
7192
7345
|
}
|
|
7193
|
-
spawn(system,
|
|
7194
|
-
const options = this.placed(
|
|
7346
|
+
spawn(system, recipe2) {
|
|
7347
|
+
const options = this.placed(recipe2);
|
|
7195
7348
|
this.context.trace?.(system, [options]);
|
|
7196
7349
|
if (system === "add") this.add.spawn(options);
|
|
7197
7350
|
else if (system === "normal") this.normal.spawn(options);
|
|
@@ -7211,43 +7364,43 @@ var GameDestructionThree = (() => {
|
|
|
7211
7364
|
this.accumulators.set(key, carried);
|
|
7212
7365
|
}
|
|
7213
7366
|
/** 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(
|
|
7367
|
+
mesh(recipe2) {
|
|
7215
7368
|
const placement = this.placement;
|
|
7216
|
-
const options = placement ? { ...
|
|
7369
|
+
const options = placement ? { ...recipe2, p: this.place(recipe2.p), ...recipe2.v ? { v: this.turn(recipe2.v) } : {} } : recipe2;
|
|
7217
7370
|
this.context.trace?.("mesh", [options]);
|
|
7218
|
-
const grouped =
|
|
7371
|
+
const grouped = recipe2.at !== void 0 || recipe2.roll !== void 0;
|
|
7219
7372
|
return this.meshes.spawn(options, placement && grouped ? placement.orientation ?? [0, Math.sin(placement.yaw / 2), 0, Math.cos(placement.yaw / 2)] : void 0);
|
|
7220
7373
|
}
|
|
7221
|
-
debrisPiece(
|
|
7222
|
-
const options = this.placed(
|
|
7374
|
+
debrisPiece(recipe2) {
|
|
7375
|
+
const options = this.placed(recipe2);
|
|
7223
7376
|
this.context.trace?.("debris", [options]);
|
|
7224
7377
|
this.debris.spawn(options);
|
|
7225
7378
|
}
|
|
7226
|
-
decal(
|
|
7227
|
-
let args =
|
|
7379
|
+
decal(recipe2) {
|
|
7380
|
+
let args = recipe2;
|
|
7228
7381
|
if (this.placement) {
|
|
7229
|
-
const [x, y, z] = this.place([
|
|
7230
|
-
const options =
|
|
7231
|
-
args = [x, y, z,
|
|
7382
|
+
const [x, y, z] = this.place([recipe2[0], recipe2[1], recipe2[2]]);
|
|
7383
|
+
const options = recipe2[5];
|
|
7384
|
+
args = [x, y, z, recipe2[3], recipe2[4], options?.n ? { ...options, n: this.turn(options.n) } : options];
|
|
7232
7385
|
}
|
|
7233
7386
|
this.context.trace?.("decal", args);
|
|
7234
7387
|
this.decals.add(args[0], args[1], args[2], args[3], args[4], args[5]);
|
|
7235
7388
|
}
|
|
7236
|
-
ring(
|
|
7237
|
-
const args = this.placement ? [this.place(
|
|
7389
|
+
ring(recipe2) {
|
|
7390
|
+
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
7391
|
this.context.trace?.("ring", args);
|
|
7239
7392
|
this.rings.add(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
|
|
7240
7393
|
}
|
|
7241
7394
|
/** `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(
|
|
7243
|
-
const args = this.placement ? [this.place(
|
|
7395
|
+
flash(recipe2) {
|
|
7396
|
+
const args = this.placement ? [this.place(recipe2[0]), recipe2[1], recipe2[2], recipe2[3], recipe2[4], recipe2[5], recipe2[6]] : recipe2;
|
|
7244
7397
|
this.context.trace?.("flash", args.slice(0, 6));
|
|
7245
7398
|
this.lights.flash(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
|
|
7246
7399
|
}
|
|
7247
7400
|
/** `fireLight` (W400): sways with the clock and flickers with one draw. */
|
|
7248
|
-
fireLight(slot,
|
|
7401
|
+
fireLight(slot, recipe2, intensity, time) {
|
|
7249
7402
|
const light = this.lights.fire[slot];
|
|
7250
|
-
const point = this.place(
|
|
7403
|
+
const point = this.place(recipe2);
|
|
7251
7404
|
light.x = point[0];
|
|
7252
7405
|
light.y = point[1];
|
|
7253
7406
|
light.z = point[2];
|
|
@@ -7305,7 +7458,7 @@ var GameDestructionThree = (() => {
|
|
|
7305
7458
|
function scaleDefinition(def, factor) {
|
|
7306
7459
|
if (factor === 1) return def;
|
|
7307
7460
|
const mushroom = def.mushroom && { ...def.mushroom, height: def.mushroom.height * factor, radius: def.mushroom.radius * factor };
|
|
7308
|
-
const residual = def.residualFires?.map((
|
|
7461
|
+
const residual = def.residualFires?.map((fire2) => ({ ...fire2, radius: fire2.radius * factor, dx: (fire2.dx ?? 0) * factor, dz: (fire2.dz ?? 0) * factor }));
|
|
7309
7462
|
return {
|
|
7310
7463
|
...def,
|
|
7311
7464
|
lethal: def.lethal * factor,
|
|
@@ -7561,7 +7714,22 @@ var GameDestructionThree = (() => {
|
|
|
7561
7714
|
return key === void 0 ? void 0 : entry[key];
|
|
7562
7715
|
}
|
|
7563
7716
|
|
|
7564
|
-
// ../game-
|
|
7717
|
+
// ../game-render-three/dist/index.js
|
|
7718
|
+
var DEFAULT_ANGLE = Math.PI / 3;
|
|
7719
|
+
var LIGHT_PRIORITY = {
|
|
7720
|
+
blast: 900,
|
|
7721
|
+
rocket: 800,
|
|
7722
|
+
muzzle: 750,
|
|
7723
|
+
fire: 600,
|
|
7724
|
+
flare: 500,
|
|
7725
|
+
headlight: 400,
|
|
7726
|
+
weaponLight: 400,
|
|
7727
|
+
lamp: 200,
|
|
7728
|
+
street: 150
|
|
7729
|
+
};
|
|
7730
|
+
var EFFECT_STEP_SECONDS = 1 / 50;
|
|
7731
|
+
|
|
7732
|
+
// ../game-vfx-three/dist/chunk-ALDCQ5RH.js
|
|
7565
7733
|
var GLOW_VERTEX = `attribute vec4 aP;attribute vec4 aC;varying vec2 vU;varying vec4 vC;
|
|
7566
7734
|
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
7735
|
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,15 +8008,33 @@ void main(){
|
|
|
7840
8008
|
}
|
|
7841
8009
|
};
|
|
7842
8010
|
}
|
|
7843
|
-
|
|
8011
|
+
var LIGHT_ON_THRESHOLD = 0.05;
|
|
8012
|
+
var FIRE_COLOR = 16742954;
|
|
8013
|
+
var FIRE_DECAY = 2;
|
|
8014
|
+
var BLAST_DECAY = 2;
|
|
8015
|
+
function followEntry(budget, handles, index, entry, request) {
|
|
8016
|
+
const handle = handles[index] ?? null;
|
|
8017
|
+
if (entry.intensity <= LIGHT_ON_THRESHOLD) {
|
|
8018
|
+
handle?.release({ fadeMs: 0 });
|
|
8019
|
+
handles[index] = null;
|
|
8020
|
+
return;
|
|
8021
|
+
}
|
|
8022
|
+
if (handle?.alive) {
|
|
8023
|
+
handle.update({ color: request.color, intensity: request.intensity, distance: request.distance, position: request.position });
|
|
8024
|
+
return;
|
|
8025
|
+
}
|
|
8026
|
+
handles[index] = budget.acquire(request);
|
|
8027
|
+
}
|
|
8028
|
+
function createLightLayer(THREE, pool, scale, budget) {
|
|
7844
8029
|
const group = new THREE.Group();
|
|
7845
|
-
|
|
8030
|
+
if (budget) return createBudgetedLightLayer(group, pool, scale, budget);
|
|
8031
|
+
const blast3 = pool.blast.map(() => {
|
|
7846
8032
|
const light = new THREE.PointLight(16752720, 0, 30, 2);
|
|
7847
8033
|
light.position.set(0, -50, 0);
|
|
7848
8034
|
group.add(light);
|
|
7849
8035
|
return light;
|
|
7850
8036
|
});
|
|
7851
|
-
const
|
|
8037
|
+
const fire2 = pool.fire.map(() => {
|
|
7852
8038
|
const light = new THREE.PointLight(16742954, 0, 16, 2);
|
|
7853
8039
|
light.position.set(0, -50, 0);
|
|
7854
8040
|
group.add(light);
|
|
@@ -7859,7 +8045,7 @@ void main(){
|
|
|
7859
8045
|
sync() {
|
|
7860
8046
|
let lit = 0;
|
|
7861
8047
|
pool.blast.forEach((entry, index) => {
|
|
7862
|
-
const light =
|
|
8048
|
+
const light = blast3[index];
|
|
7863
8049
|
light.position.set(entry.x, entry.y, entry.z);
|
|
7864
8050
|
light.distance = entry.distance;
|
|
7865
8051
|
light.color.setRGB(entry.color[0], entry.color[1], entry.color[2], THREE.LinearSRGBColorSpace);
|
|
@@ -7867,7 +8053,7 @@ void main(){
|
|
|
7867
8053
|
if (entry.intensity > 0.05) lit++;
|
|
7868
8054
|
});
|
|
7869
8055
|
pool.fire.forEach((entry, index) => {
|
|
7870
|
-
const light =
|
|
8056
|
+
const light = fire2[index];
|
|
7871
8057
|
light.position.set(entry.x, entry.y, entry.z);
|
|
7872
8058
|
light.distance = entry.distance;
|
|
7873
8059
|
light.intensity = entry.intensity * scale;
|
|
@@ -7876,7 +8062,31 @@ void main(){
|
|
|
7876
8062
|
return lit;
|
|
7877
8063
|
},
|
|
7878
8064
|
dispose() {
|
|
7879
|
-
for (const light of [...
|
|
8065
|
+
for (const light of [...blast3, ...fire2]) light.dispose();
|
|
8066
|
+
}
|
|
8067
|
+
};
|
|
8068
|
+
}
|
|
8069
|
+
function createBudgetedLightLayer(group, pool, scale, budget) {
|
|
8070
|
+
const blast3 = pool.blast.map(() => null);
|
|
8071
|
+
const fire2 = pool.fire.map(() => null);
|
|
8072
|
+
return {
|
|
8073
|
+
group,
|
|
8074
|
+
sync() {
|
|
8075
|
+
let lit = 0;
|
|
8076
|
+
pool.blast.forEach((entry, index) => {
|
|
8077
|
+
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] };
|
|
8078
|
+
followEntry(budget, blast3, index, entry, request);
|
|
8079
|
+
if (entry.intensity > LIGHT_ON_THRESHOLD) lit++;
|
|
8080
|
+
});
|
|
8081
|
+
pool.fire.forEach((entry, index) => {
|
|
8082
|
+
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] };
|
|
8083
|
+
followEntry(budget, fire2, index, entry, request);
|
|
8084
|
+
if (entry.intensity > LIGHT_ON_THRESHOLD) lit++;
|
|
8085
|
+
});
|
|
8086
|
+
return lit;
|
|
8087
|
+
},
|
|
8088
|
+
dispose() {
|
|
8089
|
+
for (const handle of [...blast3, ...fire2]) handle?.release({ fadeMs: 0 });
|
|
7880
8090
|
}
|
|
7881
8091
|
};
|
|
7882
8092
|
}
|
|
@@ -8013,6 +8223,7 @@ void main(){
|
|
|
8013
8223
|
group.add(mesh2);
|
|
8014
8224
|
return mesh2;
|
|
8015
8225
|
}
|
|
8226
|
+
let probeGeometry;
|
|
8016
8227
|
const groundMarks = [];
|
|
8017
8228
|
const spareGeometries = [];
|
|
8018
8229
|
const blood = [];
|
|
@@ -8118,6 +8329,15 @@ void main(){
|
|
|
8118
8329
|
rockMesh();
|
|
8119
8330
|
plateMesh();
|
|
8120
8331
|
},
|
|
8332
|
+
probes() {
|
|
8333
|
+
probeGeometry ?? (probeGeometry = new THREE.PlaneGeometry(1e-3, 1e-3).rotateX(-Math.PI / 2));
|
|
8334
|
+
return [...scorchMaterials, dirtMaterial].map((material) => {
|
|
8335
|
+
const mesh2 = new THREE.Mesh(probeGeometry, material);
|
|
8336
|
+
mesh2.receiveShadow = true;
|
|
8337
|
+
mesh2.frustumCulled = false;
|
|
8338
|
+
return mesh2;
|
|
8339
|
+
});
|
|
8340
|
+
},
|
|
8121
8341
|
update(dt) {
|
|
8122
8342
|
for (let index = 0; index < blood.length; index++) {
|
|
8123
8343
|
const drawn = blood[index];
|
|
@@ -8130,6 +8350,7 @@ void main(){
|
|
|
8130
8350
|
dispose() {
|
|
8131
8351
|
clearMarks();
|
|
8132
8352
|
for (const mesh2 of [instanced.blood, instanced.rock, instanced.plate]) mesh2?.dispose();
|
|
8353
|
+
probeGeometry?.dispose();
|
|
8133
8354
|
for (const item of owned) item.dispose();
|
|
8134
8355
|
}
|
|
8135
8356
|
};
|
|
@@ -8705,7 +8926,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8705
8926
|
}
|
|
8706
8927
|
};
|
|
8707
8928
|
}
|
|
8708
|
-
function createWorkshopLayers(THREE, engine, pointScale, lightScale) {
|
|
8929
|
+
function createWorkshopLayers(THREE, engine, pointScale, lightScale, budget) {
|
|
8709
8930
|
const group = new THREE.Group();
|
|
8710
8931
|
group.name = "workshop";
|
|
8711
8932
|
const layers = [
|
|
@@ -8720,15 +8941,30 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8720
8941
|
for (const layer2 of layers) group.add(layer2.object);
|
|
8721
8942
|
const meshes = createMeshLayer(THREE, engine.meshes);
|
|
8722
8943
|
group.add(meshes.group);
|
|
8723
|
-
const
|
|
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);
|
|
8944
|
+
const lights = budget ? budgetedLights(engine, lightScale, budget) : legacyLights(THREE, engine, lightScale, group);
|
|
8726
8945
|
return {
|
|
8727
8946
|
group,
|
|
8728
8947
|
sync() {
|
|
8729
8948
|
let drawCalls = 0;
|
|
8730
8949
|
for (const layer2 of layers) if (layer2.sync()) drawCalls++;
|
|
8731
8950
|
drawCalls += meshes.sync();
|
|
8951
|
+
lights.sync();
|
|
8952
|
+
return drawCalls;
|
|
8953
|
+
},
|
|
8954
|
+
dispose() {
|
|
8955
|
+
for (const layer2 of layers) layer2.dispose();
|
|
8956
|
+
meshes.dispose();
|
|
8957
|
+
lights.dispose();
|
|
8958
|
+
group.clear();
|
|
8959
|
+
}
|
|
8960
|
+
};
|
|
8961
|
+
}
|
|
8962
|
+
function legacyLights(THREE, engine, lightScale, group) {
|
|
8963
|
+
const flashLights = engine.lights.lights.map(() => new THREE.PointLight(16760944, 0, 14));
|
|
8964
|
+
const fireLights = engine.lights.fire.map(() => new THREE.PointLight(FIRE_LIGHT_COLOR, 0, FIRE_LIGHT_DISTANCE));
|
|
8965
|
+
for (const light of [...flashLights, ...fireLights]) group.add(light);
|
|
8966
|
+
return {
|
|
8967
|
+
sync() {
|
|
8732
8968
|
engine.lights.lights.forEach((source, index) => {
|
|
8733
8969
|
const light = flashLights[index];
|
|
8734
8970
|
light.position.set(source.x, source.y, source.z);
|
|
@@ -8741,12 +8977,27 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8741
8977
|
light.position.set(source.x, source.y, source.z);
|
|
8742
8978
|
light.intensity = source.intensity * lightScale;
|
|
8743
8979
|
});
|
|
8744
|
-
return drawCalls;
|
|
8745
8980
|
},
|
|
8746
8981
|
dispose() {
|
|
8747
|
-
|
|
8748
|
-
|
|
8749
|
-
|
|
8982
|
+
}
|
|
8983
|
+
};
|
|
8984
|
+
}
|
|
8985
|
+
function budgetedLights(engine, lightScale, budget) {
|
|
8986
|
+
const flash2 = engine.lights.lights.map(() => null);
|
|
8987
|
+
const fire2 = engine.lights.fire.map(() => null);
|
|
8988
|
+
return {
|
|
8989
|
+
sync() {
|
|
8990
|
+
engine.lights.lights.forEach((source, index) => {
|
|
8991
|
+
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] };
|
|
8992
|
+
followEntry(budget, flash2, index, source, request);
|
|
8993
|
+
});
|
|
8994
|
+
engine.lights.fire.forEach((source, index) => {
|
|
8995
|
+
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] };
|
|
8996
|
+
followEntry(budget, fire2, index, source, request);
|
|
8997
|
+
});
|
|
8998
|
+
},
|
|
8999
|
+
dispose() {
|
|
9000
|
+
for (const handle of [...flash2, ...fire2]) handle?.release({ fadeMs: 0 });
|
|
8750
9001
|
}
|
|
8751
9002
|
};
|
|
8752
9003
|
}
|
|
@@ -8804,7 +9055,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8804
9055
|
const chunks = createChunkLayer(THREE, pools.chunks, { rock: rockGeometry, plate: plateGeometry }, colorOfChunk);
|
|
8805
9056
|
const shocks = createShockLayer(THREE, pools.shocks);
|
|
8806
9057
|
const sprites = createSpriteLayer(THREE, world.sprites(), options.pointScale ?? 700);
|
|
8807
|
-
const lights = createLightLayer(THREE, pools.lights, options.lightScale ?? Math.PI);
|
|
9058
|
+
const lights = createLightLayer(THREE, pools.lights, options.lightScale ?? Math.PI, options.lights);
|
|
8808
9059
|
const marks = createMarkLayer(THREE, { ground: pools.ground, createCanvas: options.createCanvas, rockGeometry, plateGeometry, ...options.markLimits });
|
|
8809
9060
|
root.add(puffs.mesh, sparks.mesh, glows.mesh, chunks.rocks, chunks.plates, shocks.group, lights.group, sprites.additive, sprites.normal, marks.group);
|
|
8810
9061
|
const stopMarks = world.onMarks((event) => marks.handle(event));
|
|
@@ -8837,7 +9088,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8837
9088
|
workshopLayers?.dispose();
|
|
8838
9089
|
if (workshopLayers) root.remove(workshopLayers.group);
|
|
8839
9090
|
workshopEngine = engine;
|
|
8840
|
-
workshopLayers = createWorkshopLayers(THREE, engine, options.pointScale ?? 700, 1);
|
|
9091
|
+
workshopLayers = createWorkshopLayers(THREE, engine, options.pointScale ?? 700, 1, options.lights);
|
|
8841
9092
|
root.add(workshopLayers.group);
|
|
8842
9093
|
applyLayers();
|
|
8843
9094
|
}
|
|
@@ -8910,7 +9161,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8910
9161
|
const meshes = createMeshLayerProbes(THREE);
|
|
8911
9162
|
for (const mesh2 of meshes) mesh2.frustumCulled = false;
|
|
8912
9163
|
const group = new THREE.Group();
|
|
8913
|
-
group.add(...meshes);
|
|
9164
|
+
group.add(...meshes, ...marks.probes());
|
|
8914
9165
|
anchors = { meshes, group };
|
|
8915
9166
|
return { group, dispose: () => {
|
|
8916
9167
|
} };
|
|
@@ -9028,6 +9279,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9028
9279
|
var BIG_LENGTH = 0.2;
|
|
9029
9280
|
var BIG_WIDTH = 0.03;
|
|
9030
9281
|
var FRAGMENT_SEED = 1374496523;
|
|
9282
|
+
var SHARD_SHAPES = 6;
|
|
9031
9283
|
var isBig = (size) => {
|
|
9032
9284
|
const [, middle, longest] = [...size].sort((a, b) => a - b);
|
|
9033
9285
|
return longest >= BIG_LENGTH && middle >= BIG_WIDTH;
|
|
@@ -9077,7 +9329,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9077
9329
|
return m;
|
|
9078
9330
|
}
|
|
9079
9331
|
shardGeometry(seed) {
|
|
9080
|
-
const idx = seed %
|
|
9332
|
+
const idx = seed % SHARD_SHAPES;
|
|
9081
9333
|
let g = this.shardGeos[idx];
|
|
9082
9334
|
if (!g) {
|
|
9083
9335
|
const r = createRng(1e3 + idx);
|
|
@@ -9098,17 +9350,32 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9098
9350
|
}
|
|
9099
9351
|
return g;
|
|
9100
9352
|
}
|
|
9353
|
+
markParts() {
|
|
9354
|
+
this.markGeometry ?? (this.markGeometry = new this.THREE.CircleGeometry(1, 10));
|
|
9355
|
+
this.markMaterial ?? (this.markMaterial = new this.THREE.MeshBasicMaterial({ color: 723465, depthWrite: false, polygonOffset: true, polygonOffsetFactor: -4, polygonOffsetUnits: -4 }));
|
|
9356
|
+
return { geometry: this.markGeometry, material: this.markMaterial };
|
|
9357
|
+
}
|
|
9358
|
+
/**
|
|
9359
|
+
* 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
|
|
9360
|
+
* glass materials. A host draws them at load so the first blast does not upload these buffers in the middle of a frame.
|
|
9361
|
+
*/
|
|
9362
|
+
warmMeshes() {
|
|
9363
|
+
const opaque = this.materialFor("warm", 8421504, false);
|
|
9364
|
+
const glass2 = this.materialFor("warm", 8421504, true);
|
|
9365
|
+
const geometries = [this.box, ...Array.from({ length: SHARD_SHAPES }, (_, index) => this.shardGeometry(index))];
|
|
9366
|
+
const { geometry, material } = this.markParts();
|
|
9367
|
+
return [...geometries.flatMap((shared) => [new this.THREE.Mesh(shared, opaque), new this.THREE.Mesh(shared, glass2)]), new this.THREE.Mesh(geometry, material)];
|
|
9368
|
+
}
|
|
9101
9369
|
/**
|
|
9102
9370
|
* 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
9371
|
* 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
9372
|
*/
|
|
9105
9373
|
addMarks(mesh2, f) {
|
|
9106
9374
|
if (!f.marks?.length) return;
|
|
9107
|
-
|
|
9108
|
-
this.markMaterial ?? (this.markMaterial = new this.THREE.MeshBasicMaterial({ color: 723465, depthWrite: false, polygonOffset: true, polygonOffsetFactor: -4, polygonOffsetUnits: -4 }));
|
|
9375
|
+
const { geometry, material } = this.markParts();
|
|
9109
9376
|
const sc = mesh2.scale;
|
|
9110
9377
|
for (const mark of f.marks) {
|
|
9111
|
-
const disc = new this.THREE.Mesh(
|
|
9378
|
+
const disc = new this.THREE.Mesh(geometry, material);
|
|
9112
9379
|
const radius = Math.max(4e-3, mark.diameter) * 0.6;
|
|
9113
9380
|
const n = this.v.set(mark.normal[0], mark.normal[1], mark.normal[2]).normalize();
|
|
9114
9381
|
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);
|
|
@@ -9448,6 +9715,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9448
9715
|
};
|
|
9449
9716
|
|
|
9450
9717
|
// src/pieces.ts
|
|
9718
|
+
var SLAM_MIN_SPEED = 2;
|
|
9719
|
+
var SLAM_BOUNCE = 0.2;
|
|
9720
|
+
var SLAM_FLOOR_NORMAL = 0.7;
|
|
9451
9721
|
var SUB_DT = 1 / 120;
|
|
9452
9722
|
var MU = 0.7;
|
|
9453
9723
|
var SLEEP_AFTER = 0.5;
|
|
@@ -9682,16 +9952,41 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9682
9952
|
const fixed = this.o.staticTopAt?.(x, z, y, step) ?? -Infinity;
|
|
9683
9953
|
return Math.max(top, fixed);
|
|
9684
9954
|
}
|
|
9955
|
+
/** Stops a piece that ran into something on its way from `start` to `pos` (`sweep`): moves it back to where it met it and takes the speed into it. */
|
|
9956
|
+
sweepOn(b, start, pos) {
|
|
9957
|
+
const travel = Math.hypot(pos[0] - start[0], pos[1] - start[1], pos[2] - start[2]);
|
|
9958
|
+
if (travel < 1e-3) return;
|
|
9959
|
+
const hit = this.o.sweep?.(b, start, pos);
|
|
9960
|
+
if (!hit || hit.normal[1] > SLAM_FLOOR_NORMAL) return;
|
|
9961
|
+
const v = b.velocity;
|
|
9962
|
+
const n = hit.normal;
|
|
9963
|
+
const into = -(v[0] * n[0] + v[1] * n[1] + v[2] * n[2]);
|
|
9964
|
+
if (into <= 0) return;
|
|
9965
|
+
const k = Math.min(1, hit.distance / travel);
|
|
9966
|
+
for (let i = 0; i < 3; i++) {
|
|
9967
|
+
pos[i] = start[i] + (pos[i] - start[i]) * k;
|
|
9968
|
+
v[i] += (1 + SLAM_BOUNCE) * into * n[i];
|
|
9969
|
+
}
|
|
9970
|
+
const w = b.angularVelocity;
|
|
9971
|
+
w[0] *= 0.5;
|
|
9972
|
+
w[1] *= 0.5;
|
|
9973
|
+
w[2] *= 0.5;
|
|
9974
|
+
if (into >= SLAM_MIN_SPEED && this.time - b.lastHit > 0.15) {
|
|
9975
|
+
b.lastHit = this.time;
|
|
9976
|
+
this.o.onSlam?.(b, hit, into);
|
|
9977
|
+
}
|
|
9978
|
+
}
|
|
9685
9979
|
integrate(b, dt) {
|
|
9686
9980
|
const c = this.centerOf(b, this.p);
|
|
9687
9981
|
const pos = [c.x, c.y, c.z];
|
|
9982
|
+
const start = [c.x, c.y, c.z];
|
|
9688
9983
|
const q = this.q.copy(b.root.quaternion);
|
|
9689
9984
|
const v = b.velocity;
|
|
9690
9985
|
const w = b.angularVelocity;
|
|
9691
9986
|
const invM = 1 / b.mass;
|
|
9692
9987
|
const steps = clamp6(Math.ceil(dt / SUB_DT), 1, 8);
|
|
9693
9988
|
const h = dt / steps;
|
|
9694
|
-
let
|
|
9989
|
+
let landing2 = 0;
|
|
9695
9990
|
let landPoint = [pos[0], pos[1], pos[2]];
|
|
9696
9991
|
let contactedLast = false;
|
|
9697
9992
|
let touched = false;
|
|
@@ -9794,11 +10089,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9794
10089
|
if (contactedLast) touched = true;
|
|
9795
10090
|
if (!contactedLast) continue;
|
|
9796
10091
|
pos[1] += Math.max(0, maxPen - SLOP * 0.5);
|
|
9797
|
-
let
|
|
9798
|
-
for (const r of rs)
|
|
9799
|
-
const restitution =
|
|
9800
|
-
if (
|
|
9801
|
-
|
|
10092
|
+
let impact3 = 0;
|
|
10093
|
+
for (const r of rs) impact3 = Math.max(impact3, -(v[1] + w[2] * r[0] - w[0] * r[2]));
|
|
10094
|
+
const restitution = impact3 > 2 ? 0.25 : 0;
|
|
10095
|
+
if (impact3 > landing2) {
|
|
10096
|
+
landing2 = impact3;
|
|
9802
10097
|
let sx = 0;
|
|
9803
10098
|
let sz = 0;
|
|
9804
10099
|
for (const r of rs) {
|
|
@@ -9929,14 +10224,15 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9929
10224
|
v[0] *= fd;
|
|
9930
10225
|
v[2] *= fd;
|
|
9931
10226
|
}
|
|
10227
|
+
if (this.o.sweep && !b.held) this.sweepOn(b, start, pos);
|
|
9932
10228
|
b.root.quaternion.copy(q);
|
|
9933
10229
|
const off = this.ax.copy(b.centerV).applyQuaternion(q);
|
|
9934
10230
|
b.root.position.set(pos[0] - off.x, pos[1] - off.y, pos[2] - off.z);
|
|
9935
10231
|
b.root.updateMatrixWorld(true);
|
|
9936
10232
|
b.onGround = contactedLast;
|
|
9937
|
-
if (
|
|
10233
|
+
if (landing2 > HARD_LANDING && this.time - b.lastHit > 0.15) {
|
|
9938
10234
|
b.lastHit = this.time;
|
|
9939
|
-
this.o.onHardLanding(b, landPoint,
|
|
10235
|
+
this.o.onHardLanding(b, landPoint, landing2);
|
|
9940
10236
|
}
|
|
9941
10237
|
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
10238
|
if (still) b.still += dt;
|
|
@@ -11416,6 +11712,69 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
11416
11712
|
return out;
|
|
11417
11713
|
};
|
|
11418
11714
|
var RESTING_HOLD_COS = Math.cos(Math.PI / 6);
|
|
11715
|
+
var assemblies_default = [
|
|
11716
|
+
{
|
|
11717
|
+
id: "brick_wall_plastered",
|
|
11718
|
+
name: "Plastered brick wall",
|
|
11719
|
+
core: { material: "brick", thickness: 0.09 },
|
|
11720
|
+
layers: [{ material: "plaster", thickness: 0.02, faces: "both" }],
|
|
11721
|
+
units: { size: [0.23, 0.075, 0.11], bond: "running", mortar: "plaster" },
|
|
11722
|
+
breach: { area: 0.25 }
|
|
11723
|
+
},
|
|
11724
|
+
{
|
|
11725
|
+
id: "concrete_block_wall",
|
|
11726
|
+
name: "Concrete block wall",
|
|
11727
|
+
core: { material: "concrete", thickness: 0.14 },
|
|
11728
|
+
fill: 0.55,
|
|
11729
|
+
units: { size: [0.39, 0.19, 0.14], bond: "running", mortar: "plaster" },
|
|
11730
|
+
breach: { area: 0.25 }
|
|
11731
|
+
},
|
|
11732
|
+
{
|
|
11733
|
+
id: "reinforced_concrete",
|
|
11734
|
+
name: "Reinforced concrete wall",
|
|
11735
|
+
core: { material: "concrete", thickness: 0.2 },
|
|
11736
|
+
reinforcement: { material: "steel", spacing: 0.18, diameter: 8e-3, residualCapacity: 0.15 },
|
|
11737
|
+
units: { size: [0.6, 0.42, 0.2], bond: "stack", mortar: "concrete" },
|
|
11738
|
+
breach: { area: 0.25 }
|
|
11739
|
+
},
|
|
11740
|
+
{
|
|
11741
|
+
id: "drywall",
|
|
11742
|
+
name: "Drywall partition on steel studs",
|
|
11743
|
+
core: { material: "plaster", thickness: 0.0125 },
|
|
11744
|
+
layers: [{ material: "plaster", thickness: 0.0125, faces: "+" }],
|
|
11745
|
+
depth: 0.095,
|
|
11746
|
+
breach: { area: 0.25 }
|
|
11747
|
+
},
|
|
11748
|
+
{
|
|
11749
|
+
id: "wood_frame_wall",
|
|
11750
|
+
name: "Wood frame wall",
|
|
11751
|
+
core: { material: "plywood", thickness: 0.012 },
|
|
11752
|
+
layers: [{ material: "plaster", thickness: 0.0125, faces: "-" }],
|
|
11753
|
+
depth: 0.1135,
|
|
11754
|
+
breach: { area: 0.25 }
|
|
11755
|
+
},
|
|
11756
|
+
{
|
|
11757
|
+
id: "log_wall",
|
|
11758
|
+
name: "Log wall",
|
|
11759
|
+
core: { material: "wood_soft", thickness: 0.2 },
|
|
11760
|
+
fill: 0.785,
|
|
11761
|
+
breach: { area: 0.25 }
|
|
11762
|
+
},
|
|
11763
|
+
{
|
|
11764
|
+
id: "sheet_metal_shack",
|
|
11765
|
+
name: "Corrugated sheet metal wall",
|
|
11766
|
+
core: { material: "steel", thickness: 6e-4 },
|
|
11767
|
+
depth: 0.02,
|
|
11768
|
+
breach: { area: 0.25 }
|
|
11769
|
+
},
|
|
11770
|
+
{
|
|
11771
|
+
id: "glass_pane",
|
|
11772
|
+
name: "Glass pane",
|
|
11773
|
+
core: { material: "glass", thickness: 6e-3 },
|
|
11774
|
+
breach: { area: 0.1 }
|
|
11775
|
+
}
|
|
11776
|
+
];
|
|
11777
|
+
var SHIPPED = structuredClone(assemblies_default);
|
|
11419
11778
|
|
|
11420
11779
|
// src/spec.ts
|
|
11421
11780
|
function containsOf(value) {
|
|
@@ -11454,7 +11813,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
11454
11813
|
const fallback2 = options.fallback ?? "plastic";
|
|
11455
11814
|
const minSize = options.minSize ?? 0.015;
|
|
11456
11815
|
const inv = specFrameInverse(THREE, root);
|
|
11457
|
-
const
|
|
11816
|
+
const entries2 = [];
|
|
11458
11817
|
const byKey = /* @__PURE__ */ new Map();
|
|
11459
11818
|
const visit = (o) => {
|
|
11460
11819
|
if (!o.visible || o.userData?.destructionIgnore) return;
|
|
@@ -11491,8 +11850,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
11491
11850
|
if (powder) existing.powder = powder;
|
|
11492
11851
|
if (thickness !== void 0) existing.thickness = Math.min(existing.thickness ?? thickness, thickness);
|
|
11493
11852
|
} else {
|
|
11494
|
-
const e = { key: explicit ?? `m${
|
|
11495
|
-
|
|
11853
|
+
const e = { key: explicit ?? `m${entries2.length}`, meshes: [o], box, material, explicitId: explicit, fill, hollow, skin, tags, fixedId: !!explicit, ...contains ? { contains } : {}, ...powder ? { powder } : {}, ...thickness !== void 0 ? { thickness } : {}, name: o.name, parent: hint?.parent, jointKind: hint?.kind };
|
|
11854
|
+
entries2.push(e);
|
|
11496
11855
|
if (explicit) byKey.set(explicit, e);
|
|
11497
11856
|
}
|
|
11498
11857
|
}
|
|
@@ -11510,7 +11869,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
11510
11869
|
const largest = (e) => Math.max(...sizeOf(e.box));
|
|
11511
11870
|
const kept = [];
|
|
11512
11871
|
const tiny = [];
|
|
11513
|
-
for (const e of
|
|
11872
|
+
for (const e of entries2) (e.skin || !e.fixedId && largest(e) < minSize ? tiny : kept).push(e);
|
|
11514
11873
|
for (const t of tiny) {
|
|
11515
11874
|
let best = null;
|
|
11516
11875
|
let bestV = 0;
|
|
@@ -11781,6 +12140,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
11781
12140
|
this.b = tmpV(THREE);
|
|
11782
12141
|
this.m4 = new THREE.Matrix4();
|
|
11783
12142
|
this.ray = new THREE.Raycaster();
|
|
12143
|
+
this.ray.layers.enableAll();
|
|
11784
12144
|
this.box = new THREE.Box3();
|
|
11785
12145
|
this.t = tmpV(THREE);
|
|
11786
12146
|
this.lbox = new THREE.Box3();
|
|
@@ -11788,9 +12148,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
11788
12148
|
/**
|
|
11789
12149
|
* @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
12150
|
*/
|
|
11791
|
-
makeMeshVis(mesh2, home,
|
|
12151
|
+
makeMeshVis(mesh2, home, range2, hosts) {
|
|
11792
12152
|
let host = null;
|
|
11793
|
-
if (
|
|
12153
|
+
if (range2) {
|
|
11794
12154
|
host = hosts?.get(mesh2) ?? null;
|
|
11795
12155
|
if (!host) {
|
|
11796
12156
|
host = { mesh: mesh2, origGeometry: mesh2.geometry, origMaterial: mesh2.material, ownGeo: false, work: null, ownMat: false, sharers: [] };
|
|
@@ -11816,8 +12176,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
11816
12176
|
bottomY: 0,
|
|
11817
12177
|
moved: false,
|
|
11818
12178
|
host,
|
|
11819
|
-
range:
|
|
11820
|
-
merged: host &&
|
|
12179
|
+
range: range2 ? [range2[0], range2[1]] : null,
|
|
12180
|
+
merged: host && range2 ? { host, range: [range2[0], range2[1]] } : null,
|
|
11821
12181
|
loose: false,
|
|
11822
12182
|
collapsed: false,
|
|
11823
12183
|
hidden: false,
|
|
@@ -12386,24 +12746,24 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
12386
12746
|
geo.boundingBox = null;
|
|
12387
12747
|
geo.boundingSphere = null;
|
|
12388
12748
|
}
|
|
12389
|
-
collapseRange(arr,
|
|
12390
|
-
const x = arr[
|
|
12391
|
-
const y = arr[
|
|
12392
|
-
const z = arr[
|
|
12393
|
-
for (let i =
|
|
12749
|
+
collapseRange(arr, range2) {
|
|
12750
|
+
const x = arr[range2[0] * 3];
|
|
12751
|
+
const y = arr[range2[0] * 3 + 1];
|
|
12752
|
+
const z = arr[range2[0] * 3 + 2];
|
|
12753
|
+
for (let i = range2[0] * 3; i < range2[1] * 3; i += 3) {
|
|
12394
12754
|
arr[i] = x;
|
|
12395
12755
|
arr[i + 1] = y;
|
|
12396
12756
|
arr[i + 2] = z;
|
|
12397
12757
|
}
|
|
12398
12758
|
}
|
|
12399
12759
|
/** 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,
|
|
12760
|
+
meltRange(arr, work, range2, melt) {
|
|
12401
12761
|
let minY = Infinity;
|
|
12402
12762
|
let maxY = -Infinity;
|
|
12403
12763
|
let cx = 0;
|
|
12404
12764
|
let cz = 0;
|
|
12405
|
-
const n = Math.max(1,
|
|
12406
|
-
for (let i =
|
|
12765
|
+
const n = Math.max(1, range2[1] - range2[0]);
|
|
12766
|
+
for (let i = range2[0] * 3; i < range2[1] * 3; i += 3) {
|
|
12407
12767
|
minY = Math.min(minY, work[i + 1]);
|
|
12408
12768
|
maxY = Math.max(maxY, work[i + 1]);
|
|
12409
12769
|
cx += work[i];
|
|
@@ -12414,7 +12774,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
12414
12774
|
const h = Math.max(1e-4, maxY - minY);
|
|
12415
12775
|
const sy = lerp5(1, 0.25, melt);
|
|
12416
12776
|
const sxz = 1 + 0.18 * melt;
|
|
12417
|
-
for (let i =
|
|
12777
|
+
for (let i = range2[0] * 3, v = 0; i < range2[1] * 3; i += 3, v++) {
|
|
12418
12778
|
const noise = (Math.imul(v + 1, 2654435761) >>> 0) % 1e3 / 1e3;
|
|
12419
12779
|
arr[i] = cx + (work[i] - cx) * sxz;
|
|
12420
12780
|
arr[i + 2] = cz + (work[i + 2] - cz) * sxz;
|
|
@@ -12668,12 +13028,14 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
12668
13028
|
var QUEUE_CAP = 4e3;
|
|
12669
13029
|
var MIN_FRAGMENTS_PER_SLICE = 8;
|
|
12670
13030
|
var STATIC_REFRESH = 0.5;
|
|
13031
|
+
var STATIC_REFRESH_SLICE_MS = 1;
|
|
12671
13032
|
var STATIC_SPAN = 3;
|
|
12672
13033
|
var FEET_BAND = 0.03;
|
|
12673
13034
|
var TIP_MIN_HEIGHT = 0.3;
|
|
12674
13035
|
var TIP_NARROW = 0.3;
|
|
12675
13036
|
var FIRE_CEILING_SECONDS = 3600;
|
|
12676
13037
|
var GENERIC_REFERENCE_RADIUS = 15;
|
|
13038
|
+
var SWEEP_CORE = 0.15;
|
|
12677
13039
|
var PRIMED_FIRE_RADIUS = 0.2;
|
|
12678
13040
|
var FLUID_COLORS = {
|
|
12679
13041
|
water: 9226495,
|
|
@@ -12716,6 +13078,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
12716
13078
|
if (def.fracture === "tear") return "impact_soft";
|
|
12717
13079
|
return def.fracture === "none" ? "impact_metal" : "impact_wood";
|
|
12718
13080
|
}
|
|
13081
|
+
var shotKey = (def) => impactKey(def).replace("impact_", "shot_");
|
|
12719
13082
|
var dragSoundKey = (def) => impactKey(def).replace("impact_", "drag_");
|
|
12720
13083
|
function fractureKey(def, style) {
|
|
12721
13084
|
switch (style) {
|
|
@@ -12765,14 +13128,34 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
12765
13128
|
/** Objects to measure again; `staticAll` measures every one. */
|
|
12766
13129
|
__publicField(this, "staticDirty", /* @__PURE__ */ new Set());
|
|
12767
13130
|
__publicField(this, "staticAll", true);
|
|
13131
|
+
/** The periodic refresh: the objects still to measure again in this round, a slice per call. */
|
|
13132
|
+
__publicField(this, "staticRound", []);
|
|
12768
13133
|
/** Boxes of the pieces' meshes (as `staticBoxes`), with the time they were measured. */
|
|
12769
13134
|
__publicField(this, "pieceBoxes", /* @__PURE__ */ new WeakMap());
|
|
12770
13135
|
__publicField(this, "staticAt", 0);
|
|
12771
13136
|
__publicField(this, "queue", []);
|
|
12772
13137
|
/** 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
13138
|
__publicField(this, "slices", []);
|
|
12774
|
-
/** When the work of this frame must stop; `Infinity` outside
|
|
13139
|
+
/** When the work of this frame must stop; `Infinity` outside a drain, where a call finishes what it started. */
|
|
12775
13140
|
__publicField(this, "sliceDeadline", Infinity);
|
|
13141
|
+
/** The clock the drain reads; a budget lends its own while it runs the queue. */
|
|
13142
|
+
__publicField(this, "drainClock", () => performance.now());
|
|
13143
|
+
/** true once a frame budget owns the draining: `update` no longer drains by itself. */
|
|
13144
|
+
__publicField(this, "attached", false);
|
|
13145
|
+
__publicField(this, "eventQueue", {
|
|
13146
|
+
id: DEFERRED_QUEUE_ID.DESTRUCTION_EVENTS,
|
|
13147
|
+
pending: () => this.queue.length + this.slices.length,
|
|
13148
|
+
run: (deadline, clock) => this.runBudgeted(deadline, clock),
|
|
13149
|
+
flush: () => {
|
|
13150
|
+
for (let guard = 0; guard < 16 && this.queue.length + this.slices.length > 0; guard++) this.runBudgeted(Infinity, () => 0);
|
|
13151
|
+
},
|
|
13152
|
+
attach: () => {
|
|
13153
|
+
this.attached = true;
|
|
13154
|
+
},
|
|
13155
|
+
detach: () => {
|
|
13156
|
+
this.attached = false;
|
|
13157
|
+
}
|
|
13158
|
+
});
|
|
12776
13159
|
__publicField(this, "unsubscribe");
|
|
12777
13160
|
__publicField(this, "visuals");
|
|
12778
13161
|
__publicField(this, "fragments");
|
|
@@ -12818,6 +13201,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
12818
13201
|
const maxParticles = Math.max(8, Math.floor((o.maxParticles ?? 2500) * (this.low ? 0.5 : 1)));
|
|
12819
13202
|
this.visuals = new Visuals(THREE, this.low, 600);
|
|
12820
13203
|
this.ground = new GroundSpots(THREE, o.scene, this.groundAt, 48);
|
|
13204
|
+
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
13205
|
this.vfx = o.vfx ?? createVfx({ profile: this.quality, gravity: this.gravity, groundAt: this.groundAt, sprites: { capacity: maxParticles, density: this.low ? 0.6 : 1 } });
|
|
12822
13206
|
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
13207
|
this.stopLanding = this.vfx.onLand(this.owner, (x, _y, z, color, tag) => {
|
|
@@ -12837,7 +13221,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
12837
13221
|
staticTopAt: (x, z, y, step) => this.staticTopAt(x, z, y, step),
|
|
12838
13222
|
gravity: this.gravity,
|
|
12839
13223
|
scene: o.scene,
|
|
12840
|
-
onHardLanding: (p, point, speed) => this.hardLanding(p, point, speed)
|
|
13224
|
+
onHardLanding: (p, point, speed) => this.hardLanding(p, point, speed),
|
|
13225
|
+
sweep: (p, from, to) => this.sweepPiece(p, from, to),
|
|
13226
|
+
onSlam: (p, hit, speed) => this.slam(p, hit, speed)
|
|
12841
13227
|
});
|
|
12842
13228
|
this.unsubscribe = this.world.on("*", (ev) => {
|
|
12843
13229
|
if (this.queue.length < QUEUE_CAP) this.queue.push(ev);
|
|
@@ -13027,26 +13413,58 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13027
13413
|
const p = this.pieceSys.get(objectId);
|
|
13028
13414
|
if (p) this.pieceSys.impulse(p, impulse, point);
|
|
13029
13415
|
}
|
|
13416
|
+
/**
|
|
13417
|
+
* What a piece moving from `from` to `to` runs into: the core of its box (its thinnest half each way, at most `SWEEP_CORE`) swept through the world, the piece itself left out,
|
|
13418
|
+
* so a crate thrown by a blast stops at the wall instead of passing through it. Null for a world that cannot sweep.
|
|
13419
|
+
*/
|
|
13420
|
+
sweepPiece(p, from, to) {
|
|
13421
|
+
if (typeof this.world.sweepBox !== "function") return null;
|
|
13422
|
+
const d = [to[0] - from[0], to[1] - from[1], to[2] - from[2]];
|
|
13423
|
+
const travel = Math.hypot(d[0], d[1], d[2]);
|
|
13424
|
+
if (travel < 1e-4) return null;
|
|
13425
|
+
const core = Math.min(SWEEP_CORE, ...p.halfExtents);
|
|
13426
|
+
const box = { min: [from[0] - core, from[1] - core, from[2] - core], max: [from[0] + core, from[1] + core, from[2] + core] };
|
|
13427
|
+
const hit = this.world.sweepBox(box, d, travel, { ignore: p.objectId, spacing: Math.max(0.05, core) });
|
|
13428
|
+
if (!hit) return null;
|
|
13429
|
+
return { object: hit.object, part: hit.part, distance: hit.distance, point: hit.point, normal: hit.normal };
|
|
13430
|
+
}
|
|
13431
|
+
/** A piece thrown into a wall: the wall takes the blow of its weight where it met it, the piece takes it too, and each sounds as its material does. */
|
|
13432
|
+
slam(p, hit, speed) {
|
|
13433
|
+
const energy = 0.5 * p.mass * speed * speed;
|
|
13434
|
+
const direction = [-hit.normal[0], -hit.normal[1], -hit.normal[2]];
|
|
13435
|
+
this.landingSound(hit.object, hit.point, clamp6(speed / 10, 0.2, 1));
|
|
13436
|
+
this.syncBound(p.objectId);
|
|
13437
|
+
try {
|
|
13438
|
+
this.world.hitPart(hit.object, hit.part, { type: "blunt", point: hit.point, energy, radius: 0.1, direction, normal: hit.normal, source: p.objectId });
|
|
13439
|
+
this.world.hit(p.objectId, { type: "blunt", point: hit.point, energy, radius: 0.08, direction: hit.normal, source: "slam" });
|
|
13440
|
+
} catch {
|
|
13441
|
+
}
|
|
13442
|
+
}
|
|
13030
13443
|
hardLanding(p, point, speed) {
|
|
13031
|
-
|
|
13032
|
-
this.sound(key, point, clamp6(speed / 10, 0.2, 1));
|
|
13444
|
+
this.landingSound(p.objectId, point, clamp6(speed / 10, 0.2, 1));
|
|
13033
13445
|
this.syncBound(p.objectId);
|
|
13034
13446
|
try {
|
|
13035
13447
|
this.world.hit(p.objectId, { type: "blunt", point, energy: 0.5 * p.mass * speed * speed, radius: 0.08, direction: [0, -1, 0], source: "fall" });
|
|
13036
13448
|
} catch {
|
|
13037
13449
|
}
|
|
13038
13450
|
}
|
|
13039
|
-
|
|
13451
|
+
/** The impact of the heaviest part of an object (a piece landing): its material and the blunt blow of the fall. */
|
|
13452
|
+
landingSound(objectId, point, volume) {
|
|
13040
13453
|
const st = this.world.state(objectId);
|
|
13041
|
-
let best;
|
|
13454
|
+
let best = "";
|
|
13042
13455
|
let mass = -1;
|
|
13043
13456
|
for (const p of st?.parts ?? []) {
|
|
13044
13457
|
if (p.mass > mass) {
|
|
13045
13458
|
mass = p.mass;
|
|
13046
|
-
best =
|
|
13459
|
+
best = p.material;
|
|
13047
13460
|
}
|
|
13048
13461
|
}
|
|
13049
|
-
|
|
13462
|
+
this.impactSound(best, point, volume, "blunt");
|
|
13463
|
+
}
|
|
13464
|
+
/** The impact on a material by a blow: the legacy family key (`impactKey`) with the more specific keys to try first (`impactSoundKeys`). */
|
|
13465
|
+
impactSound(material, point, volume, blow3) {
|
|
13466
|
+
const def = this.world.materials.get(material);
|
|
13467
|
+
this.sound(impactKey(def), point, volume, impactSoundKeys(material, def, blow3));
|
|
13050
13468
|
}
|
|
13051
13469
|
// ----------------------------------------------------------------------------------------------------------------------------------
|
|
13052
13470
|
// Update
|
|
@@ -13055,7 +13473,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13055
13473
|
if (this.disposed) return;
|
|
13056
13474
|
dt = clamp6(dt, 0, 0.1);
|
|
13057
13475
|
this.time += dt;
|
|
13058
|
-
this.drainQueue();
|
|
13476
|
+
if (!this.attached) this.drainQueue();
|
|
13059
13477
|
for (const b of this.bounds.values()) b.onFrame?.(dt);
|
|
13060
13478
|
for (const b of this.bounds.values()) if (b.soilNode && this.world.state(b.id)?.soil?.status === "yielding") this.onSettle(b.id);
|
|
13061
13479
|
this.updateActive(dt);
|
|
@@ -13066,36 +13484,50 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13066
13484
|
this.ownVfx?.update(dt);
|
|
13067
13485
|
this.syncTransforms();
|
|
13068
13486
|
}
|
|
13487
|
+
deferredQueue() {
|
|
13488
|
+
return this.eventQueue;
|
|
13489
|
+
}
|
|
13069
13490
|
drainQueue() {
|
|
13491
|
+
this.runBudgeted(performance.now() + this.eventBudgetMs, () => performance.now());
|
|
13492
|
+
}
|
|
13493
|
+
/** 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. */
|
|
13494
|
+
runBudgeted(deadline, clock) {
|
|
13070
13495
|
const batch = this.queue.splice(0, this.queue.length);
|
|
13071
|
-
const
|
|
13496
|
+
const outerClock = this.drainClock;
|
|
13497
|
+
this.drainClock = clock;
|
|
13072
13498
|
this.sliceDeadline = deadline;
|
|
13499
|
+
let ran = 0;
|
|
13073
13500
|
try {
|
|
13074
|
-
this.runSlices(deadline);
|
|
13501
|
+
ran = this.runSlices(deadline);
|
|
13075
13502
|
for (let i = 0; i < batch.length; i++) {
|
|
13076
|
-
if (i > 0 &&
|
|
13503
|
+
if ((i > 0 || ran > 0 && this.attached) && clock() >= deadline) {
|
|
13077
13504
|
this.queue.unshift(...batch.slice(i));
|
|
13078
|
-
return;
|
|
13505
|
+
return ran;
|
|
13079
13506
|
}
|
|
13080
13507
|
try {
|
|
13081
13508
|
this.apply(batch[i]);
|
|
13082
13509
|
} catch (err) {
|
|
13083
13510
|
if (typeof console !== "undefined") console.error("game-destruction-three: failed to apply", batch[i].type, err);
|
|
13084
13511
|
}
|
|
13512
|
+
ran++;
|
|
13085
13513
|
}
|
|
13086
13514
|
} finally {
|
|
13087
13515
|
this.sliceDeadline = Infinity;
|
|
13516
|
+
this.drainClock = outerClock;
|
|
13088
13517
|
}
|
|
13518
|
+
return ran;
|
|
13089
13519
|
}
|
|
13090
13520
|
/** Runs the work left by earlier frames until the deadline; at least one piece per frame, so it always finishes. */
|
|
13091
13521
|
runSlices(deadline) {
|
|
13092
|
-
|
|
13522
|
+
let ran = 0;
|
|
13523
|
+
for (; this.slices.length > 0 && (ran === 0 || this.drainClock() < deadline); ran++) {
|
|
13093
13524
|
try {
|
|
13094
13525
|
this.slices.shift()();
|
|
13095
13526
|
} catch (err) {
|
|
13096
13527
|
if (typeof console !== "undefined") console.error("game-destruction-three: failed to run deferred work", err);
|
|
13097
13528
|
}
|
|
13098
13529
|
}
|
|
13530
|
+
return ran;
|
|
13099
13531
|
}
|
|
13100
13532
|
/**
|
|
13101
13533
|
* 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
|
|
@@ -13164,9 +13596,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13164
13596
|
staticNow() {
|
|
13165
13597
|
if (this.time - this.staticAt > STATIC_REFRESH) {
|
|
13166
13598
|
this.staticAt = this.time;
|
|
13167
|
-
this.
|
|
13599
|
+
if (this.staticRound.length === 0) this.staticRound = [...this.bounds.keys()].reverse();
|
|
13168
13600
|
}
|
|
13169
|
-
if (!this.staticAll && this.staticDirty.size === 0) return this.staticBoxes;
|
|
13601
|
+
if (!this.staticAll && this.staticDirty.size === 0 && this.staticRound.length === 0) return this.staticBoxes;
|
|
13170
13602
|
const box = new this.THREE.Box3();
|
|
13171
13603
|
const measure = (b) => {
|
|
13172
13604
|
if (b.piece || this.pieceSys.get(b.id)) {
|
|
@@ -13186,14 +13618,21 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13186
13618
|
}
|
|
13187
13619
|
this.staticBoxes.set(b.id, out.length > 6 ? out : []);
|
|
13188
13620
|
};
|
|
13621
|
+
const measureId = (id) => {
|
|
13622
|
+
const b = this.bounds.get(id);
|
|
13623
|
+
if (b) measure(b);
|
|
13624
|
+
else this.staticBoxes.delete(id);
|
|
13625
|
+
};
|
|
13189
13626
|
if (this.staticAll) {
|
|
13190
13627
|
this.staticBoxes.clear();
|
|
13191
13628
|
for (const b of this.bounds.values()) measure(b);
|
|
13629
|
+
this.staticRound = [];
|
|
13192
13630
|
} else {
|
|
13193
|
-
for (const id of this.staticDirty)
|
|
13194
|
-
|
|
13195
|
-
|
|
13196
|
-
|
|
13631
|
+
for (const id of this.staticDirty) measureId(id);
|
|
13632
|
+
const until = performance.now() + STATIC_REFRESH_SLICE_MS;
|
|
13633
|
+
while (this.staticRound.length > 0) {
|
|
13634
|
+
measureId(this.staticRound.pop());
|
|
13635
|
+
if (performance.now() >= until) break;
|
|
13197
13636
|
}
|
|
13198
13637
|
}
|
|
13199
13638
|
this.staticAll = false;
|
|
@@ -13253,7 +13692,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13253
13692
|
case "ricochet": {
|
|
13254
13693
|
const d = ev.direction;
|
|
13255
13694
|
this.vfx.burst("sparks", ev.point, d, 4 + Math.min(10, ev.energy / 150), 4);
|
|
13256
|
-
this.
|
|
13695
|
+
const glanced = this.partVis(ev.object, ev.part)?.material ?? "";
|
|
13696
|
+
this.sound("ricochet", ev.point, 0.7, ricochetSoundKeys(glanced, this.world.materials.get(glanced)));
|
|
13257
13697
|
break;
|
|
13258
13698
|
}
|
|
13259
13699
|
case "debris":
|
|
@@ -13295,7 +13735,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13295
13735
|
this.vfx.fire({ point: ev.point, radius: PRIMED_FIRE_RADIUS, life: ev.delay });
|
|
13296
13736
|
this.primedBurning.add(`${ev.object}|${ev.part}`);
|
|
13297
13737
|
this.activate(ev.object, ev.part);
|
|
13298
|
-
this.sound("
|
|
13738
|
+
this.sound("primed", ev.point, 0.7, { seconds: ev.delay });
|
|
13299
13739
|
this.sound("burn_loop", ev.point, 0.5);
|
|
13300
13740
|
}
|
|
13301
13741
|
break;
|
|
@@ -13409,8 +13849,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13409
13849
|
const b = this.bounds.get(objectId);
|
|
13410
13850
|
return b ? [b.root.position.x, b.root.position.y, b.root.position.z] : [0, 0, 0];
|
|
13411
13851
|
}
|
|
13412
|
-
sound(key, point, volume) {
|
|
13413
|
-
this.o.onSound?.(key, point, volume);
|
|
13852
|
+
sound(key, point, volume, detail) {
|
|
13853
|
+
if (detail) this.o.onSound?.(key, point, volume, detail);
|
|
13854
|
+
else this.o.onSound?.(key, point, volume);
|
|
13414
13855
|
}
|
|
13415
13856
|
partVis(objectId, partId) {
|
|
13416
13857
|
return this.bounds.get(objectId)?.parts.get(partId) ?? null;
|
|
@@ -13475,7 +13916,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13475
13916
|
const reach = shown.reduce((widest, mv) => Math.max(widest, this.visuals.dentReach(mv, wp, ev.dent)), ev.dent.radius);
|
|
13476
13917
|
this.visuals.applyPartDent(shown, wp, wn, ev.dent, reach);
|
|
13477
13918
|
if (def?.tags?.includes("metal")) this.vfx.burst("sparks", ev.worldPoint, [wn.x, wn.y, wn.z], 4 + Math.min(8, ev.dent.depth * 150), 3.2);
|
|
13478
|
-
this.
|
|
13919
|
+
this.impactSound(pv.material, ev.worldPoint, clamp6(0.3 + ev.dent.depth * 12, 0.3, 1), ev.blow);
|
|
13479
13920
|
}
|
|
13480
13921
|
onHole(ev) {
|
|
13481
13922
|
const b = this.bounds.get(ev.object);
|
|
@@ -13504,7 +13945,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13504
13945
|
this.vfx.burst("dust", ev.worldPoint, [n.x, n.y, n.z], this.low ? 5 : 10, 0.7, puff3);
|
|
13505
13946
|
if (ev.hole.through && ev.exitPoint) this.vfx.burst("dust", ev.exitPoint, [-n.x, -n.y, -n.z], this.low ? 10 : 20, 1.3, puff3);
|
|
13506
13947
|
}
|
|
13507
|
-
this.sound(
|
|
13948
|
+
this.sound(shotKey(def), ev.worldPoint, 0.6, impactSoundKeys(pv.material, def, ev.blow));
|
|
13508
13949
|
}
|
|
13509
13950
|
onCrack(ev) {
|
|
13510
13951
|
const b = this.bounds.get(ev.object);
|
|
@@ -13514,13 +13955,13 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13514
13955
|
const wp = new this.THREE.Vector3(ev.worldPoint[0], ev.worldPoint[1], ev.worldPoint[2]);
|
|
13515
13956
|
const mv = this.nearestMesh(pv, wp);
|
|
13516
13957
|
if (mv) {
|
|
13517
|
-
const
|
|
13958
|
+
const glass2 = !!def?.visual?.transparent;
|
|
13518
13959
|
const own = this.partColor(pv, def) ?? fallbackColor(pv.material, def);
|
|
13519
|
-
const color =
|
|
13520
|
-
const length =
|
|
13960
|
+
const color = glass2 ? 15267071 : new this.THREE.Color(own).multiplyScalar(0.45).getHex();
|
|
13961
|
+
const length = glass2 ? ev.crack.length : Math.min(ev.crack.length, OPAQUE_CRACK);
|
|
13521
13962
|
this.visuals.addCrack(mv, wp, this.worldDir(b, ev.crack.normal), ev.crack.rays, length, color, hashNumbers(ev.time, ev.worldPoint[0], ev.worldPoint[1], ev.worldPoint[2]));
|
|
13522
13963
|
}
|
|
13523
|
-
this.
|
|
13964
|
+
this.impactSound(pv.material, ev.worldPoint, 0.5, ev.blow);
|
|
13524
13965
|
}
|
|
13525
13966
|
/** Outward normal of the face of `mv` nearest to a world point: the thin axis of its world box (right for a wall; a sloped face is left to the vertex-range binding). */
|
|
13526
13967
|
faceNormal(mv, point) {
|
|
@@ -13577,7 +14018,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13577
14018
|
this.fragments.spawn(fr, this.fragments.materialFor(fr.material, fdef === def || !fdef ? color : fallbackColor(fr.material, fdef), false), ev.object, maxAlive);
|
|
13578
14019
|
}
|
|
13579
14020
|
if (!played) this.vfx.burst("dust", ev.opening.center, [0, 1, 0], 14, 1.2, { color });
|
|
13580
|
-
this.
|
|
14021
|
+
this.impactSound(pv?.material ?? "", ev.opening.center, 0.8, ev.blow);
|
|
13581
14022
|
}
|
|
13582
14023
|
/**
|
|
13583
14024
|
* A unit came out of a wall of units: the unit flies as a fragment with a puff of its dust, and the marks drawn on it (bullet holes, spalls, the impact
|
|
@@ -13595,7 +14036,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13595
14036
|
this.fragments.spawn(fr, this.fragments.materialFor(fr.material, tone, false), ev.object, maxAlive);
|
|
13596
14037
|
}
|
|
13597
14038
|
if (ev.torn) {
|
|
13598
|
-
this.
|
|
14039
|
+
this.impactSound(pv?.material ?? "", ev.center, 0.5, ev.blow);
|
|
13599
14040
|
return;
|
|
13600
14041
|
}
|
|
13601
14042
|
const T = this.THREE;
|
|
@@ -13605,7 +14046,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13605
14046
|
for (const part of this.bounds.get(ev.object)?.parts.values() ?? []) for (const mv of part.meshes) this.visuals.clearDecals(mv, center, half, rotation2);
|
|
13606
14047
|
this.vfx.peekWorkshop()?.decals.clearInside(ev.center, [half.x, half.y, half.z], ev.rotation);
|
|
13607
14048
|
this.vfx.burst("dust", ev.center, [0, 1, 0], 8, 0.8, { color });
|
|
13608
|
-
this.
|
|
14049
|
+
this.impactSound(pv?.material ?? "", ev.center, 0.7, ev.blow);
|
|
13609
14050
|
}
|
|
13610
14051
|
onFracture(ev) {
|
|
13611
14052
|
const b = this.bounds.get(ev.object);
|
|
@@ -13646,14 +14087,14 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13646
14087
|
default:
|
|
13647
14088
|
this.vfx.burst("sparks", ev.point, up, 10, 4);
|
|
13648
14089
|
}
|
|
13649
|
-
this.sound(fractureKey(def, ev.style), ev.point, clamp6(0.4 + ev.fragments.length * 0.03, 0.4, 1));
|
|
14090
|
+
this.sound(fractureKey(def, ev.style), ev.point, clamp6(0.4 + ev.fragments.length * 0.03, 0.4, 1), breakSoundKeys(matId, def, ev.blow));
|
|
13650
14091
|
}
|
|
13651
14092
|
/** Spawns the fragments of a fracture from index `from`; what does not fit in this frame's budget is spawned by the next frames. */
|
|
13652
14093
|
spawnFragments(ev, from, look) {
|
|
13653
14094
|
const maxAlive = this.low ? Math.floor(this.maxFragments / 2) : this.maxFragments;
|
|
13654
14095
|
const step = this.low ? 2 : 1;
|
|
13655
14096
|
for (let i = from; i < ev.fragments.length; i += step) {
|
|
13656
|
-
if (i - from >= MIN_FRAGMENTS_PER_SLICE * step &&
|
|
14097
|
+
if (i - from >= MIN_FRAGMENTS_PER_SLICE * step && this.drainClock() >= this.sliceDeadline) {
|
|
13657
14098
|
this.slices.push(() => this.spawnFragments(ev, i, look));
|
|
13658
14099
|
return;
|
|
13659
14100
|
}
|
|
@@ -13677,7 +14118,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13677
14118
|
if (!st || st.groups.length < 2) return;
|
|
13678
14119
|
const loose = st.groups.length - 1;
|
|
13679
14120
|
for (let i = 0; i < loose; i++) {
|
|
13680
|
-
if (i > 0 &&
|
|
14121
|
+
if (i > 0 && this.drainClock() >= this.sliceDeadline) {
|
|
13681
14122
|
this.slices.push(() => {
|
|
13682
14123
|
this.staticDirty.add(objectId);
|
|
13683
14124
|
this.splitLoose(objectId);
|
|
@@ -13783,10 +14224,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13783
14224
|
const report = options.report;
|
|
13784
14225
|
const confinement = report?.find((r) => r.blast !== void 0)?.blast?.confinement ?? 1;
|
|
13785
14226
|
const energy = ev.energy * confinement;
|
|
13786
|
-
const reachOf = (objectId) => report?.find((r) => r.object === objectId)?.blast?.shelter ?? 1;
|
|
14227
|
+
const reachOf = (objectId, at3) => report?.find((r) => r.object === objectId)?.blast?.shelter ?? this.world.blastReach?.(p, at3, { ignore: ev.object ? [ev.object, objectId] : [objectId] }) ?? 1;
|
|
13787
14228
|
const shove = (pc) => {
|
|
13788
14229
|
this.pieceSys.centerOf(pc, c);
|
|
13789
|
-
|
|
14230
|
+
if (c.distanceTo(this.v2.set(p[0], p[1], p[2])) > ev.radius) return;
|
|
14231
|
+
const hit = blastShove({ point: p, energy, radius: ev.radius, reach: reachOf(pc.objectId, [c.x, c.y, c.z]) }, { position: [c.x, c.y, c.z], mass: pc.mass }, this.o.blastShove);
|
|
13790
14232
|
if (!hit) return;
|
|
13791
14233
|
const spread = pc.halfExtents[0];
|
|
13792
14234
|
const momentum = hit.speed * pc.mass;
|
|
@@ -13795,7 +14237,12 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13795
14237
|
for (const pc of [...this.pieceSys.list]) shove(pc);
|
|
13796
14238
|
if (this.o.shoveable) this.loosen(p, ev.radius, energy, reachOf);
|
|
13797
14239
|
this.fragments.blast(p, ev.radius, Math.min(20, Math.sqrt(energy) * 0.08));
|
|
13798
|
-
this.sound("explode", p, clamp6(0.5 + ev.radius / 10, 0.5, 1)
|
|
14240
|
+
this.sound("explode", p, clamp6(0.5 + ev.radius / 10, 0.5, 1), {
|
|
14241
|
+
radius: ev.radius,
|
|
14242
|
+
...ev.effect !== void 0 ? { effect: ev.effect } : {},
|
|
14243
|
+
...options.effect !== void 0 ? { effect: options.effect } : {},
|
|
14244
|
+
...report ? { confinement } : {}
|
|
14245
|
+
});
|
|
13799
14246
|
}
|
|
13800
14247
|
/** The bound objects without a piece that the host lets a blast move (`shoveable`): the ones it reaches with enough speed become pieces and get the shove. */
|
|
13801
14248
|
loosen(point, radius, energy, reachOf) {
|
|
@@ -13806,7 +14253,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13806
14253
|
const { half, center } = this.measure(b.root, this.meshesOf(b));
|
|
13807
14254
|
const world = center.clone().applyQuaternion(b.root.quaternion).add(b.root.position);
|
|
13808
14255
|
const mass = this.massOf(b.id);
|
|
13809
|
-
|
|
14256
|
+
if (world.distanceTo(this.v2.set(point[0], point[1], point[2])) > radius) continue;
|
|
14257
|
+
const hit = blastShove({ point, energy, radius, reach: reachOf(b.id, [world.x, world.y, world.z]) }, { position: [world.x, world.y, world.z], mass }, this.o.blastShove);
|
|
13810
14258
|
if (!hit) continue;
|
|
13811
14259
|
const piece = this.makePiece(b.id, { mass });
|
|
13812
14260
|
const body = this.pieceSys.get(b.id);
|
|
@@ -13830,9 +14278,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13830
14278
|
this.fires.set(key, { object: ev.object, part: ev.part, point: ev.point, follow: { x: ev.point[0], y: ev.point[1], z: ev.point[2] } });
|
|
13831
14279
|
}
|
|
13832
14280
|
stopFire(key) {
|
|
13833
|
-
const
|
|
13834
|
-
if (!
|
|
13835
|
-
if (
|
|
14281
|
+
const fire2 = this.fires.get(key);
|
|
14282
|
+
if (!fire2) return;
|
|
14283
|
+
if (fire2.handle !== void 0) this.vfx.stop(fire2.handle);
|
|
13836
14284
|
this.fires.delete(key);
|
|
13837
14285
|
}
|
|
13838
14286
|
// ----------------------------------------------------------------------------------------------------------------------------------
|
|
@@ -13869,9 +14317,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13869
14317
|
if (ps.status === "melted") this.onGone(a.object, pv, "melted", def);
|
|
13870
14318
|
else if (ps.status === "ashes") this.onGone(a.object, pv, "ashes", def);
|
|
13871
14319
|
}
|
|
13872
|
-
const
|
|
13873
|
-
if (
|
|
13874
|
-
if (ps.burning && ps.status !== "broken" && ps.status !== "ashes" && ps.status !== "melted") this.emitFire(
|
|
14320
|
+
const fire2 = this.fires.get(key);
|
|
14321
|
+
if (fire2) {
|
|
14322
|
+
if (ps.burning && ps.status !== "broken" && ps.status !== "ashes" && ps.status !== "melted") this.emitFire(fire2, pv, def);
|
|
13875
14323
|
else this.stopFire(key);
|
|
13876
14324
|
}
|
|
13877
14325
|
const hot = def?.visual?.glows === true && ps.temperature > GLOW_FROM_C;
|
|
@@ -14001,10 +14449,12 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14001
14449
|
this.scene.remove(b.root);
|
|
14002
14450
|
this.pieceSys.remove(b.id);
|
|
14003
14451
|
this.bounds.delete(b.id);
|
|
14452
|
+
this.staticDirty.add(b.id);
|
|
14004
14453
|
}
|
|
14005
14454
|
}
|
|
14006
14455
|
}
|
|
14007
14456
|
clear() {
|
|
14457
|
+
this.staticAll = true;
|
|
14008
14458
|
this.queue.length = 0;
|
|
14009
14459
|
this.slices.length = 0;
|
|
14010
14460
|
this.active.clear();
|
|
@@ -14031,6 +14481,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14031
14481
|
this.rng = createRng(RNG_SEED);
|
|
14032
14482
|
this.time = 0;
|
|
14033
14483
|
this.staticAt = 0;
|
|
14484
|
+
this.staticRound = [];
|
|
14485
|
+
this.staticAll = true;
|
|
14034
14486
|
this.throttles.clear();
|
|
14035
14487
|
this.fragments.reset();
|
|
14036
14488
|
this.pieceSys.reset();
|
|
@@ -14056,6 +14508,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14056
14508
|
const materials = [kit.discMaterial().clone(), kit.patchMaterial(8421504).clone(), kit.lineMaterial(0).clone()];
|
|
14057
14509
|
const group = new T.Group();
|
|
14058
14510
|
group.add(new T.Mesh(kit.discGeometry(), materials[0]), new T.Mesh(kit.discGeometry(), materials[1]), new T.LineSegments(geometry, materials[2]));
|
|
14511
|
+
group.add(...this.fragments.warmMeshes());
|
|
14059
14512
|
for (const probe of group.children) probe.frustumCulled = false;
|
|
14060
14513
|
this.warmProbes = { group, geometry, materials };
|
|
14061
14514
|
return { group, dispose: () => {
|
|
@@ -14086,7 +14539,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14086
14539
|
}
|
|
14087
14540
|
};
|
|
14088
14541
|
|
|
14089
|
-
// ../game-audio/dist/
|
|
14542
|
+
// ../game-audio/dist/chunk-7TLFY7VO.js
|
|
14090
14543
|
var DISTANCE_CURVES = {
|
|
14091
14544
|
/** The game's `sfx`: `vol / (1 + d² × 0.004)`, cutoff `/ (1 + d × 0.01)`, silent below 0.01 (jogo L62091–62092, L62101). */
|
|
14092
14545
|
game_sfx: { model: "inverse_square", k: 4e-3, cutoffK: 0.01, minGain: 0.01 },
|
|
@@ -14101,31 +14554,6 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14101
14554
|
/** The game's animal calls: `max(0, 1 − d / 70) × 0.25`, silent at or below 0.005 (jogo L66627–66628). */
|
|
14102
14555
|
animals: { model: "linear", range: 70, scale: 0.25, minGain: 5e-3 }
|
|
14103
14556
|
};
|
|
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
14557
|
var game_sfx_default = {
|
|
14130
14558
|
sources: ["jogo:L62089-62107"],
|
|
14131
14559
|
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 +14598,505 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14170
14598
|
};
|
|
14171
14599
|
}
|
|
14172
14600
|
var GAME_SFX_BANK = { id: "game_sfx", sounds: [...GAME_SFX_CLASSES, GAME_SFX_FALLBACK].map((row) => noiseClassRecipe(row)) };
|
|
14601
|
+
var mix_profiles_default = [
|
|
14602
|
+
{
|
|
14603
|
+
id: "game",
|
|
14604
|
+
master: 0.5,
|
|
14605
|
+
noise: {
|
|
14606
|
+
seconds: 1.5,
|
|
14607
|
+
create: "lazy"
|
|
14608
|
+
},
|
|
14609
|
+
sources: [
|
|
14610
|
+
"jogo:L62077-62081 master 0.5 to the output",
|
|
14611
|
+
"jogo:L62082-62088 noise 1.5 s made by the first sound that needs it"
|
|
14612
|
+
]
|
|
14613
|
+
},
|
|
14614
|
+
{
|
|
14615
|
+
id: "art_scene",
|
|
14616
|
+
master: 0.85,
|
|
14617
|
+
compressor: {
|
|
14618
|
+
threshold: -12,
|
|
14619
|
+
ratio: 5
|
|
14620
|
+
},
|
|
14621
|
+
reverb: {
|
|
14622
|
+
seconds: 2.2,
|
|
14623
|
+
decay: 3.4,
|
|
14624
|
+
wet: 0.4
|
|
14625
|
+
},
|
|
14626
|
+
noise: {
|
|
14627
|
+
seconds: 2,
|
|
14628
|
+
create: "eager"
|
|
14629
|
+
},
|
|
14630
|
+
sources: [
|
|
14631
|
+
"cena:L1308 compressor -12 dB, ratio 5",
|
|
14632
|
+
"cena:L1309 master 0.85",
|
|
14633
|
+
"cena:L1310 convolver, impulse 2.2 s of (1 - i/len)^3.4",
|
|
14634
|
+
"cena:L1311 wet 0.4",
|
|
14635
|
+
"cena:L1312 noise 2 s made with the chain"
|
|
14636
|
+
]
|
|
14637
|
+
},
|
|
14638
|
+
{
|
|
14639
|
+
id: "kit",
|
|
14640
|
+
master: 0.85,
|
|
14641
|
+
compressor: {
|
|
14642
|
+
threshold: -12,
|
|
14643
|
+
ratio: 5
|
|
14644
|
+
},
|
|
14645
|
+
reverb: {
|
|
14646
|
+
seconds: 2.2,
|
|
14647
|
+
decay: 3.4,
|
|
14648
|
+
wet: 0.4
|
|
14649
|
+
},
|
|
14650
|
+
noise: {
|
|
14651
|
+
seconds: 2,
|
|
14652
|
+
create: "eager"
|
|
14653
|
+
},
|
|
14654
|
+
sources: [
|
|
14655
|
+
"sdk: the art scene chain (cena L1308-L1312) with a limiter after the compressor: the page has none, and a blast with its debris and a fire catching reached +7 dBFS"
|
|
14656
|
+
],
|
|
14657
|
+
limiter: {
|
|
14658
|
+
ceiling: -1,
|
|
14659
|
+
knee: -6
|
|
14660
|
+
}
|
|
14661
|
+
}
|
|
14662
|
+
];
|
|
14663
|
+
var MIX_PROFILES = Object.fromEntries(
|
|
14664
|
+
mix_profiles_default.map((profile) => [profile.id, profile])
|
|
14665
|
+
);
|
|
14666
|
+
var BENCH_BANK = {
|
|
14667
|
+
id: "audio_bench",
|
|
14668
|
+
sounds: [
|
|
14669
|
+
{
|
|
14670
|
+
id: "bench_ping",
|
|
14671
|
+
bus: "ui",
|
|
14672
|
+
pan: true,
|
|
14673
|
+
duration: 0.25,
|
|
14674
|
+
layers: [
|
|
14675
|
+
{
|
|
14676
|
+
gain: 0.5,
|
|
14677
|
+
wet: 0.3,
|
|
14678
|
+
source: { kind: "osc", wave: "sine", frequency: { from: 880 } },
|
|
14679
|
+
envelope: [
|
|
14680
|
+
[0, 1e-4, "set", "fixed"],
|
|
14681
|
+
[2e-3, 1, "exp"],
|
|
14682
|
+
["1d", 1e-4, "exp", "fixed"]
|
|
14683
|
+
]
|
|
14684
|
+
}
|
|
14685
|
+
],
|
|
14686
|
+
falloff: DISTANCE_CURVES.art_scene,
|
|
14687
|
+
sources: ["sdk: bench probe, a 880 Hz ping through the art scene output stage"]
|
|
14688
|
+
},
|
|
14689
|
+
{
|
|
14690
|
+
id: "bench_hiss",
|
|
14691
|
+
bus: "ui",
|
|
14692
|
+
pan: true,
|
|
14693
|
+
duration: [0.3, 0.9],
|
|
14694
|
+
layers: [
|
|
14695
|
+
{
|
|
14696
|
+
gain: 0.4,
|
|
14697
|
+
source: { kind: "noise", offset: "random", offsetScale: 1.5, loop: true },
|
|
14698
|
+
filters: { mode: "serial", bank: [{ type: "highpass", frequency: { set: 3e3 }, q: 0.7 }] },
|
|
14699
|
+
envelope: [
|
|
14700
|
+
[0, 1e-4, "set", "fixed"],
|
|
14701
|
+
[2e-3, 1, "exp"],
|
|
14702
|
+
["1d", 1e-4, "exp", "fixed"]
|
|
14703
|
+
]
|
|
14704
|
+
}
|
|
14705
|
+
],
|
|
14706
|
+
falloff: DISTANCE_CURVES.art_scene,
|
|
14707
|
+
sources: ["sdk: bench probe, a highpass hiss of random length"]
|
|
14708
|
+
}
|
|
14709
|
+
]
|
|
14710
|
+
};
|
|
14711
|
+
var entries = (category, list) => list.map(([id, priority, current]) => ({ id, category, priority, ...current ? { current } : {} }));
|
|
14712
|
+
var SFX_CATALOG = [
|
|
14713
|
+
...entries("armas", [
|
|
14714
|
+
["pistol_22lr", 2, "sfx_pistol"],
|
|
14715
|
+
["pistol_9mm", 1, "sfx_pistol"],
|
|
14716
|
+
["pistol_45", 1, "sfx_pistol"],
|
|
14717
|
+
["revolver_38_357", 2, "sfx_pistol"],
|
|
14718
|
+
["revolver_44_50ae", 3, "sfx_pistol"],
|
|
14719
|
+
["smg_9mm_auto", 1, "sfx_pistol"],
|
|
14720
|
+
["shotgun_12", 1, "sfx_shotgun"],
|
|
14721
|
+
["rifle_5_56", 1, "sfx_rifle"],
|
|
14722
|
+
["rifle_7_62x39", 1, "sfx_rifle"],
|
|
14723
|
+
["rifle_auto_burst", 1, "sfx_rifle"],
|
|
14724
|
+
["rifle_308_3006", 2, "sfx_rifle"],
|
|
14725
|
+
["sniper_338_300wm", 2, "sfx_rifle"],
|
|
14726
|
+
["antimateriel_50bmg", 3, "sfx_rifle"],
|
|
14727
|
+
["suppressed_pistol", 2],
|
|
14728
|
+
["suppressed_rifle", 2],
|
|
14729
|
+
["gunshot_far_pistol", 1],
|
|
14730
|
+
["gunshot_far_rifle", 1],
|
|
14731
|
+
["gunshot_far_shotgun", 1],
|
|
14732
|
+
["gunshot_far_heavy", 2],
|
|
14733
|
+
["gunshot_tail_indoor", 1],
|
|
14734
|
+
["gunshot_tail_outdoor", 1],
|
|
14735
|
+
["bullet_crack_by", 2],
|
|
14736
|
+
["bullet_whiz", 2],
|
|
14737
|
+
["ricochet_metal", 2, "ping"],
|
|
14738
|
+
["dry_fire", 1],
|
|
14739
|
+
["mag_out", 1],
|
|
14740
|
+
["mag_in", 1],
|
|
14741
|
+
["slide_pull_release", 1],
|
|
14742
|
+
["pump_cycle", 1],
|
|
14743
|
+
["casing_drop_concreto_pistol", 1],
|
|
14744
|
+
["casing_drop_concreto_rifle", 1],
|
|
14745
|
+
["casing_drop_concreto_shell", 1],
|
|
14746
|
+
["casing_drop_madeira_pistol", 1],
|
|
14747
|
+
["casing_drop_madeira_rifle", 1],
|
|
14748
|
+
["casing_drop_madeira_shell", 1],
|
|
14749
|
+
["casing_drop_metal_pistol", 1],
|
|
14750
|
+
["casing_drop_metal_rifle", 1],
|
|
14751
|
+
["casing_drop_metal_shell", 1],
|
|
14752
|
+
["casing_drop_terra_pistol", 1],
|
|
14753
|
+
["casing_drop_terra_rifle", 1],
|
|
14754
|
+
["casing_drop_terra_shell", 1],
|
|
14755
|
+
["rocket_fire", 2]
|
|
14756
|
+
]),
|
|
14757
|
+
...entries("explosoes-fogo", [
|
|
14758
|
+
["explosion_grenade", 1, "boom"],
|
|
14759
|
+
["explosion_big", 1, "boom_big"],
|
|
14760
|
+
["explosion_car", 2, "boom_big"],
|
|
14761
|
+
["explosion_far", 1],
|
|
14762
|
+
["debris_rain_stone", 1],
|
|
14763
|
+
["molotov_break_ignite", 1, "glass_shatter"],
|
|
14764
|
+
["fire_ignite", 1, "hiss"],
|
|
14765
|
+
["fire_loop_small", 1],
|
|
14766
|
+
["fire_loop_large", 1],
|
|
14767
|
+
["fire_extinguish", 2, "hiss"],
|
|
14768
|
+
["gas_leak_hiss", 2],
|
|
14769
|
+
["structure_collapse", 2, "crumble"]
|
|
14770
|
+
]),
|
|
14771
|
+
...entries("personagem", [
|
|
14772
|
+
["footstep_concrete_walk", 1, "sfx_step"],
|
|
14773
|
+
["footstep_wood_walk", 1, "sfx_step"],
|
|
14774
|
+
["footstep_dirt_walk", 1, "sfx_step"],
|
|
14775
|
+
["footstep_grass_walk", 1, "sfx_step"],
|
|
14776
|
+
["footstep_gravel_walk", 1, "sfx_step"],
|
|
14777
|
+
["land_concrete", 1, "sfx_step"],
|
|
14778
|
+
["punch_swing", 1],
|
|
14779
|
+
["punch_hit_body", 1, "sfx_flesh"],
|
|
14780
|
+
["kick_hit", 1, "sfx_flesh"],
|
|
14781
|
+
["knife_stab", 1],
|
|
14782
|
+
["blunt_hit", 1],
|
|
14783
|
+
["bite", 1, "sfx_flesh"],
|
|
14784
|
+
["body_fall", 1],
|
|
14785
|
+
["player_hurt", 2]
|
|
14786
|
+
]),
|
|
14787
|
+
...entries("veiculos", [
|
|
14788
|
+
["engine_loop_car", 1],
|
|
14789
|
+
["engine_start", 1],
|
|
14790
|
+
["crash_light", 1, "crash"],
|
|
14791
|
+
["crash_heavy", 1, "crash"],
|
|
14792
|
+
["car_door_open", 1, "latch"],
|
|
14793
|
+
["car_door_close", 1, "shut_metal"],
|
|
14794
|
+
["horn_car", 2, "horn"],
|
|
14795
|
+
["tire_skid_asphalt", 2]
|
|
14796
|
+
]),
|
|
14797
|
+
...entries("itens-portas", [
|
|
14798
|
+
["item_pickup_metal", 1],
|
|
14799
|
+
["can_open", 1],
|
|
14800
|
+
["can_crush", 1],
|
|
14801
|
+
["door_wood_open", 1, "latch"],
|
|
14802
|
+
["door_wood_close", 1, "shut_wood"],
|
|
14803
|
+
["door_creak", 1, "creak_wood"],
|
|
14804
|
+
["door_locked_rattle", 1, "rattle"],
|
|
14805
|
+
["drawer_open", 2, "slide"],
|
|
14806
|
+
["fridge_close", 2, "shut_metal"]
|
|
14807
|
+
]),
|
|
14808
|
+
...entries("ambiente", [
|
|
14809
|
+
["rain_loop", 2],
|
|
14810
|
+
["wind_loop", 2],
|
|
14811
|
+
["thunder_near", 2],
|
|
14812
|
+
["thunder_far", 2],
|
|
14813
|
+
["water_splash", 2]
|
|
14814
|
+
])
|
|
14815
|
+
];
|
|
14816
|
+
var SFX_MATERIALS = [
|
|
14817
|
+
["steel", "metal"],
|
|
14818
|
+
["aluminium", "metal"],
|
|
14819
|
+
["wood", "wood"],
|
|
14820
|
+
["plywood", "wood"],
|
|
14821
|
+
["mdf", "wood"],
|
|
14822
|
+
["glass", "glass"],
|
|
14823
|
+
["tempered_glass", "glass"],
|
|
14824
|
+
["ceramic", "glass"],
|
|
14825
|
+
["concrete", "masonry"],
|
|
14826
|
+
["brick", "masonry"],
|
|
14827
|
+
["plaster", "masonry"],
|
|
14828
|
+
["plastic", "soft"],
|
|
14829
|
+
["fabric", "soft"],
|
|
14830
|
+
["paper", "soft"],
|
|
14831
|
+
["foliage", "soft"],
|
|
14832
|
+
["soil", "soft"],
|
|
14833
|
+
["flesh", "soft"]
|
|
14834
|
+
];
|
|
14835
|
+
var SFX_BLOWS = ["bullet", "blunt", "blade", "break", "drag"];
|
|
14836
|
+
var CURRENT_BY_GROUP = {
|
|
14837
|
+
metal: { bullet: "clang", blunt: "clang", blade: "clang", break: "clang", drag: "drag_metal" },
|
|
14838
|
+
wood: { bullet: "wood_hit", blunt: "wood_hit", blade: "wood_hit", break: "crack", drag: "drag_wood" },
|
|
14839
|
+
glass: { bullet: "ping", blunt: "ping", break: "glass_shatter", drag: "drag_glass" },
|
|
14840
|
+
masonry: { bullet: "chip", blunt: "chip", blade: "chip", break: "crumble", drag: "drag_masonry" },
|
|
14841
|
+
soft: { bullet: "thud", blunt: "thud", blade: "thud", break: "thud", drag: "drag_soft" }
|
|
14842
|
+
};
|
|
14843
|
+
var impactSoundId = (material, blow3) => `impact_${material}_${blow3}`;
|
|
14844
|
+
var SFX_IMPACTS = SFX_MATERIALS.flatMap(
|
|
14845
|
+
([material, group]) => SFX_BLOWS.map((blow3) => {
|
|
14846
|
+
const current = CURRENT_BY_GROUP[group]?.[blow3];
|
|
14847
|
+
return { id: impactSoundId(material, blow3), category: "impactos", priority: ["steel", "wood", "glass", "concrete", "brick", "soil", "flesh"].includes(material) && blow3 === "bullet" ? 1 : 2, ...current ? { current } : {} };
|
|
14848
|
+
})
|
|
14849
|
+
);
|
|
14850
|
+
var CALIBRATION_ID = "calibration_shot";
|
|
14851
|
+
var PREFIX = "calibration/";
|
|
14852
|
+
var url = (name) => `${PREFIX}${name}`;
|
|
14853
|
+
var CALIBRATION_SOUND = {
|
|
14854
|
+
id: CALIBRATION_ID,
|
|
14855
|
+
bus: "weapons",
|
|
14856
|
+
close: ["close_1.wav", "close_2.wav", "close_3.wav", "close_4.wav"].map((name) => ({ url: url(name) })),
|
|
14857
|
+
far: ["far_1.wav", "far_2.wav"].map((name) => ({ url: url(name) })),
|
|
14858
|
+
tiers: [
|
|
14859
|
+
{ name: "light", from: 0, close: ["light_1.wav", "light_2.wav"].map((name) => ({ url: url(name) })) },
|
|
14860
|
+
{ name: "medium", from: 0.34, close: ["close_1.wav", "close_2.wav", "close_3.wav", "close_4.wav"].map((name) => ({ url: url(name) })) },
|
|
14861
|
+
{ name: "heavy", from: 0.67, close: ["heavy_1.wav", "heavy_2.wav"].map((name) => ({ url: url(name) })) }
|
|
14862
|
+
],
|
|
14863
|
+
tail: { indoor: [{ url: url("tail_in.wav") }], outdoor: [{ url: url("tail_out.wav") }] },
|
|
14864
|
+
pitch: 0.04,
|
|
14865
|
+
choke: { voices: 2 },
|
|
14866
|
+
fallback: "sfx_pistol",
|
|
14867
|
+
sources: ["sdk: calibration takes synthesized by the harness (measure/calibration.ts), no file, no licence"]
|
|
14868
|
+
};
|
|
14869
|
+
var CALIBRATION_LOOP_ID = "calibration_loop";
|
|
14870
|
+
var CALIBRATION_LOOP_SOUND = {
|
|
14871
|
+
id: CALIBRATION_LOOP_ID,
|
|
14872
|
+
bus: "ambience",
|
|
14873
|
+
close: ["loop_1.wav", "loop_2.wav", "loop_3.wav"].map((name) => ({ url: url(name) })),
|
|
14874
|
+
loop: { crossfade: 0.15 },
|
|
14875
|
+
sources: ["sdk: calibration takes synthesized by the harness (measure/calibration.ts), no file, no licence"]
|
|
14876
|
+
};
|
|
14877
|
+
var CRITERIA_TARGETS = {
|
|
14878
|
+
/** C1: event to the first sample, and onset to the peak (one 60 fps frame). */
|
|
14879
|
+
syncMs: 20,
|
|
14880
|
+
peakAfterOnsetMs: 1e3 / 60,
|
|
14881
|
+
/** C3: distances measured, metres. */
|
|
14882
|
+
distances: [1, 10, 50, 200],
|
|
14883
|
+
/** C5: plays, distinct takes and the pitch range (fraction). */
|
|
14884
|
+
plays: 10,
|
|
14885
|
+
minTakes: 4,
|
|
14886
|
+
pitch: [0.03, 0.05],
|
|
14887
|
+
/** C6: the highest peak, dBFS. */
|
|
14888
|
+
peakDb: -1,
|
|
14889
|
+
/** C8: the end of each file against its peak, dB. */
|
|
14890
|
+
endDropDb: -40,
|
|
14891
|
+
/** C9: a burst of rapid fire. */
|
|
14892
|
+
burst: 20,
|
|
14893
|
+
burstSpacing: 0.06
|
|
14894
|
+
};
|
|
14895
|
+
var PT = {
|
|
14896
|
+
group: "Som",
|
|
14897
|
+
name: "Painel de som (arquivos e C1\u2013C10)",
|
|
14898
|
+
controls: {
|
|
14899
|
+
category: "1 \xB7 Categoria",
|
|
14900
|
+
sound: "1 \xB7 Som",
|
|
14901
|
+
play: "1 \xB7 \u25B6 Tocar",
|
|
14902
|
+
material: "2 \xB7 Material",
|
|
14903
|
+
blow: "2 \xB7 Golpe",
|
|
14904
|
+
strike: "2 \xB7 \u25B6 Tocar golpe",
|
|
14905
|
+
distance: "3 \xB7 Dist\xE2ncia",
|
|
14906
|
+
bearing: "3 \xB7 Dire\xE7\xE3o",
|
|
14907
|
+
environment: "3 \xB7 Lugar",
|
|
14908
|
+
strength: "3 \xB7 For\xE7a",
|
|
14909
|
+
wall: "3 \xB7 Atr\xE1s da parede",
|
|
14910
|
+
burst: "3 \xB7 Rajada 20\xD7",
|
|
14911
|
+
play_a: "4 \xB7 \u25B6 A (atual)",
|
|
14912
|
+
play_b: "4 \xB7 \u25B6 B (novo)",
|
|
14913
|
+
ab_cycle: "4 \xB7 Alternar A/B",
|
|
14914
|
+
blind: "4 \xB7 \xC0s cegas (X)",
|
|
14915
|
+
reveal: "4 \xB7 Revelar X",
|
|
14916
|
+
prefer_a: "4 \xB7 Prefiro A",
|
|
14917
|
+
prefer_b: "4 \xB7 Prefiro B",
|
|
14918
|
+
measure: "5 \xB7 Medir C1\u2013C10",
|
|
14919
|
+
record: "5 \xB7 Gravar 3 s",
|
|
14920
|
+
approve: "5 \xB7 Aprovo (C10)"
|
|
14921
|
+
},
|
|
14922
|
+
hints: {
|
|
14923
|
+
sound: "A = arquivo, S = sintetizado, F = faltando. O que tem arquivo toca o arquivo; o resto toca o som atual.",
|
|
14924
|
+
strike: "Toca impact_<material>_<golpe>: o arquivo se existir, sen\xE3o o som atual daquele material.",
|
|
14925
|
+
wall: "P\xF5e a parede de casa entre voc\xEA e a fonte: o som fica abafado (C4).",
|
|
14926
|
+
burst: "Vinte vezes a cada 60 ms, como uma rajada (C9).",
|
|
14927
|
+
ab_cycle: "A, B, A, B, um por segundo.",
|
|
14928
|
+
blind: "Toca A ou B sem dizer qual; depois revele.",
|
|
14929
|
+
measure: "Mede o som escolhido no motor real (contexto offline): sincronia, som certo, dist\xE2ncia, lado, varia\xE7\xE3o, volume, for\xE7a, cauda e rajada.",
|
|
14930
|
+
record: "Grava 3 s da sa\xEDda com o som tocando e baixa o arquivo (C10).",
|
|
14931
|
+
approve: "Marca o C10 do som escolhido como aprovado pelo seu ouvido."
|
|
14932
|
+
},
|
|
14933
|
+
categories: {
|
|
14934
|
+
armas: "Armas",
|
|
14935
|
+
impactos: "Impactos",
|
|
14936
|
+
"explosoes-fogo": "Explos\xF5es e fogo",
|
|
14937
|
+
personagem: "Personagem",
|
|
14938
|
+
veiculos: "Ve\xEDculos",
|
|
14939
|
+
"itens-portas": "Itens e portas",
|
|
14940
|
+
ambiente: "Ambiente",
|
|
14941
|
+
calibracao: "Calibra\xE7\xE3o (SDK)"
|
|
14942
|
+
},
|
|
14943
|
+
blows: { bullet: "tiro", blunt: "soco/chute", blade: "faca", break: "quebra", drag: "arrasto/queda" },
|
|
14944
|
+
materials: {
|
|
14945
|
+
steel: "a\xE7o",
|
|
14946
|
+
aluminium: "alum\xEDnio",
|
|
14947
|
+
wood: "madeira",
|
|
14948
|
+
plywood: "compensado",
|
|
14949
|
+
mdf: "MDF",
|
|
14950
|
+
glass: "vidro",
|
|
14951
|
+
tempered_glass: "vidro temperado",
|
|
14952
|
+
ceramic: "cer\xE2mica",
|
|
14953
|
+
concrete: "concreto",
|
|
14954
|
+
brick: "tijolo",
|
|
14955
|
+
plaster: "gesso",
|
|
14956
|
+
plastic: "pl\xE1stico",
|
|
14957
|
+
fabric: "tecido",
|
|
14958
|
+
paper: "papel\xE3o",
|
|
14959
|
+
foliage: "folhagem",
|
|
14960
|
+
soil: "terra",
|
|
14961
|
+
flesh: "carne"
|
|
14962
|
+
},
|
|
14963
|
+
environments: { indoor: "interior", outdoor: "exterior" },
|
|
14964
|
+
strengths: { light: "leve", medium: "m\xE9dio", heavy: "forte" },
|
|
14965
|
+
states: { file: "A", synth: "S", missing: "F" },
|
|
14966
|
+
criteria: { C1: "C1 Sincronia", C2: "C2 Certo p/ a\xE7\xE3o", C3: "C3 Dist\xE2ncia", C4: "C4 Posi\xE7\xE3o", C5: "C5 Varia\xE7\xE3o", C6: "C6 Volume", C7: "C7 Intensidade", C8: "C8 Cauda", C9: "C9 Rajada", C10: "C10 Escuta" },
|
|
14967
|
+
metrics: { chosen: "1 \xB7 Escolhido", matrix: "2 \xB7 Matriz", last: "\xDAltimo tocado", ab: "4 \xB7 A \xD7 B", blind: "4 \xB7 X", verdicts: "4 \xB7 Prefer\xEAncias", state: "5 \xB7 Medi\xE7\xE3o", count: "1 \xB7 Cat\xE1logo" },
|
|
14968
|
+
run: { idle: "parada", measuring: "medindo\u2026", done: "pronta", failed: "falhou", recording: "gravando\u2026", recorded: "grava\xE7\xE3o baixada" },
|
|
14969
|
+
words: { none: "\u2014", hidden: "? (revele)", prefers: "prefere", approved: "aprovado pelo dono" },
|
|
14970
|
+
probe: "Fonte do som",
|
|
14971
|
+
wall: "Parede (oclus\xE3o)",
|
|
14972
|
+
scenarios: { pipeline: "Arquivo \u2192 decodifica \u2192 toca (round-robin, cauda)", criteria: "Crit\xE9rios C1\u2013C9 no som de calibra\xE7\xE3o", loop: "Loop de arquivo: emenda sem buraco (crossfade)", panel: "Painel: categorias, matriz, A/B e parede" }
|
|
14973
|
+
};
|
|
14974
|
+
var EN2 = {
|
|
14975
|
+
...PT,
|
|
14976
|
+
group: "Sound",
|
|
14977
|
+
name: "Sound panel (files and C1\u2013C10)",
|
|
14978
|
+
controls: {
|
|
14979
|
+
category: "1 \xB7 Category",
|
|
14980
|
+
sound: "1 \xB7 Sound",
|
|
14981
|
+
play: "1 \xB7 \u25B6 Play",
|
|
14982
|
+
material: "2 \xB7 Material",
|
|
14983
|
+
blow: "2 \xB7 Blow",
|
|
14984
|
+
strike: "2 \xB7 \u25B6 Play blow",
|
|
14985
|
+
distance: "3 \xB7 Distance",
|
|
14986
|
+
bearing: "3 \xB7 Bearing",
|
|
14987
|
+
environment: "3 \xB7 Place",
|
|
14988
|
+
strength: "3 \xB7 Strength",
|
|
14989
|
+
wall: "3 \xB7 Behind the wall",
|
|
14990
|
+
burst: "3 \xB7 Burst 20\xD7",
|
|
14991
|
+
play_a: "4 \xB7 \u25B6 A (current)",
|
|
14992
|
+
play_b: "4 \xB7 \u25B6 B (new)",
|
|
14993
|
+
ab_cycle: "4 \xB7 Alternate A/B",
|
|
14994
|
+
blind: "4 \xB7 Blind (X)",
|
|
14995
|
+
reveal: "4 \xB7 Reveal X",
|
|
14996
|
+
prefer_a: "4 \xB7 Prefer A",
|
|
14997
|
+
prefer_b: "4 \xB7 Prefer B",
|
|
14998
|
+
measure: "5 \xB7 Measure C1\u2013C10",
|
|
14999
|
+
record: "5 \xB7 Record 3 s",
|
|
15000
|
+
approve: "5 \xB7 Approve (C10)"
|
|
15001
|
+
},
|
|
15002
|
+
hints: {
|
|
15003
|
+
sound: "A = file, S = synthesized, F = missing. A file plays the file; the rest plays the current sound.",
|
|
15004
|
+
strike: "Plays impact_<material>_<blow>: the file if there is one, else the current sound of that material.",
|
|
15005
|
+
wall: "Puts the house wall between you and the source: the sound is muffled (C4).",
|
|
15006
|
+
burst: "Twenty times every 60 ms, like a burst (C9).",
|
|
15007
|
+
ab_cycle: "A, B, A, B, one a second.",
|
|
15008
|
+
blind: "Plays A or B without saying which; reveal it after.",
|
|
15009
|
+
measure: "Measures the chosen sound through the real engine (offline context): sync, right sound, distance, side, variation, level, strength, tail and burst.",
|
|
15010
|
+
record: "Records 3 s of the output with the sound playing and downloads it (C10).",
|
|
15011
|
+
approve: "Marks C10 of the chosen sound as approved by your ear."
|
|
15012
|
+
},
|
|
15013
|
+
categories: { armas: "Weapons", impactos: "Impacts", "explosoes-fogo": "Explosions and fire", personagem: "Character", veiculos: "Vehicles", "itens-portas": "Items and doors", ambiente: "Ambience", calibracao: "Calibration (SDK)" },
|
|
15014
|
+
blows: { bullet: "bullet", blunt: "punch/kick", blade: "knife", break: "break", drag: "drag/fall" },
|
|
15015
|
+
materials: {
|
|
15016
|
+
steel: "steel",
|
|
15017
|
+
aluminium: "aluminium",
|
|
15018
|
+
wood: "wood",
|
|
15019
|
+
plywood: "plywood",
|
|
15020
|
+
mdf: "MDF",
|
|
15021
|
+
glass: "glass",
|
|
15022
|
+
tempered_glass: "tempered glass",
|
|
15023
|
+
ceramic: "ceramic",
|
|
15024
|
+
concrete: "concrete",
|
|
15025
|
+
brick: "brick",
|
|
15026
|
+
plaster: "plaster",
|
|
15027
|
+
plastic: "plastic",
|
|
15028
|
+
fabric: "fabric",
|
|
15029
|
+
paper: "cardboard",
|
|
15030
|
+
foliage: "foliage",
|
|
15031
|
+
soil: "soil",
|
|
15032
|
+
flesh: "flesh"
|
|
15033
|
+
},
|
|
15034
|
+
environments: { indoor: "indoor", outdoor: "outdoor" },
|
|
15035
|
+
strengths: { light: "light", medium: "medium", heavy: "heavy" },
|
|
15036
|
+
criteria: { C1: "C1 Sync", C2: "C2 Right sound", C3: "C3 Distance", C4: "C4 Position", C5: "C5 Variation", C6: "C6 Level", C7: "C7 Strength", C8: "C8 Tail", C9: "C9 Burst", C10: "C10 Listening" },
|
|
15037
|
+
metrics: { chosen: "1 \xB7 Chosen", matrix: "2 \xB7 Matrix", last: "Last played", ab: "4 \xB7 A \xD7 B", blind: "4 \xB7 X", verdicts: "4 \xB7 Preferences", state: "5 \xB7 Measurement", count: "1 \xB7 Catalogue" },
|
|
15038
|
+
run: { idle: "idle", measuring: "measuring\u2026", done: "done", failed: "failed", recording: "recording\u2026", recorded: "recording downloaded" },
|
|
15039
|
+
words: { none: "\u2014", hidden: "? (reveal)", prefers: "prefers", approved: "approved by the owner" },
|
|
15040
|
+
probe: "Sound source",
|
|
15041
|
+
wall: "Wall (occlusion)",
|
|
15042
|
+
scenarios: { pipeline: "File \u2192 decode \u2192 play (round-robin, tail)", criteria: "Criteria C1\u2013C9 on the calibration sound", loop: "File loop: seamless joins (crossfade)", panel: "Panel: categories, matrix, A/B and wall" }
|
|
15043
|
+
};
|
|
15044
|
+
|
|
15045
|
+
// ../game-audio/dist/index.js
|
|
15046
|
+
var SFX_TIERS = { light: 0, medium: 0.34, heavy: 0.67 };
|
|
15047
|
+
function splitTier(id) {
|
|
15048
|
+
const match = /^(.*)_(light|medium|heavy)$/.exec(id);
|
|
15049
|
+
return match ? { base: match[1], tier: match[2] } : { base: id };
|
|
15050
|
+
}
|
|
15051
|
+
var refsOf = (entries2, baseUrl) => (entries2 ?? []).map((entry) => ({ url: `${baseUrl}${entry.file}` }));
|
|
15052
|
+
function sampleBankFromManifest(manifest, options) {
|
|
15053
|
+
const baseUrl = options.baseUrl ?? "";
|
|
15054
|
+
const samples = [];
|
|
15055
|
+
const tiersOf = /* @__PURE__ */ new Map();
|
|
15056
|
+
for (const sound of manifest.sounds) {
|
|
15057
|
+
const { base, tier } = splitTier(sound.id);
|
|
15058
|
+
if (tier) tiersOf.set(base, [...tiersOf.get(base) ?? [], sound]);
|
|
15059
|
+
}
|
|
15060
|
+
const bases = /* @__PURE__ */ new Map();
|
|
15061
|
+
for (const sound of manifest.sounds) {
|
|
15062
|
+
if (!splitTier(sound.id).tier) bases.set(sound.id, sound);
|
|
15063
|
+
}
|
|
15064
|
+
for (const [base, tiers] of tiersOf) {
|
|
15065
|
+
if (!bases.has(base)) bases.set(base, { id: base, category: tiers[0]?.category ?? "", layers: { close: tiers[0]?.layers.close ?? [] } });
|
|
15066
|
+
}
|
|
15067
|
+
for (const sound of bases.values()) {
|
|
15068
|
+
const numbers = options.sounds?.[sound.id] ?? options.defaults?.(sound);
|
|
15069
|
+
const close = refsOf(sound.layers.close, baseUrl);
|
|
15070
|
+
if (!numbers || close.length === 0) continue;
|
|
15071
|
+
const tiers = (tiersOf.get(sound.id) ?? []).map((tier) => {
|
|
15072
|
+
const name = splitTier(tier.id).tier;
|
|
15073
|
+
const tierFar = refsOf(tier.layers.far, baseUrl);
|
|
15074
|
+
return { name, from: SFX_TIERS[name] ?? 0, close: refsOf(tier.layers.close, baseUrl), ...tierFar.length > 0 ? { far: tierFar } : {} };
|
|
15075
|
+
}).sort((a, b) => a.from - b.from);
|
|
15076
|
+
const far = refsOf(sound.layers.far, baseUrl);
|
|
15077
|
+
const indoor = refsOf(sound.layers.tail_in, baseUrl);
|
|
15078
|
+
const outdoor = refsOf(sound.layers.tail_out, baseUrl);
|
|
15079
|
+
const files = [sound, ...tiersOf.get(sound.id) ?? []].flatMap((entry) => Object.values(entry.layers).flatMap((entries2) => entries2 ?? []));
|
|
15080
|
+
samples.push({
|
|
15081
|
+
id: sound.id,
|
|
15082
|
+
bus: numbers.bus,
|
|
15083
|
+
close,
|
|
15084
|
+
...far.length > 0 ? { far } : {},
|
|
15085
|
+
...tiers.length > 0 ? { tiers } : {},
|
|
15086
|
+
...numbers.crossover ? { crossover: numbers.crossover } : {},
|
|
15087
|
+
...indoor.length > 0 || outdoor.length > 0 ? { tail: { ...indoor.length > 0 ? { indoor } : {}, ...outdoor.length > 0 ? { outdoor } : {}, ...numbers.tailGain === void 0 ? {} : { gain: numbers.tailGain } } } : {},
|
|
15088
|
+
...numbers.pitch === void 0 ? {} : { pitch: numbers.pitch },
|
|
15089
|
+
...numbers.gain === void 0 ? {} : { gain: numbers.gain },
|
|
15090
|
+
...numbers.falloff ? { falloff: numbers.falloff } : {},
|
|
15091
|
+
...numbers.cutoffK === void 0 ? {} : { cutoffK: numbers.cutoffK },
|
|
15092
|
+
...numbers.group ? { group: numbers.group } : {},
|
|
15093
|
+
...numbers.priority === void 0 ? {} : { priority: numbers.priority },
|
|
15094
|
+
...numbers.fallback ? { fallback: numbers.fallback } : {},
|
|
15095
|
+
sources: files.map((file) => `${file.file}: ${file.source} ${file.author} ${file.license}`)
|
|
15096
|
+
});
|
|
15097
|
+
}
|
|
15098
|
+
return { id: options.id, samples };
|
|
15099
|
+
}
|
|
14173
15100
|
var ATTACK = 2e-3;
|
|
14174
15101
|
function lengthOf(d, attack) {
|
|
14175
15102
|
if (typeof d === "number") return attack + d;
|
|
@@ -14207,6 +15134,31 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14207
15134
|
envelope: envelope(ATTACK)
|
|
14208
15135
|
};
|
|
14209
15136
|
}
|
|
15137
|
+
function sceneVoice(o) {
|
|
15138
|
+
return {
|
|
15139
|
+
duration: lengthOf(o.d, 0),
|
|
15140
|
+
gain: o.vol,
|
|
15141
|
+
wet: 0.45,
|
|
15142
|
+
source: {
|
|
15143
|
+
kind: "formant_voice",
|
|
15144
|
+
wave: o.wave ?? "sawtooth",
|
|
15145
|
+
frequency: { from: o.f0, to: o.f1, curve: "linear", over: "duration" },
|
|
15146
|
+
vibrato: { rate: [4, 10], depth: 0.08 },
|
|
15147
|
+
formants: [
|
|
15148
|
+
{ frequency: o.fA, q: 4 },
|
|
15149
|
+
{ frequency: o.fB, q: 6 }
|
|
15150
|
+
],
|
|
15151
|
+
breath: { frequency: o.fB, q: 1.1, gain: o.noise },
|
|
15152
|
+
mix: 3.2
|
|
15153
|
+
},
|
|
15154
|
+
envelope: [
|
|
15155
|
+
[0, 1e-4, "set", "fixed"],
|
|
15156
|
+
[0.1, 1, "exp"],
|
|
15157
|
+
["0.6d", 1, "set"],
|
|
15158
|
+
["1d", 1e-4, "exp", "fixed"]
|
|
15159
|
+
]
|
|
15160
|
+
};
|
|
15161
|
+
}
|
|
14210
15162
|
var sceneMany = (count, make) => Array.from({ length: count }, make);
|
|
14211
15163
|
function sceneRecipe(id, layers, line, options = {}) {
|
|
14212
15164
|
return {
|
|
@@ -14220,50 +15172,930 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14220
15172
|
sources: [`cena:${line}`]
|
|
14221
15173
|
};
|
|
14222
15174
|
}
|
|
14223
|
-
var
|
|
14224
|
-
|
|
14225
|
-
|
|
14226
|
-
|
|
14227
|
-
|
|
14228
|
-
|
|
14229
|
-
|
|
14230
|
-
|
|
14231
|
-
|
|
14232
|
-
|
|
14233
|
-
|
|
14234
|
-
|
|
14235
|
-
|
|
14236
|
-
|
|
14237
|
-
|
|
14238
|
-
|
|
14239
|
-
|
|
14240
|
-
|
|
14241
|
-
|
|
14242
|
-
|
|
14243
|
-
|
|
14244
|
-
|
|
14245
|
-
|
|
14246
|
-
|
|
14247
|
-
|
|
14248
|
-
|
|
14249
|
-
|
|
14250
|
-
|
|
14251
|
-
|
|
14252
|
-
|
|
14253
|
-
|
|
14254
|
-
|
|
14255
|
-
|
|
14256
|
-
|
|
14257
|
-
|
|
14258
|
-
|
|
14259
|
-
|
|
14260
|
-
|
|
14261
|
-
|
|
14262
|
-
|
|
14263
|
-
|
|
14264
|
-
|
|
14265
|
-
|
|
14266
|
-
|
|
15175
|
+
var FOLEY_FALLOFF = {
|
|
15176
|
+
model: "inverse_square",
|
|
15177
|
+
k: 0.02,
|
|
15178
|
+
cutoffK: 0.04,
|
|
15179
|
+
minGain: 4e-3
|
|
15180
|
+
};
|
|
15181
|
+
var vary = (f, pct = 0.04) => [Math.round(f * (1 - pct)), Math.round(f * (1 + pct))];
|
|
15182
|
+
var foley = (id, layers, note) => ({
|
|
15183
|
+
id,
|
|
15184
|
+
bus: "world",
|
|
15185
|
+
pan: true,
|
|
15186
|
+
layers,
|
|
15187
|
+
falloff: FOLEY_FALLOFF,
|
|
15188
|
+
sources: [`sdk: ${note}`]
|
|
15189
|
+
});
|
|
15190
|
+
var click = (f, vol, delay = 0) => [
|
|
15191
|
+
sceneNoise({
|
|
15192
|
+
type: "bandpass",
|
|
15193
|
+
f: vary(f, 0.08),
|
|
15194
|
+
q: 4,
|
|
15195
|
+
d: [0.018, 0.03],
|
|
15196
|
+
vol: [vol * 0.85, vol * 1.1],
|
|
15197
|
+
delay
|
|
15198
|
+
}),
|
|
15199
|
+
sceneTone({
|
|
15200
|
+
f: vary(f * 0.8, 0.06),
|
|
15201
|
+
f2: Math.round(f * 0.62),
|
|
15202
|
+
d: [0.015, 0.025],
|
|
15203
|
+
vol: vol * 0.18,
|
|
15204
|
+
delay
|
|
15205
|
+
})
|
|
15206
|
+
];
|
|
15207
|
+
var meet = (body, panel, vol, latch) => [
|
|
15208
|
+
sceneTone({
|
|
15209
|
+
f: vary(body),
|
|
15210
|
+
f2: Math.round(body * 0.5),
|
|
15211
|
+
d: [0.12, 0.16],
|
|
15212
|
+
vol: [vol * 0.9, vol * 1.1]
|
|
15213
|
+
}),
|
|
15214
|
+
sceneNoise({
|
|
15215
|
+
type: "bandpass",
|
|
15216
|
+
f: vary(panel, 0.06),
|
|
15217
|
+
q: 1.2,
|
|
15218
|
+
d: [0.08, 0.12],
|
|
15219
|
+
vol: [vol * 0.9, vol * 1.1],
|
|
15220
|
+
wet: 0.25
|
|
15221
|
+
}),
|
|
15222
|
+
...click(latch, vol * 0.45, [0.012, 0.028])
|
|
15223
|
+
];
|
|
15224
|
+
var LEAF_FOLEY = [
|
|
15225
|
+
// a wooden house door or wardrobe door: latch, a short creak, the leaf on the frame (the hard slam adds the frame rattling)
|
|
15226
|
+
foley("door_wood_open", click(2400, 0.4), "the latch of a wooden door giving"),
|
|
15227
|
+
foley(
|
|
15228
|
+
"door_creak",
|
|
15229
|
+
[
|
|
15230
|
+
sceneTone({ f: [190, 240], f2: 310, d: [0.4, 0.55], vol: [0.04, 0.06] }),
|
|
15231
|
+
sceneNoise({
|
|
15232
|
+
type: "bandpass",
|
|
15233
|
+
f: [480, 560],
|
|
15234
|
+
q: 6,
|
|
15235
|
+
a: 0.05,
|
|
15236
|
+
d: [0.35, 0.5],
|
|
15237
|
+
vol: [0.1, 0.14]
|
|
15238
|
+
})
|
|
15239
|
+
],
|
|
15240
|
+
"a wooden hinge turning"
|
|
15241
|
+
),
|
|
15242
|
+
foley("door_wood_close", meet(120, 600, 0.55, 2400), "a wooden leaf meeting its frame"),
|
|
15243
|
+
foley(
|
|
15244
|
+
"door_wood_slam",
|
|
15245
|
+
[
|
|
15246
|
+
...meet(110, 560, 0.85, 2300),
|
|
15247
|
+
...sceneMany(
|
|
15248
|
+
3,
|
|
15249
|
+
() => sceneNoise({
|
|
15250
|
+
type: "bandpass",
|
|
15251
|
+
f: [900, 1500],
|
|
15252
|
+
q: 3,
|
|
15253
|
+
d: 0.03,
|
|
15254
|
+
vol: [0.08, 0.14],
|
|
15255
|
+
delay: [0.05, 0.18]
|
|
15256
|
+
})
|
|
15257
|
+
)
|
|
15258
|
+
],
|
|
15259
|
+
"a wooden door slammed: the frame and the hinges rattle after the hit"
|
|
15260
|
+
),
|
|
15261
|
+
// a metal door or gate: a hollow panel that rings a little
|
|
15262
|
+
foley("door_metal_open_close", [...meet(160, 900, 0.55, 2e3), sceneTone({ f: vary(1250), f2: 1150, d: [0.1, 0.16], vol: 0.06, wet: 0.3 })], "a metal door or gate meeting its frame"),
|
|
15263
|
+
foley(
|
|
15264
|
+
"door_creak_metal",
|
|
15265
|
+
[
|
|
15266
|
+
sceneTone({ f: [620, 760], f2: 900, d: [0.3, 0.4], vol: [0.03, 0.045] }),
|
|
15267
|
+
sceneNoise({
|
|
15268
|
+
type: "bandpass",
|
|
15269
|
+
f: [1300, 1500],
|
|
15270
|
+
q: 8,
|
|
15271
|
+
a: 0.04,
|
|
15272
|
+
d: [0.25, 0.35],
|
|
15273
|
+
vol: [0.07, 0.09]
|
|
15274
|
+
})
|
|
15275
|
+
],
|
|
15276
|
+
"a metal hinge turning"
|
|
15277
|
+
),
|
|
15278
|
+
// the handle of a locked door shaken: the latch knocking in its keeper, five to seven times in half a second
|
|
15279
|
+
foley(
|
|
15280
|
+
"door_locked_rattle",
|
|
15281
|
+
[
|
|
15282
|
+
...sceneMany(
|
|
15283
|
+
6,
|
|
15284
|
+
() => sceneNoise({
|
|
15285
|
+
type: "bandpass",
|
|
15286
|
+
f: [1600, 2600],
|
|
15287
|
+
q: 3,
|
|
15288
|
+
d: [0.025, 0.04],
|
|
15289
|
+
vol: [0.22, 0.34],
|
|
15290
|
+
delay: [0, 0.45]
|
|
15291
|
+
})
|
|
15292
|
+
),
|
|
15293
|
+
...sceneMany(
|
|
15294
|
+
3,
|
|
15295
|
+
() => sceneTone({
|
|
15296
|
+
f: [170, 210],
|
|
15297
|
+
f2: 120,
|
|
15298
|
+
d: 0.05,
|
|
15299
|
+
vol: [0.12, 0.18],
|
|
15300
|
+
delay: [0.02, 0.4]
|
|
15301
|
+
})
|
|
15302
|
+
)
|
|
15303
|
+
],
|
|
15304
|
+
"the handle of a locked door shaken: latch knocks and the leaf thumping in its frame"
|
|
15305
|
+
),
|
|
15306
|
+
// a fridge: the rubber seal lets go with a soft suck, the bottles in the door clink; closing it is a soft thump and the seal pulling shut
|
|
15307
|
+
foley(
|
|
15308
|
+
"fridge_open",
|
|
15309
|
+
[
|
|
15310
|
+
sceneNoise({
|
|
15311
|
+
type: "lowpass",
|
|
15312
|
+
f: [320, 380],
|
|
15313
|
+
a: 0.03,
|
|
15314
|
+
d: [0.14, 0.2],
|
|
15315
|
+
vol: [0.32, 0.4]
|
|
15316
|
+
}),
|
|
15317
|
+
sceneNoise({
|
|
15318
|
+
type: "bandpass",
|
|
15319
|
+
f: [800, 950],
|
|
15320
|
+
f2: 1400,
|
|
15321
|
+
q: 2,
|
|
15322
|
+
a: 0.02,
|
|
15323
|
+
d: 0.12,
|
|
15324
|
+
vol: [0.08, 0.12]
|
|
15325
|
+
}),
|
|
15326
|
+
...sceneMany(
|
|
15327
|
+
2,
|
|
15328
|
+
() => sceneTone({
|
|
15329
|
+
f: [2600, 4200],
|
|
15330
|
+
d: [0.04, 0.08],
|
|
15331
|
+
vol: [0.015, 0.03],
|
|
15332
|
+
delay: [0.08, 0.22]
|
|
15333
|
+
})
|
|
15334
|
+
)
|
|
15335
|
+
],
|
|
15336
|
+
"a fridge door: the seal letting go and the bottles in the door"
|
|
15337
|
+
),
|
|
15338
|
+
foley(
|
|
15339
|
+
"fridge_close",
|
|
15340
|
+
[
|
|
15341
|
+
sceneTone({ f: vary(85), f2: 45, d: [0.1, 0.13], vol: [0.38, 0.46] }),
|
|
15342
|
+
sceneNoise({
|
|
15343
|
+
type: "lowpass",
|
|
15344
|
+
f: [520, 620],
|
|
15345
|
+
d: [0.08, 0.12],
|
|
15346
|
+
vol: [0.3, 0.36]
|
|
15347
|
+
}),
|
|
15348
|
+
sceneNoise({
|
|
15349
|
+
type: "lowpass",
|
|
15350
|
+
f: [260, 320],
|
|
15351
|
+
a: 0.02,
|
|
15352
|
+
d: 0.1,
|
|
15353
|
+
vol: [0.12, 0.18],
|
|
15354
|
+
delay: [0.04, 0.07]
|
|
15355
|
+
}),
|
|
15356
|
+
...sceneMany(
|
|
15357
|
+
2,
|
|
15358
|
+
() => sceneTone({
|
|
15359
|
+
f: [2600, 4200],
|
|
15360
|
+
d: [0.04, 0.07],
|
|
15361
|
+
vol: [0.015, 0.03],
|
|
15362
|
+
delay: [0.03, 0.12]
|
|
15363
|
+
})
|
|
15364
|
+
)
|
|
15365
|
+
],
|
|
15366
|
+
"a fridge door shut: a soft thump, the seal pulling and the bottles"
|
|
15367
|
+
),
|
|
15368
|
+
// a microwave: the spring catch of the door, plastic on plastic
|
|
15369
|
+
foley(
|
|
15370
|
+
"microwave_open",
|
|
15371
|
+
[
|
|
15372
|
+
...click(1800, 0.34),
|
|
15373
|
+
sceneTone({
|
|
15374
|
+
f: [3e3, 3400],
|
|
15375
|
+
f2: 2800,
|
|
15376
|
+
d: 0.05,
|
|
15377
|
+
vol: 0.025,
|
|
15378
|
+
delay: [0.01, 0.02]
|
|
15379
|
+
})
|
|
15380
|
+
],
|
|
15381
|
+
"the spring catch of a microwave door"
|
|
15382
|
+
),
|
|
15383
|
+
foley(
|
|
15384
|
+
"microwave_close",
|
|
15385
|
+
[
|
|
15386
|
+
sceneNoise({
|
|
15387
|
+
type: "bandpass",
|
|
15388
|
+
f: [1200, 1450],
|
|
15389
|
+
q: 2.5,
|
|
15390
|
+
d: [0.04, 0.06],
|
|
15391
|
+
vol: [0.36, 0.44]
|
|
15392
|
+
}),
|
|
15393
|
+
sceneTone({ f: vary(180), f2: 120, d: 0.05, vol: [0.16, 0.22] }),
|
|
15394
|
+
...click(2200, 0.2, [8e-3, 0.016])
|
|
15395
|
+
],
|
|
15396
|
+
"a microwave door clacking shut"
|
|
15397
|
+
),
|
|
15398
|
+
foley("microwave_beep", [sceneTone({ wave: "square", f: 2e3, d: 0.12, vol: 0.05 })], "the beep of a microwave"),
|
|
15399
|
+
// drawers and sliding leaves: the runners, then the stop
|
|
15400
|
+
foley(
|
|
15401
|
+
"drawer_open",
|
|
15402
|
+
[
|
|
15403
|
+
sceneNoise({
|
|
15404
|
+
type: "bandpass",
|
|
15405
|
+
f: [620, 780],
|
|
15406
|
+
q: 2,
|
|
15407
|
+
a: 0.04,
|
|
15408
|
+
d: [0.28, 0.36],
|
|
15409
|
+
vol: [0.18, 0.24]
|
|
15410
|
+
}),
|
|
15411
|
+
sceneNoise({ type: "bandpass", f: [2e3, 2400], q: 5, d: 0.28, vol: 0.04 }),
|
|
15412
|
+
...click(1400, 0.16, [0.28, 0.34])
|
|
15413
|
+
],
|
|
15414
|
+
"a drawer on its runners and its stop"
|
|
15415
|
+
),
|
|
15416
|
+
foley(
|
|
15417
|
+
"drawer_close",
|
|
15418
|
+
[
|
|
15419
|
+
sceneNoise({
|
|
15420
|
+
type: "bandpass",
|
|
15421
|
+
f: [620, 780],
|
|
15422
|
+
q: 2,
|
|
15423
|
+
a: 0.03,
|
|
15424
|
+
d: [0.2, 0.28],
|
|
15425
|
+
vol: [0.18, 0.24]
|
|
15426
|
+
}),
|
|
15427
|
+
sceneTone({
|
|
15428
|
+
f: vary(150),
|
|
15429
|
+
f2: 90,
|
|
15430
|
+
d: 0.08,
|
|
15431
|
+
vol: [0.22, 0.3],
|
|
15432
|
+
delay: [0.2, 0.26]
|
|
15433
|
+
})
|
|
15434
|
+
],
|
|
15435
|
+
"a drawer pushed home"
|
|
15436
|
+
)
|
|
15437
|
+
];
|
|
15438
|
+
var ITEM_FOLEY = [
|
|
15439
|
+
// the hand closing on the item: a touch of skin and cloth, then the item's own small sound
|
|
15440
|
+
foley(
|
|
15441
|
+
"item_pickup_metal",
|
|
15442
|
+
[
|
|
15443
|
+
sceneNoise({ type: "bandpass", f: [700, 900], q: 1, d: 0.06, vol: 0.18 }),
|
|
15444
|
+
sceneTone({
|
|
15445
|
+
f: [1900, 2600],
|
|
15446
|
+
f2: 1700,
|
|
15447
|
+
d: [0.05, 0.08],
|
|
15448
|
+
vol: [0.06, 0.09],
|
|
15449
|
+
delay: [0.01, 0.03]
|
|
15450
|
+
})
|
|
15451
|
+
],
|
|
15452
|
+
"picking up a metal thing"
|
|
15453
|
+
),
|
|
15454
|
+
foley(
|
|
15455
|
+
"item_pickup_plastic",
|
|
15456
|
+
[
|
|
15457
|
+
sceneNoise({ type: "bandpass", f: [700, 900], q: 1, d: 0.06, vol: 0.18 }),
|
|
15458
|
+
sceneNoise({
|
|
15459
|
+
type: "bandpass",
|
|
15460
|
+
f: [1500, 1900],
|
|
15461
|
+
q: 3,
|
|
15462
|
+
d: 0.03,
|
|
15463
|
+
vol: [0.15, 0.21],
|
|
15464
|
+
delay: [0.01, 0.03]
|
|
15465
|
+
})
|
|
15466
|
+
],
|
|
15467
|
+
"picking up a plastic thing"
|
|
15468
|
+
),
|
|
15469
|
+
foley(
|
|
15470
|
+
"item_pickup_glass",
|
|
15471
|
+
[
|
|
15472
|
+
sceneNoise({ type: "bandpass", f: [700, 900], q: 1, d: 0.06, vol: 0.15 }),
|
|
15473
|
+
sceneTone({
|
|
15474
|
+
f: [3200, 4200],
|
|
15475
|
+
d: [0.05, 0.08],
|
|
15476
|
+
vol: [0.038, 0.06],
|
|
15477
|
+
delay: [0.01, 0.03]
|
|
15478
|
+
})
|
|
15479
|
+
],
|
|
15480
|
+
"picking up a glass thing"
|
|
15481
|
+
),
|
|
15482
|
+
foley(
|
|
15483
|
+
"item_pickup_paper",
|
|
15484
|
+
[
|
|
15485
|
+
sceneNoise({
|
|
15486
|
+
type: "highpass",
|
|
15487
|
+
f: [2600, 3400],
|
|
15488
|
+
a: 0.01,
|
|
15489
|
+
d: [0.1, 0.14],
|
|
15490
|
+
vol: [0.12, 0.18]
|
|
15491
|
+
}),
|
|
15492
|
+
sceneNoise({ type: "bandpass", f: [800, 1e3], q: 1, d: 0.05, vol: 0.12 })
|
|
15493
|
+
],
|
|
15494
|
+
"picking up paper or cardboard"
|
|
15495
|
+
),
|
|
15496
|
+
foley(
|
|
15497
|
+
"item_pickup_fabric",
|
|
15498
|
+
[
|
|
15499
|
+
sceneNoise({
|
|
15500
|
+
type: "bandpass",
|
|
15501
|
+
f: [900, 1300],
|
|
15502
|
+
q: 0.8,
|
|
15503
|
+
a: 0.02,
|
|
15504
|
+
d: [0.12, 0.16],
|
|
15505
|
+
vol: [0.15, 0.21]
|
|
15506
|
+
})
|
|
15507
|
+
],
|
|
15508
|
+
"picking up cloth"
|
|
15509
|
+
),
|
|
15510
|
+
foley(
|
|
15511
|
+
"item_pickup_wood",
|
|
15512
|
+
[
|
|
15513
|
+
sceneNoise({ type: "bandpass", f: [700, 900], q: 1, d: 0.06, vol: 0.18 }),
|
|
15514
|
+
sceneNoise({
|
|
15515
|
+
type: "bandpass",
|
|
15516
|
+
f: [900, 1100],
|
|
15517
|
+
q: 2,
|
|
15518
|
+
d: 0.04,
|
|
15519
|
+
vol: [0.15, 0.21],
|
|
15520
|
+
delay: [0.01, 0.03]
|
|
15521
|
+
})
|
|
15522
|
+
],
|
|
15523
|
+
"picking up a wooden thing"
|
|
15524
|
+
),
|
|
15525
|
+
foley(
|
|
15526
|
+
"item_pickup_food",
|
|
15527
|
+
[
|
|
15528
|
+
sceneNoise({ type: "bandpass", f: [700, 900], q: 1, d: 0.06, vol: 0.18 }),
|
|
15529
|
+
sceneNoise({
|
|
15530
|
+
type: "lowpass",
|
|
15531
|
+
f: [500, 650],
|
|
15532
|
+
d: 0.05,
|
|
15533
|
+
vol: 0.12,
|
|
15534
|
+
delay: [0.01, 0.03]
|
|
15535
|
+
})
|
|
15536
|
+
],
|
|
15537
|
+
"picking up food"
|
|
15538
|
+
),
|
|
15539
|
+
foley(
|
|
15540
|
+
"item_throw",
|
|
15541
|
+
[
|
|
15542
|
+
sceneNoise({
|
|
15543
|
+
type: "bandpass",
|
|
15544
|
+
f: [450, 650],
|
|
15545
|
+
f2: 1400,
|
|
15546
|
+
q: 1.5,
|
|
15547
|
+
a: 0.05,
|
|
15548
|
+
d: 0.18,
|
|
15549
|
+
vol: [0.1, 0.14]
|
|
15550
|
+
})
|
|
15551
|
+
],
|
|
15552
|
+
"the arm throwing something: air"
|
|
15553
|
+
),
|
|
15554
|
+
// a can: the tab cracks, the gas hisses out; drinking is gulps; crushing it crumples the sheet
|
|
15555
|
+
foley(
|
|
15556
|
+
"can_open",
|
|
15557
|
+
[
|
|
15558
|
+
...click(3200, 0.36),
|
|
15559
|
+
sceneNoise({
|
|
15560
|
+
type: "highpass",
|
|
15561
|
+
f: [3600, 4400],
|
|
15562
|
+
a: 5e-3,
|
|
15563
|
+
d: [0.22, 0.3],
|
|
15564
|
+
vol: [0.16, 0.22],
|
|
15565
|
+
delay: [0.012, 0.02]
|
|
15566
|
+
}),
|
|
15567
|
+
...sceneMany(5, () => sceneTone({ f: [4e3, 7e3], d: 0.02, vol: 0.01, delay: [0.05, 0.4] }))
|
|
15568
|
+
],
|
|
15569
|
+
"the tab of a can and the gas hissing out"
|
|
15570
|
+
),
|
|
15571
|
+
foley(
|
|
15572
|
+
"can_crush",
|
|
15573
|
+
[
|
|
15574
|
+
sceneNoise({
|
|
15575
|
+
type: "bandpass",
|
|
15576
|
+
f: [1600, 2200],
|
|
15577
|
+
q: 1.2,
|
|
15578
|
+
d: [0.18, 0.25],
|
|
15579
|
+
vol: [0.3, 0.38]
|
|
15580
|
+
}),
|
|
15581
|
+
...sceneMany(
|
|
15582
|
+
6,
|
|
15583
|
+
() => sceneTone({
|
|
15584
|
+
f: [1200, 3400],
|
|
15585
|
+
f2: 900,
|
|
15586
|
+
d: 0.03,
|
|
15587
|
+
vol: [0.03, 0.06],
|
|
15588
|
+
delay: [0, 0.2]
|
|
15589
|
+
})
|
|
15590
|
+
)
|
|
15591
|
+
],
|
|
15592
|
+
"a can crushed in the hand"
|
|
15593
|
+
),
|
|
15594
|
+
foley(
|
|
15595
|
+
"can_puncture_spray",
|
|
15596
|
+
[
|
|
15597
|
+
sceneNoise({
|
|
15598
|
+
type: "highpass",
|
|
15599
|
+
f: [2400, 3e3],
|
|
15600
|
+
a: 5e-3,
|
|
15601
|
+
d: [0.5, 0.7],
|
|
15602
|
+
vol: [0.2, 0.26]
|
|
15603
|
+
}),
|
|
15604
|
+
sceneNoise({ type: "bandpass", f: [600, 800], q: 2, d: 0.4, vol: 0.06 })
|
|
15605
|
+
],
|
|
15606
|
+
"a can holed: the jet hissing out"
|
|
15607
|
+
),
|
|
15608
|
+
foley(
|
|
15609
|
+
"bottle_open",
|
|
15610
|
+
[
|
|
15611
|
+
...sceneMany(
|
|
15612
|
+
5,
|
|
15613
|
+
() => sceneNoise({
|
|
15614
|
+
type: "bandpass",
|
|
15615
|
+
f: [2200, 3e3],
|
|
15616
|
+
q: 4,
|
|
15617
|
+
d: 8e-3,
|
|
15618
|
+
vol: [0.1, 0.16],
|
|
15619
|
+
delay: [0, 0.18]
|
|
15620
|
+
})
|
|
15621
|
+
),
|
|
15622
|
+
sceneNoise({
|
|
15623
|
+
type: "highpass",
|
|
15624
|
+
f: 3800,
|
|
15625
|
+
a: 5e-3,
|
|
15626
|
+
d: 0.12,
|
|
15627
|
+
vol: [0.06, 0.1],
|
|
15628
|
+
delay: [0.18, 0.22]
|
|
15629
|
+
})
|
|
15630
|
+
],
|
|
15631
|
+
"the screw cap of a bottle turning off"
|
|
15632
|
+
),
|
|
15633
|
+
foley(
|
|
15634
|
+
"drink_gulp",
|
|
15635
|
+
[
|
|
15636
|
+
sceneNoise({
|
|
15637
|
+
type: "lowpass",
|
|
15638
|
+
f: [280, 360],
|
|
15639
|
+
a: 0.02,
|
|
15640
|
+
d: [0.1, 0.14],
|
|
15641
|
+
vol: [0.24, 0.3]
|
|
15642
|
+
}),
|
|
15643
|
+
sceneTone({ f: [140, 170], f2: 85, d: [0.08, 0.11], vol: [0.12, 0.16] })
|
|
15644
|
+
],
|
|
15645
|
+
"a gulp"
|
|
15646
|
+
),
|
|
15647
|
+
foley(
|
|
15648
|
+
"eat_crunch",
|
|
15649
|
+
[
|
|
15650
|
+
...sceneMany(
|
|
15651
|
+
4,
|
|
15652
|
+
() => sceneNoise({
|
|
15653
|
+
type: "bandpass",
|
|
15654
|
+
f: [1800, 3200],
|
|
15655
|
+
q: 1.5,
|
|
15656
|
+
d: [0.02, 0.04],
|
|
15657
|
+
vol: [0.12, 0.2],
|
|
15658
|
+
delay: [0, 0.12]
|
|
15659
|
+
})
|
|
15660
|
+
)
|
|
15661
|
+
],
|
|
15662
|
+
"a bite of something crunchy"
|
|
15663
|
+
),
|
|
15664
|
+
foley(
|
|
15665
|
+
"eat_chew",
|
|
15666
|
+
[
|
|
15667
|
+
sceneNoise({
|
|
15668
|
+
type: "lowpass",
|
|
15669
|
+
f: [450, 600],
|
|
15670
|
+
a: 0.03,
|
|
15671
|
+
d: [0.1, 0.14],
|
|
15672
|
+
vol: [0.12, 0.16]
|
|
15673
|
+
}),
|
|
15674
|
+
sceneNoise({
|
|
15675
|
+
type: "bandpass",
|
|
15676
|
+
f: [1100, 1400],
|
|
15677
|
+
q: 2,
|
|
15678
|
+
d: 0.04,
|
|
15679
|
+
vol: 0.04,
|
|
15680
|
+
delay: [0.03, 0.06]
|
|
15681
|
+
})
|
|
15682
|
+
],
|
|
15683
|
+
"chewing"
|
|
15684
|
+
),
|
|
15685
|
+
foley(
|
|
15686
|
+
"bandage_wrap",
|
|
15687
|
+
[
|
|
15688
|
+
sceneNoise({
|
|
15689
|
+
type: "bandpass",
|
|
15690
|
+
f: [1400, 1800],
|
|
15691
|
+
f2: 2400,
|
|
15692
|
+
q: 1,
|
|
15693
|
+
a: 0.04,
|
|
15694
|
+
d: [0.3, 0.4],
|
|
15695
|
+
vol: [0.12, 0.16]
|
|
15696
|
+
})
|
|
15697
|
+
],
|
|
15698
|
+
"a bandage unrolled round a limb"
|
|
15699
|
+
),
|
|
15700
|
+
foley(
|
|
15701
|
+
"pills_shake",
|
|
15702
|
+
[
|
|
15703
|
+
...sceneMany(
|
|
15704
|
+
8,
|
|
15705
|
+
() => sceneTone({
|
|
15706
|
+
f: [2400, 3600],
|
|
15707
|
+
d: 0.015,
|
|
15708
|
+
vol: [0.03, 0.05],
|
|
15709
|
+
delay: [0, 0.25]
|
|
15710
|
+
})
|
|
15711
|
+
),
|
|
15712
|
+
sceneNoise({ type: "bandpass", f: [1500, 1900], q: 2, d: 0.25, vol: 0.05 })
|
|
15713
|
+
],
|
|
15714
|
+
"pills shaken in their bottle"
|
|
15715
|
+
),
|
|
15716
|
+
foley(
|
|
15717
|
+
"medkit_use",
|
|
15718
|
+
[
|
|
15719
|
+
...click(1700, 0.24),
|
|
15720
|
+
sceneNoise({
|
|
15721
|
+
type: "bandpass",
|
|
15722
|
+
f: [1e3, 1400],
|
|
15723
|
+
q: 1,
|
|
15724
|
+
a: 0.03,
|
|
15725
|
+
d: [0.25, 0.32],
|
|
15726
|
+
vol: [0.08, 0.12],
|
|
15727
|
+
delay: [0.05, 0.08]
|
|
15728
|
+
})
|
|
15729
|
+
],
|
|
15730
|
+
"a first aid kit opened and rummaged"
|
|
15731
|
+
),
|
|
15732
|
+
foley("flashlight_click", click(2600, 0.3), "the switch of a flashlight")
|
|
15733
|
+
];
|
|
15734
|
+
var FOLEY_BANK = {
|
|
15735
|
+
id: "foley",
|
|
15736
|
+
sounds: [...LEAF_FOLEY, ...ITEM_FOLEY]
|
|
15737
|
+
};
|
|
15738
|
+
var TAKES = [
|
|
15739
|
+
{ f: 1, t: 1, v: 1 },
|
|
15740
|
+
{ f: 1.08, t: 0.9, v: 0.92 },
|
|
15741
|
+
{ f: 0.93, t: 1.12, v: 1.05 },
|
|
15742
|
+
{ f: 1.03, t: 1.05, v: 0.88 }
|
|
15743
|
+
];
|
|
15744
|
+
var FOOTSTEP_FALLOFF = { model: "inverse_square", k: 0.03, cutoffK: 0.03, minGain: 4e-3 };
|
|
15745
|
+
var BODY_FALLOFF = { model: "inverse_square", k: 0.012, cutoffK: 0.015, minGain: 4e-3 };
|
|
15746
|
+
var SURFACES = {
|
|
15747
|
+
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 } },
|
|
15748
|
+
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 } },
|
|
15749
|
+
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 }] },
|
|
15750
|
+
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 }] },
|
|
15751
|
+
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 } },
|
|
15752
|
+
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 } },
|
|
15753
|
+
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 } },
|
|
15754
|
+
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 } },
|
|
15755
|
+
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 }] },
|
|
15756
|
+
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 } },
|
|
15757
|
+
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 }] },
|
|
15758
|
+
carpet: { heel: { f: 70, d: 0.05, vol: 0.32 }, roll: { type: "lowpass", f: 420, q: 0.7, d: 0.07, vol: 0.3 } }
|
|
15759
|
+
};
|
|
15760
|
+
var GAITS = {
|
|
15761
|
+
walk: { vol: 1, bright: 1, toe: 0.075, short: 1 },
|
|
15762
|
+
run: { vol: 1.35, bright: 1.25, toe: 0.035, short: 0.75 }
|
|
15763
|
+
};
|
|
15764
|
+
var grainLayers = (g, take, start, scale) => Array.from(
|
|
15765
|
+
{ length: g.count },
|
|
15766
|
+
(_, 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 })
|
|
15767
|
+
);
|
|
15768
|
+
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 }));
|
|
15769
|
+
function footLayers(surface, take, gait, weight = 1) {
|
|
15770
|
+
const s = SURFACES[surface];
|
|
15771
|
+
const toe = gait.toe * take.t;
|
|
15772
|
+
return [
|
|
15773
|
+
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 }),
|
|
15774
|
+
sceneNoise({ type: "lowpass", f: 500 * gait.bright, d: 0.02 * take.t, vol: 0.18 * gait.vol * take.v * weight ** 0.5 }),
|
|
15775
|
+
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 }),
|
|
15776
|
+
...s.grains ? grainLayers(s.grains, take, toe * 0.5, gait.vol * weight ** 0.5) : [],
|
|
15777
|
+
...s.ring ? ringLayers(s.ring, take, gait.vol * weight ** 0.5) : [],
|
|
15778
|
+
...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 })] : []
|
|
15779
|
+
];
|
|
15780
|
+
}
|
|
15781
|
+
var quieter = (level, layers) => layers.map((layer2) => {
|
|
15782
|
+
const gain = layer2.gain ?? 1;
|
|
15783
|
+
if (typeof gain === "number") return { ...layer2, gain: gain * level };
|
|
15784
|
+
if (Array.isArray(gain)) return { ...layer2, gain: [gain[0] * level, gain[1] * level] };
|
|
15785
|
+
return layer2;
|
|
15786
|
+
});
|
|
15787
|
+
var recipe = (id, layers, falloff, bus = "world") => ({ id, bus, pan: true, layers, falloff, sources: ["sdk"] });
|
|
15788
|
+
var takeId = (id, index) => `${id}_${String(index + 1).padStart(2, "0")}`;
|
|
15789
|
+
function takes(id, layersOf, falloff, rate = [0.96, 1.04], bus) {
|
|
15790
|
+
const sounds = TAKES.map((take, index) => recipe(takeId(id, index), layersOf(take, index), falloff, bus));
|
|
15791
|
+
return { sounds, set: { id, variants: sounds.map((sound) => sound.id), rate } };
|
|
15792
|
+
}
|
|
15793
|
+
var footstep = (surface, gait) => takes(footstepSoundId(surface, gait), (take) => footLayers(surface, take, GAITS[gait]), FOOTSTEP_FALLOFF);
|
|
15794
|
+
var LANDINGS = { light: { weight: 1.4, cloth: 0.12 }, heavy: { weight: 2.4, cloth: 0.22 } };
|
|
15795
|
+
var LANDING_LEVEL = 0.65;
|
|
15796
|
+
var landing = (surface, landing2) => takes(
|
|
15797
|
+
`land_${surface}_${landing2}`,
|
|
15798
|
+
(take) => quieter(LANDING_LEVEL, [
|
|
15799
|
+
// both feet, a hair apart, then the knees taking the weight (a low body thump) and the clothes settling
|
|
15800
|
+
...footLayers(surface, take, GAITS.run, LANDINGS[landing2].weight),
|
|
15801
|
+
...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 })),
|
|
15802
|
+
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 }),
|
|
15803
|
+
sceneNoise({ type: "bandpass", f: [1800, 2600], q: 0.8, d: 0.18, vol: LANDINGS[landing2].cloth * take.v, delay: 0.04 })
|
|
15804
|
+
]),
|
|
15805
|
+
FOOTSTEP_FALLOFF
|
|
15806
|
+
);
|
|
15807
|
+
var whoosh = (blow3, from, to, d, vol, q = 1.6) => (take) => [
|
|
15808
|
+
// 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
|
|
15809
|
+
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 }),
|
|
15810
|
+
sceneNoise({ type: "lowpass", f: 600 * take.f, a: SWING_LEAD_SECONDS[blow3], d: d * take.t * 0.8, vol: vol * 0.35 * take.v })
|
|
15811
|
+
];
|
|
15812
|
+
var fleshHit = (o) => (take) => [
|
|
15813
|
+
sceneNoise({ type: "bandpass", f: o.slapF * take.f, q: 1.2, d: 0.03 * take.t, vol: o.slap * take.v }),
|
|
15814
|
+
sceneNoise({ type: "highpass", f: 3500 * take.f, d: 0.012, vol: o.slap * 0.4 * take.v }),
|
|
15815
|
+
sceneTone({ f: o.thudF * take.f, f2: o.thudF * 0.5, d: o.thudD * take.t, vol: o.thud * take.v }),
|
|
15816
|
+
sceneNoise({ type: "lowpass", f: 600 * take.f, d: o.thudD * 1.2 * take.t, vol: o.thud * 0.5 * take.v }),
|
|
15817
|
+
...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 })] : [],
|
|
15818
|
+
...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 })] : []
|
|
15819
|
+
];
|
|
15820
|
+
var knock = (o) => (take) => [
|
|
15821
|
+
sceneNoise({ type: "bandpass", f: o.clickF * take.f, q: 1.5, d: 0.018, vol: o.click * take.v }),
|
|
15822
|
+
...o.tones.map(([f, d, vol]) => sceneTone({ f: f * take.f, d: d * take.t, vol: vol * take.v }))
|
|
15823
|
+
];
|
|
15824
|
+
var voice = (o) => (take) => [
|
|
15825
|
+
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 })
|
|
15826
|
+
];
|
|
15827
|
+
var BODY = {
|
|
15828
|
+
jump: (take) => [
|
|
15829
|
+
sceneNoise({ type: "bandpass", f: 900 * take.f, f2: 1700 * take.f, q: 1, a: 0.03, d: 0.12, vol: 0.18 * take.v }),
|
|
15830
|
+
sceneNoise({ type: "bandpass", f: 2200 * take.f, q: 0.8, d: 0.04, vol: 0.12 * take.v }),
|
|
15831
|
+
sceneTone({ f: 85 * take.f, f2: 60, d: 0.04, vol: 0.2 * take.v })
|
|
15832
|
+
],
|
|
15833
|
+
cloth_rustle: (take) => [
|
|
15834
|
+
{ ...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 }] }
|
|
15835
|
+
],
|
|
15836
|
+
body_fall: (take) => [
|
|
15837
|
+
sceneTone({ f: 58 * take.f, f2: 35, d: 0.28 * take.t, vol: 0.9 * take.v }),
|
|
15838
|
+
sceneNoise({ type: "lowpass", f: 480 * take.f, d: 0.3 * take.t, vol: 0.55 * take.v }),
|
|
15839
|
+
sceneNoise({ type: "bandpass", f: 1300 * take.f, q: 1, d: 0.05, vol: 0.3 * take.v }),
|
|
15840
|
+
// the limbs a moment later, and the clothes
|
|
15841
|
+
sceneTone({ f: 75 * take.f, f2: 45, d: 0.12, vol: 0.4 * take.v, delay: 0.13 * take.t }),
|
|
15842
|
+
sceneNoise({ type: "bandpass", f: 2200, q: 0.8, d: 0.2, vol: 0.12 * take.v, delay: 0.05 })
|
|
15843
|
+
],
|
|
15844
|
+
fall_damage: (take) => [
|
|
15845
|
+
...BODY.body_fall(take),
|
|
15846
|
+
sceneNoise({ type: "highpass", f: 2800, d: 0.02, vol: 0.35 * take.v, delay: 0.01 }),
|
|
15847
|
+
...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 }))
|
|
15848
|
+
],
|
|
15849
|
+
player_hurt: voice({ f0: 150, f1: 105, d: 0.26, fA: 620, fB: 1100, noise: 0.3, vol: 0.35 }),
|
|
15850
|
+
player_breath_run: (take) => [
|
|
15851
|
+
sceneNoise({ type: "bandpass", f: 1200 * take.f, q: 1, a: 0.08, d: 0.22 * take.t, vol: 0.25 * take.v }),
|
|
15852
|
+
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 })
|
|
15853
|
+
],
|
|
15854
|
+
player_death: voice({ f0: 135, f1: 68, d: 1, fA: 560, fB: 980, noise: 0.35, vol: 0.4 }),
|
|
15855
|
+
bite: (take) => [
|
|
15856
|
+
sceneNoise({ type: "highpass", f: 2600, d: 0.015, vol: 0.4 * take.v }),
|
|
15857
|
+
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 }),
|
|
15858
|
+
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 }),
|
|
15859
|
+
sceneTone({ f: 120 * take.f, f2: 70, d: 0.06, vol: 0.3 * take.v })
|
|
15860
|
+
],
|
|
15861
|
+
grab: (take) => [
|
|
15862
|
+
sceneNoise({ type: "bandpass", f: 1900 * take.f, q: 0.9, a: 4e-3, d: 0.13 * take.t, vol: 0.32 * take.v }),
|
|
15863
|
+
sceneTone({ f: 95 * take.f, f2: 60, d: 0.07, vol: 0.3 * take.v }),
|
|
15864
|
+
sceneNoise({ type: "lowpass", f: 700, d: 0.08, vol: 0.2 * take.v })
|
|
15865
|
+
],
|
|
15866
|
+
punch_swing: whoosh("punch", 450, 1500, 0.16, 0.9),
|
|
15867
|
+
punch_hit_body: fleshHit({ slap: 0.55, slapF: 1500, thud: 0.6, thudF: 115, thudD: 0.09 }),
|
|
15868
|
+
punch_hit_face: fleshHit({ slap: 0.75, slapF: 2300, thud: 0.45, thudF: 150, thudD: 0.07, crack: 0.25 }),
|
|
15869
|
+
punch_hit_object: knock({ clickF: 1800, click: 0.3, tones: [[210, 0.04, 0.25]] }),
|
|
15870
|
+
kick_swing: whoosh("kick", 280, 950, 0.22, 1, 1.3),
|
|
15871
|
+
kick_hit: fleshHit({ slap: 0.45, slapF: 1100, thud: 0.85, thudF: 82, thudD: 0.14 }),
|
|
15872
|
+
kick_hit_object: knock({ clickF: 1e3, click: 0.3, tones: [[92, 0.08, 0.5]] }),
|
|
15873
|
+
shove_two_hands: (take) => [
|
|
15874
|
+
sceneNoise({ type: "bandpass", f: 950 * take.f, q: 0.9, a: 0.01, d: 0.13 * take.t, vol: 0.38 * take.v }),
|
|
15875
|
+
sceneTone({ f: 100 * take.f, f2: 60, d: 0.1 * take.t, vol: 0.45 * take.v }),
|
|
15876
|
+
sceneNoise({ type: "bandpass", f: 2300, q: 0.8, d: 0.16, vol: 0.12 * take.v, delay: 0.03 })
|
|
15877
|
+
],
|
|
15878
|
+
knife_swing: whoosh("blade", 1900, 4600, 0.12, 1.1, 2.2),
|
|
15879
|
+
knife_stab: fleshHit({ slap: 0.2, slapF: 1200, thud: 0.4, thudF: 160, thudD: 0.06, wet: 0.5 }),
|
|
15880
|
+
knife_slash: (take) => [
|
|
15881
|
+
sceneNoise({ type: "highpass", f: 3200 * take.f, f2: 1800, d: 0.13 * take.t, vol: 0.35 * take.v }),
|
|
15882
|
+
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 })
|
|
15883
|
+
],
|
|
15884
|
+
knife_hit_object: knock({ clickF: 3200, click: 0.25, tones: [[2800, 0.08, 0.07], [4150, 0.06, 0.05]] }),
|
|
15885
|
+
blunt_swing: whoosh("blunt", 220, 760, 0.28, 1.1, 1.5),
|
|
15886
|
+
blunt_hit: fleshHit({ slap: 0.35, slapF: 900, thud: 1, thudF: 70, thudD: 0.18, crack: 0.3 }),
|
|
15887
|
+
blunt_hit_object: knock({ clickF: 1400, click: 0.35, tones: [[300, 0.12, 0.25], [760, 0.08, 0.12]] }),
|
|
15888
|
+
axe_swing: whoosh("chop", 190, 620, 0.3, 1.2, 1.4),
|
|
15889
|
+
axe_chop: fleshHit({ slap: 0.3, slapF: 1400, thud: 0.75, thudF: 140, thudD: 0.1, crack: 0.35, wet: 0.45 }),
|
|
15890
|
+
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]] })
|
|
15891
|
+
};
|
|
15892
|
+
var BODY_BUS = { player_hurt: "voices", player_death: "voices", player_breath_run: "voices", fall_damage: "voices" };
|
|
15893
|
+
var built = [
|
|
15894
|
+
...SURFACE_IDS.flatMap((surface) => GAIT_IDS.map((gait) => footstep(surface, gait))),
|
|
15895
|
+
...SURFACE_IDS.flatMap((surface) => LANDING_IDS.map((l) => landing(surface, l))),
|
|
15896
|
+
...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]))
|
|
15897
|
+
];
|
|
15898
|
+
var BODY_FOLEY_BANK = {
|
|
15899
|
+
id: "body_foley",
|
|
15900
|
+
sounds: built.flatMap((entry) => entry.sounds),
|
|
15901
|
+
sets: built.map((entry) => entry.set)
|
|
15902
|
+
};
|
|
15903
|
+
var BODY_FOLEY_SET_IDS = [...FOOTSTEP_SOUND_IDS, ...LAND_SOUND_IDS, ...BODY_SOUND_IDS];
|
|
15904
|
+
|
|
15905
|
+
// src/audio/blast-fire.ts
|
|
15906
|
+
var BLAST_FALLOFF = { model: "inverse_square", k: 12e-4, cutoffK: 6e-3, minGain: 2e-3 };
|
|
15907
|
+
var BLAST_FAR_FALLOFF = { model: "inverse_square", k: 8e-5, scale: 0.3, minGain: 1e-3 };
|
|
15908
|
+
var FIRE_FALLOFF = { model: "inverse_square", k: 0.01, cutoffK: 0.01, minGain: 5e-3 };
|
|
15909
|
+
var BLAST_FAR_DISTANCE = 60;
|
|
15910
|
+
var BLAST_KINDS = ["grenade", "big", "fuel", "car", "pipe_bomb", "rocket", "flashbang", "molotov"];
|
|
15911
|
+
var BLAST_KIND_OF = {
|
|
15912
|
+
m67: "grenade",
|
|
15913
|
+
vog_25: "grenade",
|
|
15914
|
+
m79_40mm: "grenade",
|
|
15915
|
+
claymore: "grenade",
|
|
15916
|
+
generic: "grenade",
|
|
15917
|
+
c4: "big",
|
|
15918
|
+
dynamite: "big",
|
|
15919
|
+
fuel_station_tank: "big",
|
|
15920
|
+
propane_p13: "fuel",
|
|
15921
|
+
fuel_barrel: "fuel",
|
|
15922
|
+
fuel_pump: "fuel",
|
|
15923
|
+
car_fuel_tank: "car",
|
|
15924
|
+
pipe_bomb: "pipe_bomb",
|
|
15925
|
+
m72_66mm: "rocket",
|
|
15926
|
+
at4_84mm: "rocket",
|
|
15927
|
+
carl_gustaf_84mm: "rocket",
|
|
15928
|
+
pg7v: "rocket",
|
|
15929
|
+
flashbang: "flashbang",
|
|
15930
|
+
molotov: "molotov"
|
|
15931
|
+
};
|
|
15932
|
+
var BIG_BLAST_RADIUS = 8;
|
|
15933
|
+
var blast = (id, layers, sources, falloff = BLAST_FALLOFF) => ({
|
|
15934
|
+
...sceneRecipe(id, layers, ""),
|
|
15935
|
+
falloff,
|
|
15936
|
+
sources
|
|
15937
|
+
});
|
|
15938
|
+
var crack = (f, d, vol) => sceneNoise({ type: "highpass", f, a: 1e-3, d, vol });
|
|
15939
|
+
var debris2 = (count, from, to, vol) => [
|
|
15940
|
+
...sceneMany(count, () => sceneNoise({ type: "bandpass", f: [300, 1400], q: 2.5, d: [0.03, 0.09], vol, delay: [from, to] })),
|
|
15941
|
+
...sceneMany(Math.ceil(count / 3), () => sceneNoise({ type: "bandpass", f: [1800, 3600], q: 5, d: [0.02, 0.04], vol: [vol[0] * 0.6, vol[1] * 0.6], delay: [from + 0.1, to] }))
|
|
15942
|
+
];
|
|
15943
|
+
var shrapnel = (count, to) => sceneMany(count, () => sceneNoise({ type: "bandpass", f: [2500, 6e3], q: 6, d: [0.015, 0.045], vol: [0.08, 0.16], delay: [0.02, to] }));
|
|
15944
|
+
var glass = (count, from) => [
|
|
15945
|
+
sceneNoise({ type: "highpass", f: [2600, 3e3], d: 0.4, vol: 0.7, wet: 0.5, delay: from }),
|
|
15946
|
+
...sceneMany(count, () => sceneTone({ f: [3e3, 7500], d: [0.05, 0.13], vol: 0.12, delay: [from, from + 0.7] }))
|
|
15947
|
+
];
|
|
15948
|
+
var BLAST_SOUNDS = [
|
|
15949
|
+
blast(
|
|
15950
|
+
"explosion_grenade",
|
|
15951
|
+
[
|
|
15952
|
+
crack([2300, 2700], 0.06, 2.4),
|
|
15953
|
+
sceneNoise({ f: [2500, 3e3], f2: 160, d: 0.7, vol: 2, wet: 0.35 }),
|
|
15954
|
+
sceneTone({ f: [80, 92], f2: 32, d: 0.5, vol: 1.6 }),
|
|
15955
|
+
sceneNoise({ type: "bandpass", f: [620, 760], q: 0.8, d: 0.18, vol: 1.1 }),
|
|
15956
|
+
...shrapnel(14, 0.7),
|
|
15957
|
+
...debris2(10, 0.35, 1.8, [0.1, 0.2])
|
|
15958
|
+
],
|
|
15959
|
+
["sdk: frag grenade (M67, VOG-25, 40 mm): sharp crack, short body, fragments striking, a little debris"]
|
|
15960
|
+
),
|
|
15961
|
+
blast(
|
|
15962
|
+
"explosion_big",
|
|
15963
|
+
[
|
|
15964
|
+
crack([1900, 2300], 0.08, 2.4),
|
|
15965
|
+
sceneNoise({ f: [2200, 2600], f2: 90, d: 1.6, vol: 2.2, wet: 0.45 }),
|
|
15966
|
+
sceneTone({ f: [52, 60], f2: 22, d: 1.6, vol: 1.9 }),
|
|
15967
|
+
sceneNoise({ f: [280, 330], f2: 60, a: 0.25, d: 2.8, vol: 0.8, wet: 0.6, delay: 0.05 }),
|
|
15968
|
+
...debris2(24, 0.5, 3.6, [0.12, 0.25])
|
|
15969
|
+
],
|
|
15970
|
+
["sdk: big charge (C4, dynamite, a fuel station tank): crack, long deep roar, debris falling for 2 to 4 s"]
|
|
15971
|
+
),
|
|
15972
|
+
blast(
|
|
15973
|
+
"explosion_fuel",
|
|
15974
|
+
[
|
|
15975
|
+
sceneNoise({ type: "highpass", f: [1700, 2e3], a: 1e-3, d: 0.05, vol: 1 }),
|
|
15976
|
+
sceneTone({ f: [380, 450], f2: 300, d: 0.4, vol: 0.3 }),
|
|
15977
|
+
sceneNoise({ f: [1700, 2e3], f2: 150, a: 0.03, d: 1.4, vol: 2.1, wet: 0.4 }),
|
|
15978
|
+
sceneTone({ f: [48, 56], f2: 25, d: 1.2, vol: 1.7 }),
|
|
15979
|
+
sceneNoise({ type: "bandpass", f: [450, 560], q: 0.6, a: 0.15, d: 2.2, vol: 0.85, wet: 0.5, delay: 0.1 }),
|
|
15980
|
+
...debris2(10, 0.5, 2.6, [0.08, 0.16])
|
|
15981
|
+
],
|
|
15982
|
+
['sdk: a fuel vessel bursting (propane P13, fuel barrel, pump): the shell tearing, a fireball "whoomp" and its roar']
|
|
15983
|
+
),
|
|
15984
|
+
blast(
|
|
15985
|
+
"explosion_car",
|
|
15986
|
+
[
|
|
15987
|
+
sceneNoise({ type: "highpass", f: [1700, 2e3], a: 1e-3, d: 0.06, vol: 1.1 }),
|
|
15988
|
+
sceneNoise({ f: [1600, 1900], f2: 140, a: 0.03, d: 1.6, vol: 2.1, wet: 0.4 }),
|
|
15989
|
+
sceneTone({ f: [44, 52], f2: 24, d: 1.4, vol: 1.8 }),
|
|
15990
|
+
sceneNoise({ type: "bandpass", f: [420, 520], q: 0.6, a: 0.15, d: 2.6, vol: 0.9, wet: 0.5, delay: 0.1 }),
|
|
15991
|
+
...glass(10, 0.04),
|
|
15992
|
+
sceneTone({ f: [130, 150], f2: 85, d: 1.1, vol: 0.18, delay: [0.3, 0.5] }),
|
|
15993
|
+
...debris2(16, 0.6, 3.2, [0.1, 0.2])
|
|
15994
|
+
],
|
|
15995
|
+
["sdk: a car going up: the fuel tank fireball, every window breaking, the body groaning, parts falling"]
|
|
15996
|
+
),
|
|
15997
|
+
blast(
|
|
15998
|
+
"explosion_pipe_bomb",
|
|
15999
|
+
[
|
|
16000
|
+
crack([2e3, 2400], 0.07, 2.2),
|
|
16001
|
+
sceneNoise({ f: [2e3, 2400], f2: 150, d: 0.8, vol: 1.9, wet: 0.35 }),
|
|
16002
|
+
sceneTone({ f: [70, 82], f2: 30, d: 0.6, vol: 1.5 }),
|
|
16003
|
+
...sceneMany(6, () => sceneTone({ f: [1800, 3200], f2: 1200, d: [0.06, 0.12], vol: 0.06, delay: [0.05, 0.6] })),
|
|
16004
|
+
...shrapnel(18, 0.9),
|
|
16005
|
+
...debris2(8, 0.4, 1.8, [0.08, 0.16])
|
|
16006
|
+
],
|
|
16007
|
+
["sdk: pipe bomb: crack, body, the steel pipe flying apart in ringing pieces"]
|
|
16008
|
+
),
|
|
16009
|
+
blast(
|
|
16010
|
+
"explosion_rocket",
|
|
16011
|
+
[
|
|
16012
|
+
crack([2100, 2500], 0.08, 2.4),
|
|
16013
|
+
sceneNoise({ f: [2400, 2800], f2: 120, d: 1.1, vol: 2.1, wet: 0.4 }),
|
|
16014
|
+
sceneTone({ f: [60, 70], f2: 26, d: 0.9, vol: 1.8 }),
|
|
16015
|
+
sceneNoise({ f: [350, 420], f2: 80, a: 0.08, d: 1.8, vol: 0.9, wet: 0.6, delay: 0.05 }),
|
|
16016
|
+
...shrapnel(10, 0.8),
|
|
16017
|
+
...debris2(16, 0.4, 2.6, [0.1, 0.2])
|
|
16018
|
+
],
|
|
16019
|
+
["sdk: rocket or anti-tank warhead (M72, AT4, RPG): hard crack, heavy body, debris"]
|
|
16020
|
+
),
|
|
16021
|
+
blast(
|
|
16022
|
+
"explosion_flashbang",
|
|
16023
|
+
[crack([1400, 1700], 0.12, 2.8), sceneNoise({ f: [1900, 2200], f2: 300, d: 0.35, vol: 1.3, wet: 0.3 }), sceneTone({ f: [110, 130], f2: 60, d: 0.2, vol: 0.8 })],
|
|
16024
|
+
["sdk: flashbang: a deafening crack with almost no body and no fragments"]
|
|
16025
|
+
),
|
|
16026
|
+
blast(
|
|
16027
|
+
"molotov_break_ignite",
|
|
16028
|
+
[
|
|
16029
|
+
...glass(8, 0),
|
|
16030
|
+
sceneNoise({ f: [280, 340], f2: 2200, a: 0.12, d: 0.9, vol: 1.5, wet: 0.6, delay: 0.05 }),
|
|
16031
|
+
sceneTone({ f: [85, 95], f2: 55, d: 0.4, vol: 0.5, delay: 0.08 })
|
|
16032
|
+
],
|
|
16033
|
+
['sdk: molotov: the bottle breaking and the fuel catching with a "fuuush"'],
|
|
16034
|
+
FIRE_FALLOFF
|
|
16035
|
+
),
|
|
16036
|
+
blast(
|
|
16037
|
+
"explosion_far",
|
|
16038
|
+
[
|
|
16039
|
+
sceneNoise({ f: [550, 650], f2: 80, a: 0.03, d: 2.5, vol: 2, wet: 1.4 }),
|
|
16040
|
+
sceneTone({ f: [42, 48], f2: 25, d: 1.8, vol: 1.2 }),
|
|
16041
|
+
sceneNoise({ f: [350, 420], f2: 70, a: 0.1, d: 1.6, vol: 0.6, wet: 1.5, delay: [0.6, 1] })
|
|
16042
|
+
],
|
|
16043
|
+
["sdk: any blast far away: no crack left, a dull thud and the echo rolling back"],
|
|
16044
|
+
BLAST_FAR_FALLOFF
|
|
16045
|
+
),
|
|
16046
|
+
blast(
|
|
16047
|
+
"explosion_indoor_tail",
|
|
16048
|
+
[
|
|
16049
|
+
...sceneMany(3, () => sceneNoise({ f: [650, 800], d: 0.12, vol: 0.5, delay: [0.04, 0.2] })),
|
|
16050
|
+
sceneNoise({ f: [850, 950], f2: 120, a: 0.05, d: 2.6, vol: 0.9, wet: 1.6, delay: 0.08 })
|
|
16051
|
+
],
|
|
16052
|
+
["sdk: the room answering a blast inside it: slaps off the walls and a boomy tail"]
|
|
16053
|
+
)
|
|
16054
|
+
];
|
|
16055
|
+
var FIRE_SOUNDS = [
|
|
16056
|
+
{
|
|
16057
|
+
...blast(
|
|
16058
|
+
"fire_ignite",
|
|
16059
|
+
[sceneNoise({ f: [230, 270], f2: 1500, a: 0.07, d: 0.5, vol: 1.6 }), sceneTone({ f: [80, 90], f2: 55, d: 0.3, vol: 0.45 }), sceneNoise({ type: "highpass", f: 3e3, d: 0.3, vol: 0.1, delay: 0.1 })],
|
|
16060
|
+
['sdk: fire catching, a soft "fuuump"'],
|
|
16061
|
+
FIRE_FALLOFF
|
|
16062
|
+
),
|
|
16063
|
+
group: "fire_ignite"
|
|
16064
|
+
},
|
|
16065
|
+
{
|
|
16066
|
+
...blast("gas_leak", [sceneNoise({ type: "bandpass", f: [3200, 3800], q: 0.7, a: 0.08, d: { param: "seconds" }, vol: 0.3 })], ["sdk: gas jetting from a punctured vessel"], FIRE_FALLOFF),
|
|
16067
|
+
vars: { seconds: 2 }
|
|
16068
|
+
},
|
|
16069
|
+
{
|
|
16070
|
+
...blast(
|
|
16071
|
+
"fire_extinguish",
|
|
16072
|
+
[sceneNoise({ type: "highpass", f: [2300, 2700], a: 0.03, d: { param: "seconds" }, vol: 0.3 }), sceneNoise({ type: "bandpass", f: [800, 1e3], q: 0.5, d: 0.5, vol: 0.25 })],
|
|
16073
|
+
["sdk: water or foam on a fire: a steam puff and a sizzle"],
|
|
16074
|
+
FIRE_FALLOFF
|
|
16075
|
+
),
|
|
16076
|
+
vars: { seconds: 1.2 }
|
|
16077
|
+
},
|
|
16078
|
+
{ ...blast("fire_crackle", [sceneNoise({ type: "highpass", f: [1500, 3e3], d: [0.01, 0.025], vol: [1.2, 2.2] })], ["sdk: a crackle of the fire"], FIRE_FALLOFF), group: "fire_crackle" },
|
|
16079
|
+
{ ...blast("fire_pop", [sceneNoise({ type: "bandpass", f: [500, 1100], q: 1, d: [0.03, 0.06], vol: [3, 4.5] })], ["sdk: a log or a resin pocket popping"], FIRE_FALLOFF), group: "fire_crackle" }
|
|
16080
|
+
];
|
|
16081
|
+
var FIRE_LOOP = {
|
|
16082
|
+
id: "fire_loop",
|
|
16083
|
+
bus: "world",
|
|
16084
|
+
order: "chain_loop",
|
|
16085
|
+
source: { kind: "noise", loop: true },
|
|
16086
|
+
filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: 950 }, q: 0.5 }] },
|
|
16087
|
+
volume: 0,
|
|
16088
|
+
sources: ["sdk: the roar under the crackles of a fire"]
|
|
16089
|
+
};
|
|
16090
|
+
var BLAST_SOUND_OF = {
|
|
16091
|
+
grenade: "explosion_grenade",
|
|
16092
|
+
big: "explosion_big",
|
|
16093
|
+
fuel: "explosion_fuel",
|
|
16094
|
+
car: "explosion_car",
|
|
16095
|
+
pipe_bomb: "explosion_pipe_bomb",
|
|
16096
|
+
rocket: "explosion_rocket",
|
|
16097
|
+
flashbang: "explosion_flashbang",
|
|
16098
|
+
molotov: "molotov_break_ignite"
|
|
14267
16099
|
};
|
|
14268
16100
|
|
|
14269
16101
|
// src/audio/recipes.ts
|
|
@@ -14348,11 +16180,111 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14348
16180
|
leaf("shut_metal", [sceneTone({ f: 160, f2: 85, d: 0.18, vol: 0.55 }), sceneNoise({ type: "bandpass", f: 900, q: 1.6, d: 0.12, vol: 0.45, wet: 0.3 }), sceneTone({ f: 1250, f2: 1150, d: 0.12, vol: 0.06 })]),
|
|
14349
16181
|
leaf("shut_soft", [sceneTone({ f: 95, f2: 55, d: 0.12, vol: 0.35 }), sceneNoise({ type: "lowpass", f: 500, d: 0.1, vol: 0.3 })]),
|
|
14350
16182
|
leaf("rattle", sceneMany(5, () => sceneNoise({ type: "bandpass", f: [1600, 2600], q: 3, d: 0.035, vol: 0.32, delay: [0, 0.45] }))),
|
|
14351
|
-
leaf("slide", [sceneNoise({ type: "bandpass", f: 700, q: 2, a: 0.04, d: 0.35, vol: 0.22 }), sceneNoise({ type: "bandpass", f: 2200, q: 5, d: 0.3, vol: 0.05 })])
|
|
16183
|
+
leaf("slide", [sceneNoise({ type: "bandpass", f: 700, q: 2, a: 0.04, d: 0.35, vol: 0.22 }), sceneNoise({ type: "bandpass", f: 2200, q: 5, d: 0.3, vol: 0.05 })]),
|
|
16184
|
+
// an item taken in the hand: the fingers on a hard surface and the sleeve moving, or a soft thing gathered up (the kit's recordings answer these ids)
|
|
16185
|
+
leaf("grab_hard", [sceneNoise({ type: "bandpass", f: 1800, q: 3, d: 0.03, vol: 0.18 }), sceneNoise({ type: "bandpass", f: 900, q: 1.2, a: 0.02, d: 0.14, vol: 0.08 })]),
|
|
16186
|
+
leaf("grab_soft", [sceneNoise({ type: "lowpass", f: 1400, a: 0.02, d: 0.16, vol: 0.14 })])
|
|
14352
16187
|
];
|
|
14353
|
-
var
|
|
16188
|
+
var impact2 = (id, layers) => ({ ...sceneRecipe(id, layers, ""), sources: ["sdk: a blow on a material, by material and kind of blow (synthesised until the recorded samples arrive)"] });
|
|
16189
|
+
var IMPACT_SOUNDS = [
|
|
16190
|
+
// metal
|
|
16191
|
+
impact2("metal_bullet", [sceneNoise({ type: "highpass", f: 3e3, d: 0.03, vol: 0.8 }), sceneTone({ f: [2600, 3400], f2: 1800, d: 0.14, vol: 0.28, wet: 0.35 }), sceneTone({ f: [520, 720], f2: 480, d: 0.35, vol: 0.22, wet: 0.35 })]),
|
|
16192
|
+
impact2("metal_blunt", [sceneNoise({ type: "bandpass", f: 800, q: 1, d: 0.05, vol: 0.5 }), sceneTone({ f: [150, 210], f2: 120, d: 0.6, vol: 0.5, wet: 0.4 }), sceneTone({ f: [420, 520], f2: 400, d: 0.4, vol: 0.12 })]),
|
|
16193
|
+
impact2("metal_blade", [sceneNoise({ type: "bandpass", f: [3500, 4500], q: 6, a: 0.01, d: 0.18, vol: 0.45 }), sceneTone({ f: [2200, 2800], f2: 3e3, d: 0.15, vol: 0.06 })]),
|
|
16194
|
+
impact2("metal_thin", [sceneNoise({ type: "highpass", f: 2500, d: 0.04, vol: 0.6 }), sceneTone({ f: [900, 1300], f2: 700, d: 0.25, vol: 0.22, wet: 0.3 }), sceneNoise({ type: "bandpass", f: 1200, q: 2, d: 0.08, vol: 0.3 })]),
|
|
16195
|
+
// wood (the bullet keeps the art scene's `wood_hit`)
|
|
16196
|
+
impact2("wood_blunt", [sceneTone({ f: [110, 150], f2: 70, d: 0.14, vol: 0.6 }), sceneNoise({ type: "bandpass", f: 500, q: 1.2, d: 0.12, vol: 0.55, wet: 0.2 })]),
|
|
16197
|
+
impact2("wood_blade", [sceneNoise({ type: "bandpass", f: [1800, 2400], q: 2, a: 0.01, d: 0.12, vol: 0.5 }), ...sceneMany(3, () => sceneNoise({ type: "bandpass", f: [2500, 4e3], q: 3, d: 0.02, vol: 0.22, delay: [0.02, 0.1] }))]),
|
|
16198
|
+
impact2("mdf_puff", [sceneNoise({ type: "lowpass", f: 900, d: 0.08, vol: 1 }), sceneTone({ f: [180, 230], f2: 110, d: 0.06, vol: 0.3 }), sceneNoise({ type: "highpass", f: 3e3, a: 0.01, d: 0.2, vol: 0.1 })]),
|
|
16199
|
+
// glass and ceramic
|
|
16200
|
+
impact2("glass_bullet", [sceneNoise({ type: "highpass", f: 4e3, d: 0.05, vol: 0.7 }), sceneTone({ f: [3200, 4200], f2: 2600, d: 0.2, vol: 0.14, wet: 0.3 }), ...sceneMany(4, () => sceneTone({ f: [4e3, 7e3], d: 0.05, vol: 0.07, delay: [0.02, 0.2] }))]),
|
|
16201
|
+
impact2("glass_blunt", [sceneNoise({ type: "bandpass", f: 2200, q: 3, d: 0.05, vol: 0.8 }), sceneTone({ f: [1400, 1800], f2: 1300, d: 0.2, vol: 0.16, wet: 0.3 })]),
|
|
16202
|
+
impact2("glass_blade", [sceneNoise({ type: "bandpass", f: 5e3, q: 8, a: 0.02, d: 0.25, vol: 0.3 })]),
|
|
16203
|
+
impact2("ceramic_hit", [sceneNoise({ type: "bandpass", f: 3e3, q: 2, d: 0.03, vol: 0.6 }), sceneTone({ f: [1800, 2400], f2: 1600, d: 0.1, vol: 0.2 })]),
|
|
16204
|
+
impact2("ceramic_bullet", [sceneNoise({ type: "highpass", f: 2500, d: 0.06, vol: 0.85 }), ...sceneMany(5, () => sceneTone({ f: [2500, 5e3], d: 0.04, vol: 0.09, delay: [0, 0.15] }))]),
|
|
16205
|
+
// masonry and the ground
|
|
16206
|
+
impact2("masonry_bullet", [sceneNoise({ type: "bandpass", f: [1600, 2e3], q: 1, d: 0.05, vol: 1.8 }), sceneNoise({ type: "lowpass", f: 600, d: 0.12, vol: 0.9, wet: 0.2 }), ...sceneMany(4, () => sceneNoise({ type: "bandpass", f: [2e3, 4e3], q: 3, d: 0.02, vol: 0.28, delay: [0.03, 0.3] }))]),
|
|
16207
|
+
impact2("masonry_blunt", [sceneTone({ f: [70, 95], f2: 45, d: 0.12, vol: 0.55 }), sceneNoise({ type: "lowpass", f: 400, d: 0.1, vol: 0.5 })]),
|
|
16208
|
+
impact2("masonry_blade", [sceneNoise({ type: "bandpass", f: [2500, 3500], q: 4, a: 0.01, d: 0.15, vol: 0.4 })]),
|
|
16209
|
+
impact2("plaster_puff", [sceneNoise({ type: "lowpass", f: 900, d: 0.09, vol: 0.7 }), sceneNoise({ type: "highpass", f: 3e3, a: 0.01, d: 0.2, vol: 0.14 })]),
|
|
16210
|
+
impact2("soil_bullet", [sceneNoise({ type: "lowpass", f: 700, d: 0.08, vol: 2.2 }), ...sceneMany(3, () => sceneNoise({ type: "bandpass", f: [1200, 2200], q: 2, d: 0.03, vol: 0.18, delay: [0.02, 0.2] }))]),
|
|
16211
|
+
impact2("soil_blunt", [sceneTone({ f: [55, 70], f2: 40, d: 0.1, vol: 0.45 }), sceneNoise({ type: "lowpass", f: 300, d: 0.08, vol: 0.4 })]),
|
|
16212
|
+
impact2("sand_hit", [sceneNoise({ type: "bandpass", f: 3500, q: 0.8, a: 5e-3, d: 0.15, vol: 0.45 }), sceneNoise({ type: "lowpass", f: 500, d: 0.06, vol: 0.45 })]),
|
|
16213
|
+
impact2("gravel_hit", [sceneNoise({ type: "lowpass", f: 500, d: 0.06, vol: 0.45 }), ...sceneMany(6, () => sceneNoise({ type: "bandpass", f: [1500, 3500], q: 3, d: 0.025, vol: 0.28, delay: [0, 0.18] }))]),
|
|
16214
|
+
// plastic, rubber, wax
|
|
16215
|
+
impact2("plastic_hit", [sceneNoise({ type: "bandpass", f: 1600, q: 3, d: 0.04, vol: 0.5 }), sceneTone({ f: [380, 520], f2: 300, d: 0.08, vol: 0.32 })]),
|
|
16216
|
+
impact2("plastic_bullet", [sceneNoise({ type: "bandpass", f: 2500, q: 2, d: 0.03, vol: 1.3 }), sceneTone({ f: [600, 800], f2: 450, d: 0.06, vol: 0.4 })]),
|
|
16217
|
+
impact2("rubber_hit", [sceneTone({ f: [90, 130], f2: 60, d: 0.1, vol: 0.45 }), sceneNoise({ type: "lowpass", f: 350, d: 0.06, vol: 0.28 })]),
|
|
16218
|
+
impact2("wax_hit", [sceneNoise({ type: "lowpass", f: 800, d: 0.05, vol: 0.45 }), sceneTone({ f: [240, 280], f2: 180, d: 0.05, vol: 0.22 })]),
|
|
16219
|
+
// soft things, paper, plants
|
|
16220
|
+
impact2("soft_bullet", [sceneNoise({ type: "lowpass", f: 450, a: 3e-3, d: 0.1, vol: 1.8 }), sceneNoise({ type: "bandpass", f: 900, q: 1, d: 0.05, vol: 0.5 })]),
|
|
16221
|
+
impact2("fabric_rip", [sceneNoise({ type: "bandpass", f: [2e3, 2800], q: 1.5, a: 0.02, d: 0.25, vol: 0.4 }), ...sceneMany(4, () => sceneNoise({ type: "bandpass", f: [3e3, 5e3], q: 4, d: 0.02, vol: 0.14, delay: [0, 0.22] }))]),
|
|
16222
|
+
impact2("paper_hit", [sceneNoise({ type: "bandpass", f: 2200, q: 1, d: 0.05, vol: 0.45 })]),
|
|
16223
|
+
impact2("paper_tear", [sceneNoise({ type: "highpass", f: 2500, a: 0.02, d: 0.3, vol: 0.15 })]),
|
|
16224
|
+
impact2("leaf_rustle", sceneMany(6, () => sceneNoise({ type: "bandpass", f: [2500, 6e3], q: 2, d: 0.05, vol: 0.16, delay: [0, 0.3] }))),
|
|
16225
|
+
impact2("branch_cut", [sceneNoise({ type: "bandpass", f: 1500, q: 2, d: 0.04, vol: 0.55 }), sceneTone({ f: [260, 340], f2: 150, d: 0.05, vol: 0.22 })]),
|
|
16226
|
+
impact2("fruit_splat", [sceneNoise({ type: "lowpass", f: 1200, d: 0.12, vol: 0.55 }), sceneNoise({ type: "bandpass", f: 600, q: 0.7, d: 0.2, vol: 0.32 })]),
|
|
16227
|
+
// a round glancing off: steel whines down in pitch ("piiuu"), stone and concrete give a short crack with grit
|
|
16228
|
+
impact2("ricochet_metal", [sceneNoise({ type: "highpass", f: 3e3, d: 0.02, vol: 0.6 }), sceneTone({ f: [2800, 3600], f2: 900, d: 0.38, vol: 0.2, wet: 0.4 }), sceneTone({ f: [5200, 6400], f2: 2e3, d: 0.2, vol: 0.05 })]),
|
|
16229
|
+
impact2("ricochet_stone", [sceneNoise({ type: "bandpass", f: [2200, 2800], q: 2, d: 0.04, vol: 0.8 }), sceneTone({ f: [1800, 2400], f2: 1200, d: 0.08, vol: 0.1 }), ...sceneMany(3, () => sceneNoise({ type: "bandpass", f: [2500, 4500], q: 3, d: 0.015, vol: 0.2, delay: [0.02, 0.12] }))]),
|
|
16230
|
+
// breaks a material has of its own (the rest keep the art scene's shatter, crumble and splinter)
|
|
16231
|
+
impact2("ceramic_break", [sceneNoise({ type: "highpass", f: 1800, d: 0.3, vol: 0.85, wet: 0.4 }), ...sceneMany(8, () => sceneTone({ f: [1800, 4500], d: [0.04, 0.1], vol: 0.15, delay: [0, 0.4] }))]),
|
|
16232
|
+
impact2("tempered_burst", [sceneNoise({ type: "highpass", f: 3500, d: 0.6, vol: 0.9, wet: 0.4 }), ...sceneMany(16, () => sceneTone({ f: [4e3, 8e3], d: 0.04, vol: 0.08, delay: [0, 0.8] }))]),
|
|
16233
|
+
impact2("plastic_crack", [sceneNoise({ type: "bandpass", f: 1800, q: 2, d: 0.06, vol: 0.75 }), sceneTone({ f: [450, 550], f2: 250, d: 0.05, vol: 0.22 })]),
|
|
16234
|
+
impact2("mdf_crumble", [sceneNoise({ type: "lowpass", f: 700, d: 0.25, vol: 0.75, wet: 0.2 }), ...sceneMany(4, () => sceneNoise({ type: "lowpass", f: 500, d: 0.05, vol: 0.32, delay: [0.05, 0.3] }))])
|
|
16235
|
+
];
|
|
16236
|
+
var DESTRUCTION_SOUND_BANK = { id: "destruction_three", sounds: [...SCENE_SOUNDS, ...DRAG_SOUNDS, ...LEAF_SOUNDS, ...IMPACT_SOUNDS, EXPLOSION_BOOM, ...BLAST_SOUNDS, ...FIRE_SOUNDS], loops: [BURN_LOOP, FIRE_LOOP] };
|
|
14354
16237
|
|
|
14355
16238
|
// src/audio/sound.ts
|
|
16239
|
+
var IMPACT_SOUND_KEYS = {
|
|
16240
|
+
impact_metal_ballistic: { sound: "metal_bullet" },
|
|
16241
|
+
impact_metal_blunt: { sound: "metal_blunt" },
|
|
16242
|
+
impact_metal_slash: { sound: "metal_blade" },
|
|
16243
|
+
impact_aluminium_ballistic: { sound: "metal_thin" },
|
|
16244
|
+
impact_aluminium_blunt: { sound: "metal_thin" },
|
|
16245
|
+
impact_copper_ballistic: { sound: "metal_thin" },
|
|
16246
|
+
impact_copper_blunt: { sound: "metal_thin" },
|
|
16247
|
+
impact_wood_ballistic: { sound: "wood_hit" },
|
|
16248
|
+
impact_wood_blunt: { sound: "wood_blunt" },
|
|
16249
|
+
impact_wood_slash: { sound: "wood_blade" },
|
|
16250
|
+
impact_mdf_ballistic: { sound: "mdf_puff" },
|
|
16251
|
+
impact_mdf_blunt: { sound: "mdf_puff", volumeScale: 0.8 },
|
|
16252
|
+
impact_glass_ballistic: { sound: "glass_bullet" },
|
|
16253
|
+
impact_glass_blunt: { sound: "glass_blunt" },
|
|
16254
|
+
impact_glass_slash: { sound: "glass_blade" },
|
|
16255
|
+
impact_ceramic: { sound: "ceramic_hit" },
|
|
16256
|
+
impact_ceramic_ballistic: { sound: "ceramic_bullet" },
|
|
16257
|
+
impact_masonry_ballistic: { sound: "masonry_bullet" },
|
|
16258
|
+
impact_masonry_blunt: { sound: "masonry_blunt" },
|
|
16259
|
+
impact_masonry_slash: { sound: "masonry_blade" },
|
|
16260
|
+
impact_plaster: { sound: "plaster_puff" },
|
|
16261
|
+
impact_soil: { sound: "soil_blunt" },
|
|
16262
|
+
impact_soil_ballistic: { sound: "soil_bullet" },
|
|
16263
|
+
impact_sand: { sound: "sand_hit" },
|
|
16264
|
+
impact_gravel: { sound: "gravel_hit" },
|
|
16265
|
+
impact_plastic: { sound: "plastic_hit" },
|
|
16266
|
+
impact_plastic_ballistic: { sound: "plastic_bullet" },
|
|
16267
|
+
impact_rubber: { sound: "rubber_hit" },
|
|
16268
|
+
impact_wax: { sound: "wax_hit" },
|
|
16269
|
+
impact_soft_ballistic: { sound: "soft_bullet" },
|
|
16270
|
+
impact_soft_slash: { sound: "fabric_rip" },
|
|
16271
|
+
impact_paper: { sound: "paper_hit" },
|
|
16272
|
+
impact_paper_slash: { sound: "paper_tear" },
|
|
16273
|
+
impact_plant: { sound: "leaf_rustle" },
|
|
16274
|
+
impact_plant_slash: { sound: "branch_cut" },
|
|
16275
|
+
impact_flesh_fruit: { sound: "fruit_splat" },
|
|
16276
|
+
ricochet_metal: { sound: "ricochet_metal" },
|
|
16277
|
+
ricochet_masonry: { sound: "ricochet_stone" },
|
|
16278
|
+
ricochet_soil: { sound: "ricochet_stone" },
|
|
16279
|
+
ricochet_ceramic: { sound: "ricochet_stone" },
|
|
16280
|
+
break_ceramic: { sound: "ceramic_break" },
|
|
16281
|
+
break_tempered_glass: { sound: "tempered_burst" },
|
|
16282
|
+
break_plastic: { sound: "plastic_crack" },
|
|
16283
|
+
break_mdf: { sound: "mdf_crumble" },
|
|
16284
|
+
break_soft: { sound: "fabric_rip" },
|
|
16285
|
+
break_paper: { sound: "paper_tear" },
|
|
16286
|
+
break_plant: { sound: "branch_cut" }
|
|
16287
|
+
};
|
|
14356
16288
|
var DESTRUCTION_SOUND_KEYS = {
|
|
14357
16289
|
art_scene: {
|
|
14358
16290
|
impact_metal: { sound: "clang" },
|
|
@@ -14360,6 +16292,12 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14360
16292
|
impact_glass: { sound: "ping" },
|
|
14361
16293
|
impact_masonry: { sound: "chip" },
|
|
14362
16294
|
impact_soft: { sound: "thud" },
|
|
16295
|
+
// a round strikes: the steel rings high, glass cracks, wood and masonry chip, a soft part takes a dull thunk
|
|
16296
|
+
shot_metal: { sound: "ping" },
|
|
16297
|
+
shot_wood: { sound: "chip" },
|
|
16298
|
+
shot_glass: { sound: "crack" },
|
|
16299
|
+
shot_masonry: { sound: "chip" },
|
|
16300
|
+
shot_soft: { sound: "thunk" },
|
|
14363
16301
|
drag_wood: { sound: "drag_wood" },
|
|
14364
16302
|
drag_metal: { sound: "drag_metal" },
|
|
14365
16303
|
drag_glass: { sound: "drag_glass" },
|
|
@@ -14385,22 +16323,38 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14385
16323
|
hood_shut: { sound: "shut_metal" },
|
|
14386
16324
|
trunk_shut: { sound: "shut_metal" },
|
|
14387
16325
|
door_close: { sound: "shut_wood", volumeScale: 0.7 },
|
|
16326
|
+
// a vehicle's leaves (the vehicle bank of game-vehicles-three): a car door shuts with a padded thump and a latch, not like a fridge
|
|
16327
|
+
vehicle_door_open: { sound: "car_door_open", fallback: "latch" },
|
|
16328
|
+
vehicle_door_shut: { sound: "car_door_close", fallback: "shut_metal" },
|
|
16329
|
+
vehicle_trunk_open: { sound: "car_door_open", fallback: "latch" },
|
|
16330
|
+
vehicle_trunk_shut: { sound: "car_trunk_close", fallback: "shut_metal" },
|
|
16331
|
+
vehicle_hood_open: { sound: "car_door_open", fallback: "latch" },
|
|
16332
|
+
vehicle_hood_shut: { sound: "car_hood_close", fallback: "shut_metal" },
|
|
14388
16333
|
drawer_open: { sound: "slide" },
|
|
14389
16334
|
drawer_close: { sound: "slide" },
|
|
14390
16335
|
lid_open: { sound: "latch" },
|
|
14391
16336
|
lid_close: { sound: "shut_soft" },
|
|
14392
16337
|
flap_open: { sound: "latch" },
|
|
14393
16338
|
flap_close: { sound: "shut_soft" },
|
|
16339
|
+
// an item taken in the hand, by what it is made of (game-audio's KIT_SAMPLE_ALIASES puts recordings on these ids)
|
|
16340
|
+
grab_metal: { sound: "grab_hard" },
|
|
16341
|
+
grab_wood: { sound: "grab_hard" },
|
|
16342
|
+
grab_glass: { sound: "grab_hard", volumeScale: 0.7 },
|
|
16343
|
+
grab_masonry: { sound: "grab_hard" },
|
|
16344
|
+
grab_soft: { sound: "grab_soft" },
|
|
14394
16345
|
shatter_glass: { sound: "glass_shatter" },
|
|
14395
16346
|
crumble: { sound: "crumble" },
|
|
14396
16347
|
splinter: { sound: "crack" },
|
|
14397
16348
|
ricochet: { sound: "ping" },
|
|
14398
16349
|
unscrew: { sound: "chip" },
|
|
14399
16350
|
detach: { sound: "thunk" },
|
|
14400
|
-
ignite: { sound: "
|
|
14401
|
-
|
|
16351
|
+
ignite: { sound: "fire_ignite" },
|
|
16352
|
+
primed: { sound: "gas_leak" },
|
|
16353
|
+
extinguish: { sound: "fire_extinguish" },
|
|
14402
16354
|
melt_sizzle: { sound: "hiss", params: { seconds: 0.6 } },
|
|
14403
|
-
|
|
16355
|
+
// the blast family comes from the explosion's effect (`BLAST_KIND_OF`); this rule answers only an explosion the kit cannot place
|
|
16356
|
+
explode: { sound: "explosion_grenade", above: { volume: 0.9, sound: "explosion_big" } },
|
|
16357
|
+
...IMPACT_SOUND_KEYS
|
|
14404
16358
|
},
|
|
14405
16359
|
game: {
|
|
14406
16360
|
impact_metal: { sound: "sfx_impact" },
|
|
@@ -14414,14 +16368,26 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14414
16368
|
explode: { sound: "explosion_boom" }
|
|
14415
16369
|
}
|
|
14416
16370
|
};
|
|
14417
|
-
var DEFAULT_FIRE = { perFire: 0.
|
|
16371
|
+
var DEFAULT_FIRE = { perFire: 0.08, max: 0.45 };
|
|
16372
|
+
var DEFAULT_CRACKLES = { base: 2, perSqrtFire: 3, max: 14, volume: 0.5 };
|
|
16373
|
+
var DEFAULT_INDOOR_CONFINEMENT = 1.3;
|
|
14418
16374
|
function createDestructionSound(engine, options = {}) {
|
|
14419
16375
|
engine.register(DESTRUCTION_SOUND_BANK);
|
|
14420
16376
|
engine.register(GAME_SFX_BANK);
|
|
14421
|
-
|
|
16377
|
+
engine.register(FOLEY_BANK);
|
|
16378
|
+
const fire2 = { ...DEFAULT_FIRE, ...options.fire };
|
|
16379
|
+
const crackles = options.crackles === false ? null : { ...DEFAULT_CRACKLES, ...options.crackles };
|
|
16380
|
+
const fireLoop = options.fireLoop ?? "fire_loop";
|
|
16381
|
+
const kinds = { ...BLAST_KIND_OF, ...options.blastKinds };
|
|
16382
|
+
const farDistance = options.blastFarDistance ?? BLAST_FAR_DISTANCE;
|
|
16383
|
+
const indoorFrom = options.indoorConfinement ?? DEFAULT_INDOOR_CONFINEMENT;
|
|
14422
16384
|
const rules = { ...DESTRUCTION_SOUND_KEYS.art_scene, ...options.style === "game" ? DESTRUCTION_SOUND_KEYS.game : {}, ...options.keys };
|
|
16385
|
+
const blastByKind = options.style !== "game" && !(options.keys && "explode" in options.keys);
|
|
16386
|
+
const channel = engine.channel({ seed: options.seed ?? 1 });
|
|
14423
16387
|
let loop;
|
|
14424
16388
|
let burning = 0;
|
|
16389
|
+
const fires = [];
|
|
16390
|
+
let nextCrackle = 0;
|
|
14425
16391
|
const setFire = () => {
|
|
14426
16392
|
if (options.burnLoop === false) return;
|
|
14427
16393
|
if (burning <= 0) {
|
|
@@ -14429,35 +16395,120 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14429
16395
|
loop = void 0;
|
|
14430
16396
|
return;
|
|
14431
16397
|
}
|
|
14432
|
-
loop ?? (loop = engine.loop(
|
|
14433
|
-
loop.set({ volume: Math.min(
|
|
16398
|
+
loop ?? (loop = engine.loop(fireLoop));
|
|
16399
|
+
loop.set({ volume: Math.min(fire2.max, fire2.perFire * burning) });
|
|
16400
|
+
};
|
|
16401
|
+
const forget = (point) => {
|
|
16402
|
+
let best = -1;
|
|
16403
|
+
let bestD = Infinity;
|
|
16404
|
+
fires.forEach((f, i) => {
|
|
16405
|
+
const d = (f[0] - point[0]) ** 2 + (f[1] - point[1]) ** 2 + (f[2] - point[2]) ** 2;
|
|
16406
|
+
if (d < bestD) {
|
|
16407
|
+
bestD = d;
|
|
16408
|
+
best = i;
|
|
16409
|
+
}
|
|
16410
|
+
});
|
|
16411
|
+
if (best >= 0) fires.splice(best, 1);
|
|
16412
|
+
};
|
|
16413
|
+
const blastSound = (point, volume, detail) => {
|
|
16414
|
+
const named = detail?.effect === void 0 ? void 0 : kinds[detail.effect];
|
|
16415
|
+
const kind = named ?? ((detail?.radius ?? 0) >= BIG_BLAST_RADIUS || volume >= 0.9 ? "big" : "grenade");
|
|
16416
|
+
const sound = BLAST_SOUND_OF[kind];
|
|
16417
|
+
const distance = engine.plan(sound, { position: point, seed: 1 })?.distance ?? 0;
|
|
16418
|
+
return kind !== "molotov" && distance > farDistance ? "explosion_far" : sound;
|
|
14434
16419
|
};
|
|
14435
16420
|
return {
|
|
14436
|
-
onSound(key, point, volume) {
|
|
16421
|
+
onSound(key, point, volume, detail) {
|
|
14437
16422
|
if (key === "burn_loop") {
|
|
14438
16423
|
burning += 1;
|
|
16424
|
+
fires.push(point);
|
|
14439
16425
|
setFire();
|
|
14440
16426
|
return;
|
|
14441
16427
|
}
|
|
14442
16428
|
if (key === "burn_end") {
|
|
14443
16429
|
if (burning > 0) burning -= 1;
|
|
16430
|
+
forget(point);
|
|
14444
16431
|
setFire();
|
|
14445
16432
|
return;
|
|
14446
16433
|
}
|
|
14447
|
-
|
|
14448
|
-
|
|
14449
|
-
|
|
14450
|
-
|
|
16434
|
+
if (key === "explode" && blastByKind) {
|
|
16435
|
+
const sound2 = blastSound(point, volume, detail);
|
|
16436
|
+
engine.play(sound2, { position: point, volume });
|
|
16437
|
+
if (sound2 !== "explosion_far" && sound2 !== "molotov_break_ignite" && (detail?.confinement ?? 1) >= indoorFrom) engine.play("explosion_indoor_tail", { position: point, volume });
|
|
16438
|
+
return;
|
|
16439
|
+
}
|
|
16440
|
+
let rule;
|
|
16441
|
+
for (const k of detail?.keys ?? []) {
|
|
16442
|
+
if (k in rules) {
|
|
16443
|
+
rule = rules[k];
|
|
16444
|
+
break;
|
|
16445
|
+
}
|
|
16446
|
+
}
|
|
16447
|
+
if (rule === void 0) rule = key in rules ? rules[key] : rules[key.replace(/^shot_/, "impact_")];
|
|
16448
|
+
if (rule === null) return;
|
|
16449
|
+
if (!rule) {
|
|
16450
|
+
if (engine.registry.sound(key)) engine.play(key, { position: point, volume });
|
|
16451
|
+
return;
|
|
16452
|
+
}
|
|
16453
|
+
const wanted = rule.above && volume >= rule.above.volume ? rule.above.sound : rule.sound;
|
|
16454
|
+
const sound = rule.fallback && !engine.registry.sound(wanted) ? rule.fallback : wanted;
|
|
16455
|
+
const params = detail?.seconds === void 0 ? rule.params : { ...rule.params, seconds: detail.seconds };
|
|
16456
|
+
engine.play(sound, { position: point, volume: volume * (rule.volumeScale ?? 1), ...params ? { params } : {} });
|
|
16457
|
+
},
|
|
16458
|
+
update(dt) {
|
|
16459
|
+
if (!crackles || fires.length === 0 || options.burnLoop === false) return;
|
|
16460
|
+
nextCrackle -= dt;
|
|
16461
|
+
for (let i = 0; i < 4 && nextCrackle <= 0; i++) {
|
|
16462
|
+
const rate = Math.min(crackles.max, crackles.base + crackles.perSqrtFire * Math.sqrt(fires.length));
|
|
16463
|
+
nextCrackle += (0.4 + channel.random() * 1.2) / rate;
|
|
16464
|
+
const at3 = fires[Math.floor(channel.random() * fires.length)];
|
|
16465
|
+
engine.play(channel.random() < 0.18 ? "fire_pop" : "fire_crackle", { position: at3, volume: crackles.volume });
|
|
16466
|
+
}
|
|
16467
|
+
if (nextCrackle < 0) nextCrackle = 0;
|
|
16468
|
+
},
|
|
16469
|
+
blastSound,
|
|
16470
|
+
rule(key, detail) {
|
|
16471
|
+
for (const k of detail?.keys ?? []) if (k in rules) return rules[k] ?? void 0;
|
|
16472
|
+
return rules[key] ?? void 0;
|
|
14451
16473
|
},
|
|
14452
|
-
rule: (key) => rules[key] ?? void 0,
|
|
14453
16474
|
burning: () => burning,
|
|
14454
16475
|
dispose() {
|
|
14455
16476
|
loop?.stop();
|
|
14456
16477
|
loop = void 0;
|
|
14457
16478
|
burning = 0;
|
|
16479
|
+
fires.length = 0;
|
|
14458
16480
|
}
|
|
14459
16481
|
};
|
|
14460
16482
|
}
|
|
16483
|
+
|
|
16484
|
+
// src/audio/samples.ts
|
|
16485
|
+
var blast2 = (gain) => ({ bus: "world", gain, falloff: BLAST_FALLOFF, cutoffK: 6e-3, priority: 3 });
|
|
16486
|
+
var fire = (gain, group) => ({ bus: "world", gain, falloff: FIRE_FALLOFF, cutoffK: 0.01, ...group ? { group } : {} });
|
|
16487
|
+
var BLAST_FIRE_SAMPLES = {
|
|
16488
|
+
explosion_grenade: blast2(1),
|
|
16489
|
+
explosion_big: blast2(1),
|
|
16490
|
+
explosion_car: blast2(1),
|
|
16491
|
+
explosion_flashbang: blast2(1),
|
|
16492
|
+
molotov_break_ignite: blast2(0.8),
|
|
16493
|
+
// the far takes are already muffled: no extra distance lowpass
|
|
16494
|
+
explosion_far: { bus: "world", gain: 1, falloff: BLAST_FAR_FALLOFF, cutoffK: 0, priority: 2 },
|
|
16495
|
+
fire_ignite: fire(0.8, "fire_ignite"),
|
|
16496
|
+
fire_crackle: fire(0.8, "fire_crackle"),
|
|
16497
|
+
fire_pop: fire(0.6, "fire_crackle"),
|
|
16498
|
+
fire_extinguish: fire(0.7),
|
|
16499
|
+
gas_leak: fire(0.5),
|
|
16500
|
+
// the roar: recorded fires chained with a crossfade (the level comes from `createDestructionSound`, by how much burns)
|
|
16501
|
+
fire_loop: { bus: "world", gain: 4, loop: { crossfade: 1 } }
|
|
16502
|
+
};
|
|
16503
|
+
function blastFireSampleBank(manifest, options = {}) {
|
|
16504
|
+
const sounds = options.sounds ?? BLAST_FIRE_SAMPLES;
|
|
16505
|
+
const bank = sampleBankFromManifest(manifest, { id: options.id ?? "destruction-samples", ...options.baseUrl === void 0 ? {} : { baseUrl: options.baseUrl }, sounds });
|
|
16506
|
+
const samples = (bank.samples ?? []).map((sample) => {
|
|
16507
|
+
const loop = sounds[sample.id]?.loop;
|
|
16508
|
+
return loop === void 0 ? sample : { ...sample, loop };
|
|
16509
|
+
});
|
|
16510
|
+
return { ...bank, samples };
|
|
16511
|
+
}
|
|
14461
16512
|
return __toCommonJS(index_exports);
|
|
14462
16513
|
})();
|
|
14463
16514
|
//# sourceMappingURL=index.global.js.map
|