@adatechnology/game-weather-three 0.0.0-stage → 0.2.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.
@@ -0,0 +1,2394 @@
1
+ "use strict";
2
+ var GameWeatherThree = (() => {
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod2) => __copyProps(__defProp({}, "__esModule", { value: true }), mod2);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ ASH: () => ASH,
25
+ FOG_EXP_FROM: () => FOG_EXP_FROM,
26
+ FOG_NEAR: () => FOG_NEAR,
27
+ FOG_ZONE_NEAR: () => FOG_ZONE_NEAR,
28
+ HEADLIGHTS: () => HEADLIGHTS,
29
+ HEAT_HAZE: () => HEAT_HAZE,
30
+ HEAT_HAZE_FRAGMENT: () => HEAT_HAZE_FRAGMENT,
31
+ HEAT_HAZE_VERTEX: () => HEAT_HAZE_VERTEX,
32
+ LIGHTNING: () => LIGHTNING,
33
+ MIST: () => MIST,
34
+ RAIN_CURTAIN: () => RAIN_CURTAIN,
35
+ RAIN_CURTAIN_FRAGMENT: () => RAIN_CURTAIN_FRAGMENT,
36
+ RAIN_CURTAIN_FULL_AT: () => RAIN_CURTAIN_FULL_AT,
37
+ RAIN_CURTAIN_PROFILES: () => RAIN_CURTAIN_PROFILES,
38
+ RAIN_CURTAIN_VERTEX: () => RAIN_CURTAIN_VERTEX,
39
+ SKY_DAY: () => SKY_DAY,
40
+ SKY_NIGHT: () => SKY_NIGHT,
41
+ SPLASH_FROM_MM: () => SPLASH_FROM_MM,
42
+ WEATHER_LAYERS: () => WEATHER_LAYERS,
43
+ ZONE_FOG_DEFAULTS: () => ZONE_FOG_DEFAULTS,
44
+ ZONE_LIGHT: () => ZONE_LIGHT,
45
+ createFog: () => createFog,
46
+ createHeadlights: () => createHeadlights,
47
+ createHeatHaze: () => createHeatHaze,
48
+ createLightning: () => createLightning,
49
+ createNearRain: () => createNearRain,
50
+ createRainCurtain: () => createRainCurtain,
51
+ createSky: () => createSky,
52
+ createWeatherRenderer: () => createWeatherRenderer,
53
+ createWeatherVisual: () => createWeatherVisual,
54
+ defaultHeadlightRule: () => defaultHeadlightRule,
55
+ densityForFar: () => densityForFar,
56
+ emitAirborne: () => emitAirborne,
57
+ fogOf: () => fogOf,
58
+ rainCurtainPositions: () => rainCurtainPositions,
59
+ weatherVisualTextOf: () => weatherVisualTextOf
60
+ });
61
+
62
+ // src/sky.ts
63
+ var SKY_NIGHT = 724760;
64
+ var SKY_DAY = 8885926;
65
+ var ZONE_LIGHT = { base: 0.35, sun: 0.65 };
66
+ var ZONE_FOG_DEFAULTS = { color: 3820090, far: 60 };
67
+ function createSky(THREE, scene) {
68
+ const night = new THREE.Color(SKY_NIGHT);
69
+ const day = new THREE.Color(SKY_DAY);
70
+ const zoneColor = new THREE.Color(ZONE_FOG_DEFAULTS.color);
71
+ const tint = new THREE.Color();
72
+ const color = new THREE.Color();
73
+ return {
74
+ color,
75
+ update(sample, zone) {
76
+ color.lerpColors(night, day, sample.sun);
77
+ color.multiplyScalar(sample.skyScale);
78
+ if (zone && zone.weight > 0) {
79
+ zoneColor.setHex(zone.color ?? ZONE_FOG_DEFAULTS.color);
80
+ color.lerp(tint.copy(zoneColor).multiplyScalar(ZONE_LIGHT.base + sample.sun * ZONE_LIGHT.sun), zone.weight);
81
+ }
82
+ const background = scene.background;
83
+ if (background && background.isColor) background.copy(color);
84
+ else scene.background = color.clone();
85
+ return color;
86
+ }
87
+ };
88
+ }
89
+
90
+ // src/fog.ts
91
+ var FOG_NEAR = 25;
92
+ var FOG_ZONE_NEAR = 4;
93
+ var FOG_EXP_FROM = 5e-4;
94
+ var densityForFar = (far) => Math.sqrt(Math.log(50)) / far;
95
+ function fogOf(sample, zone, expFrom = FOG_EXP_FROM) {
96
+ const weight = zone?.weight ?? 0;
97
+ const zoneFar = zone?.far ?? ZONE_FOG_DEFAULTS.far;
98
+ if (sample.fogDensity >= expFrom) return { mode: "exp2", density: sample.fogDensity + (densityForFar(zoneFar) - sample.fogDensity) * weight };
99
+ return { mode: "linear", near: FOG_NEAR + (FOG_ZONE_NEAR - FOG_NEAR) * weight, far: sample.fogFar + (zoneFar - sample.fogFar) * weight };
100
+ }
101
+ function createFog(THREE, scene, options = {}) {
102
+ const original = scene.fog;
103
+ const kept = original && original.isFog ? original.clone() : original;
104
+ return {
105
+ update(sample, sky, zone) {
106
+ const values = fogOf(sample, zone, options.expFrom);
107
+ const current = scene.fog;
108
+ if (values.mode === "linear") {
109
+ if (current?.isFog) {
110
+ current.color.copy(sky);
111
+ current.near = values.near;
112
+ current.far = values.far;
113
+ } else scene.fog = new THREE.Fog(sky.getHex(), values.near, values.far);
114
+ } else if (current?.isFogExp2) {
115
+ current.color.copy(sky);
116
+ current.density = values.density;
117
+ } else scene.fog = new THREE.FogExp2(sky.getHex(), values.density);
118
+ const fog = scene.fog;
119
+ fog.color.copy(sky);
120
+ },
121
+ clear() {
122
+ scene.fog = kept && kept.isFog ? kept.clone() : kept ?? null;
123
+ }
124
+ };
125
+ }
126
+ var MIST = { box: { x: [-14, 10], y: [0.2, 1.1], z: [-3, 8] }, drift: [0.15, 0.35], life: [9, 13], size: [3, 5], grow: 1.4, color: 8227488, alpha: 0.13, fadeIn: 0.35 };
127
+ var ASH = { box: { x: [-10, 8], y: [4, 6], z: [-3, 7] }, fall: -0.35, life: [8, 12], size: [0.025, 0.04], color: 10525328, alpha: 0.75, fadeIn: 0.1 };
128
+ function emitAirborne(engine, sample, dt, camera2) {
129
+ const { random } = engine;
130
+ const around = (box) => [
131
+ camera2[0] + random.between(box.x[0], box.x[1]),
132
+ random.between(box.y[0], box.y[1]),
133
+ camera2[2] + random.between(box.z[0], box.z[1])
134
+ ];
135
+ if (sample.mistPerSecond > 0) {
136
+ engine.emit("weather-mist", "normal", sample.mistPerSecond, dt, () => ({
137
+ p: around(MIST.box),
138
+ v: [random.between(MIST.drift[0], MIST.drift[1]), 0, random.between(-0.05, 0.05)],
139
+ sp: 0,
140
+ life: MIST.life,
141
+ size: MIST.size,
142
+ grow: MIST.grow,
143
+ c0: MIST.color,
144
+ a: MIST.alpha,
145
+ fi: MIST.fadeIn
146
+ }));
147
+ }
148
+ if (sample.ashPerSecond > 0) {
149
+ engine.emit("weather-ash", "normal", sample.ashPerSecond, dt, () => ({
150
+ p: around(ASH.box),
151
+ v: [random.between(0.1, 0.4), ASH.fall, random.between(-0.1, 0.1)],
152
+ sp: [0.05, 0.15],
153
+ life: ASH.life,
154
+ size: ASH.size,
155
+ c0: ASH.color,
156
+ a: ASH.alpha,
157
+ fi: ASH.fadeIn,
158
+ gnd: 1
159
+ }));
160
+ }
161
+ }
162
+
163
+ // src/headlights.ts
164
+ var HEADLIGHTS = {
165
+ spot: { color: 16773584, intensity: 2.4, distance: 26, angle: 0.33, penumbra: 0.5, decay: 1.6 },
166
+ /** Lamp position on the car (x is mirrored), where the spot points, and the cone drawn in the air. */
167
+ lamp: [0.65, 0.74, 2.15],
168
+ aim: [0.65, 0.2, 12],
169
+ cone: { radius: 2.4, length: 9, segments: 18, color: 16773584, opacity: 0.06 },
170
+ lens: { on: 16773584, off: 3815988 },
171
+ /** Lit drops a second, thrown into the beam 1 to 13 m ahead (W1176). */
172
+ lit: { rate: 600, depth: [1, 13], spread: 0.33, color: 13622512, alpha: 0.9, life: 0.06, len: 0.035, velocity: [0.8, -14, 0] },
173
+ /** Beyond this many metres a car keeps only the cone: no real light (W-b text of `farol`). */
174
+ lightRange: 40
175
+ };
176
+ var defaultHeadlightRule = (sample) => sample.nightFactor > 0.3 || sample.rain > 0 || sample.fogDensity > 0.02 || sample.visibility < 150;
177
+ function createHeadlights(THREE, rules = {}) {
178
+ const rigs = /* @__PURE__ */ new Set();
179
+ const cone = HEADLIGHTS.cone;
180
+ const lampWorld = new THREE.Vector3();
181
+ const aimWorld = new THREE.Vector3();
182
+ const forward = new THREE.Vector3();
183
+ const rule = rules.lit ?? defaultHeadlightRule;
184
+ const range = rules.lightRange ?? HEADLIGHTS.lightRange;
185
+ function attach(car, lens2) {
186
+ const spots = [];
187
+ const cones = [];
188
+ const targets = [];
189
+ for (const side of [-1, 1]) {
190
+ const spot = new THREE.SpotLight(HEADLIGHTS.spot.color, 0, HEADLIGHTS.spot.distance, HEADLIGHTS.spot.angle, HEADLIGHTS.spot.penumbra, HEADLIGHTS.spot.decay);
191
+ spot.position.set(side * HEADLIGHTS.lamp[0], HEADLIGHTS.lamp[1], HEADLIGHTS.lamp[2]);
192
+ const target = new THREE.Object3D();
193
+ target.position.set(side * HEADLIGHTS.aim[0], HEADLIGHTS.aim[1], HEADLIGHTS.aim[2]);
194
+ car.add(spot, target);
195
+ spot.target = target;
196
+ const geometry = new THREE.ConeGeometry(cone.radius, cone.length, cone.segments, 1, true);
197
+ geometry.translate(0, -cone.length / 2, 0);
198
+ geometry.rotateX(-Math.PI / 2);
199
+ const material = new THREE.MeshBasicMaterial({ color: cone.color, transparent: true, opacity: cone.opacity, blending: THREE.AdditiveBlending, depthWrite: false, side: THREE.DoubleSide, fog: false });
200
+ const beam = new THREE.Mesh(geometry, material);
201
+ beam.position.set(side * HEADLIGHTS.lamp[0], HEADLIGHTS.lamp[1], HEADLIGHTS.lamp[2]);
202
+ beam.visible = false;
203
+ car.add(beam);
204
+ spots.push(spot);
205
+ cones.push(beam);
206
+ targets.push(target);
207
+ }
208
+ const rig = { car, spots, cones, lens: lens2, targets };
209
+ rigs.add(rig);
210
+ return () => {
211
+ rigs.delete(rig);
212
+ for (const object of [...spots, ...cones, ...targets]) car.remove(object);
213
+ for (const beam of cones) {
214
+ beam.geometry.dispose();
215
+ beam.material.dispose();
216
+ }
217
+ for (const spot of spots) spot.dispose();
218
+ lens2?.color.setHex(HEADLIGHTS.lens.off);
219
+ };
220
+ }
221
+ return {
222
+ attach,
223
+ update(sample, dt, camera2, engine, enabled = true) {
224
+ const on = enabled && rule(sample);
225
+ let index = 0;
226
+ for (const rig of rigs) {
227
+ rig.car.updateWorldMatrix(true, false);
228
+ lampWorld.set(0, HEADLIGHTS.lamp[1], HEADLIGHTS.lamp[2]).applyMatrix4(rig.car.matrixWorld);
229
+ const near = Math.hypot(lampWorld.x - camera2[0], lampWorld.z - camera2[2]) <= range;
230
+ for (const spot of rig.spots) spot.intensity = on && near ? HEADLIGHTS.spot.intensity : 0;
231
+ for (const beam of rig.cones) beam.visible = on;
232
+ rig.lens?.color.setHex(on ? HEADLIGHTS.lens.on : HEADLIGHTS.lens.off);
233
+ if (!on || !engine || sample.rain <= 0 || !near) {
234
+ index++;
235
+ continue;
236
+ }
237
+ aimWorld.set(0, HEADLIGHTS.lamp[1], HEADLIGHTS.lamp[2] + 1).applyMatrix4(rig.car.matrixWorld);
238
+ forward.copy(aimWorld).sub(lampWorld).normalize();
239
+ const side = [forward.z, -forward.x];
240
+ const { random } = engine;
241
+ const lit = HEADLIGHTS.lit;
242
+ engine.emit(`weather-lit-${index}`, "streak", lit.rate, dt, () => {
243
+ const depth = random.between(lit.depth[0], lit.depth[1]);
244
+ const spread = depth * lit.spread * Math.sqrt(random.next());
245
+ const turn = random.between(0, 6.283);
246
+ return {
247
+ p: [lampWorld.x + forward.x * depth + side[0] * Math.cos(turn) * spread, lampWorld.y + forward.y * depth + Math.sin(turn) * spread * 0.6, lampWorld.z + forward.z * depth + side[1] * Math.cos(turn) * spread],
248
+ v: lit.velocity,
249
+ sp: 0,
250
+ life: lit.life,
251
+ len: lit.len,
252
+ c0: lit.color,
253
+ a: lit.alpha
254
+ };
255
+ });
256
+ index++;
257
+ }
258
+ },
259
+ count: () => rigs.size,
260
+ dispose() {
261
+ for (const rig of [...rigs]) {
262
+ rigs.delete(rig);
263
+ for (const object of [...rig.spots, ...rig.cones, ...rig.targets]) rig.car.remove(object);
264
+ }
265
+ }
266
+ };
267
+ }
268
+
269
+ // src/heat-haze.ts
270
+ var HEAT_HAZE = { opacity: 0.09, bands: 38, rise: 1.4, color: [1, 0.93, 0.82] };
271
+ var HEAT_HAZE_VERTEX = "varying vec2 vUv; void main(){ vUv = uv; gl_Position = vec4(position.xy, 0., 1.); }";
272
+ var HEAT_HAZE_FRAGMENT = "uniform float uTime; uniform float uHaze; uniform vec3 uColor; varying vec2 vUv; void main(){ float low = 1. - smoothstep(0.15, 0.7, vUv.y); float wave = sin(vUv.y*38. - uTime*1.4 + sin(vUv.x*11. + uTime*.7)*2.2) * .5 + .5; gl_FragColor = vec4(uColor, uHaze*low*wave*.09); }";
273
+ function createHeatHaze(THREE) {
274
+ const uniforms = { uTime: { value: 0 }, uHaze: { value: 0 }, uColor: { value: new THREE.Vector3(...HEAT_HAZE.color) } };
275
+ const material = new THREE.ShaderMaterial({ uniforms, vertexShader: HEAT_HAZE_VERTEX, fragmentShader: HEAT_HAZE_FRAGMENT, transparent: true, depthTest: false, depthWrite: false });
276
+ const object = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), material);
277
+ object.frustumCulled = false;
278
+ object.renderOrder = 999;
279
+ object.name = "weather-heat-haze";
280
+ object.visible = false;
281
+ return {
282
+ object,
283
+ update(sample, time) {
284
+ uniforms.uHaze.value = sample.haze;
285
+ uniforms.uTime.value = time;
286
+ object.visible = sample.haze > 0;
287
+ },
288
+ dispose() {
289
+ object.geometry.dispose();
290
+ material.dispose();
291
+ }
292
+ };
293
+ }
294
+
295
+ // src/lightning.ts
296
+ var LIGHTNING = { light: 3.2, background: [0.25, 0.27, 0.32], white: 0.12, skyColor: 3820650, groundColor: 723724 };
297
+ function createLightning(THREE, scene, vfx) {
298
+ const light = new THREE.HemisphereLight(LIGHTNING.skyColor, LIGHTNING.groundColor, 0);
299
+ light.name = "weather-lightning";
300
+ scene.add(light);
301
+ return {
302
+ light,
303
+ update(sample, background) {
304
+ const flash2 = sample.flash;
305
+ light.intensity = flash2 * LIGHTNING.light;
306
+ if (flash2 <= 0) return;
307
+ if (background) {
308
+ background.r += flash2 * LIGHTNING.background[0];
309
+ background.g += flash2 * LIGHTNING.background[1];
310
+ background.b += flash2 * LIGHTNING.background[2];
311
+ }
312
+ const screen2 = vfx?.workshop().screen;
313
+ if (screen2) screen2.white = Math.max(screen2.white, flash2 * LIGHTNING.white);
314
+ },
315
+ dispose() {
316
+ scene.remove(light);
317
+ light.dispose();
318
+ }
319
+ };
320
+ }
321
+
322
+ // ../game-vfx/dist/index.js
323
+ var BYTE_MAX = 255;
324
+ var SRGB_LINEAR_SLOPE = 0.0773993808;
325
+ var SRGB_THRESHOLD = 0.04045;
326
+ function hexToRgb(hex) {
327
+ const whole = Math.floor(hex);
328
+ return [(whole >> 16 & BYTE_MAX) / BYTE_MAX, (whole >> 8 & BYTE_MAX) / BYTE_MAX, (whole & BYTE_MAX) / BYTE_MAX];
329
+ }
330
+ function srgbChannelToLinear(channel) {
331
+ return channel < SRGB_THRESHOLD ? channel * SRGB_LINEAR_SLOPE : Math.pow(channel * 0.9478672986 + 0.0521327014, 2.4);
332
+ }
333
+ function hexToLinearRgb(hex) {
334
+ const [red, green, blue] = hexToRgb(hex);
335
+ return [srgbChannelToLinear(red), srgbChannelToLinear(green), srgbChannelToLinear(blue)];
336
+ }
337
+ var SMOKE_COLORS = {
338
+ grey: hexToLinearRgb(6972767),
339
+ dark: hexToLinearRgb(4867136),
340
+ black: hexToLinearRgb(3814449),
341
+ brown: hexToLinearRgb(8019006),
342
+ dust: hexToLinearRgb(8221544),
343
+ concrete: hexToLinearRgb(9407106),
344
+ white: hexToLinearRgb(11841704),
345
+ blood: hexToLinearRgb(5901067),
346
+ bile: hexToLinearRgb(8030762),
347
+ gas: hexToLinearRgb(15265007)
348
+ };
349
+ var DEFAULT_COLOR = [85 / 255, 85 / 255, 85 / 255];
350
+ function createVec3(x = 0, y = 0, z = 0) {
351
+ return { x, y, z };
352
+ }
353
+ var call = (name) => (...args) => ({ $: name, a: args });
354
+ var rnd = call("rnd");
355
+ var rand = call("rand");
356
+ var add = call("add");
357
+ var concat = call("concat");
358
+ var sub = call("sub");
359
+ var mul = call("mul");
360
+ var div = call("div");
361
+ var mod = call("mod");
362
+ var neg = call("neg");
363
+ var abs = call("abs");
364
+ var sqrt = call("sqrt");
365
+ var exp = call("exp");
366
+ var sin = call("sin");
367
+ var cos = call("cos");
368
+ var atan2 = call("atan2");
369
+ var min = call("min");
370
+ var max = call("max");
371
+ var floor = call("floor");
372
+ var ceil = call("ceil");
373
+ var round = call("round");
374
+ var clamp = call("clamp");
375
+ var lerp2 = call("lerp");
376
+ var ease = call("ease");
377
+ var lt = call("lt");
378
+ var gt = call("gt");
379
+ var le = call("le");
380
+ var ge = call("ge");
381
+ var eq = call("eq");
382
+ var ne = call("ne");
383
+ var not = call("not");
384
+ var and = call("and");
385
+ var or = call("or");
386
+ var ite = call("ite");
387
+ var vadd = call("vadd");
388
+ var vsub = call("vsub");
389
+ var vscale = call("vscale");
390
+ var vnorm = call("vnorm");
391
+ var vneg = call("vneg");
392
+ var vlerp = call("vlerp");
393
+ var vx = call("vx");
394
+ var vy = call("vy");
395
+ var vz = call("vz");
396
+ var vdist = call("vdist");
397
+ var vwith = call("with");
398
+ var pick = call("at");
399
+ var scaled = call("scaled");
400
+ var param = (...values) => ({ $: "param", a: [values] });
401
+ var local = (name, value, body) => ({ $: "let", a: [name, value, body] });
402
+ var query = (behavior2, name, ...args) => ({ $: "query", a: [behavior2, name, ...args] });
403
+ var variable = (name) => ({ $: "var", a: [name] });
404
+ var cameraForward = call("cameraForward");
405
+ var TIME = { $: "t" };
406
+ var PARAM = { $: "paramIndex" };
407
+ var DELTA = { $: "dt" };
408
+ var INDEX = { $: "index" };
409
+ var QUALITY = { $: "quality" };
410
+ var FRESH = { $: "fresh" };
411
+ var DIRECTION = { $: "direction" };
412
+ var CAMERA_POSITION = { $: "cameraPosition" };
413
+ var burst = (system, count, spawn2) => ({ op: "burst", system, count, spawn: spawn2 });
414
+ var spawn = (system, options) => ({ op: "spawn", system, spawn: options });
415
+ var emit = (key, system, rate, options) => ({ op: "emit", key, system, rate, spawn: options });
416
+ var mesh = (options) => ({ op: "mesh", spawn: options });
417
+ var debris = (options) => ({ op: "debris", spawn: options });
418
+ var decal = (...args) => ({ op: "decal", args });
419
+ var ring = (...args) => ({ op: "ring", args });
420
+ var flash = (...args) => ({ op: "flash", args });
421
+ var lens = (...args) => ({ op: "lens", args });
422
+ var screen = (change) => ({ op: "screen", ...change });
423
+ var fireLight = (slot, point, intensity, time) => ({ op: "fire_light", slot, point, intensity, time });
424
+ var fireLightOff = (slot) => ({ op: "fire_light_off", slot });
425
+ var cue = (name, args, draws) => ({ op: "cue", cue: name, ...args !== void 0 ? { args } : {}, ...draws !== void 0 ? { draws } : {} });
426
+ var call2 = (macro, ...args) => ({ op: "call", macro, args });
427
+ var after = (time, ...steps) => ({ op: "at", time, steps });
428
+ var when = (condition, steps, otherwise) => ({ op: "when", if: condition, steps, ...otherwise ? { otherwise } : {} });
429
+ var repeat = (count, ...steps) => ({ op: "repeat", count, steps });
430
+ var set = (name, value) => ({ op: "set", name, value });
431
+ var behavior = (id, action, args) => ({ op: "behavior", id, action, ...args !== void 0 ? { args } : {} });
432
+ var append = (name, value) => ({ op: "append", name, value });
433
+ var camera = (eye, target, instant) => ({ op: "camera", eye, target, ...instant ? { instant } : {} });
434
+ var post = (values) => ({ op: "post", values });
435
+ var CENTRE = [0, 1.1, 0];
436
+ var UP = [0, 1, 0];
437
+ var BLOATER_CLOUD = {
438
+ id: "bloater_cloud",
439
+ pageId: "inchado",
440
+ team: "creatures",
441
+ budget: { particles: 180 },
442
+ loop: 9,
443
+ view: { eye: [5.5, 3.2, 6], target: [0, 1, 0] },
444
+ timeline: [
445
+ after(
446
+ 1,
447
+ burst("normal", 40, { p: CENTRE, r: 0.4, sp: [1.5, 4], drag: 2.2, life: [4, 6], size: [1, 2.2], grow: 2, c0: 7309884, c1: 3885606, a: 0.32, fi: 0.08, g: 0.05 }),
448
+ burst("normal", 30, { p: CENTRE, dir: UP, cone: 1.3, sp: [2, 4], life: 1.4, size: [0.04, 0.08], c0: 4872732, g: 9.8, fi: 0, gnd: 3, ds: [0.1, 0.2] }),
449
+ repeat(scaled(10), debris({ p: CENTRE, dir: UP, cone: 1.2, sp: [2, 5], size: [0.07, 0.05, 0.07], k: [0.6, 1.4], c: ite(mod(INDEX, 2), 7043666, 4856338), life: 9 })),
450
+ decal(0, 0, 0, 1.6, 6978084, { grow: 0.6, life: 60, fluid: "bile" }),
451
+ ring([0, 0.02, 0], 4, 4, 8, 3824160, UP, 0.35),
452
+ screen({ add: { shake: 0.15 } })
453
+ )
454
+ ],
455
+ tick: [
456
+ when(and(gt(TIME, 1), lt(TIME, 8)), [
457
+ emit("gas", "normal", 7, { p: [0, 0.4, 0], r: 3.4, flat: true, dir: UP, cone: 1.3, sp: [0.1, 0.35], drag: 0.4, life: [3, 5], size: [1.2, 2.2], grow: 1.5, c0: 7309884, c1: 3885606, a: 0.22, fi: 0.3 }),
458
+ emit("mote", "add", 10, { p: [0, 0.6, 0], r: 3.6, flat: true, dir: UP, cone: 0.6, sp: [0.05, 0.25], life: [1.5, 2.5], size: [0.04, 0.07], c0: 6283402, a: 0.7, fi: 0.4 })
459
+ ])
460
+ ]
461
+ };
462
+ var BLOOD = param(9046538, 5901328, 3808274, 1906964);
463
+ var BLEED = [1, 0.7, 0.35];
464
+ var BLOOD_HIT = {
465
+ id: "blood_hit",
466
+ pageId: "sangue",
467
+ team: "creatures",
468
+ params: { id: "age", options: ["fresh", "recent", "old", "decrepit"], default: 0 },
469
+ budget: { particles: 70, decals: 1 },
470
+ loop: 3.2,
471
+ view: { eye: [2.6, 1.9, 3.4], target: [0, 1, -0.6] },
472
+ timeline: [
473
+ after(0.3, call2("blood_hit", [0, 1.3, 0.1], vnorm([0.15, 0.1, -1]), PARAM)),
474
+ after(1.5, call2("blood_hit", [0.1, 1.05, 0.1], vnorm([-0.2, 0.05, -1]), PARAM, 0.8)),
475
+ when(lt(PARAM, 3), [after(0.7, decal(0, 0, -0.2, add(0.5, mul(0.7, pick(BLEED, PARAM))), BLOOD, { grow: add(2, mul(2, pick(BLEED, PARAM))), dark: 60, dry: 3874068 }))])
476
+ ],
477
+ tick: [
478
+ when(and(lt(PARAM, 2), gt(TIME, 0.3)), [
479
+ emit("drip", "normal", param(7, 3), {
480
+ p: [0.05, 1.15, 0.17],
481
+ box: [0.08, 0.05, 0],
482
+ dir: [0, 1, 0],
483
+ cone: 0.2,
484
+ sp: [0, 0.2],
485
+ life: 1.2,
486
+ size: 0.03,
487
+ c0: BLOOD,
488
+ g: 9.8,
489
+ fi: 0,
490
+ gnd: 3,
491
+ ds: [0.04, 0.08]
492
+ })
493
+ ])
494
+ ]
495
+ };
496
+ var UP2 = [0, 1, 0];
497
+ var CORPSE_DECAY = {
498
+ id: "corpse_decay",
499
+ pageId: "corpo",
500
+ team: "creatures",
501
+ budget: { particles: 40 },
502
+ loop: 8.5,
503
+ view: { eye: [2.4, 1.6, 2.6], target: [0, 0.3, -0.6] },
504
+ timeline: [after(0.8, decal(0, 0, -0.9, 1.1, 5901328, { grow: 1.5, dark: 5, dry: 3874068 }))],
505
+ tick: [
506
+ when(and(gt(TIME, 2.5), lt(TIME, 7)), [
507
+ emit("fly", "normal", 6, { p: [0, 0.35, -0.8], r: 0.5, sp: [0.3, 0.8], life: [0.6, 1.2], size: 0.02, c0: 328965, a: 1, fi: 0.2 })
508
+ ]),
509
+ when(and(gt(TIME, 6.8), lt(TIME, 7.6)), [
510
+ emit("dust", "normal", 40, { p: [0, 0.15, -0.8], box: [0.25, 0.05, 0.8], dir: UP2, cone: 0.6, sp: [0.2, 0.6], drag: 1, life: [1, 1.8], size: [0.12, 0.22], grow: 3, c0: 2828066, a: 0.5 })
511
+ ])
512
+ ]
513
+ };
514
+ var RUNNERS = [false, true, false, false, false, true, false, false, false, true];
515
+ var RUN_SPEED = 3.8;
516
+ var WALK_SPEED = 0.7;
517
+ var SPEED = ite(pick(RUNNERS, INDEX), RUN_SPEED, WALK_SPEED);
518
+ var walkTo = behavior("walkers", "walk", { index: INDEX, to: [-12, 0, query("walkers", "z", INDEX)], speed: SPEED });
519
+ var EYES_IN_FOG = {
520
+ id: "eyes_in_fog",
521
+ pageId: "olhos",
522
+ team: "creatures",
523
+ budget: { particles: 20 },
524
+ loop: 0,
525
+ view: { eye: [-9, 1.7, 2], target: [6, 1.3, 2] },
526
+ setup: [
527
+ repeat(10, set("placeX", rnd(4, 34)), set("placeZ", rnd(-1.5, 5.5)), behavior("walkers", "place", { index: INDEX, x: variable("placeX"), z: variable("placeZ"), facing: -Math.PI / 2 }), walkTo)
528
+ ],
529
+ timeline: [],
530
+ tick: [
531
+ repeat(
532
+ 10,
533
+ when(lt(query("walkers", "x", INDEX), -8), [behavior("walkers", "move_x", { index: INDEX, x: rnd(28, 36) }), walkTo], [when(not(query("walkers", "is_walking", INDEX)), [walkTo])]),
534
+ when(and(pick(RUNNERS, INDEX), lt(QUALITY, 2)), [
535
+ spawn("streak", { p: query("walkers", "point", INDEX, [0, 1.74, 0.2]), dir: [1, 0, 0], cone: 0, sp: RUN_SPEED, v: [-RUN_SPEED, 0, 0], life: 0.25, len: 0.06, c0: 15022379, c1: 4194304, a: 0.8 })
536
+ ])
537
+ )
538
+ ]
539
+ };
540
+ var SHOULDER = [0.36, 1.46, 0.05];
541
+ var FRESH_BLOOD = 9046538;
542
+ var DISMEMBER = {
543
+ id: "dismember",
544
+ pageId: "membro",
545
+ team: "creatures",
546
+ budget: { particles: 60, debris: 1, decals: 4 },
547
+ loop: 3.6,
548
+ view: { eye: [3, 1.8, 2.2], target: [0.4, 1.1, 0] },
549
+ timeline: [
550
+ after(
551
+ 0.4,
552
+ debris({ p: SHOULDER, v: [2.2, 2.5, -0.6], sp: 0, size: [0.12, 0.12, 0.66], c: 7174746, spin: 9, life: 12, bounce: 0.25 }),
553
+ burst("normal", 12, { p: SHOULDER, r: 0.05, sp: [0.3, 1.2], drag: 3, life: [0.3, 0.6], size: [0.1, 0.16], grow: 3, c0: FRESH_BLOOD, a: 0.6 })
554
+ )
555
+ ],
556
+ tick: [
557
+ when(and(gt(TIME, 0.4), lt(TIME, 1.1)), [
558
+ emit("jet", "normal", mul(90, ite(gt(sin(mul(sub(TIME, 0.4), 28)), 0), 1, 0.2)), { p: SHOULDER, dir: vnorm([1, 0.35, 0.1]), cone: 0.25, sp: [1.5, 3], life: 1.4, size: [0.03, 0.055], c0: FRESH_BLOOD, g: 9.8, fi: 0, gnd: 3, ds: [0.05, 0.12] })
559
+ ])
560
+ ]
561
+ };
562
+ var FRESH_BLOOD2 = 9046538;
563
+ var HEADSHOT = {
564
+ id: "headshot",
565
+ pageId: "headshot",
566
+ team: "creatures",
567
+ budget: { particles: 70, debris: 8, decals: 1 },
568
+ loop: 3.2,
569
+ view: { eye: [2.2, 1.8, 3], target: [0, 1.2, -0.5] },
570
+ timeline: [
571
+ after(
572
+ 0.45,
573
+ burst("normal", 20, { p: [0, 1.7, 0], r: 0.1, sp: [0.4, 1.8], drag: 3, life: [0.4, 0.75], size: [0.12, 0.2], grow: 3, c0: FRESH_BLOOD2, a: 0.7 }),
574
+ burst("normal", 35, { p: [0, 1.7, 0], dir: vnorm([0, 0.45, -1]), cone: 0.8, sp: [2, 5], life: 1.5, size: [0.035, 0.065], c0: FRESH_BLOOD2, g: 9.8, fi: 0, gnd: 3, ds: [0.06, 0.14] }),
575
+ repeat(scaled(8), debris({ p: [0, 1.7, 0], dir: vnorm([0, 0.6, -1]), cone: 0.9, sp: [1.5, 4], size: [0.06, 0.05, 0.06], k: [0.5, 1.2], c: ite(mod(INDEX, 2), 14274495, 7174746), life: 8 })),
576
+ flash([0, 1.7, 0], 16724e3, 1.2, 0.08, 4)
577
+ ),
578
+ after(1.4, decal(0, 0, -1.5, 0.9, FRESH_BLOOD2, { grow: 2.4, dark: 60, dry: 3874068 }))
579
+ ]
580
+ };
581
+ var UP3 = [0, 1, 0];
582
+ var REANIMATION = {
583
+ id: "reanimation",
584
+ pageId: "reanima",
585
+ team: "creatures",
586
+ budget: { particles: 30 },
587
+ loop: 6.5,
588
+ view: { eye: [2.4, 1.4, 2.4], target: [0, 0.6, -0.4] },
589
+ timeline: [],
590
+ tick: [
591
+ when(lt(TIME, 3.4), [
592
+ when(gt(TIME, 1), [cue("zombie_arm_twitch", void 0, 2)]),
593
+ when(gt(TIME, 0.6), [
594
+ emit("mote", "add", 8, { p: [0, 0.2, -0.8], box: [0.25, 0.05, 0.6], dir: UP3, cone: 0.4, sp: [0.2, 0.5], life: [0.6, 1], size: [0.03, 0.05], c0: 10469978, a: 0.7, fi: 0.3 })
595
+ ])
596
+ ]),
597
+ when(and(gt(TIME, 5), lt(TIME, 5.6)), [
598
+ emit("br", "normal", 20, { p: [0, 1.68, 0.2], dir: vnorm([0, 0.3, 1]), cone: 0.3, sp: [0.6, 1.1], drag: 1.5, life: 1.2, size: [0.05, 0.1], grow: 5, c0: 14212578, a: 0.2 })
599
+ ])
600
+ ]
601
+ };
602
+ var WHITE_RING = 15262416;
603
+ var PREPARATION_END = 1.7;
604
+ var SHRIEK_END = 2.6;
605
+ var SCREAMER_SHRIEK = {
606
+ id: "screamer_shriek",
607
+ pageId: "grito",
608
+ team: "creatures",
609
+ budget: { particles: 150 },
610
+ loop: 5,
611
+ view: { eye: [6, 3, 7], target: [0, 1, -1] },
612
+ timeline: [
613
+ after(
614
+ PREPARATION_END,
615
+ repeat(3, after(add(PREPARATION_END, mul(INDEX, 0.18)), ring([0, 0.03, 0], 0.3, 9, 0.9, WHITE_RING))),
616
+ screen({ add: { chroma: 7, shake: 0.25 } })
617
+ )
618
+ ],
619
+ tick: [
620
+ when(lt(TIME, PREPARATION_END), [
621
+ emit("vap", "normal", 18, { p: [0, 1.68, 0.2], dir: vnorm([0, 0.5, 1]), cone: 0.3, sp: [0.6, 1.2], drag: 1.5, life: [0.8, 1.3], size: [0.05, 0.1], grow: 5, c0: 14212578, a: 0.2 })
622
+ ], [
623
+ when(lt(TIME, SHRIEK_END), [
624
+ set("shriekRadius", mul(div(sub(TIME, PREPARATION_END), 0.9), 8)),
625
+ emit(
626
+ "dust",
627
+ "normal",
628
+ 140,
629
+ local("angle", mul(rand(), 6.283), {
630
+ p: [mul(cos(variable("angle")), variable("shriekRadius")), 0.05, mul(sin(variable("angle")), variable("shriekRadius"))],
631
+ dir: vnorm([cos(variable("angle")), 0.6, sin(variable("angle"))]),
632
+ cone: 0.3,
633
+ sp: [0.6, 1.6],
634
+ drag: 2,
635
+ life: [0.8, 1.4],
636
+ size: [0.2, 0.35],
637
+ grow: 3,
638
+ c0: 6117197,
639
+ a: 0.35
640
+ })
641
+ )
642
+ ])
643
+ ])
644
+ ]
645
+ };
646
+ function localToWorld(translation, rotation, local2) {
647
+ const [x, y, z] = local2;
648
+ const [a, c, e] = [Math.cos(rotation[0]), Math.cos(rotation[1]), Math.cos(rotation[2])];
649
+ const [b, d, f] = [Math.sin(rotation[0]), Math.sin(rotation[1]), Math.sin(rotation[2])];
650
+ const [ae, af, be, bf] = [a * e, a * f, b * e, b * f];
651
+ return [
652
+ c * e * x + -c * f * y + d * z + translation[0],
653
+ (af + be * d) * x + (ae - bf * d) * y + -b * c * z + translation[1],
654
+ (bf - ae * d) * x + (be + af * d) * y + a * c * z + translation[2]
655
+ ];
656
+ }
657
+ var MATERIALS = ["concrete", "metal", "wood", "glass", "flesh", "water"];
658
+ var CAR_ORIGIN = [5.6, 0, 0.8];
659
+ var CAR_ROTATION = [0, -0.4, 0];
660
+ var FACING_WALL = [0, 0, 1];
661
+ var UP4 = [0, 1, 0];
662
+ var GLASS = 3;
663
+ var FLESH = 4;
664
+ var WATER = 5;
665
+ var SHOT_TIMES = [0, 0.55, 1.1];
666
+ var LAST_SHOT = 2;
667
+ var BUMPER = localToWorld(CAR_ORIGIN, CAR_ROTATION, [0.96, 0.7, 0.4]);
668
+ var CAR_SIDE = [Math.cos(CAR_ROTATION[1]), 0, -Math.sin(CAR_ROTATION[1])];
669
+ var POINTS = [[-2, 1.6, -4.45], BUMPER, [2, 1.3, -4.36], [-5, 1.9, -4.45], [0, 1.25, 0.16], [-1.5, 0.012, 2.2]];
670
+ var NORMALS = [FACING_WALL, CAR_SIDE, FACING_WALL, FACING_WALL, FACING_WALL, UP4];
671
+ var EYES = [[-2, 1.6, -1.8], localToWorld(CAR_ORIGIN, CAR_ROTATION, [3, 1.4, 1.5]), [2, 1.3, -1.6], [-5, 1.9, -1.8], [0, 1.2, 2.6], [-1.5, 0.8, 4.6]];
672
+ var WINDOW = { centreX: -5, centreY: 1.9, width: 2.6, height: 1.8, pixelsX: 256, pixelsY: 180 };
673
+ var scatter = ite(eq(PARAM, WATER), [rnd(-0.6, 0.6), 0, rnd(-0.3, 0.3)], ite(eq(PARAM, FLESH), [rnd(-0.1, 0.1), rnd(-0.15, 0.15), 0], [rnd(-0.4, 0.4), rnd(-0.3, 0.3), 0]));
674
+ var hitX = { $: "vx", a: [variable("hit")] };
675
+ var hitY = { $: "vy", a: [variable("hit")] };
676
+ var pixelX = { $: "mul", a: [{ $: "add", a: [{ $: "div", a: [{ $: "sub", a: [hitX, WINDOW.centreX] }, WINDOW.width] }, 0.5] }, WINDOW.pixelsX] };
677
+ var pixelY = { $: "mul", a: [{ $: "sub", a: [1, { $: "add", a: [{ $: "div", a: [{ $: "sub", a: [hitY, WINDOW.centreY] }, WINDOW.height] }, 0.5] }] }, WINDOW.pixelsY] };
678
+ var IMPACT_BY_MATERIAL = {
679
+ id: "impact",
680
+ pageId: "impacto",
681
+ team: "weapons",
682
+ params: { id: "material", options: MATERIALS, default: 0 },
683
+ budget: { particles: 40, decals: 1 },
684
+ loop: 2.6,
685
+ view: { eye: [0, 1.8, 4.5], target: [0, 1.2, -2] },
686
+ props: ["cracks"],
687
+ timeline: [
688
+ when({ $: "eq", a: [PARAM, WATER] }, [decal(-1.5, 0, 2.2, 3.2, 856600, { grow: 0, life: 60, ar: 0.55 })]),
689
+ repeat(
690
+ SHOT_TIMES.length,
691
+ after(
692
+ add(0.2, pick(SHOT_TIMES, INDEX)),
693
+ set("hit", vadd(pick(POINTS, PARAM), scatter)),
694
+ call2("impact", pick(MATERIALS, PARAM), variable("hit"), pick(NORMALS, PARAM), 1, 0),
695
+ when(eq(PARAM, GLASS), [behavior("cracks", "crack", { x: pixelX, y: pixelY, big: eq(INDEX, LAST_SHOT) })])
696
+ )
697
+ ),
698
+ when(FRESH, [camera(pick(EYES, PARAM), pick(POINTS, PARAM))])
699
+ ]
700
+ };
701
+ var BLOOD2 = [9046538, 5901328, 3808274, 1906964];
702
+ var BODY_Z = [2.2, 1, -0.2, -1.4, -2.6];
703
+ var MUZZLE_Z = 8.5;
704
+ var SPEED2 = 12;
705
+ var WALL_Z = -4.4;
706
+ var FORWARD = [0, 0, -1];
707
+ var bulletAt = [0, 1.45, variable("bulletZ")];
708
+ var PENETRATION = {
709
+ id: "penetration",
710
+ pageId: "penetra",
711
+ team: "weapons",
712
+ budget: { particles: 250 },
713
+ loop: 4.2,
714
+ view: { eye: [4.2, 2.2, 6.5], target: [0, 1.2, -0.5] },
715
+ setup: [
716
+ cue("zombie_visible", { index: -1, visible: false }),
717
+ repeat(
718
+ BODY_Z.length,
719
+ set("bodyX", rnd(-0.05, 0.05)),
720
+ set("bodyTurn", rnd(-0.3, 0.3)),
721
+ append("bodiesX", variable("bodyX")),
722
+ cue("zombie_place", { index: INDEX, x: variable("bodyX"), z: pick(BODY_Z, INDEX), turn: variable("bodyTurn") })
723
+ )
724
+ ],
725
+ timeline: [set("bulletZ", MUZZLE_Z), set("bulletOn", true), call2("muzzle", "barrett", [0, 1.45, MUZZLE_Z], FORWARD)],
726
+ tick: [
727
+ when(variable("bulletOn"), [
728
+ set("previousZ", variable("bulletZ")),
729
+ set("bulletZ", sub(variable("bulletZ"), mul(SPEED2, DELTA))),
730
+ spawn("streak", { p: bulletAt, dir: FORWARD, cone: 0, sp: 12, life: 0.04, len: 0.07, c0: 16765072 }),
731
+ spawn("add", { p: bulletAt, size: 0.09, life: 0.05, c0: 16760944, fi: 0 }),
732
+ spawn("normal", { p: bulletAt, r: 0.02, sp: [0, 0.05], life: [1.2, 2], size: 0.05, grow: 3, c0: 13159634, a: 0.12, fi: 0.1 }),
733
+ repeat(
734
+ BODY_Z.length,
735
+ when(and(gt(variable("previousZ"), pick(BODY_Z, INDEX)), le(variable("bulletZ"), pick(BODY_Z, INDEX))), [
736
+ call2("blood_hit", [0, 1.45, sub(pick(BODY_Z, INDEX), 0.1)], [0, 0.05, -1], mod(INDEX, 4), 0.8),
737
+ cue("zombie_fall", { index: INDEX, delay: 0.05, duration: 0.6 }),
738
+ after(
739
+ add(TIME, 0.6),
740
+ decal(pick(variable("bodiesX"), INDEX), 0, sub(pick(BODY_Z, INDEX), 1.2), 0.6, pick(BLOOD2, mod(INDEX, 4)), { grow: 2, dark: 60, dry: 3874068 })
741
+ )
742
+ ])
743
+ ),
744
+ when(lt(variable("bulletZ"), WALL_Z), [set("bulletOn", false), call2("impact", "concrete", [0, 1.45, -4.45], [0, 0, 1], 2.2)])
745
+ ])
746
+ ]
747
+ };
748
+ var TIP = [-0.8, 1.38, 3.4 - 0.78];
749
+ var PORT = [-0.76, 1.4, 3.32];
750
+ var FORWARD2 = [0, 0, -1];
751
+ var UP5 = [0, 1, 0];
752
+ var BURST_SHOTS = 12;
753
+ var CASINGS_AND_MAGAZINE = {
754
+ id: "casings_and_magazine",
755
+ pageId: "capsulas",
756
+ team: "weapons",
757
+ budget: { particles: 200, debris: 13 },
758
+ loop: 4.6,
759
+ view: { eye: [0.9, 1.3, 4.6], target: [-0.6, 0.6, 3.1] },
760
+ setup: [cue("gun_visible", { visible: true }), cue("zombie_visible", { index: -1, visible: false })],
761
+ timeline: [
762
+ repeat(
763
+ BURST_SHOTS,
764
+ after(add(0.2, mul(INDEX, 0.1)), call2("muzzle", "ak", TIP, FORWARD2), call2("casing", PORT, ""), cue("gun_recoil", { kick: 0.05 }))
765
+ ),
766
+ after(1.8, debris({ p: [-0.8, 1.15, 3.3], v: [0.1, -0.5, 0.1], sp: 0, size: [0.05, 0.2, 0.09], c: 2763306, spin: 3, life: 20, bounce: 0.2 }))
767
+ ],
768
+ tick: [
769
+ when(and(gt(TIME, 1.4), lt(TIME, 3.6)), [
770
+ emit("hot", "normal", 10, { p: TIP, dir: UP5, cone: 0.25, sp: [0.2, 0.5], life: [1.4, 2.2], size: [0.03, 0.06], grow: 5, c0: 10132118, a: 0.18, g: -0.2, fi: 0.2 })
771
+ ])
772
+ ]
773
+ };
774
+ var FROM = [-4, 1.6, 5];
775
+ var TARGET = [0, 0, -0.5];
776
+ var UP6 = [0, 1, 0];
777
+ var FLIGHT = 1;
778
+ var ARC_HEIGHT = 2.2;
779
+ var GLASS_GREEN = 3828282;
780
+ var OBJECT_ID_DRAWS = 12;
781
+ var at = (offset) => vadd(TARGET, offset);
782
+ var MOLOTOV = {
783
+ id: "molotov",
784
+ pageId: "molotov",
785
+ team: "weapons",
786
+ lab: "molotov",
787
+ budget: { particles: 300, lights: 1 },
788
+ loop: 9.5,
789
+ view: { eye: [5, 3, 6.5], target: [0, 0.6, -0.5] },
790
+ // three.js draws 12 random numbers (the object ids of the bottle's geometry, material and mesh) the first time the page builds the bottle
791
+ setup: [cue("zombie_visible", { index: -1, visible: false }), when(FRESH, [cue("prop_created", { prop: "bottle" }, OBJECT_ID_DRAWS)])],
792
+ timeline: [set("landed", false), cue("bottle_visible", { visible: true })],
793
+ tick: [
794
+ when(lt(TIME, FLIGHT), [
795
+ set("bottle", vwith(vlerp(FROM, TARGET, TIME), 1, add(lerp2(FROM[1], TARGET[1], TIME), mul(sin(mul(TIME, Math.PI)), ARC_HEIGHT)))),
796
+ cue("bottle_move", { position: variable("bottle"), spin: mul(10, DELTA) }),
797
+ emit("wick", "add", 40, { p: vadd(variable("bottle"), [0, 0.12, 0]), r: 0.03, sp: [0.1, 0.3], life: [0.15, 0.3], size: [0.08, 0.14], grow: 0.5, c0: 16765562, c1: 16732176, fi: 0, g: -1 }),
798
+ emit("wsm", "normal", 14, { p: variable("bottle"), size: [0.06, 0.1], grow: 4, life: [0.8, 1.2], c0: 3814704, a: 0.3, sp: [0, 0.2] })
799
+ ], [
800
+ when(not(variable("landed")), [
801
+ set("landed", true),
802
+ cue("bottle_visible", { visible: false }),
803
+ repeat(scaled(12), debris({ p: at([0, 0.1, 0]), dir: UP6, cone: 1.3, sp: [1, 3], size: [0.04, 0.01, 0.04], c: GLASS_GREEN, life: 12 })),
804
+ burst("add", 45, { p: at([0, 0.15, 0]), dir: UP6, cone: 1.35, sp: [2, 4], drag: 2, life: [0.3, 0.6], size: [0.3, 0.5], c0: 16765562, c1: 10493960, fi: 0.05 }),
805
+ flash(at([0, 0.6, 0]), 16747056, 6, 0.3, 12, true),
806
+ decal(TARGET[0], 0, TARGET[2], 4.2, 854793, { grow: 0.8, life: 60 })
807
+ ])
808
+ ]),
809
+ when(and(gt(TIME, FLIGHT), lt(TIME, 8.2)), [
810
+ set("poolRadius", min(2.2, add(0.5, mul(sub(TIME, 1), 2.1)))),
811
+ set("poolFade", ite(gt(TIME, 7), max(0.1, sub(1, div(sub(TIME, 7), 1.2))), 1)),
812
+ call2("fire_emit", "mol", at([0, 0.05, 0]), variable("poolRadius"), mul(1.6, variable("poolFade")), { smoke: 16 }),
813
+ fireLight(0, at([0, 1, 0]), mul(3.2, variable("poolFade")), TIME)
814
+ ], [
815
+ when(ge(TIME, 8.2), [
816
+ fireLightOff(0),
817
+ emit("emb", "add", 6, { p: TARGET, r: 2, flat: true, size: [0.04, 0.07], life: [0.6, 1.2], c0: 16738848, a: 0.8, fi: 0.3 })
818
+ ])
819
+ ])
820
+ ]
821
+ };
822
+ var BODIES = [[1.6, 0.8], [-1.8, 1.2], [0.6, -2], [-1, -1.8], [2.4, -1.2]];
823
+ var BODY_X = BODIES.map(([x]) => x);
824
+ var BODY_Z2 = BODIES.map(([, z]) => z);
825
+ var FACING = BODIES.map(([x, z]) => Math.atan2(-x, -z));
826
+ var FUSE = 1.8;
827
+ var GRAVITY = 9.8;
828
+ var FLOOR = 0.07;
829
+ var BOUNCE = -0.4;
830
+ var SIDE_LOSS = 0.5;
831
+ var GRENADE = {
832
+ id: "grenade",
833
+ pageId: "granada",
834
+ team: "weapons",
835
+ lab: "m67",
836
+ budget: { particles: 400, debris: 16, decals: 1 },
837
+ loop: 6.5,
838
+ view: { eye: [7, 3.4, 7.5], target: [0, 0.8, -0.2] },
839
+ setup: [
840
+ cue("zombie_visible", { index: -1, visible: false }),
841
+ // three.js draws 12 random numbers (the object ids of the grenade's geometry, material and mesh) the first time the page builds it
842
+ when(FRESH, [cue("prop_created", { prop: "grenade" }, 12)]),
843
+ repeat(BODIES.length, cue("zombie_place", { index: INDEX, x: pick(BODY_X, INDEX), z: pick(BODY_Z2, INDEX), turn: pick(FACING, INDEX) }))
844
+ ],
845
+ timeline: [
846
+ set("px", -4),
847
+ set("py", 1.4),
848
+ set("pz", 4.5),
849
+ set("vx", 3.2),
850
+ set("vy", 2.4),
851
+ set("vz", -3.6),
852
+ set("boom", false),
853
+ cue("grenade_visible", { visible: true })
854
+ ],
855
+ tick: [
856
+ when(lt(TIME, FUSE), [
857
+ set("vy", sub(variable("vy"), mul(GRAVITY, DELTA))),
858
+ set("px", add(variable("px"), mul(variable("vx"), DELTA))),
859
+ set("py", add(variable("py"), mul(variable("vy"), DELTA))),
860
+ set("pz", add(variable("pz"), mul(variable("vz"), DELTA))),
861
+ when(lt(variable("py"), FLOOR), [
862
+ set("py", FLOOR),
863
+ set("vy", mul(variable("vy"), BOUNCE)),
864
+ set("vx", mul(variable("vx"), SIDE_LOSS)),
865
+ set("vz", mul(variable("vz"), SIDE_LOSS))
866
+ ]),
867
+ cue("grenade_move", { position: [variable("px"), variable("py"), variable("pz")] })
868
+ ], [
869
+ when(not(variable("boom")), [
870
+ set("boom", true),
871
+ cue("grenade_visible", { visible: false }),
872
+ call2("explode", [variable("px"), 0, variable("pz")], 1),
873
+ repeat(
874
+ BODIES.length,
875
+ cue("zombie_turn_and_fall", {
876
+ index: INDEX,
877
+ turn: atan2(neg(sub(pick(BODY_X, INDEX), variable("px"))), neg(sub(pick(BODY_Z2, INDEX), variable("pz")))),
878
+ delay: 0.02,
879
+ duration: 0.45
880
+ })
881
+ )
882
+ ])
883
+ ])
884
+ ]
885
+ };
886
+ var BARREL = [-4.4, 0, 1.4];
887
+ var UP7 = [0, 1, 0];
888
+ var RED = 10165272;
889
+ var YELLOW = 14200874;
890
+ var POOLS = 3;
891
+ var above = (height) => vadd(BARREL, [0, height, 0]);
892
+ var EXPLOSIVE_BARREL = {
893
+ id: "explosive_barrel",
894
+ pageId: "barril",
895
+ team: "weapons",
896
+ lab: "barrel",
897
+ budget: { particles: 450, lights: 1 },
898
+ loop: 9,
899
+ view: { eye: [2.4, 3, 7.5], target: [-4.4, 1.6, 1.4] },
900
+ setup: [cue("zombie_visible", { index: -1, visible: false })],
901
+ timeline: [
902
+ set("pools", []),
903
+ after(
904
+ 0.4,
905
+ cue("barrel_visible", { visible: false }),
906
+ call2("explode", BARREL, 1.3, RED),
907
+ burst("add", 30, { p: above(0.8), r: 0.4, dir: UP7, cone: 0.25, sp: [3, 5], drag: 1.2, life: [1, 1.6], size: [0.9, 1.4], grow: 1.4, c0: 16756816, c1: 3804421, g: -1, fi: 0.05 }),
908
+ debris({ p: above(1), v: [0.6, 9, 0.3], sp: 0, size: [0.7, 0.05, 0.7], c: RED, spin: 8, life: 12 }),
909
+ repeat(scaled(8), debris({ p: BARREL, dir: UP7, cone: 1, sp: [3, 7], size: [0.25, 0.04, 0.18], c: ite(mod(INDEX, 2), RED, YELLOW), life: 12 })),
910
+ repeat(
911
+ POOLS,
912
+ set("poolAngle", add(mul(INDEX, 2.1), rnd(0, 0.6))),
913
+ append("pools", vadd(BARREL, [mul(cos(variable("poolAngle")), 1.4), 0.05, mul(sin(variable("poolAngle")), 1.4)]))
914
+ )
915
+ )
916
+ ],
917
+ tick: [
918
+ when(and(variable("pools"), gt(TIME, 0.5), lt(TIME, 6)), [
919
+ set("poolFade", ite(gt(TIME, 5), sub(1, sub(TIME, 5)), 1)),
920
+ repeat(POOLS, call2("fire_emit", concat("bp", INDEX), { $: "at", a: [variable("pools"), INDEX] }, 0.45, mul(0.6, variable("poolFade")), { smoke: 6 })),
921
+ fireLight(0, above(1), mul(2.6, variable("poolFade")), TIME)
922
+ ]),
923
+ when(and(gt(TIME, 0.6), lt(TIME, 8)), [
924
+ emit("col", "normal", 6, { p: above(1.5), r: 0.6, dir: UP7, cone: 0.2, sp: [1.5, 2.5], v: [0.4, 0, 0], drag: 0.05, life: [4, 6], size: [1.2, 1.8], grow: 3, c0: 1710103, c1: 789776, a: 0.5, fi: 0.2 })
925
+ ]),
926
+ when(ge(TIME, 6), [fireLightOff(0)])
927
+ ]
928
+ };
929
+ var WEAPONS = ["glock", "ak", "r870", "barrett"];
930
+ var TIP2 = [-0.8, 1.38, 3.4 - 0.78];
931
+ var PORT2 = [-0.76, 1.4, 3.32];
932
+ var FORWARD3 = [0, 0, -1];
933
+ var NORMAL = [0, 0, 1];
934
+ var WALL_Z2 = -4.45;
935
+ var SHOTS = [[0, 0.45, 0.9], [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6], [0], [0]];
936
+ var SHOT_TIME = pick(pick(SHOTS, PARAM), INDEX);
937
+ var AK = 1;
938
+ var SHOTGUN = 2;
939
+ var BARRETT = 3;
940
+ var MUZZLE_FLASH = {
941
+ id: "muzzle_flash",
942
+ pageId: "muzzle",
943
+ team: "weapons",
944
+ params: { id: "weapon", options: ["glock", "ak", "r870", "barrett"], default: 1 },
945
+ budget: { particles: 60, lights: 1 },
946
+ loop: 2.4,
947
+ view: { eye: [1.8, 1.8, 5.2], target: [-0.8, 1.3, 1.4] },
948
+ timeline: [
949
+ repeat(
950
+ param(3, 7, 1, 1),
951
+ after(
952
+ add(0.2, SHOT_TIME),
953
+ call2("muzzle", pick(WEAPONS, PARAM), TIP2, FORWARD3),
954
+ when(ne(PARAM, SHOTGUN), [call2("casing", PORT2, ite(eq(PARAM, BARRETT), "50", ""))]),
955
+ set("target", [add(-0.8, rnd(-0.4, 0.4)), add(1.4, rnd(-0.3, 0.3)), WALL_Z2]),
956
+ when(
957
+ eq(PARAM, SHOTGUN),
958
+ [repeat(8, call2("impact", "concrete", [add(-0.8, rnd(-0.5, 0.5)), add(1.4, rnd(-0.4, 0.4)), WALL_Z2], NORMAL, 0.35))],
959
+ [call2("impact", "concrete", variable("target"), NORMAL, ite(eq(PARAM, BARRETT), 2, 1))]
960
+ ),
961
+ when(and(eq(PARAM, AK), eq(mod(round(mul(SHOT_TIME, 10)), 3), 0)), [spawn("streak", { p: TIP2, dir: FORWARD3, cone: 0, sp: 90, life: 0.09, len: 0.025, c0: 16752704, a: 1 })])
962
+ )
963
+ ),
964
+ when(eq(PARAM, SHOTGUN), [after(0.75, call2("casing", PORT2, "shell"))])
965
+ ]
966
+ };
967
+ var FACING2 = [0, 0, 1];
968
+ var SHOPFRONT_GLASS = {
969
+ id: "shopfront_glass",
970
+ pageId: "vidro",
971
+ team: "destruction",
972
+ budget: { debris: 80, decals: 1 },
973
+ loop: 5,
974
+ view: { eye: [-2.4, 1.8, 1.2], target: [-5, 1.4, -4] },
975
+ timeline: [
976
+ after(0.3, call2("impact", "glass", [-5.4, 2.2, -4.45], FACING2), behavior("cracks", "crack", { x: 97, y: 59, big: false })),
977
+ after(1, call2("impact", "glass", [-4.5, 1.6, -4.45], FACING2), behavior("cracks", "crack", { x: 177, y: 108, big: true })),
978
+ after(
979
+ 1.8,
980
+ cue("glass_pane_gone"),
981
+ screen({ add: { shake: 0.08 } }),
982
+ repeat(
983
+ scaled(75),
984
+ debris({ p: [-5, 1.9, -4.42], box: [1.25, 0.85, 0.02], v: [rnd(-0.4, 0.4), rnd(-0.5, 0.8), rnd(0.6, 2)], sp: 0, size: [rnd(0.05, 0.18), 6e-3, rnd(0.05, 0.18)], c: 11061464, spin: 14, life: 30, bounce: 0.15 })
985
+ ),
986
+ burst("add", 30, { p: [-5, 1.9, -4.3], box: [1.2, 0.8, 0.05], size: [0.02, 0.05], life: [0.2, 0.6], c0: 16777215, a: 0.9, fi: 0.3 }),
987
+ burst("normal", 20, { p: [-5, 1.3, -4.2], box: [1.2, 0.5, 0.1], sp: [0.2, 0.6], drag: 2, life: [0.8, 1.4], size: [0.12, 0.2], grow: 3, c0: 12572386, a: 0.2 })
988
+ )
989
+ ]
990
+ };
991
+ function normalized(vector) {
992
+ const length = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1] + vector[2] * vector[2]) || 1;
993
+ const inverse = 1 / length;
994
+ return [vector[0] * inverse, vector[1] * inverse, vector[2] * inverse];
995
+ }
996
+ var FRAME_DUST = normalized([0, -0.3, 1]);
997
+ var SPLINTER_DIRECTION = normalized([0, 0.3, 1]);
998
+ var FALL_SECONDS = 0.4;
999
+ var blow = (heavy) => [
1000
+ cue("door_shake", { strength: 0.25 }),
1001
+ burst("normal", heavy ? 24 : 12, { p: [2, 2.3, -4.38], box: [0.6, 0.05, 0.02], dir: FRAME_DUST, cone: 0.6, sp: [0.2, 0.8], drag: 1.5, life: [0.8, 1.4], size: [0.08, 0.16], grow: 3, c0: 8024934, a: 0.45, g: 0.4 }),
1002
+ repeat(scaled(heavy ? 5 : 2), debris({ p: [add(2, rnd(-0.5, 0.5)), rnd(0.4, 2), -4.36], dir: SPLINTER_DIRECTION, cone: 0.6, sp: [1, 2.5], size: [0.015, 0.015, 0.1], c: 8017200, life: 12 }))
1003
+ ];
1004
+ var DOOR_STAGES = {
1005
+ id: "door_stages",
1006
+ pageId: "porta",
1007
+ team: "destruction",
1008
+ budget: { particles: 120, debris: 6 },
1009
+ loop: 7.5,
1010
+ view: { eye: [4.2, 1.7, 1.4], target: [2, 1.1, -4] },
1011
+ setup: [behavior("walkers", "place", { index: 0, x: 2, z: -5.4, facing: 0 })],
1012
+ timeline: [
1013
+ after(0.5, ...blow(false)),
1014
+ after(1.2, ...blow(false)),
1015
+ after(1.9, ...blow(true), cue("door_plank_crack", { stage: 1 })),
1016
+ after(2.7, ...blow(false)),
1017
+ after(
1018
+ 3.3,
1019
+ ...blow(true),
1020
+ cue("door_plank_fall", { stage: 2 }),
1021
+ repeat(3, debris({ p: [add(2, mul(sub(INDEX, 1), 0.4)), 1.1, -4.3], v: [rnd(-0.3, 0.3), 0.5, 1.2], sp: 0, size: [0.38, 0.68, 0.07], c: 5913634, spin: 4, life: 20, bounce: 0.15 }))
1022
+ ),
1023
+ after(4.4, set("fall", 0), set("falling", true), cue("door_fall", { stage: 3 })),
1024
+ after(4.6, behavior("walkers", "walk", { index: 0, to: [2, 0, -2.2], speed: 0.9 }))
1025
+ ],
1026
+ tick: [
1027
+ when(variable("falling"), [
1028
+ set("fall", add(variable("fall"), DELTA)),
1029
+ set("progress", min(1, div(variable("fall"), FALL_SECONDS))),
1030
+ when(and(ge(variable("progress"), 1), not(variable("landed"))), [
1031
+ set("landed", true),
1032
+ screen({ add: { shake: 0.2 } }),
1033
+ burst("normal", 45, { p: [2, 0.15, -3.4], box: [0.7, 0.05, 1], dir: [0, 1, 0], cone: 1.4, sp: [0.6, 2], drag: 1.8, life: [1.5, 2.8], size: [0.25, 0.5], grow: 3, c0: 6972506, a: 0.45 })
1034
+ ])
1035
+ ])
1036
+ ]
1037
+ };
1038
+ var CENTRE2 = [-1.4, 3.35, -3.75];
1039
+ var BUILDING_COLLAPSE = {
1040
+ id: "building_collapse",
1041
+ pageId: "desaba",
1042
+ team: "destruction",
1043
+ budget: { particles: 350, debris: 26 },
1044
+ loop: 8,
1045
+ view: { eye: [4, 2.4, 5], target: [-1.4, 1.4, -2.8] },
1046
+ setup: [cue("zombie_visible", { index: -1, visible: false })],
1047
+ timeline: [
1048
+ after(
1049
+ 1.5,
1050
+ cue("slab_gone"),
1051
+ screen({ add: { shake: 0.3 } }),
1052
+ repeat(6, debris({ p: CENTRE2, box: [2, 0.05, 0.6], v: [rnd(-0.5, 0.5), rnd(-0.5, 0), rnd(0, 0.8)], sp: 0, size: [rnd(0.5, 1.1), 0.25, rnd(0.4, 0.8)], c: 7236195, spin: 3, life: 30, bounce: 0.1 })),
1053
+ repeat(scaled(20), debris({ p: CENTRE2, box: [2.1, 0.1, 0.7], v: [rnd(-1, 1), rnd(-1, 1), rnd(0, 1.5)], sp: 0, size: [0.12, 0.1, 0.12], k: [0.5, 1.5], c: 6183508, life: 30 }))
1054
+ ),
1055
+ after(
1056
+ 2.15,
1057
+ screen({ add: { shake: 0.45 } }),
1058
+ repeat(
1059
+ scaled(70),
1060
+ spawn("normal", local("angle", rnd(-0.4, 3.5), {
1061
+ p: [add(-1.4, rnd(-2, 2)), 0.2, add(-3.4, rnd(-0.5, 0.5))],
1062
+ dir: vnorm([mul(cos(variable("angle")), 0.6), 0.25, abs(sin(variable("angle")))]),
1063
+ cone: 0.5,
1064
+ sp: [1, 3.2],
1065
+ drag: 1.1,
1066
+ life: [3, 6],
1067
+ size: [0.7, 1.2],
1068
+ grow: 3,
1069
+ c0: 8024680,
1070
+ c1: 4867648,
1071
+ a: 0.5,
1072
+ fi: 0.12
1073
+ }))
1074
+ )
1075
+ )
1076
+ ],
1077
+ tick: [
1078
+ when(and(gt(TIME, 0.3), lt(TIME, 1.5)), [
1079
+ emit("trk", "normal", 30, { p: [add(-1.4, rnd(-2, 2)), 3.2, -3.05], dir: [0, -1, 0], cone: 0.1, sp: [0.2, 0.5], life: [1, 1.6], size: [0.04, 0.08], grow: 2.5, c0: 9077880, a: 0.5, g: 1 }),
1080
+ when(lt(rand(), mul(DELTA, 6)), [debris({ p: [add(-1.4, rnd(-2, 2)), 3.2, -3.05], sp: 0.2, size: [0.04, 0.04, 0.04], c: 7236195, life: 10 })])
1081
+ ])
1082
+ ]
1083
+ };
1084
+ var FIRING_FROM = [0.6, 1.3, 7];
1085
+ var FIRING_DIRECTION = normalized([-2.8, -0.4, -4.4]);
1086
+ var WALL_CENTRE = [-2.2, 1, 2.6];
1087
+ var BLOCK_WALL = {
1088
+ id: "block_wall",
1089
+ pageId: "parede",
1090
+ team: "destruction",
1091
+ budget: { particles: 300, debris: 16 },
1092
+ loop: 8,
1093
+ view: { eye: [0.4, 1.7, 6.6], target: [-2.2, 0.9, 2.4] },
1094
+ props: ["blocks"],
1095
+ setup: [cue("zombie_visible", { index: -1, visible: false }), cue("work_light", { intensity: 2.2 })],
1096
+ timeline: [
1097
+ repeat(
1098
+ 14,
1099
+ after(
1100
+ add(0.3, mul(INDEX, 0.11)),
1101
+ call2("muzzle", "ak", FIRING_FROM, FIRING_DIRECTION),
1102
+ behavior("blocks", "hit", { x: rnd(-1.3, 1.3), y: rnd(0.4, 1.9), scale: 1 })
1103
+ )
1104
+ ),
1105
+ after(2.2, behavior("blocks", "hit", { x: -0.2, y: 1.1, scale: 2.2 })),
1106
+ after(
1107
+ 3,
1108
+ screen({ add: { shake: 0.4 } }),
1109
+ behavior("blocks", "collapse", { x: -0.2, y: 1.1 }),
1110
+ burst("normal", 30, { p: WALL_CENTRE, box: [1.4, 0.8, 0.2], sp: [0.3, 1.2], drag: 1.5, life: [1.5, 2.6], size: [0.4, 0.7], grow: 3, c0: 9078138, c1: 4867648, a: 0.45 }),
1111
+ repeat(scaled(16), debris({ p: WALL_CENTRE, box: [1.3, 0.8, 0.1], sp: [1, 3], size: [0.08, 0.07, 0.08], k: [0.5, 1.5], c: 7236195, life: 20 }))
1112
+ ),
1113
+ after(
1114
+ 3.6,
1115
+ screen({ add: { shake: 0.25 } }),
1116
+ repeat(
1117
+ scaled(60),
1118
+ spawn("normal", local("angle", rnd(0, 6.283), {
1119
+ p: [add(-2.2, rnd(-1.4, 1.4)), 0.2, add(2.4, rnd(-0.6, 0.4))],
1120
+ dir: vnorm([cos(variable("angle")), 0.3, sin(variable("angle"))]),
1121
+ cone: 0.4,
1122
+ sp: [0.8, 2.4],
1123
+ drag: 1.3,
1124
+ life: [2.5, 4.5],
1125
+ size: [0.6, 1],
1126
+ grow: 3,
1127
+ c0: 8024680,
1128
+ c1: 4867648,
1129
+ a: 0.45,
1130
+ fi: 0.12
1131
+ }))
1132
+ )
1133
+ )
1134
+ ]
1135
+ };
1136
+ var SPOT = [-2.2, 0, 2.6];
1137
+ var UP8 = [0, 1, 0];
1138
+ var HITS = [[0.4, 1.4], [0.75, 1], [0.3, 0.7], [0.6, 1.7], [0.5, 1.2]];
1139
+ var HIT_X = HITS.map(([x]) => x);
1140
+ var HIT_Y = HITS.map(([, y]) => y);
1141
+ var SPARK_DIRECTION = normalized([0, 0.3, 1]);
1142
+ var CHIP_DIRECTION = normalized([0, 0.2, 1]);
1143
+ var HINGE_DIRECTION = normalized([1, 0.3, 1]);
1144
+ var SCRAPE_DIRECTION = normalized([0, 0.4, 1]);
1145
+ var FALL_SECONDS2 = 0.45;
1146
+ var SCRAPES = 5;
1147
+ var METAL_DOOR_EFFECT = {
1148
+ id: "metal_door",
1149
+ pageId: "portametal",
1150
+ team: "destruction",
1151
+ budget: { particles: 160, debris: 20 },
1152
+ loop: 7.5,
1153
+ view: { eye: [0.6, 1.6, 6.2], target: [-2.2, 1.1, 2.6] },
1154
+ props: ["plate"],
1155
+ setup: [
1156
+ cue("zombie_visible", { index: -1, visible: false }),
1157
+ cue("work_light", { intensity: 2.2 }),
1158
+ behavior("walkers", "place", { index: 3, x: SPOT[0] + 0.1, z: SPOT[2] - 0.7, facing: 0 })
1159
+ ],
1160
+ timeline: [
1161
+ repeat(
1162
+ HITS.length,
1163
+ after(
1164
+ add(0.5, mul(INDEX, 0.75)),
1165
+ cue("zombie_swing", { index: 3, side: mod(INDEX, 2) }),
1166
+ after(
1167
+ add(0.5, mul(INDEX, 0.75), 0.15),
1168
+ behavior("plate", "dent", { x: pick(HIT_X, INDEX), y: pick(HIT_Y, INDEX), radius: 0.42, depth: add(0.09, mul(INDEX, 0.025)) }),
1169
+ set("spot", [add(SPOT[0] - 0.56, pick(HIT_X, INDEX)), pick(HIT_Y, INDEX), SPOT[2] + 0.06]),
1170
+ call2("spark_at", variable("spot"), SPARK_DIRECTION, 8),
1171
+ repeat(scaled(5), debris({ p: variable("spot"), dir: CHIP_DIRECTION, cone: 0.9, sp: [0.8, 2.2], size: [0.035, 4e-3, 0.03], k: [0.6, 1.4], c: ite(mod(INDEX, 2), 5333594, 9080710), life: 20, spin: 16 })),
1172
+ call2("rust_at", variable("spot"), 6),
1173
+ cue("door_dent", { level: min(3, add(1, floor(div(INDEX, 1.7)))) })
1174
+ )
1175
+ )
1176
+ ),
1177
+ after(4.4, call2("spark_at", [SPOT[0] - 0.6, 2.05, SPOT[2] + 0.05], HINGE_DIRECTION, 30, 1.2), screen({ add: { shake: 0.15 } }), set("fall", 0), set("falling", true), cue("door_hinge_burst")),
1178
+ after(5.2, behavior("walkers", "walk", { index: 3, to: [SPOT[0], 0, SPOT[2] + 2.4], speed: 0.8 }))
1179
+ ],
1180
+ tick: [
1181
+ when(variable("falling"), [
1182
+ set("fall", add(variable("fall"), DELTA)),
1183
+ set("progress", min(1, div(variable("fall"), FALL_SECONDS2))),
1184
+ when(and(ge(variable("progress"), 1), not(variable("landed"))), [
1185
+ set("landed", true),
1186
+ screen({ add: { shake: 0.3 } }),
1187
+ repeat(SCRAPES, call2("spark_at", [add(SPOT[0] - 0.5, mul(INDEX, 0.27)), 0.03, SPOT[2] + 2.1], SCRAPE_DIRECTION, 8, 1)),
1188
+ burst("normal", 40, { p: [SPOT[0], 0.15, SPOT[2] + 1.2], box: [0.6, 0.05, 1], dir: UP8, cone: 1.4, sp: [0.6, 2], drag: 1.8, life: [1.4, 2.4], size: [0.25, 0.5], grow: 3, c0: 6972506, a: 0.45 })
1189
+ ])
1190
+ ])
1191
+ ]
1192
+ };
1193
+ var PUSHERS = [[-0.6, 0.45], [0.1, 0.5], [0.6, 0.42], [-0.1, 1]];
1194
+ var BEND_LIMIT = 0.42;
1195
+ var BEND_STEP = 4e-3;
1196
+ var FALL_SECONDS3 = 0.55;
1197
+ var WELD = normalized([0, 0.3, 1]);
1198
+ var IRON_RAILING = {
1199
+ id: "iron_railing",
1200
+ pageId: "grade",
1201
+ team: "destruction",
1202
+ budget: { particles: 200 },
1203
+ loop: 7.5,
1204
+ view: { eye: [1.4, 1.8, 6.4], target: [-2.2, 0.7, 2.4] },
1205
+ setup: [
1206
+ cue("zombie_visible", { index: -1, visible: false }),
1207
+ cue("work_light", { intensity: 2.2 }),
1208
+ set("bend", 0),
1209
+ ...PUSHERS.map(([x, dz], index) => behavior("walkers", "place", { index, x: SPOT[0] + x, z: SPOT[2] - dz, facing: 0 }))
1210
+ ],
1211
+ timeline: [
1212
+ after(1.6, call2("spark_at", [SPOT[0], 0.75, SPOT[2] + 0.05], WELD, 20, 1), screen({ add: { shake: 0.08 } }), cue("weld_burst")),
1213
+ after(3.2, call2("spark_at", [SPOT[0] + 0.02, 1.2, SPOT[2] + 0.1], WELD, 20, 1), screen({ add: { shake: 0.1 } })),
1214
+ after(
1215
+ 4.3,
1216
+ set("fall", 0),
1217
+ set("falling", true),
1218
+ call2("spark_at", [SPOT[0] - 1.5, 0.25, SPOT[2]], normalized([-0.3, 0.5, 1]), 14, 1),
1219
+ call2("spark_at", [SPOT[0] + 1.5, 0.25, SPOT[2]], normalized([0.3, 0.5, 1]), 14, 1),
1220
+ cue("railing_gave_way")
1221
+ ),
1222
+ after(
1223
+ 4.9,
1224
+ repeat(PUSHERS.length, behavior("walkers", "walk", { index: INDEX, to: [add(SPOT[0], pick(PUSHERS.map(([x]) => x), INDEX)), 0, add(SPOT[2], 2.6, mul(INDEX, 0.3))], speed: 0.7 }))
1225
+ )
1226
+ ],
1227
+ tick: [
1228
+ when(lt(TIME, 4.3), [
1229
+ set("target", min(BEND_LIMIT, mul(TIME, 0.1))),
1230
+ when(gt(sub(variable("target"), variable("bend")), BEND_STEP), [set("bend", variable("target")), cue("railing_bend", { amount: variable("bend") })]),
1231
+ emit("rust", "normal", 10, { p: [add(SPOT[0], rnd(-0.6, 0.6)), rnd(0.3, 1.2), add(SPOT[2], mul(variable("bend"), 0.8))], sp: [0, 0.2], life: [0.8, 1.2], size: [0.015, 0.03], c0: 8014378, a: 0.9, g: 6, fi: 0, gnd: 1 })
1232
+ ]),
1233
+ when(variable("falling"), [
1234
+ set("fall", add(variable("fall"), DELTA)),
1235
+ set("progress", min(1, div(variable("fall"), FALL_SECONDS3))),
1236
+ when(and(ge(variable("progress"), 1), not(variable("landed"))), [
1237
+ set("landed", true),
1238
+ screen({ add: { shake: 0.25 } }),
1239
+ repeat(4, call2("spark_at", [add(SPOT[0] - 1.2, mul(INDEX, 0.8)), 0.04, SPOT[2] + 1.2], normalized([0, 0.5, 1]), 6, 1)),
1240
+ burst("normal", 30, { p: [SPOT[0], 0.12, SPOT[2] + 0.8], box: [1.4, 0.05, 0.5], dir: UP8, cone: 1.4, sp: [0.5, 1.6], drag: 1.8, life: [1.2, 2.2], size: [0.2, 0.4], grow: 3, c0: 6972506, a: 0.4 })
1241
+ ])
1242
+ ])
1243
+ ]
1244
+ };
1245
+ var ORDER = [3, 4, 8, 5, 9, 2, 7];
1246
+ var PLANK_X = Array.from({ length: 12 }, (_, index) => -1.43 + index * 0.26);
1247
+ var BODIES2 = [[-0.8, 0.6], [0.5, 0.7], [0, 1.2]];
1248
+ var SPLINTER_DIRECTION2 = normalized([0, 0.4, 1]);
1249
+ var FALL_SECONDS4 = 0.5;
1250
+ var PLANK_HEIGHT = 1.3;
1251
+ var WOODEN_FENCE = {
1252
+ id: "wooden_fence",
1253
+ pageId: "cerca",
1254
+ team: "destruction",
1255
+ budget: { particles: 150, debris: 12 },
1256
+ loop: 7.5,
1257
+ view: { eye: [1, 1.6, 6], target: [-2.2, 0.7, 2.4] },
1258
+ setup: [
1259
+ cue("zombie_visible", { index: -1, visible: false }),
1260
+ cue("work_light", { intensity: 2.2 }),
1261
+ ...BODIES2.map(([x, dz], index) => behavior("walkers", "place", { index, x: SPOT[0] + x, z: SPOT[2] - dz, facing: 0 }))
1262
+ ],
1263
+ timeline: [
1264
+ repeat(
1265
+ ORDER.length,
1266
+ after(
1267
+ add(0.4, mul(INDEX, 0.5)),
1268
+ set("cut", rnd(0.32, 0.6)),
1269
+ set("top", sub(PLANK_HEIGHT, variable("cut"))),
1270
+ set("break", [add(SPOT[0], pick(PLANK_X, pick(ORDER, INDEX))), variable("cut"), SPOT[2]]),
1271
+ cue("zombie_swing", { index: mod(INDEX, 3), side: mod(INDEX, 2) }),
1272
+ cue("plank_snap", { plank: pick(ORDER, INDEX), cut: variable("cut") }),
1273
+ debris({ p: vadd(variable("break"), [0, div(variable("top"), 2), 0]), v: [rnd(-0.8, 0.8), rnd(1.5, 3), rnd(1.5, 3)], sp: 0, size: [0.17, variable("top"), 0.035], c: 9071172, spin: 7, life: 20, bounce: 0.2 }),
1274
+ repeat(scaled(6), debris({ p: variable("break"), dir: SPLINTER_DIRECTION2, cone: 0.9, sp: [1, 3], size: [0.012, 0.012, 0.1], k: [0.6, 1.4], c: 11569754, life: 20 })),
1275
+ burst("normal", 8, { p: variable("break"), dir: [0, 0, 1], cone: 0.8, sp: [0.4, 1.4], drag: 2, life: [0.8, 1.4], size: [0.1, 0.18], grow: 3, c0: 8020040, a: 0.4 })
1276
+ )
1277
+ ),
1278
+ after(
1279
+ 4.2,
1280
+ set("fall", 0),
1281
+ set("falling", true),
1282
+ repeat(scaled(10), debris({ p: [add(SPOT[0], rnd(-1.4, 1.4)), 1, SPOT[2]], dir: SPLINTER_DIRECTION2, cone: 0.9, sp: [1, 3], size: [0.012, 0.012, 0.1], c: 11569754, life: 20 })),
1283
+ cue("fence_rail_cracked")
1284
+ ),
1285
+ after(4.8, repeat(BODIES2.length, behavior("walkers", "walk", { index: INDEX, to: [add(SPOT[0], pick(BODIES2.map(([x]) => x), INDEX)), 0, SPOT[2] + 2.5], speed: 0.75 })))
1286
+ ],
1287
+ tick: [
1288
+ when(variable("falling"), [
1289
+ set("fall", add(variable("fall"), DELTA)),
1290
+ set("progress", min(1, div(variable("fall"), FALL_SECONDS4))),
1291
+ when(and(ge(variable("progress"), 1), not(variable("landed"))), [
1292
+ set("landed", true),
1293
+ screen({ add: { shake: 0.2 } }),
1294
+ burst("normal", 35, { p: [SPOT[0], 0.12, SPOT[2] + 0.8], box: [1.5, 0.05, 0.5], dir: UP8, cone: 1.4, sp: [0.5, 1.6], drag: 1.8, life: [1.2, 2.2], size: [0.25, 0.45], grow: 3, c0: 6969928, a: 0.4 })
1295
+ ])
1296
+ ])
1297
+ ]
1298
+ };
1299
+ var at2 = (x, y, z) => [SPOT[0] + x, SPOT[1] + y, SPOT[2] + z];
1300
+ var FRONT_LEG = at2(-0.52, 0.2, 0.32);
1301
+ var TOP_CORNER = at2(0.3, 0.78, 0.2);
1302
+ var TOP_PIECE = at2(-0.3, 0.78, -0.2);
1303
+ var FAR_CORNER = at2(0.3, 0.78, -0.2);
1304
+ var HOLE_OBJECT_ID_DRAWS = 12;
1305
+ var HOLES = 9;
1306
+ var GLOCK_FROM = [-0.4, 1, 6];
1307
+ var SHOTGUN_FROM = [0.2, 1.1, 4.6];
1308
+ var toward = (target, from) => normalized([target[0] - from[0], target[1] - from[1], target[2] - from[2]]);
1309
+ var HIT_POINT_BREAKAGE = {
1310
+ id: "hit_point_breakage",
1311
+ pageId: "pontos",
1312
+ team: "destruction",
1313
+ budget: { particles: 120, debris: 20 },
1314
+ loop: 7.5,
1315
+ view: { eye: [0.2, 1.6, 5.4], target: [-2.2, 0.6, 2.6] },
1316
+ setup: [cue("zombie_visible", { index: -1, visible: false }), cue("work_light", { intensity: 2.2 })],
1317
+ timeline: [
1318
+ after(
1319
+ 0.6,
1320
+ call2("muzzle", "glock", GLOCK_FROM, toward(FRONT_LEG, GLOCK_FROM)),
1321
+ call2("chips", FRONT_LEG, normalized([0, 0.2, 1]), 6),
1322
+ cue("table_part_detached", { part: "front_leg" }),
1323
+ debris({ p: FRONT_LEG, v: [0.4, 0.6, 1.4], sp: 0, size: [0.07, 0.4, 0.07], c: 8017200, spin: 6, life: 20, bounce: 0.25 }),
1324
+ cue("table_tilt")
1325
+ ),
1326
+ after(
1327
+ 2.2,
1328
+ repeat(8, call2("chips", vadd(TOP_CORNER, [add(-0.3, mul(INDEX, 0.08)), 0.03, 0]), normalized([0, 0.6, 1]), 1, 0.4)),
1329
+ cue("table_part_detached", { part: "top_corner" }),
1330
+ debris({ p: TOP_CORNER, v: [1.2, 1.5, 1], sp: 0, size: [0.6, 0.06, 0.4], c: 8017200, spin: 6, life: 20, bounce: 0.25 })
1331
+ ),
1332
+ after(
1333
+ 3.8,
1334
+ screen({ add: { shake: 0.12 } }),
1335
+ call2("chips", TOP_PIECE, UP8, 10, 1.3),
1336
+ cue("table_part_gone", { part: "top_piece" }),
1337
+ repeat(4, debris({ p: [add(TOP_PIECE[0], rnd(-0.2, 0.2)), TOP_PIECE[1], add(TOP_PIECE[2], rnd(-0.1, 0.1))], dir: UP8, cone: 1.2, sp: [1, 2.5], size: [rnd(0.15, 0.3), 0.06, rnd(0.12, 0.25)], c: 7031338, spin: 8, life: 20, bounce: 0.2 })),
1338
+ burst("normal", 20, { p: TOP_PIECE, r: 0.2, sp: [0.3, 1], drag: 2, life: [1, 1.8], size: [0.2, 0.35], grow: 3, c0: 8020040, a: 0.4 })
1339
+ ),
1340
+ after(
1341
+ 5.3,
1342
+ call2("muzzle", "r870", SHOTGUN_FROM, toward(FAR_CORNER, SHOTGUN_FROM)),
1343
+ repeat(HOLES, set("holeX", rnd(-0.15, 0.15)), set("holeZ", rnd(-0.1, 0.1)), cue("table_hole", { x: variable("holeX"), z: variable("holeZ") }, HOLE_OBJECT_ID_DRAWS)),
1344
+ call2("chips", FAR_CORNER, UP8, 10, 1)
1345
+ )
1346
+ ]
1347
+ };
1348
+ var OXYGEN = 1;
1349
+ var PUNCTURE_AT = 0.6;
1350
+ var JET_SECONDS = 3.4;
1351
+ var CLOUD_UNTIL = 7;
1352
+ var SHOT_FROM = [0.4, 1.2, 6.4];
1353
+ var SHOT_DIRECTION = normalized([-0.5, -0.15, -1]);
1354
+ var SCRAPE_DIRECTION2 = normalized([1, 0.4, 0]);
1355
+ var jetStrength = variable("jet");
1356
+ var isOxygen = eq(PARAM, OXYGEN);
1357
+ var PRESSURE_CYLINDERS = {
1358
+ id: "pressure_cylinders",
1359
+ pageId: "pressao",
1360
+ team: "destruction",
1361
+ params: { id: "cylinder", options: ["extinguisher", "oxygen"], default: 0 },
1362
+ budget: { particles: 250 },
1363
+ loop: 7,
1364
+ view: { eye: [1.6, 1.5, 6.4], target: [-1.6, 0.4, 2.4] },
1365
+ props: ["cylinder"],
1366
+ setup: [cue("zombie_visible", { index: -1, visible: false }), cue("work_light", { intensity: 2.2 }), behavior("cylinder", "choose", { oxygen: isOxygen }), cue("cylinder_visible", { oxygen: isOxygen })],
1367
+ timeline: [
1368
+ after(
1369
+ PUNCTURE_AT,
1370
+ call2("muzzle", "glock", SHOT_FROM, SHOT_DIRECTION),
1371
+ screen({ add: { shake: 0.1 } }),
1372
+ behavior("cylinder", "puncture"),
1373
+ when(isOxygen, [call2("spark_at", [SPOT[0], 1.3, SPOT[2]], UP8, 20, 1.2)])
1374
+ )
1375
+ ],
1376
+ tick: [
1377
+ when(query("cylinder", "live"), [
1378
+ set("jet", clamp(sub(1, div(sub(TIME, PUNCTURE_AT), JET_SECONDS)), 0, 1)),
1379
+ behavior("cylinder", "step", { dt: DELTA, t: TIME }),
1380
+ when(not(isOxygen), [
1381
+ when(gt(jetStrength, 0), [
1382
+ emit("jet", "normal", 60, { p: query("cylinder", "tip"), dir: query("cylinder", "direction"), cone: 0.3, sp: [3, 6], drag: 2.5, life: [2, 3.5], size: [0.15, 0.3], grow: 6, c0: 15658730, c1: 12567496, a: 0.45, g: -0.05, fi: 0.05 })
1383
+ ]),
1384
+ when(lt(TIME, CLOUD_UNTIL), [
1385
+ emit("cloud", "normal", 4, { p: [SPOT[0], 0.6, SPOT[2]], r: 2.5, flat: true, sp: [0.05, 0.2], life: [3, 5], size: [1.5, 2.5], grow: 1.3, c0: 14869214, a: mul(0.18, min(1, sub(TIME, PUNCTURE_AT))), fi: 0.3 })
1386
+ ])
1387
+ ], [
1388
+ when(gt(jetStrength, 0), [
1389
+ emit("jet", "normal", 50, { p: query("cylinder", "back"), dir: [1, 0, 0], cone: 0.25, sp: [2, 4], drag: 3, life: [0.5, 0.9], size: [0.1, 0.2], grow: 4, c0: 14542574, a: 0.2 })
1390
+ ]),
1391
+ when(gt(abs(query("cylinder", "speed_x")), 0.5), [
1392
+ emit("scr", "streak", 120, { p: [vx(query("cylinder", "position")), 0.03, vz(query("cylinder", "position"))], dir: SCRAPE_DIRECTION2, cone: 0.6, sp: [2, 6], v: [mul(query("cylinder", "speed_x"), 0.5), 0, 0], life: [0.2, 0.45], len: 0.02, c0: 16773312, c1: 16732176, g: 9.8, gnd: 2 })
1393
+ ])
1394
+ ])
1395
+ ])
1396
+ ]
1397
+ };
1398
+ var WINDOWS = [[4.6, 5, -4.48], [-1.6, 5, -4.48]];
1399
+ var BUILDING_FIRE = {
1400
+ id: "building_fire",
1401
+ pageId: "incendio",
1402
+ team: "world",
1403
+ budget: { particles: 600, lights: 2 },
1404
+ loop: 0,
1405
+ view: { eye: [3, 2.4, 6.5], target: [1.5, 4, -4] },
1406
+ setup: [cue("zombie_visible", { index: -1, visible: false })],
1407
+ timeline: [],
1408
+ tick: [
1409
+ repeat(
1410
+ WINDOWS.length,
1411
+ call2("fire_emit", concat("w", INDEX), vadd(pick(WINDOWS, INDEX), [0, 0.55, 0.12]), 0.55, 1.1, { dir: normalized([0, 1, 0.35]), wind: [0.9, 0, 0], smoke: 22, smokeAlpha: 0.5 }),
1412
+ when(eq(INDEX, 0), [fireLight(0, vadd(pick(WINDOWS, INDEX), [0, 0, 1.4]), 3, add(TIME, INDEX))], [fireLight(1, vadd(pick(WINDOWS, INDEX), [0, 0, 1.4]), 3, add(TIME, INDEX))])
1413
+ )
1414
+ ]
1415
+ };
1416
+ var SPOTS_X = [-3, 1, 5];
1417
+ var SPOTS_Z = [-1, -2.4, 0.4];
1418
+ var FOG_AND_ASH = {
1419
+ id: "fog_and_ash",
1420
+ pageId: "neblina",
1421
+ team: "world",
1422
+ budget: { particles: 120, lights: 1 },
1423
+ loop: 0,
1424
+ view: { eye: [3, 1.8, 7.5], target: [-3, 1.2, -1] },
1425
+ setup: [
1426
+ cue("fog", { density: 0.075 }),
1427
+ cue("lamp_cone", { visible: true }),
1428
+ cue("zombie_visible", { index: -1, visible: false }),
1429
+ repeat(
1430
+ SPOTS_X.length,
1431
+ set("turn", rnd(-0.6, 0.6)),
1432
+ behavior("walkers", "place", { index: INDEX, x: pick(SPOTS_X, INDEX), z: pick(SPOTS_Z, INDEX), facing: variable("turn") })
1433
+ )
1434
+ ],
1435
+ timeline: [],
1436
+ tick: [
1437
+ emit("fog", "normal", 5, { p: [rnd(-14, 10), rnd(0.2, 1.1), rnd(-3, 8)], v: [rnd(0.15, 0.35), 0, rnd(-0.05, 0.05)], sp: 0, life: [9, 13], size: [3, 5], grow: 1.4, c0: 8227488, a: 0.13, fi: 0.35 }),
1438
+ emit("ash", "normal", 14, { p: [rnd(-10, 8), rnd(4, 6), rnd(-3, 7)], v: [rnd(0.1, 0.4), -0.35, rnd(-0.1, 0.1)], sp: [0.05, 0.15], life: [8, 12], size: [0.025, 0.04], c0: 10525328, a: 0.75, fi: 0.1, gnd: 1 })
1439
+ ]
1440
+ };
1441
+ var WORKSHOP_PUDDLES = [
1442
+ [-1.5, 2.2, 1.6, 0.9],
1443
+ [3, 3.6, 1.1, 0.7],
1444
+ [-6, 3.2, 1.3, 0.8],
1445
+ [1.4, -2.2, 0.9, 0.5]
1446
+ ];
1447
+ var inPuddle = (puddles, x, z) => puddles.some((p) => ((x - p[0]) / p[2]) ** 2 + ((z - p[1]) / p[3]) ** 2 < 1);
1448
+ var DROPS = [900, 2200, 4e3];
1449
+ var SLANT = [0.3, 1.2, 4.5];
1450
+ var STORM = 2;
1451
+ var FLASH_STEPS = ite(lt(variable("flash"), 0.08), 1, ite(lt(variable("flash"), 0.16), 0.2, ite(lt(variable("flash"), 0.26), 0.8, 0)));
1452
+ var RAIN_AND_PUDDLES = {
1453
+ id: "rain_and_puddles",
1454
+ pageId: "chuva",
1455
+ team: "world",
1456
+ params: { id: "intensity", options: ["drizzle", "rain", "storm"], default: 1 },
1457
+ budget: { particles: 4e3, decals: 4 },
1458
+ loop: 0,
1459
+ view: { eye: [2, 2, 7], target: [-1, 0.6, 1] },
1460
+ setup: [
1461
+ cue("lamp_cone", { visible: true }),
1462
+ ...WORKSHOP_PUDDLES.map(([x, z, radiusX, radiusZ]) => decal(x, 0, z, radiusX * 2, 790550, { grow: 0, life: 1e6, ar: radiusZ / radiusX }))
1463
+ ],
1464
+ timeline: [
1465
+ behavior("rain", "init", { count: ite(eq(PARAM, 0), DROPS[0], ite(eq(PARAM, 1), DROPS[1], DROPS[2])), wind: ite(eq(PARAM, 0), SLANT[0], ite(eq(PARAM, 1), SLANT[1], SLANT[2])) }),
1466
+ set("nextStrike", rnd(2, 4)),
1467
+ set("flash", -1)
1468
+ ],
1469
+ tick: [
1470
+ behavior("rain", "update", { dt: DELTA, splash: gt(PARAM, 0) }),
1471
+ when(eq(PARAM, STORM), [
1472
+ when(gt(TIME, variable("nextStrike")), [set("nextStrike", add(TIME, rnd(3.5, 6))), set("flash", 0)]),
1473
+ when(ge(variable("flash"), 0), [
1474
+ set("flash", add(variable("flash"), DELTA)),
1475
+ set("light", FLASH_STEPS),
1476
+ cue("lightning", { intensity: variable("light") }),
1477
+ screen({ max: { white: mul(variable("light"), 0.12) } }),
1478
+ when(gt(variable("flash"), 0.4), [set("flash", -1), cue("lightning", { intensity: 0 })])
1479
+ ]),
1480
+ emit("sheet", "normal", 2.5, { p: [-12, 2.5, rnd(-1, 8)], box: [0, 2, 0], v: [7, -0.5, 0], sp: 0, life: [3, 4], size: [3, 5], grow: 1.3, c0: 9083568, a: 0.06, fi: 0.3 })
1481
+ ])
1482
+ ]
1483
+ };
1484
+ var CAR_ORIGIN2 = [5.6, 0, 0.8];
1485
+ var CAR_ROTATION2 = [0, -0.4, 0];
1486
+ var carPoint = (x, y, z) => localToWorld(CAR_ORIGIN2, CAR_ROTATION2, [x, y, z]);
1487
+ var carDirection = (from, to) => normalized([to[0] - from[0], to[1] - from[1], to[2] - from[2]]);
1488
+ var BEAM_FROM = carPoint(0, 0.74, 2.15);
1489
+ var BEAM_DIRECTION = carDirection(BEAM_FROM, carPoint(0, 0.74, 3.15));
1490
+ var BEAM_SIDE = [BEAM_DIRECTION[2], 0, -BEAM_DIRECTION[0]];
1491
+ var WALKERS = 6;
1492
+ var WALK_SPEED2 = 0.7;
1493
+ var DROPS2 = 1800;
1494
+ var SLANT2 = 0.8;
1495
+ var startWalking = [
1496
+ set("spot", query("car", "point", rnd(-2, 2), 0, rnd(8, 18))),
1497
+ set("goal", query("car", "point", rnd(-0.6, 0.6), 0, 2.6)),
1498
+ behavior("walkers", "place", { index: INDEX, x: pick(variable("spot"), 0), z: pick(variable("spot"), 2), facing: 0 }),
1499
+ behavior("walkers", "walk", { index: INDEX, to: variable("goal"), speed: WALK_SPEED2 })
1500
+ ];
1501
+ var HEADLIGHTS_IN_RAIN = {
1502
+ id: "headlights_in_rain",
1503
+ pageId: "farol",
1504
+ team: "vehicles",
1505
+ budget: { particles: 600, lights: 2 },
1506
+ loop: 0,
1507
+ view: { eye: [-2.5, 2, 5.5], target: [4, 1, 6] },
1508
+ setup: [
1509
+ cue("fog", { density: 0.05 }),
1510
+ cue("headlights", { on: true, intensity: 2.4, color: 16773584 }),
1511
+ behavior("rain", "init", { count: DROPS2, wind: SLANT2 }),
1512
+ repeat(WALKERS, ...startWalking)
1513
+ ],
1514
+ timeline: [],
1515
+ tick: [
1516
+ behavior("rain", "update", { dt: DELTA, splash: true }),
1517
+ emit(
1518
+ "lit",
1519
+ "streak",
1520
+ 600,
1521
+ local(
1522
+ "depth",
1523
+ rnd(1, 13),
1524
+ local(
1525
+ "spread",
1526
+ mul(variable("depth"), 0.33, sqrt(rand())),
1527
+ local(
1528
+ "turn",
1529
+ rnd(0, 6.283),
1530
+ local("base", vadd(BEAM_FROM, vscale(BEAM_DIRECTION, variable("depth")), vscale(BEAM_SIDE, mul(cos(variable("turn")), variable("spread")))), {
1531
+ p: vwith(variable("base"), 1, add(vy(variable("base")), mul(sin(variable("turn")), variable("spread"), 0.6))),
1532
+ v: [0.8, -14, 0],
1533
+ sp: 0,
1534
+ life: 0.06,
1535
+ len: 0.035,
1536
+ c0: 13622512,
1537
+ a: 0.9
1538
+ })
1539
+ )
1540
+ )
1541
+ )
1542
+ ),
1543
+ repeat(WALKERS, when(not(query("walkers", "is_walking", INDEX)), startWalking))
1544
+ ]
1545
+ };
1546
+ var YAW = -Math.PI / 2;
1547
+ var COS = Math.cos(YAW);
1548
+ var SIN = Math.sin(YAW);
1549
+ var START_X = 10;
1550
+ var SPEED3 = 2.4;
1551
+ var contactX = (carX) => add(COS * 0.6, 0 * 0.06, SIN * 2.15, carX);
1552
+ var contactZ = add(-SIN * 0.6, 0 * 0.06, COS * 2.15, 1.5);
1553
+ var BACKWARD = normalized([1, 0.35, 0]);
1554
+ var SCRAPING_SPARKS = {
1555
+ id: "scraping_sparks",
1556
+ pageId: "faisca",
1557
+ team: "vehicles",
1558
+ budget: { particles: 200, lights: 1 },
1559
+ loop: 4.4,
1560
+ view: { eye: [2, 1.6, 5.5], target: [3, 0.3, 1.5] },
1561
+ setup: [cue("zombie_visible", { index: -1, visible: false }), cue("car_pose", { x: START_X, z: 1.5, yaw: YAW })],
1562
+ timeline: [],
1563
+ tick: [
1564
+ set("carX", sub(START_X, mul(TIME, SPEED3))),
1565
+ set("contact", [contactX(variable("carX")), 0.04, contactZ]),
1566
+ cue("car_pose", { x: variable("carX"), z: 1.5, yaw: YAW }),
1567
+ emit("sp", "streak", 200, { p: variable("contact"), dir: BACKWARD, cone: 0.55, sp: [3, 8], v: [-2.4, 0, 0], life: [0.2, 0.5], len: 0.02, c0: 16773312, c1: 16732176, g: 9.8, gnd: 2 }),
1568
+ emit("gl", "add", 40, { p: variable("contact"), r: 0.05, size: [0.08, 0.16], life: 0.05, c0: 16767136, fi: 0 }),
1569
+ when(lt(rand(), mul(DELTA, 14)), [decal(vx(variable("contact")), 0, add(vz(variable("contact")), rnd(-0.05, 0.05)), 0.12, 657930, { grow: 0, life: 60, ar: 0.25 })]),
1570
+ fireLight(0, [vx(variable("contact")), 0.34, vz(variable("contact"))], 1.6, mul(TIME, 3))
1571
+ ]
1572
+ };
1573
+ var HOOD = carPoint(0, 1.02, 1.4);
1574
+ var EXHAUST = carPoint(0.5, 0.3, -2.2);
1575
+ var EXHAUST_DIRECTION = carDirection(EXHAUST, carPoint(0.5, 0.3, -3));
1576
+ var SPARK_FROM = carPoint(0, 0.7, 2.1);
1577
+ var SPARK_DIRECTION2 = carDirection(SPARK_FROM, carPoint(0, 0.9, 3));
1578
+ var WORN = 1;
1579
+ var BAD = 2;
1580
+ var CRITICAL = 3;
1581
+ var ENGINE_SMOKE = {
1582
+ id: "engine_smoke",
1583
+ pageId: "motor",
1584
+ team: "vehicles",
1585
+ params: { id: "condition", options: ["100", "60", "30", "10"], default: 2 },
1586
+ budget: { particles: 120, lights: 1 },
1587
+ loop: 0,
1588
+ view: { eye: [9.5, 2.6, 5.2], target: [5.2, 0.9, 1.5] },
1589
+ timeline: [],
1590
+ tick: [
1591
+ emit("ex", "normal", ite(eq(PARAM, CRITICAL), 6, 3), { p: EXHAUST, dir: EXHAUST_DIRECTION, cone: 0.3, sp: [0.6, 1.2], drag: 1.2, life: [1, 1.8], size: [0.06, 0.12], grow: 5, c0: ite(gt(PARAM, 1), 5591630, 10132118), a: 0.18, g: -0.2 }),
1592
+ when(eq(PARAM, WORN), [
1593
+ emit("st", "normal", 14, { p: HOOD, box: [0.4, 0, 0.3], dir: [0, 1, 0], cone: 0.35, sp: [0.5, 1], drag: 0.8, life: [1.4, 2.4], size: [0.15, 0.25], grow: 4, c0: 13159632, a: 0.22, g: -0.3 })
1594
+ ]),
1595
+ when(eq(PARAM, BAD), [
1596
+ emit("st", "normal", 22, { p: HOOD, box: [0.4, 0, 0.3], dir: [0, 1, 0], cone: 0.35, sp: [0.6, 1.2], drag: 0.6, life: [2, 3], size: [0.2, 0.35], grow: 4, c0: 6974056, c1: 3815996, a: 0.4, g: -0.35 }),
1597
+ when(lt(rand(), mul(DELTA, 0.8)), [burst("streak", 8, { p: SPARK_FROM, dir: SPARK_DIRECTION2, cone: 0.6, sp: [2, 4], life: 0.3, len: 0.02, c0: 16765056, c1: 16728064, g: 9.8 })])
1598
+ ]),
1599
+ when(eq(PARAM, CRITICAL), [
1600
+ emit("st", "normal", 34, { p: HOOD, box: [0.5, 0, 0.4], dir: [0, 1, 0], cone: 0.3, sp: [0.8, 1.6], drag: 0.4, life: [2.5, 3.8], size: [0.3, 0.5], grow: 4.5, c0: 1841947, c1: 921104, a: 0.6, g: -0.4 }),
1601
+ call2("fire_emit", "hood", carPoint(0, 0.8, 2), 0.3, 0.45, { noSmoke: true }),
1602
+ fireLight(0, carPoint(0, 1.2, 2.6), 2.2, TIME)
1603
+ ], [fireLightOff(0)])
1604
+ ]
1605
+ };
1606
+ var UP10 = [0, 1, 0];
1607
+ var doorVelocity = (side) => {
1608
+ const out = carPoint(side * 4, 3, 0.3);
1609
+ const centre = carPoint(0, 0, 0.3);
1610
+ return [out[0] - centre[0], 3, out[2] - centre[2]];
1611
+ };
1612
+ var VEHICLE_BOOM = [
1613
+ call2("explode", vadd(query("car", "position"), [0, 0.6, 0]), 1.8, 2759188),
1614
+ cue("car_burnt"),
1615
+ behavior("car", "launch", { rise: 4.5, spin: 0.9 }),
1616
+ repeat(scaled(30), debris({ p: vadd(query("car", "position"), [0, 1.3, 0]), dir: UP10, cone: 1.3, sp: [2, 6], size: [0.06, 6e-3, 0.06], c: 11061464, life: 20 })),
1617
+ debris({ p: carPoint(0, 1, 1.4), v: [rnd(-1, 1), 9, rnd(-1, 1)], sp: 0, size: [1.6, 0.06, 1.2], c: 3808536, spin: 6, life: 20, bounce: 0.2 }),
1618
+ ...[-1, 1].map((side) => debris({ p: carPoint(side * 0.95, 0.7, 0.3), v: doorVelocity(side), sp: 0, size: [0.06, 0.7, 1.1], c: 3808536, spin: 5, life: 20, bounce: 0.2 })),
1619
+ debris({ p: carPoint(0.95, 0.38, 1.35), v: [3, 4, 2], sp: 0, size: [0.3, 0.76, 0.76], c: 1118481, spin: 8, life: 20, bounce: 0.4 })
1620
+ ];
1621
+ var TANK = carPoint(-0.6, 0.25, -1.3);
1622
+ var TANK_FLOOR = [TANK[0], 0, TANK[2]];
1623
+ var POOL = [TANK_FLOOR[0] - 1.2, 0, TANK_FLOOR[2] - 0.6];
1624
+ var BOOM_AT = 7.2;
1625
+ var HOLE = carPoint(-0.96, 0.6, -1.2);
1626
+ var FUEL_LEAK = {
1627
+ id: "fuel_leak",
1628
+ pageId: "vazamento",
1629
+ team: "vehicles",
1630
+ budget: { particles: 400, lights: 1 },
1631
+ loop: 11,
1632
+ view: { eye: [10, 2.4, 4.2], target: [5.6, 0.4, 0] },
1633
+ setup: [cue("zombie_visible", { index: -1, visible: false })],
1634
+ timeline: [
1635
+ decal(POOL[0], 0, POOL[2], 2.2, 2040368, { grow: 3, life: 60, ar: 0.6, fluid: "oil" }),
1636
+ after(
1637
+ 3.4,
1638
+ call2("impact", "metal", HOLE, carDirection(HOLE, carPoint(-2, 0.6, -1.2)), 1.5),
1639
+ burst("streak", 14, { p: [POOL[0], 0.05, POOL[2]], cone: 1.4, sp: [1, 3], life: 0.3, len: 0.02, c0: 16765056, c1: 16728064, g: 9.8 })
1640
+ ),
1641
+ after(BOOM_AT, ...VEHICLE_BOOM)
1642
+ ],
1643
+ tick: [
1644
+ when(lt(TIME, BOOM_AT), [
1645
+ emit("drip", "normal", 12, { p: TANK, box: [0.05, 0, 0.05], sp: 0, life: 1, size: 0.03, c0: 5921402, a: 0.9, g: 9.8, fi: 0, gnd: 1 })
1646
+ ]),
1647
+ when(and(gt(TIME, 3.5), lt(TIME, BOOM_AT)), [
1648
+ set("spread", clamp(div(sub(TIME, 3.5), 2.5), 0, 1)),
1649
+ set("front", vlerp(POOL, TANK_FLOOR, mul(variable("spread"), 0.8))),
1650
+ call2("fire_emit", "vz", variable("front"), add(0.6, mul(variable("spread"), 0.4)), 1.1, { smoke: 14 }),
1651
+ call2("fire_emit", "vz2", POOL, 0.7, 0.8, { smoke: 6 }),
1652
+ fireLight(0, vadd(variable("front"), [0, 1, 0]), 3, TIME)
1653
+ ]),
1654
+ when(ge(TIME, BOOM_AT), [
1655
+ call2("fire_emit", "wr", vadd(query("car", "position"), [0, 0.9, 0]), 0.8, 1.4, { smoke: 18, smokeAlpha: 0.55 }),
1656
+ fireLight(0, vadd(query("car", "position"), [0, 1.6, 0]), 3.4, TIME),
1657
+ behavior("car", "step", { dt: DELTA })
1658
+ ])
1659
+ ]
1660
+ };
1661
+ var CAR_EXPLOSION = {
1662
+ id: "car_explosion",
1663
+ pageId: "explosao",
1664
+ team: "vehicles",
1665
+ lab: "car_fuel_tank",
1666
+ budget: { particles: 600, debris: 34, lights: 1 },
1667
+ loop: 10,
1668
+ view: { eye: [12, 4.2, 8], target: [5.6, 1, 0.8] },
1669
+ props: ["car"],
1670
+ setup: [cue("zombie_visible", { index: -1, visible: false })],
1671
+ timeline: [after(0.4, ...VEHICLE_BOOM)],
1672
+ tick: [
1673
+ when(gt(TIME, 0.5), [
1674
+ call2("fire_emit", "wr", vadd(query("car", "position"), [0, 0.9, 0]), 0.8, 1.4, { smoke: 18, smokeAlpha: 0.55 }),
1675
+ fireLight(0, vadd(query("car", "position"), [0, 1.6, 0]), 3.4, TIME)
1676
+ ]),
1677
+ behavior("car", "step", { dt: DELTA })
1678
+ ]
1679
+ };
1680
+ var HITS2 = [[90, 40], [170, 60], [120, 75], [60, 50]];
1681
+ var HIT_X2 = HITS2.map(([x]) => x);
1682
+ var HIT_Y2 = HITS2.map(([, y]) => y);
1683
+ var ZOMBIE = carPoint(0, 0, 3.1);
1684
+ var CAR_YAW = -0.4;
1685
+ var WINDSHIELD = {
1686
+ id: "windshield",
1687
+ pageId: "parabrisa",
1688
+ team: "vehicles",
1689
+ budget: { particles: 6, decals: 1 },
1690
+ loop: 5.8,
1691
+ setup: [
1692
+ cue("zombie_visible", { index: -1, visible: false }),
1693
+ behavior("walkers", "place", { index: 0, x: ZOMBIE[0], z: ZOMBIE[2], facing: CAR_YAW + Math.PI }),
1694
+ camera(carPoint(-0.3, 1.35, -0.2), carPoint(0, 1.2, 2.2), true)
1695
+ ],
1696
+ timeline: [
1697
+ repeat(
1698
+ HITS2.length,
1699
+ after(
1700
+ add(0.6, mul(INDEX, 0.85)),
1701
+ cue("zombie_swing", { index: 0, side: mod(INDEX, 2) }),
1702
+ after(
1703
+ add(0.6, mul(INDEX, 0.85), 0.15),
1704
+ behavior("cracks", "crack", { x: pick(HIT_X2, INDEX), y: pick(HIT_Y2, INDEX), big: gt(INDEX, 1) }),
1705
+ when(or(eq(INDEX, 1), eq(INDEX, 3)), [behavior("cracks", "blood_streaks", { x: pick(HIT_X2, INDEX), y: pick(HIT_Y2, INDEX) })]),
1706
+ screen({ add: { shake: 0.18 } }),
1707
+ burst("add", 6, { p: carPoint(-0.1, 1.25, 0.75), box: [0.4, 0.1, 0.05], size: [0.01, 0.03], life: [0.2, 0.5], c0: 16777215, a: 0.8, fi: 0.2 }),
1708
+ cue("windshield_stage", { stage: add(INDEX, 1) })
1709
+ )
1710
+ )
1711
+ )
1712
+ ]
1713
+ };
1714
+ var FRESH_BLOOD3 = 9046538;
1715
+ var BEAT_RATE = 1.9;
1716
+ var blow2 = (side) => [
1717
+ cue("zombie_swing", { index: -1, side }),
1718
+ after(
1719
+ add(TIME, 0.18),
1720
+ screen({ set: side === 0 ? { dirL: 1 } : { dirR: 1 }, add: { shake: 0.3 } }),
1721
+ repeat(4, lens(side === 0 ? rnd(0.05, 0.3) : rnd(0.7, 0.95), rnd(0.3, 0.8), rnd(0.02, 0.05), "110,8,8", 0.8)),
1722
+ burst("normal", 12, { p: [0, 1.4, 0.9], dir: [0, 0.2, 1], cone: 0.6, sp: [1, 2], life: 1, size: [0.03, 0.05], c0: FRESH_BLOOD3, g: 9.8, fi: 0, gnd: 3, ds: [0.05, 0.1] })
1723
+ )
1724
+ ];
1725
+ var DAMAGE_AND_BITE = {
1726
+ id: "damage_and_bite",
1727
+ pageId: "dano",
1728
+ team: "screen",
1729
+ budget: {},
1730
+ loop: 5.6,
1731
+ view: { eye: [0, 1.6, 1.7], target: [0, 1.5, 0] },
1732
+ timeline: [
1733
+ after(0.3, ...blow2(0)),
1734
+ after(1.6, ...blow2(1)),
1735
+ after(3, cue("zombie_lunge", { z: 0.45 })),
1736
+ after(
1737
+ 3.15,
1738
+ screen({ set: { red: 1, desat: 0.9 }, add: { shake: 0.6 } }),
1739
+ cue("bite", { infection: 1 }),
1740
+ repeat(7, lens(rnd(0.2, 0.8), rnd(0.2, 0.7), rnd(0.04, 0.1), "100,6,6", 1))
1741
+ )
1742
+ ],
1743
+ tick: [
1744
+ when(and(gt(TIME, 3.2), lt(TIME, 5.4)), [when(lt(mod(mul(TIME, BEAT_RATE), 1), mul(DELTA, BEAT_RATE)), [screen({ set: { beat: 1 } })])])
1745
+ ]
1746
+ };
1747
+ var MALAISE = 0;
1748
+ var FEVER = 1;
1749
+ var DELIRIUM = 2;
1750
+ var LAST_HOURS = 3;
1751
+ var GHOSTS = [[-3, -1.8], [3.5, -2.4], [-6, 1.5]];
1752
+ var INFECTION_PHASES = {
1753
+ id: "infection_phases",
1754
+ pageId: "infeccao",
1755
+ team: "screen",
1756
+ params: { id: "phase", options: ["malaise", "fever", "delirium", "last_hours"], default: 1 },
1757
+ budget: { particles: 10 },
1758
+ loop: 0,
1759
+ view: { eye: [2, 1.7, 7], target: [0, 1.2, -1] },
1760
+ setup: [
1761
+ cue("zombie_visible", { index: -1, visible: false }),
1762
+ ...GHOSTS.map(([x, z], index) => cue("ghost_place", { index, x, z, turn: rnd(-0.4, 0.4) }))
1763
+ ],
1764
+ timeline: [set("nextCough", 2)],
1765
+ tick: [
1766
+ when(eq(PARAM, MALAISE), [post({ desat: 0.25, tunnel: add(0.15, mul(0.12, sin(mul(TIME, 1.2)))), blur: mul(0.3, max(0, sin(mul(TIME, 0.7)))) })]),
1767
+ when(eq(PARAM, FEVER), [
1768
+ post({ cold: add(0.8, mul(0.15, sin(mul(TIME, 2)))), blur: add(0.6, mul(0.6, sin(mul(TIME, 1.6)))), desat: 0.3 }),
1769
+ when(gt(TIME, variable("nextCough")), [
1770
+ set("nextCough", add(TIME, rnd(3, 5))),
1771
+ screen({ add: { shake: 0.25 } }),
1772
+ burst("normal", 10, { p: vadd(CAMERA_POSITION, cameraForward([0, -0.15, -0.6])), dir: cameraForward([0, 0, -1]), cone: 0.4, sp: [0.6, 1.2], drag: 1.5, life: 1.2, size: [0.05, 0.1], grow: 5, c0: 14673646, a: 0.2 })
1773
+ ])
1774
+ ]),
1775
+ when(eq(PARAM, DELIRIUM), [
1776
+ post({ chroma: add(2, mul(3, add(0.5, mul(0.5, sin(mul(TIME, 1.7)))))), hue: mul(22, sin(mul(TIME, 0.6))), tunnel: 0.3, desat: 0.15 }),
1777
+ repeat(GHOSTS.length, cue("ghost_visible", { index: INDEX, visible: gt(sin(add(mul(TIME, add(1.3, mul(INDEX, 0.7))), mul(INDEX, 2))), 0.6) })),
1778
+ when(lt(rand(), mul(DELTA, 0.3)), [screen({ set: { white: 0.12 } })])
1779
+ ]),
1780
+ when(eq(PARAM, LAST_HOURS), [
1781
+ post({ desat: 0.85, tintAlpha: 0.22, tintColor: "70,110,40", tunnel: 0.55, blur: 1.2 }),
1782
+ when(lt(mod(mul(TIME, 2.2), 1), mul(DELTA, 2.2)), [screen({ set: { beat: 1 }, add: { shake: 0.05 } })])
1783
+ ])
1784
+ ]
1785
+ };
1786
+ var WALKERS2 = 6;
1787
+ var NEAR = 9;
1788
+ var RANGE = 7;
1789
+ var RESTING_BPM = 70;
1790
+ var BPM_RISE = 80;
1791
+ var walkerPoint = [query("walkers", "x", INDEX), 0, query("walkers", "z", INDEX)];
1792
+ var PANIC = {
1793
+ id: "panic",
1794
+ pageId: "panico",
1795
+ team: "screen",
1796
+ budget: {},
1797
+ loop: 8,
1798
+ view: { eye: [0, 1.65, 6], target: [0, 1.3, -2] },
1799
+ setup: [
1800
+ cue("zombie_visible", { index: -1, visible: false }),
1801
+ repeat(
1802
+ WALKERS2,
1803
+ set("startX", rnd(-4, 4)),
1804
+ set("startZ", sub(rnd(-4, -2), mul(INDEX, 0.6))),
1805
+ behavior("walkers", "place", { index: INDEX, x: variable("startX"), z: variable("startZ"), facing: 0 }),
1806
+ behavior("walkers", "walk", { index: INDEX, to: [rnd(-1.2, 1.2), 0, 5], speed: ite(eq(mod(INDEX, 3), 1), 1.6, 0.7) })
1807
+ )
1808
+ ],
1809
+ timeline: [],
1810
+ tick: [
1811
+ set("nearest", 99),
1812
+ repeat(WALKERS2, set("nearest", min(variable("nearest"), vdist(walkerPoint, CAMERA_POSITION)))),
1813
+ set("fear", clamp(div(sub(NEAR, variable("nearest")), RANGE), 0, 1)),
1814
+ set("bpm", add(RESTING_BPM, mul(BPM_RISE, variable("fear")))),
1815
+ when(lt(mod(div(mul(TIME, variable("bpm")), 60), 1), div(mul(DELTA, variable("bpm")), 60)), [screen({ set: { beat: add(0.4, mul(0.6, variable("fear"))) } })]),
1816
+ post({ tunnel: add(0.1, mul(0.65, variable("fear"))), desat: mul(0.45, variable("fear")) }),
1817
+ set("tremor", add(0.012, mul(0.035, variable("fear"))))
1818
+ ]
1819
+ };
1820
+ var BLOATER_BLINDNESS = {
1821
+ id: "bloater_blindness",
1822
+ pageId: "cegueira",
1823
+ team: "screen",
1824
+ budget: { particles: 30 },
1825
+ loop: 7.5,
1826
+ view: { eye: [0, 1.6, 2.6], target: [0, 1.3, 0] },
1827
+ setup: [cue("bloater_pose", { eyes: 6283402, skin: 7043666, belly: [1.4, 1.25, 1.6] })],
1828
+ timeline: [
1829
+ after(
1830
+ 0.7,
1831
+ cue("zombie_visible", { index: -1, visible: false }),
1832
+ screen({ set: { gas: 1 }, add: { shake: 0.3 } }),
1833
+ burst("normal", 30, { p: [0, 1.3, 0.8], r: 0.6, sp: [0.5, 2], drag: 2, life: [3, 5], size: [0.8, 1.6], grow: 1.6, c0: 7309884, c1: 3885606, a: 0.3, fi: 0.05 }),
1834
+ repeat(8, lens(rnd(0.15, 0.85), rnd(0.15, 0.7), rnd(0.03, 0.08), "110,150,50", 0.9))
1835
+ ),
1836
+ after(2, screen({ add: { shake: 0.2 } })),
1837
+ after(3.4, screen({ add: { shake: 0.2 } }))
1838
+ ]
1839
+ };
1840
+ var FIRE = [-1.4, 0.1, 3];
1841
+ var CAMPFIRE = {
1842
+ id: "campfire",
1843
+ pageId: "fogueira",
1844
+ team: "screen",
1845
+ budget: { particles: 120, lights: 1 },
1846
+ loop: 0,
1847
+ view: { eye: [1.4, 1.6, 6.4], target: [-1.4, 0.6, 3] },
1848
+ setup: [cue("zombie_visible", { index: -1, visible: false }), cue("survivor_pose", { x: -2.6, z: 3.6, turn: 2.2 })],
1849
+ timeline: [],
1850
+ tick: [
1851
+ call2("fire_emit", "camp", FIRE, 0.25, 0.55, { smoke: 8, smokeColor: 4867650, smokeAlpha: 0.25 }),
1852
+ fireLight(0, [FIRE[0], FIRE[1] + 0.8, FIRE[2]], 2.4, TIME)
1853
+ ]
1854
+ };
1855
+ var FACING3 = 0.5;
1856
+ var MOUTH = [Math.sin(FACING3) * 0.2 + -1.6, 1.68, Math.cos(FACING3) * 0.2 + 1.8];
1857
+ var BREATH = normalized([Math.sin(FACING3), -0.15, Math.cos(FACING3)]);
1858
+ var BREATH_EVERY = 2.4;
1859
+ var COUGH_EVERY = 6.5;
1860
+ var puff = (strength) => burst("normal", 8 * strength, { p: MOUTH, dir: BREATH, cone: 0.35, sp: [0.5, 1.1 * strength], drag: 1.6, life: [1.2, 1.8], size: [0.05, 0.09], grow: 6, c0: 14673646, a: 0.22, g: -0.1 });
1861
+ var COUGH_AND_BREATH = {
1862
+ id: "cough_and_breath",
1863
+ pageId: "tosse",
1864
+ team: "screen",
1865
+ budget: { particles: 40, lights: 1 },
1866
+ loop: 0,
1867
+ view: { eye: [2.4, 1.8, 6.5], target: [-1.4, 1.3, 1.6] },
1868
+ setup: [cue("zombie_visible", { index: -1, visible: false }), cue("survivor_pose", { x: -1.6, z: 1.8, turn: FACING3 }), cue("torch", { on: true })],
1869
+ timeline: [set("nextBreath", 0.5), set("nextCough", 3)],
1870
+ tick: [
1871
+ when(gt(TIME, variable("nextBreath")), [set("nextBreath", add(TIME, BREATH_EVERY)), puff(1)]),
1872
+ when(gt(TIME, variable("nextCough")), [
1873
+ set("nextCough", add(TIME, COUGH_EVERY)),
1874
+ ...[0, 0.25, 0.5].map((delay) => after(add(TIME, delay), puff(1.6), cue("survivor_jolt", { tilt: 0.25 })))
1875
+ ])
1876
+ ]
1877
+ };
1878
+ var FACING4 = [0, 0, 1];
1879
+ var UP11 = [0, 1, 0];
1880
+ var ORIGIN = [0, 0, 0];
1881
+ var FORWARD4 = [0, 0, -1];
1882
+ var IMPACT_MATERIALS = ["concrete", "metal", "wood", "glass", "flesh", "water"];
1883
+ var impactEffect = (material) => ({
1884
+ id: `impact_${material}`,
1885
+ pageId: `event:impact_${material}`,
1886
+ team: "weapons",
1887
+ budget: { particles: 40, decals: 1 },
1888
+ loop: 2,
1889
+ timeline: [call2("impact", material, ORIGIN, FACING4, 1, 0)]
1890
+ });
1891
+ var muzzleEffect = (weapon) => ({
1892
+ id: `muzzle_${weapon}`,
1893
+ pageId: `event:muzzle_${weapon}`,
1894
+ team: "weapons",
1895
+ budget: { particles: 60, lights: 1 },
1896
+ loop: 2.4,
1897
+ timeline: [call2("muzzle", weapon, ORIGIN, FORWARD4)]
1898
+ });
1899
+ var casingEffect = (id, type) => ({
1900
+ id,
1901
+ pageId: `event:${id}`,
1902
+ team: "weapons",
1903
+ budget: { debris: 1 },
1904
+ loop: 2,
1905
+ timeline: [call2("casing", ORIGIN, type)]
1906
+ });
1907
+ var EVENT_EFFECTS = [
1908
+ ...IMPACT_MATERIALS.map(impactEffect),
1909
+ ...["glock", "ak", "r870", "barrett"].map(muzzleEffect),
1910
+ casingEffect("casing_brass", ""),
1911
+ casingEffect("casing_shell", "shell"),
1912
+ casingEffect("casing_50", "50"),
1913
+ {
1914
+ // door_stages (W856-871): the dust a blow shakes loose from the frame and the splinters.
1915
+ id: "stage_crack",
1916
+ pageId: "event:stage_crack",
1917
+ team: "destruction",
1918
+ budget: { particles: 12, debris: 5 },
1919
+ loop: 2,
1920
+ timeline: [
1921
+ burst("normal", 12, { p: ORIGIN, box: [0.6, 0.05, 0.02], dir: [0, -0.287, 0.958], cone: 0.6, sp: [0.2, 0.8], drag: 1.5, life: [0.8, 1.4], size: [0.08, 0.16], grow: 3, c0: 8024934, a: 0.45, g: 0.4 }),
1922
+ repeat(scaled(2), debris({ p: [rnd(-0.5, 0.5), rnd(0, 1.6), 0], dir: [0, 0.287, 0.958], cone: 0.6, sp: [1, 2.5], size: [0.015, 0.015, 0.1], c: 8017200, life: 12 }))
1923
+ ]
1924
+ },
1925
+ {
1926
+ // block_wall (W872-886, 3.6 s): the ring of dust that rolls out when the wall gives way.
1927
+ id: "breach_dust",
1928
+ pageId: "event:breach_dust",
1929
+ team: "destruction",
1930
+ budget: { particles: 90, debris: 16 },
1931
+ loop: 5,
1932
+ timeline: [
1933
+ screen({ add: { shake: 0.25 } }),
1934
+ burst("normal", 30, { p: ORIGIN, box: [1.4, 0.8, 0.2], sp: [0.3, 1.2], drag: 1.5, life: [1.5, 2.6], size: [0.4, 0.7], grow: 3, c0: 9078138, c1: 4867648, a: 0.45 }),
1935
+ repeat(scaled(16), debris({ p: ORIGIN, box: [1.3, 0.8, 0.1], sp: [1, 3], size: [0.08, 0.07, 0.08], k: [0.5, 1.5], c: 7236195, life: 20 })),
1936
+ repeat(
1937
+ scaled(60),
1938
+ spawn("normal", local("angle", rnd(0, 6.283), {
1939
+ p: [rnd(-1.4, 1.4), 0.2, rnd(-0.6, 0.4)],
1940
+ dir: vnorm([cos(variable("angle")), 0.3, sin(variable("angle"))]),
1941
+ cone: 0.4,
1942
+ sp: [0.8, 2.4],
1943
+ drag: 1.3,
1944
+ life: [2.5, 4.5],
1945
+ size: [0.6, 1],
1946
+ grow: 3,
1947
+ c0: 8024680,
1948
+ c1: 4867648,
1949
+ a: 0.45,
1950
+ fi: 0.12
1951
+ }))
1952
+ )
1953
+ ]
1954
+ },
1955
+ {
1956
+ // building_collapse (W872-886): the slab dropping in blocks and the cloud that rolls along the street.
1957
+ id: "collapse_dust",
1958
+ pageId: "event:collapse_dust",
1959
+ team: "destruction",
1960
+ budget: { particles: 350, debris: 26 },
1961
+ loop: 8,
1962
+ timeline: [
1963
+ screen({ add: { shake: 0.45 } }),
1964
+ repeat(6, debris({ p: ORIGIN, box: [2, 0.05, 0.6], v: [rnd(-0.5, 0.5), rnd(-0.5, 0), rnd(0, 0.8)], sp: 0, size: [rnd(0.5, 1.1), 0.25, rnd(0.4, 0.8)], c: 7236195, spin: 3, life: 30, bounce: 0.1 })),
1965
+ repeat(scaled(20), debris({ p: ORIGIN, box: [2.1, 0.1, 0.7], v: [rnd(-1, 1), rnd(-1, 1), rnd(0, 1.5)], sp: 0, size: [0.12, 0.1, 0.12], k: [0.5, 1.5], c: 6183508, life: 30 })),
1966
+ repeat(
1967
+ scaled(70),
1968
+ spawn("normal", local("angle", rnd(-0.4, 3.5), {
1969
+ p: [rnd(-2, 2), 0.2, rnd(-0.5, 0.5)],
1970
+ dir: vnorm([mul(cos(variable("angle")), 0.6), 0.25, sin(variable("angle"))]),
1971
+ cone: 0.5,
1972
+ sp: [1, 3.2],
1973
+ drag: 1.1,
1974
+ life: [3, 6],
1975
+ size: [0.7, 1.2],
1976
+ grow: 3,
1977
+ c0: 8024680,
1978
+ c1: 4867648,
1979
+ a: 0.5,
1980
+ fi: 0.12
1981
+ }))
1982
+ )
1983
+ ]
1984
+ },
1985
+ {
1986
+ // shopfront_glass (W846-855): the pane coming down as shards, the flash of the glints and the pale dust.
1987
+ id: "glass_burst",
1988
+ pageId: "event:glass_burst",
1989
+ team: "destruction",
1990
+ budget: { debris: 80, particles: 50 },
1991
+ loop: 5,
1992
+ timeline: [
1993
+ screen({ add: { shake: 0.08 } }),
1994
+ repeat(
1995
+ scaled(75),
1996
+ debris({ p: ORIGIN, box: [1.25, 0.85, 0.02], v: [rnd(-0.4, 0.4), rnd(-0.5, 0.8), rnd(0.6, 2)], sp: 0, size: [rnd(0.05, 0.18), 6e-3, rnd(0.05, 0.18)], c: 11061464, spin: 14, life: 30, bounce: 0.15 })
1997
+ ),
1998
+ burst("add", 30, { p: ORIGIN, box: [1.2, 0.8, 0.05], size: [0.02, 0.05], life: [0.2, 0.6], c0: 16777215, a: 0.9, fi: 0.3 }),
1999
+ burst("normal", 20, { p: [0, -0.6, 0.1], box: [1.2, 0.5, 0.1], sp: [0.2, 0.6], drag: 2, life: [0.8, 1.4], size: [0.12, 0.2], grow: 3, c0: 12572386, a: 0.2 })
2000
+ ]
2001
+ },
2002
+ {
2003
+ // The chips macro the page uses on wood (W849) plus rust (`rustAt`), for rebar laid bare in concrete.
2004
+ id: "rebar_expose",
2005
+ pageId: "event:rebar_expose",
2006
+ team: "destruction",
2007
+ budget: { particles: 20, debris: 8 },
2008
+ loop: 2,
2009
+ timeline: [call2("chips", ORIGIN, UP11, 8, 0.9), call2("rust_at", ORIGIN, 6)]
2010
+ }
2011
+ ];
2012
+ var PI = Math.PI;
2013
+ var SMOKE = { color: 9079430, width: 10, height: 8, gravity: -0.08, drag: 2.2, growth: 2.1 };
2014
+ var FLASH_SECONDS = 0.055;
2015
+ var FLASH_RAMP = [0.6, 0.9];
2016
+ var FLASH_OPACITY = 0.95;
2017
+ var FLAME_COLORS = [16742946, 16760896, 13121050, 16773296];
2018
+ var FLAME_ADDITIVE = 3;
2019
+ var ORIGIN2 = [0, 0, 0];
2020
+ var SIZE = variable("size");
2021
+ var wide = (width) => mul(sub(rand(), 0.5), width);
2022
+ var puff2 = (point, velocity, world, radius, life, opacity) => mesh({
2023
+ shape: "sphere",
2024
+ p: point,
2025
+ v: velocity,
2026
+ wv: world,
2027
+ r: radius,
2028
+ seg: SMOKE.width,
2029
+ seg2: SMOKE.height,
2030
+ life,
2031
+ c0: SMOKE.color,
2032
+ a: opacity,
2033
+ fade: true,
2034
+ lit: true,
2035
+ g: SMOKE.gravity,
2036
+ drag: SMOKE.drag,
2037
+ grow: SMOKE.growth
2038
+ });
2039
+ var flameBit = (point, velocity, world, radius, life) => [
2040
+ set("bitPoint", point),
2041
+ set("bitVelocity", velocity),
2042
+ set("bitWorld", world),
2043
+ set("bitRadius", radius),
2044
+ set("bitLife", life),
2045
+ set("bitColour", floor(mul(rand(), FLAME_COLORS.length))),
2046
+ mesh({
2047
+ shape: "icosahedron",
2048
+ p: variable("bitPoint"),
2049
+ v: variable("bitVelocity"),
2050
+ wv: variable("bitWorld"),
2051
+ r: variable("bitRadius"),
2052
+ seg: 1,
2053
+ rot: [mul(rand(), 3), mul(rand(), 3), 0],
2054
+ spin: [3, 4, 2],
2055
+ life: variable("bitLife"),
2056
+ c0: pick(FLAME_COLORS, variable("bitColour")),
2057
+ add: eq(variable("bitColour"), FLAME_ADDITIVE),
2058
+ a: 0.9,
2059
+ fade: true,
2060
+ g: -1.4,
2061
+ drag: 1.4,
2062
+ grow: 2.2
2063
+ })
2064
+ ];
2065
+ var flashPiece = (shape, radius, height, width, at3, rot, color) => mesh({ shape, r: radius, ...height === void 0 ? {} : { h: height }, seg: width, seg2: shape === "sphere" ? 8 : 1, p: ORIGIN2, at: at3, rot, roll: variable("roll"), ramp: FLASH_RAMP, life: FLASH_SECONDS, c0: color, a: FLASH_OPACITY, add: true });
2066
+ var MUZZLE_FLASH2 = {
2067
+ id: "viewmodel_muzzle_flash",
2068
+ pageId: "event:viewmodel_muzzle_flash",
2069
+ team: "weapons",
2070
+ budget: { particles: 12, lights: 1 },
2071
+ loop: 0.1,
2072
+ timeline: [
2073
+ set("angles", []),
2074
+ repeat(4, append("angles", add(div(mul(INDEX, PI), 2), mul(rand(), 0.6)))),
2075
+ set("roll", mul(rand(), PI)),
2076
+ flash(ORIGIN2, 16756832, mul(4, SIZE), FLASH_SECONDS, mul(2, SIZE), true, true),
2077
+ flashPiece("cone_open", mul(0.013, SIZE), mul(0.085, SIZE), 10, [mul(0.042, SIZE), 0, 0], [0, 0, -PI / 2], 16757575),
2078
+ repeat(
2079
+ 4,
2080
+ set("angle", pick(variable("angles"), INDEX)),
2081
+ flashPiece(
2082
+ "cone",
2083
+ mul(5e-3, SIZE),
2084
+ mul(0.04, SIZE),
2085
+ 6,
2086
+ [mul(0.012, SIZE), mul(cos(variable("angle")), 0.016, SIZE), mul(sin(variable("angle")), 0.016, SIZE)],
2087
+ [variable("angle"), 0, -PI / 2],
2088
+ 16757575
2089
+ )
2090
+ ),
2091
+ flashPiece("sphere", mul(0.016, SIZE), void 0, 12, [mul(8e-3, SIZE), 0, 0], [0, 0, 0], 16773568),
2092
+ repeat(
2093
+ 6,
2094
+ puff2(
2095
+ ORIGIN2,
2096
+ [add(0.15, mul(rand(), 0.35)), add(0.03, mul(rand(), 0.12)), mul(sub(rand(), 0.5), 0.14)],
2097
+ [0, 0, 0],
2098
+ mul(0.013, SIZE),
2099
+ add(1.3, mul(rand(), 0.6)),
2100
+ 0.45
2101
+ )
2102
+ )
2103
+ ]
2104
+ };
2105
+ var SMOKE_PUFF = {
2106
+ id: "weapon_smoke_puff",
2107
+ pageId: "event:weapon_smoke_puff",
2108
+ team: "weapons",
2109
+ budget: { particles: 1 },
2110
+ loop: 0.1,
2111
+ timeline: [puff2(ORIGIN2, void 0, [variable("vx"), variable("vy"), variable("vz")], variable("radius"), variable("life"), variable("opacity"))]
2112
+ };
2113
+ var BACKBLAST = {
2114
+ id: "launcher_backblast",
2115
+ pageId: "event:launcher_backblast",
2116
+ team: "weapons",
2117
+ budget: { particles: 40 },
2118
+ loop: 0.1,
2119
+ timeline: [
2120
+ repeat(26, puff2(ORIGIN2, [neg(add(1.5, mul(rand(), 3))), 0, 0], [wide(1.4), wide(0.8), wide(1.4)], add(0.03, mul(rand(), 0.03)), add(1.8, mul(rand(), 1.4)), 0.5)),
2121
+ repeat(14, ...flameBit(ORIGIN2, [neg(add(2, mul(rand(), 2))), 0, 0], [wide(1), wide(0.6), wide(1)], 0.02, 0.25))
2122
+ ]
2123
+ };
2124
+ var FLAME_JET = {
2125
+ id: "flame_jet",
2126
+ pageId: "event:flame_jet",
2127
+ team: "weapons",
2128
+ budget: { particles: 30 },
2129
+ loop: 0.1,
2130
+ timeline: [
2131
+ repeat(
2132
+ floor(mul(420, variable("dt"), variable("scale"))),
2133
+ set("speed", mul(add(2.2, mul(rand(), 1.1)), variable("scale"))),
2134
+ ...flameBit(
2135
+ [mul(rand(), variable("speed"), min(variable("dt"), 0.3)), 0, 0],
2136
+ [variable("speed"), 0, 0],
2137
+ [wide(0.35), mul(sub(rand(), 0.3), 0.3), wide(0.35)],
2138
+ add(0.02, mul(rand(), 0.022)),
2139
+ add(0.4, mul(rand(), 0.35))
2140
+ )
2141
+ ),
2142
+ when(lt(rand(), 0.5), [
2143
+ puff2([add(mul(1.1, variable("scale")), mul(rand(), 0.4)), 0, 0], void 0, [mul(vx(DIRECTION), 0.6), 0.35, mul(vz(DIRECTION), 0.6)], 0.03, 1.6, 0.4)
2144
+ ])
2145
+ ]
2146
+ };
2147
+ var VIEWMODEL_EFFECTS = [MUZZLE_FLASH2, SMOKE_PUFF, BACKBLAST, FLAME_JET];
2148
+ 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];
2149
+ var WORKSHOP_EFFECTS = Object.fromEntries(ALL_EFFECTS.map((effect) => [effect.id, effect]));
2150
+ var GAME_EFFECTS = { ...WORKSHOP_EFFECTS, ...Object.fromEntries([...EVENT_EFFECTS, ...VIEWMODEL_EFFECTS].map((effect) => [effect.id, effect])) };
2151
+ var QUARTER_TURN2 = Math.PI / 2;
2152
+ var SCRATCH = createVec3();
2153
+ var QUARTER_TURN3 = Math.PI / 2;
2154
+
2155
+ // src/rain-near.ts
2156
+ var SPLASH_FROM_MM = 5;
2157
+ function createNearRain(vfx, options = {}) {
2158
+ const puddles = options.puddles ?? [];
2159
+ return {
2160
+ update(sample, dt, camera2) {
2161
+ const raining = sample.rain > 0;
2162
+ const engine = raining ? vfx?.workshop() : vfx?.peekWorkshop();
2163
+ if (!engine) return;
2164
+ const field = engine.rain;
2165
+ if (!raining) {
2166
+ if (field.on) field.clear();
2167
+ return;
2168
+ }
2169
+ field.puddleAt = options.puddleAt ?? ((x, z) => inPuddle(puddles, x, z));
2170
+ field.drive(sample.drops, sample.windX, camera2[0], camera2[2]);
2171
+ field.update(dt, sample.rain >= (options.splashFrom ?? SPLASH_FROM_MM), camera2);
2172
+ },
2173
+ count: () => vfx?.peekWorkshop()?.rain.count ?? 0,
2174
+ clear() {
2175
+ vfx?.peekWorkshop()?.rain.clear();
2176
+ }
2177
+ };
2178
+ }
2179
+
2180
+ // src/rain-curtain.ts
2181
+ var RAIN_CURTAIN = { count: 14e3, width: 70, height: 25, speed: 13, pointSize: 1.6 };
2182
+ var RAIN_CURTAIN_VERTEX = "uniform float uTime; uniform vec3 uCam; void main(){ vec3 p = position; p.y = mod(p.y - uTime*13., 25.); p.xz = uCam.xz + mod(p.xz - uCam.xz + 35., 70.) - 35.; gl_Position = projectionMatrix*viewMatrix*vec4(p,1.); gl_PointSize = 1.6; }";
2183
+ var RAIN_CURTAIN_FRAGMENT = "uniform float uDay; void main(){ gl_FragColor = vec4(vec3(.68,.72,.78)*(0.25+0.75*uDay), .22+.1*uDay); }";
2184
+ var RAIN_CURTAIN_PROFILES = { low: 0.35, medium: 0.7, high: 1 };
2185
+ var RAIN_CURTAIN_FULL_AT = 4e3;
2186
+ function rainCurtainPositions(random = Math.random, count = RAIN_CURTAIN.count) {
2187
+ const positions = new Float32Array(count * 3);
2188
+ for (let index = 0; index < count; index++) {
2189
+ positions[index * 3] = (random() - 0.5) * RAIN_CURTAIN.width;
2190
+ positions[index * 3 + 1] = random() * RAIN_CURTAIN.height;
2191
+ positions[index * 3 + 2] = (random() - 0.5) * RAIN_CURTAIN.width;
2192
+ }
2193
+ return positions;
2194
+ }
2195
+ function createRainCurtain(THREE, options = {}) {
2196
+ const count = options.count ?? RAIN_CURTAIN.count;
2197
+ const geometry = new THREE.BufferGeometry();
2198
+ geometry.setAttribute("position", new THREE.BufferAttribute(rainCurtainPositions(options.random, count), 3));
2199
+ const uniforms = { uTime: { value: 0 }, uCam: { value: new THREE.Vector3() }, uDay: { value: 1 } };
2200
+ const material = new THREE.ShaderMaterial({ uniforms, transparent: true, depthWrite: false, vertexShader: RAIN_CURTAIN_VERTEX, fragmentShader: RAIN_CURTAIN_FRAGMENT });
2201
+ const object = new THREE.Points(geometry, material);
2202
+ object.frustumCulled = false;
2203
+ object.name = "weather-rain-curtain";
2204
+ object.visible = false;
2205
+ let share = RAIN_CURTAIN_PROFILES[options.profile ?? "high"];
2206
+ let drawn = 0;
2207
+ return {
2208
+ object,
2209
+ update(sample, camera2, time, daylight) {
2210
+ const fullAt = options.fullAt ?? RAIN_CURTAIN_FULL_AT;
2211
+ drawn = sample.rain > 0 ? Math.round(count * share * Math.min(1, sample.drops / fullAt)) : 0;
2212
+ object.visible = drawn > 0;
2213
+ geometry.setDrawRange(0, drawn);
2214
+ uniforms.uTime.value = time;
2215
+ uniforms.uDay.value = daylight;
2216
+ uniforms.uCam.value.set(camera2.x, camera2.y, camera2.z);
2217
+ },
2218
+ setProfile(profile) {
2219
+ share = RAIN_CURTAIN_PROFILES[profile];
2220
+ },
2221
+ drawn: () => drawn,
2222
+ dispose() {
2223
+ geometry.dispose();
2224
+ material.dispose();
2225
+ }
2226
+ };
2227
+ }
2228
+
2229
+ // src/renderer.ts
2230
+ var WEATHER_LAYERS = ["far_rain", "near_rain", "mist", "fog", "sky", "lightning", "headlights", "haze"];
2231
+ function createWeatherRenderer(THREE, options) {
2232
+ const { scene, camera: camera2, weather, vfx } = options;
2233
+ const group = new THREE.Group();
2234
+ group.name = "weather";
2235
+ scene.add(group);
2236
+ const curtain = createRainCurtain(THREE, { ...options.random ? { random: options.random } : {}, ...options.profile ? { profile: options.profile } : {} });
2237
+ const haze = createHeatHaze(THREE);
2238
+ group.add(curtain.object, haze.object);
2239
+ const sky = createSky(THREE, scene);
2240
+ const fog = createFog(THREE, scene);
2241
+ const lightning = createLightning(THREE, scene, vfx);
2242
+ const headlights = createHeadlights(THREE, options.headlights);
2243
+ const nearRain = createNearRain(vfx, { ...options.puddles ? { puddles: options.puddles } : {}, ...options.puddleAt ? { puddleAt: options.puddleAt } : {} });
2244
+ const on = new Map(WEATHER_LAYERS.map((id) => [id, options.layers?.[id] ?? true]));
2245
+ let zone = options.zone;
2246
+ let time = 0;
2247
+ const eye = { x: 0, y: 0, z: 0 };
2248
+ const switchedOff = (id) => {
2249
+ if (id === "far_rain") curtain.object.visible = false;
2250
+ else if (id === "near_rain") nearRain.clear();
2251
+ else if (id === "fog") fog.clear();
2252
+ else if (id === "lightning") lightning.light.intensity = 0;
2253
+ else if (id === "haze") haze.object.visible = false;
2254
+ };
2255
+ return {
2256
+ curtain,
2257
+ nearRain,
2258
+ fog,
2259
+ sky,
2260
+ lightning,
2261
+ headlights,
2262
+ haze,
2263
+ update(dt) {
2264
+ time += dt;
2265
+ const sample = weather.sample();
2266
+ const position = camera2.position;
2267
+ eye.x = position.x;
2268
+ eye.y = position.y;
2269
+ eye.z = position.z;
2270
+ const where = [eye.x, eye.y, eye.z];
2271
+ const background = scene.background ?? void 0;
2272
+ const ownSky = on.get("sky") === true;
2273
+ const color = ownSky ? sky.update(sample, zone) : background && background.isColor ? background : sky.color;
2274
+ if (on.get("fog")) fog.update(sample, color, zone);
2275
+ if (on.get("lightning")) lightning.update(sample, ownSky ? scene.background : void 0);
2276
+ if (on.get("far_rain")) curtain.update(sample, eye, time, sample.sun);
2277
+ if (on.get("haze")) haze.update(sample, time);
2278
+ if (vfx) {
2279
+ if (on.get("near_rain")) nearRain.update(sample, dt, where);
2280
+ if (on.get("mist")) emitAirborne(vfx.workshop(), sample, dt, where);
2281
+ }
2282
+ headlights.update(sample, dt, where, vfx && sample.rain > 0 ? vfx.workshop() : void 0, on.get("headlights"));
2283
+ },
2284
+ setLayer(id, value) {
2285
+ on.set(id, value);
2286
+ if (!value) switchedOff(id);
2287
+ },
2288
+ layer: (id) => on.get(id) === true,
2289
+ setZoneFog(next) {
2290
+ zone = next;
2291
+ },
2292
+ setProfile(profile) {
2293
+ curtain.setProfile(profile);
2294
+ },
2295
+ stats: () => ({ rainCurtain: curtain.drawn(), rainNear: nearRain.count(), cars: headlights.count() }),
2296
+ dispose() {
2297
+ scene.remove(group);
2298
+ curtain.dispose();
2299
+ haze.dispose();
2300
+ lightning.dispose();
2301
+ headlights.dispose();
2302
+ nearRain.clear();
2303
+ }
2304
+ };
2305
+ }
2306
+
2307
+ // src/bench/text.ts
2308
+ var TEXTS = {
2309
+ en: { layers: { far_rain: "Rain curtain", near_rain: "Near rain and splashes", mist: "Mist and ash", sky: "Sky", lightning: "Lightning", headlights: "Headlights", haze: "Heat haze" } },
2310
+ "pt-BR": { layers: { far_rain: "Cortina de chuva", near_rain: "Chuva perto e respingos", mist: "N\xE9voa e cinza", sky: "C\xE9u", lightning: "Rel\xE2mpago", headlights: "Far\xF3is", haze: "N\xE9voa de calor" } }
2311
+ };
2312
+ var weatherVisualTextOf = (locale) => TEXTS[locale] ?? TEXTS[locale.split("-")[0] ?? "en"] ?? TEXTS.en;
2313
+
2314
+ // src/bench/visual.ts
2315
+ var LAYER_ORDER = ["far_rain", "near_rain", "mist", "sky", "lightning", "headlights", "haze"];
2316
+ var RIG_DEFAULT = [1.6, -2.5, 0];
2317
+ function buildRig(THREE) {
2318
+ const group = new THREE.Group();
2319
+ group.name = "weather-lamp-rig";
2320
+ const body = new THREE.Mesh(new THREE.BoxGeometry(1.8, 0.7, 4.2), new THREE.MeshBasicMaterial({ color: 1776928 }));
2321
+ body.position.set(0, 0.5, 0);
2322
+ const lens2 = new THREE.MeshBasicMaterial({ color: HEADLIGHTS.lens.off });
2323
+ const lampGeometry = new THREE.BoxGeometry(0.35, 0.15, 0.05);
2324
+ for (const side of [-1, 1]) {
2325
+ const lamp = new THREE.Mesh(lampGeometry, lens2);
2326
+ lamp.position.set(side * HEADLIGHTS.lamp[0], HEADLIGHTS.lamp[1], 2.11);
2327
+ group.add(lamp);
2328
+ }
2329
+ group.add(body);
2330
+ return {
2331
+ group,
2332
+ lens: lens2,
2333
+ dispose() {
2334
+ body.geometry.dispose();
2335
+ body.material.dispose();
2336
+ lampGeometry.dispose();
2337
+ lens2.dispose();
2338
+ }
2339
+ };
2340
+ }
2341
+ function createWeatherVisual(options = {}) {
2342
+ return {
2343
+ layers(locale) {
2344
+ const text = weatherVisualTextOf(locale);
2345
+ return LAYER_ORDER.map((id) => ({ id, name: text.layers[id], default: options.layers?.[id] ?? true }));
2346
+ },
2347
+ create({ host, weather }) {
2348
+ const THREE = host.THREE;
2349
+ const renderer = createWeatherRenderer(THREE, {
2350
+ scene: host.scene,
2351
+ camera: host.camera,
2352
+ weather,
2353
+ ...options.vfx ? { vfx: options.vfx } : {},
2354
+ ...options.profile ? { profile: options.profile } : {},
2355
+ ...options.random ? { random: options.random } : {},
2356
+ ...options.zone ? { zone: options.zone } : {},
2357
+ ...options.puddles ? { puddles: options.puddles } : {},
2358
+ ...options.puddleAt ? { puddleAt: options.puddleAt } : {},
2359
+ ...options.headlights ? { headlights: options.headlights } : {},
2360
+ ...options.layers ? { layers: options.layers } : {}
2361
+ });
2362
+ return attachRig(THREE, host, renderer, options.rig === void 0 ? RIG_DEFAULT : options.rig);
2363
+ }
2364
+ };
2365
+ }
2366
+ function attachRig(THREE, host, renderer, place) {
2367
+ const scene = host.scene;
2368
+ const rig = place === false ? void 0 : buildRig(THREE);
2369
+ let detach;
2370
+ if (rig && place) {
2371
+ rig.group.position.set(place[0], 0, place[1]);
2372
+ rig.group.rotation.y = place[2];
2373
+ scene.add(rig.group);
2374
+ detach = renderer.headlights.attach(rig.group, rig.lens);
2375
+ }
2376
+ return {
2377
+ update: (dt) => renderer.update(dt),
2378
+ setLayer(id, on) {
2379
+ if (WEATHER_IDS.includes(id)) renderer.setLayer(id, on);
2380
+ },
2381
+ dispose() {
2382
+ detach?.();
2383
+ if (rig) {
2384
+ scene.remove(rig.group);
2385
+ rig.dispose();
2386
+ }
2387
+ renderer.dispose();
2388
+ }
2389
+ };
2390
+ }
2391
+ var WEATHER_IDS = ["far_rain", "near_rain", "mist", "fog", "sky", "lightning", "headlights", "haze"];
2392
+ return __toCommonJS(index_exports);
2393
+ })();
2394
+ //# sourceMappingURL=index.global.js.map