@adatechnology/game-animals-three 0.3.8 → 0.4.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.
@@ -45,6 +45,618 @@ var GameAnimalsThree = (() => {
45
45
  updateSplit: () => updateSplit
46
46
  });
47
47
 
48
+ // ../game-contracts/dist/index.js
49
+ var DEFAULT_COST_ALLOCATION_BYTES = 1024 * 1024;
50
+ var DEFERRED_QUEUE_ID = {
51
+ DESTRUCTION_EVENTS: "destruction.events",
52
+ BREACH_PAINT: "floorplan.breach-paint",
53
+ AUDIO_VOICES: "audio.voices",
54
+ VEHICLE_CREASE: "vehicles.crease"
55
+ };
56
+ var DEFERRED_PRIORITY = {
57
+ [DEFERRED_QUEUE_ID.DESTRUCTION_EVENTS]: { priority: 300, phase: "present" },
58
+ [DEFERRED_QUEUE_ID.BREACH_PAINT]: { priority: 250, phase: "present" },
59
+ [DEFERRED_QUEUE_ID.AUDIO_VOICES]: { priority: 200, phase: "idle", reserveMs: 1 },
60
+ [DEFERRED_QUEUE_ID.VEHICLE_CREASE]: { priority: 100, phase: "idle" }
61
+ };
62
+ var KIT_ORIGIN_KEY = "kitOrigin";
63
+ function markKitOrigin(object2, origin) {
64
+ const node = object2;
65
+ node.userData = node.userData ?? {};
66
+ node.userData[KIT_ORIGIN_KEY] = { ...origin };
67
+ return object2;
68
+ }
69
+ var SURFACE_IDS = ["concrete", "asphalt", "wood", "tile", "dirt", "grass", "gravel", "sand", "metal", "water", "glass", "carpet"];
70
+ var GAIT_IDS = ["walk", "run"];
71
+ var LANDING_IDS = ["light", "heavy"];
72
+ var BODY_SOUND_IDS = [
73
+ "jump",
74
+ "cloth_rustle",
75
+ "body_fall",
76
+ "fall_damage",
77
+ "player_hurt",
78
+ "player_breath_run",
79
+ "player_death",
80
+ "bite",
81
+ "grab",
82
+ "punch_swing",
83
+ "punch_hit_body",
84
+ "punch_hit_face",
85
+ "punch_hit_object",
86
+ "kick_swing",
87
+ "kick_hit",
88
+ "kick_hit_object",
89
+ "shove_two_hands",
90
+ "knife_swing",
91
+ "knife_stab",
92
+ "knife_slash",
93
+ "knife_hit_object",
94
+ "blunt_swing",
95
+ "blunt_hit",
96
+ "blunt_hit_object",
97
+ "axe_swing",
98
+ "axe_chop",
99
+ "axe_hit_object"
100
+ ];
101
+ var footstepSoundId = (surface, gait) => `footstep_${surface}_${gait}`;
102
+ var FOOTSTEP_SOUND_IDS = SURFACE_IDS.flatMap((surface) => GAIT_IDS.map((gait) => footstepSoundId(surface, gait)));
103
+ var LAND_SOUND_IDS = SURFACE_IDS.flatMap((surface) => LANDING_IDS.map((landing2) => `land_${surface}_${landing2}`));
104
+ var BODY_FOLEY_IDS = [...FOOTSTEP_SOUND_IDS, ...LAND_SOUND_IDS, ...BODY_SOUND_IDS];
105
+ var SWING_LEAD_SECONDS = { punch: 0.07, kick: 0.09, shove: 0, blade: 0.05, blunt: 0.1, chop: 0.11 };
106
+
107
+ // ../game-audio/dist/chunk-DZITVFMJ.js
108
+ var DISTANCE_CURVES = {
109
+ /** The game's `sfx`: `vol / (1 + d² × 0.004)`, cutoff `/ (1 + d × 0.01)`, silent below 0.01 (jogo L62091–62092, L62101). */
110
+ game_sfx: { model: "inverse_square", k: 4e-3, cutoffK: 0.01, minGain: 0.01 },
111
+ /** The game's `grito`: `1 / (1 + d² × 0.006)`, silent below 0.02 (jogo L62129–62130). */
112
+ game_scream: { model: "inverse_square", k: 6e-3, minGain: 0.02 },
113
+ /** The game's `voz`: `1 / (1 + d² × 0.01)`, silent below 0.02 (jogo L62146–62147). */
114
+ game_voice: { model: "inverse_square", k: 0.01, minGain: 0.02 },
115
+ /** The game's `boom`: `1.4 / (1 + d² × 0.002)`, never silent (jogo L68280). */
116
+ game_boom: { model: "inverse_square", k: 2e-3, scale: 1.4 },
117
+ /** The art scene's `outAt`: `vol / (1 + d² × 0.008)` (cena L1320). */
118
+ art_scene: { model: "inverse_square", k: 8e-3 },
119
+ /** The game's animal calls: `max(0, 1 − d / 70) × 0.25`, silent at or below 0.005 (jogo L66627–66628). */
120
+ animals: { model: "linear", range: 70, scale: 0.25, minGain: 5e-3 }
121
+ };
122
+ var game_sfx_default = {
123
+ sources: ["jogo:L62089-62107"],
124
+ 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`.",
125
+ classes: [
126
+ { id: "pistol", team: "pistola", bus: "weapons", cutoff: 1800, gain: 0.9, seconds: 0.18 },
127
+ { id: "shotgun", team: "escopeta", bus: "weapons", cutoff: 900, gain: 1.2, seconds: 0.35 },
128
+ { id: "rifle", team: "fuzil", bus: "weapons", cutoff: 1400, gain: 1.1, seconds: 0.28 },
129
+ { id: "long_rifle", team: "rifle", bus: "weapons", cutoff: 1100, gain: 1.3, seconds: 0.45 },
130
+ { id: "impact", team: "impacto", bus: "world", cutoff: 2500, gain: 0.25, seconds: 0.05 },
131
+ { id: "flesh", team: "carne", bus: "world", cutoff: 600, gain: 0.35, seconds: 0.08 },
132
+ { id: "step", team: "passo", bus: "world", cutoff: 400, gain: 0.08, seconds: 0.05 },
133
+ { id: "glass", team: "vidro", bus: "world", cutoff: 5e3, gain: 0.5, seconds: 0.3 },
134
+ { id: "wood", team: "madeira", bus: "world", cutoff: 700, gain: 0.5, seconds: 0.2 },
135
+ { id: "reload", team: "recarga", bus: "weapons", cutoff: 3e3, gain: 0.15, seconds: 0.06 },
136
+ { id: "empty", team: "vazio", bus: "weapons", cutoff: 4e3, gain: 0.12, seconds: 0.03 },
137
+ { id: "explosion", team: "explosao", bus: "world", cutoff: 200, gain: 1.6, seconds: 1.4 }
138
+ ],
139
+ fallback: { id: "unknown", team: "(any other name)", bus: "world", cutoff: 1e3, gain: 0.5, seconds: 0.1 }
140
+ };
141
+ var GAME_SFX = game_sfx_default;
142
+ var GAME_SFX_CLASSES = GAME_SFX.classes;
143
+ var GAME_SFX_FALLBACK = GAME_SFX.fallback;
144
+ function noiseClassRecipe(row, id = `sfx_${row.id}`) {
145
+ return {
146
+ id,
147
+ bus: row.bus,
148
+ order: "burst",
149
+ duration: row.seconds,
150
+ tail: 0.05,
151
+ layers: [
152
+ {
153
+ source: { kind: "noise", offset: "random" },
154
+ filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: row.cutoff } }] },
155
+ envelope: [
156
+ [0, row.gain, "set"],
157
+ ["1d", 1e-3, "exp", "fixed"]
158
+ ]
159
+ }
160
+ ],
161
+ falloff: DISTANCE_CURVES.game_sfx,
162
+ sources: [...GAME_SFX.sources.map((source) => `${source} ${row.team}`)]
163
+ };
164
+ }
165
+ var GAME_SFX_BANK = { id: "game_sfx", sounds: [...GAME_SFX_CLASSES, GAME_SFX_FALLBACK].map((row) => noiseClassRecipe(row)) };
166
+ var mix_profiles_default = [
167
+ {
168
+ id: "game",
169
+ master: 0.5,
170
+ noise: { seconds: 1.5, create: "lazy" },
171
+ 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"]
172
+ },
173
+ {
174
+ id: "art_scene",
175
+ master: 0.85,
176
+ compressor: { threshold: -12, ratio: 5 },
177
+ reverb: { seconds: 2.2, decay: 3.4, wet: 0.4 },
178
+ noise: { seconds: 2, create: "eager" },
179
+ sources: [
180
+ "cena:L1308 compressor -12 dB, ratio 5",
181
+ "cena:L1309 master 0.85",
182
+ "cena:L1310 convolver, impulse 2.2 s of (1 - i/len)^3.4",
183
+ "cena:L1311 wet 0.4",
184
+ "cena:L1312 noise 2 s made with the chain"
185
+ ]
186
+ }
187
+ ];
188
+ var MIX_PROFILES = Object.fromEntries(
189
+ mix_profiles_default.map((profile) => [profile.id, profile])
190
+ );
191
+ var BENCH_BANK = {
192
+ id: "audio_bench",
193
+ sounds: [
194
+ {
195
+ id: "bench_ping",
196
+ bus: "ui",
197
+ pan: true,
198
+ duration: 0.25,
199
+ layers: [
200
+ {
201
+ gain: 0.5,
202
+ wet: 0.3,
203
+ source: { kind: "osc", wave: "sine", frequency: { from: 880 } },
204
+ envelope: [
205
+ [0, 1e-4, "set", "fixed"],
206
+ [2e-3, 1, "exp"],
207
+ ["1d", 1e-4, "exp", "fixed"]
208
+ ]
209
+ }
210
+ ],
211
+ falloff: DISTANCE_CURVES.art_scene,
212
+ sources: ["sdk: bench probe, a 880 Hz ping through the art scene output stage"]
213
+ },
214
+ {
215
+ id: "bench_hiss",
216
+ bus: "ui",
217
+ pan: true,
218
+ duration: [0.3, 0.9],
219
+ layers: [
220
+ {
221
+ gain: 0.4,
222
+ source: { kind: "noise", offset: "random", offsetScale: 1.5, loop: true },
223
+ filters: { mode: "serial", bank: [{ type: "highpass", frequency: { set: 3e3 }, q: 0.7 }] },
224
+ envelope: [
225
+ [0, 1e-4, "set", "fixed"],
226
+ [2e-3, 1, "exp"],
227
+ ["1d", 1e-4, "exp", "fixed"]
228
+ ]
229
+ }
230
+ ],
231
+ falloff: DISTANCE_CURVES.art_scene,
232
+ sources: ["sdk: bench probe, a highpass hiss of random length"]
233
+ }
234
+ ]
235
+ };
236
+
237
+ // ../game-audio/dist/index.js
238
+ var ATTACK = 2e-3;
239
+ function lengthOf(d, attack) {
240
+ if (typeof d === "number") return attack + d;
241
+ if (Array.isArray(d)) return [attack + d[0], attack + d[1]];
242
+ return { ...d, add: (d.add ?? 0) + attack };
243
+ }
244
+ var envelope = (attack) => [
245
+ [0, 1e-4, "set", "fixed"],
246
+ [attack, 1, "exp"],
247
+ ["1d", 1e-4, "exp", "fixed"]
248
+ ];
249
+ function sceneNoise(o) {
250
+ const a = o.a ?? ATTACK;
251
+ const over = typeof o.d === "number" ? o.d : 0;
252
+ return {
253
+ filters: {
254
+ mode: "serial",
255
+ bank: [{ type: o.type ?? "lowpass", frequency: o.f2 === void 0 ? { set: o.f } : { set: o.f, to: o.f2, curve: "exp", over }, q: o.q ?? 0.7 }]
256
+ },
257
+ duration: lengthOf(o.d, a),
258
+ gain: o.vol,
259
+ ...o.delay === void 0 ? {} : { at: o.delay },
260
+ ...o.wet === void 0 ? {} : { wet: o.wet },
261
+ source: { kind: "noise", offset: "random", offsetScale: 1.5, loop: true },
262
+ envelope: envelope(a)
263
+ };
264
+ }
265
+ function sceneTone(o) {
266
+ return {
267
+ source: { kind: "osc", wave: o.wave ?? "sine", frequency: { from: o.f, to: o.f2 ?? "from", curve: "exp", over: "duration" } },
268
+ duration: lengthOf(o.d, ATTACK),
269
+ gain: o.vol,
270
+ ...o.delay === void 0 ? {} : { at: o.delay },
271
+ ...o.wet === void 0 ? {} : { wet: o.wet },
272
+ envelope: envelope(ATTACK)
273
+ };
274
+ }
275
+ function sceneVoice(o) {
276
+ return {
277
+ duration: lengthOf(o.d, 0),
278
+ gain: o.vol,
279
+ wet: 0.45,
280
+ source: {
281
+ kind: "formant_voice",
282
+ wave: o.wave ?? "sawtooth",
283
+ frequency: { from: o.f0, to: o.f1, curve: "linear", over: "duration" },
284
+ vibrato: { rate: [4, 10], depth: 0.08 },
285
+ formants: [
286
+ { frequency: o.fA, q: 4 },
287
+ { frequency: o.fB, q: 6 }
288
+ ],
289
+ breath: { frequency: o.fB, q: 1.1, gain: o.noise },
290
+ mix: 3.2
291
+ },
292
+ envelope: [
293
+ [0, 1e-4, "set", "fixed"],
294
+ [0.1, 1, "exp"],
295
+ ["0.6d", 1, "set"],
296
+ ["1d", 1e-4, "exp", "fixed"]
297
+ ]
298
+ };
299
+ }
300
+ var TAKES = [
301
+ { f: 1, t: 1, v: 1 },
302
+ { f: 1.08, t: 0.9, v: 0.92 },
303
+ { f: 0.93, t: 1.12, v: 1.05 },
304
+ { f: 1.03, t: 1.05, v: 0.88 }
305
+ ];
306
+ var FOOTSTEP_FALLOFF = { model: "inverse_square", k: 0.03, cutoffK: 0.03, minGain: 4e-3 };
307
+ var BODY_FALLOFF = { model: "inverse_square", k: 0.012, cutoffK: 0.015, minGain: 4e-3 };
308
+ var SURFACES = {
309
+ 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 } },
310
+ 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 } },
311
+ 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 }] },
312
+ 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 }] },
313
+ 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 } },
314
+ 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 } },
315
+ 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 } },
316
+ 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 } },
317
+ 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 }] },
318
+ 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 } },
319
+ 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 }] },
320
+ carpet: { heel: { f: 70, d: 0.05, vol: 0.32 }, roll: { type: "lowpass", f: 420, q: 0.7, d: 0.07, vol: 0.3 } }
321
+ };
322
+ var GAITS = {
323
+ walk: { vol: 1, bright: 1, toe: 0.075, short: 1 },
324
+ run: { vol: 1.35, bright: 1.25, toe: 0.035, short: 0.75 }
325
+ };
326
+ var grainLayers = (g, take, start, scale) => Array.from(
327
+ { length: g.count },
328
+ (_, 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 })
329
+ );
330
+ var ringLayers = (rings, take, scale, at = 0) => rings.map((r) => sceneTone({ f: r.f * take.f, d: r.d * take.t, vol: r.vol * scale * take.v, delay: at }));
331
+ function footLayers(surface, take, gait, weight = 1) {
332
+ const s = SURFACES[surface];
333
+ const toe = gait.toe * take.t;
334
+ return [
335
+ 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 }),
336
+ sceneNoise({ type: "lowpass", f: 500 * gait.bright, d: 0.02 * take.t, vol: 0.18 * gait.vol * take.v * weight ** 0.5 }),
337
+ 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 }),
338
+ ...s.grains ? grainLayers(s.grains, take, toe * 0.5, gait.vol * weight ** 0.5) : [],
339
+ ...s.ring ? ringLayers(s.ring, take, gait.vol * weight ** 0.5) : [],
340
+ ...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 })] : []
341
+ ];
342
+ }
343
+ var quieter = (level, layers) => layers.map((layer) => {
344
+ const gain = layer.gain ?? 1;
345
+ if (typeof gain === "number") return { ...layer, gain: gain * level };
346
+ if (Array.isArray(gain)) return { ...layer, gain: [gain[0] * level, gain[1] * level] };
347
+ return layer;
348
+ });
349
+ var recipe = (id, layers, falloff, bus = "world") => ({ id, bus, pan: true, layers, falloff, sources: ["sdk"] });
350
+ var takeId = (id, index) => `${id}_${String(index + 1).padStart(2, "0")}`;
351
+ function takes(id, layersOf, falloff, rate = [0.96, 1.04], bus) {
352
+ const sounds = TAKES.map((take, index) => recipe(takeId(id, index), layersOf(take, index), falloff, bus));
353
+ return { sounds, set: { id, variants: sounds.map((sound) => sound.id), rate } };
354
+ }
355
+ var footstep = (surface, gait) => takes(footstepSoundId(surface, gait), (take) => footLayers(surface, take, GAITS[gait]), FOOTSTEP_FALLOFF);
356
+ var LANDINGS = { light: { weight: 1.4, cloth: 0.12 }, heavy: { weight: 2.4, cloth: 0.22 } };
357
+ var LANDING_LEVEL = 0.65;
358
+ var landing = (surface, landing2) => takes(
359
+ `land_${surface}_${landing2}`,
360
+ (take) => quieter(LANDING_LEVEL, [
361
+ // both feet, a hair apart, then the knees taking the weight (a low body thump) and the clothes settling
362
+ ...footLayers(surface, take, GAITS.run, LANDINGS[landing2].weight),
363
+ ...footLayers(surface, { ...take, v: take.v * 0.6 }, GAITS.run, LANDINGS[landing2].weight * 0.6).map((layer) => ({ ...layer, at: (typeof layer.at === "number" ? layer.at : 0) + 0.018 * take.t })),
364
+ 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 }),
365
+ sceneNoise({ type: "bandpass", f: [1800, 2600], q: 0.8, d: 0.18, vol: LANDINGS[landing2].cloth * take.v, delay: 0.04 })
366
+ ]),
367
+ FOOTSTEP_FALLOFF
368
+ );
369
+ var whoosh = (blow, from, to, d, vol, q = 1.6) => (take) => [
370
+ // 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
371
+ sceneNoise({ type: "bandpass", f: from * take.f, f2: to * take.f, q, a: SWING_LEAD_SECONDS[blow], d: d * take.t, vol: vol * take.v }),
372
+ sceneNoise({ type: "lowpass", f: 600 * take.f, a: SWING_LEAD_SECONDS[blow], d: d * take.t * 0.8, vol: vol * 0.35 * take.v })
373
+ ];
374
+ var fleshHit = (o) => (take) => [
375
+ sceneNoise({ type: "bandpass", f: o.slapF * take.f, q: 1.2, d: 0.03 * take.t, vol: o.slap * take.v }),
376
+ sceneNoise({ type: "highpass", f: 3500 * take.f, d: 0.012, vol: o.slap * 0.4 * take.v }),
377
+ sceneTone({ f: o.thudF * take.f, f2: o.thudF * 0.5, d: o.thudD * take.t, vol: o.thud * take.v }),
378
+ sceneNoise({ type: "lowpass", f: 600 * take.f, d: o.thudD * 1.2 * take.t, vol: o.thud * 0.5 * take.v }),
379
+ ...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 })] : [],
380
+ ...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 })] : []
381
+ ];
382
+ var knock = (o) => (take) => [
383
+ sceneNoise({ type: "bandpass", f: o.clickF * take.f, q: 1.5, d: 0.018, vol: o.click * take.v }),
384
+ ...o.tones.map(([f, d, vol]) => sceneTone({ f: f * take.f, d: d * take.t, vol: vol * take.v }))
385
+ ];
386
+ var voice = (o) => (take) => [
387
+ 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 })
388
+ ];
389
+ var BODY = {
390
+ jump: (take) => [
391
+ sceneNoise({ type: "bandpass", f: 900 * take.f, f2: 1700 * take.f, q: 1, a: 0.03, d: 0.12, vol: 0.18 * take.v }),
392
+ sceneNoise({ type: "bandpass", f: 2200 * take.f, q: 0.8, d: 0.04, vol: 0.12 * take.v }),
393
+ sceneTone({ f: 85 * take.f, f2: 60, d: 0.04, vol: 0.2 * take.v })
394
+ ],
395
+ cloth_rustle: (take) => [
396
+ { ...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 }] }
397
+ ],
398
+ body_fall: (take) => [
399
+ sceneTone({ f: 58 * take.f, f2: 35, d: 0.28 * take.t, vol: 0.9 * take.v }),
400
+ sceneNoise({ type: "lowpass", f: 480 * take.f, d: 0.3 * take.t, vol: 0.55 * take.v }),
401
+ sceneNoise({ type: "bandpass", f: 1300 * take.f, q: 1, d: 0.05, vol: 0.3 * take.v }),
402
+ // the limbs a moment later, and the clothes
403
+ sceneTone({ f: 75 * take.f, f2: 45, d: 0.12, vol: 0.4 * take.v, delay: 0.13 * take.t }),
404
+ sceneNoise({ type: "bandpass", f: 2200, q: 0.8, d: 0.2, vol: 0.12 * take.v, delay: 0.05 })
405
+ ],
406
+ fall_damage: (take) => [
407
+ ...BODY.body_fall(take),
408
+ sceneNoise({ type: "highpass", f: 2800, d: 0.02, vol: 0.35 * take.v, delay: 0.01 }),
409
+ ...voice({ f0: 170, f1: 110, d: 0.32, fA: 650, fB: 1150, noise: 0.25, vol: 0.32 })(take).map((layer) => ({ ...layer, at: 0.08 }))
410
+ ],
411
+ player_hurt: voice({ f0: 150, f1: 105, d: 0.26, fA: 620, fB: 1100, noise: 0.3, vol: 0.35 }),
412
+ player_breath_run: (take) => [
413
+ sceneNoise({ type: "bandpass", f: 1200 * take.f, q: 1, a: 0.08, d: 0.22 * take.t, vol: 0.25 * take.v }),
414
+ 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 })
415
+ ],
416
+ player_death: voice({ f0: 135, f1: 68, d: 1, fA: 560, fB: 980, noise: 0.35, vol: 0.4 }),
417
+ bite: (take) => [
418
+ sceneNoise({ type: "highpass", f: 2600, d: 0.015, vol: 0.4 * take.v }),
419
+ 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 }),
420
+ 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 }),
421
+ sceneTone({ f: 120 * take.f, f2: 70, d: 0.06, vol: 0.3 * take.v })
422
+ ],
423
+ grab: (take) => [
424
+ sceneNoise({ type: "bandpass", f: 1900 * take.f, q: 0.9, a: 4e-3, d: 0.13 * take.t, vol: 0.32 * take.v }),
425
+ sceneTone({ f: 95 * take.f, f2: 60, d: 0.07, vol: 0.3 * take.v }),
426
+ sceneNoise({ type: "lowpass", f: 700, d: 0.08, vol: 0.2 * take.v })
427
+ ],
428
+ punch_swing: whoosh("punch", 450, 1500, 0.16, 0.9),
429
+ punch_hit_body: fleshHit({ slap: 0.55, slapF: 1500, thud: 0.6, thudF: 115, thudD: 0.09 }),
430
+ punch_hit_face: fleshHit({ slap: 0.75, slapF: 2300, thud: 0.45, thudF: 150, thudD: 0.07, crack: 0.25 }),
431
+ punch_hit_object: knock({ clickF: 1800, click: 0.3, tones: [[210, 0.04, 0.25]] }),
432
+ kick_swing: whoosh("kick", 280, 950, 0.22, 1, 1.3),
433
+ kick_hit: fleshHit({ slap: 0.45, slapF: 1100, thud: 0.85, thudF: 82, thudD: 0.14 }),
434
+ kick_hit_object: knock({ clickF: 1e3, click: 0.3, tones: [[92, 0.08, 0.5]] }),
435
+ shove_two_hands: (take) => [
436
+ sceneNoise({ type: "bandpass", f: 950 * take.f, q: 0.9, a: 0.01, d: 0.13 * take.t, vol: 0.38 * take.v }),
437
+ sceneTone({ f: 100 * take.f, f2: 60, d: 0.1 * take.t, vol: 0.45 * take.v }),
438
+ sceneNoise({ type: "bandpass", f: 2300, q: 0.8, d: 0.16, vol: 0.12 * take.v, delay: 0.03 })
439
+ ],
440
+ knife_swing: whoosh("blade", 1900, 4600, 0.12, 1.1, 2.2),
441
+ knife_stab: fleshHit({ slap: 0.2, slapF: 1200, thud: 0.4, thudF: 160, thudD: 0.06, wet: 0.5 }),
442
+ knife_slash: (take) => [
443
+ sceneNoise({ type: "highpass", f: 3200 * take.f, f2: 1800, d: 0.13 * take.t, vol: 0.35 * take.v }),
444
+ 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 })
445
+ ],
446
+ knife_hit_object: knock({ clickF: 3200, click: 0.25, tones: [[2800, 0.08, 0.07], [4150, 0.06, 0.05]] }),
447
+ blunt_swing: whoosh("blunt", 220, 760, 0.28, 1.1, 1.5),
448
+ blunt_hit: fleshHit({ slap: 0.35, slapF: 900, thud: 1, thudF: 70, thudD: 0.18, crack: 0.3 }),
449
+ blunt_hit_object: knock({ clickF: 1400, click: 0.35, tones: [[300, 0.12, 0.25], [760, 0.08, 0.12]] }),
450
+ axe_swing: whoosh("chop", 190, 620, 0.3, 1.2, 1.4),
451
+ axe_chop: fleshHit({ slap: 0.3, slapF: 1400, thud: 0.75, thudF: 140, thudD: 0.1, crack: 0.35, wet: 0.45 }),
452
+ 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]] })
453
+ };
454
+ var BODY_BUS = { player_hurt: "voices", player_death: "voices", player_breath_run: "voices", fall_damage: "voices" };
455
+ var built = [
456
+ ...SURFACE_IDS.flatMap((surface) => GAIT_IDS.map((gait) => footstep(surface, gait))),
457
+ ...SURFACE_IDS.flatMap((surface) => LANDING_IDS.map((l) => landing(surface, l))),
458
+ ...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]))
459
+ ];
460
+ var BODY_FOLEY_BANK = {
461
+ id: "body_foley",
462
+ sounds: built.flatMap((entry) => entry.sounds),
463
+ sets: built.map((entry) => entry.set)
464
+ };
465
+ var BODY_FOLEY_SET_IDS = [...FOOTSTEP_SOUND_IDS, ...LAND_SOUND_IDS, ...BODY_SOUND_IDS];
466
+
467
+ // src/audio/recipes.ts
468
+ var call = (o) => ({
469
+ id: o.id,
470
+ bus: "world",
471
+ order: "calls",
472
+ duration: o.seconds,
473
+ tail: 0.02,
474
+ // the page makes its gain and filter before it looks at the level
475
+ keepSilent: true,
476
+ layers: [
477
+ {
478
+ repeat: { count: o.count, spacing: o.spacing },
479
+ source: { kind: "osc", wave: o.wave, frequency: { from: o.frequency, to: o.frequency * o.drop, curve: "exp", over: "duration" } },
480
+ filters: { mode: "serial", bank: [{ type: "bandpass", frequency: { set: o.frequency * 1.2 }, q: 3 }] },
481
+ envelope: [
482
+ [0, 1e-4, "set", "fixed"],
483
+ [0.015, 1, "exp"],
484
+ ["1d", 1e-4, "exp", "fixed"]
485
+ ]
486
+ }
487
+ ],
488
+ falloff: DISTANCE_CURVES.animals,
489
+ sources: [`jogo:L66625-66639 bichoSom('${o.team}')`]
490
+ });
491
+ var DOG_BARK = call({ id: "dog_bark", team: "latido", count: 2, spacing: 0.22, seconds: 0.12, wave: "square", frequency: 520, drop: 0.6 });
492
+ var LAPWING_CALL = call({ id: "lapwing_call", team: "queroquero", count: 3, spacing: 0.14, seconds: 0.09, wave: "square", frequency: 2400, drop: 0.6 });
493
+ var HEN_CLUCK = call({ id: "hen_cluck", team: "cacarejo", count: 4, spacing: 0.14, seconds: 0.09, wave: "square", frequency: 900, drop: 0.6 });
494
+ var COW_MOO = call({ id: "cow_moo", team: "mugido", count: 1, spacing: 0.14, seconds: 1.2, wave: "sawtooth", frequency: 140, drop: 0.8 });
495
+ var ANIMAL_SOUNDS = [DOG_BARK, LAPWING_CALL, HEN_CLUCK, COW_MOO];
496
+ var ANIMAL_SOUND_BANK = { id: "animals_three", sounds: ANIMAL_SOUNDS };
497
+
498
+ // src/audio/calls.json
499
+ var calls_default = {
500
+ sources: [
501
+ "jogo:L66677 galinha: cacarejo, 6 s between calls, noise 30 m",
502
+ "jogo:L66680 quero-quero: queroquero when the player is near (alarme), 8 s, noise 40 m",
503
+ "jogo:L66681 vaca: mugido, 30 s, noise 80 m",
504
+ "jogo:L66683 cachorro: latido when a zombie is within 12 m, 3 s, noise 60 m"
505
+ ],
506
+ species: {
507
+ chicken: { sound: "hen_cluck", cooldown: 6, noiseMetres: 30 },
508
+ southern_lapwing: { sound: "lapwing_call", cooldown: 8, noiseMetres: 40 },
509
+ cow: { sound: "cow_moo", cooldown: 30, noiseMetres: 80 },
510
+ dog: { sound: "dog_bark", cooldown: 3, noiseMetres: 60 }
511
+ }
512
+ };
513
+
514
+ // src/audio/sound.ts
515
+ var DATA = calls_default;
516
+ var ANIMAL_CALLS = DATA.species;
517
+ var ANIMAL_CALL_SOURCES = DATA.sources;
518
+ function createAnimalSound(sink, options = {}) {
519
+ sink.register?.(ANIMAL_SOUND_BANK);
520
+ const calls = { ...ANIMAL_CALLS, ...options.calls };
521
+ const waiting = /* @__PURE__ */ new Map();
522
+ const callOf = (speciesId) => calls[speciesId] ?? void 0;
523
+ return {
524
+ callOf,
525
+ call(speciesId, position, o = {}) {
526
+ const found = callOf(speciesId);
527
+ if (!found) return false;
528
+ const key = o.key ?? speciesId;
529
+ if ((waiting.get(key) ?? 0) > 0) return false;
530
+ waiting.set(key, found.cooldown);
531
+ sink.play({ sound: found.sound, position, ...o.volume === void 0 ? {} : { volume: o.volume } });
532
+ return true;
533
+ },
534
+ update(dt) {
535
+ for (const [key, seconds] of waiting) {
536
+ if (seconds - dt <= 0) waiting.delete(key);
537
+ else waiting.set(key, seconds - dt);
538
+ }
539
+ },
540
+ dispose: () => waiting.clear()
541
+ };
542
+ }
543
+
544
+ // src/context.ts
545
+ var createStream = (seed) => {
546
+ let state = seed;
547
+ return () => {
548
+ state |= 0;
549
+ state = state + 1831565813 | 0;
550
+ let t = Math.imul(state ^ state >>> 15, 1 | state);
551
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
552
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
553
+ };
554
+ };
555
+ var addMesh = (THREE, parent, geometry, material, x, y, z, rx, ry, rz) => {
556
+ const mesh2 = new THREE.Mesh(geometry, material);
557
+ mesh2.position.set(x || 0, y || 0, z || 0);
558
+ if (rx || ry || rz) mesh2.rotation.set(rx || 0, ry || 0, rz || 0);
559
+ mesh2.castShadow = true;
560
+ mesh2.receiveShadow = true;
561
+ parent.add(mesh2);
562
+ return mesh2;
563
+ };
564
+ var createRigTools = (THREE, rig) => {
565
+ const tools = {
566
+ material(color2, extras) {
567
+ const material = new THREE.MeshStandardMaterial(Object.assign({ color: color2, roughness: 0.8, metalness: 0, flatShading: false }, extras || {}));
568
+ material.userData.base = material.color.clone();
569
+ rig.materials.push(material);
570
+ return material;
571
+ },
572
+ ball(parent, radius, material, x, y, z, sx, sy, sz) {
573
+ const mesh2 = addMesh(THREE, parent, new THREE.SphereGeometry(radius, 16, 12), material, x, y, z);
574
+ if (sx) mesh2.scale.set(sx, sy, sz);
575
+ return mesh2;
576
+ },
577
+ limb: (parent, radius1, radius2, length, material) => addMesh(THREE, parent, new THREE.CylinderGeometry(radius2, radius1, length, 14, 1).translate(0, -length / 2, 0), material),
578
+ joint(parent, radius, material, x, y, z) {
579
+ const joint = tools.ball(parent, radius, material, x, y, z);
580
+ joint.userData.joint = 1;
581
+ rig.joints.push(joint);
582
+ return joint;
583
+ },
584
+ pivot(parent, x, y, z, offset) {
585
+ const group = new THREE.Group();
586
+ group.position.set(x, y, z);
587
+ parent.add(group);
588
+ rig.pivots.push({ group, rest: group.position.clone(), offset: offset || new THREE.Vector3() });
589
+ return group;
590
+ },
591
+ eyes(head, radius, x, y, z) {
592
+ const eyeMaterial = tools.material(1118481, { roughness: 0.25 });
593
+ rig.eyeMaterial = eyeMaterial;
594
+ for (const side of [-1, 1]) rig.eyes.push(tools.ball(head, radius, eyeMaterial, side * x, y, z));
595
+ }
596
+ };
597
+ return tools;
598
+ };
599
+ var createRig = (THREE, species, options) => {
600
+ const rig = {
601
+ species,
602
+ root: new THREE.Group(),
603
+ trunk: new THREE.Group(),
604
+ materials: [],
605
+ joints: [],
606
+ pivots: [],
607
+ legs: [],
608
+ tail: [],
609
+ ears: [],
610
+ infectionParts: [],
611
+ eyes: [],
612
+ wings: [],
613
+ segments: [],
614
+ phase: options.phase,
615
+ size: 1,
616
+ segmentLength: 0,
617
+ segmentCount: 0,
618
+ home: new THREE.Vector3(),
619
+ infected: false,
620
+ dead: false,
621
+ neckAngle: 0,
622
+ headAngle: 0,
623
+ triangles: 0
624
+ };
625
+ rig.root.add(rig.trunk);
626
+ return rig;
627
+ };
628
+
629
+ // src/extras.ts
630
+ var cow = ({ THREE, tools }, k) => {
631
+ tools.ball(k.bodyGroup, 0.2, k.main, 0, k.Rb * 1.1, k.L * 0.33, 0.95, 1.1, 1.1);
632
+ tools.ball(k.neck, 0.13, k.main, 0, 0.12, 0.08, 0.55, 1.4, 1.05);
633
+ for (const side of [-1, 1]) addMesh(THREE, k.head, new THREE.ConeGeometry(0.025, 0.13, 8), tools.material(13616808), side * 0.12, k.hd.r * 1.05, k.hd.r * 0.05, 0, 0, side * -0.9);
634
+ };
635
+ var horse = ({ THREE, rig, tools }, k) => {
636
+ for (let index = 0; index < 9; index++) addMesh(THREE, k.neck, new THREE.BoxGeometry(0.035, 0.16, 0.08), k.dark, 0, 0.06 + index * 0.065, -0.12 + index * 4e-3, -0.25, 0, 0);
637
+ addMesh(THREE, k.head, new THREE.BoxGeometry(0.03, 0.12, 0.06), k.dark, 0, k.hd.r * 1.05, k.hd.r * 0.3, 0.6, 0, 0);
638
+ const last = rig.tail[rig.tail.length - 1];
639
+ tools.ball(last, 0.08, k.dark, 0, -0.42, 0, 0.7, 4.5, 0.7);
640
+ };
641
+ var wildBoar = ({ THREE, tools }, k) => {
642
+ for (let index = 0; index < 12; index++) addMesh(THREE, k.bodyGroup, new THREE.BoxGeometry(0.03, 0.1, 0.06), k.dark, 0, k.Rb * 1.12 - index * 4e-3, k.L * 0.38 - index * 0.06, -0.2, 0, 0);
643
+ for (const side of [-1, 1])
644
+ addMesh(THREE, k.head, new THREE.ConeGeometry(0.012, 0.09, 6), tools.material(15261900), side * 0.07, k.hd.r * -0.25 + 0.03, k.hd.r * 0.25 + k.hd.r * k.hd.s[2] * 0.55 + 0.13, -0.6, 0, side * 0.5);
645
+ };
646
+ var chicken = ({ tools }, k) => {
647
+ const red = tools.material(12593182);
648
+ for (let index = 0; index < 4; index++) tools.ball(k.head, 0.014, red, 0, 0.045, -0.012 + index * 0.014, 0.5, 1, 0.8);
649
+ tools.ball(k.head, 0.012, red, 0, -0.04, 0.04, 0.5, 1.3, 0.7);
650
+ };
651
+ var southernLapwing = ({ THREE, tools }, k) => {
652
+ const black = tools.material(1447446);
653
+ tools.ball(k.bodyGroup, 0.05, black, 0, 0.03, 0.08, 1.1, 0.9, 0.6);
654
+ addMesh(THREE, k.head, new THREE.BoxGeometry(6e-3, 6e-3, 0.06).translate(0, 0, -0.03), black, 0, 0.025, -0.01, 0.5, 0, 0);
655
+ tools.ball(k.head, 0.026, black, 0, -5e-3, 0.02, 1.05, 0.7, 0.9);
656
+ };
657
+ var QUADRUPED_EXTRAS = { cow, horse, wild_boar: wildBoar };
658
+ var BIRD_EXTRAS = { chicken, southern_lapwing: southernLapwing };
659
+
48
660
  // ../game-animals/dist/index.js
49
661
  var gaits_default = {
50
662
  walk: [
@@ -92,16 +704,27 @@ var GameAnimalsThree = (() => {
92
704
  return outcome === "dies" ? "dead" : "alive";
93
705
  };
94
706
 
95
- // ../game-contracts/dist/index.js
96
- var KIT_ORIGIN_KEY = "kitOrigin";
97
- function markKitOrigin(object2, origin) {
98
- const node = object2;
99
- node.userData = node.userData ?? {};
100
- node.userData[KIT_ORIGIN_KEY] = { ...origin };
101
- return object2;
102
- }
707
+ // src/line-up.ts
708
+ var lineUpAnimals = (rigs, options = {}) => {
709
+ const start = options.start ?? -19;
710
+ const gap = options.gap ?? 0.85;
711
+ let cursor = start;
712
+ for (const rig of rigs) {
713
+ const length = lineUpLength(rig.species.body);
714
+ cursor += length / 2;
715
+ rig.root.position.set(cursor, 0, 0);
716
+ rig.home = rig.root.position.clone();
717
+ rig.root.rotation.y = rig.species.body.kind === "snake" ? options.snakeYaw ?? 2.2 : options.yaw ?? 1.1;
718
+ cursor += length / 2 + gap;
719
+ }
720
+ const offset = -(cursor - gap + start) / 2;
721
+ for (const rig of rigs) {
722
+ rig.root.position.x += offset;
723
+ rig.home.x += offset;
724
+ }
725
+ };
103
726
 
104
- // ../game-shapes/dist/chunk-R4RY23RH.js
727
+ // ../game-shapes/dist/chunk-IFXQIDSA.js
105
728
  function canonicalJson(value) {
106
729
  if (value === null) return "null";
107
730
  if (typeof value === "number") return Object.is(value, -0) ? "-0" : Number.isFinite(value) ? String(value) : JSON.stringify(String(value));
@@ -1396,10 +2019,10 @@ var GameAnimalsThree = (() => {
1396
2019
  return cached;
1397
2020
  }
1398
2021
  stats.misses++;
1399
- const built = make(node.kind, parameters);
1400
- templates.set(key, built);
2022
+ const built2 = make(node.kind, parameters);
2023
+ templates.set(key, built2);
1401
2024
  if (templates.size > templateLimit) templates.delete(templates.keys().next().value);
1402
- return built;
2025
+ return built2;
1403
2026
  }
1404
2027
  var num = (v) => typeof v === "number" && Number.isFinite(v) ? null : "expected a finite number";
1405
2028
  var bool = (v) => typeof v === "boolean" ? null : "expected a boolean";
@@ -1702,6 +2325,134 @@ var GameAnimalsThree = (() => {
1702
2325
  return releaseObject3D(list2, { keep });
1703
2326
  }
1704
2327
 
2328
+ // src/profile.ts
2329
+ var profileGeometry = (THREE, length, profile, segments) => {
2330
+ const points = profile.map((radius, index) => new THREE.Vector2(index / (profile.length - 1), radius));
2331
+ const curve = new THREE.SplineCurve(points);
2332
+ const outline = [];
2333
+ outline.push(new THREE.Vector2(1e-3, -length / 2 - 0));
2334
+ for (let step = 0; step <= 30; step++) {
2335
+ const point = curve.getPoint(step / 30);
2336
+ outline.push(new THREE.Vector2(Math.max(1e-3, point.y), -length / 2 + point.x * length));
2337
+ }
2338
+ outline.push(new THREE.Vector2(1e-3, length / 2));
2339
+ return new (shapeGeometries(THREE)).LatheGeometry(outline, segments || 22).rotateX(Math.PI / 2);
2340
+ };
2341
+
2342
+ // src/textures.ts
2343
+ var canvasTexture = (THREE, document2, width, height, draw) => {
2344
+ const canvas = document2.createElement("canvas");
2345
+ canvas.width = width;
2346
+ canvas.height = height;
2347
+ draw(canvas.getContext("2d"), width, height);
2348
+ const texture = new THREE.CanvasTexture(canvas);
2349
+ texture.colorSpace = THREE.SRGBColorSpace;
2350
+ return texture;
2351
+ };
2352
+ var spotsTexture = (THREE, document2, base, spot) => canvasTexture(THREE, document2, 256, 256, (painter, width, height) => {
2353
+ painter.fillStyle = base;
2354
+ painter.fillRect(0, 0, width, height);
2355
+ const next = createStream(11);
2356
+ for (let index = 0; index < 70; index++) {
2357
+ const x = next() * width;
2358
+ const y = next() * height;
2359
+ const radius = 5 + next() * 7;
2360
+ painter.strokeStyle = spot;
2361
+ painter.lineWidth = 3;
2362
+ painter.beginPath();
2363
+ painter.arc(x, y, radius, 0, 7);
2364
+ painter.stroke();
2365
+ painter.fillStyle = "rgba(120,70,20,.45)";
2366
+ painter.beginPath();
2367
+ painter.arc(x, y, radius * 0.6, 0, 7);
2368
+ painter.fill();
2369
+ }
2370
+ for (let index = 0; index < 60; index++) {
2371
+ painter.fillStyle = spot;
2372
+ painter.beginPath();
2373
+ painter.arc(next() * width, next() * height, 1.5 + next() * 2.5, 0, 7);
2374
+ painter.fill();
2375
+ }
2376
+ });
2377
+ var stripesTexture = (THREE, document2, base, stripe) => canvasTexture(THREE, document2, 128, 128, (painter, width) => {
2378
+ painter.fillStyle = base;
2379
+ painter.fillRect(0, 0, width, 128);
2380
+ painter.fillStyle = stripe;
2381
+ for (let index = 0; index < 9; index++) painter.fillRect(0, index * 14 + index % 2 * 3, width, 4);
2382
+ });
2383
+ var createCoatTexture = (THREE, document2, marker) => marker.kind === "stripes" ? stripesTexture(THREE, document2, marker.base, marker.stripe) : spotsTexture(THREE, document2, marker.base, marker.spot);
2384
+
2385
+ // src/visual-state.ts
2386
+ var INFECTED_TINT = 8227430;
2387
+ var POOL_COLOR = 3803142;
2388
+ var applyVisualState = (THREE, rig, state) => {
2389
+ rig.infected = state === "infected";
2390
+ rig.dead = state === "dead";
2391
+ const tint = new THREE.Color(INFECTED_TINT);
2392
+ rig.materials.forEach((material) => {
2393
+ if (material === rig.eyeMaterial || material === rig.woundMaterial) return;
2394
+ material.color.copy(material.userData.base);
2395
+ if (rig.infected) material.color.lerp(tint, 0.45).multiplyScalar(0.72);
2396
+ });
2397
+ const eye = rig.eyeMaterial;
2398
+ if (eye) {
2399
+ if (rig.infected) {
2400
+ eye.color.set(14473392);
2401
+ eye.emissive = new THREE.Color(9078368);
2402
+ eye.emissiveIntensity = 0.8;
2403
+ } else {
2404
+ eye.color.copy(eye.userData.base);
2405
+ eye.emissive = new THREE.Color(0);
2406
+ }
2407
+ }
2408
+ rig.infectionParts.forEach((part) => part.visible = rig.infected);
2409
+ const firstEar = rig.ears[0];
2410
+ if (firstEar) firstEar.visible = !rig.infected;
2411
+ if (!rig.dead) {
2412
+ rig.root.rotation.z = 0;
2413
+ rig.root.position.y = 0;
2414
+ }
2415
+ if (rig.pool) {
2416
+ rig.pool.visible = rig.dead;
2417
+ return;
2418
+ }
2419
+ if (!rig.dead) return;
2420
+ const pool = new THREE.Mesh(new THREE.CircleGeometry(1, 22).rotateX(-Math.PI / 2), new THREE.MeshStandardMaterial({ color: POOL_COLOR, roughness: 0.25 }));
2421
+ pool.position.set(rig.home.x, 5e-3, rig.home.z + 0.1);
2422
+ pool.receiveShadow = true;
2423
+ pool.scale.setScalar(rig.size * 0.45);
2424
+ rig.root.parent?.add(pool);
2425
+ rig.pool = pool;
2426
+ };
2427
+
2428
+ // src/views.ts
2429
+ var setJointView = (rig, on, jointMaterial) => {
2430
+ rig.materials.forEach((material) => {
2431
+ if (material === rig.eyeMaterial) return;
2432
+ material.transparent = on;
2433
+ material.opacity = on ? 0.28 : 1;
2434
+ material.depthWrite = !on;
2435
+ material.needsUpdate = true;
2436
+ });
2437
+ rig.joints.forEach((joint) => {
2438
+ var _a;
2439
+ if (on) (_a = joint.userData).restMaterial ?? (_a.restMaterial = joint.material);
2440
+ joint.material = on ? jointMaterial : joint.userData.restMaterial;
2441
+ joint.renderOrder = on ? 2 : 0;
2442
+ });
2443
+ };
2444
+ var setSplit = (rig, on) => {
2445
+ rig.pivots.forEach((pivot) => {
2446
+ pivot.group.userData.target = on ? pivot.rest.clone().add(pivot.offset) : pivot.rest.clone();
2447
+ });
2448
+ };
2449
+ var updateSplit = (rig, delta) => {
2450
+ rig.pivots.forEach((pivot) => {
2451
+ const target = pivot.group.userData.target;
2452
+ if (target) pivot.group.position.lerp(target, Math.min(1, delta * 5));
2453
+ });
2454
+ };
2455
+
1705
2456
  // ../game-props3d/dist/index.js
1706
2457
  var T = (id, interior, w, d, h, y, solid, group) => ({ id, interior, w, d, h, y, solid, group });
1707
2458
  var TYPE_DEFS = [
@@ -2069,105 +2820,20 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
2069
2820
  for (let index = 0; index < rig.segmentCount; index++) {
2070
2821
  const group = rig.segments[index].group;
2071
2822
  group.position.copy(at(index));
2072
- const direction = index === 0 ? at(0).clone().sub(at(1)) : at(index - 1).clone().sub(at(index));
2073
- group.rotation.set(0, Math.atan2(direction.x, direction.z), 0);
2074
- group.rotateX(-Math.atan2(direction.y, Math.hypot(direction.x, direction.z)));
2075
- }
2076
- const head = rig.head;
2077
- const headDirection = at(0).clone().sub(at(1)).normalize();
2078
- head.position.copy(at(0)).addScaledVector(headDirection, 0.02);
2079
- head.rotation.set(0, Math.atan2(headDirection.x, headDirection.z), 0);
2080
- head.rotateX(-Math.atan2(headDirection.y, Math.hypot(headDirection.x, headDirection.z)) * 0.5);
2081
- const jaw = rig.jaw;
2082
- jaw.rotation.x = clip === "run" ? Math.max(0, Math.sin(t * 2.2)) ** 6 * 0.9 : 0;
2083
- const tongue = rig.tongue;
2084
- tongue.visible = Math.sin(t * 5) > 0.3;
2085
- tongue.scale.z = 1 + Math.sin(t * 20) * 0.3;
2086
- };
2087
-
2088
- // src/context.ts
2089
- var createStream = (seed) => {
2090
- let state = seed;
2091
- return () => {
2092
- state |= 0;
2093
- state = state + 1831565813 | 0;
2094
- let t = Math.imul(state ^ state >>> 15, 1 | state);
2095
- t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
2096
- return ((t ^ t >>> 14) >>> 0) / 4294967296;
2097
- };
2098
- };
2099
- var addMesh = (THREE, parent, geometry, material, x, y, z, rx, ry, rz) => {
2100
- const mesh2 = new THREE.Mesh(geometry, material);
2101
- mesh2.position.set(x || 0, y || 0, z || 0);
2102
- if (rx || ry || rz) mesh2.rotation.set(rx || 0, ry || 0, rz || 0);
2103
- mesh2.castShadow = true;
2104
- mesh2.receiveShadow = true;
2105
- parent.add(mesh2);
2106
- return mesh2;
2107
- };
2108
- var createRigTools = (THREE, rig) => {
2109
- const tools = {
2110
- material(color2, extras) {
2111
- const material = new THREE.MeshStandardMaterial(Object.assign({ color: color2, roughness: 0.8, metalness: 0, flatShading: false }, extras || {}));
2112
- material.userData.base = material.color.clone();
2113
- rig.materials.push(material);
2114
- return material;
2115
- },
2116
- ball(parent, radius, material, x, y, z, sx, sy, sz) {
2117
- const mesh2 = addMesh(THREE, parent, new THREE.SphereGeometry(radius, 16, 12), material, x, y, z);
2118
- if (sx) mesh2.scale.set(sx, sy, sz);
2119
- return mesh2;
2120
- },
2121
- limb: (parent, radius1, radius2, length, material) => addMesh(THREE, parent, new THREE.CylinderGeometry(radius2, radius1, length, 14, 1).translate(0, -length / 2, 0), material),
2122
- joint(parent, radius, material, x, y, z) {
2123
- const joint = tools.ball(parent, radius, material, x, y, z);
2124
- joint.userData.joint = 1;
2125
- rig.joints.push(joint);
2126
- return joint;
2127
- },
2128
- pivot(parent, x, y, z, offset) {
2129
- const group = new THREE.Group();
2130
- group.position.set(x, y, z);
2131
- parent.add(group);
2132
- rig.pivots.push({ group, rest: group.position.clone(), offset: offset || new THREE.Vector3() });
2133
- return group;
2134
- },
2135
- eyes(head, radius, x, y, z) {
2136
- const eyeMaterial = tools.material(1118481, { roughness: 0.25 });
2137
- rig.eyeMaterial = eyeMaterial;
2138
- for (const side of [-1, 1]) rig.eyes.push(tools.ball(head, radius, eyeMaterial, side * x, y, z));
2139
- }
2140
- };
2141
- return tools;
2142
- };
2143
- var createRig = (THREE, species, options) => {
2144
- const rig = {
2145
- species,
2146
- root: new THREE.Group(),
2147
- trunk: new THREE.Group(),
2148
- materials: [],
2149
- joints: [],
2150
- pivots: [],
2151
- legs: [],
2152
- tail: [],
2153
- ears: [],
2154
- infectionParts: [],
2155
- eyes: [],
2156
- wings: [],
2157
- segments: [],
2158
- phase: options.phase,
2159
- size: 1,
2160
- segmentLength: 0,
2161
- segmentCount: 0,
2162
- home: new THREE.Vector3(),
2163
- infected: false,
2164
- dead: false,
2165
- neckAngle: 0,
2166
- headAngle: 0,
2167
- triangles: 0
2168
- };
2169
- rig.root.add(rig.trunk);
2170
- return rig;
2823
+ const direction = index === 0 ? at(0).clone().sub(at(1)) : at(index - 1).clone().sub(at(index));
2824
+ group.rotation.set(0, Math.atan2(direction.x, direction.z), 0);
2825
+ group.rotateX(-Math.atan2(direction.y, Math.hypot(direction.x, direction.z)));
2826
+ }
2827
+ const head = rig.head;
2828
+ const headDirection = at(0).clone().sub(at(1)).normalize();
2829
+ head.position.copy(at(0)).addScaledVector(headDirection, 0.02);
2830
+ head.rotation.set(0, Math.atan2(headDirection.x, headDirection.z), 0);
2831
+ head.rotateX(-Math.atan2(headDirection.y, Math.hypot(headDirection.x, headDirection.z)) * 0.5);
2832
+ const jaw = rig.jaw;
2833
+ jaw.rotation.x = clip === "run" ? Math.max(0, Math.sin(t * 2.2)) ** 6 * 0.9 : 0;
2834
+ const tongue = rig.tongue;
2835
+ tongue.visible = Math.sin(t * 5) > 0.3;
2836
+ tongue.scale.z = 1 + Math.sin(t * 20) * 0.3;
2171
2837
  };
2172
2838
 
2173
2839
  // src/bird.ts
@@ -2254,94 +2920,6 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
2254
2920
  return rig;
2255
2921
  };
2256
2922
 
2257
- // src/extras.ts
2258
- var cow = ({ THREE, tools }, k) => {
2259
- tools.ball(k.bodyGroup, 0.2, k.main, 0, k.Rb * 1.1, k.L * 0.33, 0.95, 1.1, 1.1);
2260
- tools.ball(k.neck, 0.13, k.main, 0, 0.12, 0.08, 0.55, 1.4, 1.05);
2261
- for (const side of [-1, 1]) addMesh(THREE, k.head, new THREE.ConeGeometry(0.025, 0.13, 8), tools.material(13616808), side * 0.12, k.hd.r * 1.05, k.hd.r * 0.05, 0, 0, side * -0.9);
2262
- };
2263
- var horse = ({ THREE, rig, tools }, k) => {
2264
- for (let index = 0; index < 9; index++) addMesh(THREE, k.neck, new THREE.BoxGeometry(0.035, 0.16, 0.08), k.dark, 0, 0.06 + index * 0.065, -0.12 + index * 4e-3, -0.25, 0, 0);
2265
- addMesh(THREE, k.head, new THREE.BoxGeometry(0.03, 0.12, 0.06), k.dark, 0, k.hd.r * 1.05, k.hd.r * 0.3, 0.6, 0, 0);
2266
- const last = rig.tail[rig.tail.length - 1];
2267
- tools.ball(last, 0.08, k.dark, 0, -0.42, 0, 0.7, 4.5, 0.7);
2268
- };
2269
- var wildBoar = ({ THREE, tools }, k) => {
2270
- for (let index = 0; index < 12; index++) addMesh(THREE, k.bodyGroup, new THREE.BoxGeometry(0.03, 0.1, 0.06), k.dark, 0, k.Rb * 1.12 - index * 4e-3, k.L * 0.38 - index * 0.06, -0.2, 0, 0);
2271
- for (const side of [-1, 1])
2272
- addMesh(THREE, k.head, new THREE.ConeGeometry(0.012, 0.09, 6), tools.material(15261900), side * 0.07, k.hd.r * -0.25 + 0.03, k.hd.r * 0.25 + k.hd.r * k.hd.s[2] * 0.55 + 0.13, -0.6, 0, side * 0.5);
2273
- };
2274
- var chicken = ({ tools }, k) => {
2275
- const red = tools.material(12593182);
2276
- for (let index = 0; index < 4; index++) tools.ball(k.head, 0.014, red, 0, 0.045, -0.012 + index * 0.014, 0.5, 1, 0.8);
2277
- tools.ball(k.head, 0.012, red, 0, -0.04, 0.04, 0.5, 1.3, 0.7);
2278
- };
2279
- var southernLapwing = ({ THREE, tools }, k) => {
2280
- const black = tools.material(1447446);
2281
- tools.ball(k.bodyGroup, 0.05, black, 0, 0.03, 0.08, 1.1, 0.9, 0.6);
2282
- addMesh(THREE, k.head, new THREE.BoxGeometry(6e-3, 6e-3, 0.06).translate(0, 0, -0.03), black, 0, 0.025, -0.01, 0.5, 0, 0);
2283
- tools.ball(k.head, 0.026, black, 0, -5e-3, 0.02, 1.05, 0.7, 0.9);
2284
- };
2285
- var QUADRUPED_EXTRAS = { cow, horse, wild_boar: wildBoar };
2286
- var BIRD_EXTRAS = { chicken, southern_lapwing: southernLapwing };
2287
-
2288
- // src/textures.ts
2289
- var canvasTexture = (THREE, document2, width, height, draw) => {
2290
- const canvas = document2.createElement("canvas");
2291
- canvas.width = width;
2292
- canvas.height = height;
2293
- draw(canvas.getContext("2d"), width, height);
2294
- const texture = new THREE.CanvasTexture(canvas);
2295
- texture.colorSpace = THREE.SRGBColorSpace;
2296
- return texture;
2297
- };
2298
- var spotsTexture = (THREE, document2, base, spot) => canvasTexture(THREE, document2, 256, 256, (painter, width, height) => {
2299
- painter.fillStyle = base;
2300
- painter.fillRect(0, 0, width, height);
2301
- const next = createStream(11);
2302
- for (let index = 0; index < 70; index++) {
2303
- const x = next() * width;
2304
- const y = next() * height;
2305
- const radius = 5 + next() * 7;
2306
- painter.strokeStyle = spot;
2307
- painter.lineWidth = 3;
2308
- painter.beginPath();
2309
- painter.arc(x, y, radius, 0, 7);
2310
- painter.stroke();
2311
- painter.fillStyle = "rgba(120,70,20,.45)";
2312
- painter.beginPath();
2313
- painter.arc(x, y, radius * 0.6, 0, 7);
2314
- painter.fill();
2315
- }
2316
- for (let index = 0; index < 60; index++) {
2317
- painter.fillStyle = spot;
2318
- painter.beginPath();
2319
- painter.arc(next() * width, next() * height, 1.5 + next() * 2.5, 0, 7);
2320
- painter.fill();
2321
- }
2322
- });
2323
- var stripesTexture = (THREE, document2, base, stripe) => canvasTexture(THREE, document2, 128, 128, (painter, width) => {
2324
- painter.fillStyle = base;
2325
- painter.fillRect(0, 0, width, 128);
2326
- painter.fillStyle = stripe;
2327
- for (let index = 0; index < 9; index++) painter.fillRect(0, index * 14 + index % 2 * 3, width, 4);
2328
- });
2329
- var createCoatTexture = (THREE, document2, marker) => marker.kind === "stripes" ? stripesTexture(THREE, document2, marker.base, marker.stripe) : spotsTexture(THREE, document2, marker.base, marker.spot);
2330
-
2331
- // src/profile.ts
2332
- var profileGeometry = (THREE, length, profile, segments) => {
2333
- const points = profile.map((radius, index) => new THREE.Vector2(index / (profile.length - 1), radius));
2334
- const curve = new THREE.SplineCurve(points);
2335
- const outline = [];
2336
- outline.push(new THREE.Vector2(1e-3, -length / 2 - 0));
2337
- for (let step = 0; step <= 30; step++) {
2338
- const point = curve.getPoint(step / 30);
2339
- outline.push(new THREE.Vector2(Math.max(1e-3, point.y), -length / 2 + point.x * length));
2340
- }
2341
- outline.push(new THREE.Vector2(1e-3, length / 2));
2342
- return new (shapeGeometries(THREE)).LatheGeometry(outline, segments || 22).rotateX(Math.PI / 2);
2343
- };
2344
-
2345
2923
  // src/quadruped.ts
2346
2924
  var RIB_COUNT = 4;
2347
2925
  var LEG_DEFINITIONS = [
@@ -2535,305 +3113,263 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
2535
3113
  tools.eyes(head, 6e-3, 0.03, 0.04, 0.045);
2536
3114
  rig.tongue = addMesh(THREE, head, new THREE.BoxGeometry(4e-3, 2e-3, 0.05), tools.material(11542560), 0, 0.024, 0.1);
2537
3115
  return rig;
2538
- };
2539
-
2540
- // src/visual-state.ts
2541
- var INFECTED_TINT = 8227430;
2542
- var POOL_COLOR = 3803142;
2543
- var applyVisualState = (THREE, rig, state) => {
2544
- rig.infected = state === "infected";
2545
- rig.dead = state === "dead";
2546
- const tint = new THREE.Color(INFECTED_TINT);
2547
- rig.materials.forEach((material) => {
2548
- if (material === rig.eyeMaterial || material === rig.woundMaterial) return;
2549
- material.color.copy(material.userData.base);
2550
- if (rig.infected) material.color.lerp(tint, 0.45).multiplyScalar(0.72);
2551
- });
2552
- const eye = rig.eyeMaterial;
2553
- if (eye) {
2554
- if (rig.infected) {
2555
- eye.color.set(14473392);
2556
- eye.emissive = new THREE.Color(9078368);
2557
- eye.emissiveIntensity = 0.8;
2558
- } else {
2559
- eye.color.copy(eye.userData.base);
2560
- eye.emissive = new THREE.Color(0);
2561
- }
2562
- }
2563
- rig.infectionParts.forEach((part) => part.visible = rig.infected);
2564
- const firstEar = rig.ears[0];
2565
- if (firstEar) firstEar.visible = !rig.infected;
2566
- if (!rig.dead) {
2567
- rig.root.rotation.z = 0;
2568
- rig.root.position.y = 0;
2569
- }
2570
- if (rig.pool) {
2571
- rig.pool.visible = rig.dead;
2572
- return;
2573
- }
2574
- if (!rig.dead) return;
2575
- const pool = new THREE.Mesh(new THREE.CircleGeometry(1, 22).rotateX(-Math.PI / 2), new THREE.MeshStandardMaterial({ color: POOL_COLOR, roughness: 0.25 }));
2576
- pool.position.set(rig.home.x, 5e-3, rig.home.z + 0.1);
2577
- pool.receiveShadow = true;
2578
- pool.scale.setScalar(rig.size * 0.45);
2579
- rig.root.parent?.add(pool);
2580
- rig.pool = pool;
2581
- };
2582
-
2583
- // src/views.ts
2584
- var setJointView = (rig, on, jointMaterial) => {
2585
- rig.materials.forEach((material) => {
2586
- if (material === rig.eyeMaterial) return;
2587
- material.transparent = on;
2588
- material.opacity = on ? 0.28 : 1;
2589
- material.depthWrite = !on;
2590
- material.needsUpdate = true;
2591
- });
2592
- rig.joints.forEach((joint) => {
2593
- var _a;
2594
- if (on) (_a = joint.userData).restMaterial ?? (_a.restMaterial = joint.material);
2595
- joint.material = on ? jointMaterial : joint.userData.restMaterial;
2596
- joint.renderOrder = on ? 2 : 0;
2597
- });
2598
- };
2599
- var setSplit = (rig, on) => {
2600
- rig.pivots.forEach((pivot) => {
2601
- pivot.group.userData.target = on ? pivot.rest.clone().add(pivot.offset) : pivot.rest.clone();
2602
- });
2603
- };
2604
- var updateSplit = (rig, delta) => {
2605
- rig.pivots.forEach((pivot) => {
2606
- const target = pivot.group.userData.target;
2607
- if (target) pivot.group.position.lerp(target, Math.min(1, delta * 5));
2608
- });
2609
- };
2610
-
2611
- // ../game-audio/dist/index.js
2612
- var DISTANCE_CURVES = {
2613
- /** The game's `sfx`: `vol / (1 + d² × 0.004)`, cutoff `/ (1 + d × 0.01)`, silent below 0.01 (jogo L62091–62092, L62101). */
2614
- game_sfx: { model: "inverse_square", k: 4e-3, cutoffK: 0.01, minGain: 0.01 },
2615
- /** The game's `grito`: `1 / (1 + d² × 0.006)`, silent below 0.02 (jogo L62129–62130). */
2616
- game_scream: { model: "inverse_square", k: 6e-3, minGain: 0.02 },
2617
- /** The game's `voz`: `1 / (1 + d² × 0.01)`, silent below 0.02 (jogo L62146–62147). */
2618
- game_voice: { model: "inverse_square", k: 0.01, minGain: 0.02 },
2619
- /** The game's `boom`: `1.4 / (1 + d² × 0.002)`, never silent (jogo L68280). */
2620
- game_boom: { model: "inverse_square", k: 2e-3, scale: 1.4 },
2621
- /** The art scene's `outAt`: `vol / (1 + d² × 0.008)` (cena L1320). */
2622
- art_scene: { model: "inverse_square", k: 8e-3 },
2623
- /** The game's animal calls: `max(0, 1 − d / 70) × 0.25`, silent at or below 0.005 (jogo L66627–66628). */
2624
- animals: { model: "linear", range: 70, scale: 0.25, minGain: 5e-3 }
2625
- };
2626
- var mix_profiles_default = [
2627
- {
2628
- id: "game",
2629
- master: 0.5,
2630
- noise: { seconds: 1.5, create: "lazy" },
2631
- 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"]
2632
- },
2633
- {
2634
- id: "art_scene",
2635
- master: 0.85,
2636
- compressor: { threshold: -12, ratio: 5 },
2637
- reverb: { seconds: 2.2, decay: 3.4, wet: 0.4 },
2638
- noise: { seconds: 2, create: "eager" },
2639
- sources: [
2640
- "cena:L1308 compressor -12 dB, ratio 5",
2641
- "cena:L1309 master 0.85",
2642
- "cena:L1310 convolver, impulse 2.2 s of (1 - i/len)^3.4",
2643
- "cena:L1311 wet 0.4",
2644
- "cena:L1312 noise 2 s made with the chain"
2645
- ]
2646
- }
2647
- ];
2648
- var MIX_PROFILES = Object.fromEntries(
2649
- mix_profiles_default.map((profile) => [profile.id, profile])
2650
- );
2651
- var game_sfx_default = {
2652
- sources: ["jogo:L62089-62107"],
2653
- 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`.",
2654
- classes: [
2655
- { id: "pistol", team: "pistola", bus: "weapons", cutoff: 1800, gain: 0.9, seconds: 0.18 },
2656
- { id: "shotgun", team: "escopeta", bus: "weapons", cutoff: 900, gain: 1.2, seconds: 0.35 },
2657
- { id: "rifle", team: "fuzil", bus: "weapons", cutoff: 1400, gain: 1.1, seconds: 0.28 },
2658
- { id: "long_rifle", team: "rifle", bus: "weapons", cutoff: 1100, gain: 1.3, seconds: 0.45 },
2659
- { id: "impact", team: "impacto", bus: "world", cutoff: 2500, gain: 0.25, seconds: 0.05 },
2660
- { id: "flesh", team: "carne", bus: "world", cutoff: 600, gain: 0.35, seconds: 0.08 },
2661
- { id: "step", team: "passo", bus: "world", cutoff: 400, gain: 0.08, seconds: 0.05 },
2662
- { id: "glass", team: "vidro", bus: "world", cutoff: 5e3, gain: 0.5, seconds: 0.3 },
2663
- { id: "wood", team: "madeira", bus: "world", cutoff: 700, gain: 0.5, seconds: 0.2 },
2664
- { id: "reload", team: "recarga", bus: "weapons", cutoff: 3e3, gain: 0.15, seconds: 0.06 },
2665
- { id: "empty", team: "vazio", bus: "weapons", cutoff: 4e3, gain: 0.12, seconds: 0.03 },
2666
- { id: "explosion", team: "explosao", bus: "world", cutoff: 200, gain: 1.6, seconds: 1.4 }
2667
- ],
2668
- fallback: { id: "unknown", team: "(any other name)", bus: "world", cutoff: 1e3, gain: 0.5, seconds: 0.1 }
2669
- };
2670
- var GAME_SFX = game_sfx_default;
2671
- var GAME_SFX_CLASSES = GAME_SFX.classes;
2672
- var GAME_SFX_FALLBACK = GAME_SFX.fallback;
2673
- function noiseClassRecipe(row, id = `sfx_${row.id}`) {
2674
- return {
2675
- id,
2676
- bus: row.bus,
2677
- order: "burst",
2678
- duration: row.seconds,
2679
- tail: 0.05,
2680
- layers: [
2681
- {
2682
- source: { kind: "noise", offset: "random" },
2683
- filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: row.cutoff } }] },
2684
- envelope: [
2685
- [0, row.gain, "set"],
2686
- ["1d", 1e-3, "exp", "fixed"]
2687
- ]
2688
- }
2689
- ],
2690
- falloff: DISTANCE_CURVES.game_sfx,
2691
- sources: [...GAME_SFX.sources.map((source) => `${source} ${row.team}`)]
3116
+ };
3117
+
3118
+ // src/animals-three.ts
3119
+ var countTriangles = (root) => {
3120
+ let triangles = 0;
3121
+ root.traverse((object2) => {
3122
+ const mesh2 = object2;
3123
+ if (!mesh2.isMesh) return;
3124
+ const geometry = mesh2.geometry;
3125
+ triangles += geometry.index ? geometry.index.count / 3 : geometry.attributes.position.count / 3;
3126
+ });
3127
+ return Math.round(triangles);
3128
+ };
3129
+ var createAnimalsThree = (host, options = {}) => {
3130
+ const THREE = shapeGeometries(host);
3131
+ const random = options.random ?? createStream(options.seed ?? 7);
3132
+ const documentOf = () => {
3133
+ const found = options.document ?? globalThis.document;
3134
+ if (!found) throw new Error("game-animals-three: a species with a coat texture needs `options.document` (no global document found)");
3135
+ return found;
2692
3136
  };
2693
- }
2694
- var GAME_SFX_BANK = { id: "game_sfx", sounds: [...GAME_SFX_CLASSES, GAME_SFX_FALLBACK].map((row) => noiseClassRecipe(row)) };
2695
- var BENCH_BANK = {
2696
- id: "audio_bench",
2697
- sounds: [
2698
- {
2699
- id: "bench_ping",
2700
- bus: "ui",
2701
- pan: true,
2702
- duration: 0.25,
2703
- layers: [
2704
- {
2705
- gain: 0.5,
2706
- wet: 0.3,
2707
- source: { kind: "osc", wave: "sine", frequency: { from: 880 } },
2708
- envelope: [
2709
- [0, 1e-4, "set", "fixed"],
2710
- [2e-3, 1, "exp"],
2711
- ["1d", 1e-4, "exp", "fixed"]
2712
- ]
2713
- }
2714
- ],
2715
- falloff: DISTANCE_CURVES.art_scene,
2716
- sources: ["sdk: bench probe, a 880 Hz ping through the art scene output stage"]
3137
+ const jointMaterial = new THREE.MeshStandardMaterial({ color: 14722376, emissive: 14722376, emissiveIntensity: 0.8, roughness: 0.4 });
3138
+ const gaits = GAIT_PHASES;
3139
+ return {
3140
+ jointMaterial,
3141
+ build(species, buildOptions = {}) {
3142
+ const rig = createRig(THREE, species, { phase: buildOptions.phase ?? random() * 6 });
3143
+ if (species.body.kind === "snake") buildSnake(THREE, rig);
3144
+ else if (species.body.kind === "bird") buildBird(THREE, rig, { extra: BIRD_EXTRAS[species.id] });
3145
+ else buildQuadruped(THREE, rig, { document: species.body.texture ? documentOf() : void 0, extra: QUADRUPED_EXTRAS[species.id] });
3146
+ rig.triangles = countTriangles(rig.root);
3147
+ markKitOrigin(rig.root, { owner: "game-animals-three", kind: "animal", id: species.id });
3148
+ rig.root.traverse((object2) => {
3149
+ if (object2.isMesh) object2.userData.speciesId = species.id;
3150
+ });
3151
+ return rig;
2717
3152
  },
2718
- {
2719
- id: "bench_hiss",
2720
- bus: "ui",
2721
- pan: true,
2722
- duration: [0.3, 0.9],
2723
- layers: [
2724
- {
2725
- gain: 0.4,
2726
- source: { kind: "noise", offset: "random", offsetScale: 1.5, loop: true },
2727
- filters: { mode: "serial", bank: [{ type: "highpass", frequency: { set: 3e3 }, q: 0.7 }] },
2728
- envelope: [
2729
- [0, 1e-4, "set", "fixed"],
2730
- [2e-3, 1, "exp"],
2731
- ["1d", 1e-4, "exp", "fixed"]
2732
- ]
2733
- }
2734
- ],
2735
- falloff: DISTANCE_CURVES.art_scene,
2736
- sources: ["sdk: bench probe, a highpass hiss of random length"]
2737
- }
2738
- ]
3153
+ animate(rig, params) {
3154
+ const kind = rig.species.body.kind;
3155
+ if (kind === "snake") animateSnake(THREE, rig, params);
3156
+ else if (kind === "bird") animateBird(rig, params);
3157
+ else animateQuadruped(rig, params, gaits);
3158
+ },
3159
+ setVisualState: (rig, state) => applyVisualState(THREE, rig, state),
3160
+ setJointView: (rig, on) => setJointView(rig, on, jointMaterial),
3161
+ setSplit,
3162
+ updateSplit,
3163
+ createSkin: (rig) => createNodeSkin(THREE, rig.root, { nodes: collectReferencedNodes(rig, ["species"]) })
3164
+ };
2739
3165
  };
2740
3166
 
2741
- // src/audio/recipes.ts
2742
- var call = (o) => ({
2743
- id: o.id,
2744
- bus: "world",
2745
- order: "calls",
2746
- duration: o.seconds,
2747
- tail: 0.02,
2748
- // the page makes its gain and filter before it looks at the level
2749
- keepSilent: true,
2750
- layers: [
2751
- {
2752
- repeat: { count: o.count, spacing: o.spacing },
2753
- source: { kind: "osc", wave: o.wave, frequency: { from: o.frequency, to: o.frequency * o.drop, curve: "exp", over: "duration" } },
2754
- filters: { mode: "serial", bank: [{ type: "bandpass", frequency: { set: o.frequency * 1.2 }, q: 3 }] },
2755
- envelope: [
2756
- [0, 1e-4, "set", "fixed"],
2757
- [0.015, 1, "exp"],
2758
- ["1d", 1e-4, "exp", "fixed"]
2759
- ]
2760
- }
2761
- ],
2762
- falloff: DISTANCE_CURVES.animals,
2763
- sources: [`jogo:L66625-66639 bichoSom('${o.team}')`]
2764
- });
2765
- var DOG_BARK = call({ id: "dog_bark", team: "latido", count: 2, spacing: 0.22, seconds: 0.12, wave: "square", frequency: 520, drop: 0.6 });
2766
- var LAPWING_CALL = call({ id: "lapwing_call", team: "queroquero", count: 3, spacing: 0.14, seconds: 0.09, wave: "square", frequency: 2400, drop: 0.6 });
2767
- var HEN_CLUCK = call({ id: "hen_cluck", team: "cacarejo", count: 4, spacing: 0.14, seconds: 0.09, wave: "square", frequency: 900, drop: 0.6 });
2768
- var COW_MOO = call({ id: "cow_moo", team: "mugido", count: 1, spacing: 0.14, seconds: 1.2, wave: "sawtooth", frequency: 140, drop: 0.8 });
2769
- var ANIMAL_SOUNDS = [DOG_BARK, LAPWING_CALL, HEN_CLUCK, COW_MOO];
2770
- var ANIMAL_SOUND_BANK = { id: "animals_three", sounds: ANIMAL_SOUNDS };
2771
-
2772
- // src/audio/calls.json
2773
- var calls_default = {
2774
- sources: [
2775
- "jogo:L66677 galinha: cacarejo, 6 s between calls, noise 30 m",
2776
- "jogo:L66680 quero-quero: queroquero when the player is near (alarme), 8 s, noise 40 m",
2777
- "jogo:L66681 vaca: mugido, 30 s, noise 80 m",
2778
- "jogo:L66683 cachorro: latido when a zombie is within 12 m, 3 s, noise 60 m"
2779
- ],
2780
- species: {
2781
- chicken: { sound: "hen_cluck", cooldown: 6, noiseMetres: 30 },
2782
- southern_lapwing: { sound: "lapwing_call", cooldown: 8, noiseMetres: 40 },
2783
- cow: { sound: "cow_moo", cooldown: 30, noiseMetres: 80 },
2784
- dog: { sound: "dog_bark", cooldown: 3, noiseMetres: 60 }
3167
+ // ../game-render-three/dist/index.js
3168
+ var DEFAULT_ANGLE = Math.PI / 3;
3169
+ var EFFECT_STEP_SECONDS = 1 / 50;
3170
+ var BATCHED_LAYER = 30;
3171
+ var MATRIX_EPSILON = 1e-6;
3172
+ var ATTRIBUTES = ["position", "normal", "uv", "color"];
3173
+ var isShown = (object2, root) => {
3174
+ for (let node = object2; node; node = node.parent) {
3175
+ if (node === root) return true;
3176
+ if (!node.visible) return false;
2785
3177
  }
3178
+ return false;
2786
3179
  };
2787
-
2788
- // src/audio/sound.ts
2789
- var DATA = calls_default;
2790
- var ANIMAL_CALLS = DATA.species;
2791
- var ANIMAL_CALL_SOURCES = DATA.sources;
2792
- function createAnimalSound(sink, options = {}) {
2793
- sink.register?.(ANIMAL_SOUND_BANK);
2794
- const calls = { ...ANIMAL_CALLS, ...options.calls };
2795
- const waiting = /* @__PURE__ */ new Map();
2796
- const callOf = (speciesId) => calls[speciesId] ?? void 0;
3180
+ var isBatchable = (mesh2) => {
3181
+ const flags = mesh2;
3182
+ if (flags.isInstancedMesh === true || flags.isSkinnedMesh === true || Array.isArray(mesh2.material)) return false;
3183
+ const geometry = mesh2.geometry;
3184
+ return geometry.attributes.position !== void 0 && Object.keys(geometry.morphAttributes).length === 0;
3185
+ };
3186
+ var signatureOf = (mesh2) => {
3187
+ const { attributes } = mesh2.geometry;
3188
+ const present = ATTRIBUTES.filter((name) => attributes[name] !== void 0).map((name) => `${name}${attributes[name].itemSize}`);
3189
+ return `${mesh2.material.uuid}|${present.join(",")}|${mesh2.castShadow ? 1 : 0}${mesh2.receiveShadow ? 1 : 0}`;
3190
+ };
3191
+ var sameMatrix = (a, b) => a.elements.every((value, index) => Math.abs(value - b.elements[index]) < MATRIX_EPSILON);
3192
+ function createStaticBatcher(THREE, root, options = {}) {
3193
+ const include = options.include ?? (() => true);
3194
+ const follow = options.followMeshes === true;
3195
+ root.updateMatrixWorld(true);
3196
+ const inverse = new THREE.Matrix4().copy(root.matrixWorld).invert();
3197
+ const groups = /* @__PURE__ */ new Map();
3198
+ root.traverse((object2) => {
3199
+ const mesh2 = object2;
3200
+ if (!mesh2.isMesh || !isShown(mesh2, root) || !isBatchable(mesh2) || !include(mesh2)) return;
3201
+ const key = signatureOf(mesh2);
3202
+ groups.set(key, [...groups.get(key) ?? [], mesh2]);
3203
+ });
3204
+ const group = new THREE.Group();
3205
+ group.name = "static-batches";
3206
+ const sources = [];
3207
+ const pending = [...groups.values()].filter((meshes) => meshes.length >= 2);
3208
+ const mergeSome = (deadline) => {
3209
+ while (pending.length > 0) {
3210
+ const meshes = pending.shift();
3211
+ if (meshes.every((mesh2) => isShown(mesh2, root))) sources.push(...mergeGroup(THREE, { meshes, inverse: new THREE.Matrix4().copy(root.matrixWorld).invert(), group, follow }));
3212
+ if (performance.now() >= deadline) return;
3213
+ }
3214
+ };
3215
+ if (options.budgetMs === void 0) mergeSome(Number.POSITIVE_INFINITY);
3216
+ root.add(group);
3217
+ const scratch = new THREE.Matrix4();
3218
+ let released = 0;
3219
+ const release = (source) => {
3220
+ source.released = true;
3221
+ source.mesh.layers.mask = source.mask;
3222
+ const { index } = source.batch;
3223
+ const first = index.array[source.start];
3224
+ for (let at = source.start; at < source.start + source.count; at++) index.array[at] = first;
3225
+ index.needsUpdate = true;
3226
+ if (--source.batch.live === 0) source.batch.mesh.visible = false;
3227
+ released++;
3228
+ };
3229
+ const hasChanged = (source) => {
3230
+ const { mesh: mesh2 } = source;
3231
+ if (mesh2.geometry !== source.geometry || mesh2.material !== source.material || !isShown(mesh2, root)) return true;
3232
+ if (mesh2.geometry.attributes.position.version !== source.version) return true;
3233
+ if (follow || sameMatrix(mesh2.matrixWorld, source.world)) return false;
3234
+ if (!sameMatrix(scratch.multiplyMatrices(inverse, mesh2.matrixWorld), source.local)) return true;
3235
+ source.world.copy(mesh2.matrixWorld);
3236
+ return false;
3237
+ };
2797
3238
  return {
2798
- callOf,
2799
- call(speciesId, position, o = {}) {
2800
- const found = callOf(speciesId);
2801
- if (!found) return false;
2802
- const key = o.key ?? speciesId;
2803
- if ((waiting.get(key) ?? 0) > 0) return false;
2804
- waiting.set(key, found.cooldown);
2805
- sink.play({ sound: found.sound, position, ...o.volume === void 0 ? {} : { volume: o.volume } });
2806
- return true;
3239
+ group,
3240
+ update() {
3241
+ if (pending.length > 0) mergeSome(performance.now() + (options.budgetMs ?? Number.POSITIVE_INFINITY));
3242
+ const before = released;
3243
+ inverse.copy(root.matrixWorld).invert();
3244
+ for (const source of sources) if (!source.released && hasChanged(source)) release(source);
3245
+ return released - before;
2807
3246
  },
2808
- update(dt) {
2809
- for (const [key, seconds] of waiting) {
2810
- if (seconds - dt <= 0) waiting.delete(key);
2811
- else waiting.set(key, seconds - dt);
3247
+ stats: () => ({ batches: group.children.length, batched: sources.filter((source) => !source.released).length, released }),
3248
+ dispose() {
3249
+ for (const source of sources) if (!source.released) source.mesh.layers.mask = source.mask;
3250
+ group.removeFromParent();
3251
+ for (const child of group.children) {
3252
+ ;
3253
+ child.geometry.dispose();
3254
+ child.skeleton?.dispose();
2812
3255
  }
2813
- },
2814
- dispose: () => waiting.clear()
3256
+ }
2815
3257
  };
2816
3258
  }
2817
-
2818
- // src/line-up.ts
2819
- var lineUpAnimals = (rigs, options = {}) => {
2820
- const start = options.start ?? -19;
2821
- const gap = options.gap ?? 0.85;
2822
- let cursor = start;
2823
- for (const rig of rigs) {
2824
- const length = lineUpLength(rig.species.body);
2825
- cursor += length / 2;
2826
- rig.root.position.set(cursor, 0, 0);
2827
- rig.home = rig.root.position.clone();
2828
- rig.root.rotation.y = rig.species.body.kind === "snake" ? options.snakeYaw ?? 2.2 : options.yaw ?? 1.1;
2829
- cursor += length / 2 + gap;
3259
+ function copyAttribute(name, attribute, target, offset, matrix, normalMatrix2) {
3260
+ const size = attribute.itemSize;
3261
+ const source = attribute.array;
3262
+ const flat = !attribute.isInterleavedBufferAttribute && source.length === attribute.count * size;
3263
+ if (name !== "position" && name !== "normal") {
3264
+ if (flat) target.set(source, offset * size);
3265
+ else for (let at = 0; at < attribute.count; at++) for (let k = 0; k < size; k++) target[(offset + at) * size + k] = attribute.getComponent(at, k);
3266
+ return;
2830
3267
  }
2831
- const offset = -(cursor - gap + start) / 2;
2832
- for (const rig of rigs) {
2833
- rig.root.position.x += offset;
2834
- rig.home.x += offset;
3268
+ const e = (name === "position" ? matrix : normalMatrix2).elements;
3269
+ const isNormal = name === "normal";
3270
+ for (let at = 0; at < attribute.count; at++) {
3271
+ const x = attribute.getX(at);
3272
+ const y = attribute.getY(at);
3273
+ const z = attribute.getZ(at);
3274
+ const out = (offset + at) * 3;
3275
+ if (isNormal) {
3276
+ const nx = e[0] * x + e[3] * y + e[6] * z;
3277
+ const ny = e[1] * x + e[4] * y + e[7] * z;
3278
+ const nz = e[2] * x + e[5] * y + e[8] * z;
3279
+ const length = Math.hypot(nx, ny, nz) || 1;
3280
+ target[out] = nx / length;
3281
+ target[out + 1] = ny / length;
3282
+ target[out + 2] = nz / length;
3283
+ } else {
3284
+ target[out] = e[0] * x + e[4] * y + e[8] * z + e[12];
3285
+ target[out + 1] = e[1] * x + e[5] * y + e[9] * z + e[13];
3286
+ target[out + 2] = e[2] * x + e[6] * y + e[10] * z + e[14];
3287
+ }
2835
3288
  }
2836
- };
3289
+ }
3290
+ function skinnedBatch(THREE, merged, material, meshes) {
3291
+ const vertices = merged.attributes.position.count;
3292
+ const skinIndex = new Uint16Array(vertices * 4);
3293
+ const skinWeight = new Float32Array(vertices * 4);
3294
+ let at = 0;
3295
+ meshes.forEach((mesh22, bone) => {
3296
+ for (let k = 0; k < mesh22.geometry.attributes.position.count; k++, at++) {
3297
+ skinIndex[at * 4] = bone;
3298
+ skinWeight[at * 4] = 1;
3299
+ }
3300
+ });
3301
+ merged.setAttribute("skinIndex", new THREE.BufferAttribute(skinIndex, 4));
3302
+ merged.setAttribute("skinWeight", new THREE.BufferAttribute(skinWeight, 4));
3303
+ const mesh2 = new THREE.SkinnedMesh(merged, material);
3304
+ const identities = meshes.map(() => new THREE.Matrix4());
3305
+ mesh2.bind(new THREE.Skeleton(meshes, identities), new THREE.Matrix4());
3306
+ mesh2.frustumCulled = false;
3307
+ return mesh2;
3308
+ }
3309
+ function mergeGroup(THREE, { meshes, inverse: rootInverse, group, follow }) {
3310
+ const identity = new THREE.Matrix4();
3311
+ const inverse = follow ? identity : rootInverse;
3312
+ const first = meshes[0];
3313
+ const names = ATTRIBUTES.filter((name) => first.geometry.attributes[name] !== void 0);
3314
+ const pieces = meshes.map((mesh22) => {
3315
+ const local = new THREE.Matrix4().multiplyMatrices(inverse, mesh22.matrixWorld);
3316
+ const vertices = mesh22.geometry.attributes.position.count;
3317
+ const indexCount = mesh22.geometry.index ? mesh22.geometry.index.count : vertices;
3318
+ return { mesh: mesh22, local, vertices, indexCount };
3319
+ });
3320
+ const totalVertices = pieces.reduce((sum, piece) => sum + piece.vertices, 0);
3321
+ const totalIndices = pieces.reduce((sum, piece) => sum + piece.indexCount, 0);
3322
+ const merged = new THREE.BufferGeometry();
3323
+ const normalMatrix2 = new THREE.Matrix3();
3324
+ for (const name of names) {
3325
+ const size = first.geometry.attributes[name].itemSize;
3326
+ const array = new Float32Array(totalVertices * size);
3327
+ let offset = 0;
3328
+ for (const piece of pieces) {
3329
+ const matrix = follow ? identity : piece.local;
3330
+ copyAttribute(name, piece.mesh.geometry.attributes[name], array, offset, matrix, normalMatrix2.getNormalMatrix(matrix));
3331
+ offset += piece.vertices;
3332
+ }
3333
+ merged.setAttribute(name, new THREE.BufferAttribute(array, size));
3334
+ }
3335
+ const indexArray2 = totalVertices > 65535 ? new Uint32Array(totalIndices) : new Uint16Array(totalIndices);
3336
+ const indexAttribute = new THREE.BufferAttribute(indexArray2, 1);
3337
+ merged.setIndex(indexAttribute);
3338
+ const mesh2 = follow ? skinnedBatch(THREE, merged, first.material, meshes) : new THREE.Mesh(merged, first.material);
3339
+ mesh2.name = `batch:${first.material.name || first.material.type}`;
3340
+ mesh2.castShadow = first.castShadow;
3341
+ mesh2.receiveShadow = first.receiveShadow;
3342
+ mesh2.raycast = () => void 0;
3343
+ merged.computeBoundingBox();
3344
+ merged.computeBoundingSphere();
3345
+ group.add(mesh2);
3346
+ const batch = { mesh: mesh2, live: pieces.length, index: indexAttribute };
3347
+ const sources = [];
3348
+ let vertexOffset = 0;
3349
+ let indexOffset = 0;
3350
+ for (const piece of pieces) {
3351
+ const index = piece.mesh.geometry.index;
3352
+ if (index) for (let at = 0; at < piece.indexCount; at++) indexArray2[indexOffset + at] = index.getX(at) + vertexOffset;
3353
+ else for (let at = 0; at < piece.indexCount; at++) indexArray2[indexOffset + at] = at + vertexOffset;
3354
+ sources.push({
3355
+ mesh: piece.mesh,
3356
+ geometry: piece.mesh.geometry,
3357
+ material: piece.mesh.material,
3358
+ version: piece.mesh.geometry.attributes.position.version,
3359
+ local: piece.local,
3360
+ world: piece.mesh.matrixWorld.clone(),
3361
+ mask: piece.mesh.layers.mask,
3362
+ batch,
3363
+ start: indexOffset,
3364
+ count: piece.indexCount,
3365
+ released: false
3366
+ });
3367
+ piece.mesh.layers.set(BATCHED_LAYER);
3368
+ vertexOffset += piece.vertices;
3369
+ indexOffset += piece.indexCount;
3370
+ }
3371
+ return sources;
3372
+ }
2837
3373
 
2838
3374
  // src/test-area/scenarios.ts
2839
3375
  var SKIN_COUNT = 40;
@@ -2960,6 +3496,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
2960
3496
  var SKIN_COUNT2 = 40;
2961
3497
  var SKIN_FIELD = { x0: -18, z0: -10, columns: 8, gap: 4.5 };
2962
3498
  var DEFAULT_TIME_SCALE = 60;
3499
+ var BATCH_BUDGET_MS = 1;
2963
3500
  function createAnimalsTestModule(options = {}) {
2964
3501
  const locale = options.locale ?? "pt-BR";
2965
3502
  const text = TEXT[locale] ?? TEXT["pt-BR"];
@@ -2988,14 +3525,24 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
2988
3525
  outbreak = options.createOutbreak ? options.createOutbreak({ species, enabled: settings.outbreak, timeScale: settings.timeScale }) : null;
2989
3526
  };
2990
3527
  rebuildOutbreak();
3528
+ const batch = (entry) => {
3529
+ entry.batcher?.dispose();
3530
+ if (three) entry.batcher = createStaticBatcher(three, entry.rig.root, { followMeshes: true, budgetMs: BATCH_BUDGET_MS });
3531
+ };
3532
+ const look = (entry, state) => {
3533
+ animals?.setVisualState(entry.rig, state);
3534
+ batch(entry);
3535
+ };
2991
3536
  const item = (animal) => ({
2992
3537
  id: `${ANIMAL_ITEM_PREFIX}${animal.id}`,
2993
3538
  name: animal.name[locale] ?? animal.id,
2994
3539
  movable: false,
2995
3540
  build: (THREE) => {
2996
3541
  const rig = ensure(THREE).build(animal);
2997
- placed.set(rig.root.uuid, { id: rig.root.uuid, rig });
3542
+ const entry = { id: rig.root.uuid, rig };
3543
+ placed.set(rig.root.uuid, entry);
2998
3544
  if (settings.visual !== "alive") animals?.setVisualState(rig, settings.visual);
3545
+ batch(entry);
2999
3546
  return rig.root;
3000
3547
  }
3001
3548
  });
@@ -3004,7 +3551,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
3004
3551
  const state = outbreak.stateOf(entry.fate, clock);
3005
3552
  if (state === "incubating") return;
3006
3553
  entry.counted = state === "turned" ? "turned" : "dead";
3007
- animals?.setVisualState(entry.rig, visualStateOf(entry.rig.species.outcome, true, state === "dead"));
3554
+ look(entry, visualStateOf(entry.rig.species.outcome, true, state === "dead"));
3008
3555
  };
3009
3556
  const clearRings = () => {
3010
3557
  discardObject3D(rings);
@@ -3055,7 +3602,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
3055
3602
  const applyVisual = () => {
3056
3603
  for (const entry of placed.values()) {
3057
3604
  if (entry.fate) continue;
3058
- animals?.setVisualState(entry.rig, settings.visual);
3605
+ look(entry, settings.visual);
3059
3606
  }
3060
3607
  };
3061
3608
  const onChange = (change) => {
@@ -3064,13 +3611,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
3064
3611
  if (change.id === "bite_all") {
3065
3612
  for (const entry of placed.values()) {
3066
3613
  entry.fate = outbreak?.bite(entry.rig.species.id, random, clock);
3067
- if (entry.fate) animals?.setVisualState(entry.rig, visualStateOf(entry.rig.species.outcome, true));
3614
+ if (entry.fate) look(entry, visualStateOf(entry.rig.species.outcome, true));
3068
3615
  }
3069
3616
  } else if (change.id === "recover") {
3070
3617
  for (const entry of placed.values()) {
3071
3618
  delete entry.fate;
3072
3619
  delete entry.counted;
3073
- animals?.setVisualState(entry.rig, settings.visual);
3620
+ look(entry, settings.visual);
3074
3621
  }
3075
3622
  } else if (change.id === "call" && sound) {
3076
3623
  for (const [id, entry] of placed) {
@@ -3110,12 +3657,14 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
3110
3657
  sound?.update(dt);
3111
3658
  for (const [id, entry] of placed) {
3112
3659
  if (!entry.rig.root.parent) {
3660
+ entry.batcher?.dispose();
3113
3661
  placed.delete(id);
3114
3662
  continue;
3115
3663
  }
3116
3664
  statusOf(entry);
3117
3665
  animals?.animate(entry.rig, { time, delta: dt * settings.speed, clip: settings.gait, split: host?.bench?.layer("split") ?? false });
3118
3666
  animals?.updateSplit(entry.rig, dt);
3667
+ entry.batcher?.update();
3119
3668
  }
3120
3669
  for (const { rig, skin } of skins) {
3121
3670
  animals?.animate(rig, { time, delta: dt * settings.speed, clip: settings.gait });
@@ -3219,55 +3768,6 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
3219
3768
  }
3220
3769
  };
3221
3770
  }
3222
-
3223
- // src/index.ts
3224
- var countTriangles = (root) => {
3225
- let triangles = 0;
3226
- root.traverse((object2) => {
3227
- const mesh2 = object2;
3228
- if (!mesh2.isMesh) return;
3229
- const geometry = mesh2.geometry;
3230
- triangles += geometry.index ? geometry.index.count / 3 : geometry.attributes.position.count / 3;
3231
- });
3232
- return Math.round(triangles);
3233
- };
3234
- var createAnimalsThree = (host, options = {}) => {
3235
- const THREE = shapeGeometries(host);
3236
- const random = options.random ?? createStream(options.seed ?? 7);
3237
- const documentOf = () => {
3238
- const found = options.document ?? globalThis.document;
3239
- if (!found) throw new Error("game-animals-three: a species with a coat texture needs `options.document` (no global document found)");
3240
- return found;
3241
- };
3242
- const jointMaterial = new THREE.MeshStandardMaterial({ color: 14722376, emissive: 14722376, emissiveIntensity: 0.8, roughness: 0.4 });
3243
- const gaits = GAIT_PHASES;
3244
- return {
3245
- jointMaterial,
3246
- build(species, buildOptions = {}) {
3247
- const rig = createRig(THREE, species, { phase: buildOptions.phase ?? random() * 6 });
3248
- if (species.body.kind === "snake") buildSnake(THREE, rig);
3249
- else if (species.body.kind === "bird") buildBird(THREE, rig, { extra: BIRD_EXTRAS[species.id] });
3250
- else buildQuadruped(THREE, rig, { document: species.body.texture ? documentOf() : void 0, extra: QUADRUPED_EXTRAS[species.id] });
3251
- rig.triangles = countTriangles(rig.root);
3252
- markKitOrigin(rig.root, { owner: "game-animals-three", kind: "animal", id: species.id });
3253
- rig.root.traverse((object2) => {
3254
- if (object2.isMesh) object2.userData.speciesId = species.id;
3255
- });
3256
- return rig;
3257
- },
3258
- animate(rig, params) {
3259
- const kind = rig.species.body.kind;
3260
- if (kind === "snake") animateSnake(THREE, rig, params);
3261
- else if (kind === "bird") animateBird(rig, params);
3262
- else animateQuadruped(rig, params, gaits);
3263
- },
3264
- setVisualState: (rig, state) => applyVisualState(THREE, rig, state),
3265
- setJointView: (rig, on) => setJointView(rig, on, jointMaterial),
3266
- setSplit,
3267
- updateSplit,
3268
- createSkin: (rig) => createNodeSkin(THREE, rig.root, { nodes: collectReferencedNodes(rig, ["species"]) })
3269
- };
3270
- };
3271
3771
  return __toCommonJS(index_exports);
3272
3772
  })();
3273
3773
  //# sourceMappingURL=index.global.js.map