@adatechnology/game-fluids-three 0.1.7 → 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.
- package/README.md +23 -0
- package/dist/index.d.ts +315 -4
- package/dist/index.global.js +9628 -241
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +827 -193
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
package/dist/index.global.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/clip.ts","../src/liquid-body.ts","../src/util.ts","../src/materials.ts","../src/particles.ts","../src/quality.ts","../../game-fluids/src/data/liquids.json","../../game-fluids/src/liquids.ts","../../game-fluids/src/math.ts","../../game-fluids/src/landing.ts","../../game-fluids/src/locale.ts","../../game-fluids/src/shapes.ts","../../game-fluids/src/stains.ts","../../game-fluids/src/world.ts","../../game-fluids/src/data/item-uses.json","../../game-fluids/src/item-use.ts","../src/stains.ts","../src/streams.ts","../src/renderer.ts"],"sourcesContent":["export { createFluidRenderer } from './renderer'\nexport { smoothOutline } from './stains'\nexport type { BindOptions, FluidQuality, FluidRenderer, FluidRendererOptions, FluidRendererStats, ThreeLib } from './types'\n","/**\n * Convex solids (a cylinder or a box, in container space) clipped by the liquid surface plane `u · p <= d`.\n * This is robust for any tilt (upright, lying on its side, upside down): the result is exactly the part of the container the liquid fills,\n * with the surface cap lying on the plane. Written into preallocated buffers, no per-frame geometry is created.\n */\nexport type V3 = [number, number, number]\n\nexport interface SolidFace {\n p: V3[]\n n: V3[]\n}\n\nexport interface Solid {\n faces: SolidFace[]\n /** Upper bound of the vertices a clip can write (non-indexed triangles). */\n maxVertices: number\n}\n\nconst sub = (a: V3, b: V3): V3 => [a[0] - b[0], a[1] - b[1], a[2] - b[2]]\nconst cross = (a: V3, b: V3): V3 => [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]]\nconst dot = (a: V3, b: V3): number => a[0] * b[0] + a[1] * b[1] + a[2] * b[2]\nconst norm = (a: V3): V3 => {\n const l = Math.hypot(a[0], a[1], a[2]) || 1\n return [a[0] / l, a[1] / l, a[2] / l]\n}\n\n/** Reverse the winding when the face does not look outward. */\nconst orient = (p: V3[], n: V3[], outward: V3): SolidFace => {\n if (dot(cross(sub(p[1], p[0]), sub(p[2], p[0])), outward) < 0) return { p: [...p].reverse(), n: [...n].reverse() }\n return { p, n }\n}\n\nconst finish = (faces: SolidFace[]): Solid => {\n let maxVertices = 0\n for (const f of faces) maxVertices += (f.p.length * 2 + 2) * 3\n // The surface cap: at most one point per face edge cut.\n maxVertices += faces.length * 3 * 2 + 64\n return { faces, maxVertices }\n}\n\nexport const cylinderSolid = (radius: number, height: number, segments: number): Solid => {\n const faces: SolidFace[] = []\n const ring = (y: number, i: number): V3 => {\n const a = (i / segments) * Math.PI * 2\n return [Math.cos(a) * radius, y, Math.sin(a) * radius]\n }\n const radial = (i: number): V3 => {\n const a = (i / segments) * Math.PI * 2\n return [Math.cos(a), 0, Math.sin(a)]\n }\n for (let i = 0; i < segments; i++) {\n const j = (i + 1) % segments\n const mid = norm([radial(i)[0] + radial(j)[0], 0, radial(i)[2] + radial(j)[2]])\n faces.push(orient([ring(0, i), ring(0, j), ring(height, j), ring(height, i)], [radial(i), radial(j), radial(j), radial(i)], mid))\n }\n const bottom: V3[] = []\n const top: V3[] = []\n for (let i = 0; i < segments; i++) {\n bottom.push(ring(0, i))\n top.push(ring(height, i))\n }\n faces.push(orient(bottom, bottom.map((): V3 => [0, -1, 0]), [0, -1, 0]))\n faces.push(orient(top, top.map((): V3 => [0, 1, 0]), [0, 1, 0]))\n return finish(faces)\n}\n\nexport const boxSolid = (size: V3): Solid => {\n const hx = size[0] / 2\n const hz = size[2] / 2\n const hy = size[1]\n const c = (x: number, y: number, z: number): V3 => [x * hx, y * hy, z * hz]\n const quad = (a: V3, b: V3, cc: V3, d: V3, n: V3): SolidFace => orient([a, b, cc, d], [n, n, n, n], n)\n return finish([\n quad(c(1, 0, -1), c(1, 1, -1), c(1, 1, 1), c(1, 0, 1), [1, 0, 0]),\n quad(c(-1, 0, -1), c(-1, 1, -1), c(-1, 1, 1), c(-1, 0, 1), [-1, 0, 0]),\n quad(c(-1, 1, -1), c(1, 1, -1), c(1, 1, 1), c(-1, 1, 1), [0, 1, 0]),\n quad(c(-1, 0, -1), c(1, 0, -1), c(1, 0, 1), c(-1, 0, 1), [0, -1, 0]),\n quad(c(-1, 0, 1), c(1, 0, 1), c(1, 1, 1), c(-1, 1, 1), [0, 0, 1]),\n quad(c(-1, 0, -1), c(1, 0, -1), c(1, 1, -1), c(-1, 1, -1), [0, 0, -1]),\n ])\n}\n\nconst polyP: number[] = []\nconst polyN: number[] = []\nconst cut: number[] = []\n\n/**\n * Writes the triangles of `solid ∩ { u · p <= d }` into `pos` / `nor` (non-indexed, 3 floats per vertex) and returns the vertex count.\n * The cap on the plane has normal `u`.\n */\nexport const clipSolid = (solid: Solid, u: V3, d: number, pos: Float32Array, nor: Float32Array): number => {\n let count = 0\n const capacity = Math.floor(pos.length / 3)\n cut.length = 0\n const put = (px: number, py: number, pz: number, nx: number, ny: number, nz: number): void => {\n if (count >= capacity) return\n pos[count * 3] = px\n pos[count * 3 + 1] = py\n pos[count * 3 + 2] = pz\n nor[count * 3] = nx\n nor[count * 3 + 1] = ny\n nor[count * 3 + 2] = nz\n count++\n }\n\n for (const face of solid.faces) {\n const m = face.p.length\n polyP.length = 0\n polyN.length = 0\n for (let i = 0; i < m; i++) {\n const a = face.p[i]\n const b = face.p[(i + 1) % m]\n const na = face.n[i]\n const nb = face.n[(i + 1) % m]\n const da = u[0] * a[0] + u[1] * a[1] + u[2] * a[2] - d\n const db = u[0] * b[0] + u[1] * b[1] + u[2] * b[2] - d\n const ina = da <= 0\n const inb = db <= 0\n if (ina) {\n polyP.push(a[0], a[1], a[2])\n polyN.push(na[0], na[1], na[2])\n if (da > -1e-7) cut.push(a[0], a[1], a[2])\n }\n if (ina !== inb) {\n const t = da / (da - db)\n const x = a[0] + (b[0] - a[0]) * t\n const y = a[1] + (b[1] - a[1]) * t\n const z = a[2] + (b[2] - a[2]) * t\n polyP.push(x, y, z)\n polyN.push(na[0] + (nb[0] - na[0]) * t, na[1] + (nb[1] - na[1]) * t, na[2] + (nb[2] - na[2]) * t)\n cut.push(x, y, z)\n }\n }\n const n = polyP.length / 3\n for (let i = 1; i < n - 1; i++) {\n for (const k of [0, i, i + 1]) put(polyP[k * 3], polyP[k * 3 + 1], polyP[k * 3 + 2], polyN[k * 3], polyN[k * 3 + 1], polyN[k * 3 + 2])\n }\n }\n\n // Cap on the plane: unique cut points sorted around their centre.\n const pts: V3[] = []\n for (let i = 0; i < cut.length; i += 3) {\n let dup = false\n for (const q of pts) {\n if (Math.abs(q[0] - cut[i]) + Math.abs(q[1] - cut[i + 1]) + Math.abs(q[2] - cut[i + 2]) < 1e-6) {\n dup = true\n break\n }\n }\n if (!dup) pts.push([cut[i], cut[i + 1], cut[i + 2]])\n }\n if (pts.length >= 3) {\n const c: V3 = [0, 0, 0]\n for (const q of pts) {\n c[0] += q[0] / pts.length\n c[1] += q[1] / pts.length\n c[2] += q[2] / pts.length\n }\n const e1 = norm(cross(u, Math.abs(u[0]) < 0.9 ? [1, 0, 0] : [0, 1, 0]))\n const e2 = cross(u, e1)\n const keyed = pts.map((q) => ({ q, a: Math.atan2(dot(sub(q, c), e2), dot(sub(q, c), e1)) })).sort((p, r) => p.a - r.a)\n for (let i = 0; i < keyed.length; i++) {\n for (const q of [c, keyed[i].q, keyed[(i + 1) % keyed.length].q]) put(q[0], q[1], q[2], u[0], u[1], u[2])\n }\n }\n return count\n}\n","import type * as THREE_NS from 'three'\nimport type { ContainerState, FluidWorld, Vec3 } from '@adatechnology/game-fluids'\nimport { boxSolid, clipSolid, cylinderSolid } from './clip'\nimport type { Solid, V3 } from './clip'\nimport type { MaterialCache } from './materials'\nimport type { ParticlePool } from './particles'\nimport type { QualityProfile } from './quality'\nimport type { ThreeLib } from './types'\n\n/** How far the liquid is kept inside the container walls so it does not fight with them. */\nconst INSET = 0.985\n\n/**\n * The liquid inside one bound container: the container's inner shape clipped by the world surface plane, expressed in the bound object's space,\n * so the cap always lies on the plane whatever the tilt. Plus steam puffs above a hot surface.\n */\nexport class LiquidBody {\n readonly group: THREE_NS.Group\n private mesh: THREE_NS.Mesh | null = null\n private geometry: THREE_NS.BufferGeometry | null = null\n private pos: Float32Array = new Float32Array(0)\n private nor: Float32Array = new Float32Array(0)\n private solid: Solid | null = null\n private shapeKey = ''\n private steamAcc = 0\n private readonly inverse: THREE_NS.Matrix4\n private readonly tmpN: THREE_NS.Vector3\n private readonly tmpP: THREE_NS.Vector3\n vertices = 0\n\n constructor(\n private readonly THREE: ThreeLib,\n readonly containerId: string,\n readonly object: THREE_NS.Object3D,\n offset: Vec3,\n ) {\n this.group = new THREE.Group()\n this.group.name = 'fluid_liquid_body'\n this.group.position.set(offset[0], offset[1], offset[2])\n this.inverse = new THREE.Matrix4()\n this.tmpN = new THREE.Vector3()\n this.tmpP = new THREE.Vector3()\n object.add(this.group)\n }\n\n get visible(): boolean {\n return !!this.mesh && this.mesh.visible\n }\n\n private build(state: ContainerState, quality: QualityProfile): void {\n const shape = state.shape\n if (!shape) return\n const key = JSON.stringify(shape) + quality.bodySegments\n if (key === this.shapeKey) return\n this.shapeKey = key\n this.solid =\n shape.kind === 'cylinder'\n ? cylinderSolid(shape.radius * INSET, shape.height * INSET, quality.bodySegments)\n : boxSolid([shape.size[0] * INSET, shape.size[1] * INSET, shape.size[2] * INSET])\n const T = this.THREE\n this.geometry?.dispose()\n this.pos = new Float32Array(this.solid.maxVertices * 3)\n this.nor = new Float32Array(this.solid.maxVertices * 3)\n this.geometry = new T.BufferGeometry()\n const p = new T.BufferAttribute(this.pos, 3)\n const n = new T.BufferAttribute(this.nor, 3)\n p.setUsage(T.DynamicDrawUsage)\n n.setUsage(T.DynamicDrawUsage)\n this.geometry.setAttribute('position', p)\n this.geometry.setAttribute('normal', n)\n if (!this.mesh) {\n this.mesh = new T.Mesh(this.geometry)\n this.mesh.name = 'fluid_liquid'\n this.mesh.frustumCulled = false\n this.mesh.renderOrder = 3\n this.group.add(this.mesh)\n } else this.mesh.geometry = this.geometry\n }\n\n update(dt: number, world: FluidWorld, materials: MaterialCache, quality: QualityProfile, particles: ParticlePool): void {\n const state = world.container(this.containerId)\n const def = state?.liquid ? world.liquids.get(state.liquid) : undefined\n if (!state || !state.shape || !def || state.liters <= 1e-6) {\n if (this.mesh) this.mesh.visible = false\n this.vertices = 0\n return\n }\n this.build(state, quality)\n if (!this.mesh || !this.solid) return\n this.mesh.material = materials.liquidBody(def)\n\n // The surface plane in the group's space.\n this.object.updateWorldMatrix(true, false)\n this.group.updateWorldMatrix(true, false)\n this.inverse.copy(this.group.matrixWorld).invert()\n this.tmpN.set(state.surfaceNormal[0], state.surfaceNormal[1], state.surfaceNormal[2]).transformDirection(this.inverse)\n this.tmpP.set(state.surfacePoint[0], state.surfacePoint[1], state.surfacePoint[2]).applyMatrix4(this.inverse)\n const u: V3 = [this.tmpN.x, this.tmpN.y, this.tmpN.z]\n const d = u[0] * this.tmpP.x + u[1] * this.tmpP.y + u[2] * this.tmpP.z\n\n this.vertices = clipSolid(this.solid, u, d, this.pos, this.nor)\n const geometry = this.geometry as THREE_NS.BufferGeometry\n geometry.setDrawRange(0, this.vertices)\n ;(geometry.getAttribute('position') as THREE_NS.BufferAttribute).needsUpdate = true\n ;(geometry.getAttribute('normal') as THREE_NS.BufferAttribute).needsUpdate = true\n this.mesh.visible = this.vertices > 0\n\n // Steam above a hot open surface.\n if (state.steaming && this.vertices > 0) {\n this.steamAcc += dt * quality.steamRate\n while (this.steamAcc >= 1) {\n this.steamAcc -= 1\n const o = this.tmpP.set(state.surfacePoint[0], state.surfacePoint[1], state.surfacePoint[2])\n particles.steam([o.x, o.y + 0.01, o.z])\n }\n } else this.steamAcc = 0\n }\n\n get triangles(): number {\n return this.visible ? Math.floor(this.vertices / 3) : 0\n }\n\n dispose(): void {\n this.object.remove(this.group)\n this.geometry?.dispose()\n this.geometry = null\n this.mesh = null\n }\n}\n","export const clamp = (v: number, lo: number, hi: number): number => (v < lo ? lo : v > hi ? hi : v)\nexport const lerp = (a: number, b: number, t: number): number => a + (b - a) * t\n\n/** Small seeded generator for cosmetic randomness (droplet directions): the same input gives the same droplets. */\nexport class Rng {\n private state: number\n constructor(seed: number) {\n this.state = (seed >>> 0) || 1\n }\n next(): number {\n this.state = (this.state + 0x6d2b79f5) >>> 0\n let t = this.state\n t = Math.imul(t ^ (t >>> 15), t | 1)\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61)\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296\n }\n}\n\n/** Integer hash of a point, to seed cosmetic randomness from world state. */\nexport const hashPoint = (x: number, y: number, z: number): number => {\n let h = 2166136261\n for (const v of [x, y, z]) h = Math.imul(h ^ Math.round(v * 1000), 16777619)\n return h >>> 0\n}\n","import type * as THREE_NS from 'three'\nimport type { LiquidDef } from '@adatechnology/game-fluids'\nimport { clamp } from './util'\nimport type { ThreeLib } from './types'\n\n/** Colour of a stain: the liquid's `stainColor`, else a darker `color`. */\nexport const stainColorOf = (def: LiquidDef): number => {\n if (def.stainColor !== undefined) return def.stainColor\n const r = ((def.color >> 16) & 255) * 0.65\n const g = ((def.color >> 8) & 255) * 0.65\n const b = (def.color & 255) * 0.65\n return (Math.round(r) << 16) | (Math.round(g) << 8) | Math.round(b)\n}\n\n/**\n * One material per liquid and role, created on demand and shared by every mesh: each liquid is its own material (colour, opacity, gloss).\n * Wet surfaces use a low roughness from `gloss` so they reflect the scene's environment.\n */\nexport class MaterialCache {\n private body = new Map<string, THREE_NS.MeshStandardMaterial>()\n private stream = new Map<string, THREE_NS.MeshStandardMaterial>()\n private film = new Map<string, THREE_NS.MeshStandardMaterial>()\n private wet = new Map<string, THREE_NS.MeshStandardMaterial>()\n\n constructor(\n private readonly THREE: ThreeLib,\n private readonly envMap: THREE_NS.Texture | null,\n ) {}\n\n private make(params: THREE_NS.MeshStandardMaterialParameters): THREE_NS.MeshStandardMaterial {\n const m = new this.THREE.MeshStandardMaterial({ metalness: 0, ...params })\n if (this.envMap) m.envMap = this.envMap\n return m\n }\n\n /** The volume of liquid inside a container. */\n liquidBody(def: LiquidDef): THREE_NS.MeshStandardMaterial {\n let m = this.body.get(def.id)\n if (!m) {\n m = this.make({\n color: def.color,\n opacity: def.opacity,\n transparent: def.opacity < 0.999,\n roughness: clamp(1 - def.gloss * 0.9, 0.04, 1),\n envMapIntensity: 0.5 + def.gloss * 0.9,\n depthWrite: def.opacity >= 0.999,\n })\n this.body.set(def.id, m)\n }\n return m\n }\n\n /** A jet or a drop in the air. */\n liquidStream(def: LiquidDef): THREE_NS.MeshStandardMaterial {\n let m = this.stream.get(def.id)\n if (!m) {\n m = this.make({\n color: def.color,\n opacity: clamp(def.opacity + 0.1, 0.35, 1),\n transparent: true,\n roughness: clamp(1 - def.gloss * 0.95, 0.03, 1),\n envMapIntensity: 0.8 + def.gloss,\n side: this.THREE.DoubleSide,\n depthWrite: false,\n })\n this.stream.set(def.id, m)\n }\n return m\n }\n\n /** The thin outer film of a stain (and the permanent mark): tinted by vertex colour, soft rim by vertex alpha, mostly matte. */\n stainFilm(def: LiquidDef): THREE_NS.MeshStandardMaterial {\n let m = this.film.get(def.id)\n if (!m) {\n m = this.make({\n color: 0xffffff,\n vertexColors: true,\n transparent: true,\n roughness: clamp(0.95 - def.gloss * 0.45, 0.3, 1),\n envMapIntensity: 0.4 + def.gloss * 0.4,\n depthWrite: false,\n polygonOffset: true,\n polygonOffsetFactor: -2,\n polygonOffsetUnits: -2,\n })\n this.film.set(def.id, m)\n }\n return m\n }\n\n /** The glossy wet part of a stain, which shrinks while it dries. */\n stainWet(def: LiquidDef): THREE_NS.MeshStandardMaterial {\n let m = this.wet.get(def.id)\n if (!m) {\n m = this.make({\n color: 0xffffff,\n vertexColors: true,\n transparent: true,\n roughness: clamp(1 - def.gloss, 0.02, 0.7),\n envMapIntensity: 0.9 + def.gloss * 1.4,\n depthWrite: false,\n polygonOffset: true,\n polygonOffsetFactor: -3,\n polygonOffsetUnits: -3,\n })\n this.wet.set(def.id, m)\n }\n return m\n }\n\n dispose(): void {\n for (const map of [this.body, this.stream, this.film, this.wet]) {\n for (const m of map.values()) m.dispose()\n map.clear()\n }\n }\n}\n","import type * as THREE_NS from 'three'\nimport { Rng, clamp, hashPoint } from './util'\nimport type { ThreeLib } from './types'\n\nconst VERTEX = /* glsl */ `\nattribute float aSize;\nattribute vec4 aColor;\nattribute float aSoft;\nuniform float uScale;\nvarying vec4 vColor;\nvarying float vSoft;\nvoid main() {\n vColor = aColor;\n vSoft = aSoft;\n vec4 mv = modelViewMatrix * vec4(position, 1.0);\n gl_PointSize = aSize * uScale / max(0.1, -mv.z);\n gl_Position = projectionMatrix * mv;\n}\n`\n\n// Round sprite drawn in the fragment shader: no textures.\nconst FRAGMENT = /* glsl */ `\nvarying vec4 vColor;\nvarying float vSoft;\nvoid main() {\n float d = length(gl_PointCoord - vec2(0.5)) * 2.0;\n if (d > 1.0 || vColor.a <= 0.001) discard;\n float edge = mix(smoothstep(1.0, 0.8, d), 1.0 - d, vSoft);\n gl_FragColor = vec4(vColor.rgb, vColor.a * edge);\n}\n`\n\n/**\n * A fixed pool of round point sprites: splash droplets (ballistic, they die on the floor they hit) and steam (soft, rising puffs).\n * One draw call. When the pool is full the oldest slot is reused.\n */\nexport class ParticlePool {\n readonly points: THREE_NS.Points\n private readonly geometry: THREE_NS.BufferGeometry\n private readonly material: THREE_NS.ShaderMaterial\n private readonly capacity: number\n private next = 0\n private readonly pos: Float32Array\n private readonly size: Float32Array\n private readonly color: Float32Array\n private readonly soft: Float32Array\n private readonly vel: Float32Array\n private readonly life: Float32Array\n private readonly maxLife: Float32Array\n private readonly size0: Float32Array\n private readonly size1: Float32Array\n private readonly alpha: Float32Array\n private readonly floorY: Float32Array\n private readonly kind: Uint8Array\n private readonly rgb: Float32Array\n private readonly rng = new Rng(1234567)\n private alive = 0\n\n constructor(\n private readonly THREE: ThreeLib,\n capacity: number,\n pointScale: number,\n ) {\n this.capacity = capacity\n this.pos = new Float32Array(capacity * 3)\n this.size = new Float32Array(capacity)\n this.color = new Float32Array(capacity * 4)\n this.soft = new Float32Array(capacity)\n this.vel = new Float32Array(capacity * 3)\n this.life = new Float32Array(capacity)\n this.maxLife = new Float32Array(capacity)\n this.size0 = new Float32Array(capacity)\n this.size1 = new Float32Array(capacity)\n this.alpha = new Float32Array(capacity)\n this.floorY = new Float32Array(capacity)\n this.kind = new Uint8Array(capacity)\n this.rgb = new Float32Array(capacity * 3)\n this.life.fill(0)\n const T = THREE\n this.geometry = new T.BufferGeometry()\n const attr = (array: Float32Array, n: number) => {\n const a = new T.BufferAttribute(array, n)\n a.setUsage(T.DynamicDrawUsage)\n return a\n }\n this.geometry.setAttribute('position', attr(this.pos, 3))\n this.geometry.setAttribute('aSize', attr(this.size, 1))\n this.geometry.setAttribute('aColor', attr(this.color, 4))\n this.geometry.setAttribute('aSoft', attr(this.soft, 1))\n this.material = new T.ShaderMaterial({\n vertexShader: VERTEX,\n fragmentShader: FRAGMENT,\n uniforms: { uScale: { value: pointScale } },\n transparent: true,\n depthWrite: false,\n })\n this.points = new T.Points(this.geometry, this.material)\n this.points.frustumCulled = false\n this.points.renderOrder = 4\n this.points.name = 'fluid_particles'\n }\n\n get count(): number {\n return this.alive\n }\n\n private slot(): number {\n const i = this.next\n this.next = (this.next + 1) % this.capacity\n return i\n }\n\n private setColor(i: number, hex: number): void {\n this.rgb[i * 3] = ((hex >> 16) & 255) / 255\n this.rgb[i * 3 + 1] = ((hex >> 8) & 255) / 255\n this.rgb[i * 3 + 2] = (hex & 255) / 255\n }\n\n /** Droplets thrown up by an impact; `floorY` is the height they stop at. */\n splash(point: [number, number, number], intensity: number, hex: number, opacity: number, count: number): void {\n const rng = new Rng(hashPoint(point[0], point[1], point[2]) ^ this.next)\n const n = Math.max(2, Math.round(count * (0.35 + 0.65 * clamp(intensity, 0, 1))))\n for (let k = 0; k < n; k++) {\n const i = this.slot()\n const a = rng.next() * Math.PI * 2\n const h = (0.25 + rng.next() * 0.9) * (0.4 + intensity)\n const up = (0.7 + rng.next() * 1.5) * (0.5 + intensity)\n this.pos.set([point[0], point[1] + 0.01, point[2]], i * 3)\n this.vel.set([Math.cos(a) * h, up, Math.sin(a) * h], i * 3)\n this.life[i] = this.maxLife[i] = 0.45 + rng.next() * 0.45\n this.size0[i] = 0.012 + rng.next() * 0.014\n this.size1[i] = this.size0[i] * 0.7\n this.alpha[i] = clamp(opacity + 0.2, 0.35, 1)\n this.soft[i] = 0\n this.kind[i] = 0\n this.floorY[i] = point[1]\n this.setColor(i, hex)\n }\n }\n\n /** One puff of steam rising from `point`. */\n steam(point: [number, number, number]): void {\n const i = this.slot()\n const r = this.rng\n this.pos.set([point[0] + (r.next() - 0.5) * 0.04, point[1], point[2] + (r.next() - 0.5) * 0.04], i * 3)\n this.vel.set([(r.next() - 0.5) * 0.06, 0.22 + r.next() * 0.12, (r.next() - 0.5) * 0.06], i * 3)\n this.life[i] = this.maxLife[i] = 1.2 + r.next() * 0.6\n this.size0[i] = 0.03\n this.size1[i] = 0.1\n this.alpha[i] = 0.24\n this.soft[i] = 1\n this.kind[i] = 1\n this.floorY[i] = -1e9\n this.setColor(i, 0xf4f8ff)\n }\n\n update(dt: number, gravity: number): void {\n let alive = 0\n for (let i = 0; i < this.capacity; i++) {\n if (this.life[i] <= 0) {\n this.size[i] = 0\n this.color[i * 4 + 3] = 0\n continue\n }\n this.life[i] -= dt\n const k = 1 - this.life[i] / this.maxLife[i]\n if (this.life[i] <= 0) {\n this.size[i] = 0\n this.color[i * 4 + 3] = 0\n continue\n }\n alive++\n if (this.kind[i] === 0) {\n this.vel[i * 3 + 1] -= gravity * dt\n } else {\n // Steam drifts sideways a little while rising.\n this.vel[i * 3] += Math.sin((k + i) * 7) * 0.02 * dt\n }\n this.pos[i * 3] += this.vel[i * 3] * dt\n this.pos[i * 3 + 1] += this.vel[i * 3 + 1] * dt\n this.pos[i * 3 + 2] += this.vel[i * 3 + 2] * dt\n if (this.kind[i] === 0 && this.pos[i * 3 + 1] < this.floorY[i]) {\n this.life[i] = 0\n alive--\n this.size[i] = 0\n this.color[i * 4 + 3] = 0\n continue\n }\n this.size[i] = this.size0[i] + (this.size1[i] - this.size0[i]) * k\n const fade = this.kind[i] === 1 ? Math.min(1, k * 4) * (1 - k) : 1 - k * k\n this.color[i * 4] = this.rgb[i * 3]\n this.color[i * 4 + 1] = this.rgb[i * 3 + 1]\n this.color[i * 4 + 2] = this.rgb[i * 3 + 2]\n this.color[i * 4 + 3] = this.alpha[i] * fade\n }\n this.alive = alive\n for (const name of ['position', 'aSize', 'aColor', 'aSoft']) (this.geometry.getAttribute(name) as THREE_NS.BufferAttribute).needsUpdate = true\n }\n\n clear(): void {\n this.life.fill(0)\n this.alive = 0\n this.update(0, 0)\n }\n\n dispose(): void {\n this.geometry.dispose()\n this.material.dispose()\n }\n}\n","import type { FluidQuality } from './types'\n\nexport interface QualityProfile {\n /** Radial segments of the container liquid volume. */\n bodySegments: number\n /** Samples along a stream and sides of its tube. */\n streamSamples: number\n streamSides: number\n /** Drops of a thin stream. */\n thinDrops: number\n /** Subdivisions per front sample of a stain outline (16 samples). */\n stainSubdiv: number\n maxStains: number\n maxStreams: number\n maxParticles: number\n /** Steam puffs per second of a steaming container. */\n steamRate: number\n /** Droplets of a full-intensity splash. */\n splashDroplets: number\n}\n\nexport const PROFILES: Record<FluidQuality, QualityProfile> = {\n low: { bodySegments: 10, streamSamples: 8, streamSides: 4, thinDrops: 4, stainSubdiv: 1, maxStains: 24, maxStreams: 12, maxParticles: 96, steamRate: 4, splashDroplets: 5 },\n medium: { bodySegments: 14, streamSamples: 12, streamSides: 5, thinDrops: 6, stainSubdiv: 2, maxStains: 48, maxStreams: 24, maxParticles: 256, steamRate: 8, splashDroplets: 9 },\n high: { bodySegments: 20, streamSamples: 20, streamSides: 8, thinDrops: 8, stainSubdiv: 4, maxStains: 96, maxStreams: 48, maxParticles: 512, steamRate: 14, splashDroplets: 14 },\n}\n\nexport const profileOf = (q: FluidQuality | undefined): QualityProfile => PROFILES[q ?? 'medium'] ?? PROFILES.medium\n","[\n {\n \"id\": \"water\",\n \"color\": 11064558,\n \"opacity\": 0.35,\n \"stainColor\": 7309206,\n \"viscosity\": 1,\n \"density\": 1000,\n \"dryTime\": 600,\n \"gloss\": 0.9,\n \"drink\": {\n \"thirst\": 100,\n \"effects\": []\n },\n \"tags\": [\n \"drink\",\n \"clear\"\n ]\n },\n {\n \"id\": \"coffee\",\n \"color\": 3875604,\n \"opacity\": 0.95,\n \"stainColor\": 2824202,\n \"viscosity\": 1.5,\n \"density\": 1005,\n \"dryTime\": 900,\n \"gloss\": 0.8,\n \"leavesMark\": true,\n \"steamC\": 60,\n \"drink\": {\n \"thirst\": 80,\n \"effects\": [\n \"caffeine\"\n ]\n },\n \"tags\": [\n \"drink\",\n \"hot\"\n ]\n },\n {\n \"id\": \"tea\",\n \"color\": 9063188,\n \"opacity\": 0.8,\n \"stainColor\": 6961682,\n \"viscosity\": 1.4,\n \"density\": 1005,\n \"dryTime\": 800,\n \"gloss\": 0.8,\n \"leavesMark\": true,\n \"steamC\": 60,\n \"drink\": {\n \"thirst\": 90,\n \"effects\": []\n },\n \"tags\": [\n \"drink\",\n \"hot\"\n ]\n },\n {\n \"id\": \"milk\",\n \"color\": 16052712,\n \"opacity\": 0.98,\n \"stainColor\": 14473160,\n \"viscosity\": 2,\n \"density\": 1030,\n \"dryTime\": 900,\n \"gloss\": 0.5,\n \"leavesMark\": true,\n \"drink\": {\n \"thirst\": 70,\n \"effects\": []\n },\n \"tags\": [\n \"drink\"\n ]\n },\n {\n \"id\": \"soda\",\n \"color\": 3808530,\n \"opacity\": 0.85,\n \"stainColor\": 2757901,\n \"viscosity\": 1.3,\n \"density\": 1040,\n \"dryTime\": 1200,\n \"gloss\": 0.85,\n \"leavesMark\": true,\n \"drink\": {\n \"thirst\": 60,\n \"effects\": []\n },\n \"tags\": [\n \"drink\",\n \"sticky\"\n ]\n },\n {\n \"id\": \"juice\",\n \"color\": 15239467,\n \"opacity\": 0.9,\n \"stainColor\": 12741916,\n \"viscosity\": 2,\n \"density\": 1045,\n \"dryTime\": 1100,\n \"gloss\": 0.8,\n \"leavesMark\": true,\n \"drink\": {\n \"thirst\": 75,\n \"effects\": []\n },\n \"tags\": [\n \"drink\",\n \"sticky\"\n ]\n },\n {\n \"id\": \"beer\",\n \"color\": 14259740,\n \"opacity\": 0.8,\n \"stainColor\": 11039764,\n \"viscosity\": 1.4,\n \"density\": 1010,\n \"dryTime\": 900,\n \"gloss\": 0.8,\n \"drink\": {\n \"thirst\": 50,\n \"effects\": [\n \"alcohol\"\n ]\n },\n \"tags\": [\n \"drink\",\n \"alcohol\"\n ]\n },\n {\n \"id\": \"wine\",\n \"color\": 7016223,\n \"opacity\": 0.9,\n \"stainColor\": 5048854,\n \"viscosity\": 1.6,\n \"density\": 990,\n \"dryTime\": 800,\n \"gloss\": 0.85,\n \"leavesMark\": true,\n \"drink\": {\n \"thirst\": 40,\n \"effects\": [\n \"alcohol\"\n ]\n },\n \"tags\": [\n \"drink\",\n \"alcohol\"\n ]\n },\n {\n \"id\": \"spirits\",\n \"color\": 13214042,\n \"opacity\": 0.6,\n \"stainColor\": 10320959,\n \"viscosity\": 1.8,\n \"density\": 940,\n \"dryTime\": 200,\n \"gloss\": 0.85,\n \"flammable\": true,\n \"drink\": {\n \"thirst\": 5,\n \"effects\": [\n \"alcohol\"\n ]\n },\n \"tags\": [\n \"drink\",\n \"alcohol\"\n ]\n },\n {\n \"id\": \"fuel\",\n \"color\": 14926682,\n \"opacity\": 0.6,\n \"stainColor\": 7232298,\n \"viscosity\": 0.6,\n \"density\": 740,\n \"dryTime\": 120,\n \"gloss\": 0.95,\n \"leavesMark\": true,\n \"flammable\": true,\n \"drink\": {\n \"thirst\": 0,\n \"effects\": [\n \"poison\"\n ]\n },\n \"tags\": [\n \"fuel\",\n \"gasoline\"\n ]\n },\n {\n \"id\": \"diesel\",\n \"color\": 12098094,\n \"opacity\": 0.8,\n \"stainColor\": 5917208,\n \"viscosity\": 3,\n \"density\": 830,\n \"dryTime\": 3600,\n \"gloss\": 0.95,\n \"leavesMark\": true,\n \"flammable\": true,\n \"drink\": {\n \"thirst\": 0,\n \"effects\": [\n \"poison\"\n ]\n },\n \"tags\": [\n \"fuel\"\n ]\n },\n {\n \"id\": \"motor_oil\",\n \"color\": 3877388,\n \"opacity\": 0.95,\n \"stainColor\": 1840134,\n \"viscosity\": 100,\n \"density\": 880,\n \"dryTime\": 0,\n \"gloss\": 1,\n \"leavesMark\": true,\n \"flammable\": true,\n \"drink\": {\n \"thirst\": 0,\n \"effects\": [\n \"poison\"\n ]\n },\n \"tags\": [\n \"oil\"\n ]\n },\n {\n \"id\": \"cooking_oil\",\n \"color\": 14267708,\n \"opacity\": 0.85,\n \"stainColor\": 11110698,\n \"viscosity\": 60,\n \"density\": 920,\n \"dryTime\": 0,\n \"gloss\": 1,\n \"leavesMark\": true,\n \"flammable\": true,\n \"drink\": {\n \"thirst\": 0,\n \"effects\": [\n \"poison\"\n ]\n },\n \"tags\": [\n \"oil\",\n \"food\"\n ]\n },\n {\n \"id\": \"blood\",\n \"color\": 7997964,\n \"opacity\": 0.92,\n \"stainColor\": 4850950,\n \"viscosity\": 4,\n \"density\": 1060,\n \"dryTime\": 1200,\n \"gloss\": 0.75,\n \"leavesMark\": true,\n \"drink\": {\n \"thirst\": 10,\n \"effects\": [\n \"disgust\"\n ]\n },\n \"tags\": [\n \"organic\"\n ]\n },\n {\n \"id\": \"sap\",\n \"color\": 13146940,\n \"opacity\": 0.8,\n \"stainColor\": 10122282,\n \"viscosity\": 200,\n \"density\": 1100,\n \"dryTime\": 3600,\n \"gloss\": 0.8,\n \"leavesMark\": true,\n \"drink\": {\n \"thirst\": 20,\n \"effects\": []\n },\n \"tags\": [\n \"organic\",\n \"sticky\"\n ]\n },\n {\n \"id\": \"acid\",\n \"color\": 10146378,\n \"opacity\": 0.6,\n \"stainColor\": 7313970,\n \"viscosity\": 1.5,\n \"density\": 1200,\n \"dryTime\": 900,\n \"gloss\": 0.9,\n \"leavesMark\": true,\n \"drink\": {\n \"thirst\": 0,\n \"effects\": [\n \"poison\"\n ]\n },\n \"tags\": [\n \"corrosive\"\n ]\n },\n {\n \"id\": \"paint\",\n \"color\": 11740715,\n \"opacity\": 0.98,\n \"stainColor\": 9050655,\n \"viscosity\": 300,\n \"density\": 1300,\n \"dryTime\": 1800,\n \"gloss\": 0.6,\n \"leavesMark\": true,\n \"drink\": {\n \"thirst\": 0,\n \"effects\": [\n \"poison\"\n ]\n },\n \"tags\": [\n \"paint\"\n ]\n },\n {\n \"id\": \"bleach\",\n \"color\": 15134688,\n \"opacity\": 0.7,\n \"stainColor\": 13621446,\n \"viscosity\": 1.2,\n \"density\": 1100,\n \"dryTime\": 900,\n \"gloss\": 0.85,\n \"leavesMark\": true,\n \"drink\": {\n \"thirst\": 0,\n \"effects\": [\n \"poison\"\n ]\n },\n \"tags\": [\n \"corrosive\"\n ]\n },\n {\n \"id\": \"honey\",\n \"color\": 14193162,\n \"opacity\": 0.9,\n \"stainColor\": 12089350,\n \"viscosity\": 3000,\n \"density\": 1420,\n \"dryTime\": 14400,\n \"gloss\": 0.95,\n \"leavesMark\": true,\n \"drink\": {\n \"thirst\": 10,\n \"effects\": []\n },\n \"tags\": [\n \"drink\",\n \"sticky\"\n ]\n }\n]","import liquidsData from './data/liquids.json'\nimport type { LiquidDef } from './types'\n\n/**\n * Built-in liquids (data in `data/liquids.json`; colours are `0xRRGGBB` stored as integers).\n * Viscosity is relative to water, density in kg/m³, `dryTime` in seconds for a thin film at 20 °C (0 = never dries).\n */\nexport const LIQUIDS: LiquidDef[] = liquidsData as unknown as LiquidDef[]\n","import type { Quat, Vec3 } from './types'\n\nexport const GRAVITY = 9.81\nexport const EPS = 1e-7\n\nexport const clamp = (v: number, lo: number, hi: number): number => (v < lo ? lo : v > hi ? hi : v)\nexport const lerp = (a: number, b: number, t: number): number => a + (b - a) * t\n\nexport const add = (a: Vec3, b: Vec3): Vec3 => [a[0] + b[0], a[1] + b[1], a[2] + b[2]]\nexport const sub = (a: Vec3, b: Vec3): Vec3 => [a[0] - b[0], a[1] - b[1], a[2] - b[2]]\nexport const scale = (a: Vec3, s: number): Vec3 => [a[0] * s, a[1] * s, a[2] * s]\nexport const dot = (a: Vec3, b: Vec3): number => a[0] * b[0] + a[1] * b[1] + a[2] * b[2]\nexport const cross = (a: Vec3, b: Vec3): Vec3 => [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]]\nexport const length = (a: Vec3): number => Math.hypot(a[0], a[1], a[2])\nexport const distance = (a: Vec3, b: Vec3): number => Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2])\nexport const normalize = (a: Vec3, fallback: Vec3 = [0, 1, 0]): Vec3 => {\n const l = length(a)\n return l < 1e-9 ? [fallback[0], fallback[1], fallback[2]] : [a[0] / l, a[1] / l, a[2] / l]\n}\nexport const copy3 = (a: Vec3): Vec3 => [a[0], a[1], a[2]]\nexport const lerp3 = (a: Vec3, b: Vec3, t: number): Vec3 => [lerp(a[0], b[0], t), lerp(a[1], b[1], t), lerp(a[2], b[2], t)]\n\n/** Rotate `v` by the unit quaternion `q` (x, y, z, w). */\nexport const quatRotate = (q: Quat, v: Vec3): Vec3 => {\n const u: Vec3 = [q[0], q[1], q[2]]\n const c1 = cross(u, v)\n const c2 = cross(u, c1)\n return [v[0] + 2 * (q[3] * c1[0] + c2[0]), v[1] + 2 * (q[3] * c1[1] + c2[1]), v[2] + 2 * (q[3] * c1[2] + c2[2])]\n}\nexport const quatConjugate = (q: Quat): Quat => [-q[0], -q[1], -q[2], q[3]]\nexport const quatMultiply = (a: Quat, b: Quat): Quat => [\n a[3] * b[0] + a[0] * b[3] + a[1] * b[2] - a[2] * b[1],\n a[3] * b[1] - a[0] * b[2] + a[1] * b[3] + a[2] * b[0],\n a[3] * b[2] + a[0] * b[1] - a[1] * b[0] + a[2] * b[3],\n a[3] * b[3] - a[0] * b[0] - a[1] * b[1] - a[2] * b[2],\n]\nexport const quatNormalize = (q: Quat): Quat => {\n const l = Math.hypot(q[0], q[1], q[2], q[3])\n return l < 1e-9 ? [0, 0, 0, 1] : [q[0] / l, q[1] / l, q[2] / l, q[3] / l]\n}\n/** Quaternion from Euler angles in XYZ order (the same convention as three.js). */\nexport const quatFromEuler = (x: number, y: number, z: number): Quat => {\n const c1 = Math.cos(x / 2)\n const c2 = Math.cos(y / 2)\n const c3 = Math.cos(z / 2)\n const s1 = Math.sin(x / 2)\n const s2 = Math.sin(y / 2)\n const s3 = Math.sin(z / 2)\n return [s1 * c2 * c3 + c1 * s2 * s3, c1 * s2 * c3 - s1 * c2 * s3, c1 * c2 * s3 + s1 * s2 * c3, c1 * c2 * c3 - s1 * s2 * s3]\n}\n\n/** Small seeded generator (mulberry32). Its whole state is one integer, so it serialises trivially. */\nexport class Rng {\n state: number\n constructor(seed: number) {\n this.state = (Math.imul((seed | 0) ^ 0x9e3779b9, 0x85ebca6b) >>> 0) || 1\n }\n next(): number {\n this.state = (this.state + 0x6d2b79f5) >>> 0\n let t = this.state\n t = Math.imul(t ^ (t >>> 15), t | 1)\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61)\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296\n }\n}\n","import type { StreamState, Vec3 } from './types'\nimport { dot, sub } from './math'\nimport type { Basis } from './stains'\n\nexport const GROUND_ID = 'ground'\n/** Longest flight simulated for a stream before it counts as landing nowhere. */\nexport const MAX_FLIGHT = 3\n\n/** Position of a ballistic jet `t` seconds after leaving its origin. */\nexport const ballistic = (origin: Vec3, velocity: Vec3, gravity: number, t: number): Vec3 => [\n origin[0] + velocity[0] * t,\n origin[1] + velocity[1] * t - 0.5 * gravity * t * t,\n origin[2] + velocity[2] * t,\n]\n\n/** Point of a stream's path `t` seconds after its origin (use with `StreamState.flightTime` and the world's `gravity`). */\nexport const sampleStream = (stream: Pick<StreamState, 'origin' | 'velocity'>, gravity: number, t: number): Vec3 =>\n ballistic(stream.origin, stream.velocity, gravity, t)\n\nexport interface LandingSurface extends Basis {\n id: string\n point: Vec3\n half: [number, number]\n}\n\nexport interface Hit {\n t: number\n point: Vec3\n normal: Vec3\n surface: string\n /** Velocity of the jet at impact. */\n velocity: Vec3\n}\n\nconst velocityAt = (velocity: Vec3, gravity: number, t: number): Vec3 => [velocity[0], velocity[1] - gravity * t, velocity[2]]\n\n/** First thing a jet hits: an authored surface (plane with half extents) or else the ground. */\nexport const trace = (\n surfaces: Iterable<LandingSurface>,\n groundAt: (x: number, z: number) => number,\n origin: Vec3,\n velocity: Vec3,\n gravity: number,\n): Hit | null => {\n let best: Hit | null = null\n for (const s of surfaces) {\n const n = s.normal\n const c = dot(n, sub(origin, s.point))\n if (c < -0.005) continue\n const a = -0.5 * gravity * n[1]\n const b = dot(n, velocity)\n let t = Infinity\n if (Math.abs(a) < 1e-9) {\n if (b < -1e-9) t = -c / b\n } else {\n const disc = b * b - 4 * a * c\n if (disc >= 0) {\n const sq = Math.sqrt(disc)\n for (const root of [(-b - sq) / (2 * a), (-b + sq) / (2 * a)]) {\n if (root >= -1e-6 && b + 2 * a * root < 0 && root < t) t = root\n }\n }\n }\n if (!Number.isFinite(t) || t > MAX_FLIGHT) continue\n t = Math.max(0, t)\n if (best && t >= best.t) continue\n const p = ballistic(origin, velocity, gravity, t)\n const rel = sub(p, s.point)\n if (Math.abs(dot(rel, s.t1)) > s.half[0] || Math.abs(dot(rel, s.t2)) > s.half[1]) continue\n best = { t, point: p, normal: n, surface: s.id, velocity: velocityAt(velocity, gravity, t) }\n }\n\n // Ground: march the path and refine the crossing.\n const step = 0.02\n let prevT = 0\n const startOver = origin[1] - groundAt(origin[0], origin[2])\n const limit = best ? best.t : MAX_FLIGHT\n if (startOver <= 0) {\n return best && best.t <= 0 ? best : { t: 0, point: [origin[0], groundAt(origin[0], origin[2]), origin[2]], normal: [0, 1, 0], surface: GROUND_ID, velocity }\n }\n for (let t = step; t <= limit + step; t += step) {\n const tt = Math.min(t, limit)\n const p = ballistic(origin, velocity, gravity, tt)\n const over = p[1] - groundAt(p[0], p[2])\n if (over <= 0) {\n let lo = prevT\n let hi = tt\n for (let i = 0; i < 18; i++) {\n const mid = (lo + hi) / 2\n const q = ballistic(origin, velocity, gravity, mid)\n if (q[1] - groundAt(q[0], q[2]) > 0) lo = mid\n else hi = mid\n }\n if (best && hi >= best.t - 1e-3) return best\n const q = ballistic(origin, velocity, gravity, hi)\n return { t: hi, point: [q[0], groundAt(q[0], q[2]), q[2]], normal: [0, 1, 0], surface: GROUND_ID, velocity: velocityAt(velocity, gravity, hi) }\n }\n prevT = tt\n if (tt >= limit) break\n }\n return best\n}\n","import type { FluidEvent, LiquidId } from './types'\n\n/** Locales shipped with the package. Unknown locales fall back to `en`. */\nexport type FluidLocale = 'en' | 'pt-BR'\n\nexport interface FluidStrings {\n /** Display name per liquid id (unknown ids fall back to the id itself). */\n liquids: Record<LiquidId, string>\n /** Short label per event type. */\n events: Record<FluidEvent['type'], string>\n on: string\n off: string\n}\n\nexport const locales: Record<FluidLocale, FluidStrings> = {\n en: {\n liquids: {\n water: 'water',\n coffee: 'coffee',\n tea: 'tea',\n milk: 'milk',\n soda: 'soda',\n juice: 'juice',\n beer: 'beer',\n wine: 'wine',\n spirits: 'spirits',\n fuel: 'gasoline',\n diesel: 'diesel',\n motor_oil: 'motor oil',\n cooking_oil: 'cooking oil',\n blood: 'blood',\n sap: 'sap',\n acid: 'acid',\n paint: 'paint',\n bleach: 'bleach',\n honey: 'honey',\n },\n events: {\n fill: 'filled',\n pour: 'poured',\n spill: 'spilled',\n leak: 'leaking',\n drink: 'drank',\n empty: 'emptied',\n stream_end: 'stream ended',\n splash: 'splash',\n stain: 'stain',\n dried: 'dried',\n steam: 'steam',\n mix: 'mixed',\n tap: 'tap',\n },\n on: 'on',\n off: 'off',\n },\n 'pt-BR': {\n liquids: {\n water: 'água',\n coffee: 'café',\n tea: 'chá',\n milk: 'leite',\n soda: 'refrigerante',\n juice: 'suco',\n beer: 'cerveja',\n wine: 'vinho',\n spirits: 'destilado',\n fuel: 'gasolina',\n diesel: 'diesel',\n motor_oil: 'óleo de motor',\n cooking_oil: 'óleo de cozinha',\n blood: 'sangue',\n sap: 'seiva',\n acid: 'ácido',\n paint: 'tinta',\n bleach: 'água sanitária',\n honey: 'mel',\n },\n events: {\n fill: 'encheu',\n pour: 'despejou',\n spill: 'derramou',\n leak: 'vazando',\n drink: 'bebeu',\n empty: 'esvaziou',\n stream_end: 'parou de escorrer',\n splash: 'respingo',\n stain: 'mancha',\n dried: 'secou',\n steam: 'vapor',\n mix: 'misturou',\n tap: 'torneira',\n },\n on: 'ligado',\n off: 'desligado',\n },\n}\n\nexport const resolveStrings = (locale: string | undefined): FluidStrings => locales[(locale as FluidLocale) ?? 'en'] ?? locales.en\n\nconst fmt = (n: number): string => `${(Math.round(n * 1000) / 1000).toString()} l`\nconst pt = (p: [number, number, number]): string => `(${p.map((v) => Math.round(v * 100) / 100).join(', ')})`\n\n/** One-line description of an event for logs and debug panels, e.g. `drank: coffee 0.03 l`. */\nexport const describeEvent = (strings: FluidStrings, event: FluidEvent): string => {\n const label = strings.events[event.type]\n const name = (id: LiquidId) => strings.liquids[id] ?? id\n switch (event.type) {\n case 'fill':\n return `${label}: ${event.container} ${name(event.liquid)} ${fmt(event.liters)}`\n case 'pour':\n case 'spill':\n return `${label}: ${event.container} ${name(event.liquid)} ${fmt(event.rate)}/s`\n case 'leak':\n return `${label}: ${event.container} ${name(event.liquid)} ${fmt(event.rate)}/s ${pt(event.point)}`\n case 'drink':\n return `${label}: ${event.container} ${name(event.liquid)} ${fmt(event.liters)}${event.effects.length ? ` [${event.effects.join(', ')}]` : ''}${event.burn ? ' !' : ''}`\n case 'empty':\n return `${label}: ${event.container}`\n case 'stream_end':\n return `${label}: ${event.stream}`\n case 'splash':\n return `${label}: ${name(event.liquid)} ${pt(event.point)}`\n case 'stain':\n return `${label}: ${event.stain} ${name(event.liquid)} ${event.surface} ${pt(event.point)}`\n case 'dried':\n return `${label}: ${event.stain}${event.mark ? ' (mark)' : ''}`\n case 'steam':\n return `${label}: ${event.container} ${event.on ? strings.on : strings.off}`\n case 'mix':\n return `${label}: ${event.container} ${name(event.from)} -> ${name(event.into)}`\n case 'tap':\n return `${label}: ${event.tap} ${event.on ? strings.on : strings.off}`\n }\n}\n","import type { ContainerSpec, Vec3 } from './types'\nimport { clamp, normalize } from './math'\n\nexport type Shape = ContainerSpec['shape']\n\n/** Sample columns of a container's inner volume: equal-weight (x, z) positions with the full height `h`. */\nexport interface Columns {\n x: Float64Array\n z: Float64Array\n h: number\n /** Radius (cylinder) or half extents (box) used for the extent of a plane. */\n kind: 'cylinder' | 'box'\n r: number\n hx: number\n hz: number\n}\n\nconst RINGS = 8\nconst SEGMENTS = 16\nconst GRID = 12\n\nexport const makeColumns = (shape: Shape): Columns => {\n if (shape.kind === 'cylinder') {\n const x = new Float64Array(RINGS * SEGMENTS)\n const z = new Float64Array(RINGS * SEGMENTS)\n let n = 0\n for (let k = 0; k < RINGS; k++) {\n const rr = shape.radius * Math.sqrt((k + 0.5) / RINGS)\n for (let m = 0; m < SEGMENTS; m++) {\n // Offset every other ring so the samples do not line up radially.\n const a = ((m + (k % 2) * 0.5) / SEGMENTS) * Math.PI * 2\n x[n] = Math.cos(a) * rr\n z[n] = Math.sin(a) * rr\n n++\n }\n }\n return { x, z, h: shape.height, kind: 'cylinder', r: shape.radius, hx: shape.radius, hz: shape.radius }\n }\n const [sx, sy, sz] = shape.size\n const x = new Float64Array(GRID * GRID)\n const z = new Float64Array(GRID * GRID)\n let n = 0\n for (let i = 0; i < GRID; i++) {\n for (let j = 0; j < GRID; j++) {\n x[n] = ((i + 0.5) / GRID - 0.5) * sx\n z[n] = ((j + 0.5) / GRID - 0.5) * sz\n n++\n }\n }\n return { x, z, h: sy, kind: 'box', r: Math.hypot(sx, sz) / 2, hx: sx / 2, hz: sz / 2 }\n}\n\n/** Range of `u · p` over the container: below `lo` it is empty, above `hi` it is full. */\nexport const planeRange = (c: Columns, u: Vec3): [number, number] => {\n const horizontal = c.kind === 'cylinder' ? c.r * Math.hypot(u[0], u[2]) : Math.abs(u[0]) * c.hx + Math.abs(u[2]) * c.hz\n const top = u[1] * c.h\n return [-horizontal + Math.min(0, top), horizontal + Math.max(0, top)]\n}\n\n/** Fraction (0..1) of the inner volume with `u · p <= d` (the liquid for a surface plane with normal `u` at offset `d`). */\nexport const planeFraction = (c: Columns, u: Vec3, d: number): number => {\n const uy = u[1]\n let total = 0\n const n = c.x.length\n for (let i = 0; i < n; i++) {\n const s0 = u[0] * c.x[i] + u[2] * c.z[i]\n let len: number\n if (uy > 1e-6) len = clamp((d - s0) / uy, 0, c.h)\n else if (uy < -1e-6) len = c.h - clamp((d - s0) / uy, 0, c.h)\n else len = s0 <= d ? c.h : 0\n total += len\n }\n return total / (n * c.h)\n}\n\n/** Plane offset `d` along unit normal `u` for which the liquid takes the volume fraction `f`. */\nexport const levelFor = (c: Columns, u: Vec3, f: number): number => {\n const [lo0, hi0] = planeRange(c, u)\n if (f <= 0) return lo0\n if (f >= 1) return hi0\n if (Math.abs(u[0]) + Math.abs(u[2]) < 1e-9) {\n return u[1] > 0 ? f * c.h * u[1] : u[1] * c.h - f * c.h * u[1]\n }\n let lo = lo0\n let hi = hi0\n for (let i = 0; i < 28; i++) {\n const mid = (lo + hi) / 2\n if (planeFraction(c, u, mid) < f) lo = mid\n else hi = mid\n }\n return (lo + hi) / 2\n}\n\n/** Radius of the lip circle (the opening); a closed spec (`opening` 0) uses half the container width once opened. */\nexport const lipRadius = (shape: Shape, opening: number): number => {\n const limit = shape.kind === 'cylinder' ? shape.radius : Math.min(shape.size[0], shape.size[2]) / 2\n return opening > 0 ? Math.min(opening, limit) : limit * 0.5\n}\n\nexport const shapeHeight = (shape: Shape): number => (shape.kind === 'cylinder' ? shape.height : shape.size[1])\n\nexport interface Lip {\n /** Lowest lip point along the surface normal, container space. */\n point: Vec3\n /** Centre of the lip circle, container space. */\n center: Vec3\n /** Outward direction (container space, horizontal in the container frame) the liquid leaves along, or null when upright. */\n outward: Vec3 | null\n /** `u · point`: the surface offset above which the liquid spills. */\n level: number\n}\n\nexport const lipOf = (shape: Shape, opening: number, u: Vec3): Lip => {\n const h = shapeHeight(shape)\n const ro = lipRadius(shape, opening)\n const center: Vec3 = [0, h, 0]\n const uh = Math.hypot(u[0], u[2])\n if (uh < 1e-6) return { point: center, center, outward: null, level: u[1] * h }\n const out: Vec3 = normalize([-u[0], 0, -u[2]])\n const point: Vec3 = [out[0] * ro, h, out[2] * ro]\n return { point, center, outward: out, level: u[0] * point[0] + u[1] * point[1] + u[2] * point[2] }\n}\n","import type { LiquidDef, StainState, Vec3 } from './types'\nimport { Rng, add, clamp, cross, dot, length, normalize, scale, sub } from './math'\n\n/** Number of radial samples of the flow front. */\nexport const SAMPLES = 16\n\nconst COS: number[] = []\nconst SIN: number[] = []\nfor (let i = 0; i < SAMPLES; i++) {\n COS.push(Math.cos((i / SAMPLES) * Math.PI * 2))\n SIN.push(Math.sin((i / SAMPLES) * Math.PI * 2))\n}\n\n/** Orthonormal frame of a surface: `normal`, `t1` (tangent) and `t2 = normal × t1`. */\nexport interface Basis {\n normal: Vec3\n t1: Vec3\n t2: Vec3\n}\n\nexport const makeBasis = (normalIn: Vec3, tangentIn?: Vec3): Basis => {\n const normal = normalize(normalIn)\n const ref: Vec3 = tangentIn && length(tangentIn) > 1e-6 ? tangentIn : Math.abs(normal[1]) > 0.9 ? [1, 0, 0] : [0, 1, 0]\n const t1 = normalize(sub(ref, scale(normal, dot(ref, normal))), [1, 0, 0])\n return { normal, t1, t2: cross(normal, t1) }\n}\n\n/** What a stain needs to know about the surface it lies on. */\nexport interface StainSurface extends Basis {\n point: Vec3\n half: [number, number]\n porosity: number\n}\n\nexport interface StainInt extends StainState {\n normal: Vec3\n tangent: Vec3\n noise: number[]\n /** Seconds since the last inflow. */\n sinceInflow: number\n /** Drying progress 0..1. */\n dry: number\n mark: boolean\n age: number\n}\n\n/** Direction (world space) of radial sample `i` on a surface frame. */\nexport const sampleDir = (b: Basis, i: number): Vec3 => [\n b.t1[0] * COS[i] + b.t2[0] * SIN[i],\n b.t1[1] * COS[i] + b.t2[1] * SIN[i],\n b.t1[2] * COS[i] + b.t2[2] * SIN[i],\n]\n\nexport const stainArea = (front: number[]): number => {\n let a = 0\n for (let i = 0; i < SAMPLES; i++) a += front[i] * front[(i + 1) % SAMPLES]\n return 0.5 * Math.sin((Math.PI * 2) / SAMPLES) * a\n}\n\nexport const meanRadius = (front: number[]): number => front.reduce((s, v) => s + v, 0) / front.length\n\n/** Thickness (m) a puddle of this liquid cannot get thinner than: 1 mm for water, thicker for viscous liquids. */\nexport const minFilm = (def: LiquidDef): number => 0.001 * (1 + 0.8 * Math.log10(Math.max(1, def.viscosity)))\n\n/** Per-direction growth multipliers: low harmonics plus smoothed seeded noise, so the outline is never a disc. */\nexport const makeNoise = (rng: Rng): number[] => {\n const amp = [0.12 + rng.next() * 0.18, 0.08 + rng.next() * 0.14, 0.05 + rng.next() * 0.08]\n const phase = [rng.next() * Math.PI * 2, rng.next() * Math.PI * 2, rng.next() * Math.PI * 2]\n const raw: number[] = []\n for (let i = 0; i < SAMPLES; i++) {\n const a = (i / SAMPLES) * Math.PI * 2\n let v = 1 + (rng.next() - 0.5) * 0.3\n for (let k = 0; k < 3; k++) v += amp[k] * Math.sin((k + 2) * a + phase[k])\n raw.push(v)\n }\n const out = raw.map((v, i) => 0.25 * raw[(i + SAMPLES - 1) % SAMPLES] + 0.5 * v + 0.25 * raw[(i + 1) % SAMPLES])\n return out.map((v) => clamp(v, 0.45, 1.7))\n}\n\n/** Horizontal run direction on a surface (downhill, unit, in the plane) and the slope strength 0..1 (sin of the angle). */\nexport const downhill = (normal: Vec3): { dir: Vec3; slope: number } => {\n const d = add([0, -1, 0], scale(normal, normal[1]))\n const slope = length(d)\n return slope < 1e-6 ? { dir: [0, 0, 0], slope: 0 } : { dir: scale(d, 1 / slope), slope }\n}\n\n/** Creates a stain at `center` (on the surface) with a small starting front. */\nexport const createStain = (\n id: string,\n surfaceId: string,\n def: LiquidDef,\n basis: Basis,\n center: Vec3,\n liters: number,\n rng: Rng,\n): StainInt => {\n const r0 = clamp(Math.sqrt(liters / 1000 / minFilm(def) / Math.PI) * 0.4, 0.012, 0.12)\n return {\n id,\n surface: surfaceId,\n liquid: def.id,\n center: [center[0], center[1], center[2]],\n front: new Array(SAMPLES).fill(r0),\n liters,\n soaked: 0,\n wetness: 1,\n wetRadius: 1,\n normal: basis.normal,\n tangent: basis.t1,\n noise: makeNoise(rng),\n sinceInflow: 0,\n dry: 0,\n mark: false,\n age: 0,\n }\n}\n\n/** Adds liquid to a stain (rewets a dried mark). `velocity` makes the front stretch the way the liquid came in. */\nexport const addInflow = (s: StainInt, liters: number, velocity: Vec3 | null): void => {\n s.liters += liters\n s.sinceInflow = 0\n if (s.dry > 0 || s.mark) {\n s.dry = 0\n s.mark = false\n s.wetness = 1\n s.wetRadius = 1\n }\n if (velocity) {\n const n = s.normal\n const tangential = sub(velocity, scale(n, dot(velocity, n)))\n if (length(tangential) > 0.2) s.flow = normalize(tangential)\n }\n}\n\n/** Moves the centre towards an inflow point without moving the outline. */\nexport const nudgeCenter = (s: StainInt, point: Vec3, weight: number, basis: Basis): void => {\n const delta = scale(sub(point, s.center), weight)\n if (length(delta) < 1e-6) return\n s.center = add(s.center, delta)\n for (let i = 0; i < SAMPLES; i++) s.front[i] = Math.max(0.008, s.front[i] - dot(delta, sampleDir(basis, i)))\n}\n\nexport type StainStatus = 'alive' | 'dried_mark' | 'dried_gone'\n\n/** Largest radius along each sample direction before the centre's surface rectangle ends. */\nconst edgeLimit = (s: StainInt, surface: StainSurface, i: number): number => {\n if (!Number.isFinite(surface.half[0]) && !Number.isFinite(surface.half[1])) return Infinity\n const rel = sub(s.center, surface.point)\n const cx = dot(rel, surface.t1)\n const cy = dot(rel, surface.t2)\n let limit = Infinity\n if (COS[i] > 1e-6) limit = Math.min(limit, (surface.half[0] - cx) / COS[i])\n else if (COS[i] < -1e-6) limit = Math.min(limit, (-surface.half[0] - cx) / COS[i])\n if (SIN[i] > 1e-6) limit = Math.min(limit, (surface.half[1] - cy) / SIN[i])\n else if (SIN[i] < -1e-6) limit = Math.min(limit, (-surface.half[1] - cy) / SIN[i])\n return Math.max(0.01, limit)\n}\n\n/**\n * Advances a stain: the front grows (faster downhill, with seeded noise, tapering as the area approaches what the liquid can cover at its\n * minimum film thickness), porous surfaces soak the pool, and once the inflow stopped the stain dries from the edge in.\n */\nexport const stepStain = (s: StainInt, def: LiquidDef, surface: StainSurface, ambientC: number, dt: number): StainStatus => {\n s.age += dt\n s.sinceInflow += dt\n const basis: Basis = surface\n const { dir: down, slope } = downhill(s.normal)\n\n // Soak into porous surfaces.\n if (s.liters > 0 && surface.porosity > 0) {\n const soak = Math.min(s.liters, surface.porosity * 0.2 * stainArea(s.front) * dt)\n s.liters -= soak\n s.soaked += soak\n }\n\n // Grow the flow front.\n if (s.liters > 1e-7 && s.wetness > 0) {\n const area = stainArea(s.front)\n const cover = s.liters / 1000 / (minFilm(def) * (1 - 0.7 * slope))\n if (area < cover) {\n const taper = clamp(1 - area / cover, 0, 1)\n const v0 = (0.02 + 0.2 * Math.sqrt(s.liters)) / Math.sqrt(Math.max(1, def.viscosity))\n const delta: number[] = []\n for (let i = 0; i < SAMPLES; i++) {\n let m = s.noise[i]\n if (s.flow) m *= 1 + 0.5 * Math.max(0, dot(sampleDir(basis, i), s.flow))\n if (slope > 0.02) {\n const a = dot(sampleDir(basis, i), down)\n m *= a > 0 ? 1 + 14 * slope * a : Math.max(0.1, 1 + 2.5 * slope * a)\n }\n delta.push(Math.max(0, Math.min(s.front[i] + v0 * m * taper * dt, edgeLimit(s, surface, i)) - s.front[i]))\n }\n // Never overshoot the area the liquid can cover: scale the growth (not the outline) down to fit.\n let lambda = 1\n const apply = (k: number) => s.front.map((r, i) => r + delta[i] * k)\n if (stainArea(apply(1)) > cover) {\n let lo = 0\n let hi = 1\n for (let i = 0; i < 14; i++) {\n const mid = (lo + hi) / 2\n if (stainArea(apply(mid)) > cover) hi = mid\n else lo = mid\n }\n lambda = lo\n }\n s.front = apply(lambda)\n }\n }\n\n // Dry from the edge in once the inflow stopped.\n if (s.sinceInflow > 1 && def.dryTime > 0 && s.dry < 1) {\n const warm = Math.pow(2, (ambientC - 20) / 12)\n const ds = (dt * warm * (1 + 0.5 * surface.porosity)) / def.dryTime\n const before = s.dry\n s.dry = Math.min(1, s.dry + ds)\n if (before < 1) s.liters *= 1 - (s.dry - before) / (1 - before)\n s.wetness = 1 - s.dry\n s.wetRadius = Math.sqrt(Math.max(0, 1 - s.dry))\n if (s.dry >= 1) {\n s.liters = 0\n s.wetness = 0\n s.wetRadius = 0\n if (def.leavesMark) {\n s.mark = true\n return 'dried_mark'\n }\n return 'dried_gone'\n }\n }\n return 'alive'\n}\n\n/** Unit direction (world space) of front sample `i` of a stain, from its `normal` and `tangent`. */\nexport const stainDirection = (s: Pick<StainState, 'normal' | 'tangent'>, i: number): Vec3 => sampleDir(makeBasis(s.normal ?? [0, 1, 0], s.tangent), i)\n","import { LIQUIDS } from './liquids'\nimport { GROUND_ID, MAX_FLIGHT, ballistic, trace } from './landing'\nimport type { Hit, LandingSurface } from './landing'\nimport { describeEvent, resolveStrings } from './locale'\nimport { GRAVITY, Rng, add, clamp, copy3, cross, distance, dot, length, normalize, quatConjugate, quatRotate, scale } from './math'\nimport { levelFor, lipOf, lipRadius, makeColumns, planeFraction, planeRange, shapeHeight } from './shapes'\nimport type { Columns } from './shapes'\nimport { addInflow, createStain, makeBasis, meanRadius, nudgeCenter, stepStain } from './stains'\nimport type { Basis, StainInt, StainSurface } from './stains'\nimport type {\n ContainerSpec,\n ContainerState,\n FluidEvent,\n FluidOptions,\n FluidWorld,\n LiquidDef,\n LiquidId,\n Quat,\n StainState,\n StreamCause,\n StreamState,\n SurfaceSpec,\n Vec3,\n} from './types'\n\ninterface Hole {\n point: Vec3\n normal: Vec3\n diameter: number\n}\n\ninterface ContainerInt {\n id: string\n spec: ContainerSpec\n cols: Columns\n liquid: LiquidId | null\n liters: number\n temperature: number\n closed: boolean\n position: Vec3\n rotation: Quat\n velocity: Vec3\n prevVelocity: Vec3\n hasPrev: boolean\n holes: Hole[]\n /** Slosh of the liquid surface: tilt offsets and their rates (x, z, vx, vz). */\n slosh: [number, number, number, number]\n /** Surface normal in world space (up plus slosh) and in container space, and the surface offset along it. */\n normal: Vec3\n u: Vec3\n d: number\n steaming: boolean\n outflow: number\n /** Stream key (`spill`, `pour`, `leak:<n>`) to stream id. */\n streams: Record<string, string>\n pourRate: number\n pourTTL: number\n pourVisual: boolean\n lastMix: number\n}\n\ninterface TapInt {\n id: string\n position: Vec3\n direction: Vec3\n liquid: LiquidId\n rate: number\n temperature: number\n on: boolean\n stream?: string\n}\n\ninterface StreamInt extends StreamState {\n key: string\n lastSplash: number\n visualOnly: boolean\n temperature: number\n}\n\ninterface SurfaceInt extends SurfaceSpec, StainSurface {}\n\ninterface Snapshot {\n version: 1\n time: number\n rng: number\n counters: { container: number; stream: number; stain: number; tap?: number }\n taps?: TapInt[]\n containers: Array<Omit<ContainerInt, 'cols'>>\n surfaces: SurfaceSpec[]\n streams: StreamInt[]\n stains: StainInt[]\n}\n\nconst GROUND_POROSITY = 0.2\nconst MAX_STEP = 0.1\nconst MAX_SUBSTEPS = 50\nconst MAX_HOLES = 32\n\nconst clone = <T>(v: T): T => JSON.parse(JSON.stringify(v)) as T\n\nexport function createFluids(options: FluidOptions = {}): FluidWorld {\n const strings = resolveStrings(options.locale)\n const liquids = new Map<LiquidId, LiquidDef>()\n for (const def of LIQUIDS) liquids.set(def.id, def)\n for (const def of options.liquids ?? []) liquids.set(def.id, def)\n const ambientC = options.ambientC ?? 20\n const gravity = options.gravity ?? GRAVITY\n const groundAt = options.groundAt ?? (() => 0)\n const mergeDistance = options.mergeDistance ?? 0.15\n const maxStains = options.maxStains ?? 64\n\n let rng = new Rng(options.seed ?? 1)\n let time = 0\n const counters = { container: 0, stream: 0, stain: 0, tap: 0 }\n let containers = new Map<string, ContainerInt>()\n let surfaces = new Map<string, SurfaceInt>()\n let streams = new Map<string, StreamInt>()\n let taps = new Map<string, TapInt>()\n let stains = new Map<string, StainInt>()\n const listeners = new Map<string, Set<(e: FluidEvent) => void>>()\n let queue: FluidEvent[] = []\n /** Only events produced by `update` are queued and returned by it; direct calls just notify the listeners. */\n let updating = false\n\n const groundSurface: StainSurface = {\n normal: [0, 1, 0],\n t1: [1, 0, 0],\n t2: [0, 0, -1],\n point: [0, 0, 0],\n half: [Infinity, Infinity],\n porosity: GROUND_POROSITY,\n }\n\n // ---- events ----\n\n const emit = (e: FluidEvent): void => {\n if (updating) queue.push(e)\n options.onEvent?.(e)\n listeners.get(e.type)?.forEach((fn) => fn(e))\n listeners.get('*')?.forEach((fn) => fn(e))\n }\n\n // ---- helpers ----\n\n const toWorld = (c: ContainerInt, p: Vec3): Vec3 => add(c.position, quatRotate(c.rotation, p))\n const defOf = (c: ContainerInt): LiquidDef | null => (c.liquid ? (liquids.get(c.liquid) ?? null) : null)\n const viscScale = (def: LiquidDef): number => 1 / Math.sqrt(Math.max(1, def.viscosity))\n\n const refreshLevel = (c: ContainerInt): void => {\n c.normal = normalize([c.slosh[0], 1, c.slosh[1]])\n c.u = normalize(quatRotate(quatConjugate(c.rotation), c.normal))\n c.d = levelFor(c.cols, c.u, c.spec.capacity > 0 ? c.liters / c.spec.capacity : 0)\n }\n\n const surfaceFor = (id: string): StainSurface => (id === GROUND_ID ? groundSurface : (surfaces.get(id) ?? groundSurface))\n\n const landingSurfaces = (): LandingSurface[] => {\n const list: LandingSurface[] = []\n for (const [id, s] of surfaces) list.push({ id, normal: s.normal, t1: s.t1, t2: s.t2, point: s.point, half: s.half })\n return list\n }\n\n const stopStream = (id: string): void => {\n const s = streams.get(id)\n if (!s) return\n streams.delete(id)\n const c = s.container ? containers.get(s.container) : undefined\n if (c) delete c.streams[s.key]\n const tap = s.tap ? taps.get(s.tap) : undefined\n if (tap && tap.stream === id) tap.stream = undefined\n emit({ type: 'stream_end', stream: id })\n }\n\n const endStreamKey = (c: ContainerInt, key: string): void => {\n const id = c.streams[key]\n if (id) stopStream(id)\n }\n\n /** Create or update the stream under `key` of a container; creating one emits the matching cause event. */\n const syncStream = (\n c: ContainerInt,\n key: string,\n cause: StreamCause,\n rate: number,\n origin: Vec3,\n velocity: Vec3,\n visualOnly = false,\n ): void => {\n if (rate < 1e-5 || !c.liquid) {\n endStreamKey(c, key)\n return\n }\n const existing = c.streams[key] ? streams.get(c.streams[key]) : undefined\n if (existing) {\n existing.rate = rate\n existing.origin = origin\n existing.velocity = velocity\n existing.thin = rate < 0.04\n existing.visualOnly = visualOnly\n existing.temperature = c.temperature\n return\n }\n const id = `stream_${++counters.stream}`\n const stream: StreamInt = {\n id,\n liquid: c.liquid,\n cause,\n container: c.id,\n origin,\n velocity,\n rate,\n landing: null,\n thin: rate < 0.04,\n flightTime: MAX_FLIGHT,\n key,\n lastSplash: -1,\n visualOnly,\n temperature: c.temperature,\n }\n streams.set(id, stream)\n c.streams[key] = id\n if (cause === 'leak') emit({ type: 'leak', container: c.id, liquid: c.liquid, rate, stream: id, point: copy3(origin) })\n else if (cause === 'spill') emit({ type: 'spill', container: c.id, liquid: c.liquid, rate, stream: id })\n else emit({ type: 'pour', container: c.id, liquid: c.liquid, rate, stream: id })\n }\n\n /** Speed (m/s) at which a tap's liquid leaves the nozzle. */\n const TAP_SPEED = 1.2\n\n /** Create or update the `tap` stream of a tap that is on; end it when it is off. */\n const syncTap = (t: TapInt): void => {\n if (!t.on || t.rate < 1e-5 || !liquids.has(t.liquid)) {\n if (t.stream) stopStream(t.stream)\n return\n }\n const velocity: Vec3 = [t.direction[0] * TAP_SPEED, t.direction[1] * TAP_SPEED, t.direction[2] * TAP_SPEED]\n const existing = t.stream ? streams.get(t.stream) : undefined\n if (existing) {\n existing.rate = t.rate\n existing.thin = t.rate < 0.04\n existing.temperature = t.temperature\n existing.velocity = velocity\n return\n }\n const id = `stream_${++counters.stream}`\n streams.set(id, {\n id,\n liquid: t.liquid,\n cause: 'tap',\n tap: t.id,\n origin: copy3(t.position),\n velocity,\n rate: t.rate,\n landing: null,\n thin: t.rate < 0.04,\n flightTime: MAX_FLIGHT,\n key: `tap:${t.id}`,\n lastSplash: -1,\n visualOnly: false,\n temperature: t.temperature,\n })\n t.stream = id\n }\n\n /** Where and how fast the liquid leaves over the lowest lip point. */\n const lipFlow = (c: ContainerInt, head: number): { origin: Vec3; velocity: Vec3 } => {\n const lip = lipOf(c.spec.shape, c.spec.opening, c.u)\n const origin = toWorld(c, lip.point)\n let dir: Vec3 = [1, 0, 0]\n const horizontal = (v: Vec3): Vec3 | null => (Math.hypot(v[0], v[2]) > 1e-3 ? normalize([v[0], 0, v[2]]) : null)\n if (lip.outward) dir = horizontal(quatRotate(c.rotation, lip.outward)) ?? horizontal(quatRotate(c.rotation, [1, 0, 0])) ?? dir\n else dir = horizontal(quatRotate(c.rotation, [1, 0, 0])) ?? dir\n const speed = clamp(0.3 + 0.5 * Math.sqrt(2 * gravity * Math.max(0, head)), 0.2, 2.5)\n return { origin, velocity: [dir[0] * speed, -0.1, dir[2] * speed] }\n }\n\n const removeLiters = (c: ContainerInt, amount: number): void => {\n c.liters = Math.max(0, c.liters - amount)\n }\n\n /** Clear a container that ran out and emit `empty` once. */\n const settle = (c: ContainerInt): void => {\n if (c.liquid && c.liters <= 1e-6) {\n c.liters = 0\n c.liquid = null\n emit({ type: 'empty', container: c.id })\n }\n }\n\n const fillInternal = (c: ContainerInt, liquid: LiquidId, liters: number, temperature: number | undefined, announce: boolean): number => {\n if (!(liters > 0) || !liquids.has(liquid)) return 0\n const room = c.spec.capacity - c.liters\n const accepted = Math.min(liters, Math.max(0, room))\n if (accepted <= 0) return 0\n const t = temperature ?? ambientC\n if (c.liquid && c.liquid !== liquid && c.liters > 1e-6) {\n const incomingWins = accepted > c.liters\n const into = incomingWins ? liquid : c.liquid\n const from = incomingWins ? c.liquid : liquid\n if (announce || time - c.lastMix > 0.5) {\n c.lastMix = time\n emit({ type: 'mix', container: c.id, from, into })\n }\n c.liquid = into\n } else if (!c.liquid || c.liters <= 1e-6) {\n c.liquid = liquid\n }\n c.temperature = c.liters > 1e-6 ? (c.temperature * c.liters + t * accepted) / (c.liters + accepted) : t\n c.liters += accepted\n if (announce) emit({ type: 'fill', container: c.id, liquid, liters: accepted })\n return accepted\n }\n\n // ---- stains ----\n\n const stainAt = (surfaceId: string, liquid: LiquidId, point: Vec3): StainInt | null => {\n let best: StainInt | null = null\n let bestD = Infinity\n for (const s of stains.values()) {\n if (s.surface !== surfaceId || s.liquid !== liquid) continue\n const d = distance(point, s.center)\n if (d < mergeDistance + 0.5 * meanRadius(s.front) && d < bestD) {\n best = s\n bestD = d\n }\n }\n return best\n }\n\n const evictStain = (): void => {\n let victim: StainInt | null = null\n for (const s of stains.values()) {\n const rank = (x: StainInt) => (x.wetness <= 0 ? 0 : 1) * 1e9 - x.age\n if (!victim || rank(s) < rank(victim)) victim = s\n }\n if (victim) stains.delete(victim.id)\n }\n\n const deposit = (liquid: LiquidId, hit: Pick<Hit, 'point' | 'surface' | 'normal'>, liters: number, velocity: Vec3 | null): void => {\n const def = liquids.get(liquid)\n if (!def || liters <= 0) return\n let s = stainAt(hit.surface, liquid, hit.point)\n if (s) {\n const basis: Basis = { normal: s.normal, t1: s.tangent, t2: cross(s.normal, s.tangent) }\n nudgeCenter(s, hit.point, clamp((liters / (s.liters + liters)) * 0.5, 0, 0.5), basis)\n addInflow(s, liters, velocity)\n return\n }\n while (stains.size >= maxStains) evictStain()\n const surface = surfaceFor(hit.surface)\n const basis: Basis = hit.surface === GROUND_ID ? makeBasis(hit.normal) : surface\n s = createStain(`stain_${++counters.stain}`, hit.surface, def, basis, hit.point, liters, rng)\n if (velocity) addInflow(s, 0, velocity)\n s.sinceInflow = 0\n stains.set(s.id, s)\n emit({ type: 'stain', stain: s.id, surface: hit.surface, liquid, point: copy3(hit.point) })\n }\n\n // ---- streams ----\n\n const lipCenterWorld = (c: ContainerInt): Vec3 => toWorld(c, [0, shapeHeight(c.spec.shape), 0])\n\n /** A container whose opening the jet passes through before it lands (pouring into a cup). */\n const catcher = (s: StreamInt, landingT: number): { container: ContainerInt; t: number; point: Vec3 } | null => {\n let best: { container: ContainerInt; t: number; point: Vec3 } | null = null\n for (const oc of containers.values()) {\n if (oc.id === s.container || oc.closed || oc.liters >= oc.spec.capacity) continue\n const lip = lipCenterWorld(oc)\n const disc = s.velocity[1] * s.velocity[1] - 2 * gravity * (lip[1] - s.origin[1])\n if (disc < 0) continue\n const t = (s.velocity[1] + Math.sqrt(disc)) / gravity\n if (t <= 0 || t >= landingT || (best && t >= best.t)) continue\n const p = ballistic(s.origin, s.velocity, gravity, t)\n if (Math.hypot(p[0] - lip[0], p[2] - lip[2]) <= lipRadius(oc.spec.shape, oc.spec.opening) + 0.01) best = { container: oc, t, point: p }\n }\n return best\n }\n\n const stepStream = (s: StreamInt, dt: number): void => {\n const hit = trace(landingSurfaces(), groundAt, s.origin, s.velocity, gravity)\n const caught = catcher(s, hit ? hit.t : MAX_FLIGHT)\n if (caught) {\n s.landing = caught.point\n s.flightTime = caught.t\n if (!s.visualOnly) fillInternal(caught.container, s.liquid, s.rate * dt, s.temperature, false)\n return\n }\n s.landing = hit ? copy3(hit.point) : null\n s.flightTime = hit ? hit.t : MAX_FLIGHT\n if (!hit || s.visualOnly) return\n const speed = length(hit.velocity)\n if (time - s.lastSplash >= 0.12) {\n s.lastSplash = time\n emit({ type: 'splash', liquid: s.liquid, point: copy3(hit.point), intensity: clamp(0.15 + s.rate * 1.2 + speed * 0.05, 0, 1) })\n }\n deposit(s.liquid, hit, s.rate * dt, hit.velocity)\n }\n\n // ---- containers ----\n\n const spillRate = (c: ContainerInt, def: LiquidDef, h: number): number => {\n const ro = lipRadius(c.spec.shape, c.spec.opening)\n const area = 2 * ro * Math.min(h, ro)\n return Math.max(0.6 * area * Math.sqrt(2 * gravity * h) * 1000 * viscScale(def), 0.004)\n }\n\n const steamThreshold = (def: LiquidDef): number => def.steamC ?? (def.drink && def.drink.thirst > 0 ? 60 : Infinity)\n\n const stepContainer = (c: ContainerInt, dt: number): void => {\n // Slosh: the surface tilts towards the horizontal acceleration and settles with a damped spring.\n if (c.hasPrev && dt > 0) {\n const ax = (c.velocity[0] - c.prevVelocity[0]) / dt\n const az = (c.velocity[2] - c.prevVelocity[2]) / dt\n const tx = clamp(ax / gravity, -0.35, 0.35)\n const tz = clamp(az / gravity, -0.35, 0.35)\n const w = 9\n const z = 0.12\n c.slosh[2] += (w * w * (tx - c.slosh[0]) - 2 * z * w * c.slosh[2]) * dt\n c.slosh[3] += (w * w * (tz - c.slosh[1]) - 2 * z * w * c.slosh[3]) * dt\n } else {\n c.slosh[2] *= 0.5\n c.slosh[3] *= 0.5\n }\n c.slosh[0] += c.slosh[2] * dt\n c.slosh[1] += c.slosh[3] * dt\n if (Math.abs(c.slosh[0]) + Math.abs(c.slosh[1]) + Math.abs(c.slosh[2]) + Math.abs(c.slosh[3]) < 1e-5) c.slosh = [0, 0, 0, 0]\n c.prevVelocity = copy3(c.velocity)\n c.hasPrev = true\n\n // Cooling towards ambient (insulated containers 10× slower).\n if (c.temperature !== ambientC) {\n const tau = (600 + 2400 * c.liters) * (c.spec.insulated ? 10 : 1)\n c.temperature = ambientC + (c.temperature - ambientC) * Math.exp(-dt / tau)\n if (Math.abs(c.temperature - ambientC) < 0.01) c.temperature = ambientC\n }\n\n refreshLevel(c)\n const def = defOf(c)\n\n const steaming = !!def && c.liters > 1e-6 && !c.closed && c.temperature > steamThreshold(def)\n if (steaming !== c.steaming) {\n c.steaming = steaming\n emit({ type: 'steam', container: c.id, on: steaming })\n }\n\n c.outflow = 0\n if (!def || c.liters <= 1e-6) {\n for (const key of Object.keys(c.streams)) endStreamKey(c, key)\n c.pourTTL = 0\n settle(c)\n return\n }\n\n // Pour (driven by `pour()`), else spill over the lip when the tilted surface rises above it.\n if (c.pourTTL > 0) {\n c.pourTTL -= dt\n if (c.pourTTL > 0) {\n const flow = lipFlow(c, 0.05)\n syncStream(c, 'pour', 'pour', c.pourRate, flow.origin, flow.velocity, c.pourVisual)\n c.outflow += c.pourRate\n } else endStreamKey(c, 'pour')\n endStreamKey(c, 'spill')\n } else {\n endStreamKey(c, 'pour')\n const lip = lipOf(c.spec.shape, c.spec.opening, c.u)\n const h = c.d - lip.level\n let handled = false\n if (!c.closed && h > 1e-5) {\n const excess = c.liters - c.spec.capacity * planeFraction(c.cols, c.u, lip.level)\n if (excess > 1e-6) {\n const rate = Math.min(spillRate(c, def, h), excess / Math.max(dt, 1e-6))\n removeLiters(c, Math.min(rate * dt, excess, c.liters))\n const flow = lipFlow(c, h)\n syncStream(c, 'spill', 'spill', rate, flow.origin, flow.velocity)\n c.outflow += rate\n handled = true\n }\n }\n if (!handled) endStreamKey(c, 'spill')\n }\n\n // Holes leak by Torricelli with the head above each hole.\n for (let i = 0; i < c.holes.length; i++) {\n const hole = c.holes[i]\n const key = `leak:${i}`\n const hh = dot(c.u, hole.point)\n const head = c.d - hh\n if (c.liters > 1e-6 && head > 1e-4) {\n const area = Math.PI * (hole.diameter / 2) ** 2\n const natural = 0.6 * area * Math.sqrt(2 * gravity * head) * 1000 * viscScale(def)\n const avail = c.liters - c.spec.capacity * planeFraction(c.cols, c.u, hh)\n if (avail > 1e-7) {\n const rate = Math.min(natural, avail / Math.max(dt, 1e-6))\n removeLiters(c, Math.min(rate * dt, avail))\n const nW = normalize(quatRotate(c.rotation, hole.normal))\n const v = Math.min(3, 0.6 * Math.sqrt(2 * gravity * head))\n syncStream(c, key, 'leak', rate, toWorld(c, hole.point), [nW[0] * v, nW[1] * v - 0.05, nW[2] * v])\n c.outflow += rate\n continue\n }\n }\n endStreamKey(c, key)\n }\n if (c.holes.length > 0 || c.outflow > 0) refreshLevel(c)\n settle(c)\n }\n\n const step = (dt: number): void => {\n time += dt\n for (const c of [...containers.values()]) stepContainer(c, dt)\n for (const s of [...streams.values()]) stepStream(s, dt)\n for (const st of [...stains.values()]) {\n const def = liquids.get(st.liquid)\n if (!def) continue\n const status = stepStain(st, def, surfaceFor(st.surface), ambientC, dt)\n if (status === 'dried_mark') emit({ type: 'dried', stain: st.id, mark: true })\n else if (status === 'dried_gone') {\n stains.delete(st.id)\n emit({ type: 'dried', stain: st.id, mark: false })\n }\n }\n }\n\n // ---- public state views ----\n\n const stateOf = (c: ContainerInt): ContainerState => ({\n id: c.id,\n liquid: c.liquid,\n liters: c.liters,\n capacity: c.spec.capacity,\n temperature: c.temperature,\n closed: c.closed,\n surfaceHeight: c.liters > 0 ? c.d : 0,\n surfacePoint: add(c.position, scale(c.normal, c.liters > 0 ? c.d : 0)),\n surfaceNormal: copy3(c.normal),\n holes: c.holes.map((h) => ({ point: copy3(h.point), normal: copy3(h.normal), diameter: h.diameter })),\n outflow: c.outflow,\n steaming: c.steaming,\n shape: c.spec.shape,\n lipRadius: lipRadius(c.spec.shape, c.spec.opening),\n position: copy3(c.position),\n rotation: [...c.rotation] as Quat,\n })\n\n const streamView = (s: StreamInt): StreamState => ({\n id: s.id,\n liquid: s.liquid,\n cause: s.cause,\n container: s.container,\n tap: s.tap,\n origin: copy3(s.origin),\n velocity: copy3(s.velocity),\n rate: s.rate,\n landing: s.landing ? copy3(s.landing) : null,\n thin: s.thin,\n flightTime: s.flightTime,\n })\n\n const stainView = (s: StainInt): StainState => ({\n id: s.id,\n surface: s.surface,\n liquid: s.liquid,\n center: copy3(s.center),\n front: [...s.front],\n liters: s.liters,\n soaked: s.soaked,\n wetness: s.wetness,\n wetRadius: s.wetRadius,\n normal: copy3(s.normal),\n tangent: copy3(s.tangent),\n flow: s.flow ? copy3(s.flow) : undefined,\n mark: s.mark,\n age: s.age,\n })\n\n // ---- world API ----\n\n const world: FluidWorld = {\n liquids,\n gravity,\n describe: (event) => describeEvent(strings, event),\n\n addContainer(spec, opts = {}) {\n const id = opts.id ?? `container_${++counters.container}`\n if (containers.has(id)) world.removeContainer(id)\n const liquid = spec.liquid && liquids.has(spec.liquid) ? spec.liquid : null\n const liters = liquid ? clamp(spec.liters ?? 0, 0, spec.capacity) : 0\n const c: ContainerInt = {\n id,\n spec: clone(spec),\n cols: makeColumns(spec.shape),\n liquid: liters > 0 ? liquid : null,\n liters,\n temperature: spec.temperature ?? ambientC,\n closed: spec.closed ?? spec.opening <= 0,\n position: copy3(opts.position ?? [0, 0, 0]),\n rotation: opts.rotation ? ([...opts.rotation] as Quat) : [0, 0, 0, 1],\n velocity: [0, 0, 0],\n prevVelocity: [0, 0, 0],\n hasPrev: false,\n holes: [],\n slosh: [0, 0, 0, 0],\n normal: [0, 1, 0],\n u: [0, 1, 0],\n d: 0,\n steaming: false,\n outflow: 0,\n streams: {},\n pourRate: 0,\n pourTTL: 0,\n pourVisual: false,\n lastMix: -10,\n }\n containers.set(id, c)\n refreshLevel(c)\n return id\n },\n\n addTap(spec) {\n const id = spec.id ?? `tap_${++counters.tap}`\n if (taps.has(id)) world.removeTap(id)\n const d = spec.direction ?? [0, -1, 0]\n const len = Math.hypot(d[0], d[1], d[2]) || 1\n const t: TapInt = {\n id,\n position: copy3(spec.position),\n direction: [d[0] / len, d[1] / len, d[2] / len],\n liquid: spec.liquid,\n rate: Math.max(0, spec.rate),\n temperature: spec.temperature ?? ambientC,\n on: false,\n }\n taps.set(id, t)\n if (spec.on ?? true) world.setTap(id, true)\n return id\n },\n\n setTap(id, on, rate) {\n const t = taps.get(id)\n if (!t) return\n if (rate !== undefined) t.rate = Math.max(0, rate)\n const changed = t.on !== on\n t.on = on\n syncTap(t)\n if (changed) emit({ type: 'tap', tap: id, on })\n },\n\n removeTap(id) {\n const t = taps.get(id)\n if (!t) return\n t.on = false\n syncTap(t)\n taps.delete(id)\n },\n\n taps: () => [...taps.values()].map((t) => ({ id: t.id, position: copy3(t.position), direction: copy3(t.direction), liquid: t.liquid, rate: t.rate, temperature: t.temperature, on: t.on, stream: t.stream })),\n\n removeContainer(id) {\n const c = containers.get(id)\n if (!c) return\n for (const key of Object.keys(c.streams)) endStreamKey(c, key)\n containers.delete(id)\n },\n\n setTransform(id, position, rotation, velocity) {\n const c = containers.get(id)\n if (!c) return\n c.position = copy3(position)\n c.rotation = [...rotation] as Quat\n if (velocity) c.velocity = copy3(velocity)\n refreshLevel(c)\n },\n\n open(id) {\n const c = containers.get(id)\n if (c) c.closed = false\n },\n\n close(id) {\n const c = containers.get(id)\n if (!c) return\n c.closed = true\n for (const key of ['pour', 'spill']) endStreamKey(c, key)\n c.pourTTL = 0\n },\n\n fill(id, liquid, liters, temperature) {\n const c = containers.get(id)\n if (!c || c.closed) return 0\n const accepted = fillInternal(c, liquid, liters, temperature, true)\n refreshLevel(c)\n return accepted\n },\n\n pour(id, dt, into) {\n const c = containers.get(id)\n const def = c ? defOf(c) : null\n if (!c || !def || c.closed || c.liters <= 1e-6 || !(dt > 0)) return 0\n refreshLevel(c)\n const lip = lipOf(c.spec.shape, c.spec.opening, c.u)\n const depth = Math.max(0, c.d - planeRange(c.cols, c.u)[0])\n const head = Math.max(c.d - lip.level, 0.02 + 0.5 * depth)\n const ro = lipRadius(c.spec.shape, c.spec.opening)\n const natural = 0.6 * Math.PI * ro * ro * Math.sqrt(2 * gravity * head) * 1000\n const rate = Math.min(natural, 0.15 + 12 * ro) * viscScale(def)\n const out = Math.min(rate * dt, c.liters)\n const target = into ? containers.get(into) : undefined\n let delivered = out\n if (target) {\n delivered = fillInternal(target, c.liquid as LiquidId, out, c.temperature, false)\n refreshLevel(target)\n const overflow = out - delivered\n if (overflow > 1e-6) {\n const flow = lipFlow(c, head)\n const hit = trace(landingSurfaces(), groundAt, flow.origin, flow.velocity, gravity)\n if (hit) deposit(c.liquid as LiquidId, hit, overflow, hit.velocity)\n }\n }\n removeLiters(c, out)\n c.pourRate = out / dt\n c.pourTTL = Math.max(0.12, dt * 1.5)\n c.pourVisual = !!target\n const flow = lipFlow(c, head)\n syncStream(c, 'pour', 'pour', c.pourRate, flow.origin, flow.velocity, c.pourVisual)\n refreshLevel(c)\n settle(c)\n return out\n },\n\n drink(id, liters) {\n const c = containers.get(id)\n const def = c ? defOf(c) : null\n if (!c || !def || c.closed || c.liters <= 1e-6 || !(liters > 0)) return null\n const amount = Math.min(liters, c.liters)\n removeLiters(c, amount)\n const event: Extract<FluidEvent, { type: 'drink' }> = {\n type: 'drink',\n container: c.id,\n liquid: def.id,\n liters: amount,\n temperature: c.temperature,\n thirst: (def.drink?.thirst ?? 0) * amount,\n effects: [...(def.drink?.effects ?? [])],\n burn: c.temperature > 65,\n }\n emit(event)\n refreshLevel(c)\n settle(c)\n return event\n },\n\n puncture(id, point, normal, diameter) {\n const c = containers.get(id)\n if (!c || !(diameter > 0) || c.holes.length >= MAX_HOLES) return\n c.holes.push({ point: copy3(point), normal: normalize(normal), diameter })\n },\n\n heat(id, deltaC) {\n const c = containers.get(id)\n if (c) c.temperature = clamp(c.temperature + deltaC, -50, 1000)\n },\n\n addSurface(spec) {\n const basis = makeBasis(spec.normal, spec.tangent)\n surfaces.set(spec.id, {\n ...clone(spec),\n ...basis,\n point: copy3(spec.point),\n half: [spec.halfSize[0], spec.halfSize[1]],\n })\n },\n\n removeSurface(id) {\n surfaces.delete(id)\n for (const s of [...stains.values()]) if (s.surface === id) stains.delete(s.id)\n },\n\n splash(liquid, point, liters, velocity) {\n if (!liquids.has(liquid) || !(liters > 0)) return\n const v: Vec3 = velocity ?? [0, -0.1, 0]\n const origin = add(point, [0, 0.02, 0])\n const hit = trace(landingSurfaces(), groundAt, origin, v, gravity)\n if (!hit) return\n emit({ type: 'splash', liquid, point: copy3(hit.point), intensity: clamp(0.3 + liters * 2, 0, 1) })\n deposit(liquid, hit, liters, length(v) > 0.3 ? hit.velocity : null)\n },\n\n clearStains(point, radius) {\n for (const s of [...stains.values()]) {\n if (distance(point, s.center) <= radius + 0.5 * meanRadius(s.front)) {\n stains.delete(s.id)\n emit({ type: 'dried', stain: s.id, mark: false })\n }\n }\n },\n\n update(dt) {\n if (dt > 0) {\n const n = clamp(Math.ceil(dt / MAX_STEP), 1, MAX_SUBSTEPS)\n updating = true\n try {\n for (let i = 0; i < n; i++) step(dt / n)\n } finally {\n updating = false\n }\n }\n const out = queue\n queue = []\n return out\n },\n\n container: (id) => {\n const c = containers.get(id)\n return c ? stateOf(c) : null\n },\n containers: () => [...containers.values()].map(stateOf),\n streams: () => [...streams.values()].map(streamView),\n stains: () => [...stains.values()].map(stainView),\n\n on(type, fn) {\n let set = listeners.get(type)\n if (!set) listeners.set(type, (set = new Set()))\n set.add(fn)\n return () => {\n set?.delete(fn)\n }\n },\n\n serialize() {\n const snapshot: Snapshot = {\n version: 1,\n time,\n rng: rng.state,\n counters: { ...counters },\n containers: [...containers.values()].map((c) => {\n const { cols: _cols, ...rest } = c\n return clone(rest)\n }),\n surfaces: [...surfaces.values()].map((s) => clone({ id: s.id, point: s.point, normal: s.normal, halfSize: s.half, tangent: s.t1, porosity: s.porosity })),\n streams: [...streams.values()].map((s) => clone(s)),\n taps: [...taps.values()].map((t) => clone(t)),\n stains: [...stains.values()].map((s) => clone(s)),\n }\n return snapshot\n },\n\n load(input) {\n const snap = clone(input) as Snapshot\n if (!snap || snap.version !== 1) throw new Error('game-fluids: unsupported snapshot')\n time = snap.time\n rng = new Rng(0)\n rng.state = snap.rng\n counters.container = snap.counters.container\n counters.stream = snap.counters.stream\n counters.stain = snap.counters.stain\n containers = new Map()\n for (const raw of snap.containers) containers.set(raw.id, { ...raw, cols: makeColumns(raw.spec.shape) })\n surfaces = new Map()\n for (const s of snap.surfaces) world.addSurface(s)\n counters.tap = snap.counters.tap ?? 0\n streams = new Map(snap.streams.map((s) => [s.id, s]))\n taps = new Map((snap.taps ?? []).map((t) => [t.id, t]))\n stains = new Map(snap.stains.map((s) => [s.id, s]))\n queue = []\n },\n }\n\n return world\n}\n","[\n {\n \"id\": \"sip\",\n \"duration\": 1.2,\n \"keys\": [\n {\n \"t\": 0,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 0.35,\n \"position\": [\n 0.0,\n 0.02,\n -0.2\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 0.45,\n \"position\": [\n 0.0,\n 0.02,\n -0.2\n ],\n \"rotation\": [\n -0.9,\n 0,\n 0\n ],\n \"action\": \"drink\",\n \"rate\": 0.06\n },\n {\n \"t\": 0.95,\n \"position\": [\n 0.0,\n 0.02,\n -0.2\n ],\n \"rotation\": [\n -0.9,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 1.2,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n }\n ]\n },\n {\n \"id\": \"drink\",\n \"duration\": 2.5,\n \"keys\": [\n {\n \"t\": 0,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 0.5,\n \"position\": [\n 0.0,\n 0.02,\n -0.2\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 0.8,\n \"position\": [\n 0.0,\n 0.02,\n -0.2\n ],\n \"rotation\": [\n -1.22,\n 0,\n 0\n ],\n \"action\": \"drink\",\n \"rate\": 0.1667\n },\n {\n \"t\": 2.0,\n \"position\": [\n 0.0,\n 0.02,\n -0.2\n ],\n \"rotation\": [\n -1.22,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 2.5,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n }\n ]\n },\n {\n \"id\": \"chug\",\n \"duration\": 4,\n \"keys\": [\n {\n \"t\": 0,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 0.5,\n \"position\": [\n 0.0,\n 0.02,\n -0.2\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 0.9,\n \"position\": [\n 0.0,\n 0.02,\n -0.2\n ],\n \"rotation\": [\n -1.45,\n 0,\n 0\n ],\n \"action\": \"drink\",\n \"rate\": 0.2\n },\n {\n \"t\": 3.4,\n \"position\": [\n 0.0,\n 0.02,\n -0.2\n ],\n \"rotation\": [\n -1.45,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 4,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n }\n ]\n },\n {\n \"id\": \"pour\",\n \"duration\": 3,\n \"keys\": [\n {\n \"t\": 0,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 0.4,\n \"position\": [\n 0.1,\n 0.0,\n -0.35\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 0.8,\n \"position\": [\n 0.1,\n 0.0,\n -0.35\n ],\n \"rotation\": [\n -1.745,\n 0,\n 0\n ],\n \"action\": \"pour\",\n \"rate\": 1\n },\n {\n \"t\": 2.4,\n \"position\": [\n 0.1,\n 0.0,\n -0.35\n ],\n \"rotation\": [\n -1.745,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 3,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n }\n ]\n },\n {\n \"id\": \"pour_out\",\n \"duration\": 4,\n \"keys\": [\n {\n \"t\": 0,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 0.5,\n \"position\": [\n 0.1,\n 0.1,\n -0.35\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 1.0,\n \"position\": [\n 0.1,\n 0.1,\n -0.35\n ],\n \"rotation\": [\n -2.2,\n 0,\n 0\n ],\n \"action\": \"pour\",\n \"rate\": 40\n },\n {\n \"t\": 3.2,\n \"position\": [\n 0.1,\n 0.1,\n -0.35\n ],\n \"rotation\": [\n -2.2,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 4,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n }\n ]\n },\n {\n \"id\": \"open\",\n \"duration\": 1,\n \"keys\": [\n {\n \"t\": 0,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 0.25,\n \"position\": [\n 0.1,\n 0,\n -0.3\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"open\"\n },\n {\n \"t\": 0.7,\n \"position\": [\n 0.1,\n 0,\n -0.3\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 1,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n }\n ]\n },\n {\n \"id\": \"close\",\n \"duration\": 1,\n \"keys\": [\n {\n \"t\": 0,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 0.25,\n \"position\": [\n 0.1,\n 0,\n -0.3\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"close\"\n },\n {\n \"t\": 0.7,\n \"position\": [\n 0.1,\n 0,\n -0.3\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 1,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n }\n ]\n }\n]","import itemUsesData from './data/item-uses.json'\nimport { lerp3, quatFromEuler, quatMultiply, quatRotate, add } from './math'\nimport type { FluidEvent, FluidWorld, ItemUseDef, ItemUsePlayer, Quat, Vec3 } from './types'\n\n/** Built-in scripted uses of a held item (data in `data/item-uses.json`): `sip`, `drink`, `chug`, `pour`, `pour_out`, `open`, `close`. */\nexport const ITEM_USES: ItemUseDef[] = itemUsesData as unknown as ItemUseDef[]\n\n/** World transform of the hand (or any holder) the item pose is relative to. */\nexport type HandTransform = () => { position: Vec3; rotation: Quat }\n\nconst DEFAULT_DRINK_RATE = 0.1\n\n/**\n * Plays an item use on a container: interpolates the keys, applies `drink` (rate × dt litres) and `pour` (`world.pour`, `rate` multiplies the\n * time) while the current key has that action, and fires `open` / `close` once when their key is reached. When `getHandTransform` is given, the\n * container's world transform follows hand ∘ pose every update; otherwise the host animates the viewmodel from `pose()`.\n */\nexport function createItemUse(world: FluidWorld, containerId: string, def: ItemUseDef, getHandTransform?: HandTransform): ItemUsePlayer {\n const keys = [...def.keys].sort((a, b) => a.t - b.t)\n let time = 0\n let done = keys.length === 0 || def.duration <= 0\n let checked = false\n let lastPos: Vec3 | null = null\n const fired = new Set<number>()\n\n const pose = (): { position: Vec3; rotation: Vec3 } => {\n if (keys.length === 0) return { position: [0, 0, 0], rotation: [0, 0, 0] }\n if (time <= keys[0].t) return { position: [...keys[0].position], rotation: [...keys[0].rotation] }\n for (let i = 0; i < keys.length - 1; i++) {\n const a = keys[i]\n const b = keys[i + 1]\n if (time <= b.t) {\n const k = b.t > a.t ? (time - a.t) / (b.t - a.t) : 1\n return { position: lerp3(a.position, b.position, k), rotation: lerp3(a.rotation, b.rotation, k) }\n }\n }\n const last = keys[keys.length - 1]\n return { position: [...last.position], rotation: [...last.rotation] }\n }\n\n const follow = (dt: number): void => {\n if (!getHandTransform) return\n const hand = getHandTransform()\n const p = pose()\n const position = add(hand.position, quatRotate(hand.rotation, p.position))\n const rotation = quatMultiply(hand.rotation, quatFromEuler(p.rotation[0], p.rotation[1], p.rotation[2]))\n const velocity: Vec3 | undefined =\n lastPos && dt > 0 ? [(position[0] - lastPos[0]) / dt, (position[1] - lastPos[1]) / dt, (position[2] - lastPos[2]) / dt] : undefined\n lastPos = position\n world.setTransform(containerId, position, rotation, velocity)\n }\n\n const player: ItemUsePlayer = {\n def,\n get time() {\n return time\n },\n get done() {\n return done\n },\n pose,\n update(dt) {\n if (done) return []\n const events: FluidEvent[] = []\n const off = world.on('*', (e) => events.push(e))\n try {\n if (!checked) {\n checked = true\n const state = world.container(containerId)\n if (!state || (def.liquids && def.liquids.length > 0 && (!state.liquid || !def.liquids.includes(state.liquid)))) {\n done = true\n return events\n }\n }\n const start = time\n const end = Math.min(def.duration, time + Math.max(0, dt))\n for (let i = 0; i < keys.length && !done; i++) {\n const key = keys[i]\n const spanStart = key.t\n const spanEnd = i + 1 < keys.length ? keys[i + 1].t : def.duration\n if ((key.action === 'open' || key.action === 'close') && !fired.has(i) && end >= spanStart) {\n fired.add(i)\n if (key.action === 'open') world.open(containerId)\n else world.close(containerId)\n }\n const overlap = Math.min(end, spanEnd) - Math.max(start, spanStart)\n if (overlap <= 0) continue\n if (key.action === 'drink') {\n const ev = world.drink(containerId, (key.rate ?? DEFAULT_DRINK_RATE) * overlap)\n const left = world.container(containerId)\n if (!ev || !left || left.liters <= 1e-6) {\n time = def.duration\n done = true\n }\n } else if (key.action === 'pour') {\n world.pour(containerId, overlap * (key.rate ?? 1))\n }\n }\n if (!done) {\n time = end\n done = time >= def.duration\n }\n follow(dt)\n } finally {\n off()\n }\n return events\n },\n cancel() {\n done = true\n },\n }\n return player\n}\n","import type * as THREE_NS from 'three'\nimport { STAIN_SAMPLES } from '@adatechnology/game-fluids'\nimport type { LiquidDef, StainState, Vec3 } from '@adatechnology/game-fluids'\nimport type { MaterialCache } from './materials'\nimport { stainColorOf } from './materials'\nimport type { QualityProfile } from './quality'\nimport { clamp } from './util'\nimport type { ThreeLib } from './types'\n\n/** Periodic Catmull-Rom of the 16 front radii: a smooth outline with `subdiv` points per sample. */\nexport const smoothOutline = (front: number[], subdiv: number, out?: number[]): number[] => {\n const n = front.length\n const res = out ?? []\n res.length = n * subdiv\n for (let i = 0; i < n; i++) {\n const p0 = front[(i + n - 1) % n]\n const p1 = front[i]\n const p2 = front[(i + 1) % n]\n const p3 = front[(i + 2) % n]\n for (let j = 0; j < subdiv; j++) {\n const t = j / subdiv\n const r = 0.5 * (2 * p1 + (-p0 + p2) * t + (2 * p0 - 5 * p1 + 4 * p2 - p3) * t * t + (-p0 + 3 * p1 - 3 * p2 + p3) * t * t * t)\n res[i * subdiv + j] = Math.max(0.004, r)\n }\n }\n return res\n}\n\ninterface Fan {\n mesh: THREE_NS.Mesh\n position: Float32Array\n color: Float32Array\n}\n\n/**\n * One stain: an outer film (the whole flow-front polygon, soft at the rim) and an inner glossy fan at `wetRadius` that recedes while it dries.\n * Both are triangle fans with preallocated buffers, lying on the surface plane a hair above it.\n */\nexport class StainVisual {\n readonly group: THREE_NS.Group\n private readonly outer: Fan\n private readonly inner: Fan\n private readonly ring: number\n private readonly radii: number[] = []\n private readonly tint: THREE_NS.Color\n stainId = ''\n signature = Number.NaN\n triangles = 0\n\n constructor(\n private readonly THREE: ThreeLib,\n profile: QualityProfile,\n readonly slot: number,\n ) {\n this.ring = STAIN_SAMPLES * profile.stainSubdiv\n this.group = new THREE.Group()\n this.group.name = 'fluid_stain'\n this.tint = new THREE.Color()\n this.outer = this.makeFan('fluid_stain_film', 1)\n this.inner = this.makeFan('fluid_stain_wet', 2)\n this.group.add(this.outer.mesh, this.inner.mesh)\n this.group.visible = false\n }\n\n private makeFan(name: string, order: number): Fan {\n const T = this.THREE\n const count = 1 + this.ring\n const position = new Float32Array(count * 3)\n const color = new Float32Array(count * 4)\n const normal = new Float32Array(count * 3)\n const geometry = new T.BufferGeometry()\n const p = new T.BufferAttribute(position, 3)\n const c = new T.BufferAttribute(color, 4)\n p.setUsage(T.DynamicDrawUsage)\n c.setUsage(T.DynamicDrawUsage)\n geometry.setAttribute('position', p)\n geometry.setAttribute('normal', new T.BufferAttribute(normal, 3))\n geometry.setAttribute('color', c)\n const index: number[] = []\n for (let i = 0; i < this.ring; i++) index.push(0, 1 + i, 1 + ((i + 1) % this.ring))\n geometry.setIndex(index)\n const mesh = new T.Mesh(geometry)\n mesh.name = name\n mesh.frustumCulled = false\n mesh.renderOrder = order\n return { mesh, position, color }\n }\n\n assign(def: LiquidDef, materials: MaterialCache): void {\n this.outer.mesh.material = materials.stainFilm(def)\n this.inner.mesh.material = materials.stainWet(def)\n }\n\n hide(): void {\n this.group.visible = false\n this.stainId = ''\n this.signature = Number.NaN\n this.triangles = 0\n }\n\n /** Rebuild the vertices from the stain state (skipped when nothing changed). */\n update(stain: StainState, def: LiquidDef, profile: QualityProfile): void {\n const sig =\n stain.center[0] * 3 +\n stain.center[1] * 5 +\n stain.center[2] * 7 +\n stain.front.reduce((s, v, i) => s + v * (i + 1), 0) * 11 +\n stain.wetness * 13 +\n stain.wetRadius * 17 +\n (stain.soaked / (stain.soaked + stain.liters + 1e-6)) * 19\n if (sig === this.signature && this.group.visible) return\n this.signature = sig\n\n const n: Vec3 = stain.normal ?? [0, 1, 0]\n const t1: Vec3 = stain.tangent ?? [1, 0, 0]\n const t2: Vec3 = [n[1] * t1[2] - n[2] * t1[1], n[2] * t1[0] - n[0] * t1[2], n[0] * t1[1] - n[1] * t1[0]]\n smoothOutline(stain.front, profile.stainSubdiv, this.radii)\n const ring = this.ring\n const lift = 0.0015 + 0.0004 * (this.slot % 6)\n const mean = stain.front.reduce((s, v) => s + v, 0) / stain.front.length\n const wet = clamp(stain.wetness, 0, 1)\n const soakRatio = stain.soaked / (stain.soaked + stain.liters + 1e-6)\n\n // Outer film: tinted by the liquid, darker while wet and where it soaked in, fainter once only the mark is left.\n this.tint.setHex(stainColorOf(def))\n const dark = 1 - 0.3 * wet - 0.2 * soakRatio\n const markAlpha = def.leavesMark ? 0.34 : 0.1\n const aMid = (markAlpha + (0.62 - markAlpha) * wet) * (0.75 + 0.25 * Math.min(1, stain.liters * 20 + soakRatio))\n this.writeFan(this.outer, stain.center, n, t1, t2, lift, 1, this.tint.r * dark, this.tint.g * dark, this.tint.b * dark, aMid, aMid * 0.45)\n this.outer.mesh.visible = true\n\n // Inner glossy wet fan, centred where the liquid came in, shrinking as it dries.\n const wr = clamp(stain.wetRadius, 0, 1)\n const showInner = wr > 0.05 && wet > 0.03\n this.inner.mesh.visible = showInner\n if (showInner) {\n this.tint.setHex(def.color)\n const shift = stain.flow ? -mean * 0.1 * (1 - wr) : 0\n const centre: Vec3 = stain.flow\n ? [stain.center[0] + stain.flow[0] * shift, stain.center[1] + stain.flow[1] * shift, stain.center[2] + stain.flow[2] * shift]\n : stain.center\n const a = clamp(def.opacity, 0.2, 0.85) * wet * 0.8\n this.writeFan(this.inner, centre, n, t1, t2, lift + 0.0007, 0.9 * wr, this.tint.r, this.tint.g, this.tint.b, a, a * 0.5)\n }\n this.triangles = ring * (showInner ? 2 : 1)\n this.group.visible = true\n }\n\n private writeFan(\n fan: Fan,\n center: Vec3,\n n: Vec3,\n t1: Vec3,\n t2: Vec3,\n lift: number,\n scale: number,\n r: number,\n g: number,\n b: number,\n aCentre: number,\n aRim: number,\n ): void {\n const ring = this.ring\n const px = center[0] + n[0] * lift\n const py = center[1] + n[1] * lift\n const pz = center[2] + n[2] * lift\n fan.position[0] = px\n fan.position[1] = py\n fan.position[2] = pz\n fan.color[0] = r * 0.85\n fan.color[1] = g * 0.85\n fan.color[2] = b * 0.85\n fan.color[3] = aCentre\n const normalAttr = fan.mesh.geometry.getAttribute('normal') as THREE_NS.BufferAttribute\n for (let i = 0; i <= ring; i++) {\n normalAttr.setXYZ(i, n[0], n[1], n[2])\n }\n normalAttr.needsUpdate = true\n for (let i = 0; i < ring; i++) {\n const a = (i / ring) * Math.PI * 2\n const c = Math.cos(a) * this.radii[i] * scale\n const s = Math.sin(a) * this.radii[i] * scale\n const o = (1 + i) * 3\n fan.position[o] = px + t1[0] * c + t2[0] * s\n fan.position[o + 1] = py + t1[1] * c + t2[1] * s\n fan.position[o + 2] = pz + t1[2] * c + t2[2] * s\n const k = (1 + i) * 4\n fan.color[k] = r\n fan.color[k + 1] = g\n fan.color[k + 2] = b\n fan.color[k + 3] = aRim\n }\n const geometry = fan.mesh.geometry\n ;(geometry.getAttribute('position') as THREE_NS.BufferAttribute).needsUpdate = true\n ;(geometry.getAttribute('color') as THREE_NS.BufferAttribute).needsUpdate = true\n }\n\n dispose(): void {\n for (const fan of [this.outer, this.inner]) fan.mesh.geometry.dispose()\n }\n}\n","import type * as THREE_NS from 'three'\nimport { sampleStream } from '@adatechnology/game-fluids'\nimport type { StreamState } from '@adatechnology/game-fluids'\nimport type { QualityProfile } from './quality'\nimport { clamp } from './util'\nimport type { ThreeLib } from './types'\n\n/** Seconds a stream takes to break up into drops after the world ended it. */\nconst GHOST_TIME = 0.4\n\n/**\n * One stream: a thread along the ballistic path (a tube updated in place) with a growing drop at its leading tip, a trailing drop while it ends,\n * and for thin streams only a sequence of falling drops. The jet narrows as it accelerates.\n */\nexport class StreamVisual {\n readonly tube: THREE_NS.Mesh\n readonly drops: THREE_NS.InstancedMesh\n private readonly positions: Float32Array\n private readonly normals: Float32Array\n private readonly samples: number\n private readonly sides: number\n private readonly matrix: THREE_NS.Matrix4\n private readonly maxDrops: number\n id = ''\n age = 0\n ghostAge = -1\n /** Last known state, kept while the stream breaks into drops. */\n state: StreamState | null = null\n triangles = 0\n\n constructor(\n private readonly THREE: ThreeLib,\n profile: QualityProfile,\n dropGeometry: THREE_NS.SphereGeometry,\n ) {\n const T = THREE\n this.samples = profile.streamSamples\n this.sides = profile.streamSides\n this.maxDrops = Math.max(profile.thinDrops, 2) + 2\n this.positions = new Float32Array(this.samples * this.sides * 3)\n this.normals = new Float32Array(this.samples * this.sides * 3)\n const geometry = new T.BufferGeometry()\n const p = new T.BufferAttribute(this.positions, 3)\n const n = new T.BufferAttribute(this.normals, 3)\n p.setUsage(T.DynamicDrawUsage)\n n.setUsage(T.DynamicDrawUsage)\n geometry.setAttribute('position', p)\n geometry.setAttribute('normal', n)\n const index: number[] = []\n for (let i = 0; i < this.samples - 1; i++) {\n for (let j = 0; j < this.sides; j++) {\n const a = i * this.sides + j\n const b = i * this.sides + ((j + 1) % this.sides)\n const c = (i + 1) * this.sides + j\n const d = (i + 1) * this.sides + ((j + 1) % this.sides)\n index.push(a, c, b, b, c, d)\n }\n }\n geometry.setIndex(index)\n this.tube = new T.Mesh(geometry)\n this.tube.name = 'fluid_stream'\n this.tube.frustumCulled = false\n this.tube.renderOrder = 2\n this.drops = new T.InstancedMesh(dropGeometry, undefined as unknown as THREE_NS.Material, this.maxDrops)\n this.drops.name = 'fluid_stream_drops'\n this.drops.frustumCulled = false\n this.drops.renderOrder = 2\n this.drops.instanceMatrix.setUsage(T.DynamicDrawUsage)\n this.drops.count = 0\n this.matrix = new T.Matrix4()\n }\n\n setMaterial(material: THREE_NS.Material): void {\n this.tube.material = material\n this.drops.material = material\n }\n\n hide(): void {\n this.tube.visible = false\n this.drops.visible = false\n this.drops.count = 0\n this.triangles = 0\n }\n\n private dropAt(slot: number, x: number, y: number, z: number, radius: number): void {\n this.matrix.makeScale(radius, radius, radius).setPosition(x, y, z)\n this.drops.setMatrixAt(slot, this.matrix)\n }\n\n /**\n * Draw the stream. `state` is the world's stream while it lives, or the last known one while ghost (`ghostAge >= 0`).\n */\n update(state: StreamState, gravity: number, dt: number, profile: QualityProfile, time: number): void {\n this.state = state\n this.age += dt\n if (this.ghostAge >= 0) this.ghostAge += dt\n\n const flight = Math.max(0.02, state.flightTime ?? 1)\n const [vx, vy, vz] = state.velocity\n const speed0 = Math.max(0.3, Math.hypot(vx, vy, vz))\n const q = Math.max(state.rate, 0) / 1000\n const r0 = clamp(Math.sqrt(q / (Math.PI * speed0)), 0.0015, 0.03)\n\n const tB = Math.min(flight, this.age)\n // The tail leaves the origin at free fall once the world stopped the stream.\n const tA = this.ghostAge >= 0 ? Math.min(flight, this.ghostAge) : 0\n\n let drops = 0\n const thin = state.thin && this.ghostAge < 0\n\n if (thin) {\n this.tube.visible = false\n this.triangles = 0\n const n = profile.thinDrops\n const phase = time * 1.3\n for (let k = 0; k < n && drops < this.maxDrops; k++) {\n const tau = ((phase + k / n) % 1) * flight\n if (tau > tB) continue\n const p = sampleStream(state, gravity, tau)\n const grow = Math.min(1, tau / (0.12 * flight + 0.02))\n this.dropAt(drops++, p[0], p[1], p[2], Math.max(0.002, r0 * 1.8) * (0.4 + 0.6 * grow))\n }\n } else if (tB - tA > 0.015) {\n this.writeTube(state, gravity, tA, tB, r0, speed0)\n this.tube.visible = true\n this.triangles = (this.samples - 1) * this.sides * 2\n // Leading drop that grows and detaches while the jet is still advancing; trailing drop while it ends.\n if (tB < flight) {\n const p = sampleStream(state, gravity, tB)\n const w = this.radiusAt(state, gravity, tB, r0, speed0)\n this.dropAt(drops++, p[0], p[1], p[2], w * 1.35)\n }\n if (this.ghostAge >= 0) {\n const p = sampleStream(state, gravity, tA)\n const w = this.radiusAt(state, gravity, tA, r0, speed0)\n this.dropAt(drops++, p[0], p[1], p[2], w * 1.4 * (1 - this.ghostAge / GHOST_TIME * 0.4))\n }\n } else {\n this.tube.visible = false\n this.triangles = 0\n if (tA < flight) {\n // Only the drop is left.\n const p = sampleStream(state, gravity, this.ghostAge >= 0 ? tA : tB)\n this.dropAt(drops++, p[0], p[1], p[2], Math.max(0.003, r0 * 1.3))\n }\n }\n\n this.drops.count = drops\n this.drops.visible = drops > 0\n this.drops.instanceMatrix.needsUpdate = true\n this.triangles += drops * 24\n }\n\n /** The ghost is over once its tail reached the landing. */\n get finished(): boolean {\n return this.ghostAge >= 0 && (this.ghostAge >= GHOST_TIME || this.ghostAge >= (this.state?.flightTime ?? 0))\n }\n\n private radiusAt(state: StreamState, gravity: number, t: number, r0: number, speed0: number): number {\n const vy = state.velocity[1] - gravity * t\n const speed = Math.max(0.3, Math.hypot(state.velocity[0], vy, state.velocity[2]))\n const fade = this.ghostAge >= 0 ? clamp(1 - this.ghostAge / GHOST_TIME, 0.15, 1) : 1\n return r0 * Math.sqrt(speed0 / speed) * fade\n }\n\n private writeTube(state: StreamState, gravity: number, tA: number, tB: number, r0: number, speed0: number): void {\n const n = this.samples\n const sides = this.sides\n for (let i = 0; i < n; i++) {\n const k = i / (n - 1)\n const t = tA + (tB - tA) * k\n const p = sampleStream(state, gravity, t)\n const vx = state.velocity[0]\n const vy = state.velocity[1] - gravity * t\n const vz = state.velocity[2]\n const l = Math.hypot(vx, vy, vz) || 1\n const tx = vx / l\n const ty = vy / l\n const tz = vz / l\n // Any vector not parallel to the tangent gives the first side axis.\n const ax = Math.abs(tz) < 0.9 ? 0 : 1\n const az = Math.abs(tz) < 0.9 ? 1 : 0\n let b1x = ty * az - tz * 0\n let b1y = tz * ax - tx * az\n let b1z = tx * 0 - ty * ax\n const bl = Math.hypot(b1x, b1y, b1z) || 1\n b1x /= bl\n b1y /= bl\n b1z /= bl\n const b2x = ty * b1z - tz * b1y\n const b2y = tz * b1x - tx * b1z\n const b2z = tx * b1y - ty * b1x\n // Rounded ends: the radius tapers to a bead at both ends.\n const end = Math.min(1, 0.35 + Math.min(k, 1 - k) * 8)\n const r = this.radiusAt(state, gravity, t, r0, speed0) * end\n for (let j = 0; j < sides; j++) {\n const a = (j / sides) * Math.PI * 2\n const c = Math.cos(a)\n const s = Math.sin(a)\n const nx = b1x * c + b2x * s\n const ny = b1y * c + b2y * s\n const nz = b1z * c + b2z * s\n const o = (i * sides + j) * 3\n this.positions[o] = p[0] + nx * r\n this.positions[o + 1] = p[1] + ny * r\n this.positions[o + 2] = p[2] + nz * r\n this.normals[o] = nx\n this.normals[o + 1] = ny\n this.normals[o + 2] = nz\n }\n }\n const g = this.tube.geometry\n ;(g.getAttribute('position') as THREE_NS.BufferAttribute).needsUpdate = true\n ;(g.getAttribute('normal') as THREE_NS.BufferAttribute).needsUpdate = true\n }\n\n start(id: string): void {\n this.id = id\n this.age = 0\n this.ghostAge = -1\n this.state = null\n }\n\n dispose(): void {\n this.tube.geometry.dispose()\n this.drops.dispose()\n }\n}\n","import type * as THREE_NS from 'three'\nimport type { FluidEvent, StainState, StreamState } from '@adatechnology/game-fluids'\nimport { LiquidBody } from './liquid-body'\nimport { MaterialCache } from './materials'\nimport { ParticlePool } from './particles'\nimport { profileOf } from './quality'\nimport { StainVisual } from './stains'\nimport { StreamVisual } from './streams'\nimport type { BindOptions, FluidQuality, FluidRenderer, FluidRendererOptions, FluidRendererStats, ThreeLib } from './types'\n\n/**\n * Draws a `FluidWorld` with three.js. It only reads the world state (plus `splash` events for droplets), so a LAN client that receives the\n * same world state draws the same liquids.\n */\nexport function createFluidRenderer(THREE: ThreeLib, options: FluidRendererOptions): FluidRenderer {\n const { scene, fluids } = options\n const quality: FluidQuality = options.quality ?? 'medium'\n const profile = profileOf(quality)\n const materials = new MaterialCache(THREE, options.envMap ?? null)\n const root = new THREE.Group()\n root.name = 'fluid_renderer'\n scene.add(root)\n const particles = new ParticlePool(THREE, profile.maxParticles, options.pointScale ?? 600)\n root.add(particles.points)\n const dropGeometry = new THREE.SphereGeometry(1, 8, 6)\n\n const bodies = new Map<string, LiquidBody>()\n const streamsActive = new Map<string, StreamVisual>()\n const streamsFree: StreamVisual[] = []\n let streamsCreated = 0\n const stainsActive = new Map<string, StainVisual>()\n const stainsFree: StainVisual[] = []\n let stainsCreated = 0\n let time = 0\n let disposed = false\n\n const offSplash = fluids.on('splash', (e: FluidEvent) => {\n if (e.type !== 'splash' || disposed) return\n const def = fluids.liquids.get(e.liquid)\n if (!def) return\n particles.splash(e.point, e.intensity, def.color, def.opacity, profile.splashDroplets)\n })\n\n // ---- streams ----\n\n const acquireStream = (id: string): StreamVisual | null => {\n let v = streamsFree.pop() ?? null\n if (!v) {\n if (streamsCreated >= profile.maxStreams) return null\n streamsCreated++\n v = new StreamVisual(THREE, profile, dropGeometry)\n root.add(v.tube, v.drops)\n }\n v.start(id)\n return v\n }\n\n const releaseStream = (v: StreamVisual): void => {\n v.hide()\n streamsActive.delete(v.id)\n streamsFree.push(v)\n }\n\n const syncStreams = (dt: number): void => {\n const live = fluids.streams()\n const seen = new Set<string>()\n // Biggest first, so the budget keeps the visible ones.\n live.sort((a: StreamState, b: StreamState) => b.rate - a.rate)\n for (const s of live) {\n seen.add(s.id)\n let v = streamsActive.get(s.id)\n if (!v) {\n const fresh = acquireStream(s.id)\n if (!fresh) continue\n v = fresh\n streamsActive.set(s.id, v)\n }\n const def = fluids.liquids.get(s.liquid)\n if (!def) continue\n v.ghostAge = -1\n v.setMaterial(materials.liquidStream(def))\n v.update(s, fluids.gravity, dt, profile, time)\n }\n for (const v of [...streamsActive.values()]) {\n if (seen.has(v.id)) continue\n // The world ended it: let it break into drops.\n if (v.ghostAge < 0) v.ghostAge = 0\n if (!v.state || v.finished) {\n releaseStream(v)\n continue\n }\n v.update(v.state, fluids.gravity, dt, profile, time)\n if (v.finished) releaseStream(v)\n }\n }\n\n // ---- stains ----\n\n const syncStains = (): void => {\n const all = fluids.stains()\n // Keep the newest stains when the budget is smaller than the world.\n const wanted = all.length > profile.maxStains ? [...all].sort((a: StainState, b: StainState) => (a.age ?? 0) - (b.age ?? 0)).slice(0, profile.maxStains) : all\n const ids = new Set(wanted.map((s: StainState) => s.id))\n for (const [id, v] of [...stainsActive]) {\n if (ids.has(id)) continue\n v.hide()\n stainsActive.delete(id)\n stainsFree.push(v)\n }\n for (const s of wanted) {\n const def = fluids.liquids.get(s.liquid)\n if (!def) continue\n let v = stainsActive.get(s.id)\n if (!v) {\n v = stainsFree.pop()\n if (!v) {\n if (stainsCreated >= profile.maxStains) continue\n v = new StainVisual(THREE, profile, stainsCreated++)\n root.add(v.group)\n }\n v.stainId = s.id\n v.signature = Number.NaN\n v.assign(def, materials)\n stainsActive.set(s.id, v)\n }\n v.update(s, def, profile)\n }\n }\n\n // ---- public ----\n\n const renderer: FluidRenderer = {\n bindContainer(containerId: string, object: THREE_NS.Object3D, opts: BindOptions = {}) {\n renderer.unbindContainer(containerId)\n bodies.set(containerId, new LiquidBody(THREE, containerId, object, opts.innerOffset ?? [0, 0, 0]))\n },\n\n unbindContainer(containerId: string) {\n const body = bodies.get(containerId)\n if (!body) return\n body.dispose()\n bodies.delete(containerId)\n },\n\n update(dt: number) {\n if (disposed) return\n time += dt\n for (const body of bodies.values()) body.update(dt, fluids, materials, profile, particles)\n syncStreams(dt)\n syncStains()\n particles.update(dt, fluids.gravity)\n },\n\n clear() {\n for (const v of [...streamsActive.values()]) releaseStream(v)\n for (const [id, v] of [...stainsActive]) {\n v.hide()\n stainsActive.delete(id)\n stainsFree.push(v)\n }\n particles.clear()\n },\n\n dispose() {\n if (disposed) return\n disposed = true\n offSplash()\n for (const body of bodies.values()) body.dispose()\n bodies.clear()\n for (const v of [...streamsActive.values(), ...streamsFree]) v.dispose()\n for (const v of [...stainsActive.values(), ...stainsFree]) v.dispose()\n streamsActive.clear()\n stainsActive.clear()\n streamsFree.length = 0\n stainsFree.length = 0\n particles.dispose()\n dropGeometry.dispose()\n materials.dispose()\n scene.remove(root)\n root.clear()\n },\n\n stats(): FluidRendererStats {\n let triangles = 0\n let containers = 0\n for (const body of bodies.values()) {\n triangles += body.triangles\n if (body.visible) containers++\n }\n for (const v of streamsActive.values()) triangles += v.triangles\n for (const v of stainsActive.values()) triangles += v.triangles\n return {\n quality,\n containers,\n streams: streamsActive.size,\n stains: stainsActive.size,\n particles: particles.count,\n triangles,\n budget: { stains: profile.maxStains, streams: profile.maxStreams, particles: profile.maxParticles },\n }\n },\n }\n\n return renderer\n}\n\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../../game-vfx/src/area-cap.ts","../../game-vfx/src/budget.ts","../../game-vfx/src/data/explosion-aliases.json","../../game-vfx/src/data/explosions.json","../../game-vfx/src/catalog.ts","../../game-vfx/src/color.ts","../../game-vfx/src/constants.ts","../../game-vfx/src/pool.ts","../../game-vfx/src/pools/chunks.ts","../../game-vfx/src/vector.ts","../../game-vfx/src/pools/glows.ts","../../game-vfx/src/pools/puffs.ts","../../game-vfx/src/pools/sparks.ts","../../game-vfx/src/spawn.ts","../../game-vfx/src/emitters/fire.ts","../../game-vfx/src/effects/dsl.ts","../../game-vfx/src/effects/steps.ts","../../game-vfx/src/effects/creatures/bloater-cloud.ts","../../game-vfx/src/effects/creatures/blood-hit.ts","../../game-vfx/src/effects/creatures/corpse-decay.ts","../../game-vfx/src/effects/creatures/eyes-in-fog.ts","../../game-vfx/src/effects/creatures/dismember.ts","../../game-vfx/src/effects/creatures/headshot.ts","../../game-vfx/src/effects/creatures/reanimation.ts","../../game-vfx/src/effects/creatures/screamer-shriek.ts","../../game-vfx/src/effects/props.ts","../../game-vfx/src/effects/weapons/impact-by-material.ts","../../game-vfx/src/effects/weapons/penetration.ts","../../game-vfx/src/effects/weapons/casings-and-magazine.ts","../../game-vfx/src/effects/weapons/molotov.ts","../../game-vfx/src/effects/weapons/grenade.ts","../../game-vfx/src/effects/weapons/explosive-barrel.ts","../../game-vfx/src/effects/weapons/muzzle-flash.ts","../../game-vfx/src/effects/destruction/shopfront-glass.ts","../../game-vfx/src/effects/vector-math.ts","../../game-vfx/src/effects/destruction/door-stages.ts","../../game-vfx/src/effects/destruction/building-collapse.ts","../../game-vfx/src/effects/destruction/block-wall.ts","../../game-vfx/src/effects/destruction/spot.ts","../../game-vfx/src/effects/destruction/metal-door.ts","../../game-vfx/src/effects/destruction/iron-railing.ts","../../game-vfx/src/effects/destruction/wooden-fence.ts","../../game-vfx/src/effects/destruction/hit-point-breakage.ts","../../game-vfx/src/effects/destruction/pressure-cylinders.ts","../../game-vfx/src/effects/world/building-fire.ts","../../game-vfx/src/effects/vehicles/scraping-sparks.ts","../../game-vfx/src/effects/vehicles/car.ts","../../game-vfx/src/effects/vehicles/engine-smoke.ts","../../game-vfx/src/effects/vehicles/boom.ts","../../game-vfx/src/effects/vehicles/fuel-leak.ts","../../game-vfx/src/effects/vehicles/car-explosion.ts","../../game-vfx/src/effects/vehicles/windshield.ts","../../game-vfx/src/effects/screen/damage-and-bite.ts","../../game-vfx/src/effects/screen/infection-phases.ts","../../game-vfx/src/effects/screen/panic.ts","../../game-vfx/src/effects/screen/bloater-blindness.ts","../../game-vfx/src/effects/screen/campfire.ts","../../game-vfx/src/effects/screen/cough-and-breath.ts","../../game-vfx/src/effects/events.ts","../../game-vfx/src/effects/catalog.ts","../../game-vfx/src/random.ts","../../game-vfx/src/effects/builders/impact-table.ts","../../game-vfx/src/effects/builders/blood.ts","../../game-vfx/src/effects/behaviors/blocks.ts","../../game-vfx/src/effects/behaviors/car.ts","../../game-vfx/src/effects/behaviors/cracks.ts","../../game-vfx/src/effects/builders/shared.ts","../../game-vfx/src/effects/behaviors/cylinder.ts","../../game-vfx/src/effects/behaviors/plate.ts","../../game-vfx/src/effects/behaviors/walkers.ts","../../game-vfx/src/effects/behaviors.ts","../../game-vfx/src/schedule.ts","../../game-vfx/src/effects/expression.ts","../../game-vfx/src/effects/builders/fire.ts","../../game-vfx/src/effects/builders/weapon.ts","../../game-vfx/src/effects/macros.ts","../../game-vfx/src/effects/instance.ts","../../game-vfx/src/effects/player.ts","../../game-vfx/src/lights.ts","../../game-vfx/src/lod.ts","../../game-vfx/src/marks.ts","../../game-vfx/src/pools/rings.ts","../../game-vfx/src/pools/shocks.ts","../../game-vfx/src/explosion.ts","../../game-vfx/src/pools/chunk-motion.ts","../../game-vfx/src/screen.ts","../../game-vfx/src/sprites/kind-pool.ts","../../game-vfx/src/data/sprite-kinds.json","../../game-vfx/src/sprites/kinds.ts","../../game-vfx/src/sprites/system.ts","../../game-vfx/src/workshop/direction.ts","../../game-vfx/src/workshop/debris.ts","../../game-vfx/src/workshop/decals.ts","../../game-vfx/src/workshop/sprites.ts","../../game-vfx/src/workshop/streaks.ts","../../game-vfx/src/workshop/waves.ts","../../game-vfx/src/workshop/engine.ts","../../game-vfx/src/types.ts","../../game-vfx/src/world.ts","../../game-vfx/src/data/event-effects.json","../../game-vfx/src/effects/event-map.ts","../../game-vfx/src/locales/en.ts","../../game-vfx/src/locales/pt-BR.ts","../../game-vfx/src/locale.ts","../../game-vfx/src/effects/camera.ts","../../game-vfx/src/effects/runner.ts","../../game-vfx/src/ground.ts","../../game-vfx-three/src/renderer.ts","../../game-vfx-three/src/shaders/glow.ts","../../game-vfx-three/src/shaders/noise.ts","../../game-vfx-three/src/shaders/puff.ts","../../game-vfx-three/src/shaders/spark.ts","../../game-vfx-three/src/instanced.ts","../../game-vfx-three/src/lights.ts","../../game-vfx-three/src/canvas.ts","../../game-vfx-three/src/marks.ts","../../game-vfx-three/src/screen-overlay.ts","../../game-vfx-three/src/shaders/shock.ts","../../game-vfx-three/src/shocks.ts","../../game-vfx-three/src/shaders/sprite.ts","../../game-vfx-three/src/sprites.ts","../../game-vfx-three/src/shaders/workshop-sprite.ts","../../game-vfx-three/src/workshop-layers.ts","../../game-vfx-three/src/bench/module.ts","../../game-vfx-three/src/bench/text.ts","../../game-vfx-three/src/ground-patch.ts","../src/clip.ts","../src/liquid-body.ts","../src/util.ts","../src/materials.ts","../src/quality.ts","../src/sprites.ts","../../game-fluids/src/data/liquids.json","../../game-fluids/src/liquids.ts","../../game-fluids/src/math.ts","../../game-fluids/src/landing.ts","../../game-fluids/src/locale.ts","../../game-fluids/src/shapes.ts","../../game-fluids/src/stains.ts","../../game-fluids/src/world.ts","../../game-fluids/src/data/item-uses.json","../../game-fluids/src/item-use.ts","../../game-fluids/src/film/deposit.ts","../../game-fluids/src/film/drops.ts","../../game-fluids/src/film/simulate.ts","../../game-fluids/src/film/sources.ts","../../game-fluids/src/film/film.ts","../../game-fluids/src/film/random.ts","../../game-fluids/src/film/noise.ts","../../game-fluids/src/film/layout.ts","../../game-fluids/src/film/surface-layout.ts","../../game-fluids/src/data/film-profiles.json","../../game-fluids/src/film/profiles.ts","../src/stains.ts","../src/streams.ts","../src/renderer.ts","../src/film/drops.ts","../src/film/shader.ts","../src/film/material.ts","../src/film/strips.ts","../src/film/grid.ts","../src/film/manager.ts","../src/film/bridge.ts"],"sourcesContent":["export { createFluidRenderer } from './renderer'\nexport { smoothOutline } from './stains'\nexport type { BindOptions, FluidQuality, FluidRenderer, FluidRendererOptions, FluidRendererStats, ThreeLib } from './types'\nexport * from './film'\n","import type { AreaCap } from './types'\n\n/** The game's cap (G68194-68202): from the 7th explosion within 40 m in 4 s, the extra ones are made at 0.3. */\nexport const AREA_CAP_GAME: AreaCap = { radius: 40, window: 4, count: 6, excessScale: 0.3 }\n\nexport interface AreaCapTracker {\n /** Registers an explosion at (`x`, `z`) at `time` seconds and answers the scale it gets: 1, or the cap's `excessScale` when `count` or more are already recent in the area. */\n register(time: number, x: number, z: number): number\n clear(): void\n}\n\ninterface Recent {\n time: number\n x: number\n z: number\n}\n\nexport function createAreaCap(cap: AreaCap): AreaCapTracker {\n const recent: Recent[] = []\n const spare: Recent[] = []\n const radiusSquared = cap.radius * cap.radius\n return {\n register(time, x, z) {\n let kept = 0\n for (const entry of recent) {\n if (time - entry.time < cap.window) recent[kept++] = entry\n else spare.push(entry)\n }\n recent.length = kept\n let near = 0\n for (const entry of recent) if ((entry.x - x) ** 2 + (entry.z - z) ** 2 < radiusSquared) near++\n const entry = spare.pop() ?? { time: 0, x: 0, z: 0 }\n entry.time = time\n entry.x = x\n entry.z = z\n recent.push(entry)\n return near >= cap.count ? cap.excessScale : 1\n },\n clear() {\n for (const entry of recent) spare.push(entry)\n recent.length = 0\n },\n }\n}\n","import type { ExplosionDef, VfxProfile, VfxProfileName } from './types'\n\n/**\n * The pool sizes of the three profiles. `high` is the bench and the goldens (the Lab's caps L131/240/295/367 and the Workshop's W221), `medium` the\n * game's default (G67747/67762), `low` close to the game's own budgets (G68074, G68108).\n */\nexport const PROFILES: Readonly<Record<VfxProfileName, VfxProfile>> = {\n high: { puffs: 2600, sparks: 3200, glows: 320, chunks: 300, sprites: { add: 9000, normal: 9000 }, streaks: 3000, debris: 500 },\n medium: { puffs: 1300, sparks: 1600, glows: 160, chunks: 150, sprites: { add: 1500, normal: 600 }, streaks: 1000, debris: 120 },\n low: { puffs: 260, sparks: 800, glows: 80, chunks: 120, sprites: { add: 600, normal: 300 }, streaks: 400, debris: 60 },\n}\n\n/** The Lab's per-event meters (L1043-1044): 400 GPU particles, 40 physics debris, and 4 + 2 lights. */\nexport const EVENT_BUDGET = { particles: 400, debris: 40, blastLights: 4, fireLights: 2 } as const\n\n/** Particles an explosion makes on the GPU, the number of the Lab's effect sheet (L1033): fireballs, smoke, dust, shrapnel, the mushroom and the embers. */\nexport function estimateCost(def: ExplosionDef): number {\n const mushroom = def.mushroom ? def.mushroom.count + def.mushroom.stem : 0\n return def.fireballs + def.smoke.count + def.dust.count + def.shrapnel.count + mushroom + (def.shrapnel.embers ?? 0)\n}\n","{\n \"carl_gustaf_84mm\": \"at4_84mm\",\n \"vog_25\": \"m79_40mm\"\n}\n","{\n \"m67\": {\n \"id\": \"m67\",\n \"lethal\": 5,\n \"injury\": 15,\n \"visualRadius\": 1.3,\n \"fireballs\": 16,\n \"heat\": 1,\n \"smoke\": {\n \"color\": 6972767,\n \"count\": 18,\n \"life\": 5.5\n },\n \"dust\": {\n \"color\": 8221544,\n \"count\": 30\n },\n \"shrapnel\": {\n \"count\": 150,\n \"kind\": 0,\n \"speed\": 34\n },\n \"chunks\": {\n \"count\": 14,\n \"colors\": [\n 3881528,\n 2894634,\n 4867650\n ],\n \"hot\": 0.25\n },\n \"mark\": {\n \"scorch\": 1.7,\n \"crater\": 0.9,\n \"dent\": 0.25\n },\n \"flash\": {\n \"light\": 45,\n \"life\": 0.45,\n \"color\": 16752720,\n \"intensity\": 6\n },\n \"shock\": {\n \"duration\": 0.32\n },\n \"shake\": 1.1,\n \"push\": 9,\n \"sources\": [\n \"lab:L796\",\n \"lab:L793\"\n ]\n },\n \"molotov\": {\n \"id\": \"molotov\",\n \"lethal\": 0,\n \"injury\": 4,\n \"visualRadius\": 1.1,\n \"fireballs\": 24,\n \"fuel\": true,\n \"heat\": 0.95,\n \"heatLifeScale\": 1.5,\n \"smoke\": {\n \"color\": 3814449,\n \"count\": 3,\n \"life\": 3.5\n },\n \"dust\": {\n \"color\": 8221544,\n \"count\": 0\n },\n \"shrapnel\": {\n \"count\": 0,\n \"kind\": 0,\n \"speed\": 28\n },\n \"chunks\": {\n \"count\": 0,\n \"colors\": [\n 3881528,\n 2894634,\n 4867650\n ],\n \"hot\": 0.3\n },\n \"mark\": {\n \"scorch\": 2.4\n },\n \"flash\": {\n \"light\": 22,\n \"life\": 1.2,\n \"color\": 16747066,\n \"intensity\": 6\n },\n \"shock\": false,\n \"shake\": 0.25,\n \"push\": 0,\n \"ignite\": 4.2,\n \"sources\": [\n \"lab:L797\",\n \"lab:L793\"\n ]\n },\n \"pipe_bomb\": {\n \"id\": \"pipe_bomb\",\n \"lethal\": 3,\n \"injury\": 10,\n \"visualRadius\": 1,\n \"fireballs\": 8,\n \"heat\": 1,\n \"smoke\": {\n \"color\": 11841704,\n \"count\": 26,\n \"life\": 6,\n \"kind\": 3\n },\n \"dust\": {\n \"color\": 8221544,\n \"count\": 22\n },\n \"shrapnel\": {\n \"count\": 220,\n \"kind\": 1,\n \"speed\": 30,\n \"width\": 0.03\n },\n \"chunks\": {\n \"count\": 8,\n \"colors\": [\n 5132886,\n 3816768\n ],\n \"plate\": true,\n \"hot\": 0.2\n },\n \"mark\": {\n \"scorch\": 1.3,\n \"crater\": 0.7,\n \"dent\": 0.18\n },\n \"flash\": {\n \"light\": 30,\n \"life\": 0.45,\n \"color\": 16752720,\n \"intensity\": 6\n },\n \"shock\": {\n \"duration\": 0.32\n },\n \"shake\": 1,\n \"push\": 7,\n \"sources\": [\n \"lab:L798\",\n \"lab:L793\"\n ]\n },\n \"propane_p13\": {\n \"id\": \"propane_p13\",\n \"lethal\": 5,\n \"injury\": 14,\n \"visualRadius\": 2.2,\n \"fireballs\": 34,\n \"fuel\": true,\n \"heat\": 1,\n \"smoke\": {\n \"color\": 3814449,\n \"count\": 10,\n \"life\": 8\n },\n \"dust\": {\n \"color\": 8221544,\n \"count\": 26\n },\n \"shrapnel\": {\n \"count\": 70,\n \"kind\": 0,\n \"speed\": 28,\n \"embers\": 30\n },\n \"chunks\": {\n \"count\": 8,\n \"colors\": [\n 3891098,\n 2835824\n ],\n \"plate\": true,\n \"hot\": 0.5\n },\n \"mark\": {\n \"scorch\": 2.6\n },\n \"flash\": {\n \"light\": 70,\n \"life\": 1.1,\n \"color\": 16751168,\n \"intensity\": 6\n },\n \"shock\": {\n \"duration\": 0.32\n },\n \"shake\": 1.4,\n \"push\": 8,\n \"mushroom\": {\n \"height\": 7,\n \"radius\": 2.6,\n \"count\": 36,\n \"stem\": 16,\n \"life\": 8\n },\n \"residualFires\": [\n {\n \"radius\": 1.4,\n \"life\": 7,\n \"rate\": 20\n }\n ],\n \"ignite\": 5,\n \"hotParts\": true,\n \"sources\": [\n \"lab:L799\",\n \"lab:L793\"\n ]\n },\n \"dynamite\": {\n \"id\": \"dynamite\",\n \"lethal\": 4,\n \"injury\": 12,\n \"visualRadius\": 1.4,\n \"fireballs\": 12,\n \"heat\": 1,\n \"smoke\": {\n \"color\": 8019006,\n \"count\": 24,\n \"life\": 7\n },\n \"dust\": {\n \"color\": 8019006,\n \"count\": 46\n },\n \"shrapnel\": {\n \"count\": 90,\n \"kind\": 0,\n \"speed\": 28\n },\n \"chunks\": {\n \"count\": 26,\n \"colors\": [\n 6965806,\n 8018490,\n 4863526\n ],\n \"hot\": 0.15,\n \"speed\": 1.2\n },\n \"mark\": {\n \"scorch\": 2.4,\n \"crater\": 1.8,\n \"dent\": 0.7\n },\n \"flash\": {\n \"light\": 55,\n \"life\": 0.45,\n \"color\": 16752720,\n \"intensity\": 6\n },\n \"shock\": {\n \"duration\": 0.32\n },\n \"shake\": 1.5,\n \"push\": 10,\n \"sources\": [\n \"lab:L800\",\n \"lab:L793\"\n ]\n },\n \"c4\": {\n \"id\": \"c4\",\n \"lethal\": 6,\n \"injury\": 18,\n \"visualRadius\": 1.6,\n \"fireballs\": 20,\n \"heat\": 1.25,\n \"smoke\": {\n \"color\": 6972767,\n \"count\": 22,\n \"life\": 7\n },\n \"dust\": {\n \"color\": 9407106,\n \"count\": 50\n },\n \"shrapnel\": {\n \"count\": 200,\n \"kind\": 0,\n \"speed\": 42\n },\n \"chunks\": {\n \"count\": 32,\n \"colors\": [\n 9078140,\n 7301730,\n 3881528\n ],\n \"hot\": 0.3,\n \"speed\": 1.4\n },\n \"mark\": {\n \"scorch\": 3,\n \"crater\": 2.3,\n \"dent\": 0.9\n },\n \"flash\": {\n \"light\": 90,\n \"life\": 0.35,\n \"color\": 16773328,\n \"intensity\": 9\n },\n \"shock\": {\n \"duration\": 0.28\n },\n \"shake\": 2.2,\n \"push\": 13,\n \"sources\": [\n \"lab:L801\",\n \"lab:L793\"\n ]\n },\n \"claymore\": {\n \"id\": \"claymore\",\n \"lethal\": 15,\n \"injury\": 50,\n \"visualRadius\": 0.7,\n \"fireballs\": 6,\n \"heat\": 1,\n \"smoke\": {\n \"color\": 6972767,\n \"count\": 8,\n \"life\": 4\n },\n \"dust\": {\n \"color\": 8221544,\n \"count\": 34\n },\n \"shrapnel\": {\n \"count\": 700,\n \"kind\": 1,\n \"speed\": 55,\n \"width\": 0.025\n },\n \"chunks\": {\n \"count\": 6,\n \"colors\": [\n 3881528,\n 2894634,\n 4867650\n ],\n \"hot\": 0.3\n },\n \"mark\": {\n \"scorch\": 0.9\n },\n \"flash\": {\n \"light\": 28,\n \"life\": 0.45,\n \"color\": 16752720,\n \"intensity\": 6\n },\n \"shock\": {\n \"duration\": 0.32\n },\n \"shake\": 1,\n \"push\": 8,\n \"cone\": {\n \"angle\": 0,\n \"half\": 0.5235987755982988,\n \"length\": 50\n },\n \"directionBias\": 2,\n \"sources\": [\n \"lab:L802\",\n \"lab:L793\"\n ]\n },\n \"flashbang\": {\n \"id\": \"flashbang\",\n \"lethal\": 0,\n \"injury\": 10,\n \"visualRadius\": 0.5,\n \"fireballs\": 0,\n \"heat\": 1,\n \"smoke\": {\n \"color\": 11841704,\n \"count\": 10,\n \"life\": 5,\n \"kind\": 3\n },\n \"dust\": {\n \"color\": 8221544,\n \"count\": 0\n },\n \"shrapnel\": {\n \"count\": 60,\n \"kind\": 2,\n \"speed\": 12\n },\n \"chunks\": {\n \"count\": 0,\n \"colors\": [\n 3881528,\n 2894634,\n 4867650\n ],\n \"hot\": 0.3\n },\n \"mark\": {\n \"scorch\": 0.4\n },\n \"flash\": {\n \"light\": 160,\n \"life\": 0.2,\n \"color\": 16054015,\n \"intensity\": 14,\n \"blinding\": true\n },\n \"shock\": false,\n \"shake\": 0.4,\n \"push\": 0,\n \"sources\": [\n \"lab:L803\",\n \"lab:L793\"\n ]\n },\n \"m79_40mm\": {\n \"id\": \"m79_40mm\",\n \"lethal\": 4,\n \"injury\": 12,\n \"visualRadius\": 1.1,\n \"fireballs\": 14,\n \"heat\": 1,\n \"smoke\": {\n \"color\": 6972767,\n \"count\": 14,\n \"life\": 5\n },\n \"dust\": {\n \"color\": 8221544,\n \"count\": 24\n },\n \"shrapnel\": {\n \"count\": 130,\n \"kind\": 0,\n \"speed\": 28\n },\n \"chunks\": {\n \"count\": 12,\n \"colors\": [\n 3881528,\n 2894634,\n 4867650\n ],\n \"hot\": 0.3\n },\n \"mark\": {\n \"scorch\": 1.5,\n \"crater\": 0.8,\n \"dent\": 0.22\n },\n \"flash\": {\n \"light\": 40,\n \"life\": 0.45,\n \"color\": 16752720,\n \"intensity\": 6\n },\n \"shock\": {\n \"duration\": 0.32\n },\n \"shake\": 1,\n \"push\": 8,\n \"sources\": [\n \"lab:L804\",\n \"lab:L793\"\n ]\n },\n \"m72_66mm\": {\n \"id\": \"m72_66mm\",\n \"lethal\": 5,\n \"injury\": 15,\n \"visualRadius\": 1.5,\n \"fireballs\": 22,\n \"heat\": 1,\n \"smoke\": {\n \"color\": 6972767,\n \"count\": 16,\n \"life\": 6\n },\n \"dust\": {\n \"color\": 9407106,\n \"count\": 30\n },\n \"shrapnel\": {\n \"count\": 180,\n \"kind\": 0,\n \"speed\": 40\n },\n \"chunks\": {\n \"count\": 24,\n \"colors\": [\n 9078140,\n 7301730\n ],\n \"hot\": 0.3\n },\n \"mark\": {\n \"scorch\": 2\n },\n \"flash\": {\n \"light\": 60,\n \"life\": 0.45,\n \"color\": 16752720,\n \"intensity\": 6\n },\n \"shock\": {\n \"duration\": 0.32\n },\n \"shake\": 1.5,\n \"push\": 11,\n \"directionBias\": 1.4,\n \"sources\": [\n \"lab:L805\",\n \"lab:L793\"\n ]\n },\n \"at4_84mm\": {\n \"id\": \"at4_84mm\",\n \"lethal\": 5,\n \"injury\": 16,\n \"visualRadius\": 1.7,\n \"fireballs\": 26,\n \"heat\": 1.15,\n \"smoke\": {\n \"color\": 6972767,\n \"count\": 18,\n \"life\": 6.5\n },\n \"dust\": {\n \"color\": 9407106,\n \"count\": 34\n },\n \"shrapnel\": {\n \"count\": 220,\n \"kind\": 0,\n \"speed\": 46\n },\n \"chunks\": {\n \"count\": 30,\n \"colors\": [\n 9078140,\n 7301730\n ],\n \"hot\": 0.35\n },\n \"mark\": {\n \"scorch\": 2.2,\n \"crater\": 1.4,\n \"dent\": 0.5\n },\n \"flash\": {\n \"light\": 70,\n \"life\": 0.45,\n \"color\": 16752720,\n \"intensity\": 6\n },\n \"shock\": {\n \"duration\": 0.32\n },\n \"shake\": 1.7,\n \"push\": 12,\n \"directionBias\": 1.6,\n \"sources\": [\n \"lab:L806\",\n \"lab:L793\"\n ]\n },\n \"pg7v\": {\n \"id\": \"pg7v\",\n \"lethal\": 5,\n \"injury\": 16,\n \"visualRadius\": 1.8,\n \"fireballs\": 28,\n \"heat\": 1,\n \"smoke\": {\n \"color\": 6972767,\n \"count\": 18,\n \"life\": 7\n },\n \"dust\": {\n \"color\": 9407106,\n \"count\": 34\n },\n \"shrapnel\": {\n \"count\": 200,\n \"kind\": 0,\n \"speed\": 42,\n \"embers\": 20\n },\n \"chunks\": {\n \"count\": 28,\n \"colors\": [\n 9078140,\n 7301730\n ],\n \"hot\": 0.4\n },\n \"mark\": {\n \"scorch\": 2.2\n },\n \"flash\": {\n \"light\": 70,\n \"life\": 0.45,\n \"color\": 16752720,\n \"intensity\": 6\n },\n \"shock\": {\n \"duration\": 0.32\n },\n \"shake\": 1.7,\n \"push\": 12,\n \"directionBias\": 1.2,\n \"sources\": [\n \"lab:L807\",\n \"lab:L793\"\n ]\n },\n \"fuel_barrel\": {\n \"id\": \"fuel_barrel\",\n \"lethal\": 4,\n \"injury\": 12,\n \"visualRadius\": 1.9,\n \"fireballs\": 28,\n \"fuel\": true,\n \"heat\": 1,\n \"smoke\": {\n \"color\": 3814449,\n \"count\": 8,\n \"life\": 8\n },\n \"dust\": {\n \"color\": 8221544,\n \"count\": 22\n },\n \"shrapnel\": {\n \"count\": 60,\n \"kind\": 0,\n \"speed\": 28,\n \"embers\": 24\n },\n \"chunks\": {\n \"count\": 10,\n \"colors\": [\n 10165272,\n 5902864\n ],\n \"plate\": true,\n \"hot\": 0.6\n },\n \"mark\": {\n \"scorch\": 2.4\n },\n \"flash\": {\n \"light\": 60,\n \"life\": 1,\n \"color\": 16748608,\n \"intensity\": 6\n },\n \"shock\": {\n \"duration\": 0.32\n },\n \"shake\": 1.2,\n \"push\": 8,\n \"mushroom\": {\n \"height\": 6,\n \"radius\": 2.1,\n \"count\": 30,\n \"stem\": 14,\n \"life\": 8\n },\n \"residualFires\": [\n {\n \"dx\": 1.6,\n \"dz\": 0.4,\n \"radius\": 0.9,\n \"life\": 6\n },\n {\n \"dx\": -1.1,\n \"dz\": 1.2,\n \"radius\": 0.7,\n \"life\": 5.5\n },\n {\n \"dx\": -0.4,\n \"dz\": -1.5,\n \"radius\": 0.8,\n \"life\": 6.5\n }\n ],\n \"ignite\": 4,\n \"hotParts\": true,\n \"sources\": [\n \"lab:L808\",\n \"lab:L793\"\n ]\n },\n \"car_fuel_tank\": {\n \"id\": \"car_fuel_tank\",\n \"lethal\": 5,\n \"injury\": 14,\n \"visualRadius\": 2.3,\n \"fireballs\": 36,\n \"fuel\": true,\n \"heat\": 1,\n \"smoke\": {\n \"color\": 3814449,\n \"count\": 12,\n \"life\": 9\n },\n \"dust\": {\n \"color\": 8221544,\n \"count\": 30\n },\n \"shrapnel\": {\n \"count\": 90,\n \"kind\": 0,\n \"speed\": 28,\n \"embers\": 36\n },\n \"chunks\": {\n \"count\": 14,\n \"colors\": [\n 3881528,\n 2894634,\n 4867650\n ],\n \"plate\": true,\n \"hot\": 0.5\n },\n \"mark\": {\n \"scorch\": 3.2\n },\n \"flash\": {\n \"light\": 80,\n \"life\": 1.2,\n \"color\": 16748608,\n \"intensity\": 6\n },\n \"shock\": {\n \"duration\": 0.32\n },\n \"shake\": 1.8,\n \"push\": 9,\n \"mushroom\": {\n \"height\": 7.5,\n \"radius\": 2.8,\n \"count\": 40,\n \"stem\": 16,\n \"life\": 9\n },\n \"ignite\": 5,\n \"hotParts\": true,\n \"sources\": [\n \"lab:L809\",\n \"lab:L793\"\n ]\n },\n \"fuel_pump\": {\n \"id\": \"fuel_pump\",\n \"lethal\": 4,\n \"injury\": 12,\n \"visualRadius\": 1.7,\n \"fireballs\": 24,\n \"fuel\": true,\n \"heat\": 1,\n \"smoke\": {\n \"color\": 3814449,\n \"count\": 6,\n \"life\": 7\n },\n \"dust\": {\n \"color\": 8221544,\n \"count\": 16\n },\n \"shrapnel\": {\n \"count\": 60,\n \"kind\": 0,\n \"speed\": 28,\n \"embers\": 16\n },\n \"chunks\": {\n \"count\": 12,\n \"colors\": [\n 14209732,\n 2058810,\n 9407106\n ],\n \"plate\": true,\n \"hot\": 0.5\n },\n \"mark\": {\n \"scorch\": 2.4\n },\n \"flash\": {\n \"light\": 55,\n \"life\": 0.9,\n \"color\": 16748608,\n \"intensity\": 6\n },\n \"shock\": {\n \"duration\": 0.32\n },\n \"shake\": 1.2,\n \"push\": 8,\n \"mushroom\": {\n \"height\": 5,\n \"radius\": 1.8,\n \"count\": 22,\n \"stem\": 10,\n \"life\": 7\n },\n \"residualFires\": [\n {\n \"radius\": 1.1,\n \"life\": 14,\n \"rate\": 22\n }\n ],\n \"ignite\": 4,\n \"hotParts\": true,\n \"sources\": [\n \"lab:L810\",\n \"lab:L793\"\n ]\n },\n \"fuel_station_tank\": {\n \"id\": \"fuel_station_tank\",\n \"lethal\": 12,\n \"injury\": 30,\n \"visualRadius\": 4.5,\n \"fireballs\": 44,\n \"fuel\": true,\n \"heat\": 1,\n \"smoke\": {\n \"color\": 3814449,\n \"count\": 20,\n \"life\": 11\n },\n \"dust\": {\n \"color\": 8221544,\n \"count\": 46\n },\n \"shrapnel\": {\n \"count\": 160,\n \"kind\": 0,\n \"speed\": 40,\n \"embers\": 60\n },\n \"chunks\": {\n \"count\": 30,\n \"colors\": [\n 9407106,\n 3881528,\n 2058810\n ],\n \"hot\": 0.5\n },\n \"mark\": {\n \"scorch\": 6,\n \"crater\": 4,\n \"dent\": 1.6\n },\n \"flash\": {\n \"light\": 140,\n \"life\": 1.8,\n \"color\": 16748608,\n \"intensity\": 8\n },\n \"shock\": {\n \"duration\": 0.32\n },\n \"shake\": 3,\n \"push\": 14,\n \"mushroom\": {\n \"height\": 16,\n \"radius\": 5.5,\n \"count\": 60,\n \"stem\": 26,\n \"life\": 12,\n \"riseRate\": 0.42\n },\n \"residualFires\": [\n {\n \"radius\": 3.2,\n \"life\": 20,\n \"rate\": 26,\n \"smoke\": 1.4\n },\n {\n \"dx\": 4,\n \"dz\": 3,\n \"radius\": 1.4,\n \"life\": 16\n },\n {\n \"dx\": -4,\n \"dz\": 2,\n \"radius\": 1.2,\n \"life\": 15\n }\n ],\n \"ignite\": 12,\n \"hotParts\": true,\n \"sources\": [\n \"lab:L811\",\n \"lab:L793\"\n ]\n },\n \"generic\": {\n \"id\": \"generic\",\n \"lethal\": 5,\n \"injury\": 15,\n \"visualRadius\": 1.3,\n \"fireballs\": 16,\n \"heat\": 1,\n \"smoke\": {\n \"color\": 6972767,\n \"count\": 18,\n \"life\": 5.5\n },\n \"dust\": {\n \"color\": 8221544,\n \"count\": 30\n },\n \"shrapnel\": {\n \"count\": 150,\n \"kind\": 0,\n \"speed\": 34\n },\n \"chunks\": {\n \"count\": 14,\n \"colors\": [\n 3881528,\n 2894634,\n 4867650\n ],\n \"hot\": 0.25\n },\n \"mark\": {\n \"scorch\": 1.7,\n \"crater\": 0.9,\n \"dent\": 0.25\n },\n \"flash\": {\n \"light\": 45,\n \"life\": 0.45,\n \"color\": 16752720,\n \"intensity\": 6\n },\n \"shock\": {\n \"duration\": 0.32\n },\n \"shake\": 1.1,\n \"push\": 9,\n \"sources\": [\n \"lab:L796\",\n \"lab:L793\",\n \"derived: the M67 profile, for a part with no explosive id; the caller scales it by injury / 15\"\n ]\n }\n}\n","import aliasData from './data/explosion-aliases.json'\nimport explosionData from './data/explosions.json'\nimport type { ExplosionDef } from './types'\n\n/**\n * The 16 profiles of the Explosion Lab (`PROF`, L795-812) keyed by the id of the explosive in `game-weapons` (`m67`, `molotov`, `pipe_bomb`,\n * `propane_p13`, `dynamite`, `c4`, `claymore`, `flashbang`, `m79_40mm`, `m72_66mm`, `at4_84mm`, `pg7v`, `fuel_barrel`, `car_fuel_tank`, `fuel_pump`,\n * `fuel_station_tank`), plus `generic` (the M67 for a part with no explosive id: scale it by `injury / 15`). Each one lists the Lab lines it comes from.\n */\nexport const EXPLOSIONS = explosionData as unknown as Readonly<Record<string, ExplosionDef>>\n\n/** Explosives with no profile of their own: the Carl Gustaf blasts like the AT4, the VOG-25 like the M79. */\nexport const EXPLOSION_ALIASES: Readonly<Record<string, string>> = aliasData\n\n/** A copy of a catalogue with some profiles changed (a partial replaces the fields it names) or removed (`null`); a game adds, tunes or drops effects without touching the core. */\nexport function patchExplosions(base: Readonly<Record<string, ExplosionDef>>, changes: Readonly<Record<string, Partial<ExplosionDef> | ExplosionDef | null>>): Record<string, ExplosionDef> {\n const result: Record<string, ExplosionDef> = { ...base }\n for (const [id, change] of Object.entries(changes)) {\n if (change === null) delete result[id]\n else result[id] = { ...(base[id] ?? {}), ...change, id } as ExplosionDef\n }\n return result\n}\n","export type Rgb = readonly [number, number, number]\n\nconst BYTE_MAX = 255\nconst SRGB_LINEAR_SLOPE = 0.0773993808\nconst SRGB_THRESHOLD = 0.04045\n\n/** The channels of `0xRRGGBB` exactly as three.js r128 reads them (no colour-space conversion): the numbers the Lab's shaders and lights were tuned with. */\nexport function hexToRgb(hex: number): Rgb {\n const whole = Math.floor(hex)\n return [((whole >> 16) & BYTE_MAX) / BYTE_MAX, ((whole >> 8) & BYTE_MAX) / BYTE_MAX, (whole & BYTE_MAX) / BYTE_MAX]\n}\n\nexport function srgbChannelToLinear(channel: number): number {\n return channel < SRGB_THRESHOLD ? channel * SRGB_LINEAR_SLOPE : Math.pow(channel * 0.9478672986 + 0.0521327014, 2.4)\n}\n\n/** `new Color(hex).convertSRGBToLinear()` of r128: the smoke, dust and fire colours the puff shader multiplies the light with. Converted once, at spawn. */\nexport function hexToLinearRgb(hex: number): Rgb {\n const [red, green, blue] = hexToRgb(hex)\n return [srgbChannelToLinear(red), srgbChannelToLinear(green), srgbChannelToLinear(blue)]\n}\n\nexport function linearRgbFromRaw(color: Rgb): Rgb {\n return [srgbChannelToLinear(color[0]), srgbChannelToLinear(color[1]), srgbChannelToLinear(color[2])]\n}\n","import { hexToLinearRgb } from './color'\n\n/** The Lab's smoke colours (L211-214), converted to linear once. Colours of an explosion in data are `0xRRGGBB` and go through `hexToLinearRgb` the same way. */\nexport const SMOKE_COLORS = {\n grey: hexToLinearRgb(0x6a655f),\n dark: hexToLinearRgb(0x4a4440),\n black: hexToLinearRgb(0x3a3431),\n brown: hexToLinearRgb(0x7a5c3e),\n dust: hexToLinearRgb(0x7d7368),\n concrete: hexToLinearRgb(0x8f8a82),\n white: hexToLinearRgb(0xb4b0a8),\n blood: hexToLinearRgb(0x5a0b0b),\n bile: hexToLinearRgb(0x7a8a2a),\n gas: hexToLinearRgb(0xe8ecef),\n} as const\n\nexport const DEFAULT_GRAVITY = 9.8\nexport const DEFAULT_WIND = { x: 0.55, y: 0, z: 0.2 } as const\nexport const FRAME_STEP_LIMIT = 0.05\n","/**\n * A pool that keeps its live records in the order they were added, reuses dead records instead of allocating, and compacts in place. When it is full\n * the oldest record goes (the Lab's `P.shift()`); a pool may choose which one instead (the chunks drop a resting one first).\n */\nexport class OrderedPool<T extends object> {\n /** The live records, oldest first. */\n readonly items: T[] = []\n private readonly spare: T[] = []\n\n constructor(\n readonly capacity: number,\n private readonly create: () => T,\n private readonly release?: (item: T) => void,\n ) {}\n\n get count(): number {\n return this.items.length\n }\n\n /** A record to fill in. At capacity the record `victim` picks (default: the oldest) is dropped and reused. */\n acquire(victim?: (items: readonly T[]) => number): T {\n if (this.items.length >= this.capacity) this.removeAt(victim ? victim(this.items) : 0)\n const record = this.spare.pop() ?? this.create()\n this.items.push(record)\n return record\n }\n\n removeAt(index: number): void {\n const record = this.items[index]\n if (record === undefined) return\n for (let shifted = index; shifted < this.items.length - 1; shifted++) this.items[shifted] = this.items[shifted + 1]!\n this.items.length -= 1\n this.release?.(record)\n this.spare.push(record)\n }\n\n /** Keeps the records `keep` accepts, in the same order, without allocating. `context` goes to `keep` so no closure is needed per frame. */\n compact<TContext>(keep: (item: T, context: TContext) => boolean, context: TContext): void {\n let write = 0\n for (let read = 0; read < this.items.length; read++) {\n const record = this.items[read]!\n if (keep(record, context)) {\n this.items[write++] = record\n continue\n }\n this.release?.(record)\n this.spare.push(record)\n }\n this.items.length = write\n }\n\n clear(): void {\n for (const record of this.items) {\n this.release?.(record)\n this.spare.push(record)\n }\n this.items.length = 0\n }\n}\n","import type { Rgb } from '../color'\nimport { OrderedPool } from '../pool'\nimport type { RandomStream } from '../random'\nimport type { Glow } from './glows'\n\n/** Debris with embers (L366-400). The last block is what the renderer reads: the colour already blended towards the glow of a hot piece. */\nexport interface Chunk {\n x: number\n y: number\n z: number\n vx: number\n vy: number\n vz: number\n rx: number\n ry: number\n rz: number\n ax: number\n ay: number\n az: number\n sx: number\n sy: number\n sz: number\n color: Rgb\n plate: boolean\n hot: boolean\n rest: boolean\n blood: boolean\n splat: boolean\n t: number\n trail: number\n glow: Glow | null\n rng: RandomStream\n outColor: [number, number, number]\n}\n\nexport type ChunkInit = Partial<Pick<Chunk, 'x' | 'y' | 'z' | 'vx' | 'vy' | 'vz' | 'sx' | 'sy' | 'sz' | 'color' | 'plate' | 'hot' | 'blood'>>\n\nconst DEFAULT_COLOR: Rgb = [0x55 / 255, 0x55 / 255, 0x55 / 255]\n\n/** A glow that follows a chunk keeps the spot the chunk had when the chunk leaves the pool, as the Lab's does (the object stays where it was). */\nfunction freezeFollowingGlow(chunk: Chunk): void {\n const glow = chunk.glow\n chunk.glow = null\n if (!glow || glow.follow !== chunk) return\n glow.x = chunk.x + glow.fx\n glow.y = chunk.y + glow.fy\n glow.z = chunk.z + glow.fz\n glow.fx = glow.fy = glow.fz = 0\n glow.follow = null\n}\n\nexport function createChunkPool(capacity: number, placeholder: RandomStream): OrderedPool<Chunk> {\n return new OrderedPool<Chunk>(\n capacity,\n () => ({\n x: 0, y: 0, z: 0, vx: 0, vy: 0, vz: 0, rx: 0, ry: 0, rz: 0, ax: 0, ay: 0, az: 0, sx: 0.2, sy: 0.2, sz: 0.2, color: DEFAULT_COLOR, plate: false, hot: false, rest: false,\n blood: false, splat: false, t: 0, trail: 0, glow: null, rng: placeholder, outColor: [0, 0, 0],\n }),\n freezeFollowingGlow,\n )\n}\n\nfunction firstResting(chunks: readonly Chunk[]): number {\n const index = chunks.findIndex((chunk) => chunk.rest)\n return index >= 0 ? index : 0\n}\n\n/** `chunk()` (L368-376). Draws rotation and spin first (six numbers, always), then lets the caller add what it wants; a full pool drops a resting piece, else the oldest. */\nexport function spawnChunk(pool: OrderedPool<Chunk>, random: RandomStream, init: ChunkInit): Chunk {\n const chunk = pool.acquire(firstResting)\n chunk.x = init.x ?? 0\n chunk.y = init.y ?? 0\n chunk.z = init.z ?? 0\n chunk.vx = init.vx ?? 0\n chunk.vy = init.vy ?? 0\n chunk.vz = init.vz ?? 0\n chunk.rx = random.next() * 6\n chunk.ry = random.next() * 6\n chunk.rz = random.next() * 6\n chunk.ax = random.between(-12, 12)\n chunk.ay = random.between(-12, 12)\n chunk.az = random.between(-12, 12)\n chunk.sx = init.sx ?? 0.2\n chunk.sy = init.sy ?? 0.2\n chunk.sz = init.sz ?? 0.2\n chunk.color = init.color ?? DEFAULT_COLOR\n chunk.plate = init.plate ?? false\n chunk.hot = init.hot ?? false\n chunk.rest = false\n chunk.blood = init.blood ?? false\n chunk.splat = false\n chunk.t = 0\n chunk.trail = 0\n chunk.glow = null\n chunk.rng = random\n return chunk\n}\n\nexport const HOT_COLOR: Rgb = [1, 0.42, 0.1]\nexport const HOT_SECONDS = 1.6\nconst HOT_BLEND = 0.85\n\n/** `_hotCol` (L399-400): the colour of a hot piece, blended towards the glow while it is young. */\nexport function colorOfChunk(chunk: Chunk): readonly [number, number, number] {\n if (!chunk.hot || chunk.t >= HOT_SECONDS) {\n chunk.outColor[0] = chunk.color[0]\n chunk.outColor[1] = chunk.color[1]\n chunk.outColor[2] = chunk.color[2]\n return chunk.outColor\n }\n const amount = Math.max(0, 1 - chunk.t / HOT_SECONDS) * HOT_BLEND\n chunk.outColor[0] = chunk.color[0] + (HOT_COLOR[0] - chunk.color[0]) * amount\n chunk.outColor[1] = chunk.color[1] + (HOT_COLOR[1] - chunk.color[1]) * amount\n chunk.outColor[2] = chunk.color[2] + (HOT_COLOR[2] - chunk.color[2]) * amount\n return chunk.outColor\n}\n","import type { MutableVec3 } from './random'\n\n/** Operations on `{ x, y, z }` written the way three.js r128 does them (`normalize` multiplies by the reciprocal), so a port repeats the Lab's numbers bit for bit. */\n\nexport function createVec3(x = 0, y = 0, z = 0): MutableVec3 {\n return { x, y, z }\n}\n\nexport function addScaled(target: MutableVec3, direction: MutableVec3, scale: number): MutableVec3 {\n target.x += direction.x * scale\n target.y += direction.y * scale\n target.z += direction.z * scale\n return target\n}\n\nexport function normalize(target: MutableVec3): MutableVec3 {\n const length = Math.sqrt(target.x * target.x + target.y * target.y + target.z * target.z) || 1\n const inverse = 1 / length\n target.x *= inverse\n target.y *= inverse\n target.z *= inverse\n return target\n}\n\nexport function distanceBetween(ax: number, ay: number, az: number, bx: number, by: number, bz: number): number {\n const dx = ax - bx\n const dy = ay - by\n const dz = az - bz\n return Math.sqrt(dx * dx + dy * dy + dz * dz)\n}\n\nexport function lerp(from: number, to: number, amount: number): number {\n return from + (to - from) * amount\n}\n\nexport function clamp(value: number, min: number, max: number): number {\n return value < min ? min : value > max ? max : value\n}\n","import type { Rgb } from '../color'\nimport { lerp } from '../vector'\nimport { OrderedPool } from '../pool'\n\n/** The position a glow follows (a chunk, or anything with `x`, `y`, `z`). */\nexport interface FollowPoint {\n readonly x: number\n readonly y: number\n readonly z: number\n}\n\n/** An additive halo (L294-318). The last block is what the renderer reads after `update`: where it is, how big and how bright. */\nexport interface Glow {\n x: number\n y: number\n z: number\n s: number\n s1: number | null\n i: number\n t: number\n life: number\n color: Rgb\n curve: number\n flick: boolean\n follow: FollowPoint | null\n fx: number\n fy: number\n fz: number\n outX: number\n outY: number\n outZ: number\n outSize: number\n outIntensity: number\n}\n\nexport type GlowInit = Partial<Omit<Glow, 't' | 'outX' | 'outY' | 'outZ' | 'outSize' | 'outIntensity'>>\n\nconst DEFAULT_COLOR: Rgb = [1, 0.6, 0.25]\n\nexport function createGlowPool(capacity: number): OrderedPool<Glow> {\n return new OrderedPool<Glow>(capacity, () => ({\n x: 0, y: 0, z: 0, s: 1, s1: null, i: 1, t: 0, life: 0.3, color: DEFAULT_COLOR, curve: 2, flick: false, follow: null, fx: 0, fy: 0, fz: 0,\n outX: 0, outY: 0, outZ: 0, outSize: 0, outIntensity: 0,\n }))\n}\n\nexport function spawnGlow(pool: OrderedPool<Glow>, init: GlowInit): Glow {\n const glow = pool.acquire()\n glow.x = init.x ?? 0\n glow.y = init.y ?? 0\n glow.z = init.z ?? 0\n glow.s = init.s ?? 1\n glow.s1 = init.s1 ?? null\n glow.i = init.i ?? 1\n glow.t = 0\n glow.life = init.life ?? 0.3\n glow.color = init.color ?? DEFAULT_COLOR\n glow.curve = init.curve ?? 2\n glow.flick = init.flick ?? false\n glow.follow = init.follow ?? null\n glow.fx = init.fx ?? 0\n glow.fy = init.fy ?? 0\n glow.fz = init.fz ?? 0\n return glow\n}\n\nfunction ageOrKeep(glow: Glow, dt: number): boolean {\n glow.t += dt\n return glow.t < glow.life\n}\n\nconst FLICKER_RATE = 40\nconst FLICKER_SPREAD = 9\n\n/** `updateGlows` (L305-318). */\nexport function updateGlows(pool: OrderedPool<Glow>, dt: number): void {\n pool.compact(ageOrKeep, dt)\n for (const glow of pool.items) {\n const age = glow.t / glow.life\n let x = glow.x\n let y = glow.y\n let z = glow.z\n if (glow.follow) {\n x = glow.follow.x + glow.fx\n y = glow.follow.y + glow.fy\n z = glow.follow.z + glow.fz\n }\n glow.outX = x\n glow.outY = y\n glow.outZ = z\n glow.outSize = glow.s1 !== null ? lerp(glow.s, glow.s1, 1 - Math.pow(1 - age, 3)) : glow.s\n const flicker = glow.flick ? 0.75 + 0.25 * Math.sin(glow.t * FLICKER_RATE + glow.s * FLICKER_SPREAD) : 1\n glow.outIntensity = glow.i * Math.pow(1 - age, glow.curve) * flicker\n }\n}\n","import type { Rgb } from '../color'\nimport { SMOKE_COLORS } from '../constants'\nimport { OrderedPool } from '../pool'\nimport type { RandomStream } from '../random'\n\n/** The ring a mushroom cloud's puffs travel on (L722-735). */\nexport interface Ring {\n x: number\n z: number\n y0: number\n rise: number\n rk: number\n R0: number\n R1: number\n r: number\n roll: number\n t: number\n life: number\n}\n\n/**\n * Fire, smoke, dust, blood and gas (L193-238). `kind`: 0 fire that turns into smoke, 1 dust, 2 blood, 3 gas or white powder smoke, 4 bile.\n * The last block is what the renderer reads after `update`.\n */\nexport interface Puff {\n x: number\n y: number\n z: number\n vx: number\n vy: number\n vz: number\n s0: number\n s1: number\n sg: number\n gk: number\n life: number\n t: number\n h0: number\n hl: number\n drag: number\n buoy: number\n grav: number\n kind: number\n seed: number\n wind: number\n color: Rgb\n ring: Ring | null\n ph: number\n th: number\n rng: RandomStream\n age: number\n heat: number\n size: number\n}\n\nexport type PuffInit = Partial<Omit<Puff, 'seed' | 'age' | 'heat' | 'size' | 't' | 'rng'>>\n\nconst FLOOR_FRACTION = 0.4\nconst FLOOR_BOUNCE = -0.2\nconst ARRIVAL_FRAME = 30\nconst ARRIVAL_BASE = 0.3\nconst GROWTH_CAP_SECONDS = 3.5\n\nexport function createPuffPool(capacity: number): OrderedPool<Puff> {\n const placeholder: RandomStream = { next: () => 0, between: (min) => min, direction: (out) => out, state: 0 }\n return new OrderedPool<Puff>(capacity, () => ({\n x: 0, y: 0, z: 0, vx: 0, vy: 0, vz: 0, s0: 0.2, s1: 1, sg: 0.1, gk: 6, life: 3, t: 0, h0: 0, hl: 0.4, drag: 3, buoy: 0.5, grav: 0, kind: 0, seed: 0, wind: 1,\n color: SMOKE_COLORS.grey, ring: null, ph: 0, th: 0, rng: placeholder, age: 0, heat: 0, size: 0,\n }))\n}\n\n/** `puff()` of the Lab: draws `seed` first, always, even when the caller gives one (L196), and drops the oldest puff when the pool is full. */\nexport function spawnPuff(pool: OrderedPool<Puff>, random: RandomStream, init: PuffInit): Puff {\n const puff = pool.acquire()\n puff.x = init.x ?? 0\n puff.y = init.y ?? 0\n puff.z = init.z ?? 0\n puff.vx = init.vx ?? 0\n puff.vy = init.vy ?? 0\n puff.vz = init.vz ?? 0\n puff.s0 = init.s0 ?? 0.2\n puff.s1 = init.s1 ?? 1\n puff.sg = init.sg ?? 0.1\n puff.gk = init.gk ?? 6\n puff.life = init.life ?? 3\n puff.t = 0\n puff.h0 = init.h0 ?? 0\n puff.hl = init.hl ?? 0.4\n puff.drag = init.drag ?? 3\n puff.buoy = init.buoy ?? 0.5\n puff.grav = init.grav ?? 0\n puff.kind = init.kind ?? 0\n puff.seed = random.next()\n puff.wind = init.wind ?? 1\n puff.color = init.color ?? SMOKE_COLORS.grey\n puff.ring = init.ring ?? null\n puff.ph = init.ph ?? 0\n puff.th = init.th ?? 0\n puff.rng = random\n puff.age = 0\n puff.heat = 0\n puff.size = 0\n return puff\n}\n\nfunction ageOrKeep(puff: Puff, dt: number): boolean {\n puff.t += dt\n return puff.t < puff.life\n}\n\nfunction moveOnRing(puff: Puff, ring: Ring, wind: { x: number; z: number }): void {\n const ringTime = ring.t\n const centerY = ring.y0 + ring.rise * (1 - Math.exp(-ringTime * ring.rk))\n const majorRadius = ring.R0 + (ring.R1 - ring.R0) * (1 - Math.exp(-ringTime * 1.6))\n const theta = puff.th + ringTime * ring.roll\n const minorRadius = ring.r * (0.8 + 0.4 * Math.min(1, ringTime))\n const radius = majorRadius + minorRadius * Math.cos(theta)\n puff.x = ring.x + Math.cos(puff.ph) * radius + wind.x * ringTime * 0.6\n puff.z = ring.z + Math.sin(puff.ph) * radius + wind.z * ringTime * 0.6\n puff.y = centerY + minorRadius * Math.sin(theta)\n}\n\nfunction moveFree(puff: Puff, dt: number, age: number, heat: number, wind: { x: number; z: number }): void {\n const damping = Math.exp(-puff.drag * dt)\n puff.vx *= damping\n puff.vy *= damping\n puff.vz *= damping\n puff.vy += (puff.buoy * (heat > 0 ? 1.6 : 1) - puff.grav) * dt\n puff.x += (puff.vx + wind.x * puff.wind * age) * dt\n puff.y += puff.vy * dt\n puff.z += (puff.vz + wind.z * puff.wind * age) * dt\n if (puff.y < puff.s0 * FLOOR_FRACTION) {\n puff.y = puff.s0 * FLOOR_FRACTION\n if (puff.vy < 0) puff.vy *= FLOOR_BOUNCE\n }\n}\n\n/** `updatePuffs` (L205-238): ages, removes what ended, then moves the rest with the wind (or along their mushroom ring) and derives what the shader reads. */\nexport function updatePuffs(pool: OrderedPool<Puff>, dt: number, wind: { x: number; z: number }): void {\n pool.compact(ageOrKeep, dt)\n for (const puff of pool.items) {\n const age = puff.t / puff.life\n const heat = puff.h0 > 0 ? puff.h0 * Math.pow(Math.max(0, 1 - puff.t / puff.hl), 1.25) : 0\n if (puff.ring) moveOnRing(puff, puff.ring, wind)\n else moveFree(puff, dt, age, heat, wind)\n const size = puff.s0 + (puff.s1 - puff.s0) * (1 - Math.exp(-puff.t * puff.gk)) + puff.sg * Math.min(puff.t, GROWTH_CAP_SECONDS)\n puff.size = size * Math.min(1, puff.t * ARRIVAL_FRAME + ARRIVAL_BASE)\n puff.age = age\n puff.heat = heat\n }\n}\n","import { OrderedPool } from '../pool'\n\n/** Shrapnel, sparks and embers, stretched along their velocity by the shader (L239-293). `kind`: 0 hot fragment, 1 steel ball, 2 white spark, 3 glass, 4 slow ember. */\nexport interface Spark {\n x: number\n y: number\n z: number\n vx: number\n vy: number\n vz: number\n t: number\n life: number\n w: number\n st: number\n kind: number\n drag: number\n g: number\n bounce: number\n age: number\n}\n\nexport type SparkInit = Partial<Omit<Spark, 't' | 'age'>>\n\nconst GROUND_LIFT = 0.02\nconst BOUNCE_SLIDE = 0.55\n\nexport function createSparkPool(capacity: number): OrderedPool<Spark> {\n return new OrderedPool<Spark>(capacity, () => ({ x: 0, y: 0, z: 0, vx: 0, vy: 0, vz: 0, t: 0, life: 0.8, w: 0.04, st: 0.035, kind: 0, drag: 0.6, g: 9.8, bounce: 0.35, age: 0 }))\n}\n\nexport function spawnSpark(pool: OrderedPool<Spark>, init: SparkInit): Spark {\n const spark = pool.acquire()\n spark.x = init.x ?? 0\n spark.y = init.y ?? 0\n spark.z = init.z ?? 0\n spark.vx = init.vx ?? 0\n spark.vy = init.vy ?? 0\n spark.vz = init.vz ?? 0\n spark.t = 0\n spark.life = init.life ?? 0.8\n spark.w = init.w ?? 0.04\n spark.st = init.st ?? 0.035\n spark.kind = init.kind ?? 0\n spark.drag = init.drag ?? 0.6\n spark.g = init.g ?? 9.8\n spark.bounce = init.bounce ?? 0.35\n spark.age = 0\n return spark\n}\n\nfunction ageOrKeep(spark: Spark, dt: number): boolean {\n spark.t += dt\n return spark.t < spark.life\n}\n\nexport interface SparkStep {\n readonly dt: number\n readonly groundAt: (x: number, z: number) => number\n}\n\n/** `updateSparks` (L277-293): drag, gravity, a bounce that kills some speed on the ground. */\nexport function updateSparks(pool: OrderedPool<Spark>, step: SparkStep): void {\n pool.compact(ageOrKeep, step.dt)\n for (const spark of pool.items) {\n const damping = Math.exp(-spark.drag * step.dt)\n spark.vx *= damping\n spark.vy *= damping\n spark.vz *= damping\n spark.vy -= spark.g * step.dt\n spark.x += spark.vx * step.dt\n spark.y += spark.vy * step.dt\n spark.z += spark.vz * step.dt\n const floor = step.groundAt(spark.x, spark.z) + GROUND_LIFT\n if (spark.y < floor) {\n spark.y = floor\n if (spark.vy < 0) {\n spark.vy *= -spark.bounce\n spark.vx *= BOUNCE_SLIDE\n spark.vz *= BOUNCE_SLIDE\n }\n }\n spark.age = spark.t / spark.life\n }\n}\n","import type { VfxCore } from './core'\nimport { spawnChunk } from './pools/chunks'\nimport type { Chunk, ChunkInit } from './pools/chunks'\nimport { spawnGlow } from './pools/glows'\nimport type { Glow, GlowInit } from './pools/glows'\nimport { spawnPuff } from './pools/puffs'\nimport type { Puff, PuffInit } from './pools/puffs'\nimport { spawnSpark } from './pools/sparks'\nimport type { Spark, SparkInit } from './pools/sparks'\nimport type { RandomStream } from './random'\n\nconst HOT_GLOW_COLOR = [1, 0.45, 0.12] as const\n\n/** The spawners count what an explosion makes (the Lab's `evCount`) on top of the pools, and a hot chunk carries a halo, as in the Lab (L371-376). */\nexport function addPuff(core: VfxCore, random: RandomStream, init: PuffInit): Puff {\n core.spawned.puffs++\n const puff = spawnPuff(core.puffs, random, init)\n core.trace?.puff?.(puff)\n return puff\n}\n\nexport function addSpark(core: VfxCore, init: SparkInit): Spark {\n core.spawned.sparks++\n const spark = spawnSpark(core.sparks, init)\n core.trace?.spark?.(spark)\n return spark\n}\n\nexport function addGlow(core: VfxCore, init: GlowInit): Glow {\n const glow = spawnGlow(core.glows, init)\n core.trace?.glow?.(glow)\n return glow\n}\n\nexport function addChunk(core: VfxCore, random: RandomStream, init: ChunkInit): Chunk {\n const chunk = spawnChunk(core.chunks, random, init)\n core.spawned.chunks++\n if (chunk.hot && core.layers.shrapnel) {\n chunk.glow = addGlow(core, { follow: chunk, s: 0.35 + chunk.sx, i: 2.2, life: random.between(1, 2.2), color: HOT_GLOW_COLOR, curve: 1.5 })\n }\n core.trace?.chunk?.(chunk)\n return chunk\n}\n","import type { Rgb } from '../color'\nimport { SMOKE_COLORS } from '../constants'\nimport type { VfxCore } from '../core'\nimport { OrderedPool } from '../pool'\nimport type { RandomStream } from '../random'\nimport { addPuff, addSpark } from '../spawn'\nimport type { Follow } from '../types'\nimport { lerp } from '../vector'\n\n/** A lingering fire (L424-442): it grows, gives flames that cool into smoke, and fades out. */\nexport interface Fire {\n x: number\n y: number\n z: number\n r: number\n r0: number\n grow: number\n life: number\n t: number\n rate: number\n acc: number\n smoke: number\n smokeAcc: number\n fade: number\n follow: Follow | null\n fx: number\n fy: number\n fz: number\n color: Rgb\n rng: RandomStream\n handle: number\n}\n\nexport type FireInit = Partial<Pick<Fire, 'x' | 'y' | 'z' | 'r' | 'grow' | 'life' | 'rate' | 'smoke' | 'follow' | 'fx' | 'fy' | 'fz' | 'color'>> & { startRadius?: number }\n\nexport function createFirePool(capacity: number, placeholder: RandomStream): OrderedPool<Fire> {\n return new OrderedPool<Fire>(capacity, () => ({\n x: 0, y: 0, z: 0, r: 0.6, r0: 0.6, grow: 0, life: 6, t: 0, rate: 22, acc: 0, smoke: 0.6, smokeAcc: 0, fade: 1, follow: null, fx: 0, fy: 0, fz: 0, color: SMOKE_COLORS.black,\n rng: placeholder, handle: 0,\n }))\n}\n\nexport function spawnFire(pool: OrderedPool<Fire>, random: RandomStream, init: FireInit, handle: number): Fire {\n const fire = pool.acquire()\n fire.x = init.x ?? 0\n fire.y = init.y ?? 0\n fire.z = init.z ?? 0\n fire.r = init.r ?? 0.6\n fire.r0 = init.startRadius ?? fire.r\n fire.grow = init.grow ?? 0\n fire.life = init.life ?? 6\n fire.t = 0\n fire.rate = init.rate ?? 22\n fire.acc = 0\n fire.smoke = init.smoke ?? 0.6\n fire.smokeAcc = 0\n fire.fade = 1\n fire.follow = init.follow ?? null\n fire.fx = init.fx ?? 0\n fire.fy = init.fy ?? 0\n fire.fz = init.fz ?? 0\n fire.color = init.color ?? SMOKE_COLORS.black\n fire.rng = random\n fire.handle = handle\n return fire\n}\n\nexport function addFire(core: VfxCore, random: RandomStream, init: FireInit, handle: number): Fire {\n const fire = spawnFire(core.fires, random, init, handle)\n core.trace?.fire?.(fire)\n return fire\n}\n\nfunction positionOf(follow: Follow): { x: number; y: number; z: number } {\n return 'position' in follow ? follow.position : follow\n}\n\nfunction emitFlames(core: VfxCore, fire: Fire, radius: number, dt: number): void {\n const random = fire.rng\n fire.acc += dt * fire.rate * (0.5 + radius * 0.9) * fire.fade * core.clock.lod\n while (fire.acc >= 1) {\n fire.acc -= 1\n const angle = random.next() * Math.PI * 2\n const distance = Math.sqrt(random.next()) * radius\n addPuff(core, random, {\n x: fire.x + Math.cos(angle) * distance, y: fire.y + 0.05, z: fire.z + Math.sin(angle) * distance,\n vx: random.between(-0.2, 0.2), vy: random.between(0.6, 1.4), vz: random.between(-0.2, 0.2),\n s0: 0.1, s1: random.between(0.28, 0.46) * (0.8 + Math.min(radius, 3) * 0.22), sg: -0.08,\n life: random.between(0.45, 0.85), h0: random.between(1, 1.25), hl: random.between(0.35, 0.6),\n drag: 1.5, buoy: 3.2, color: fire.color, kind: 0, wind: 0.4,\n })\n }\n}\n\nfunction emitSmoke(core: VfxCore, fire: Fire, radius: number, dt: number): void {\n const random = fire.rng\n fire.smokeAcc += dt * fire.smoke * (1 + radius * 0.8) * fire.fade * core.clock.lod\n while (fire.smokeAcc >= 1 && core.layers.smoke) {\n fire.smokeAcc -= 1\n addPuff(core, random, {\n x: fire.x + random.between(-radius, radius) * 0.5, y: fire.y + 0.6 + radius * 0.3, z: fire.z + random.between(-radius, radius) * 0.5,\n vy: random.between(0.8, 1.5), s0: 0.25, s1: 0.6 + Math.min(radius, 2) * 0.3, sg: 0.3, life: random.between(4, 6.5),\n h0: 0.25, hl: 0.25, drag: 0.6, buoy: 0.55, color: fire.color, kind: 0,\n })\n }\n}\n\nfunction emitEmber(core: VfxCore, fire: Fire, radius: number, dt: number): void {\n const random = fire.rng\n if (!(random.next() < dt * 6 * fire.fade && core.layers.shrapnel)) return\n addSpark(core, {\n x: fire.x + random.between(-radius, radius) * 0.6, y: fire.y + 0.4, z: fire.z + random.between(-radius, radius) * 0.6,\n vx: random.between(-0.4, 0.4), vy: random.between(1.5, 3.2), vz: random.between(-0.4, 0.4),\n life: random.between(0.8, 1.6), w: 0.025, st: 0.02, kind: 4, g: -0.6, drag: 1.2,\n })\n}\n\n/** `updateFires` (L430-442), walking the list backwards like the Lab. */\nexport function updateFires(core: VfxCore, dt: number): void {\n const pool = core.fires\n for (let index = pool.items.length - 1; index >= 0; index--) {\n const fire = pool.items[index]!\n fire.t += dt\n if (fire.follow) {\n const point = positionOf(fire.follow)\n fire.x = point.x + fire.fx\n fire.y = point.y + fire.fy\n fire.z = point.z + fire.fz\n }\n const radius = fire.grow ? lerp(fire.r0, fire.r, Math.min(1, fire.t / fire.grow)) : fire.r\n fire.fade = fire.t < fire.life - 1.5 ? Math.min(1, fire.t * 3) : Math.max(0, (fire.life - fire.t) / 1.5)\n if (fire.t >= fire.life) {\n pool.removeAt(index)\n continue\n }\n if (!core.layers.fire) continue\n emitFlames(core, fire, radius, dt)\n emitSmoke(core, fire, radius, dt)\n emitEmber(core, fire, radius, dt)\n }\n}\n","import type { Expr } from './expression'\n\n/** Constructors for the computed fields of a recipe. They only build data (`{ $, a }`); `expression.ts` runs it. */\nconst call =\n (name: string) =>\n (...args: readonly unknown[]): Expr => ({ $: name, a: args })\n\nexport const rnd = call('rnd')\nexport const rand = call('rand')\nexport const add = call('add')\nexport const concat = call('concat')\nexport const sub = call('sub')\nexport const mul = call('mul')\nexport const div = call('div')\nexport const mod = call('mod')\nexport const neg = call('neg')\nexport const abs = call('abs')\nexport const sqrt = call('sqrt')\nexport const sin = call('sin')\nexport const cos = call('cos')\nexport const atan2 = call('atan2')\nexport const min = call('min')\nexport const max = call('max')\nexport const floor = call('floor')\nexport const ceil = call('ceil')\nexport const round = call('round')\nexport const clamp = call('clamp')\nexport const lerp = call('lerp')\nexport const ease = call('ease')\nexport const lt = call('lt')\nexport const gt = call('gt')\nexport const le = call('le')\nexport const ge = call('ge')\nexport const eq = call('eq')\nexport const ne = call('ne')\nexport const not = call('not')\nexport const and = call('and')\nexport const or = call('or')\nexport const ite = call('ite')\nexport const vadd = call('vadd')\nexport const vsub = call('vsub')\nexport const vscale = call('vscale')\nexport const vnorm = call('vnorm')\nexport const vneg = call('vneg')\nexport const vlerp = call('vlerp')\nexport const vx = call('vx')\nexport const vy = call('vy')\nexport const vz = call('vz')\nexport const vdist = call('vdist')\nexport const vwith = call('with')\nexport const pick = call('at')\nexport const scaled = call('scaled')\nexport const param = (...values: readonly unknown[]): Expr => ({ $: 'param', a: [values] })\n/** Binds `name` to `value` while `body` resolves: the page's `const a = ...` inside a spawn callback. */\nexport const local = (name: string, value: unknown, body: unknown): Expr => ({ $: 'let', a: [name, value, body] })\nexport const query = (behavior: string, name: string, ...args: readonly unknown[]): Expr => ({ $: 'query', a: [behavior, name, ...args] })\nexport const variable = (name: string): Expr => ({ $: 'var', a: [name] })\nexport const cameraForward = call('cameraForward')\nexport const TIME: Expr = { $: 't' }\nexport const PARAM: Expr = { $: 'paramIndex' }\nexport const DELTA: Expr = { $: 'dt' }\nexport const INDEX: Expr = { $: 'index' }\nexport const QUALITY: Expr = { $: 'quality' }\nexport const FRESH: Expr = { $: 'fresh' }\nexport const DIRECTION: Expr = { $: 'direction' }\nexport const NORMAL: Expr = { $: 'normal' }\nexport const CAMERA_POSITION: Expr = { $: 'cameraPosition' }\nexport const UP = [0, 1, 0] as const\n","import type { Computed, EffectStep, ScreenKey } from './types'\n\ntype System = 'add' | 'normal' | 'streak'\n\n/** Constructors for the steps of a recipe (they only build data). */\nexport const burst = (system: System, count: Computed, spawn: Computed): EffectStep => ({ op: 'burst', system, count, spawn })\nexport const spawn = (system: System, options: Computed): EffectStep => ({ op: 'spawn', system, spawn: options })\nexport const emit = (key: string, system: System, rate: Computed, options: Computed): EffectStep => ({ op: 'emit', key, system, rate, spawn: options })\nexport const debris = (options: Computed): EffectStep => ({ op: 'debris', spawn: options })\nexport const decal = (...args: readonly Computed[]): EffectStep => ({ op: 'decal', args })\nexport const ring = (...args: readonly Computed[]): EffectStep => ({ op: 'ring', args })\nexport const flash = (...args: readonly Computed[]): EffectStep => ({ op: 'flash', args })\nexport const lens = (...args: readonly Computed[]): EffectStep => ({ op: 'lens', args })\nexport const screen = (change: { add?: Partial<Record<ScreenKey, Computed>>; set?: Partial<Record<ScreenKey, Computed>>; max?: Partial<Record<ScreenKey, Computed>> }): EffectStep => ({ op: 'screen', ...change })\nexport const fireLight = (slot: 0 | 1, point: Computed, intensity: Computed, time: Computed): EffectStep => ({ op: 'fire_light', slot, point, intensity, time })\nexport const fireLightOff = (slot: 0 | 1): EffectStep => ({ op: 'fire_light_off', slot })\nexport const cue = (name: string, args?: Computed, draws?: number): EffectStep => ({ op: 'cue', cue: name, ...(args !== undefined ? { args } : {}), ...(draws !== undefined ? { draws } : {}) })\nexport const call = (macro: string, ...args: readonly Computed[]): EffectStep => ({ op: 'call', macro, args })\nexport const after = (time: Computed, ...steps: readonly EffectStep[]): EffectStep => ({ op: 'at', time, steps })\nexport const when = (condition: Computed, steps: readonly EffectStep[], otherwise?: readonly EffectStep[]): EffectStep => ({ op: 'when', if: condition, steps, ...(otherwise ? { otherwise } : {}) })\nexport const repeat = (count: Computed, ...steps: readonly EffectStep[]): EffectStep => ({ op: 'repeat', count, steps })\nexport const set = (name: string, value: Computed): EffectStep => ({ op: 'set', name, value })\nexport const behavior = (id: string, action: string, args?: Computed): EffectStep => ({ op: 'behavior', id, action, ...(args !== undefined ? { args } : {}) })\nexport const append = (name: string, value: Computed): EffectStep => ({ op: 'append', name, value })\nexport const camera = (eye: Computed, target: Computed, instant?: boolean): EffectStep => ({ op: 'camera', eye, target, ...(instant ? { instant } : {}) })\nexport const post = (values: Readonly<Record<string, Computed>>): EffectStep => ({ op: 'post', values })\n","import { gt, lt, and, INDEX, ite, mod, scaled, TIME } from '../dsl'\nimport { after, burst, debris, decal, emit, repeat, ring, screen, when } from '../steps'\nimport type { EffectDef } from '../types'\n\nconst CENTRE: readonly [number, number, number] = [0, 1.1, 0]\nconst UP: readonly [number, number, number] = [0, 1, 0]\n\n/** W501-520: the belly bursts after a second and leaves a green cloud near the ground for eight. */\nexport const BLOATER_CLOUD: EffectDef = {\n id: 'bloater_cloud',\n pageId: 'inchado',\n team: 'creatures',\n budget: { particles: 180 },\n loop: 9,\n view: { eye: [5.5, 3.2, 6], target: [0, 1, 0] },\n timeline: [\n after(\n 1,\n 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: 0x6f8a3c, c1: 0x3b4a26, a: 0.32, fi: 0.08, g: 0.05 }),\n burst('normal', 30, { p: CENTRE, dir: UP, cone: 1.3, sp: [2, 4], life: 1.4, size: [0.04, 0.08], c0: 0x4a5a1c, g: 9.8, fi: 0, gnd: 3, ds: [0.1, 0.2] }),\n 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), 0x6b7a52, 0x4a1a12), life: 9 })),\n decal(0, 0, 0, 1.6, 0x6a7a24, { grow: 0.6, life: 60, fluid: 'bile' }),\n ring([0, 0.02, 0], 4, 4, 8, 0x3a5a20, UP, 0.35),\n screen({ add: { shake: 0.15 } }),\n ),\n ],\n tick: [\n when(and(gt(TIME, 1), lt(TIME, 8)), [\n 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: 0x6f8a3c, c1: 0x3b4a26, a: 0.22, fi: 0.3 }),\n 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: 0x5fe08a, a: 0.7, fi: 0.4 }),\n ]),\n ],\n}\n","import { add, and, gt, lt, mul, param, PARAM, pick, TIME, vnorm } from '../dsl'\nimport { after, call, decal, emit, when } from '../steps'\nimport type { EffectDef } from '../types'\n\nconst BLOOD = param(0x8a0a0a, 0x5a0c10, 0x3a1c12, 0x1d1914)\nconst BLEED = [1, 0.7, 0.35]\n\n/** W449-473: blood by infection age: a mist at the entry, a jet at the exit, a pool that grows and darkens. */\nexport const BLOOD_HIT: EffectDef = {\n id: 'blood_hit',\n pageId: 'sangue',\n team: 'creatures',\n params: { id: 'age', options: ['fresh', 'recent', 'old', 'decrepit'], default: 0 },\n budget: { particles: 70, decals: 1 },\n loop: 3.2,\n view: { eye: [2.6, 1.9, 3.4], target: [0, 1, -0.6] },\n timeline: [\n after(0.3, call('blood_hit', [0, 1.3, 0.1], vnorm([0.15, 0.1, -1]), PARAM)),\n after(1.5, call('blood_hit', [0.1, 1.05, 0.1], vnorm([-0.2, 0.05, -1]), PARAM, 0.8)),\n 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: 0x3b1d14 }))]),\n ],\n tick: [\n when(and(lt(PARAM, 2), gt(TIME, 0.3)), [\n emit('drip', 'normal', param(7, 3), {\n p: [0.05, 1.15, 0.17], box: [0.08, 0.05, 0], dir: [0, 1, 0], cone: 0.2, sp: [0, 0.2], life: 1.2, size: 0.03, c0: BLOOD, g: 9.8, fi: 0, gnd: 3, ds: [0.04, 0.08],\n }),\n ]),\n ],\n}\n","import { and, gt, lt, TIME } from '../dsl'\nimport { after, decal, emit, when } from '../steps'\nimport type { EffectDef } from '../types'\n\nconst UP: readonly [number, number, number] = [0, 1, 0]\n\n/** W540-553: fifteen minutes in eight seconds: the pool dries, flies appear from the minute five and the body turns to dust. */\nexport const CORPSE_DECAY: EffectDef = {\n id: 'corpse_decay',\n pageId: 'corpo',\n team: 'creatures',\n budget: { particles: 40 },\n loop: 8.5,\n view: { eye: [2.4, 1.6, 2.6], target: [0, 0.3, -0.6] },\n timeline: [after(0.8, decal(0, 0, -0.9, 1.1, 0x5a0c10, { grow: 1.5, dark: 5, dry: 0x3b1d14 }))],\n tick: [\n when(and(gt(TIME, 2.5), lt(TIME, 7)), [\n 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: 0x050505, a: 1, fi: 0.2 }),\n ]),\n when(and(gt(TIME, 6.8), lt(TIME, 7.6)), [\n emit('dust', 'normal', 40, { p: [0, 0.15, -0.8], box: [0.25, 0.05, 0.8], dir: UP, cone: 0.6, sp: [0.2, 0.6], drag: 1, life: [1, 1.8], size: [0.12, 0.22], grow: 3, c0: 0x2b2722, a: 0.5 }),\n ]),\n ],\n}\n","import { and, INDEX, ite, lt, not, pick, query, QUALITY, rnd, variable } from '../dsl'\nimport { behavior, repeat, set, spawn, when } from '../steps'\nimport type { EffectDef } from '../types'\n\n/** Which of the ten walkers run: the ones with red eyes (W192). */\nconst RUNNERS = [false, true, false, false, false, true, false, false, false, true]\nconst RUN_SPEED = 3.8\nconst WALK_SPEED = 0.7\nconst SPEED = ite(pick(RUNNERS, INDEX), RUN_SPEED, WALK_SPEED)\nconst walkTo = behavior('walkers', 'walk', { index: INDEX, to: [-12, 0, query('walkers', 'z', INDEX)], speed: SPEED })\n\n/** W562-571 (the page's `olhos`): ten zombies walk through fog from the east; the runners leave a short red trail behind their eyes. */\nexport const EYES_IN_FOG: EffectDef = {\n id: 'eyes_in_fog',\n pageId: 'olhos',\n team: 'creatures',\n budget: { particles: 20 },\n loop: 0,\n view: { eye: [-9, 1.7, 2], target: [6, 1.3, 2] },\n setup: [\n 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),\n ],\n timeline: [],\n tick: [\n repeat(\n 10,\n 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])]),\n when(and(pick(RUNNERS, INDEX), lt(QUALITY, 2)), [\n 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: 0xe5392b, c1: 0x400000, a: 0.8 }),\n ]),\n ),\n ],\n}\n","import { ite, gt, lt, and, mul, sin, sub, vnorm, TIME } from '../dsl'\nimport { after, burst, debris, emit, when } from '../steps'\nimport type { EffectDef } from '../types'\n\nconst SHOULDER: readonly [number, number, number] = [0.36, 1.46, 0.05]\nconst FRESH_BLOOD = 0x8a0a0a\n\n/** W487-500: the arm leaves as a loose piece and the shoulder spurts in pulses for half a second. */\nexport const DISMEMBER: EffectDef = {\n id: 'dismember',\n pageId: 'membro',\n team: 'creatures',\n budget: { particles: 60, debris: 1, decals: 4 },\n loop: 3.6,\n view: { eye: [3, 1.8, 2.2], target: [0.4, 1.1, 0] },\n timeline: [\n after(\n 0.4,\n debris({ p: SHOULDER, v: [2.2, 2.5, -0.6], sp: 0, size: [0.12, 0.12, 0.66], c: 0x6d7a5a, spin: 9, life: 12, bounce: 0.25 }),\n 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 }),\n ),\n ],\n tick: [\n when(and(gt(TIME, 0.4), lt(TIME, 1.1)), [\n 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] }),\n ]),\n ],\n}\n","import { ite, mod, INDEX, scaled, vnorm } from '../dsl'\nimport { after, burst, debris, decal, flash, repeat } from '../steps'\nimport type { EffectDef } from '../types'\n\nconst FRESH_BLOOD = 0x8a0a0a\n\n/** W474-486: the head bursts, the eyes go out in the same frame and the body falls. */\nexport const HEADSHOT: EffectDef = {\n id: 'headshot',\n pageId: 'headshot',\n team: 'creatures',\n budget: { particles: 70, debris: 8, decals: 1 },\n loop: 3.2,\n view: { eye: [2.2, 1.8, 3], target: [0, 1.2, -0.5] },\n timeline: [\n after(\n 0.45,\n 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_BLOOD, a: 0.7 }),\n 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_BLOOD, g: 9.8, fi: 0, gnd: 3, ds: [0.06, 0.14] }),\n 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), 0xd9cfbf, 0x6d7a5a), life: 8 })),\n flash([0, 1.7, 0], 0xff3020, 1.2, 0.08, 4),\n ),\n after(1.4, decal(0, 0, -1.5, 0.9, FRESH_BLOOD, { grow: 2.4, dark: 60, dry: 0x3b1d14 })),\n ],\n}\n","import { and, gt, lt, TIME, vnorm } from '../dsl'\nimport { cue, emit, when } from '../steps'\nimport type { EffectDef } from '../types'\n\nconst UP: readonly [number, number, number] = [0, 1, 0]\n\n/** W554-561: green motes while the body twitches (two random draws a frame for the arms), the eyes light up and a breath of steam on the first scream. */\nexport const REANIMATION: EffectDef = {\n id: 'reanimation',\n pageId: 'reanima',\n team: 'creatures',\n budget: { particles: 30 },\n loop: 6.5,\n view: { eye: [2.4, 1.4, 2.4], target: [0, 0.6, -0.4] },\n timeline: [],\n tick: [\n when(lt(TIME, 3.4), [\n when(gt(TIME, 1), [cue('zombie_arm_twitch', undefined, 2)]),\n when(gt(TIME, 0.6), [\n emit('mote', 'add', 8, { p: [0, 0.2, -0.8], box: [0.25, 0.05, 0.6], dir: UP, cone: 0.4, sp: [0.2, 0.5], life: [0.6, 1], size: [0.03, 0.05], c0: 0x9fc25a, a: 0.7, fi: 0.3 }),\n ]),\n ]),\n when(and(gt(TIME, 5), lt(TIME, 5.6)), [\n 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: 0xd8dde2, a: 0.2 }),\n ]),\n ],\n}\n","import { add, cos, div, INDEX, local, lt, mul, rand, sin, sub, TIME, variable, vnorm } from '../dsl'\nimport { after, emit, repeat, ring, screen, set, when } from '../steps'\nimport type { EffectDef } from '../types'\n\nconst WHITE_RING = 0xe8e2d0\nconst PREPARATION_END = 1.7\nconst SHRIEK_END = 2.6\n\n/** W521-539: a steam of breath while the screamer prepares, then three pressure rings, a ring of dust and a burst of chromatic aberration. */\nexport const SCREAMER_SHRIEK: EffectDef = {\n id: 'screamer_shriek',\n pageId: 'grito',\n team: 'creatures',\n budget: { particles: 150 },\n loop: 5,\n view: { eye: [6, 3, 7], target: [0, 1, -1] },\n timeline: [\n after(\n PREPARATION_END,\n repeat(3, after(add(PREPARATION_END, mul(INDEX, 0.18)), ring([0, 0.03, 0], 0.3, 9, 0.9, WHITE_RING))),\n screen({ add: { chroma: 7, shake: 0.25 } }),\n ),\n ],\n tick: [\n when(lt(TIME, PREPARATION_END), [\n 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: 0xd8dde2, a: 0.2 }),\n ], [\n when(lt(TIME, SHRIEK_END), [\n set('shriekRadius', mul(div(sub(TIME, PREPARATION_END), 0.9), 8)),\n emit(\n 'dust', 'normal', 140,\n local('angle', mul(rand(), 6.283), {\n p: [mul(cos(variable('angle')), variable('shriekRadius')), 0.05, mul(sin(variable('angle')), variable('shriekRadius'))],\n dir: vnorm([cos(variable('angle')), 0.6, sin(variable('angle'))]),\n cone: 0.3, sp: [0.6, 1.6], drag: 2, life: [0.8, 1.4], size: [0.2, 0.35], grow: 3, c0: 0x5d574d, a: 0.35,\n }),\n ),\n ]),\n ]),\n ],\n}\n","import type { Vec3Tuple } from '../workshop/types'\n\n/**\n * A point of a prop's own space in the world, for a prop placed at `translation` and turned by `rotation` (x, y, z radians, applied in the order x, y,\n * z) as three.js composes it: how the page's `carW` and `tbW` find the bumper or a table leg.\n */\nexport function localToWorld(translation: Vec3Tuple, rotation: Vec3Tuple, local: Vec3Tuple): Vec3Tuple {\n const [x, y, z] = local\n const [a, c, e] = [Math.cos(rotation[0]), Math.cos(rotation[1]), Math.cos(rotation[2])]\n const [b, d, f] = [Math.sin(rotation[0]), Math.sin(rotation[1]), Math.sin(rotation[2])]\n const [ae, af, be, bf] = [a * e, a * f, b * e, b * f]\n return [\n c * e * x + -c * f * y + d * z + translation[0],\n (af + be * d) * x + (ae - bf * d) * y + -b * c * z + translation[1],\n (bf - ae * d) * x + (be + af * d) * y + a * c * z + translation[2],\n ]\n}\n","import { add, eq, FRESH, INDEX, ite, PARAM, pick, rnd, variable, vadd } from '../dsl'\nimport { localToWorld } from '../props'\nimport { after, behavior, call, camera, decal, repeat, set, when } from '../steps'\nimport type { EffectDef } from '../types'\n\nconst MATERIALS = ['concrete', 'metal', 'wood', 'glass', 'flesh', 'water']\nconst CAR_ORIGIN = [5.6, 0, 0.8] as const\nconst CAR_ROTATION = [0, -0.4, 0] as const\nconst FACING_WALL = [0, 0, 1] as const\nconst UP = [0, 1, 0] as const\nconst GLASS = 3\nconst FLESH = 4\nconst WATER = 5\nconst SHOT_TIMES = [0, 0.55, 1.1]\nconst LAST_SHOT = 2\nconst BUMPER = localToWorld(CAR_ORIGIN, CAR_ROTATION, [0.96, 0.7, 0.4])\nconst CAR_SIDE = [Math.cos(CAR_ROTATION[1]), 0, -Math.sin(CAR_ROTATION[1])] as const\n/** Where each material is hit and the way the surface faces (W752-756). */\nconst 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]]\nconst NORMALS = [FACING_WALL, CAR_SIDE, FACING_WALL, FACING_WALL, FACING_WALL, UP]\n/** The eye the page glides to for each material on the first run (W773-774); the car's is found from the car. */\nconst 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]]\n/** The shop window as the glass texture sees it (`crackAt` takes pixels): 2.6 x 1.8 m on 256 x 180. */\nconst WINDOW = { centreX: -5, centreY: 1.9, width: 2.6, height: 1.8, pixelsX: 256, pixelsY: 180 }\n\nconst 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]))\nconst hitX = { $: 'vx', a: [variable('hit')] }\nconst hitY = { $: 'vy', a: [variable('hit')] }\nconst pixelX = { $: 'mul', a: [{ $: 'add', a: [{ $: 'div', a: [{ $: 'sub', a: [hitX, WINDOW.centreX] }, WINDOW.width] }, 0.5] }, WINDOW.pixelsX] }\nconst pixelY = { $: 'mul', a: [{ $: 'sub', a: [1, { $: 'add', a: [{ $: 'div', a: [{ $: 'sub', a: [hitY, WINDOW.centreY] }, WINDOW.height] }, 0.5] }] }, WINDOW.pixelsY] }\n\n/** W762-775: three hits, 0.55 s apart, on the material the parameter names; the window keeps its cracks and the puddle takes a dark patch. */\nexport const IMPACT_BY_MATERIAL: EffectDef = {\n id: 'impact',\n pageId: 'impacto',\n team: 'weapons',\n params: { id: 'material', options: MATERIALS, default: 0 },\n budget: { particles: 40, decals: 1 },\n loop: 2.6,\n view: { eye: [0, 1.8, 4.5], target: [0, 1.2, -2] },\n props: ['cracks'],\n timeline: [\n when({ $: 'eq', a: [PARAM, WATER] }, [decal(-1.5, 0, 2.2, 3.2, 0x0d1218, { grow: 0, life: 60, ar: 0.55 })]),\n repeat(\n SHOT_TIMES.length,\n after(\n add(0.2, pick(SHOT_TIMES, INDEX)),\n set('hit', vadd(pick(POINTS, PARAM), scatter)),\n call('impact', pick(MATERIALS, PARAM), variable('hit'), pick(NORMALS, PARAM), 1, 0),\n when(eq(PARAM, GLASS), [behavior('cracks', 'crack', { x: pixelX, y: pixelY, big: eq(INDEX, LAST_SHOT) })]),\n ),\n ),\n when(FRESH, [camera(pick(EYES, PARAM), pick(POINTS, PARAM))]),\n ],\n}\n","import { and, DELTA, gt, INDEX, le, lt, mod, mul, add, pick, rnd, sub, TIME, variable } from '../dsl'\nimport { after, append, call, cue, decal, repeat, set, spawn, when } from '../steps'\nimport type { EffectDef } from '../types'\n\nconst BLOOD = [0x8a0a0a, 0x5a0c10, 0x3a1c12, 0x1d1914]\n/** Where the five bodies stand along the line of the shot (W786). */\nconst BODY_Z = [2.2, 1, -0.2, -1.4, -2.6]\nconst MUZZLE_Z = 8.5\nconst SPEED = 12\nconst WALL_Z = -4.4\nconst FORWARD = [0, 0, -1] as const\nconst bulletAt = [0, 1.45, variable('bulletZ')]\n\n/** W776-785: a tracer of the Barrett crosses five bodies, each bleeding the colour of its age, then hits the wall. */\nexport const PENETRATION: EffectDef = {\n id: 'penetration',\n pageId: 'penetra',\n team: 'weapons',\n budget: { particles: 250 },\n loop: 4.2,\n view: { eye: [4.2, 2.2, 6.5], target: [0, 1.2, -0.5] },\n setup: [\n cue('zombie_visible', { index: -1, visible: false }),\n repeat(\n BODY_Z.length,\n set('bodyX', rnd(-0.05, 0.05)),\n set('bodyTurn', rnd(-0.3, 0.3)),\n append('bodiesX', variable('bodyX')),\n cue('zombie_place', { index: INDEX, x: variable('bodyX'), z: pick(BODY_Z, INDEX), turn: variable('bodyTurn') }),\n ),\n ],\n timeline: [set('bulletZ', MUZZLE_Z), set('bulletOn', true), call('muzzle', 'barrett', [0, 1.45, MUZZLE_Z], FORWARD)],\n tick: [\n when(variable('bulletOn'), [\n set('previousZ', variable('bulletZ')),\n set('bulletZ', sub(variable('bulletZ'), mul(SPEED, DELTA))),\n spawn('streak', { p: bulletAt, dir: FORWARD, cone: 0, sp: 12, life: 0.04, len: 0.07, c0: 0xffd090 }),\n spawn('add', { p: bulletAt, size: 0.09, life: 0.05, c0: 0xffc070, fi: 0 }),\n spawn('normal', { p: bulletAt, r: 0.02, sp: [0, 0.05], life: [1.2, 2], size: 0.05, grow: 3, c0: 0xc8ccd2, a: 0.12, fi: 0.1 }),\n repeat(\n BODY_Z.length,\n when(and(gt(variable('previousZ'), pick(BODY_Z, INDEX)), le(variable('bulletZ'), pick(BODY_Z, INDEX))), [\n call('blood_hit', [0, 1.45, sub(pick(BODY_Z, INDEX), 0.1)], [0, 0.05, -1], mod(INDEX, 4), 0.8),\n cue('zombie_fall', { index: INDEX, delay: 0.05, duration: 0.6 }),\n after(\n add(TIME, 0.6),\n decal(pick(variable('bodiesX'), INDEX), 0, sub(pick(BODY_Z, INDEX), 1.2), 0.6, pick(BLOOD, mod(INDEX, 4)), { grow: 2, dark: 60, dry: 0x3b1d14 }),\n ),\n ]),\n ),\n when(lt(variable('bulletZ'), WALL_Z), [set('bulletOn', false), call('impact', 'concrete', [0, 1.45, -4.45], [0, 0, 1], 2.2)]),\n ]),\n ],\n}\n","import { add, gt, INDEX, lt, and, mul, TIME } from '../dsl'\nimport { after, call, cue, debris, emit, repeat, when } from '../steps'\nimport type { EffectDef } from '../types'\n\nconst TIP = [-0.8, 1.38, 3.4 - 0.78] as const\nconst PORT = [-0.76, 1.4, 3.32] as const\nconst FORWARD = [0, 0, -1] as const\nconst UP = [0, 1, 0] as const\nconst BURST_SHOTS = 12\n\n/** W787-797: twelve rounds of an AK with their brass, the empty magazine falling at 1.8 s and a thread of smoke from the hot barrel. */\nexport const CASINGS_AND_MAGAZINE: EffectDef = {\n id: 'casings_and_magazine',\n pageId: 'capsulas',\n team: 'weapons',\n budget: { particles: 200, debris: 13 },\n loop: 4.6,\n view: { eye: [0.9, 1.3, 4.6], target: [-0.6, 0.6, 3.1] },\n setup: [cue('gun_visible', { visible: true }), cue('zombie_visible', { index: -1, visible: false })],\n timeline: [\n repeat(\n BURST_SHOTS,\n after(add(0.2, mul(INDEX, 0.1)), call('muzzle', 'ak', TIP, FORWARD), call('casing', PORT, ''), cue('gun_recoil', { kick: 0.05 })),\n ),\n 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: 0x2a2a2a, spin: 3, life: 20, bounce: 0.2 })),\n ],\n tick: [\n when(and(gt(TIME, 1.4), lt(TIME, 3.6)), [\n emit('hot', 'normal', 10, { p: TIP, dir: UP, cone: 0.25, sp: [0.2, 0.5], life: [1.4, 2.2], size: [0.03, 0.06], grow: 5, c0: 0x9a9a96, a: 0.18, g: -0.2, fi: 0.2 }),\n ]),\n ],\n}\n","import { add, and, div, DELTA, FRESH, ge, gt, ite, lerp, lt, max, min, mul, not, scaled, sin, sub, TIME, variable, vadd, vlerp, vwith } from '../dsl'\nimport { call, cue, debris, burst, decal, emit, fireLight, fireLightOff, flash, repeat, set, when } from '../steps'\nimport type { EffectDef } from '../types'\n\nconst FROM = [-4, 1.6, 5] as const\nconst TARGET = [0, 0, -0.5] as const\nconst UP = [0, 1, 0] as const\nconst FLIGHT = 1\nconst ARC_HEIGHT = 2.2\nconst GLASS_GREEN = 0x3a6a3a\nconst OBJECT_ID_DRAWS = 12\nconst at = (offset: readonly [number, number, number]) => vadd(TARGET, offset)\n\n/** W798-818 (`lab: 1`): a bottle in an arc with a wick burning, glass and a splash of fire on landing, and a pool of fire that spreads for 6 s. */\nexport const MOLOTOV: EffectDef = {\n id: 'molotov',\n pageId: 'molotov',\n team: 'weapons',\n lab: 'molotov',\n budget: { particles: 300, lights: 1 },\n loop: 9.5,\n view: { eye: [5, 3, 6.5], target: [0, 0.6, -0.5] },\n // 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\n setup: [cue('zombie_visible', { index: -1, visible: false }), when(FRESH, [cue('prop_created', { prop: 'bottle' }, OBJECT_ID_DRAWS)])],\n timeline: [set('landed', false), cue('bottle_visible', { visible: true })],\n tick: [\n when(lt(TIME, FLIGHT), [\n set('bottle', vwith(vlerp(FROM, TARGET, TIME), 1, add(lerp(FROM[1], TARGET[1], TIME), mul(sin(mul(TIME, Math.PI)), ARC_HEIGHT)))),\n cue('bottle_move', { position: variable('bottle'), spin: mul(10, DELTA) }),\n 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: 0xffd27a, c1: 0xff5010, fi: 0, g: -1 }),\n emit('wsm', 'normal', 14, { p: variable('bottle'), size: [0.06, 0.1], grow: 4, life: [0.8, 1.2], c0: 0x3a3530, a: 0.3, sp: [0, 0.2] }),\n ], [\n when(not(variable('landed')), [\n set('landed', true),\n cue('bottle_visible', { visible: false }),\n repeat(scaled(12), debris({ p: at([0, 0.1, 0]), dir: UP, cone: 1.3, sp: [1, 3], size: [0.04, 0.01, 0.04], c: GLASS_GREEN, life: 12 })),\n burst('add', 45, { p: at([0, 0.15, 0]), dir: UP, cone: 1.35, sp: [2, 4], drag: 2, life: [0.3, 0.6], size: [0.3, 0.5], c0: 0xffd27a, c1: 0xa02008, fi: 0.05 }),\n flash(at([0, 0.6, 0]), 0xff8a30, 6, 0.3, 12, true),\n decal(TARGET[0], 0, TARGET[2], 4.2, 0x0d0b09, { grow: 0.8, life: 60 }),\n ]),\n ]),\n when(and(gt(TIME, FLIGHT), lt(TIME, 8.2)), [\n set('poolRadius', min(2.2, add(0.5, mul(sub(TIME, 1), 2.1)))),\n set('poolFade', ite(gt(TIME, 7), max(0.1, sub(1, div(sub(TIME, 7), 1.2))), 1)),\n call('fire_emit', 'mol', at([0, 0.05, 0]), variable('poolRadius'), mul(1.6, variable('poolFade')), { smoke: 16 }),\n fireLight(0, at([0, 1, 0]), mul(3.2, variable('poolFade')), TIME),\n ], [\n when(ge(TIME, 8.2), [\n fireLightOff(0),\n emit('emb', 'add', 6, { p: TARGET, r: 2, flat: true, size: [0.04, 0.07], life: [0.6, 1.2], c0: 0xff6a20, a: 0.8, fi: 0.3 }),\n ]),\n ]),\n ],\n}\n","import { add, atan2, DELTA, FRESH, INDEX, lt, mul, neg, not, pick, sub, TIME, variable } from '../dsl'\nimport { call, cue, repeat, set, when } from '../steps'\nimport type { EffectDef } from '../types'\n\n/** The five bodies the blast reaches (W826), where they stand and the way each faces (`atan2(-x, -z)`, toward the origin). */\nconst BODIES = [[1.6, 0.8], [-1.8, 1.2], [0.6, -2], [-1, -1.8], [2.4, -1.2]] as const\nconst BODY_X = BODIES.map(([x]) => x)\nconst BODY_Z = BODIES.map(([, z]) => z)\nconst FACING = BODIES.map(([x, z]) => Math.atan2(-x, -z))\nconst FUSE = 1.8\nconst GRAVITY = 9.8\nconst FLOOR = 0.07\nconst BOUNCE = -0.4\nconst SIDE_LOSS = 0.5\n\n/** W819-830 (`lab: 1`): a grenade thrown in an arc, bouncing on the asphalt, then the Workshop's blast and the five bodies turning away to fall. */\nexport const GRENADE: EffectDef = {\n id: 'grenade',\n pageId: 'granada',\n team: 'weapons',\n lab: 'm67',\n budget: { particles: 400, debris: 16, decals: 1 },\n loop: 6.5,\n view: { eye: [7, 3.4, 7.5], target: [0, 0.8, -0.2] },\n setup: [\n cue('zombie_visible', { index: -1, visible: false }),\n // three.js draws 12 random numbers (the object ids of the grenade's geometry, material and mesh) the first time the page builds it\n when(FRESH, [cue('prop_created', { prop: 'grenade' }, 12)]),\n repeat(BODIES.length, cue('zombie_place', { index: INDEX, x: pick(BODY_X, INDEX), z: pick(BODY_Z, INDEX), turn: pick(FACING, INDEX) })),\n ],\n timeline: [\n set('px', -4), set('py', 1.4), set('pz', 4.5),\n set('vx', 3.2), set('vy', 2.4), set('vz', -3.6),\n set('boom', false),\n cue('grenade_visible', { visible: true }),\n ],\n tick: [\n when(lt(TIME, FUSE), [\n set('vy', sub(variable('vy'), mul(GRAVITY, DELTA))),\n set('px', add(variable('px'), mul(variable('vx'), DELTA))),\n set('py', add(variable('py'), mul(variable('vy'), DELTA))),\n set('pz', add(variable('pz'), mul(variable('vz'), DELTA))),\n when(lt(variable('py'), FLOOR), [\n set('py', FLOOR),\n set('vy', mul(variable('vy'), BOUNCE)),\n set('vx', mul(variable('vx'), SIDE_LOSS)),\n set('vz', mul(variable('vz'), SIDE_LOSS)),\n ]),\n cue('grenade_move', { position: [variable('px'), variable('py'), variable('pz')] }),\n ], [\n when(not(variable('boom')), [\n set('boom', true),\n cue('grenade_visible', { visible: false }),\n call('explode', [variable('px'), 0, variable('pz')], 1),\n repeat(\n BODIES.length,\n cue('zombie_turn_and_fall', {\n index: INDEX,\n turn: atan2(neg(sub(pick(BODY_X, INDEX), variable('px'))), neg(sub(pick(BODY_Z, INDEX), variable('pz')))),\n delay: 0.02,\n duration: 0.45,\n }),\n ),\n ]),\n ]),\n ],\n}\n","import { add, and, cos, gt, ge, INDEX, ite, lt, mod, mul, rnd, scaled, sin, sub, TIME, variable, vadd, concat } from '../dsl'\nimport { after, append, burst, call, cue, debris, emit, fireLight, fireLightOff, repeat, set, when } from '../steps'\nimport type { EffectDef } from '../types'\n\nconst BARREL = [-4.4, 0, 1.4] as const\nconst UP = [0, 1, 0] as const\nconst RED = 0x9b1c18\nconst YELLOW = 0xd8b02a\nconst POOLS = 3\nconst above = (height: number) => vadd(BARREL, [0, height, 0])\n\n/** W831-845 (`lab: 1`): the barrel goes up in a mushroom, its lid and red scraps fly, three pools of petrol burn for 5 s and a black column marks it from afar. */\nexport const EXPLOSIVE_BARREL: EffectDef = {\n id: 'explosive_barrel',\n pageId: 'barril',\n team: 'weapons',\n lab: 'barrel',\n budget: { particles: 450, lights: 1 },\n loop: 9,\n view: { eye: [2.4, 3, 7.5], target: [-4.4, 1.6, 1.4] },\n setup: [cue('zombie_visible', { index: -1, visible: false })],\n timeline: [\n set('pools', []),\n after(\n 0.4,\n cue('barrel_visible', { visible: false }),\n call('explode', BARREL, 1.3, RED),\n burst('add', 30, { p: above(0.8), r: 0.4, dir: UP, cone: 0.25, sp: [3, 5], drag: 1.2, life: [1, 1.6], size: [0.9, 1.4], grow: 1.4, c0: 0xffb050, c1: 0x3a0d05, g: -1, fi: 0.05 }),\n 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 }),\n repeat(scaled(8), debris({ p: BARREL, dir: UP, cone: 1, sp: [3, 7], size: [0.25, 0.04, 0.18], c: ite(mod(INDEX, 2), RED, YELLOW), life: 12 })),\n repeat(\n POOLS,\n set('poolAngle', add(mul(INDEX, 2.1), rnd(0, 0.6))),\n append('pools', vadd(BARREL, [mul(cos(variable('poolAngle')), 1.4), 0.05, mul(sin(variable('poolAngle')), 1.4)])),\n ),\n ),\n ],\n tick: [\n when(and(variable('pools'), gt(TIME, 0.5), lt(TIME, 6)), [\n set('poolFade', ite(gt(TIME, 5), sub(1, sub(TIME, 5)), 1)),\n repeat(POOLS, call('fire_emit', concat('bp', INDEX), { $: 'at', a: [variable('pools'), INDEX] }, 0.45, mul(0.6, variable('poolFade')), { smoke: 6 })),\n fireLight(0, above(1), mul(2.6, variable('poolFade')), TIME),\n ]),\n when(and(gt(TIME, 0.6), lt(TIME, 8)), [\n emit('col', 'normal', 6, { p: above(1.5), r: 0.6, dir: UP, 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: 0x1a1817, c1: 0x0c0d10, a: 0.5, fi: 0.2 }),\n ]),\n when(ge(TIME, 6), [fireLightOff(0)]),\n ],\n}\n","import { add, and, eq, INDEX, ite, mod, mul, ne, param, PARAM, pick, rnd, round, variable } from '../dsl'\nimport { after, call, repeat, set, spawn, when } from '../steps'\nimport type { EffectDef } from '../types'\n\nconst WEAPONS = ['glock', 'ak', 'r870', 'barrett']\nconst TIP = [-0.8, 1.38, 3.4 - 0.78] as const\nconst PORT = [-0.76, 1.4, 3.32] as const\nconst FORWARD = [0, 0, -1] as const\nconst NORMAL = [0, 0, 1] as const\nconst WALL_Z = -4.45\nconst SHOTS = [[0, 0.45, 0.9], [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6], [0], [0]]\nconst SHOT_TIME = pick(pick(SHOTS, PARAM), INDEX)\nconst AK = 1\nconst SHOTGUN = 2\nconst BARRETT = 3\n\n/** W745-761: core, petals and smoke scaled by weapon (the Barrett also vents sideways and lifts dust), with the shot's casing and hit on the wall. */\nexport const MUZZLE_FLASH: EffectDef = {\n id: 'muzzle_flash',\n pageId: 'muzzle',\n team: 'weapons',\n params: { id: 'weapon', options: ['glock', 'ak', 'r870', 'barrett'], default: 1 },\n budget: { particles: 60, lights: 1 },\n loop: 2.4,\n view: { eye: [1.8, 1.8, 5.2], target: [-0.8, 1.3, 1.4] },\n timeline: [\n repeat(\n param(3, 7, 1, 1),\n after(\n add(0.2, SHOT_TIME),\n call('muzzle', pick(WEAPONS, PARAM), TIP, FORWARD),\n when(ne(PARAM, SHOTGUN), [call('casing', PORT, ite(eq(PARAM, BARRETT), '50', ''))]),\n set('target', [add(-0.8, rnd(-0.4, 0.4)), add(1.4, rnd(-0.3, 0.3)), WALL_Z]),\n when(\n eq(PARAM, SHOTGUN),\n [repeat(8, call('impact', 'concrete', [add(-0.8, rnd(-0.5, 0.5)), add(1.4, rnd(-0.4, 0.4)), WALL_Z], NORMAL, 0.35))],\n [call('impact', 'concrete', variable('target'), NORMAL, ite(eq(PARAM, BARRETT), 2, 1))],\n ),\n when(and(eq(PARAM, AK), eq(mod(round(mul(SHOT_TIME, 10)), 3), 0)), [spawn('streak', { p: TIP, dir: FORWARD, cone: 0, sp: 90, life: 0.09, len: 0.025, c0: 0xffa040, a: 1 })]),\n ),\n ),\n when(eq(PARAM, SHOTGUN), [after(0.75, call('casing', PORT, 'shell'))]),\n ],\n}\n","import { rnd, scaled } from '../dsl'\nimport { after, behavior, burst, call, cue, debris, repeat, screen } from '../steps'\nimport type { EffectDef } from '../types'\n\nconst FACING = [0, 0, 1] as const\n\n/** W846-855 (`vidro`): two cracks open in the shop window, then the whole pane comes down as 60 to 80 shards that stay on the pavement. */\nexport const SHOPFRONT_GLASS: EffectDef = {\n id: 'shopfront_glass',\n pageId: 'vidro',\n team: 'destruction',\n budget: { debris: 80, decals: 1 },\n loop: 5,\n view: { eye: [-2.4, 1.8, 1.2], target: [-5, 1.4, -4] },\n timeline: [\n after(0.3, call('impact', 'glass', [-5.4, 2.2, -4.45], FACING), behavior('cracks', 'crack', { x: 97, y: 59, big: false })),\n after(1, call('impact', 'glass', [-4.5, 1.6, -4.45], FACING), behavior('cracks', 'crack', { x: 177, y: 108, big: true })),\n after(\n 1.8,\n cue('glass_pane_gone'),\n screen({ add: { shake: 0.08 } }),\n repeat(\n scaled(75),\n 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), 0.006, rnd(0.05, 0.18)], c: 0xa8c8d8, spin: 14, life: 30, bounce: 0.15 }),\n ),\n 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: 0xffffff, a: 0.9, fi: 0.3 }),\n 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: 0xbfd6e2, a: 0.2 }),\n ),\n ],\n}\n","import type { Vec3Tuple } from '../workshop/types'\n\n/** Vector helpers written as three.js r128 does them, so a recipe's numbers match the page's. */\nexport function normalized(vector: Vec3Tuple): Vec3Tuple {\n const length = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1] + vector[2] * vector[2]) || 1\n const inverse = 1 / length\n return [vector[0] * inverse, vector[1] * inverse, vector[2] * inverse]\n}\n\nexport function plus(vector: Vec3Tuple, x: number, y: number, z: number): Vec3Tuple {\n return [vector[0] + x, vector[1] + y, vector[2] + z]\n}\n\nexport function scaledBy(vector: Vec3Tuple, factor: number): Vec3Tuple {\n return [vector[0] * factor, vector[1] * factor, vector[2] * factor]\n}\n\nexport function plusScaled(vector: Vec3Tuple, direction: Vec3Tuple, factor: number): Vec3Tuple {\n return [vector[0] + direction[0] * factor, vector[1] + direction[1] * factor, vector[2] + direction[2] * factor]\n}\n\nexport function negated(vector: Vec3Tuple): Vec3Tuple {\n return [-vector[0], -vector[1], -vector[2]]\n}\n","import { add, and, div, DELTA, ge, INDEX, min, mul, not, rnd, scaled, sub, variable } from '../dsl'\nimport { after, behavior, burst, cue, debris, repeat, screen, set, when } from '../steps'\nimport { normalized } from '../vector-math'\nimport type { EffectDef, EffectStep } from '../types'\n\nconst FRAME_DUST = normalized([0, -0.3, 1])\nconst SPLINTER_DIRECTION = normalized([0, 0.3, 1])\nconst FALL_SECONDS = 0.4\n\n/** One blow on the door (`hit`, W779): the frame sheds dust and splinters; a heavy blow sheds twice as much. */\nconst blow = (heavy: boolean): EffectStep[] => [\n cue('door_shake', { strength: 0.25 }),\n 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: 0x7a7366, a: 0.45, g: 0.4 }),\n 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: 0x7a5530, life: 12 })),\n]\n\n/** W856-871 (`porta`): blows loosen dust from the frame, a plank cracks at 66 %, falls at 33 % and the door drops at the third stage. */\nexport const DOOR_STAGES: EffectDef = {\n id: 'door_stages',\n pageId: 'porta',\n team: 'destruction',\n budget: { particles: 120, debris: 6 },\n loop: 7.5,\n view: { eye: [4.2, 1.7, 1.4], target: [2, 1.1, -4] },\n setup: [behavior('walkers', 'place', { index: 0, x: 2, z: -5.4, facing: 0 })],\n timeline: [\n after(0.5, ...blow(false)),\n after(1.2, ...blow(false)),\n after(1.9, ...blow(true), cue('door_plank_crack', { stage: 1 })),\n after(2.7, ...blow(false)),\n after(\n 3.3,\n ...blow(true),\n cue('door_plank_fall', { stage: 2 }),\n 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: 0x5a3c22, spin: 4, life: 20, bounce: 0.15 })),\n ),\n after(4.4, set('fall', 0), set('falling', true), cue('door_fall', { stage: 3 })),\n after(4.6, behavior('walkers', 'walk', { index: 0, to: [2, 0, -2.2], speed: 0.9 })),\n ],\n tick: [\n when(variable('falling'), [\n set('fall', add(variable('fall'), DELTA)),\n set('progress', min(1, div(variable('fall'), FALL_SECONDS))),\n when(and(ge(variable('progress'), 1), not(variable('landed'))), [\n set('landed', true),\n screen({ add: { shake: 0.2 } }),\n 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: 0x6a645a, a: 0.45 }),\n ]),\n ]),\n ],\n}\n","import { abs, add, and, cos, DELTA, gt, local, lt, mul, rand, rnd, scaled, sin, TIME, variable, vnorm } from '../dsl'\nimport { after, cue, debris, emit, repeat, screen, spawn, when } from '../steps'\nimport type { EffectDef } from '../types'\n\nconst CENTRE = [-1.4, 3.35, -3.75] as const\n\n/** W872-886 (`desaba`): dust trickles from the awning for a second as a warning, then the slab drops in blocks and a cloud rolls along the street. */\nexport const BUILDING_COLLAPSE: EffectDef = {\n id: 'building_collapse',\n pageId: 'desaba',\n team: 'destruction',\n budget: { particles: 350, debris: 26 },\n loop: 8,\n view: { eye: [4, 2.4, 5], target: [-1.4, 1.4, -2.8] },\n setup: [cue('zombie_visible', { index: -1, visible: false })],\n timeline: [\n after(\n 1.5,\n cue('slab_gone'),\n screen({ add: { shake: 0.3 } }),\n repeat(6, debris({ p: CENTRE, 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: 0x6e6a63, spin: 3, life: 30, bounce: 0.1 })),\n repeat(scaled(20), debris({ p: CENTRE, 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: 0x5e5a54, life: 30 })),\n ),\n after(\n 2.15,\n screen({ add: { shake: 0.45 } }),\n repeat(\n scaled(70),\n spawn('normal', local('angle', rnd(-0.4, 3.5), {\n p: [add(-1.4, rnd(-2, 2)), 0.2, add(-3.4, rnd(-0.5, 0.5))],\n dir: vnorm([mul(cos(variable('angle')), 0.6), 0.25, abs(sin(variable('angle')))]),\n cone: 0.5, sp: [1, 3.2], drag: 1.1, life: [3, 6], size: [0.7, 1.2], grow: 3, c0: 0x7a7268, c1: 0x4a4640, a: 0.5, fi: 0.12,\n })),\n ),\n ),\n ],\n tick: [\n when(and(gt(TIME, 0.3), lt(TIME, 1.5)), [\n 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: 0x8a8478, a: 0.5, g: 1 }),\n 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: 0x6e6a63, life: 10 })]),\n ]),\n ],\n}\n","import { add, cos, INDEX, local, mul, rnd, scaled, sin, variable, vnorm } from '../dsl'\nimport { after, behavior, burst, call, cue, debris, repeat, screen, spawn } from '../steps'\nimport { normalized } from '../vector-math'\nimport type { EffectDef } from '../types'\n\nconst FIRING_FROM = [0.6, 1.3, 7] as const\nconst FIRING_DIRECTION = normalized([-2.8, -0.4, -4.4])\nconst WALL_CENTRE = [-2.2, 1, 2.6] as const\n\n/** W1039-1053 (`parede`): a burst of AK fire leaves holes that are stuck to the blocks, then the wall gives way and the holes fall with them. */\nexport const BLOCK_WALL: EffectDef = {\n id: 'block_wall',\n pageId: 'parede',\n team: 'destruction',\n budget: { particles: 300, debris: 16 },\n loop: 8,\n view: { eye: [0.4, 1.7, 6.6], target: [-2.2, 0.9, 2.4] },\n props: ['blocks'],\n setup: [cue('zombie_visible', { index: -1, visible: false }), cue('work_light', { intensity: 2.2 })],\n timeline: [\n repeat(\n 14,\n after(\n add(0.3, mul(INDEX, 0.11)),\n call('muzzle', 'ak', FIRING_FROM, FIRING_DIRECTION),\n behavior('blocks', 'hit', { x: rnd(-1.3, 1.3), y: rnd(0.4, 1.9), scale: 1 }),\n ),\n ),\n after(2.2, behavior('blocks', 'hit', { x: -0.2, y: 1.1, scale: 2.2 })),\n after(\n 3,\n screen({ add: { shake: 0.4 } }),\n behavior('blocks', 'collapse', { x: -0.2, y: 1.1 }),\n 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: 0x8a857a, c1: 0x4a4640, a: 0.45 }),\n 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: 0x6e6a63, life: 20 })),\n ),\n after(\n 3.6,\n screen({ add: { shake: 0.25 } }),\n repeat(\n scaled(60),\n spawn('normal', local('angle', rnd(0, 6.283), {\n p: [add(-2.2, rnd(-1.4, 1.4)), 0.2, add(2.4, rnd(-0.6, 0.4))],\n dir: vnorm([cos(variable('angle')), 0.3, sin(variable('angle'))]),\n cone: 0.4, sp: [0.8, 2.4], drag: 1.3, life: [2.5, 4.5], size: [0.6, 1], grow: 3, c0: 0x7a7268, c1: 0x4a4640, a: 0.45, fi: 0.12,\n })),\n ),\n ),\n ],\n}\n","import type { Vec3Tuple } from '../../workshop/types'\n\n/** The free spot of the Workshop's set where the wall, door, railing, fence, table and cylinders are put one at a time (W937, `SPOT`). */\nexport const SPOT: Vec3Tuple = [-2.2, 0, 2.6]\nexport const UP: Vec3Tuple = [0, 1, 0]\n","import { add, and, DELTA, div, floor, ge, INDEX, ite, min, mod, mul, not, pick, scaled, variable } from '../dsl'\nimport { after, behavior, burst, call, cue, debris, repeat, screen, set, when } from '../steps'\nimport { normalized } from '../vector-math'\nimport type { EffectDef } from '../types'\nimport { SPOT, UP } from './spot'\n\nconst HITS = [[0.4, 1.4], [0.75, 1.0], [0.3, 0.7], [0.6, 1.7], [0.5, 1.2]]\nconst HIT_X = HITS.map(([x]) => x)\nconst HIT_Y = HITS.map(([, y]) => y)\nconst SPARK_DIRECTION = normalized([0, 0.3, 1])\nconst CHIP_DIRECTION = normalized([0, 0.2, 1])\nconst HINGE_DIRECTION = normalized([1, 0.3, 1])\nconst SCRAPE_DIRECTION = normalized([0, 0.4, 1])\nconst FALL_SECONDS = 0.45\nconst SCRAPES = 5\n\n/** W960-985 (`portametal`): each blow dents the plate where it lands and throws sparks, paint chips and rust; the top hinge bursts and the door scrapes down. */\nexport const METAL_DOOR_EFFECT: EffectDef = {\n id: 'metal_door',\n pageId: 'portametal',\n team: 'destruction',\n budget: { particles: 160, debris: 20 },\n loop: 7.5,\n view: { eye: [0.6, 1.6, 6.2], target: [-2.2, 1.1, 2.6] },\n props: ['plate'],\n setup: [\n cue('zombie_visible', { index: -1, visible: false }),\n cue('work_light', { intensity: 2.2 }),\n behavior('walkers', 'place', { index: 3, x: SPOT[0] + 0.1, z: SPOT[2] - 0.7, facing: 0 }),\n ],\n timeline: [\n repeat(\n HITS.length,\n after(\n add(0.5, mul(INDEX, 0.75)),\n cue('zombie_swing', { index: 3, side: mod(INDEX, 2) }),\n after(\n add(0.5, mul(INDEX, 0.75), 0.15),\n behavior('plate', 'dent', { x: pick(HIT_X, INDEX), y: pick(HIT_Y, INDEX), radius: 0.42, depth: add(0.09, mul(INDEX, 0.025)) }),\n set('spot', [add(SPOT[0] - 0.56, pick(HIT_X, INDEX)), pick(HIT_Y, INDEX), SPOT[2] + 0.06]),\n call('spark_at', variable('spot'), SPARK_DIRECTION, 8),\n repeat(scaled(5), debris({ p: variable('spot'), dir: CHIP_DIRECTION, cone: 0.9, sp: [0.8, 2.2], size: [0.035, 0.004, 0.03], k: [0.6, 1.4], c: ite(mod(INDEX, 2), 0x51625a, 0x8a8f86), life: 20, spin: 16 })),\n call('rust_at', variable('spot'), 6),\n cue('door_dent', { level: min(3, add(1, floor(div(INDEX, 1.7)))) }),\n ),\n ),\n ),\n after(4.4, call('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')),\n after(5.2, behavior('walkers', 'walk', { index: 3, to: [SPOT[0], 0, SPOT[2] + 2.4], speed: 0.8 })),\n ],\n tick: [\n when(variable('falling'), [\n set('fall', add(variable('fall'), DELTA)),\n set('progress', min(1, div(variable('fall'), FALL_SECONDS))),\n when(and(ge(variable('progress'), 1), not(variable('landed'))), [\n set('landed', true),\n screen({ add: { shake: 0.3 } }),\n repeat(SCRAPES, call('spark_at', [add(SPOT[0] - 0.5, mul(INDEX, 0.27)), 0.03, SPOT[2] + 2.1], SCRAPE_DIRECTION, 8, 1)),\n burst('normal', 40, { p: [SPOT[0], 0.15, SPOT[2] + 1.2], box: [0.6, 0.05, 1], dir: UP, cone: 1.4, sp: [0.6, 2], drag: 1.8, life: [1.4, 2.4], size: [0.25, 0.5], grow: 3, c0: 0x6a645a, a: 0.45 }),\n ]),\n ]),\n ],\n}\n","import { add, and, DELTA, div, ge, gt, INDEX, lt, min, mul, not, pick, rnd, sub, TIME, variable } from '../dsl'\nimport { after, behavior, burst, call, cue, emit, repeat, screen, set, when } from '../steps'\nimport { normalized } from '../vector-math'\nimport type { EffectDef } from '../types'\nimport { SPOT, UP } from './spot'\n\n/** Where the four pushing bodies stand, relative to the railing (W987). */\nconst PUSHERS = [[-0.6, 0.45], [0.1, 0.5], [0.6, 0.42], [-0.1, 1]]\nconst BEND_LIMIT = 0.42\nconst BEND_STEP = 0.004\nconst FALL_SECONDS = 0.55\nconst WELD = normalized([0, 0.3, 1])\n\n/** W986-1003 (`grade`): the horde pushes, the bars bend and rust falls in a thread; the weld bursts in sparks twice and the railing topples. */\nexport const IRON_RAILING: EffectDef = {\n id: 'iron_railing',\n pageId: 'grade',\n team: 'destruction',\n budget: { particles: 200 },\n loop: 7.5,\n view: { eye: [1.4, 1.8, 6.4], target: [-2.2, 0.7, 2.4] },\n setup: [\n cue('zombie_visible', { index: -1, visible: false }),\n cue('work_light', { intensity: 2.2 }),\n set('bend', 0),\n ...PUSHERS.map(([x, dz], index) => behavior('walkers', 'place', { index, x: SPOT[0] + x!, z: SPOT[2] - dz!, facing: 0 })),\n ],\n timeline: [\n after(1.6, call('spark_at', [SPOT[0], 0.75, SPOT[2] + 0.05], WELD, 20, 1), screen({ add: { shake: 0.08 } }), cue('weld_burst')),\n after(3.2, call('spark_at', [SPOT[0] + 0.02, 1.2, SPOT[2] + 0.1], WELD, 20, 1), screen({ add: { shake: 0.1 } })),\n after(\n 4.3,\n set('fall', 0),\n set('falling', true),\n call('spark_at', [SPOT[0] - 1.5, 0.25, SPOT[2]], normalized([-0.3, 0.5, 1]), 14, 1),\n call('spark_at', [SPOT[0] + 1.5, 0.25, SPOT[2]], normalized([0.3, 0.5, 1]), 14, 1),\n cue('railing_gave_way'),\n ),\n after(\n 4.9,\n 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 })),\n ),\n ],\n tick: [\n when(lt(TIME, 4.3), [\n set('target', min(BEND_LIMIT, mul(TIME, 0.1))),\n when(gt(sub(variable('target'), variable('bend')), BEND_STEP), [set('bend', variable('target')), cue('railing_bend', { amount: variable('bend') })]),\n 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: 0x7a4a2a, a: 0.9, g: 6, fi: 0, gnd: 1 }),\n ]),\n when(variable('falling'), [\n set('fall', add(variable('fall'), DELTA)),\n set('progress', min(1, div(variable('fall'), FALL_SECONDS))),\n when(and(ge(variable('progress'), 1), not(variable('landed'))), [\n set('landed', true),\n screen({ add: { shake: 0.25 } }),\n repeat(4, call('spark_at', [add(SPOT[0] - 1.2, mul(INDEX, 0.8)), 0.04, SPOT[2] + 1.2], normalized([0, 0.5, 1]), 6, 1)),\n burst('normal', 30, { p: [SPOT[0], 0.12, SPOT[2] + 0.8], box: [1.4, 0.05, 0.5], dir: UP, cone: 1.4, sp: [0.5, 1.6], drag: 1.8, life: [1.2, 2.2], size: [0.2, 0.4], grow: 3, c0: 0x6a645a, a: 0.4 }),\n ]),\n ]),\n ],\n}\n","import { add, and, DELTA, div, ge, INDEX, min, mul, not, pick, rnd, scaled, sub, variable, vadd, mod } from '../dsl'\nimport { after, behavior, burst, cue, debris, repeat, screen, set, when } from '../steps'\nimport { normalized } from '../vector-math'\nimport type { EffectDef } from '../types'\nimport { SPOT, UP } from './spot'\n\n/** The order the planks break in and where each stands along the fence (W1005-1006). */\nconst ORDER = [3, 4, 8, 5, 9, 2, 7]\nconst PLANK_X = Array.from({ length: 12 }, (_, index) => -1.43 + index * 0.26)\nconst BODIES = [[-0.8, 0.6], [0.5, 0.7], [0, 1.2]]\nconst SPLINTER_DIRECTION = normalized([0, 0.4, 1])\nconst FALL_SECONDS = 0.5\nconst PLANK_HEIGHT = 1.3\n\n/** W1004-1020 (`cerca`): the planks snap one by one at a random height, the stump stays on the rail and the rest flies; the section finally tips over. */\nexport const WOODEN_FENCE: EffectDef = {\n id: 'wooden_fence',\n pageId: 'cerca',\n team: 'destruction',\n budget: { particles: 150, debris: 12 },\n loop: 7.5,\n view: { eye: [1, 1.6, 6], target: [-2.2, 0.7, 2.4] },\n setup: [\n cue('zombie_visible', { index: -1, visible: false }),\n cue('work_light', { intensity: 2.2 }),\n ...BODIES.map(([x, dz], index) => behavior('walkers', 'place', { index, x: SPOT[0] + x!, z: SPOT[2] - dz!, facing: 0 })),\n ],\n timeline: [\n repeat(\n ORDER.length,\n after(\n add(0.4, mul(INDEX, 0.5)),\n set('cut', rnd(0.32, 0.6)),\n set('top', sub(PLANK_HEIGHT, variable('cut'))),\n set('break', [add(SPOT[0], pick(PLANK_X, pick(ORDER, INDEX))), variable('cut'), SPOT[2]]),\n cue('zombie_swing', { index: mod(INDEX, 3), side: mod(INDEX, 2) }),\n cue('plank_snap', { plank: pick(ORDER, INDEX), cut: variable('cut') }),\n 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: 0x8a6a44, spin: 7, life: 20, bounce: 0.2 }),\n repeat(scaled(6), debris({ p: variable('break'), dir: SPLINTER_DIRECTION, cone: 0.9, sp: [1, 3], size: [0.012, 0.012, 0.1], k: [0.6, 1.4], c: 0xb08a5a, life: 20 })),\n 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: 0x7a6048, a: 0.4 }),\n ),\n ),\n after(\n 4.2,\n set('fall', 0),\n set('falling', true),\n repeat(scaled(10), debris({ p: [add(SPOT[0], rnd(-1.4, 1.4)), 1, SPOT[2]], dir: SPLINTER_DIRECTION, cone: 0.9, sp: [1, 3], size: [0.012, 0.012, 0.1], c: 0xb08a5a, life: 20 })),\n cue('fence_rail_cracked'),\n ),\n after(4.8, repeat(BODIES.length, behavior('walkers', 'walk', { index: INDEX, to: [add(SPOT[0], pick(BODIES.map(([x]) => x), INDEX)), 0, SPOT[2] + 2.5], speed: 0.75 }))),\n ],\n tick: [\n when(variable('falling'), [\n set('fall', add(variable('fall'), DELTA)),\n set('progress', min(1, div(variable('fall'), FALL_SECONDS))),\n when(and(ge(variable('progress'), 1), not(variable('landed'))), [\n set('landed', true),\n screen({ add: { shake: 0.2 } }),\n burst('normal', 35, { p: [SPOT[0], 0.12, SPOT[2] + 0.8], box: [1.5, 0.05, 0.5], dir: UP, cone: 1.4, sp: [0.5, 1.6], drag: 1.8, life: [1.2, 2.2], size: [0.25, 0.45], grow: 3, c0: 0x6a5a48, a: 0.4 }),\n ]),\n ]),\n ],\n}\n","import { add, INDEX, mul, rnd, variable, vadd } from '../dsl'\nimport { after, call, cue, debris, burst, repeat, screen, set } from '../steps'\nimport { normalized } from '../vector-math'\nimport type { EffectDef } from '../types'\nimport { SPOT, UP } from './spot'\n\nconst at = (x: number, y: number, z: number): readonly [number, number, number] => [SPOT[0] + x, SPOT[1] + y, SPOT[2] + z]\n/**\n * Where each part of the table is in the world. The page asks its scene graph for it (`tbW`), and the capture does not render, so the table's tilt never\n * reaches the world matrix it reads: the parts are always where the table stands level (W1009, recorded at 2.2 s, 3.8 s and 5.3 s).\n */\nconst FRONT_LEG = at(-0.52, 0.2, 0.32)\nconst TOP_CORNER = at(0.3, 0.78, 0.2)\nconst TOP_PIECE = at(-0.3, 0.78, -0.2)\nconst FAR_CORNER = at(0.3, 0.78, -0.2)\nconst HOLE_OBJECT_ID_DRAWS = 12\nconst HOLES = 9\nconst GLOCK_FROM = [-0.4, 1, 6] as const\nconst SHOTGUN_FROM = [0.2, 1.1, 4.6] as const\nconst toward = (target: readonly [number, number, number], from: readonly [number, number, number]) => normalized([target[0] - from[0], target[1] - from[1], target[2] - from[2]])\n\n/** W1008-1020 (`pontos`): a 9 mm in the leg, a machete on a corner, a bat on the top and a 12-gauge up close: each breaks only the part it hit. */\nexport const HIT_POINT_BREAKAGE: EffectDef = {\n id: 'hit_point_breakage',\n pageId: 'pontos',\n team: 'destruction',\n budget: { particles: 120, debris: 20 },\n loop: 7.5,\n view: { eye: [0.2, 1.6, 5.4], target: [-2.2, 0.6, 2.6] },\n setup: [cue('zombie_visible', { index: -1, visible: false }), cue('work_light', { intensity: 2.2 })],\n timeline: [\n after(\n 0.6,\n call('muzzle', 'glock', GLOCK_FROM, toward(FRONT_LEG, GLOCK_FROM)),\n call('chips', FRONT_LEG, normalized([0, 0.2, 1]), 6),\n cue('table_part_detached', { part: 'front_leg' }),\n debris({ p: FRONT_LEG, v: [0.4, 0.6, 1.4], sp: 0, size: [0.07, 0.4, 0.07], c: 0x7a5530, spin: 6, life: 20, bounce: 0.25 }),\n cue('table_tilt'),\n ),\n after(\n 2.2,\n repeat(8, call('chips', vadd(TOP_CORNER, [add(-0.3, mul(INDEX, 0.08)), 0.03, 0]), normalized([0, 0.6, 1]), 1, 0.4)),\n cue('table_part_detached', { part: 'top_corner' }),\n debris({ p: TOP_CORNER, v: [1.2, 1.5, 1], sp: 0, size: [0.6, 0.06, 0.4], c: 0x7a5530, spin: 6, life: 20, bounce: 0.25 }),\n ),\n after(\n 3.8,\n screen({ add: { shake: 0.12 } }),\n call('chips', TOP_PIECE, UP, 10, 1.3),\n cue('table_part_gone', { part: 'top_piece' }),\n 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: UP, cone: 1.2, sp: [1, 2.5], size: [rnd(0.15, 0.3), 0.06, rnd(0.12, 0.25)], c: 0x6b4a2a, spin: 8, life: 20, bounce: 0.2 })),\n 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: 0x7a6048, a: 0.4 }),\n ),\n after(\n 5.3,\n call('muzzle', 'r870', SHOTGUN_FROM, toward(FAR_CORNER, SHOTGUN_FROM)),\n 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)),\n call('chips', FAR_CORNER, UP, 10, 1),\n ),\n ],\n}\n","import { abs, clamp, DELTA, div, eq, gt, lt, min, mul, not, PARAM, query, sub, TIME, variable, vx, vz } from '../dsl'\nimport { after, behavior, call, cue, emit, set, screen, when } from '../steps'\nimport { normalized } from '../vector-math'\nimport type { EffectDef } from '../types'\nimport { SPOT, UP } from './spot'\n\nconst OXYGEN = 1\nconst PUNCTURE_AT = 0.6\nconst JET_SECONDS = 3.4\nconst CLOUD_UNTIL = 7\nconst SHOT_FROM = [0.4, 1.2, 6.4] as const\nconst SHOT_DIRECTION = normalized([-0.5, -0.15, -1])\nconst SCRAPE_DIRECTION = normalized([1, 0.4, 0])\nconst jetStrength = variable('jet')\nconst isOxygen = eq(PARAM, OXYGEN)\n\n/** W1021-1038 (`pressao`): a shot into an extinguisher (a white cloud and a spin) or into an oxygen bottle (the valve breaks and it flies off as a rocket). */\nexport const PRESSURE_CYLINDERS: EffectDef = {\n id: 'pressure_cylinders',\n pageId: 'pressao',\n team: 'destruction',\n params: { id: 'cylinder', options: ['extinguisher', 'oxygen'], default: 0 },\n budget: { particles: 250 },\n loop: 7,\n view: { eye: [1.6, 1.5, 6.4], target: [-1.6, 0.4, 2.4] },\n props: ['cylinder'],\n setup: [cue('zombie_visible', { index: -1, visible: false }), cue('work_light', { intensity: 2.2 }), behavior('cylinder', 'choose', { oxygen: isOxygen }), cue('cylinder_visible', { oxygen: isOxygen })],\n timeline: [\n after(\n PUNCTURE_AT,\n call('muzzle', 'glock', SHOT_FROM, SHOT_DIRECTION),\n screen({ add: { shake: 0.1 } }),\n behavior('cylinder', 'puncture'),\n when(isOxygen, [call('spark_at', [SPOT[0], 1.3, SPOT[2]], UP, 20, 1.2)]),\n ),\n ],\n tick: [\n when(query('cylinder', 'live'), [\n set('jet', clamp(sub(1, div(sub(TIME, PUNCTURE_AT), JET_SECONDS)), 0, 1)),\n behavior('cylinder', 'step', { dt: DELTA, t: TIME }),\n when(not(isOxygen), [\n when(gt(jetStrength, 0), [\n 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: 0xeeeeea, c1: 0xbfc3c8, a: 0.45, g: -0.05, fi: 0.05 }),\n ]),\n when(lt(TIME, CLOUD_UNTIL), [\n 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: 0xe2e2de, a: mul(0.18, min(1, sub(TIME, PUNCTURE_AT))), fi: 0.3 }),\n ]),\n ], [\n when(gt(jetStrength, 0), [\n 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: 0xdde6ee, a: 0.2 }),\n ]),\n when(gt(abs(query('cylinder', 'speed_x')), 0.5), [\n emit('scr', 'streak', 120, { p: [vx(query('cylinder', 'position')), 0.03, vz(query('cylinder', 'position'))], dir: SCRAPE_DIRECTION, 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: 0xfff0c0, c1: 0xff5010, g: 9.8, gnd: 2 }),\n ]),\n ]),\n ]),\n ],\n}\n","import { add, concat, eq, INDEX, pick, TIME, vadd } from '../dsl'\nimport { call, cue, fireLight, repeat, when } from '../steps'\nimport { normalized } from '../vector-math'\nimport type { EffectDef } from '../types'\n\n/** The two burning windows of the shop front (W73). */\nconst WINDOWS = [[4.6, 5, -4.48], [-1.6, 5, -4.48]]\n\n/** W1054-1068 (`incendio`): fire out of the top of each window, licking up the facade, embers and smoke carried by the wind, and a light that paints the street. */\nexport const BUILDING_FIRE: EffectDef = {\n id: 'building_fire',\n pageId: 'incendio',\n team: 'world',\n budget: { particles: 600, lights: 2 },\n loop: 0,\n view: { eye: [3, 2.4, 6.5], target: [1.5, 4, -4] },\n setup: [cue('zombie_visible', { index: -1, visible: false })],\n timeline: [],\n tick: [\n repeat(\n WINDOWS.length,\n call('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 }),\n 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))]),\n ),\n ],\n}\n","import { add, DELTA, lt, mul, rand, rnd, sub, TIME, variable, vx, vz } from '../dsl'\nimport { cue, decal, emit, fireLight, set, when } from '../steps'\nimport { normalized } from '../vector-math'\nimport type { EffectDef } from '../types'\n\nconst YAW = -Math.PI / 2\nconst COS = Math.cos(YAW)\nconst SIN = Math.sin(YAW)\nconst START_X = 10\nconst SPEED = 2.4\n/** `carW(0.6, 0.06, 2.15)` for a car turned by `YAW`, as the matrix multiplies it (the y component is replaced by 0.04). */\nconst contactX = (carX: number | object) => add(COS * 0.6, 0 * 0.06, SIN * 2.15, carX)\nconst contactZ = add(-SIN * 0.6, 0 * 0.06, COS * 2.15, 1.5)\nconst BACKWARD = normalized([1, 0.35, 0])\n\n/** W1090-1100 (`faisca`): a bumper dragging on the asphalt throws a river of sparks that bounce and go from white to orange, with a short light and dark scratches. */\nexport const SCRAPING_SPARKS: EffectDef = {\n id: 'scraping_sparks',\n pageId: 'faisca',\n team: 'vehicles',\n budget: { particles: 200, lights: 1 },\n loop: 4.4,\n view: { eye: [2, 1.6, 5.5], target: [3, 0.3, 1.5] },\n setup: [cue('zombie_visible', { index: -1, visible: false }), cue('car_pose', { x: START_X, z: 1.5, yaw: YAW })],\n timeline: [],\n tick: [\n set('carX', sub(START_X, mul(TIME, SPEED))),\n set('contact', [contactX(variable('carX')), 0.04, contactZ]),\n cue('car_pose', { x: variable('carX'), z: 1.5, yaw: YAW }),\n 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: 0xfff0c0, c1: 0xff5010, g: 9.8, gnd: 2 }),\n emit('gl', 'add', 40, { p: variable('contact'), r: 0.05, size: [0.08, 0.16], life: 0.05, c0: 0xffd8a0, fi: 0 }),\n when(lt(rand(), mul(DELTA, 14)), [decal(vx(variable('contact')), 0, add(vz(variable('contact')), rnd(-0.05, 0.05)), 0.12, 0x0a0a0a, { grow: 0, life: 60, ar: 0.25 })]),\n fireLight(0, [vx(variable('contact')), 0.34, vz(variable('contact'))], 1.6, mul(TIME, 3)),\n ],\n}\n","import { localToWorld } from '../props'\nimport type { Vec3Tuple } from '../../workshop/types'\nimport { normalized } from '../vector-math'\n\n/** The car of the Workshop's set as the page leaves it between effects (`CAR0`, W104): at (5.6, 0, 0.8), turned -0.4 rad. */\nexport const CAR_ORIGIN: Vec3Tuple = [5.6, 0, 0.8]\nexport const CAR_ROTATION: Vec3Tuple = [0, -0.4, 0]\n/** A point of the car's own space in the world (`carW`). */\nexport const carPoint = (x: number, y: number, z: number): Vec3Tuple => localToWorld(CAR_ORIGIN, CAR_ROTATION, [x, y, z])\nexport const carDirection = (from: Vec3Tuple, to: Vec3Tuple): Vec3Tuple => normalized([to[0] - from[0], to[1] - from[1], to[2] - from[2]])\n","import { DELTA, eq, gt, ite, lt, mul, PARAM, rand, TIME } from '../dsl'\nimport { burst, call, emit, fireLight, fireLightOff, when } from '../steps'\nimport type { EffectDef } from '../types'\nimport { carDirection, carPoint } from './car'\n\nconst HOOD = carPoint(0, 1.02, 1.4)\nconst EXHAUST = carPoint(0.5, 0.3, -2.2)\nconst EXHAUST_DIRECTION = carDirection(EXHAUST, carPoint(0.5, 0.3, -3))\nconst SPARK_FROM = carPoint(0, 0.7, 2.1)\nconst SPARK_DIRECTION = carDirection(SPARK_FROM, carPoint(0, 0.9, 3))\nconst WORN = 1\nconst BAD = 2\nconst CRITICAL = 3\n\n/** W1101-1112 (`motor`): exhaust only at 100, a thread of white steam at 60, steady grey smoke and the odd spark at 30, thick black smoke with fire at 10. */\nexport const ENGINE_SMOKE: EffectDef = {\n id: 'engine_smoke',\n pageId: 'motor',\n team: 'vehicles',\n params: { id: 'condition', options: ['100', '60', '30', '10'], default: 2 },\n budget: { particles: 120, lights: 1 },\n loop: 0,\n view: { eye: [9.5, 2.6, 5.2], target: [5.2, 0.9, 1.5] },\n timeline: [],\n tick: [\n 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), 0x55524e, 0x9a9a96), a: 0.18, g: -0.2 }),\n when(eq(PARAM, WORN), [\n 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: 0xc8ccd0, a: 0.22, g: -0.3 }),\n ]),\n when(eq(PARAM, BAD), [\n 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: 0x6a6a68, c1: 0x3a3a3c, a: 0.4, g: -0.35 }),\n when(lt(rand(), mul(DELTA, 0.8)), [burst('streak', 8, { p: SPARK_FROM, dir: SPARK_DIRECTION, cone: 0.6, sp: [2, 4], life: 0.3, len: 0.02, c0: 0xffd080, c1: 0xff4000, g: 9.8 })]),\n ]),\n when(eq(PARAM, CRITICAL), [\n 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: 0x1c1b1b, c1: 0x0e0e10, a: 0.6, g: -0.4 }),\n call('fire_emit', 'hood', carPoint(0, 0.8, 2.0), 0.3, 0.45, { noSmoke: true }),\n fireLight(0, carPoint(0, 1.2, 2.6), 2.2, TIME),\n ], [fireLightOff(0)]),\n ],\n}\n","import { rnd, scaled, query, vadd } from '../dsl'\nimport { behavior, call, cue, debris, repeat } from '../steps'\nimport type { EffectStep } from '../types'\nimport { carPoint } from './car'\n\nconst UP = [0, 1, 0] as const\n\n/** The velocity the page gives a door: the car's own space pushed outward by `s * 4`, minus the centre, with the height set to 3 (W1124). */\nconst doorVelocity = (side: number): readonly [number, number, number] => {\n const out = carPoint(side * 4, 3, 0.3)\n const centre = carPoint(0, 0, 0.3)\n return [out[0] - centre[0], 3, out[2] - centre[2]]\n}\n\n/** `vehicleBoom` (W1118-1128): the blast at the car, the car jumping, glass in the air, then the bonnet, both doors and a wheel flying off. */\nexport const VEHICLE_BOOM: readonly EffectStep[] = [\n call('explode', vadd(query('car', 'position'), [0, 0.6, 0]), 1.8, 0x2a1a14),\n cue('car_burnt'),\n behavior('car', 'launch', { rise: 4.5, spin: 0.9 }),\n repeat(scaled(30), debris({ p: vadd(query('car', 'position'), [0, 1.3, 0]), dir: UP, cone: 1.3, sp: [2, 6], size: [0.06, 0.006, 0.06], c: 0xa8c8d8, life: 20 })),\n 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: 0x3a1d18, spin: 6, life: 20, bounce: 0.2 }),\n ...[-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: 0x3a1d18, spin: 5, life: 20, bounce: 0.2 })),\n debris({ p: carPoint(0.95, 0.38, 1.35), v: [3, 4, 2], sp: 0, size: [0.3, 0.76, 0.76], c: 0x111111, spin: 8, life: 20, bounce: 0.4 }),\n]\n","import { add, and, clamp, DELTA, div, ge, gt, lt, mul, query, sub, TIME, variable, vadd, vlerp } from '../dsl'\nimport { after, behavior, burst, call, cue, decal, emit, fireLight, set, when } from '../steps'\nimport type { EffectDef } from '../types'\nimport { VEHICLE_BOOM } from './boom'\nimport { carDirection, carPoint } from './car'\n\nconst TANK = carPoint(-0.6, 0.25, -1.3)\nconst TANK_FLOOR = [TANK[0], 0, TANK[2]] as const\nconst POOL = [TANK_FLOOR[0] - 1.2, 0, TANK_FLOOR[2] - 0.6] as const\nconst BOOM_AT = 7.2\nconst HOLE = carPoint(-0.96, 0.6, -1.2)\n\n/** W1113-1132 (`vazamento`): petrol drips into a pool, a shot into the metal throws a spark, the fire runs along the pool to the tank and the car blows up. */\nexport const FUEL_LEAK: EffectDef = {\n id: 'fuel_leak',\n pageId: 'vazamento',\n team: 'vehicles',\n budget: { particles: 400, lights: 1 },\n loop: 11,\n view: { eye: [10, 2.4, 4.2], target: [5.6, 0.4, 0] },\n setup: [cue('zombie_visible', { index: -1, visible: false })],\n timeline: [\n decal(POOL[0], 0, POOL[2], 2.2, 0x1f2230, { grow: 3, life: 60, ar: 0.6, fluid: 'oil' }),\n after(\n 3.4,\n call('impact', 'metal', HOLE, carDirection(HOLE, carPoint(-2, 0.6, -1.2)), 1.5),\n burst('streak', 14, { p: [POOL[0], 0.05, POOL[2]], cone: 1.4, sp: [1, 3], life: 0.3, len: 0.02, c0: 0xffd080, c1: 0xff4000, g: 9.8 }),\n ),\n after(BOOM_AT, ...VEHICLE_BOOM),\n ],\n tick: [\n when(lt(TIME, BOOM_AT), [\n emit('drip', 'normal', 12, { p: TANK, box: [0.05, 0, 0.05], sp: 0, life: 1, size: 0.03, c0: 0x5a5a7a, a: 0.9, g: 9.8, fi: 0, gnd: 1 }),\n ]),\n when(and(gt(TIME, 3.5), lt(TIME, BOOM_AT)), [\n set('spread', clamp(div(sub(TIME, 3.5), 2.5), 0, 1)),\n set('front', vlerp(POOL, TANK_FLOOR, mul(variable('spread'), 0.8))),\n call('fire_emit', 'vz', variable('front'), add(0.6, mul(variable('spread'), 0.4)), 1.1, { smoke: 14 }),\n call('fire_emit', 'vz2', POOL, 0.7, 0.8, { smoke: 6 }),\n fireLight(0, vadd(variable('front'), [0, 1, 0]), 3, TIME),\n ]),\n when(ge(TIME, BOOM_AT), [\n call('fire_emit', 'wr', vadd(query('car', 'position'), [0, 0.9, 0]), 0.8, 1.4, { smoke: 18, smokeAlpha: 0.55 }),\n fireLight(0, vadd(query('car', 'position'), [0, 1.6, 0]), 3.4, TIME),\n behavior('car', 'step', { dt: DELTA }),\n ]),\n ],\n}\n","import { DELTA, gt, query, TIME, vadd } from '../dsl'\nimport { after, behavior, call, cue, fireLight, when } from '../steps'\nimport type { EffectDef } from '../types'\nimport { VEHICLE_BOOM } from './boom'\n\n/** W1133-1139 (`explosao`, `lab: 1`): a big blast (scale 1.8 of the grenade), the car jumps and lands crooked, bonnet, doors and a wheel fly, and the wreck burns. */\nexport const CAR_EXPLOSION: EffectDef = {\n id: 'car_explosion',\n pageId: 'explosao',\n team: 'vehicles',\n lab: 'car_fuel_tank',\n budget: { particles: 600, debris: 34, lights: 1 },\n loop: 10,\n view: { eye: [12, 4.2, 8], target: [5.6, 1, 0.8] },\n props: ['car'],\n setup: [cue('zombie_visible', { index: -1, visible: false })],\n timeline: [after(0.4, ...VEHICLE_BOOM)],\n tick: [\n when(gt(TIME, 0.5), [\n call('fire_emit', 'wr', vadd(query('car', 'position'), [0, 0.9, 0]), 0.8, 1.4, { smoke: 18, smokeAlpha: 0.55 }),\n fireLight(0, vadd(query('car', 'position'), [0, 1.6, 0]), 3.4, TIME),\n ]),\n behavior('car', 'step', { dt: DELTA }),\n ],\n}\n","import { add, eq, gt, INDEX, mod, mul, or, pick } from '../dsl'\nimport { after, behavior, burst, camera, cue, screen, repeat, when } from '../steps'\nimport type { EffectDef } from '../types'\nimport { carPoint } from './car'\n\nconst HITS = [[90, 40], [170, 60], [120, 75], [60, 50]]\nconst HIT_X = HITS.map(([x]) => x)\nconst HIT_Y = HITS.map(([, y]) => y)\nconst ZOMBIE = carPoint(0, 0, 3.1)\nconst CAR_YAW = -0.4\n\n/** W1140-1152 (`parabrisa`): from inside the car, blows shake the camera and add a star crack; at the second and fourth a smear of blood with a handprint runs down. */\nexport const WINDSHIELD: EffectDef = {\n id: 'windshield',\n pageId: 'parabrisa',\n team: 'vehicles',\n budget: { particles: 6, decals: 1 },\n loop: 5.8,\n setup: [\n cue('zombie_visible', { index: -1, visible: false }),\n behavior('walkers', 'place', { index: 0, x: ZOMBIE[0], z: ZOMBIE[2], facing: CAR_YAW + Math.PI }),\n camera(carPoint(-0.3, 1.35, -0.2), carPoint(0, 1.2, 2.2), true),\n ],\n timeline: [\n repeat(\n HITS.length,\n after(\n add(0.6, mul(INDEX, 0.85)),\n cue('zombie_swing', { index: 0, side: mod(INDEX, 2) }),\n after(\n add(0.6, mul(INDEX, 0.85), 0.15),\n behavior('cracks', 'crack', { x: pick(HIT_X, INDEX), y: pick(HIT_Y, INDEX), big: gt(INDEX, 1) }),\n when(or(eq(INDEX, 1), eq(INDEX, 3)), [behavior('cracks', 'blood_streaks', { x: pick(HIT_X, INDEX), y: pick(HIT_Y, INDEX) })]),\n screen({ add: { shake: 0.18 } }),\n 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: 0xffffff, a: 0.8, fi: 0.2 }),\n cue('windshield_stage', { stage: add(INDEX, 1) }),\n ),\n ),\n ),\n ],\n}\n","import { add, and, DELTA, gt, lt, mod, mul, rnd, TIME } from '../dsl'\nimport { after, burst, cue, lens, repeat, screen, when } from '../steps'\nimport type { EffectDef, EffectStep } from '../types'\n\nconst FRESH_BLOOD = 0x8a0a0a\nconst BEAT_RATE = 1.9\n\n/** A blow from one side (`swing`, W1178): a red edge on that side, a shake and splashes on the lens, 0.18 s after the swing starts. */\nconst blow = (side: 0 | 1): EffectStep[] => [\n cue('zombie_swing', { index: -1, side }),\n after(\n add(TIME, 0.18),\n screen({ set: side === 0 ? { dirL: 1 } : { dirR: 1 }, add: { shake: 0.3 } }),\n 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)),\n 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_BLOOD, g: 9.8, fi: 0, gnd: 3, ds: [0.05, 0.1] }),\n ),\n]\n\n/** W1169-1185 (`dano`): two blows, from the left and from the right, then the bite: a red flash, the colour drains, the heartbeat pulses and big blood runs down the lens. */\nexport const DAMAGE_AND_BITE: EffectDef = {\n id: 'damage_and_bite',\n pageId: 'dano',\n team: 'screen',\n budget: {},\n loop: 5.6,\n view: { eye: [0, 1.6, 1.7], target: [0, 1.5, 0] },\n timeline: [\n after(0.3, ...blow(0)),\n after(1.6, ...blow(1)),\n after(3, cue('zombie_lunge', { z: 0.45 })),\n after(\n 3.15,\n screen({ set: { red: 1, desat: 0.9 }, add: { shake: 0.6 } }),\n cue('bite', { infection: 1 }),\n repeat(7, lens(rnd(0.2, 0.8), rnd(0.2, 0.7), rnd(0.04, 0.1), '100,6,6', 1)),\n ),\n ],\n tick: [\n 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 } })])]),\n ],\n}\n","import { add, CAMERA_POSITION, cameraForward, DELTA, eq, gt, INDEX, lt, max, mod, mul, PARAM, rand, rnd, sin, TIME, variable, vadd } from '../dsl'\nimport { burst, cue, post, repeat, screen, set, when } from '../steps'\nimport type { EffectDef } from '../types'\n\nconst MALAISE = 0\nconst FEVER = 1\nconst DELIRIUM = 2\nconst LAST_HOURS = 3\nconst GHOSTS = [[-3, -1.8], [3.5, -2.4], [-6, 1.5]]\n\n/** W1186-1205 (`infeccao`): malaise (washed colour, a breathing vignette), fever (cold edge, waves of blur, a cough that shakes the camera), delirium, the last hours. */\nexport const INFECTION_PHASES: EffectDef = {\n id: 'infection_phases',\n pageId: 'infeccao',\n team: 'screen',\n params: { id: 'phase', options: ['malaise', 'fever', 'delirium', 'last_hours'], default: 1 },\n budget: { particles: 10 },\n loop: 0,\n view: { eye: [2, 1.7, 7], target: [0, 1.2, -1] },\n setup: [\n cue('zombie_visible', { index: -1, visible: false }),\n ...GHOSTS.map(([x, z], index) => cue('ghost_place', { index, x, z, turn: rnd(-0.4, 0.4) })),\n ],\n timeline: [set('nextCough', 2)],\n tick: [\n 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)))) })]),\n when(eq(PARAM, FEVER), [\n 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 }),\n when(gt(TIME, variable('nextCough')), [\n set('nextCough', add(TIME, rnd(3, 5))),\n screen({ add: { shake: 0.25 } }),\n 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: 0xdfe6ee, a: 0.2 }),\n ]),\n ]),\n when(eq(PARAM, DELIRIUM), [\n 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 }),\n 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) })),\n when(lt(rand(), mul(DELTA, 0.3)), [screen({ set: { white: 0.12 } })]),\n ]),\n when(eq(PARAM, LAST_HOURS), [\n post({ desat: 0.85, tintAlpha: 0.22, tintColor: '70,110,40', tunnel: 0.55, blur: 1.2 }),\n when(lt(mod(mul(TIME, 2.2), 1), mul(DELTA, 2.2)), [screen({ set: { beat: 1 }, add: { shake: 0.05 } })]),\n ]),\n ],\n}\n","import { add, CAMERA_POSITION, clamp, DELTA, div, eq, INDEX, ite, lt, min, mod, mul, query, rnd, sub, TIME, variable, vdist } from '../dsl'\nimport { behavior, cue, post, repeat, screen, set, when } from '../steps'\nimport type { EffectDef } from '../types'\n\nconst WALKERS = 6\nconst NEAR = 9\nconst RANGE = 7\nconst RESTING_BPM = 70\nconst BPM_RISE = 80\nconst walkerPoint = [query('walkers', 'x', INDEX), 0, query('walkers', 'z', INDEX)]\n\n/** W1206-1217 (`panico`): the aim trembles, the tunnel closes as the nearest zombie arrives and the heartbeat quickens from 70 to 150 bpm. */\nexport const PANIC: EffectDef = {\n id: 'panic',\n pageId: 'panico',\n team: 'screen',\n budget: {},\n loop: 8,\n view: { eye: [0, 1.65, 6], target: [0, 1.3, -2] },\n setup: [\n cue('zombie_visible', { index: -1, visible: false }),\n repeat(\n WALKERS,\n set('startX', rnd(-4, 4)),\n set('startZ', sub(rnd(-4, -2), mul(INDEX, 0.6))),\n behavior('walkers', 'place', { index: INDEX, x: variable('startX'), z: variable('startZ'), facing: 0 }),\n behavior('walkers', 'walk', { index: INDEX, to: [rnd(-1.2, 1.2), 0, 5], speed: ite(eq(mod(INDEX, 3), 1), 1.6, 0.7) }),\n ),\n ],\n timeline: [],\n tick: [\n set('nearest', 99),\n repeat(WALKERS, set('nearest', min(variable('nearest'), vdist(walkerPoint, CAMERA_POSITION)))),\n set('fear', clamp(div(sub(NEAR, variable('nearest')), RANGE), 0, 1)),\n set('bpm', add(RESTING_BPM, mul(BPM_RISE, variable('fear')))),\n 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'))) } })]),\n post({ tunnel: add(0.1, mul(0.65, variable('fear'))), desat: mul(0.45, variable('fear')) }),\n set('tremor', add(0.012, mul(0.035, variable('fear')))),\n ],\n}\n","import { rnd } from '../dsl'\nimport { after, burst, cue, lens, repeat, screen } from '../steps'\nimport type { EffectDef } from '../types'\n\n/** W1218-1226 (`cegueira`): a green cloud bursts between the bloater and the camera, the screen goes green and blurred and clears in 5 s; two coughs shake the camera. */\nexport const BLOATER_BLINDNESS: EffectDef = {\n id: 'bloater_blindness',\n pageId: 'cegueira',\n team: 'screen',\n budget: { particles: 30 },\n loop: 7.5,\n view: { eye: [0, 1.6, 2.6], target: [0, 1.3, 0] },\n setup: [cue('bloater_pose', { eyes: 0x5fe08a, skin: 0x6b7a52, belly: [1.4, 1.25, 1.6] })],\n timeline: [\n after(\n 0.7,\n cue('zombie_visible', { index: -1, visible: false }),\n screen({ set: { gas: 1 }, add: { shake: 0.3 } }),\n 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: 0x6f8a3c, c1: 0x3b4a26, a: 0.3, fi: 0.05 }),\n repeat(8, lens(rnd(0.15, 0.85), rnd(0.15, 0.7), rnd(0.03, 0.08), '110,150,50', 0.9)),\n ),\n after(2, screen({ add: { shake: 0.2 } })),\n after(3.4, screen({ add: { shake: 0.2 } })),\n ],\n}\n","import { TIME } from '../dsl'\nimport { call, cue, fireLight } from '../steps'\nimport type { EffectDef } from '../types'\n\nconst FIRE = [-1.4, 0.1, 3.0] as const\n\n/** W1227-1233 (`fogueira`): a low flame, embers that spiral up, thin smoke and a warm light that trembles and paints the survivor. */\nexport const CAMPFIRE: EffectDef = {\n id: 'campfire',\n pageId: 'fogueira',\n team: 'screen',\n budget: { particles: 120, lights: 1 },\n loop: 0,\n view: { eye: [1.4, 1.6, 6.4], target: [-1.4, 0.6, 3] },\n setup: [cue('zombie_visible', { index: -1, visible: false }), cue('survivor_pose', { x: -2.6, z: 3.6, turn: 2.2 })],\n timeline: [],\n tick: [\n call('fire_emit', 'camp', FIRE, 0.25, 0.55, { smoke: 8, smokeColor: 0x4a4642, smokeAlpha: 0.25 }),\n fireLight(0, [FIRE[0], FIRE[1] + 0.8, FIRE[2]], 2.4, TIME),\n ],\n}\n","import { add, gt, TIME, variable } from '../dsl'\nimport { after, burst, cue, set, when } from '../steps'\nimport { normalized } from '../vector-math'\nimport type { EffectDef, EffectStep } from '../types'\n\nconst FACING = 0.5\n/** The survivor's mouth and the way it breathes out, with the survivor at (-1.6, 0, 1.8) turned by `FACING` (W1236-1238). */\nconst MOUTH = [Math.sin(FACING) * 0.2 + -1.6, 1.68, Math.cos(FACING) * 0.2 + 1.8] as const\nconst BREATH = normalized([Math.sin(FACING), -0.15, Math.cos(FACING)])\nconst BREATH_EVERY = 2.4\nconst COUGH_EVERY = 6.5\n\n/** A puff of breath (`puff`, W1239): `strength` scales how many and how fast. */\nconst puff = (strength: number): EffectStep =>\n 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: 0xdfe6ee, a: 0.22, g: -0.1 })\n\n/** W1234-1243 (`tosse`): a short breath every 2.4 s; in a cough, three quick bigger ones and a jolt of the body; the torch shows its beam in the fog. */\nexport const COUGH_AND_BREATH: EffectDef = {\n id: 'cough_and_breath',\n pageId: 'tosse',\n team: 'screen',\n budget: { particles: 40, lights: 1 },\n loop: 0,\n view: { eye: [2.4, 1.8, 6.5], target: [-1.4, 1.3, 1.6] },\n setup: [cue('zombie_visible', { index: -1, visible: false }), cue('survivor_pose', { x: -1.6, z: 1.8, turn: FACING }), cue('torch', { on: true })],\n timeline: [set('nextBreath', 0.5), set('nextCough', 3)],\n tick: [\n when(gt(TIME, variable('nextBreath')), [set('nextBreath', add(TIME, BREATH_EVERY)), puff(1)]),\n when(gt(TIME, variable('nextCough')), [\n set('nextCough', add(TIME, COUGH_EVERY)),\n ...[0, 0.25, 0.5].map((delay) => after(add(TIME, delay), puff(1.6), cue('survivor_jolt', { tilt: 0.25 }))),\n ]),\n ],\n}\n","import { cos, local, mul, rnd, scaled, sin, variable, vnorm } from './dsl'\nimport { burst, call, debris, repeat, screen, spawn } from './steps'\nimport type { EffectDef } from './types'\n\nconst FACING = [0, 0, 1] as const\nconst UP = [0, 1, 0] as const\nconst ORIGIN = [0, 0, 0] as const\nconst FORWARD = [0, 0, -1] as const\nconst IMPACT_MATERIALS = ['concrete', 'metal', 'wood', 'glass', 'flesh', 'water'] as const\n\n/** What a hit leaves on one material, the way the Workshop's `impact` helper does (W374-391), played once at the hit. */\nconst impactEffect = (material: (typeof IMPACT_MATERIALS)[number]): EffectDef => ({\n id: `impact_${material}`,\n pageId: `event:impact_${material}`,\n team: 'weapons',\n budget: { particles: 40, decals: 1 },\n loop: 2,\n timeline: [call('impact', material, ORIGIN, FACING, 1, 0)],\n})\n\n/** The flash of one weapon at its muzzle (W417 `muzzle`), played once: the recipe's own per-weapon sizes, petals, light and smoke. */\nconst muzzleEffect = (weapon: 'glock' | 'ak' | 'r870' | 'barrett'): EffectDef => ({\n id: `muzzle_${weapon}`,\n pageId: `event:muzzle_${weapon}`,\n team: 'weapons',\n budget: { particles: 60, lights: 1 },\n loop: 2.4,\n timeline: [call('muzzle', weapon, ORIGIN, FORWARD)],\n})\n\n/** A case thrown out of the port (W430 `casing`): brass, a red shell or a .50 case. */\nconst casingEffect = (id: string, type: '' | 'shell' | '50'): EffectDef => ({\n id,\n pageId: `event:${id}`,\n team: 'weapons',\n budget: { debris: 1 },\n loop: 2,\n timeline: [call('casing', ORIGIN, type)],\n})\n\n/**\n * The effects the destruction events play, as slices of the Workshop's recipes: the same bursts, with the recipe's own numbers, centred on the point the event\n * happens (`data/event-effects.json` says which event plays which). They are not Workshop pages, so they have no golden of their own; each cites the lines it was cut from.\n */\nexport const EVENT_EFFECTS: readonly EffectDef[] = [\n ...IMPACT_MATERIALS.map(impactEffect),\n ...(['glock', 'ak', 'r870', 'barrett'] as const).map(muzzleEffect),\n casingEffect('casing_brass', ''),\n casingEffect('casing_shell', 'shell'),\n casingEffect('casing_50', '50'),\n {\n // door_stages (W856-871): the dust a blow shakes loose from the frame and the splinters.\n id: 'stage_crack',\n pageId: 'event:stage_crack',\n team: 'destruction',\n budget: { particles: 12, debris: 5 },\n loop: 2,\n timeline: [\n 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: 0x7a7366, a: 0.45, g: 0.4 }),\n 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: 0x7a5530, life: 12 })),\n ],\n },\n {\n // block_wall (W872-886, 3.6 s): the ring of dust that rolls out when the wall gives way.\n id: 'breach_dust',\n pageId: 'event:breach_dust',\n team: 'destruction',\n budget: { particles: 90, debris: 16 },\n loop: 5,\n timeline: [\n screen({ add: { shake: 0.25 } }),\n 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: 0x8a857a, c1: 0x4a4640, a: 0.45 }),\n 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: 0x6e6a63, life: 20 })),\n repeat(\n scaled(60),\n spawn('normal', local('angle', rnd(0, 6.283), {\n p: [rnd(-1.4, 1.4), 0.2, rnd(-0.6, 0.4)],\n dir: vnorm([cos(variable('angle')), 0.3, sin(variable('angle'))]),\n cone: 0.4, sp: [0.8, 2.4], drag: 1.3, life: [2.5, 4.5], size: [0.6, 1], grow: 3, c0: 0x7a7268, c1: 0x4a4640, a: 0.45, fi: 0.12,\n })),\n ),\n ],\n },\n {\n // building_collapse (W872-886): the slab dropping in blocks and the cloud that rolls along the street.\n id: 'collapse_dust',\n pageId: 'event:collapse_dust',\n team: 'destruction',\n budget: { particles: 350, debris: 26 },\n loop: 8,\n timeline: [\n screen({ add: { shake: 0.45 } }),\n 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: 0x6e6a63, spin: 3, life: 30, bounce: 0.1 })),\n 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: 0x5e5a54, life: 30 })),\n repeat(\n scaled(70),\n spawn('normal', local('angle', rnd(-0.4, 3.5), {\n p: [rnd(-2, 2), 0.2, rnd(-0.5, 0.5)],\n dir: vnorm([mul(cos(variable('angle')), 0.6), 0.25, sin(variable('angle'))]),\n cone: 0.5, sp: [1, 3.2], drag: 1.1, life: [3, 6], size: [0.7, 1.2], grow: 3, c0: 0x7a7268, c1: 0x4a4640, a: 0.5, fi: 0.12,\n })),\n ),\n ],\n },\n {\n // shopfront_glass (W846-855): the pane coming down as shards, the flash of the glints and the pale dust.\n id: 'glass_burst',\n pageId: 'event:glass_burst',\n team: 'destruction',\n budget: { debris: 80, particles: 50 },\n loop: 5,\n timeline: [\n screen({ add: { shake: 0.08 } }),\n repeat(\n scaled(75),\n 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), 0.006, rnd(0.05, 0.18)], c: 0xa8c8d8, spin: 14, life: 30, bounce: 0.15 }),\n ),\n burst('add', 30, { p: ORIGIN, box: [1.2, 0.8, 0.05], size: [0.02, 0.05], life: [0.2, 0.6], c0: 0xffffff, a: 0.9, fi: 0.3 }),\n 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: 0xbfd6e2, a: 0.2 }),\n ],\n },\n {\n // The chips macro the page uses on wood (W849) plus rust (`rustAt`), for rebar laid bare in concrete.\n id: 'rebar_expose',\n pageId: 'event:rebar_expose',\n team: 'destruction',\n budget: { particles: 20, debris: 8 },\n loop: 2,\n timeline: [call('chips', ORIGIN, UP, 8, 0.9), call('rust_at', ORIGIN, 6)],\n },\n]\n","import { BLOATER_CLOUD } from './creatures/bloater-cloud'\nimport { BLOOD_HIT } from './creatures/blood-hit'\nimport { CORPSE_DECAY } from './creatures/corpse-decay'\nimport { EYES_IN_FOG } from './creatures/eyes-in-fog'\nimport { DISMEMBER } from './creatures/dismember'\nimport { HEADSHOT } from './creatures/headshot'\nimport { REANIMATION } from './creatures/reanimation'\nimport { SCREAMER_SHRIEK } from './creatures/screamer-shriek'\nimport { IMPACT_BY_MATERIAL } from './weapons/impact-by-material'\nimport { PENETRATION } from './weapons/penetration'\nimport { CASINGS_AND_MAGAZINE } from './weapons/casings-and-magazine'\nimport { MOLOTOV } from './weapons/molotov'\nimport { GRENADE } from './weapons/grenade'\nimport { EXPLOSIVE_BARREL } from './weapons/explosive-barrel'\nimport { MUZZLE_FLASH } from './weapons/muzzle-flash'\nimport { SHOPFRONT_GLASS } from './destruction/shopfront-glass'\nimport { DOOR_STAGES } from './destruction/door-stages'\nimport { BUILDING_COLLAPSE } from './destruction/building-collapse'\nimport { BLOCK_WALL } from './destruction/block-wall'\nimport { METAL_DOOR_EFFECT } from './destruction/metal-door'\nimport { IRON_RAILING } from './destruction/iron-railing'\nimport { WOODEN_FENCE } from './destruction/wooden-fence'\nimport { HIT_POINT_BREAKAGE } from './destruction/hit-point-breakage'\nimport { PRESSURE_CYLINDERS } from './destruction/pressure-cylinders'\nimport { BUILDING_FIRE } from './world/building-fire'\nimport { SCRAPING_SPARKS } from './vehicles/scraping-sparks'\nimport { ENGINE_SMOKE } from './vehicles/engine-smoke'\nimport { FUEL_LEAK } from './vehicles/fuel-leak'\nimport { CAR_EXPLOSION } from './vehicles/car-explosion'\nimport { WINDSHIELD } from './vehicles/windshield'\nimport { DAMAGE_AND_BITE } from './screen/damage-and-bite'\nimport { INFECTION_PHASES } from './screen/infection-phases'\nimport { PANIC } from './screen/panic'\nimport { BLOATER_BLINDNESS } from './screen/bloater-blindness'\nimport { CAMPFIRE } from './screen/campfire'\nimport { COUGH_AND_BREATH } from './screen/cough-and-breath'\nimport { EVENT_EFFECTS } from './events'\nimport type { EffectDef } from './types'\n\nconst ALL_EFFECTS: readonly EffectDef[] = [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, SCRAPING_SPARKS, ENGINE_SMOKE, FUEL_LEAK, CAR_EXPLOSION, WINDSHIELD, DAMAGE_AND_BITE, INFECTION_PHASES, PANIC, BLOATER_BLINDNESS, CAMPFIRE, COUGH_AND_BREATH]\n\n/** Every effect of the Workshop by id. A game adds, changes or removes entries with `patchEffects`. */\nexport const WORKSHOP_EFFECTS: Readonly<Record<string, EffectDef>> = Object.fromEntries(ALL_EFFECTS.map((effect) => [effect.id, effect]))\n\n/** The Workshop's recipes plus the one-shot effects the destruction events play (`event-effects.json`): what a world plays by default. */\nexport const GAME_EFFECTS: Readonly<Record<string, EffectDef>> = { ...WORKSHOP_EFFECTS, ...Object.fromEntries(EVENT_EFFECTS.map((effect) => [effect.id, effect])) }\n\n/** A copy of the recipes with some changed (a partial replaces the fields it names), added or removed (`null`); a game tunes the Workshop's effects without touching the core. */\nexport function patchEffects(base: Readonly<Record<string, EffectDef>>, changes: Readonly<Record<string, Partial<EffectDef> | EffectDef | null>>): Record<string, EffectDef> {\n const result: Record<string, EffectDef> = { ...base }\n for (const [id, change] of Object.entries(changes)) {\n if (change === null) delete result[id]\n else result[id] = { ...(base[id] ?? {}), ...change, id } as EffectDef\n }\n return result\n}\n","const LEHMER_MULTIPLIER = 16807\nconst LEHMER_MODULUS = 2147483647\nconst LCG_MULTIPLIER = 1664525\nconst LCG_INCREMENT = 1013904223\nconst LCG_RANGE = 4294967296\nconst HASH_MIX = 0x9e3779b1\n\nexport interface MutableVec3 {\n x: number\n y: number\n z: number\n}\n\n/** A seeded stream of numbers in [0, 1). The Lab's generator and the Fluid Standard's are both here, behind the same three calls. */\nexport interface RandomStream {\n /** The next number (the Lab's `rnd`). */\n next(): number\n /** The Lab's `rr(min, max)`: one draw, scaled. */\n between(min: number, max: number): number\n /** The Lab's `rdir`: a random unit vector, two draws (`u`, then the angle), written into `out`. */\n direction(out: MutableVec3): MutableVec3\n readonly state: number\n}\n\nfunction normalizeLehmerSeed(seed: number): number {\n const whole = Math.floor(Math.abs(seed)) % (LEHMER_MODULUS - 1)\n return whole === 0 ? 1 : whole\n}\n\n/** Park-Miller 16807 (the Lab's L16-18). A seed in 1..2147483646 is used as is, so a stream replays the page's. */\nexport function lehmer(seed: number): RandomStream {\n let state = normalizeLehmerSeed(seed)\n const next = (): number => {\n state = (state * LEHMER_MULTIPLIER) % LEHMER_MODULUS\n return (state - 1) / (LEHMER_MODULUS - 1)\n }\n return {\n next,\n between: (min, max) => min + (max - min) * next(),\n direction(out) {\n const up = next() * 2 - 1\n const angle = next() * Math.PI * 2\n const ring = Math.sqrt(1 - up * up)\n out.x = ring * Math.cos(angle)\n out.y = up\n out.z = ring * Math.sin(angle)\n return out\n },\n get state() {\n return state\n },\n }\n}\n\n/** The Fluid Standard's generator (F17-18): a 32-bit linear congruential one. */\nexport function lcg(seed: number): RandomStream {\n let state = seed >>> 0\n const next = (): number => {\n state = (Math.imul(state, LCG_MULTIPLIER) + LCG_INCREMENT) >>> 0\n return state / LCG_RANGE\n }\n return {\n next,\n between: (min, max) => min + (max - min) * next(),\n direction(out) {\n const up = next() * 2 - 1\n const angle = next() * Math.PI * 2\n const ring = Math.sqrt(1 - up * up)\n out.x = ring * Math.cos(angle)\n out.y = up\n out.z = ring * Math.sin(angle)\n return out\n },\n get state() {\n return state\n },\n }\n}\n\nfunction mixEventSeed(eventSeed: number): number {\n let mixed = Math.imul((eventSeed | 0) ^ HASH_MIX, 0x85ebca6b)\n mixed ^= mixed >>> 13\n mixed = Math.imul(mixed, 0xc2b2ae35)\n mixed ^= mixed >>> 16\n return (mixed >>> 0) % (LEHMER_MODULUS - 1) + 1\n}\n\n/** The stream of one event: the same event seed draws the same effect on every machine, whatever else is playing (`randomMode: 'per_event'`). */\nexport function streamFor(eventSeed: number): RandomStream {\n return lehmer(mixEventSeed(eventSeed))\n}\n","import type { ImpactMaterial } from './blood'\n\n/** What each material raises when hit: dust colour, chip colour and the number of sparks (W374-381). Zero means none. */\nconst TABLE: Readonly<Record<ImpactMaterial, { readonly dust: number; readonly chip: number; readonly spark: number }>> = {\n concrete: { dust: 0x8a857a, chip: 0x6e6a63, spark: 2 },\n metal: { dust: 0, chip: 0, spark: 18 },\n wood: { dust: 0x6a5038, chip: 0x7a5530, spark: 0 },\n glass: { dust: 0xbfd6e2, chip: 0xa8c8d8, spark: 0 },\n flesh: { dust: 0, chip: 0, spark: 0 },\n water: { dust: 0, chip: 0, spark: 0 },\n}\n\nexport function sparkSpec(material: ImpactMaterial): { readonly dust: number; readonly chip: number; readonly spark: number } {\n return TABLE[material]\n}\n","import type { WorkshopEngine } from '../../workshop/engine'\nimport type { Vec3Tuple } from '../../workshop/types'\nimport { negated, plus, plusScaled } from '../vector-math'\nimport { sparkSpec } from './impact-table'\n\n/** The blood colour of each infection age (W116) and how much each bleeds (W392). */\nexport const BLOOD_COLORS = [0x8a0a0a, 0x5a0c10, 0x3a1c12, 0x1d1914] as const\nconst BLEED_BY_AGE = [1, 0.7, 0.35, 0.1] as const\n\n/** The dust and dry flakes a decrepit body gives instead of blood. */\nfunction drySplash(engine: WorkshopEngine, point: Vec3Tuple, exitDirection: Vec3Tuple, scale: number): void {\n engine.burst('normal', 16 * scale, { p: point, dir: exitDirection, cone: 1, sp: [0.5, 1.6], drag: 2, life: [0.9, 1.6], size: [0.1, 0.2], grow: 4, c0: 0x2e2822, c1: 0x15130f, a: 0.6, g: -0.15 })\n for (let index = 0; index < engine.scaled(5); index++) engine.debrisPiece({ p: point, dir: exitDirection, cone: 1, sp: [0.5, 2], size: [0.03, 0.008, 0.03], c: 0x1c1916, life: 6 })\n engine.decal([point[0], 0, point[2] + exitDirection[2] * 0.6, 0.25, 0x1a1714, { grow: 0.4, ar: 0.7 }])\n}\n\n/** `bloodHit` (W392-398): a mist at the entry, a jet out of the exit and a cloud, by infection age. */\nexport function bloodHit(engine: WorkshopEngine, point: Vec3Tuple, exitDirection: Vec3Tuple, age: number, scale = 1): void {\n if (age === 3) return drySplash(engine, point, exitDirection, scale)\n const amount = BLEED_BY_AGE[age]!\n const color = BLOOD_COLORS[age]!\n engine.burst('normal', 10 * amount * scale, { p: point, dir: negated(exitDirection), cone: 0.9, sp: [0.2, 0.9], drag: 3, life: [0.25, 0.5], size: [0.08, 0.14], grow: 3, c0: color, a: 0.65 })\n engine.burst('normal', 45 * amount * scale, { p: point, dir: exitDirection, cone: 0.45, sp: [2.5, 6], life: [1, 1.6], size: [0.035, 0.07], c0: color, a: 0.95, g: 9.8, fi: 0, gnd: 3, ds: [0.06, 0.16] })\n engine.burst('normal', 14 * amount * scale, { p: point, dir: exitDirection, cone: 0.5, sp: [1, 2.5], drag: 2.5, life: [0.4, 0.8], size: [0.1, 0.18], grow: 3, c0: color, a: 0.45 })\n}\n\nconst MATERIAL_NAMES = ['concrete', 'metal', 'wood', 'glass', 'flesh', 'water'] as const\nexport type ImpactMaterial = (typeof MATERIAL_NAMES)[number]\n\nfunction waterImpact(engine: WorkshopEngine, point: Vec3Tuple, scale: number): void {\n engine.burst('normal', 22 * scale, { p: point, dir: [0, 1, 0], cone: 0.5, sp: [1.5, 3.5], life: [0.4, 0.7], size: [0.03, 0.06], c0: 0xc8d6e6, a: 0.7, g: 9.8, fi: 0, gnd: 1 })\n engine.ring([plus(point, 0, 0.012, 0), 0.02, 0.6, 0.7, 0x8090a8])\n engine.ring([plus(point, 0, 0.013, 0), 0.02, 0.35, 0.5, 0x8090a8])\n}\n\n/** `impact` (W374-391): what a bullet raises on each material. */\nexport function impact(engine: WorkshopEngine, material: ImpactMaterial, point: Vec3Tuple, normal: Vec3Tuple, scale = 1, age = 0): void {\n if (material === 'flesh') return bloodHit(engine, point, negated(normal), age, scale)\n if (material === 'water') return waterImpact(engine, point, scale)\n const spec = sparkSpec(material)\n if (spec.dust) engine.burst('normal', 14 * scale, { p: point, dir: normal, cone: 0.6, sp: [0.6, 2.4], drag: 2.5, life: [0.6, 1.2], size: [0.08, 0.16], grow: 4, c0: spec.dust, c1: 0x3a3936, a: 0.55, g: -0.1 })\n if (spec.chip) {\n const size = material === 'wood' ? ([0.015, 0.015, 0.09] as const) : material === 'glass' ? ([0.03, 0.004, 0.03] as const) : ([0.03, 0.03, 0.03] as const)\n for (let index = 0; index < engine.scaled(material === 'wood' ? 8 : 6) * scale; index++) {\n engine.debrisPiece({ p: point, dir: normal, cone: 0.8, sp: [1.5, 4], size, k: [0.6, 1.4], c: spec.chip, life: 6 })\n }\n }\n if (spec.spark && engine.quality < 2) {\n engine.burst('streak', spec.spark * scale, { p: point, dir: normal, cone: material === 'metal' ? 0.9 : 0.5, sp: [3, 9], life: [0.15, 0.45], len: 0.025, c0: 0xfff0c0, c1: 0xff5a10, g: 9.8, gnd: 2 })\n engine.burst('add', material === 'metal' ? 3 : 1, { p: point, size: [0.12, 0.2], life: 0.06, c0: 0xffe0a0, fi: 0 })\n if (material === 'metal') engine.flash([plusScaled(point, normal, 0.2), 0xffb060, 2, 0.06, 5])\n }\n if (material === 'glass') engine.burst('add', 5, { p: point, r: 0.15, size: [0.02, 0.05], life: [0.2, 0.5], c0: 0xffffff, a: 0.9, fi: 0.3 })\n if (engine.quality < 2) {\n const size = material === 'glass' ? 0.07 : material === 'metal' ? 0.05 : 0.06\n engine.decal([point[0], point[1], point[2], size, material === 'metal' ? 0x2a2522 : 0x0e0d0c, { n: normal, grow: 0, life: 60 }])\n }\n}\n\nexport function impactMaterialAt(index: number): ImpactMaterial {\n return MATERIAL_NAMES[index]!\n}\n","import { impact } from '../builders/blood'\nimport type { Behavior } from '../behavior'\n\nconst COLUMNS = 6\nconst ROWS = 5\nconst ORIGIN = [-2.2, 0, 2.6] as const\nconst BLOCK_SIZE = [0.5, 0.44, 0.25] as const\nconst OBJECT_ID_DRAWS = 4\nconst FLOOR = 0.13\nconst QUARTER_TURN = 1.5708\n\ninterface Motion {\n vx: number\n vy: number\n vz: number\n wx: number\n wy: number\n wz: number\n rest: boolean\n}\n\nexport interface WallBlock {\n readonly home: readonly [number, number]\n x: number\n y: number\n rotation: [number, number, number]\n /** Bullet holes stuck to this block (local to the block). */\n holes: { x: number; y: number; scale: number; chipTurn: number; chipScale: number }[]\n motion: Motion | undefined\n}\n\nconst clamp = (value: number, low: number, high: number): number => (value < low ? low : value > high ? high : value)\n\nfunction createBlocks(): WallBlock[] {\n const blocks: WallBlock[] = []\n for (let row = 0; row < ROWS; row++) {\n for (let column = 0; column < COLUMNS; column++) {\n const home = [(column - 2.5) * BLOCK_SIZE[0] + (row % 2 ? 0.12 : 0), 0.22 + row * BLOCK_SIZE[1]] as const\n blocks.push({ home, x: home[0], y: home[1], rotation: [0, 0, 0], holes: [], motion: undefined })\n }\n }\n return blocks\n}\n\n/**\n * The free-standing wall of 30 blocks of the page (W926-935, W1039-1046): a bullet leaves a hole and a chip stuck to the nearest block (`bwHit`, which\n * also costs the page the ids three.js gives each new mesh), and when it gives way the blocks fall with the velocities the page draws for them and\n * rest on their sides. The holes travel with the blocks, which is the point of the effect.\n */\nexport function createBlocksBehavior(): Behavior {\n let blocks = createBlocks()\n return {\n reset() {\n blocks = createBlocks()\n },\n act(host, action, args) {\n const random = host.engine.random\n if (action === 'hit') {\n const { x, y, scale } = args as { readonly x: number; readonly y: number; readonly scale: number }\n let best: WallBlock | undefined\n let bestDistance = 9\n for (const block of blocks) {\n const distance = Math.hypot(block.x - x, block.y - y)\n if (distance < bestDistance) {\n bestDistance = distance\n best = block\n }\n }\n if (!best) return\n const offsetX = clamp(x - best.x, -0.2, 0.2)\n const offsetY = clamp(y - best.y, -0.17, 0.17)\n for (let draw = 0; draw < OBJECT_ID_DRAWS; draw++) random.next()\n const chipTurn = random.between(0, 6)\n const chipScale = random.between(0.8, 1.3) * scale\n for (let draw = 0; draw < OBJECT_ID_DRAWS; draw++) random.next()\n best.holes.push({ x: offsetX, y: offsetY, scale, chipTurn, chipScale })\n impact(host.engine, 'concrete', [ORIGIN[0] + x, y, ORIGIN[2] + 0.13], [0, 0, 1], 0.8)\n } else if (action === 'collapse') {\n const { x, y } = args as { readonly x: number; readonly y: number }\n for (const block of blocks) {\n const dx = block.x - x\n const dy = block.y - y\n block.motion = {\n vx: dx * random.between(0.8, 2),\n vy: random.between(0.5, 2.5) + dy * 0.5,\n vz: random.between(-2.2, -0.4),\n wx: random.between(-4, 4),\n wy: random.between(-3, 3),\n wz: random.between(-4, 4),\n rest: false,\n }\n }\n }\n },\n query(_host, name) {\n if (name === 'blocks') return blocks\n throw new Error(`blocks: unknown query ${name}`)\n },\n update(_host, dt) {\n for (const block of blocks) {\n const motion = block.motion\n if (!motion || motion.rest) continue\n motion.vy -= 9.8 * dt\n block.x += motion.vx * dt\n block.y += motion.vy * dt\n block.rotation[0] += motion.wx * dt\n block.rotation[1] += motion.wy * dt\n block.rotation[2] += motion.wz * dt\n if (block.y < FLOOR) {\n block.y = FLOOR\n motion.vy *= -0.25\n motion.vx *= 0.5\n motion.vz *= 0.5\n motion.wx *= 0.5\n motion.wy *= 0.5\n motion.wz *= 0.5\n if (Math.abs(motion.vy) < 0.4 && Math.hypot(motion.vx, motion.vz) < 0.3) {\n motion.rest = true\n block.rotation[0] = Math.round(block.rotation[0] / QUARTER_TURN) * QUARTER_TURN\n block.rotation[2] = Math.round(block.rotation[2] / QUARTER_TURN) * QUARTER_TURN\n }\n }\n }\n },\n }\n}\n","import type { Behavior } from '../behavior'\nimport type { Vec3Tuple } from '../../workshop/types'\n\nconst ORIGIN: Vec3Tuple = [5.6, 0, 0.8]\nconst MAX_TILT = 0.25\n\n/**\n * The car of the page after it blew up (`carTick`, W1131): it leaps, spins and lands crooked. Its height is what the burning wreck's fire is emitted\n * from, so the model keeps it; the body is drawn by whoever owns the car.\n */\nexport function createCarBehavior(): Behavior {\n let y = 0\n let rise: number | undefined\n let spin = 0\n let roll = 0\n return {\n reset() {\n y = 0\n rise = undefined\n spin = 0\n roll = 0\n },\n act(_host, action, args) {\n if (action === 'launch') {\n const launch = args as { readonly rise: number; readonly spin: number }\n rise = launch.rise\n spin = launch.spin\n } else if (action === 'step') {\n if (rise === undefined) return\n const dt = (args as { readonly dt: number }).dt\n rise -= 9.8 * dt\n y = Math.max(0, y + rise * dt)\n roll += spin * dt\n spin *= Math.exp(-dt * 2)\n if (y === 0 && rise < 0) {\n rise = undefined\n roll = Math.min(MAX_TILT, Math.max(-MAX_TILT, roll))\n }\n }\n },\n query(_host, name) {\n if (name === 'position') return [ORIGIN[0], y, ORIGIN[2]]\n if (name === 'roll') return roll\n throw new Error(`car: unknown query ${name}`)\n },\n update() {},\n }\n}\n","import type { Behavior } from '../behavior'\n\n/** A smear of blood that runs down the windshield from a hit (`wsMarks`, W1156). */\nexport interface BloodStreak {\n readonly x: number\n readonly y: number\n readonly dx: number\n readonly dy: number\n readonly width: number\n}\n\nexport interface Crack {\n readonly x: number\n readonly y: number\n /** Each ray is a list of `[dx, dy]` segments that start at the impact. */\n readonly rays: readonly (readonly (readonly [number, number])[])[]\n readonly rings: readonly number[]\n}\n\nconst FULL_TURN_ROUNDED = 6.28\nconst SHORT_RAYS = 7\nconst LONG_RAYS = 12\nconst STREAKS_PER_HIT = 5\n\n/** `crackAt` (W62-63): the star of cracks a hit leaves in glass. Its draws depend on the lengths it draws, so the model keeps the same loop. */\nexport function createCracksBehavior(): Behavior {\n let cracks: Crack[] = []\n let streaks: BloodStreak[][] = []\n return {\n reset() {\n cracks = []\n streaks = []\n },\n act(host, action, args) {\n if (action === 'blood_streaks') {\n const { x, y } = args as { readonly x: number; readonly y: number }\n const random = host.engine.random\n const group: BloodStreak[] = []\n for (let index = 0; index < STREAKS_PER_HIT; index++) {\n group.push({ x: x - 18 + index * 8, y: y - 10, dx: random.between(-3, 3), dy: random.between(18, 40), width: random.between(5, 9) })\n }\n streaks.push(group)\n return\n }\n if (action !== 'crack') return\n const { x, y, big } = args as { readonly x: number; readonly y: number; readonly big: boolean }\n const random = host.engine.random\n const count = big ? LONG_RAYS : SHORT_RAYS\n const rays: [number, number][][] = []\n for (let ray = 0; ray < count; ray++) {\n let angle = (ray / count) * FULL_TURN_ROUNDED + random.between(-0.3, 0.3)\n const segments: [number, number][] = []\n let remaining = big ? random.between(40, 110) : random.between(15, 45)\n while (remaining > 0) {\n const length = random.between(6, 14)\n angle += random.between(-0.35, 0.35)\n segments.push([Math.cos(angle) * length, Math.sin(angle) * length])\n remaining -= length\n }\n rays.push(segments)\n }\n cracks.push({ x, y, rays, rings: big ? [12, 26] : [8] })\n },\n query(_host, name) {\n if (name === 'cracks') return cracks\n if (name === 'streaks') return streaks\n throw new Error(`cracks: unknown query ${name}`)\n },\n update() {},\n }\n}\n","import type { WorkshopEngine } from '../../workshop/engine'\nimport type { Vec3Tuple } from '../../workshop/types'\n\n/** Wood chips and a puff of dust (`chips`, W849). */\nexport function chips(engine: WorkshopEngine, point: Vec3Tuple, direction: Vec3Tuple, count: number, spread?: number): void {\n for (let index = 0; index < engine.scaled(count); index++) {\n engine.debrisPiece({ p: point, dir: direction, cone: spread || 0.9, sp: [1, 3], size: [0.012, 0.012, 0.09], k: [0.6, 1.4], c: 0xb08a5a, life: 20 })\n }\n engine.burst('normal', count, { p: point, dir: direction, cone: 0.8, sp: [0.3, 1.2], drag: 2, life: [0.8, 1.4], size: [0.08, 0.15], grow: 3, c0: 0x7a6048, a: 0.4 })\n}\n\n/** Sparks off metal (`sparkAt`, W899). */\nexport function sparkAt(engine: WorkshopEngine, point: Vec3Tuple, direction: Vec3Tuple, count: number, spread?: number): void {\n engine.burst('streak', count, { p: point, dir: direction, cone: spread || 0.8, sp: [2.5, 7], life: [0.2, 0.5], len: 0.025, c0: 0xfff0c0, c1: 0xff5010, g: 9.8, gnd: 2 })\n engine.burst('add', 2, { p: point, size: [0.1, 0.18], life: 0.06, c0: 0xffd8a0, fi: 0 })\n}\n\n/** Rust flakes and a little dust (`rustAt`, W900). */\nexport function rustAt(engine: WorkshopEngine, point: Vec3Tuple, count: number): void {\n engine.burst('normal', count, { p: point, r: 0.1, sp: [0.1, 0.5], life: [1, 1.6], size: [0.02, 0.035], c0: 0x7a4a2a, a: 0.9, g: 6, fi: 0, gnd: 1 })\n engine.burst('normal', Math.ceil(count / 2), { p: point, r: 0.08, sp: [0.2, 0.5], drag: 2, life: [0.8, 1.3], size: [0.08, 0.14], grow: 3, c0: 0x6a4a34, a: 0.3 })\n}\n","import type { Behavior, BehaviorHost } from '../behavior'\nimport { sparkAt } from '../builders/shared'\nimport { normalized } from '../vector-math'\nimport type { Vec3Tuple } from '../../workshop/types'\n\nconst SPOT: Vec3Tuple = [-2.2, 0, 2.6]\nconst WALL_X = -8.2\nconst QUARTER_TURN = Math.PI / 2\n\nconst lerp = (from: number, to: number, amount: number): number => from + (to - from) * amount\nconst clamp = (value: number, low: number, high: number): number => (value < low ? low : value > high ? high : value)\n\n/**\n * The pressure cylinder of the page (W1021-1038): an extinguisher that spins on the floor pushed by its own jet, or an oxygen bottle that becomes a\n * rocket and hits the wall. `step` moves it the way the page's tick does, drawing the sparks of the wall hit; the recipe then asks where the jet comes\n * out (`tip`, `back`, `direction`) to emit it.\n */\nexport function createCylinderBehavior(): Behavior {\n let position: Vec3Tuple = SPOT\n let velocity: Vec3Tuple = [0, 0, 0]\n let rotationZ = 0\n let angle = 0\n let spin = 0\n let live = false\n let oxygen = false\n let direction: Vec3Tuple = [1, 0, 0]\n\n const clear = (): void => {\n position = SPOT\n velocity = [0, 0, 0]\n rotationZ = 0\n angle = 0\n spin = 0\n live = false\n direction = [1, 0, 0]\n }\n\n return {\n reset: clear,\n act(host: BehaviorHost, action: string, args: unknown) {\n if (action === 'choose') {\n clear()\n oxygen = (args as { readonly oxygen: boolean }).oxygen\n } else if (action === 'puncture') {\n live = true\n if (oxygen) velocity = [-5, 0, -0.3]\n else spin = 9\n } else if (action === 'step') {\n if (!live) return\n const { dt, t } = args as { readonly dt: number; readonly t: number }\n const k = clamp(1 - (t - 0.6) / 3.4, 0, 1)\n if (!oxygen) {\n rotationZ = lerp(rotationZ, QUARTER_TURN, dt * 8)\n const y = lerp(position[1], 0.09, dt * 8)\n angle += spin * k * dt\n direction = [Math.cos(angle), 0, -Math.sin(angle)]\n position = [position[0] + direction[0] * (-0.6 * k * dt), y + direction[1] * (-0.6 * k * dt), position[2] + direction[2] * (-0.6 * k * dt)]\n return\n }\n let [vx, vy, vz] = velocity\n if (k > 0) vx -= 4 * k * dt\n const damping = Math.exp(-dt * (k > 0 ? 0.3 : 3))\n vx *= damping\n vy *= damping\n vz *= damping\n velocity = [vx, vy, vz]\n rotationZ = lerp(rotationZ, -QUARTER_TURN, dt * 10)\n position = [position[0] + vx * dt, lerp(position[1], 0.12, dt * 10) + vy * dt, position[2] + vz * dt]\n if (position[0] < WALL_X) {\n position = [WALL_X, position[1], position[2]]\n velocity = [velocity[0] * -0.3, velocity[1], velocity[2]]\n host.engine.screen.shake += 0.08\n sparkAt(host.engine, position, normalized([1, 0.5, 0]), 16, 1)\n }\n }\n },\n query(_host, name) {\n switch (name) {\n case 'live': return live\n case 'oxygen': return oxygen\n case 'position': return position\n case 'direction': return direction\n case 'speed_x': return velocity[0]\n case 'rotation_z': return rotationZ\n case 'tip': return [position[0] + direction[0] * 0.3, 0.1, position[2] + direction[2] * 0.3]\n case 'back': return [position[0] + 1.1, position[1], position[2]]\n default: throw new Error(`cylinder: unknown query ${name}`)\n }\n },\n update() {},\n }\n}\n","import type { Behavior } from '../behavior'\n\n/** The geometry of a box plate as three.js lays out its vertices: `BoxGeometry(width, height, depth, columns, rows, 1)` moved by `(shiftX, shiftY)`. */\nexport interface PlateGeometry {\n readonly width: number\n readonly height: number\n readonly columns: number\n readonly rows: number\n readonly shiftX: number\n readonly shiftY: number\n}\n\n/** The metal door of the page: 1.1 x 2.2 m, 14 x 24 segments, hinged at its left edge (W960). */\nexport const METAL_DOOR: PlateGeometry = { width: 1.1, height: 2.2, columns: 14, rows: 24, shiftX: 0.56, shiftY: 1.1 }\n\n/** Every vertex of the plate seen from the front (x, y), the front and back faces and the four thin edges, each vertex once per face it belongs to. */\nexport function plateVertices(geometry: PlateGeometry): readonly (readonly [number, number])[] {\n const { width, height, columns, rows, shiftX, shiftY } = geometry\n const xs = Array.from({ length: columns + 1 }, (_, column) => (column * width) / columns - width / 2)\n const ys = Array.from({ length: rows + 1 }, (_, row) => (row * height) / rows - height / 2)\n const vertices: [number, number][] = []\n // the sides (px, nx): two vertices, front and back, for each row; then top and bottom (py, ny): the same for each column; then the two big faces\n for (const x of [width / 2, -width / 2]) for (const y of ys) for (let depth = 0; depth < 2; depth++) vertices.push([x, y])\n for (const y of [height / 2, -height / 2]) for (const x of xs) for (let depth = 0; depth < 2; depth++) vertices.push([x, y])\n for (let face = 0; face < 2; face++) for (const y of ys) for (const x of xs) vertices.push([x, y])\n return vertices.map(([x, y]) => [x + shiftX, y + shiftY] as const)\n}\n\ninterface DentArgs {\n readonly x: number\n readonly y: number\n readonly radius: number\n readonly depth: number\n}\n\n/**\n * `dentAt` (W1002): pushes the vertices within `radius` of a hit in and draws one random number for each, so how many it draws depends on how the plate\n * is cut. The model counts them; the deformed mesh belongs to whoever draws the door.\n */\nlet doorVertices: ReturnType<typeof plateVertices> | undefined\n\nexport function createPlateBehavior(geometry: PlateGeometry = METAL_DOOR): Behavior {\n const vertices = geometry === METAL_DOOR ? (doorVertices ??= plateVertices(geometry)) : plateVertices(geometry)\n let dents: { x: number; y: number; radius: number; depth: number; vertices: number }[] = []\n return {\n reset() {\n dents = []\n },\n act(host, action, args) {\n if (action !== 'dent') return\n const { x, y, radius, depth } = args as DentArgs\n let touched = 0\n for (const [vertexX, vertexY] of vertices) {\n if (Math.hypot(vertexX - x, vertexY - y) < radius) {\n host.engine.random.next()\n touched++\n }\n }\n dents.push({ x, y, radius, depth, vertices: touched })\n },\n query(_host, name) {\n if (name === 'dents') return dents\n throw new Error(`plate: unknown query ${name}`)\n },\n update() {},\n }\n}\n","import type { Behavior } from '../behavior'\n\nconst WALKER_COUNT = 10\nconst ARRIVAL_DISTANCE = 0.05\nconst SWAY_AMPLITUDE = 0.04\nconst BOB_AMPLITUDE = 0.04\nconst STRIDE_FACTOR = 4.5\n\n/**\n * The `ph` of each walker, the offset of its sway and stride. The page drew them at load (`Math.random() * 6`) after three.js had drawn a few hundred\n * numbers for object ids, so they cannot be replayed; the three that matter (the runners with red eyes, whose trails start at the eyes) were fitted to\n * the recording until every position matched to four decimals. The others only turn a zombie nobody reads.\n */\nconst PHASES = [0, 0, 3.59629, 0, 0, 0, 2.946505, 0, 0, 0, 2.44693] as const\n\ninterface Walker {\n x: number\n z: number\n facing: number\n bob: number\n sway: number\n walk: { toX: number; toZ: number; speed: number } | undefined\n}\n\nfunction createWalkers(): Walker[] {\n return Array.from({ length: WALKER_COUNT }, () => ({ x: 0, z: 0, facing: 0, bob: 0, sway: 0, walk: undefined }))\n}\n\ninterface PlaceArgs { readonly index: number; readonly x: number; readonly z: number; readonly facing: number }\ninterface WalkArgs { readonly index: number; readonly to: readonly [number, number, number]; readonly speed: number }\ninterface MoveArgs { readonly index: number; readonly x: number }\n\n/**\n * The ten walking zombies of the page (W151, W190-197): they walk to a point at a speed, face where they go, bob with each stride and sway. An effect\n * reads where their eyes are (`point`) to start a trail from them. Phases are the page's, so the sway and bob repeat to the last digit.\n */\nexport function createWalkersBehavior(phases: readonly number[] = PHASES): Behavior {\n let walkers = createWalkers()\n return {\n reset() {\n walkers = createWalkers()\n },\n act(_host, action, args) {\n if (action === 'place') {\n const { index, x, z, facing } = args as PlaceArgs\n Object.assign(walkers[index]!, { x, z, facing })\n } else if (action === 'walk') {\n const { index, to, speed } = args as WalkArgs\n walkers[index]!.walk = { toX: to[0], toZ: to[2], speed }\n } else if (action === 'move_x') {\n const { index, x } = args as MoveArgs\n walkers[index]!.x = x\n }\n },\n query(_host, name, args) {\n const walker = walkers[args[0] as number]!\n if (name === 'x') return walker.x\n if (name === 'z') return walker.z\n if (name === 'is_walking') return walker.walk !== undefined\n if (name !== 'point') throw new Error(`walkers: unknown query ${name}`)\n const [localX, localY, localZ] = args[1] as readonly [number, number, number]\n const swayedX = Math.cos(walker.sway) * localX - Math.sin(walker.sway) * localY\n const swayedY = Math.sin(walker.sway) * localX + Math.cos(walker.sway) * localY + walker.bob\n return [Math.cos(walker.facing) * swayedX + Math.sin(walker.facing) * localZ + walker.x, swayedY, -Math.sin(walker.facing) * swayedX + Math.cos(walker.facing) * localZ + walker.z]\n },\n update(_host, dt, elapsed) {\n walkers.forEach((walker, index) => {\n walker.sway = Math.sin(elapsed * 1.3 + phases[index + 1]!) * SWAY_AMPLITUDE\n const walk = walker.walk\n if (!walk) return\n const dx = walk.toX - walker.x\n const dz = walk.toZ - walker.z\n const distance = Math.hypot(dx, dz)\n if (distance <= ARRIVAL_DISTANCE) {\n walker.walk = undefined\n return\n }\n const step = Math.min(distance, walk.speed * dt)\n walker.x += (dx / distance) * step\n walker.z += (dz / distance) * step\n walker.facing = Math.atan2(dx, dz)\n walker.bob = Math.abs(Math.cos(elapsed * walk.speed * STRIDE_FACTOR + phases[index + 1]!)) * BOB_AMPLITUDE\n })\n },\n}\n}\n","import type { Behavior } from './behavior'\nimport { createBlocksBehavior } from './behaviors/blocks'\nimport { createCarBehavior } from './behaviors/car'\nimport { createCracksBehavior } from './behaviors/cracks'\nimport { createCylinderBehavior } from './behaviors/cylinder'\nimport { createPlateBehavior } from './behaviors/plate'\nimport { createWalkersBehavior } from './behaviors/walkers'\n\n/** How to build each prop the Workshop animates by hand, by id (see `Behavior`). A game adds its own the same way. */\nexport const WORKSHOP_BEHAVIORS: Readonly<Record<string, () => Behavior>> = {\n walkers: () => createWalkersBehavior(),\n cracks: () => createCracksBehavior(),\n blocks: () => createBlocksBehavior(),\n plate: () => createPlateBehavior(),\n cylinder: () => createCylinderBehavior(),\n car: () => createCarBehavior(),\n}\n\n/** A fresh set of the props the Workshop animates by hand, by id; one per runner, since they keep state. */\nexport function createWorkshopBehaviors(factories: Readonly<Record<string, () => Behavior>> = WORKSHOP_BEHAVIORS): Record<string, Behavior> {\n return Object.fromEntries(Object.entries(factories).map(([id, make]) => [id, make()]))\n}\n","export interface Schedule {\n /** Runs `action` once `now` reaches `time`. */\n at(time: number, action: () => void): void\n /** Runs what is due at `now`, from the last one added to the first (the Lab's L1085 and the Workshop's W1280 walk the list backwards, and so does this). */\n update(now: number): void\n clear(): void\n readonly pending: number\n}\n\ninterface Entry {\n time: number\n action: () => void\n}\n\nconst NOOP = (): void => {}\n\nexport function createSchedule(): Schedule {\n const entries: Entry[] = []\n const spare: Entry[] = []\n return {\n at(time, action) {\n const entry = spare.pop() ?? { time: 0, action: NOOP }\n entry.time = time\n entry.action = action\n entries.push(entry)\n },\n update(now) {\n for (let index = entries.length - 1; index >= 0; index--) {\n const entry = entries[index]\n if (!entry || entry.time > now) continue\n const action = entry.action\n entries.splice(index, 1)\n entry.action = NOOP\n spare.push(entry)\n action()\n }\n },\n clear() {\n for (const entry of entries) {\n entry.action = NOOP\n spare.push(entry)\n }\n entries.length = 0\n },\n get pending() {\n return entries.length\n },\n }\n}\n","import type { RandomStream } from '../random'\nimport { normalized } from './vector-math'\n\n/** A value an effect computes: a number, a flag, a name or a vector. */\nexport type EffectValue = number | boolean | string | readonly number[]\n\n/** A computed field of a recipe. Plain data: `{ $: 'add', a: [1, { $: 't' }] }`. Built with the helpers in `dsl.ts`. */\nexport interface Expr {\n readonly $: string\n readonly a?: readonly unknown[]\n}\n\nexport interface ExprContext {\n readonly random: RandomStream\n readonly param: number\n readonly time: number\n readonly dt: number\n readonly index: number\n readonly variables: ReadonlyMap<string, unknown>\n readonly scaled: (count: number) => number\n readonly quality: number\n /** True on the first run after the effect was chosen, false on its repeats (the page's `fresh`). */\n readonly fresh: boolean\n /** Which way the played effect points and which surface it hit, in the world (`play`'s `direction` and `normal`). */\n readonly direction: readonly number[]\n readonly normal: readonly number[]\n readonly query: (behavior: string, name: string, args: readonly unknown[]) => unknown\n readonly camera: { readonly position: readonly number[]; readonly forward: (local: readonly number[]) => readonly number[] }\n}\n\nexport function isExpr(value: unknown): value is Expr {\n return typeof value === 'object' && value !== null && !Array.isArray(value) && '$' in value\n}\n\ntype Vector = readonly number[]\nconst asNumber = (value: unknown): number => value as number\nconst asVector = (value: unknown): Vector => value as Vector\nconst EASE_LIMIT = 1\n\nfunction ease(value: number): number {\n return value < 0 ? 0 : value > EASE_LIMIT ? EASE_LIMIT : value * value * (3 - 2 * value)\n}\n\nfunction reduceVectors(operands: readonly unknown[], combine: (accumulated: number, next: number) => number): Vector {\n const [first, ...rest] = operands.map(asVector)\n return rest.reduce<Vector>((total, next) => [combine(total[0]!, next[0]!), combine(total[1]!, next[1]!), combine(total[2]!, next[2]!)], first!)\n}\n\nfunction evaluateCall(name: string, args: readonly unknown[]): unknown {\n const [first, second, third] = args\n switch (name) {\n case 'concat': return args.map(String).join('')\n case 'add': return args.reduce<number>((total, next) => total + asNumber(next), 0)\n case 'sub': return asNumber(first) - asNumber(second)\n case 'mul': return args.reduce<number>((total, next) => total * asNumber(next), 1)\n case 'div': return asNumber(first) / asNumber(second)\n case 'mod': return asNumber(first) % asNumber(second)\n case 'neg': return -asNumber(first)\n case 'abs': return Math.abs(asNumber(first))\n case 'sqrt': return Math.sqrt(asNumber(first))\n case 'sin': return Math.sin(asNumber(first))\n case 'cos': return Math.cos(asNumber(first))\n case 'atan2': return Math.atan2(asNumber(first), asNumber(second))\n case 'min': return Math.min(...args.map(asNumber))\n case 'max': return Math.max(...args.map(asNumber))\n case 'floor': return Math.floor(asNumber(first))\n case 'ceil': return Math.ceil(asNumber(first))\n case 'round': return Math.round(asNumber(first))\n case 'clamp': return asNumber(first) < asNumber(second) ? asNumber(second) : asNumber(first) > asNumber(third) ? asNumber(third) : asNumber(first)\n case 'lerp': return asNumber(first) + (asNumber(second) - asNumber(first)) * asNumber(third)\n case 'ease': return ease(asNumber(first))\n case 'lt': return asNumber(first) < asNumber(second)\n case 'gt': return asNumber(first) > asNumber(second)\n case 'le': return asNumber(first) <= asNumber(second)\n case 'ge': return asNumber(first) >= asNumber(second)\n case 'eq': return first === second\n case 'ne': return first !== second\n case 'not': return !first\n case 'vadd': return reduceVectors(args, (total, next) => total + next)\n case 'vsub': return reduceVectors(args, (total, next) => total - next)\n case 'vscale': return asVector(first).map((component) => component * asNumber(second))\n case 'vnorm': return normalized(asVector(first) as readonly [number, number, number])\n case 'vneg': return asVector(first).map((component) => -component)\n case 'vlerp': return asVector(first).map((component, axis) => component + (asVector(second)[axis]! - component) * asNumber(third))\n case 'vx': return asVector(first)[0]\n case 'vy': return asVector(first)[1]\n case 'vz': return asVector(first)[2]\n case 'vdist': {\n const dx = asVector(first)[0]! - asVector(second)[0]!\n const dy = asVector(first)[1]! - asVector(second)[1]!\n const dz = asVector(first)[2]! - asVector(second)[2]!\n return Math.sqrt(dx * dx + dy * dy + dz * dz)\n }\n case 'with': return asVector(first).map((component, axis) => (axis === asNumber(second) ? asNumber(third) : component))\n default: throw new Error(`unknown expression: ${name}`)\n }\n}\n\n/** Resolves every `Expr` inside `value` (arrays and plain objects are walked in order, so the draws follow the order the page wrote its fields). */\nexport function resolve(value: unknown, context: ExprContext): unknown {\n if (Array.isArray(value)) return value.map((entry) => resolve(entry, context))\n if (typeof value !== 'object' || value === null) return value\n if (!isExpr(value)) return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, resolve(entry, context)]))\n const args = value.a ?? []\n switch (value.$) {\n case 'rnd': return context.random.between(asNumber(resolve(args[0], context)), asNumber(resolve(args[1], context)))\n case 'rand': return context.random.next()\n case 'param': return (args[0] as readonly unknown[])[context.param]\n case 'paramIndex': return context.param\n case 't': return context.time\n case 'dt': return context.dt\n case 'index': return context.index\n case 'quality': return context.quality\n case 'fresh': return context.fresh\n case 'direction': return context.direction\n case 'normal': return context.normal\n case 'scaled': return context.scaled(asNumber(resolve(args[0], context)))\n case 'var': return context.variables.get(args[0] as string)\n case 'at': return (resolve(args[0], context) as readonly unknown[])[asNumber(resolve(args[1], context))]\n case 'query': return context.query(args[0] as string, args[1] as string, args.slice(2).map((entry) => resolve(entry, context)))\n case 'cameraPosition': return context.camera.position\n case 'cameraForward': return context.camera.forward(asVector(resolve(args[0], context)))\n case 'let': return resolve(args[2], { ...context, variables: new Map([...context.variables, [args[0] as string, resolve(args[1], context)]]) })\n case 'ite': return resolve(resolve(args[0], context) ? args[1] : args[2], context)\n case 'and': return args.reduce<unknown>((result, next) => (result ? resolve(next, context) : result), true)\n case 'or': return args.reduce<unknown>((result, next) => (result ? result : resolve(next, context)), false)\n default: return evaluateCall(value.$, args.map((entry) => resolve(entry, context)))\n }\n}\n","import type { WorkshopEngine } from '../../workshop/engine'\nimport type { SpriteSpawn, Vec3Tuple } from '../../workshop/types'\nimport { normalized, plus } from '../vector-math'\n\nconst UP: Vec3Tuple = [0, 1, 0]\nconst ZERO: Vec3Tuple = [0, 0, 0]\nconst FULL_TURN_DRAWN = 6.283\n\nexport interface FireEmitOptions {\n readonly wind?: Vec3Tuple\n readonly dir?: Vec3Tuple\n readonly smoke?: number\n readonly smokeColor?: number\n readonly smokeAlpha?: number\n readonly noSmoke?: boolean\n}\n\nfunction scaledSizes(range: readonly [number, number], factor: number): readonly [number, number] {\n return [range[0] * Math.sqrt(factor), range[1] * Math.sqrt(factor)]\n}\n\n/** `fireEmit` (W399-405): flames, a hot core, embers and smoke for one frame, from a point and a radius, scaled by `factor`. */\nexport function fireEmit(engine: WorkshopEngine, key: string, dt: number, centre: Vec3Tuple, radius: number, factor: number, options: FireEmitOptions = {}): void {\n const wind = options.wind ?? ZERO\n const sqrtFactor = Math.sqrt(factor)\n engine.emit(`${key}f`, 'add', 90 * factor, dt, () => ({\n p: centre, r: radius, flat: true, dir: options.dir ?? UP, cone: 0.35, sp: [0.4, 1.4], v: wind, life: [0.35, 0.85], size: scaledSizes([0.22, 0.5], factor), grow: 0.35, c0: 0xffd27a, c1: 0x8a1a05, a: 0.85, fi: 0.15, g: -2.2,\n }))\n engine.emit(`${key}c`, 'add', 30 * factor, dt, () => ({\n p: centre, r: radius * 0.6, flat: true, dir: UP, cone: 0.3, sp: [0.3, 0.8], life: [0.2, 0.4], size: scaledSizes([0.25, 0.4], factor), grow: 0.6, c0: 0xfff2c0, c1: 0xff8a20, a: 0.8, fi: 0.1, g: -1,\n }))\n if (engine.quality < 2) {\n engine.emit(`${key}e`, 'streak', 10 * factor, dt, () => {\n const jitter = plus(wind, engine.random.between(-0.4, 0.4), 0, engine.random.between(-0.4, 0.4))\n return { p: centre, box: [radius, 0, radius], dir: UP, cone: 0.5, sp: [1.5, 3.5], v: jitter, life: [0.8, 1.8], len: 0.03, c0: 0xffb050, c1: 0x601000, g: -0.6, drag: 0.4 }\n })\n }\n if (options.noSmoke) return\n engine.emit(`${key}s`, 'normal', (options.smoke || 14) * factor, dt, () => ({\n p: plus(centre, 0, 0.8 * sqrtFactor, 0), r: radius * 0.7, dir: UP, cone: 0.35, sp: [0.6, 1.4], v: wind, drag: 0.15, life: [2.5, 4.5], size: scaledSizes([0.4, 0.8], factor), grow: 4, c0: options.smokeColor || 0x2a2522, c1: 0x121318, a: options.smokeAlpha || 0.42, fi: 0.2, g: -0.25,\n }))\n}\n\nfunction ringOfDust(engine: WorkshopEngine, point: Vec3Tuple, scale: number): void {\n for (let index = 0; index < engine.scaled(44); index++) {\n const angle = engine.random.next() * FULL_TURN_DRAWN\n const spec: SpriteSpawn = {\n p: plus(point, Math.cos(angle) * 0.5, 0.15, Math.sin(angle) * 0.5), dir: [Math.cos(angle), 0.08, Math.sin(angle)], cone: 0.15, sp: [4, 9 * scale], drag: 2.4, life: [1.4, 2.6], size: [0.5, 0.9], grow: 4, c0: 0x5d574d, c1: 0x2a2724, a: 0.5, fi: 0.1,\n }\n engine.spawn('normal', spec)\n }\n}\n\n/** `explode` (W406-416): the Workshop's blast, scaled by `scale`, that the Lab's profiles later replaced. `cameraPosition` turns the second ring to face the viewer. */\nexport function explode(engine: WorkshopEngine, point: Vec3Tuple, scale: number, cameraPosition: Vec3Tuple, chipColor?: number): void {\n engine.flash([plus(point, 0, 1, 0), 0xffa040, 14 * scale, 0.5, 30, 'boom'])\n engine.screen.shake += 0.7 * scale\n engine.screen.white += 0.18 * scale\n engine.burst('add', 55 * scale, { p: point, r: 0.3 * scale, sp: [2, 7 * scale], drag: 4, life: [0.25, 0.6], size: [0.7 * scale, 1.4 * scale], grow: 1.7, c0: 0xfff0b0, c1: 0x9a2a08, fi: 0.04 })\n engine.burst('add', 28 * scale, { p: plus(point, 0, 0.3, 0), r: 0.3, dir: UP, cone: 0.7, sp: [2, 5 * scale], drag: 1.5, life: [0.6, 1.1], size: [0.6 * scale, 1 * scale], grow: 1.3, c0: 0xffc060, c1: 0x5a1205, g: -2, fi: 0.05 })\n engine.ring([plus(point, 0, 0.05, 0), 0.3, 7 * scale, 0.55, 0xffd8a0])\n const toCamera = normalized([cameraPosition[0] - point[0], cameraPosition[1] - point[1], cameraPosition[2] - point[2]])\n engine.ring([plus(point, 0, 1, 0), 0.3, 5 * scale, 0.35, 0xfff0d0, toCamera])\n engine.burst('normal', 32 * scale, { p: plus(point, 0, 0.6, 0), r: 0.8 * scale, dir: UP, cone: 1.1, sp: [1, 3], drag: 1.2, life: [3, 6], size: [1 * scale, 1.8 * scale], grow: 2.6, c0: 0x2b2724, c1: 0x0f1013, a: 0.6, fi: 0.18, g: -0.35 })\n ringOfDust(engine, point, scale)\n if (engine.quality < 2) engine.burst('streak', 44 * scale, { p: point, dir: UP, cone: 1.45, sp: [10, 26], life: [0.3, 0.7], len: 0.03, c0: 0xffc070, c1: 0x802000, g: 9.8, gnd: 2 })\n for (let index = 0; index < engine.scaled(16 * scale); index++) {\n engine.debrisPiece({ p: point, dir: UP, cone: 1.2, sp: [3, 9], size: [0.12, 0.06, 0.1], k: [0.6, 1.6], c: chipColor || 0x2a2622, life: 9 })\n }\n engine.decal([point[0], engine.context.groundAt(point[0], point[2]), point[2], 3 * scale, 0x0a0a0a, { grow: 0.15, life: 60 }])\n}\n","import type { WorkshopEngine } from '../../workshop/engine'\nimport type { Vec3Tuple } from '../../workshop/types'\nimport { normalized, plus, plusScaled, scaledBy } from '../vector-math'\n\nconst FULL_TURN_DRAWN = 6.283\n\nexport const WEAPON_NAMES = ['glock', 'ak', 'r870', 'barrett'] as const\nexport type WeaponName = (typeof WEAPON_NAMES)[number]\n\n/** Per weapon: flash size, petals, light, smoke puffs and length of the petals (W417). */\nconst WEAPON_SIZES: Readonly<Record<WeaponName, readonly [number, number, number, number, number]>> = {\n glock: [0.35, 4, 3, 3, 0.25],\n ak: [0.6, 5, 5, 5, 0.45],\n r870: [0.95, 8, 7, 10, 0.6],\n barrett: [1.45, 6, 11, 14, 0.5],\n}\n\nfunction barrettBrake(engine: WorkshopEngine, tip: Vec3Tuple, direction: Vec3Tuple): void {\n const side = normalized([direction[2], 0, -direction[0]])\n for (const sign of [-1, 1]) {\n const sideways = scaledBy(side, sign)\n for (let index = 0; index < engine.scaled(8); index++) {\n engine.spawn('add', { p: plusScaled(tip, side, sign * 0.06), dir: sideways, cone: 0.25, sp: [2, 5], drag: 6, life: 0.09, size: [0.25, 0.4], c0: 0xffc070, c1: 0xff6010, fi: 0 })\n }\n engine.burst('normal', 10, { p: tip, dir: normalized(plus(sideways, 0, 0.2, 0)), cone: 0.3, sp: [1.5, 3], drag: 2, life: [1.2, 2], size: [0.15, 0.25], grow: 5, c0: 0x8a8a86, a: 0.3, g: -0.2 })\n }\n for (let index = 0; index < engine.scaled(26); index++) {\n const angle = engine.random.next() * FULL_TURN_DRAWN\n engine.spawn('normal', { p: [tip[0] + Math.cos(angle) * 0.3, 0.05, tip[2] + Math.sin(angle) * 0.3], dir: [Math.cos(angle), 0.15, Math.sin(angle)], cone: 0.2, sp: [1.5, 3.5], drag: 2, life: [1, 1.8], size: [0.2, 0.35], grow: 3, c0: 0x5a554b, a: 0.4 })\n }\n}\n\n/** `muzzle` (W417-429): flash core, petals along the barrel and smoke, scaled by weapon; the Barrett also vents sideways and lifts the dust. */\nexport function muzzle(engine: WorkshopEngine, weapon: WeaponName, tip: Vec3Tuple, direction: Vec3Tuple): void {\n const [flashSize, petals, light, smoke, length] = WEAPON_SIZES[weapon]\n engine.burst('add', 3, { p: tip, size: [flashSize * 0.45, flashSize * 0.75], life: 0.055, c0: 0xfff4d0, fi: 0 })\n for (let index = 0; index < engine.scaled(petals * 2); index++) {\n engine.spawn('add', { p: plusScaled(tip, direction, engine.random.between(0, length)), r: 0.03, size: [flashSize * 0.22, flashSize * 0.38], life: 0.06, c0: 0xffb050, c1: 0xff7020, fi: 0 })\n }\n engine.burst('streak', petals, { p: tip, dir: direction, cone: 0.55, sp: [6, 12], life: 0.05, len: 0.025, c0: 0xffd080 })\n if (weapon === 'barrett') barrettBrake(engine, tip, direction)\n engine.burst('normal', smoke, { p: tip, dir: direction, cone: 0.45, sp: [0.4, 1.6], drag: 2.2, life: [1, 2.2], size: [0.06, 0.14], grow: 6, c0: 0x8a8a86, c1: 0x55565a, a: 0.22, g: -0.25, fi: 0.05 })\n engine.flash([tip, 0xffc070, light, 0.05, 10])\n}\n\nexport type CasingType = 'shell' | '50' | ''\n\n/** `casing` (W430): a brass case, or a red shell, thrown out of the port. */\nexport function casing(engine: WorkshopEngine, port: Vec3Tuple, type: CasingType): void {\n const size = type === 'shell' ? ([0.03, 0.03, 0.07] as const) : type === '50' ? ([0.024, 0.024, 0.13] as const) : ([0.014, 0.014, 0.05] as const)\n engine.debrisPiece({ p: port, dir: normalized([0.8, 0.6, 0.2]), cone: 0.3, sp: [1.6, 2.6], size, c: type === 'shell' ? 0xb5281e : 0xc9a24a, spin: 20, bounce: 0.45, life: 20 })\n}\n","import type { WorkshopEngine } from '../workshop/engine'\nimport type { Vec3Tuple } from '../workshop/types'\nimport { bloodHit, impact } from './builders/blood'\nimport type { ImpactMaterial } from './builders/blood'\nimport { explode, fireEmit } from './builders/fire'\nimport type { FireEmitOptions } from './builders/fire'\nimport { chips, rustAt, sparkAt } from './builders/shared'\nimport { casing, muzzle } from './builders/weapon'\nimport type { CasingType, WeaponName } from './builders/weapon'\n\nexport interface MacroContext {\n readonly engine: WorkshopEngine\n readonly cameraPosition: Vec3Tuple\n readonly dt: number\n}\n\ntype MacroArgs = readonly unknown[]\n\n/** The helpers the page's recipes share (`bloodHit`, `impact`, `fireEmit`, `explode`, `muzzle`, `casing`, `chips`, `sparkAt`, `rustAt`), called by name from a step. */\nexport const MACROS: Readonly<Record<string, (context: MacroContext, args: MacroArgs) => void>> = {\n blood_hit: ({ engine }, [point, direction, age, scale]) => bloodHit(engine, point as Vec3Tuple, direction as Vec3Tuple, age as number, scale as number | undefined),\n impact: ({ engine }, [material, point, normal, scale, age]) => impact(engine, material as ImpactMaterial, point as Vec3Tuple, normal as Vec3Tuple, scale as number | undefined, age as number | undefined),\n fire_emit: ({ engine, dt }, [key, centre, radius, factor, options]) => fireEmit(engine, key as string, dt, centre as Vec3Tuple, radius as number, factor as number, options as FireEmitOptions | undefined),\n explode: ({ engine, cameraPosition }, [point, scale, chipColor]) => explode(engine, point as Vec3Tuple, scale as number, cameraPosition, chipColor as number | undefined),\n muzzle: ({ engine }, [weapon, tip, direction]) => muzzle(engine, weapon as WeaponName, tip as Vec3Tuple, direction as Vec3Tuple),\n casing: ({ engine }, [port, type]) => casing(engine, port as Vec3Tuple, type as CasingType),\n chips: ({ engine }, [point, direction, count, spread]) => chips(engine, point as Vec3Tuple, direction as Vec3Tuple, count as number, spread as number | undefined),\n spark_at: ({ engine }, [point, direction, count, spread]) => sparkAt(engine, point as Vec3Tuple, direction as Vec3Tuple, count as number, spread as number | undefined),\n rust_at: ({ engine }, [point, count]) => rustAt(engine, point as Vec3Tuple, count as number),\n}\n","import { createSchedule } from '../schedule'\nimport type { ScreenState } from '../types'\nimport type { Placement, WorkshopEngine } from '../workshop/engine'\nimport type { DebrisSpawn, SpriteSpawn, Vec3Tuple } from '../workshop/types'\nimport type { Behavior, BehaviorHost } from './behavior'\nimport type { ExprContext } from './expression'\nimport { resolve } from './expression'\nimport { MACROS } from './macros'\nimport type { CueRequest, EffectDef, EffectStep, ScreenKey } from './types'\n\nconst DEFAULT_DIRECTION: Vec3Tuple = [0, 0, -1]\nconst DEFAULT_NORMAL: Vec3Tuple = [0, 1, 0]\nconst SCREEN_FIELDS: Readonly<Record<ScreenKey, keyof ScreenState>> = {\n shake: 'shake', white: 'white', red: 'red', dirL: 'directionLeft', dirR: 'directionRight', chroma: 'chroma', desat: 'desat', gas: 'gas', beat: 'beat',\n}\n\n/** What an effect reads off the view: where it is and which way the camera's own axes point. The Workshop's bench also moves it (`camera` steps). */\nexport interface EffectView {\n readonly position: Vec3Tuple\n forward(local: readonly number[]): Vec3Tuple\n goTo?(eye: Vec3Tuple, target: Vec3Tuple): void\n place?(eye: Vec3Tuple, target: Vec3Tuple): void\n}\n\nexport interface InstanceDeps {\n readonly engine: WorkshopEngine\n readonly view: EffectView\n /** The prop models the recipe can call, by id (see `Behavior`). */\n readonly behavior: (id: string) => Behavior\n /** Every behaviour made so far, to age them every frame. */\n readonly behaviors: () => Iterable<Behavior>\n readonly onCue?: (cue: CueRequest) => void\n}\n\n/** Where and how a played effect sits in the world. */\nexport interface InstanceSetup {\n readonly placement?: Placement\n /** Which way the effect points in the world (`direction` in a recipe). */\n readonly direction?: Vec3Tuple\n /** The surface it hit (`normal` in a recipe). */\n readonly normal?: Vec3Tuple\n}\n\n/**\n * One run of one effect (W1259-1295): `begin` runs the recipe's `setup` and `timeline`, `step` runs the timers that came due (from the last added to the\n * first) and the `tick`, once per frame. Step by step it draws random numbers where the page did and in the same order.\n */\nexport class EffectInstance {\n readonly variables = new Map<string, unknown>()\n def: EffectDef | undefined\n param = 0\n /** True until the first run of a chosen effect is over: a repeat of the loop is not fresh (`f.fresh`). */\n fresh = false\n time = 0\n /** Seconds since the effect was chosen: unlike `time`, a restart of the loop does not reset it (`el`). */\n elapsed = 0\n setup: InstanceSetup = {}\n private dt = 0\n private index = 0\n private readonly schedule = createSchedule()\n private readonly host: BehaviorHost\n private readonly context: ExprContext\n\n constructor(private readonly deps: InstanceDeps) {\n const instance = (): EffectInstance => this\n this.host = {\n engine: deps.engine,\n camera: deps.view,\n variables: this.variables,\n get param() { return instance().param },\n get time() { return instance().time },\n after: (seconds, action) => this.schedule.at(seconds, action),\n }\n this.context = {\n get random() { return deps.engine.random },\n get param() { return instance().param },\n get time() { return instance().time },\n get dt() { return instance().dt },\n get index() { return instance().index },\n variables: this.variables,\n scaled: (count) => deps.engine.scaled(count),\n query: (behavior, name, args) => deps.behavior(behavior).query(this.host, name, args),\n get quality() { return deps.engine.quality },\n get fresh() { return instance().fresh },\n get direction() { return instance().setup.direction ?? DEFAULT_DIRECTION },\n get normal() { return instance().setup.normal ?? DEFAULT_NORMAL },\n camera: { get position() { return deps.view.position }, forward: (local) => deps.view.forward(local) },\n }\n }\n\n /** The effect starts (`select` + `startLoop`): the recipe's `setup` and `timeline` run now. */\n begin(def: EffectDef, param: number | undefined, setup: InstanceSetup = {}): void {\n this.def = def\n this.param = param ?? def.params?.default ?? 0\n this.elapsed = 0\n this.fresh = true\n this.setup = setup\n this.restart()\n }\n\n /** The loop starts over (`startLoop`): timers, variables and props go back to the start. */\n restart(): void {\n const def = this.def\n if (!def) return\n this.schedule.clear()\n this.variables.clear()\n for (const behavior of this.deps.behaviors()) behavior.reset(this.host)\n this.time = 0\n this.within(() => {\n this.execute(def.setup ?? [])\n this.execute(def.timeline)\n })\n this.fresh = false\n }\n\n /** The effect's part of one frame: the timers due, then the tick, then the props. The pools are aged by whoever owns the engine. */\n step(dt: number): void {\n const def = this.def\n this.dt = dt\n if (def) {\n this.time += dt\n this.elapsed += dt\n this.within(() => {\n this.schedule.update(this.time)\n this.execute(def.tick ?? [])\n })\n }\n for (const behavior of this.deps.behaviors()) behavior.update(this.host, dt, this.elapsed)\n }\n\n /** True once a looping effect has run its seconds, or a one-shot has nothing left to fire. */\n get finished(): boolean {\n const def = this.def\n if (!def) return true\n if (def.loop && def.loop > 0) return this.time >= def.loop\n return false\n }\n\n /** The recipe's own camera shake on top of the screen's (`panico` trembles the aim). */\n get tremor(): number {\n return (this.variables.get('tremor') as number | undefined) ?? 0\n }\n\n private within(action: () => void): void {\n const engine = this.deps.engine\n const previous = engine.placement\n engine.placement = this.setup.placement\n try {\n action()\n } finally {\n engine.placement = previous\n }\n }\n\n private value(expression: unknown): unknown {\n return resolve(expression, this.context)\n }\n\n execute(steps: readonly EffectStep[]): void {\n for (const step of steps) this.run(step)\n }\n\n private run(step: EffectStep): void {\n const engine = this.deps.engine\n switch (step.op) {\n case 'burst': return engine.burst(step.system, this.value(step.count) as number, this.value(step.spawn) as SpriteSpawn)\n case 'spawn': return engine.spawn(step.system, this.value(step.spawn) as SpriteSpawn)\n case 'emit': return engine.emit(step.key, step.system, this.value(step.rate) as number, this.dt, () => this.value(step.spawn) as SpriteSpawn)\n case 'debris': return engine.debrisPiece(this.value(step.spawn) as DebrisSpawn)\n case 'decal': return engine.decal(this.value(step.args) as Parameters<WorkshopEngine['decal']>[0])\n case 'ring': return engine.ring(this.value(step.args) as Parameters<WorkshopEngine['ring']>[0])\n case 'flash': return engine.flash(this.value(step.args) as Parameters<WorkshopEngine['flash']>[0])\n case 'lens': return engine.lensBlob(...(this.value(step.args) as [number, number, number, string, number]))\n case 'screen': return this.changeScreen(step)\n case 'fire_light': return engine.fireLight(step.slot, this.value(step.point) as Vec3Tuple, this.value(step.intensity) as number, this.value(step.time) as number)\n case 'fire_light_off': engine.lights.fire[step.slot]!.intensity = 0; return\n case 'cue': return this.cue(step)\n case 'call': return MACROS[step.macro]!({ engine, cameraPosition: this.deps.view.position, dt: this.dt }, this.value(step.args) as readonly unknown[])\n case 'at': return this.scheduleSteps(step)\n case 'when': return this.execute(this.value(step.if) ? step.steps : (step.otherwise ?? []))\n case 'repeat': return this.repeat(step)\n case 'set': { this.variables.set(step.name, this.value(step.value)); return }\n case 'append': { this.variables.set(step.name, [...((this.variables.get(step.name) as unknown[] | undefined) ?? []), this.value(step.value)]); return }\n case 'camera': return this.moveView(step)\n case 'post': { Object.assign(engine.post, this.value(step.values)); return }\n case 'behavior': return this.deps.behavior(step.id).act(this.host, step.action, this.value(step.args))\n }\n }\n\n private moveView(step: Extract<EffectStep, { op: 'camera' }>): void {\n const eye = this.value(step.eye) as Vec3Tuple\n const target = this.value(step.target) as Vec3Tuple\n if (step.instant) this.deps.view.place?.(eye, target)\n else this.deps.view.goTo?.(eye, target)\n }\n\n private changeScreen(step: Extract<EffectStep, { op: 'screen' }>): void {\n const screen = this.deps.engine.screen\n for (const [key, expression] of Object.entries(step.add ?? {})) screen[SCREEN_FIELDS[key as ScreenKey]] += this.value(expression) as number\n for (const [key, expression] of Object.entries(step.set ?? {})) screen[SCREEN_FIELDS[key as ScreenKey]] = this.value(expression) as number\n for (const [key, expression] of Object.entries(step.max ?? {})) screen[SCREEN_FIELDS[key as ScreenKey]] = Math.max(screen[SCREEN_FIELDS[key as ScreenKey]], this.value(expression) as number)\n }\n\n private cue(step: Extract<EffectStep, { op: 'cue' }>): void {\n const args = this.value(step.args)\n for (let draw = 0; draw < (step.draws ?? 0); draw++) this.deps.engine.random.next()\n this.deps.onCue?.({ cue: step.cue, args, time: this.time })\n }\n\n private scheduleSteps(step: Extract<EffectStep, { op: 'at' }>): void {\n const captured = this.index\n this.schedule.at(this.value(step.time) as number, () => {\n const previous = this.index\n this.index = captured\n this.execute(step.steps)\n this.index = previous\n })\n }\n\n private repeat(step: Extract<EffectStep, { op: 'repeat' }>): void {\n const previous = this.index\n const count = this.value(step.count) as number\n for (let index = 0; index < count; index++) {\n this.index = index\n this.execute(step.steps)\n }\n this.index = previous\n }\n}\n","import { lehmer } from '../random'\nimport type { RandomStream } from '../random'\nimport type { Follow } from '../types'\nimport type { Placement, WorkshopEngine } from '../workshop/engine'\nimport type { Vec3Tuple } from '../workshop/types'\nimport type { Behavior } from './behavior'\nimport { WORKSHOP_BEHAVIORS } from './behaviors'\nimport { EffectInstance } from './instance'\nimport type { EffectView } from './instance'\nimport type { CueRequest, EffectDef } from './types'\n\nconst ORIGIN: Vec3Tuple = [0, 0, 0]\nconst DEFAULT_HEADING: Vec3Tuple = [0, 0, -1]\nconst DEFAULT_MAX_INSTANCES = 48\n/** A recipe with no end of its own (`loop: 0`: a campfire, a fire in a building) plays this long unless it is stopped. */\nconst ENDLESS_SECONDS = 60\n\nexport interface PlayOptions {\n /** Which way the effect points in the world (the muzzle's barrel, the spray of a jet). */\n readonly direction?: Vec3Tuple\n /** The surface it hit. */\n readonly normal?: Vec3Tuple\n /** Repeats the same visual: a LAN client draws the same numbers from the same seed. */\n readonly seed?: number\n /** The recipe's parameter (age, material, weapon...). */\n readonly param?: number | string\n /** Moves with a thing: the effect sits at its position plus the point it was played at. */\n readonly follow?: Follow\n /** Stops after this many seconds (default: the recipe's loop). */\n readonly seconds?: number\n}\n\nexport interface EffectPlayerOptions {\n readonly engine: WorkshopEngine\n readonly effects: Readonly<Record<string, EffectDef>>\n readonly behaviors?: Readonly<Record<string, () => Behavior>>\n /** Where the viewer is, for the recipes that read it. */\n readonly view: EffectView\n /** A stream for a seed (default `lehmer`). */\n readonly streamFor?: (seed: number) => RandomStream\n readonly maxInstances?: number\n readonly onCue?: (cue: CueRequest) => void\n}\n\nexport interface EffectPlayer {\n /** Plays `id` at `point`; `null` when no recipe has that id or the cap of live effects is reached. Returns a handle for `stop`. */\n play(id: string, point: Vec3Tuple, options?: PlayOptions): number | null\n stop(handle: number): void\n /** Runs the timers and ticks of every live effect (the engine's pools are aged by its owner). */\n update(dt: number): void\n clear(): void\n readonly active: number\n has(id: string): boolean\n ids(): readonly string[]\n}\n\ninterface Playing {\n readonly handle: number\n readonly instance: EffectInstance\n readonly random: RandomStream | undefined\n readonly follow: Follow | undefined\n readonly origin: Vec3Tuple\n readonly anchor: Vec3Tuple\n readonly yaw: number\n readonly seconds: number\n}\n\nconst positionOf = (follow: Follow): Vec3Tuple => {\n const source = 'position' in follow ? follow.position : follow\n return [source.x, source.y, source.z]\n}\n\n/** A parameter given by its option id (`'ak'`, `'concrete'`) as the index of the option; a number is taken as it is. */\nfunction paramIndex(def: EffectDef, param: number | string | undefined): number | undefined {\n if (typeof param !== 'string') return param\n const index = def.params?.options.indexOf(param) ?? -1\n return index >= 0 ? index : undefined\n}\n\n/** The turn about the vertical axis that takes the recipe's `heading` onto `direction` (three.js turns by `x' = cos x + sin z`). */\nfunction yawOf(heading: Vec3Tuple, direction: Vec3Tuple | undefined): number {\n if (!direction || (direction[0] === 0 && direction[2] === 0)) return 0\n return Math.atan2(direction[0], direction[2]) - Math.atan2(heading[0], heading[2])\n}\n\n/**\n * Plays the Workshop's recipes anywhere in the world, many at once, each with its own clock, variables and props, a place and a way it faces. They all\n * spawn into the one engine, so the pools stay shared and bounded. This is what `VfxWorld.play` and the host's `EffectSink` use.\n */\nexport function createEffectPlayer(options: EffectPlayerOptions): EffectPlayer {\n const { engine, view } = options\n const factories = options.behaviors ?? WORKSHOP_BEHAVIORS\n const streamOf = options.streamFor ?? ((seed: number) => lehmer(seed))\n const limit = options.maxInstances ?? DEFAULT_MAX_INSTANCES\n const playing: Playing[] = []\n let next = 0\n\n function makeInstance(): EffectInstance {\n const made = new Map<string, Behavior>()\n return new EffectInstance({\n engine,\n view,\n behavior: (id) => {\n let behavior = made.get(id)\n if (!behavior) {\n const make = factories[id]\n if (!make) throw new Error(`no behaviour ${id}`)\n behavior = make()\n made.set(id, behavior)\n }\n return behavior\n },\n behaviors: () => made.values(),\n ...(options.onCue ? { onCue: options.onCue } : {}),\n })\n }\n\n function placementOf(entry: Playing): Placement {\n const origin = entry.follow ? [entry.origin[0] + positionOf(entry.follow)[0], entry.origin[1] + positionOf(entry.follow)[1], entry.origin[2] + positionOf(entry.follow)[2]] as const : entry.origin\n return { origin, anchor: entry.anchor, yaw: entry.yaw }\n }\n\n /** Runs `action` drawing from the effect's own stream, if it has one. */\n function drawing(entry: Playing, action: () => void): void {\n const context = engine.context\n const shared = context.random\n if (entry.random) context.random = entry.random\n try {\n action()\n } finally {\n context.random = shared\n }\n }\n\n return {\n play(id, point, playOptions = {}) {\n const def = options.effects[id]\n if (!def || playing.length >= limit) return null\n const heading = def.heading ?? DEFAULT_HEADING\n const instance = makeInstance()\n const entry: Playing = {\n handle: ++next,\n instance,\n random: playOptions.seed !== undefined ? streamOf(playOptions.seed) : undefined,\n follow: playOptions.follow,\n origin: point,\n anchor: def.anchor ?? ORIGIN,\n yaw: yawOf(heading, playOptions.direction),\n seconds: playOptions.seconds ?? (def.loop && def.loop > 0 ? def.loop : ENDLESS_SECONDS),\n }\n playing.push(entry)\n drawing(entry, () =>\n instance.begin(def, paramIndex(def, playOptions.param), {\n placement: placementOf(entry),\n ...(playOptions.direction ? { direction: playOptions.direction } : {}),\n ...(playOptions.normal ? { normal: playOptions.normal } : {}),\n }),\n )\n return entry.handle\n },\n stop(handle) {\n const index = playing.findIndex((entry) => entry.handle === handle)\n if (index >= 0) playing.splice(index, 1)\n },\n update(dt) {\n for (let index = playing.length - 1; index >= 0; index--) {\n const entry = playing[index]!\n const instance = entry.instance\n instance.setup = { ...instance.setup, placement: placementOf(entry) }\n drawing(entry, () => instance.step(dt))\n if (instance.time >= entry.seconds) playing.splice(index, 1)\n }\n },\n clear() {\n playing.length = 0\n },\n get active() {\n return playing.length\n },\n has: (id) => id in options.effects,\n ids: () => Object.keys(options.effects),\n }\n}\n","import type { Rgb } from './color'\nimport { hexToRgb, srgbChannelToLinear } from './color'\n\n/** One of the 4 blast lights (L349-365). */\nexport interface BlastLight {\n x: number\n y: number\n z: number\n distance: number\n peak: number\n life: number\n t: number\n color: Rgb\n /** The level before the flash layer is applied. */\n level: number\n intensity: number\n}\n\n/** One of the 2 fire lights: they go to the two largest fires. */\nexport interface FireLight {\n x: number\n y: number\n z: number\n distance: number\n intensity: number\n}\n\n/** The glow the puff shader reads (`uGlowPos`, `uGlowCol`, `uGlowI`): where the brightest light is, in linear colour. */\nexport interface LightGlow {\n x: number\n y: number\n z: number\n color: [number, number, number]\n intensity: number\n}\n\nexport interface LightPool {\n readonly blast: BlastLight[]\n readonly fire: FireLight[]\n readonly glow: LightGlow\n /** Which blast light the next explosion takes (the Lab's `xlIdx`). */\n cursor: number\n}\n\nexport const BLAST_LIGHTS = 4\nexport const FIRE_LIGHTS = 2\nconst DEFAULT_BLAST_COLOR = 0xffa050\nconst FIRE_GLOW_COLOR: Rgb = [1, 0.42, 0.12]\nconst PEAK_HOLD = 0.05\n\nexport function createLightPool(): LightPool {\n return {\n blast: Array.from({ length: BLAST_LIGHTS }, () => ({ x: 0, y: -50, z: 0, distance: 30, peak: 0, life: 1, t: 9, color: hexToRgb(DEFAULT_BLAST_COLOR), level: 0, intensity: 0 })),\n fire: Array.from({ length: FIRE_LIGHTS }, () => ({ x: 0, y: -50, z: 0, distance: 16, intensity: 0 })),\n glow: { x: 0, y: 0, z: 0, color: [1, 0.5, 0.2], intensity: 0 },\n cursor: 0,\n }\n}\n\nexport function resetLightPool(pool: LightPool): void {\n for (const light of pool.blast) {\n light.t = 9\n light.level = 0\n light.intensity = 0\n }\n pool.cursor = 0\n}\n\nexport interface BlastLightSpec {\n readonly x: number\n readonly y: number\n readonly z: number\n readonly peak: number\n readonly distance: number\n readonly life: number\n readonly color: number\n}\n\n/** `boomLight` (L357-358): the next blast light, round robin. */\nexport function lightBlast(pool: LightPool, spec: BlastLightSpec): void {\n const light = pool.blast[pool.cursor++ % pool.blast.length]!\n light.x = spec.x\n light.y = spec.y\n light.z = spec.z\n light.distance = spec.distance\n light.peak = spec.peak\n light.life = spec.life\n light.t = 0\n light.color = hexToRgb(spec.color)\n}\n\nexport interface FireForLight {\n readonly x: number\n readonly y: number\n readonly z: number\n readonly r: number\n readonly fade: number\n}\n\nexport interface LightStep {\n readonly dt: number\n /** 1 while the flash layer is on, 0 while it is off (the Lab multiplies by it). */\n readonly flashLayer: number\n readonly clock: number\n readonly fires: readonly FireForLight[]\n}\n\nfunction largestFire(fires: readonly FireForLight[], skip: FireForLight | null): FireForLight | null {\n let best: FireForLight | null = null\n for (const fire of fires) {\n if (fire === skip) continue\n if (best === null || fire.r > best.r) best = fire\n }\n return best\n}\n\nfunction updateBlastLights(pool: LightPool, step: LightStep): BlastLight | null {\n let brightest: BlastLight | null = null\n for (const light of pool.blast) {\n light.t += step.dt\n const age = light.t / light.life\n const level = age < 1 ? light.peak * (light.t < PEAK_HOLD ? 1 : Math.pow(1 - age, 2.2)) * (0.85 + 0.15 * Math.sin(light.t * 60)) : 0\n light.level = level\n light.intensity = level * step.flashLayer\n if (level > (brightest?.level ?? 0)) brightest = light\n }\n return brightest\n}\n\n/** `updateLights` (L350-364): blast lights fade by their curve, the two fire lights follow the two largest fires, and the glow follows the brightest light. */\nexport function updateLights(pool: LightPool, step: LightStep): void {\n const brightest = updateBlastLights(pool, step)\n const brightness = brightest?.level ?? 0\n const first = largestFire(step.fires, null)\n const second = largestFire(step.fires, first)\n const largest = [first, second]\n for (let index = 0; index < FIRE_LIGHTS; index++) {\n const fire = largest[index]\n const light = pool.fire[index]!\n if (!fire) {\n light.intensity = 0\n continue\n }\n light.x = fire.x\n light.y = fire.y + Math.max(0.8, fire.r * 0.6)\n light.z = fire.z\n light.intensity = (1.4 + fire.r * 0.6) * (0.75 + 0.25 * Math.sin(step.clock * 17 + index * 3) + 0.1 * Math.sin(step.clock * 41)) * fire.fade\n light.distance = 10 + fire.r * 4\n }\n if (brightest && brightness > 0.05) {\n pool.glow.x = brightest.x\n pool.glow.y = brightest.y\n pool.glow.z = brightest.z\n pool.glow.color[0] = srgbChannelToLinear(brightest.color[0])\n pool.glow.color[1] = srgbChannelToLinear(brightest.color[1])\n pool.glow.color[2] = srgbChannelToLinear(brightest.color[2])\n pool.glow.intensity = brightness * 0.06 * step.flashLayer\n } else if (first) {\n pool.glow.x = first.x\n pool.glow.y = first.y + 0.5\n pool.glow.z = first.z\n pool.glow.color[0] = FIRE_GLOW_COLOR[0]\n pool.glow.color[1] = FIRE_GLOW_COLOR[1]\n pool.glow.color[2] = FIRE_GLOW_COLOR[2]\n pool.glow.intensity = (0.35 + first.r * 0.12) * first.fade\n } else pool.glow.intensity = 0\n}\n","import type { LodBands } from './types'\n\n/** The game's bands: distance minus the visual radius, 15 / 40 / 80 m, scales 1 / .5 / .2 / .1 (G68203, the Workshop's quality bands). The default. */\nexport const LOD_GAME: LodBands = { edges: [15, 40, 80], scales: [1, 0.5, 0.2, 0.1], radiusFactor: 1 }\n\n/** The Lab's own bands (L481): distance minus three visual radii, 20 / 45 / 85 m, scales 1 / .5 / .2 / .08. Used on the bench and in the goldens. */\nexport const LOD_LAB: LodBands = { edges: [20, 45, 85], scales: [1, 0.5, 0.2, 0.08], radiusFactor: 3 }\n\n/** The scale of an effect `distance` metres from the camera, for an effect whose fireball has `visualRadius`. */\nexport function lodScale(bands: LodBands, distance: number, visualRadius: number): number {\n const effective = distance - visualRadius * bands.radiusFactor\n if (effective < bands.edges[0]) return bands.scales[0]\n if (effective < bands.edges[1]) return bands.scales[1]\n if (effective < bands.edges[2]) return bands.scales[2]\n return bands.scales[3]\n}\n","import type { GroundField } from './ground'\nimport type { RandomStream } from './random'\n\n/** What an explosion leaves on the ground, as data: the renderer builds the meshes (L443-476). */\nexport interface ScorchMark {\n readonly kind: 'scorch'\n readonly x: number\n readonly z: number\n readonly radius: number\n /** Which of the three scorch textures. */\n readonly variant: number\n readonly lift: number\n readonly rotation: number\n}\n\nexport interface DirtMark {\n readonly kind: 'dirt'\n readonly x: number\n readonly z: number\n readonly radius: number\n readonly lift: number\n readonly rotation: number\n}\n\n/** A piece of broken asphalt on the rim of a crater. */\nexport interface RockMark {\n readonly kind: 'rock'\n readonly x: number\n readonly y: number\n readonly z: number\n readonly scale: readonly [number, number, number]\n readonly rotation: readonly [number, number, number]\n readonly plate: boolean\n readonly color: number\n}\n\nexport interface BloodMark {\n readonly kind: 'blood'\n readonly x: number\n readonly y: number\n readonly z: number\n readonly scaleX: number\n readonly scaleZ: number\n readonly rotation: number\n}\n\nexport type Mark = ScorchMark | DirtMark | RockMark | BloodMark\n\nexport type MarkEvent = { readonly type: 'add'; readonly mark: Mark } | { readonly type: 'clear' }\n\nexport interface MarkStore {\n readonly entries: Mark[]\n /** Goes up on every mark added or cleared. */\n readonly version: number\n readonly bloodCount: number\n /** The counter that lifts each decal a hair higher than the last (the Lab's `decalY`). */\n lift: number\n add(mark: Mark): void\n clear(): void\n /** Tells a renderer the moment a mark is made (the ground it conforms to is the ground of that moment) or taken away. */\n subscribe(listener: (event: MarkEvent) => void): () => void\n}\n\nconst BLOOD_LIMIT = 450\nconst SCORCH_VARIANTS = 3\nconst RIM_COLORS = [0x2b2c2f, 0x3a3836] as const\n\nexport function createMarkStore(): MarkStore {\n const entries: Mark[] = []\n let version = 0\n let bloodCount = 0\n const listeners = new Set<(event: MarkEvent) => void>()\n return {\n entries,\n get version() {\n return version\n },\n get bloodCount() {\n return bloodCount\n },\n lift: 0,\n add(mark) {\n entries.push(mark)\n if (mark.kind === 'blood') bloodCount++\n version++\n for (const listener of listeners) listener({ type: 'add', mark })\n },\n clear() {\n entries.length = 0\n bloodCount = 0\n this.lift = 0\n version++\n for (const listener of listeners) listener({ type: 'clear' })\n },\n subscribe(listener) {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n },\n }\n}\n\nfunction nextLift(store: MarkStore): number {\n return store.lift++ % 20\n}\n\n/** `scorch` (L458): a burn mark; the texture and the turn are drawn in that order. */\nexport function addScorch(store: MarkStore, random: RandomStream, x: number, z: number, radius: number): void {\n const variant = Math.floor(random.next() * SCORCH_VARIANTS)\n const lift = 0.03 + nextLift(store) * 0.001\n const rotation = random.next() * 6\n store.add({ kind: 'scorch', x, z, radius, variant, lift, rotation })\n}\n\nfunction addRimRocks(store: MarkStore, ground: GroundField | undefined, random: RandomStream, x: number, z: number, radius: number): void {\n const count = Math.round(6 + radius * 7)\n for (let index = 0; index < count; index++) {\n const angle = (index / count) * Math.PI * 2 + random.between(-0.15, 0.15)\n const distance = radius * random.between(0.95, 1.3)\n const size = random.between(0.12, 0.26) * Math.min(1.6, 0.6 + radius * 0.4)\n const px = x + Math.cos(angle) * distance\n const pz = z + Math.sin(angle) * distance\n const plate = random.next() < 0.6\n const color = RIM_COLORS[random.next() < 0.5 ? 0 : 1]\n const tiltX = random.between(-0.5, 0.5)\n const tiltZ = random.between(-0.6, 0.2)\n store.add({\n kind: 'rock',\n x: px,\n y: (ground?.heightAt(px, pz) ?? 0) + size * 0.12,\n z: pz,\n scale: [size * 2, size * 0.35, size * 1.4],\n rotation: [tiltX, angle, tiltZ],\n plate,\n color,\n })\n }\n}\n\n/** `crater` (L460-468): digs the ground (always), then, when marks are on, lays the dirt, and broken asphalt around the rim. */\nexport function addCrater(store: MarkStore, ground: GroundField | undefined, random: RandomStream, spec: { x: number; z: number; radius: number; depth: number; marks: boolean }): void {\n ground?.dent(random, spec.x, spec.z, spec.radius, spec.depth)\n if (!spec.marks) return\n const rotation = random.next() * 6\n store.add({ kind: 'dirt', x: spec.x, z: spec.z, radius: spec.radius * 1.02, lift: 0.045, rotation })\n addRimRocks(store, ground, random, spec.x, spec.z, spec.radius)\n}\n\n/** `bloodSplat` (L471-475). */\nexport function addBloodSplat(store: MarkStore, ground: GroundField | undefined, random: RandomStream, spec: { x: number; z: number; radius: number }): void {\n if (store.bloodCount > BLOOD_LIMIT) return\n const y = (ground?.heightAt(spec.x, spec.z) ?? 0) + 0.03 + nextLift(store) * 0.0008\n const scaleX = spec.radius * random.between(1.6, 2.4)\n const scaleZ = spec.radius * random.between(1.2, 2)\n store.add({ kind: 'blood', x: spec.x, y, z: spec.z, scaleX, scaleZ, rotation: random.next() * 6 })\n}\n","import { OrderedPool } from '../pool'\nimport type { Ring } from './puffs'\n\nexport function createRingPool(capacity: number): OrderedPool<Ring> {\n return new OrderedPool<Ring>(capacity, () => ({ x: 0, z: 0, y0: 0, rise: 0, rk: 0, R0: 0, R1: 0, r: 0, roll: 0, t: 0, life: 0 }))\n}\n\nfunction ageOrKeep(ring: Ring, dt: number): boolean {\n ring.t += dt\n return ring.t <= ring.life + 1\n}\n\n/** `updateRings` (L736): a ring is kept one second past the life of its puffs. */\nexport function updateRings(pool: OrderedPool<Ring>, dt: number): void {\n pool.compact(ageOrKeep, dt)\n}\n\nexport function spawnRing(pool: OrderedPool<Ring>, spec: Omit<Ring, 't'>): Ring {\n const ring = pool.acquire()\n ring.x = spec.x\n ring.z = spec.z\n ring.y0 = spec.y0\n ring.rise = spec.rise\n ring.rk = spec.rk\n ring.R0 = spec.R0\n ring.R1 = spec.R1\n ring.r = spec.r\n ring.roll = spec.roll\n ring.t = 0\n ring.life = spec.life\n return ring\n}\n","import { OrderedPool } from '../pool'\nimport type { Puff } from './puffs'\n\n/** A shock wave: an air shell that grows to the lethal radius in 120 ms, and a dust ring on the ground (L319-348). The last block is what the renderer reads. */\nexport interface Shock {\n x: number\n y: number\n z: number\n Rs: number\n Rg: number\n t: number\n dur: number\n shellScale: number\n shellAlpha: number\n ringScale: number\n ringAlpha: number\n}\n\nconst DEFAULT_DURATION = 0.3\nconst SHELL_GROWTH_SECONDS = 0.12\nconst SHELL_FADE_SECONDS = 0.16\nconst PUSH_BAND = 0.8\nconst PUSH_CEILING = 3\nconst PUSH_AGE_LIMIT = 2\n\nexport function createShockPool(capacity: number): OrderedPool<Shock> {\n return new OrderedPool<Shock>(capacity, () => ({ x: 0, y: 0, z: 0, Rs: 0, Rg: 0, t: 0, dur: DEFAULT_DURATION, shellScale: 0, shellAlpha: 0, ringScale: 0, ringAlpha: 0 }))\n}\n\nexport function spawnShock(pool: OrderedPool<Shock>, spec: { x: number; y: number; z: number; shellRadius: number; groundRadius: number; duration: number }): Shock {\n const shock = pool.acquire()\n shock.x = spec.x\n shock.y = spec.y\n shock.z = spec.z\n shock.Rs = spec.shellRadius\n shock.Rg = spec.groundRadius\n shock.t = 0\n shock.dur = spec.duration || DEFAULT_DURATION\n shock.shellScale = 0\n shock.shellAlpha = 0\n shock.ringScale = 0\n shock.ringAlpha = 0\n return shock\n}\n\nfunction pushPuffs(puffs: readonly Puff[], shock: Shock, reach: number, dt: number, age: number): void {\n for (const puff of puffs) {\n if (puff.ring || puff.t > PUSH_AGE_LIMIT) continue\n const dx = puff.x - shock.x\n const dz = puff.z - shock.z\n const distance = Math.hypot(dx, dz) + 0.001\n if (Math.abs(distance - reach) < PUSH_BAND && puff.y < PUSH_CEILING) {\n const strength = 4 * dt * (1 - age)\n puff.vx += (dx / distance) * strength * 6\n puff.vz += (dz / distance) * strength * 6\n }\n }\n}\n\n/** `updateShocks` (L335-348), walking the list backwards like the Lab: the fronts push the smoke and dust they meet. */\nexport function updateShocks(pool: OrderedPool<Shock>, puffs: readonly Puff[], dt: number): void {\n for (let index = pool.items.length - 1; index >= 0; index--) {\n const shock = pool.items[index]!\n shock.t += dt\n const age = shock.t / shock.dur\n const shellEase = 1 - Math.pow(1 - Math.min(1, shock.t / SHELL_GROWTH_SECONDS), 3)\n shock.shellScale = Math.max(0.01, shock.Rs * shellEase)\n shock.shellAlpha = Math.max(0, 1 - shock.t / SHELL_FADE_SECONDS) * 0.9\n const ringEase = 1 - Math.pow(1 - Math.min(1, age), 2.2)\n shock.ringScale = Math.max(0.01, shock.Rg * ringEase)\n shock.ringAlpha = Math.max(0, 1 - age) * 0.85\n pushPuffs(puffs, shock, shock.Rg * ringEase, dt, age)\n if (age >= 1) pool.removeAt(index)\n }\n}\n","import { hexToLinearRgb, hexToRgb } from './color'\nimport { SMOKE_COLORS } from './constants'\nimport type { VfxCore } from './core'\nimport { lightBlast } from './lights'\nimport { lodScale } from './lod'\nimport { addCrater, addScorch } from './marks'\nimport { spawnRing } from './pools/rings'\nimport { spawnShock } from './pools/shocks'\nimport type { RandomStream, MutableVec3 } from './random'\nimport { addFire } from './emitters/fire'\nimport { addChunk, addGlow, addPuff, addSpark } from './spawn'\nimport type { ExplosionDef, MushroomSpec } from './types'\nimport { addScaled, createVec3, distanceBetween, normalize } from './vector'\n\n/** Where and how one explosion goes off. */\nexport interface ExplosionCall {\n readonly x: number\n readonly y: number\n readonly z: number\n /** Where it is aimed (a rocket's direction, a charge's face). */\n readonly direction?: MutableVec3\n /** The stream the whole effect draws from: the world's in `shared` mode, the event's own in `per_event`. */\n readonly random: RandomStream\n readonly handle: number\n}\n\ninterface Context {\n readonly core: VfxCore\n readonly def: ExplosionDef\n readonly call: ExplosionCall\n readonly random: RandomStream\n readonly x: number\n readonly y: number\n readonly z: number\n /** Fireball radius. */\n readonly size: number\n /** LOD scale of this explosion. */\n readonly scale: number\n readonly direction: MutableVec3 | undefined\n readonly smokeColor: readonly [number, number, number]\n /** Where the camera is (for the screen flash), distance to the blast. */\n readonly cameraDistance: number\n}\n\nconst DEFAULT_LIGHT = 40\nconst DEFAULT_LIGHT_LIFE = 0.45\nconst DEFAULT_LIGHT_COLOR = 0xffa050\nconst DEFAULT_FLASH_INTENSITY = 6\nconst DEFAULT_SHOCK_DURATION = 0.32\nconst DEFAULT_CHUNK_COLOR = 0x47423d\nconst ROCK_COLORS = [0x2b2c2f, 0x4a3a2c] as const\nconst SCRATCH = createVec3()\n\nfunction pick<T>(random: RandomStream, items: readonly T[]): T {\n return items[Math.floor(random.next() * items.length)]!\n}\n\nfunction composeFlash(context: Context): void {\n const { core, def, x, y, z, size } = context\n if (!core.layers.flash) return\n const flash = def.flash\n const light = { x, y: y + size * 0.6, z, peak: (flash.light || DEFAULT_LIGHT) * 0.7, distance: (def.injury || 10) * 2.4, life: flash.life || DEFAULT_LIGHT_LIFE, color: flash.color || DEFAULT_LIGHT_COLOR }\n core.trace?.light?.(light)\n lightBlast(core.lights, light)\n core.spawned.lights++\n const blinding = flash.blinding === true\n addGlow(core, { x, y: y + size * 0.4, z, s: size * 1.4, s1: size * 3.2, i: flash.intensity || DEFAULT_FLASH_INTENSITY, life: 0.22, color: [1, 0.92, 0.75], curve: 2.5 })\n if (!blinding) addGlow(core, { x, y: y + size * 0.5, z, s: size * 2.2, s1: size * 3.6, i: 2.2, life: def.fuel ? 1.4 : 0.6, color: [1, 0.45, 0.15], curve: 1.6 })\n if (core.reducedMotion) return\n const distance = context.cameraDistance\n const screen = core.screen\n if (distance < 15) {\n screen.flash = Math.max(screen.flash, (blinding ? 1 : 0.32) * (1 - distance / 15))\n screen.flashDecay = blinding ? 0.55 : 7\n }\n if (blinding && distance < 40) {\n screen.flash = Math.max(screen.flash, 1)\n screen.flashDecay = 0.5\n }\n}\n\nfunction composeFireballs(context: Context): void {\n const { core, def, random, x, y, z, size, scale, direction } = context\n if (!core.layers.fire || !def.fireballs) return\n const count = Math.round(def.fireballs * Math.max(scale, 0.35))\n const fuel = def.fuel === true\n const heat = def.heat ?? 1\n for (let index = 0; index < count; index++) {\n const d = random.direction(SCRATCH)\n d.y = fuel ? Math.abs(d.y) * 0.5 + 0.05 : Math.abs(d.y) * 0.8 + 0.1\n if (direction) normalize(addScaled(d, direction, def.directionBias ?? 1.2))\n const speed = size * random.between(4.5, 7.5) * (fuel ? 0.75 : 1)\n const s1 = size * random.between(0.42, 0.62)\n const sg = size * random.between(0.12, 0.22)\n const life = random.between(def.smoke.life * 0.7, def.smoke.life)\n const h0 = random.between(1.05, 1.35) * heat\n const hl = fuel ? random.between(0.8, 1.4) * (def.heatLifeScale ?? 1) : random.between(0.22, 0.42)\n addPuff(core, random, {\n x: x + d.x * size * 0.15, y: y + size * 0.25 + d.y * size * 0.1, z: z + d.z * size * 0.15,\n vx: d.x * speed, vy: d.y * speed + (fuel ? size * 1.4 : size * 0.6), vz: d.z * speed,\n s0: size * 0.22, s1, sg, gk: 9, life, h0, hl, drag: fuel ? 4.2 : 6.5, buoy: fuel ? 2.2 : 1.2, color: context.smokeColor, kind: 0,\n })\n }\n}\n\nfunction composeSmoke(context: Context): void {\n const { core, def, random, x, y, z, size, scale } = context\n if (!core.layers.smoke || !def.smoke.count) return\n const count = Math.round(def.smoke.count * Math.max(scale, 0.3))\n for (let index = 0; index < count; index++) {\n const d = random.direction(SCRATCH)\n d.y = Math.abs(d.y)\n const vy = size * random.between(0.6, 2.2)\n const s1 = size * random.between(0.55, 0.85)\n const sg = size * random.between(0.18, 0.3)\n const life = random.between(def.smoke.life * 0.7, def.smoke.life * 1.1)\n addPuff(core, random, {\n x: x + d.x * size * 0.6, y: y + size * 0.4 + d.y * size * 0.5, z: z + d.z * size * 0.6,\n vx: d.x * size * 2.4, vy, vz: d.z * size * 2.4, s0: size * 0.25, s1, sg, gk: 4, life, h0: 0.32, hl: 0.25, drag: 3, buoy: 0.45, color: context.smokeColor, kind: def.smoke.kind ?? 0,\n })\n }\n}\n\nfunction composeShock(context: Context): void {\n const { core, def, x, y, z, size, scale } = context\n if (!core.layers.shock || def.shock === false || !(scale > 0.15)) return\n const duration = def.shock ? def.shock.duration : DEFAULT_SHOCK_DURATION\n const spec = { x, y: y + size * 0.3, z, shellRadius: def.lethal * 0.9, groundRadius: def.injury * 0.6, duration }\n core.trace?.shock?.(spec)\n spawnShock(core.shocks, spec)\n}\n\nfunction composeDust(context: Context): void {\n const { core, def, random, x, z, size, scale } = context\n if (!core.layers.smoke || !def.dust.count || !(scale > 0.15)) return\n const count = Math.round(def.dust.count * scale)\n const color = hexToLinearRgb(def.dust.color)\n const cone = def.cone\n for (let index = 0; index < count; index++) {\n let angle = random.next() * Math.PI * 2\n if (cone) angle = coneAngle(context) + random.between(-cone.half, cone.half)\n const speed = random.between(0.6, 1) * def.injury * (cone ? 1.6 : 0.9)\n const vy = random.between(0.2, 1.2)\n const s1 = size * random.between(0.5, 0.8)\n const sg = size * random.between(0.2, 0.35)\n const life = random.between(2.2, 4)\n addPuff(core, random, {\n x: x + Math.cos(angle) * 0.3, y: 0.15, z: z + Math.sin(angle) * 0.3,\n vx: Math.cos(angle) * speed, vy, vz: Math.sin(angle) * speed, s0: size * 0.2, s1, sg, gk: 5, life, drag: 3.6, buoy: 0.2, color, kind: 1,\n })\n }\n}\n\n/** The angle of the cone's axis: the direction it is aimed at, else the one in its profile. */\nfunction coneAngle(context: Context): number {\n const cone = context.def.cone\n if (!cone) return 0\n return context.direction ? Math.atan2(context.direction.z, context.direction.x) : cone.angle\n}\n\nfunction sparkDirection(context: Context): MutableVec3 {\n const { def, random, direction } = context\n const cone = def.cone\n if (cone) {\n const angle = coneAngle(context) + random.between(-cone.half, cone.half)\n SCRATCH.x = Math.cos(angle)\n SCRATCH.y = random.between(-0.05, 0.25)\n SCRATCH.z = Math.sin(angle)\n return SCRATCH\n }\n const d = random.direction(SCRATCH)\n d.y = Math.abs(d.y) * 0.9 + 0.05\n if (direction) normalize(addScaled(d, direction, def.directionBias ?? 1))\n return d\n}\n\nfunction composeEmbers(context: Context): void {\n const { core, def, random, x, y, z, size, scale } = context\n const embers = def.shrapnel.embers\n if (!embers) return\n for (let index = 0; index < embers * scale; index++) {\n const d = random.direction(SCRATCH)\n d.y = Math.abs(d.y)\n const vx = d.x * random.between(2, 6)\n const vy = random.between(3, 8)\n const vz = d.z * random.between(2, 6)\n addSpark(core, { x, y: y + size * 0.5, z, vx, vy, vz, life: random.between(1.2, 2.6), w: 0.04, st: 0.02, kind: 4, drag: 1.6, g: 2.5 })\n }\n}\n\nfunction composeShrapnel(context: Context): void {\n const { core, def, random, x, y, z, size, scale } = context\n if (!core.layers.shrapnel || !def.shrapnel.count) return\n const count = Math.round(def.shrapnel.count * scale)\n for (let index = 0; index < count; index++) {\n const d = sparkDirection(context)\n const speed = def.shrapnel.speed * random.between(0.45, 1)\n addSpark(core, {\n x: x + d.x * 0.1, y: y + size * 0.25, z: z + d.z * 0.1, vx: d.x * speed, vy: d.y * speed, vz: d.z * speed,\n life: random.between(0.35, 1.1), w: def.shrapnel.width ?? 0.035, st: 0.03, kind: def.shrapnel.kind, drag: def.shrapnel.kind === 1 ? 0.15 : 0.9,\n })\n }\n composeEmbers(context)\n}\n\nfunction composeDebris(context: Context): void {\n const { core, def, random, x, y, z, scale, direction } = context\n const chunks = def.chunks\n if (!core.layers.shrapnel || !chunks.count || scale < 0.5) return\n const count = Math.round(chunks.count * (scale >= 1 ? 1 : 0.5))\n for (let index = 0; index < count; index++) {\n const d = random.direction(SCRATCH)\n d.y = Math.abs(d.y) * 0.9 + 0.2\n if (direction) normalize(addScaled(d, direction, 0.8))\n const speed = random.between(4, 11) * (chunks.speed ?? 1)\n const s = random.between(0.07, 0.2) * (chunks.size ?? 1)\n const plate = chunks.plate ? random.next() < 0.6 : random.next() < 0.15\n const sy = plate ? s * 0.3 : s * random.between(0.6, 1)\n const color = hexToRgb(chunks.colors.length > 0 ? pick(random, chunks.colors) : DEFAULT_CHUNK_COLOR)\n const hot = random.next() < chunks.hot\n addChunk(core, random, {\n x: x + d.x * 0.2, y: y + 0.2, z: z + d.z * 0.2, vx: d.x * speed, vy: d.y * speed + 2, vz: d.z * speed,\n sx: plate ? s * 1.6 : s, sy, sz: plate ? s * 1.1 : s, color, plate, hot,\n })\n }\n}\n\nfunction composeRimDebris(context: Context): void {\n const { core, random, x, z } = context\n const radius = context.def.mark.crater ?? 0\n if (!core.layers.shrapnel) return\n const speedScale = context.def.chunks.speed ?? 1\n const count = Math.round(4 + radius * 4)\n for (let index = 0; index < count; index++) {\n const angle = random.next() * Math.PI * 2\n const speed = random.between(3, 8) * speedScale\n const s = random.between(0.12, 0.28)\n const vy = random.between(4, 10)\n const color = hexToRgb(random.next() < 0.6 ? ROCK_COLORS[0] : ROCK_COLORS[1])\n addChunk(core, random, {\n x: x + Math.cos(angle) * 0.3, y: 0.1, z: z + Math.sin(angle) * 0.3, vx: Math.cos(angle) * speed * 0.6, vy, vz: Math.sin(angle) * speed * 0.6,\n sx: s * 1.6, sy: s * 0.35, sz: s * 1.2, color, plate: true,\n })\n }\n}\n\nfunction composeMushroom(context: Context, spec: MushroomSpec): void {\n const { core, random, x, y, z } = context\n core.trace?.mushroom?.(x, y, z, spec)\n const color = spec.color !== undefined ? hexToLinearRgb(spec.color) : SMOKE_COLORS.black\n const ring = spawnRing(core.rings, {\n x, z, y0: y + spec.height * 0.15, rise: spec.height, rk: spec.riseRate ?? 0.55, R0: spec.radius * 0.3, R1: spec.radius, r: spec.radius * 0.42, roll: spec.roll ?? 2.4, life: spec.life || 7,\n })\n const lod = core.clock.lod\n const count = Math.round((spec.count || 40) * Math.max(lod, 0.4))\n for (let index = 0; index < count; index++) {\n const s1 = spec.radius * random.between(0.35, 0.5)\n const life = random.between(ring.life * 0.75, ring.life)\n const h0 = random.between(1, 1.25)\n const hl = random.between(0.9, 1.6)\n const puff = addPuff(core, random, { x, y, z, s0: spec.radius * 0.2, s1, sg: spec.radius * 0.06, gk: 3, life, h0, hl, color, kind: 0 })\n puff.ring = ring\n puff.ph = random.next() * Math.PI * 2\n puff.th = random.next() * Math.PI * 2\n }\n const stemCount = Math.round((spec.stem || 18) * Math.max(lod, 0.4))\n for (let index = 0; index < stemCount; index++) {\n const spread = random.next() * 0.6\n const px = x + random.between(-0.4, 0.4) * spec.radius * 0.2\n const pz = z + random.between(-0.4, 0.4) * spec.radius * 0.2\n const vx = random.between(-0.3, 0.3)\n const vy = spec.height * random.between(0.6, 1.4) * (1 - spread)\n const vz = random.between(-0.3, 0.3)\n const s1 = spec.radius * random.between(0.22, 0.32)\n const life = random.between(3, 5)\n const h0 = random.between(0.9, 1.2)\n const hl = random.between(0.6, 1.1)\n addPuff(core, random, { x: px, y: y + 0.3, z: pz, vx, vy, vz, s0: spec.radius * 0.12, s1, sg: spec.radius * 0.05, gk: 4, life, h0, hl, drag: 1.2, buoy: 0.5, color })\n }\n}\n\nfunction composeAfterEffects(context: Context): void {\n const { core, def, random, x, y, z } = context\n const mark = def.mark\n if (mark.crater && mark.dent && y < 1.2) {\n core.emit({ type: 'crater', x, z, radius: mark.crater, depth: mark.dent })\n core.trace?.crater?.(x, z, mark.crater, mark.dent)\n addCrater(core.marks, core.ground, random, { x, z, radius: mark.crater, depth: mark.dent, marks: core.layers.mark })\n if (core.layers.shrapnel) composeRimDebris(context)\n }\n if (mark.scorch && y < 1.2 && core.layers.mark) {\n core.trace?.scorch?.(x, z, mark.scorch)\n addScorch(core.marks, random, x, z, mark.scorch)\n }\n if (def.mushroom && core.layers.fire) composeMushroom(context, def.mushroom)\n if (def.residualFires && core.layers.fire) {\n for (const fire of def.residualFires) {\n addFire(core, random, {\n x: x + (fire.dx ?? 0), y: 0, z: z + (fire.dz ?? 0), r: fire.radius, life: fire.life, rate: fire.rate, smoke: fire.smoke, startRadius: fire.startRadius, grow: fire.grow,\n color: fire.smokeColor !== undefined ? hexToLinearRgb(fire.smokeColor) : undefined,\n }, context.call.handle)\n }\n }\n}\n\nfunction emitRequests(context: Context): void {\n const { core, def, x, y, z } = context\n const point = [x, y, z] as const\n core.emit({\n type: 'blast_front', explosion: def.id, point, glassRadius: Math.min(def.injury, 20) * 0.85, speed: 340, lethal: def.lethal, injury: def.injury, push: def.push,\n hotParts: def.hotParts === true, blinding: def.flash.blinding === true, cone: def.cone ? { angle: coneAngle(context), half: def.cone.half, length: def.cone.length } : undefined,\n })\n if (def.ignite) core.emit({ type: 'ignite', point, radius: def.ignite })\n}\n\n/**\n * The Lab's layer compositor (`blast`, L642-714), layer by layer in the Lab's order and drawing from the stream in the Lab's order: flash, fireball,\n * its own smoke, shock wave, dust, shrapnel, debris, then marks, mushroom and lingering fires, and last what the host does (glass, characters).\n */\nexport function composeExplosion(core: VfxCore, def: ExplosionDef, call: ExplosionCall): number {\n const cameraDistance = distanceBetween(core.camera.x, core.camera.y, core.camera.z, call.x, call.y, call.z)\n const forced = core.clock.forcedLod\n const bandScale = forced ?? lodScale(core.bands, cameraDistance, def.visualRadius)\n const capScale = core.areaCap ? core.areaCap.register(core.clock.time, call.x, call.z) : 1\n const scale = forced ?? bandScale * capScale\n core.clock.lod = scale\n core.clock.lastBlast = core.clock.time\n core.spawned.puffs = core.spawned.sparks = core.spawned.chunks = core.spawned.lights = 0\n const context: Context = {\n core, def, call, random: call.random, x: call.x, y: call.y, z: call.z, size: def.visualRadius, scale, direction: call.direction,\n smokeColor: hexToLinearRgb(def.smoke.color), cameraDistance,\n }\n composeFlash(context)\n core.screen.shake = Math.max(core.screen.shake, (def.shake || 1) / (1 + cameraDistance * 0.12))\n composeFireballs(context)\n composeSmoke(context)\n composeShock(context)\n composeDust(context)\n composeShrapnel(context)\n composeDebris(context)\n composeAfterEffects(context)\n emitRequests(context)\n return scale\n}\n","import type { VfxCore } from '../core'\nimport type { Chunk } from './chunks'\nimport { addBloodSplat } from '../marks'\nimport { SMOKE_COLORS } from '../constants'\nimport { addPuff, addSpark } from '../spawn'\n\nconst RESTITUTION = -0.32\nconst GROUND_FRICTION = 0.5\nconst REST_SPEED = 0.6\nconst REST_SLIDE = 0.5\nconst TRAIL_SECONDS = 1.8\nconst TRAIL_STEP = 0.025\nconst QUARTER_TURN = Math.PI / 2\n\nfunction bounce(core: VfxCore, chunk: Chunk, floor: number): void {\n chunk.y = floor\n if (chunk.blood && !chunk.splat && core.layers.mark) {\n chunk.splat = true\n addBloodSplat(core.marks, core.ground, chunk.rng, { x: chunk.x, z: chunk.z, radius: chunk.rng.between(0.06, 0.13) })\n }\n chunk.vy *= RESTITUTION\n chunk.vx *= GROUND_FRICTION\n chunk.vz *= GROUND_FRICTION\n chunk.ax *= GROUND_FRICTION\n chunk.ay *= GROUND_FRICTION\n chunk.az *= GROUND_FRICTION\n if (Math.abs(chunk.vy) < REST_SPEED && Math.hypot(chunk.vx, chunk.vz) < REST_SLIDE) {\n chunk.rest = true\n chunk.rx = Math.round(chunk.rx / QUARTER_TURN) * QUARTER_TURN\n chunk.rz = Math.round(chunk.rz / QUARTER_TURN) * QUARTER_TURN\n }\n}\n\nfunction leaveTrail(core: VfxCore, chunk: Chunk, dt: number): void {\n chunk.trail -= dt\n if (chunk.trail > 0) return\n chunk.trail = TRAIL_STEP\n const random = chunk.rng\n addSpark(core, {\n x: chunk.x, y: chunk.y, z: chunk.z, vx: chunk.vx * 0.15, vy: chunk.vy * 0.15, vz: chunk.vz * 0.15,\n life: random.between(0.25, 0.45), w: 0.05 + chunk.sx * 0.2, st: 0.05, kind: 0, g: 0, drag: 1,\n })\n if (random.next() < 0.25) {\n addPuff(core, random, { x: chunk.x, y: chunk.y, z: chunk.z, s0: 0.04, s1: 0.14 + chunk.sx * 0.3, sg: 0.15, life: random.between(1, 1.6), drag: 2, buoy: 0.6, color: SMOKE_COLORS.grey, kind: 0 })\n }\n}\n\n/** `updateChunks` (L383-398): gravity, spin, a bounce that kills speed, a rest pose on a quarter turn, and the ember trail of a hot piece. */\nexport function updateChunks(core: VfxCore, dt: number): void {\n for (const chunk of core.chunks.items) {\n chunk.t += dt\n if (chunk.rest) continue\n chunk.vy -= core.gravity * dt\n chunk.x += chunk.vx * dt\n chunk.y += chunk.vy * dt\n chunk.z += chunk.vz * dt\n chunk.rx += chunk.ax * dt\n chunk.ry += chunk.ay * dt\n chunk.rz += chunk.az * dt\n const floor = chunk.sy * 0.45 + core.groundAt(chunk.x, chunk.z)\n if (chunk.y < floor) bounce(core, chunk, floor)\n if (chunk.hot && chunk.t < TRAIL_SECONDS && core.layers.smoke) leaveTrail(core, chunk, dt)\n }\n}\n","import type { ScreenState } from './types'\n\nconst DECAY = { shake: 5, white: 7, red: 2.2, side: 3, chroma: 3, desat: 0.7, gasSeconds: 5, beat: 6 } as const\nexport const INITIAL_FLASH_DECAY = 6\n\nexport function createScreenState(): ScreenState {\n return { shake: 0, flash: 0, flashDecay: INITIAL_FLASH_DECAY, white: 0, red: 0, directionLeft: 0, directionRight: 0, chroma: 0, desat: 0, gas: 0, beat: 0 }\n}\n\nexport function resetScreenState(state: ScreenState): void {\n Object.assign(state, createScreenState())\n}\n\n/**\n * The decays of the Lab (L1087, L1092: flash by its own rate, shake by 5 per second) and the Workshop (W354-355: white, red, the two sides, chroma,\n * desaturation, gas and the heartbeat), with the same constants.\n */\nexport function updateScreen(state: ScreenState, dt: number): void {\n state.flash *= Math.exp(-state.flashDecay * dt)\n state.shake *= Math.exp(-DECAY.shake * dt)\n state.white *= Math.exp(-DECAY.white * dt)\n state.red *= Math.exp(-DECAY.red * dt)\n state.directionLeft *= Math.exp(-DECAY.side * dt)\n state.directionRight *= Math.exp(-DECAY.side * dt)\n state.chroma *= Math.exp(-DECAY.chroma * dt)\n state.desat *= Math.exp(-DECAY.desat * dt)\n state.gas = Math.max(0, state.gas - dt / DECAY.gasSeconds)\n state.beat *= Math.exp(-DECAY.beat * dt)\n}\n","import type { SpriteKindDef } from './kinds'\n\nconst clamp = (v: number, lo: number, hi: number): number => (v < lo ? lo : v > hi ? hi : v)\nconst lerp = (a: number, b: number, t: number): number => a + (b - a) * t\n\nfunction rgb(hex: number): readonly [number, number, number] {\n return [((hex >> 16) & 255) / 255, ((hex >> 8) & 255) / 255, (hex & 255) / 255]\n}\n\n/** Per-spawn overrides. */\nexport interface SpriteSpawnOptions {\n /** `0xRRGGBB`: replaces the kind's colour ramp. */\n color?: number\n /** Multiplier on the start size. */\n size?: number\n /** Multiplier on the life. */\n life?: number\n /** Multiplier on the peak alpha. */\n alpha?: number\n /** Non-zero: a `land` sprite (a drip) that reaches the ground calls the owner's `onLand` with this tag. */\n tag?: number\n /** Who hears the landing (see `VfxWorld.onLand`). */\n owner?: string\n /** Height the sprite stops or lands at, instead of the ground's under it (a splash on a table top). */\n floor?: number\n /** Multiplier on the kind's upward acceleration (slow steam). */\n rise?: number\n}\n\nexport interface KindPoolHost {\n groundAt(x: number, z: number): number\n gravity: number\n land(owner: string, x: number, y: number, z: number, color: number, tag: number): void\n}\n\n/** The destruction renderer's old generator (mulberry32), so a seed gives the same sprites. */\nexport function spriteRandom(seed: number): () => number {\n let a = seed >>> 0\n return () => {\n a = (a + 0x6d2b79f5) >>> 0\n let t = a\n t = Math.imul(t ^ (t >>> 15), t | 1)\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61)\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296\n }\n}\n\n/**\n * One fixed-size ring of sprites of the game's kinds, in typed arrays a renderer draws as points: `position`, `color`, `alpha` and `size` are what it\n * reads, `drawCount` how many records from the start. Additive and normal blending are two instances.\n */\nexport class SpriteKindPool {\n live = 0\n /** Records from the start the renderer draws (the highest live index + 1). */\n drawCount = 0\n readonly position: Float32Array\n readonly color: Float32Array\n readonly alpha: Float32Array\n readonly size: Float32Array\n private next = 0\n private high = 0\n private readonly velocity: Float32Array\n private readonly age: Float32Array\n private readonly life: Float32Array\n private readonly size0: Float32Array\n private readonly alive: Uint8Array\n private readonly kind: Uint8Array\n private readonly seed: Float32Array\n private readonly alphaMul: Float32Array\n private readonly tag: Int32Array\n private readonly owner: Int32Array\n private readonly colorHex: Int32Array\n private readonly rgbStart: Float32Array\n private readonly rgbEnd: Float32Array\n private readonly bounced: Uint8Array\n private readonly floor: Float32Array\n private readonly riseMul: Float32Array\n\n constructor(\n readonly capacity: number,\n private readonly defs: readonly SpriteKindDef[],\n private readonly owners: string[],\n ) {\n const n = Math.max(1, capacity)\n this.position = new Float32Array(n * 3)\n this.color = new Float32Array(n * 3)\n this.alpha = new Float32Array(n)\n this.size = new Float32Array(n)\n this.velocity = new Float32Array(n * 3)\n this.age = new Float32Array(n)\n this.life = new Float32Array(n)\n this.size0 = new Float32Array(n)\n this.alive = new Uint8Array(n)\n this.kind = new Uint8Array(n)\n this.seed = new Float32Array(n)\n this.alphaMul = new Float32Array(n)\n this.tag = new Int32Array(n)\n this.owner = new Int32Array(n)\n this.colorHex = new Int32Array(n)\n this.rgbStart = new Float32Array(n * 3)\n this.rgbEnd = new Float32Array(n * 3)\n this.bounced = new Uint8Array(n)\n this.floor = new Float32Array(n)\n this.riseMul = new Float32Array(n)\n }\n\n spawn(kind: number, p: readonly number[], v: readonly number[], random: () => number, options?: SpriteSpawnOptions): void {\n if (this.capacity <= 0) return\n const def = this.defs[kind]!\n const i = this.next\n this.next = (this.next + 1) % this.capacity\n if (this.alive[i]) this.live--\n this.alive[i] = 1\n this.live++\n if (i + 1 > this.high) this.high = i + 1\n this.kind[i] = kind\n this.position.set(p, i * 3)\n this.velocity.set(v, i * 3)\n this.age[i] = 0\n this.life[i] = lerp(def.life[0], def.life[1], random()) * (options?.life ?? 1)\n this.size0[i] = lerp(def.size[0], def.size[1], random()) * (options?.size ?? 1)\n this.seed[i] = random()\n this.alphaMul[i] = options?.alpha ?? 1\n this.tag[i] = options?.tag ?? 0\n this.owner[i] = options?.owner === undefined ? -1 : this.ownerIndex(options.owner)\n this.colorHex[i] = options?.color ?? -1\n this.rgbStart.set(rgb(options?.color ?? def.c0), i * 3)\n this.rgbEnd.set(rgb(options?.color ?? def.c1), i * 3)\n this.bounced[i] = 0\n this.floor[i] = options?.floor ?? Number.NaN\n this.riseMul[i] = options?.rise ?? 1\n this.size[i] = this.size0[i]!\n this.alpha[i] = 0\n }\n\n private ownerIndex(name: string): number {\n let index = this.owners.indexOf(name)\n if (index < 0) index = this.owners.push(name) - 1\n return index\n }\n\n private kill(i: number): void {\n if (!this.alive[i]) return\n this.alive[i] = 0\n this.live--\n this.alpha[i] = 0\n this.size[i] = 0\n }\n\n clear(): void {\n this.alive.fill(0)\n this.alpha.fill(0)\n this.size.fill(0)\n this.live = 0\n this.high = 0\n this.next = 0\n this.drawCount = 0\n }\n\n /** Shoves the live sprites within `radius` of a point away from it (a blast). */\n push(cx: number, cy: number, cz: number, radius: number, speed: number): void {\n for (let i = 0; i < this.high; i++) {\n if (!this.alive[i]) continue\n const dx = this.position[i * 3]! - cx\n const dy = this.position[i * 3 + 1]! - cy\n const dz = this.position[i * 3 + 2]! - cz\n const d = Math.hypot(dx, dy, dz)\n if (d > radius || d < 1e-4) continue\n const f = (1 - d / radius) * speed\n this.velocity[i * 3]! += (dx / d) * f\n this.velocity[i * 3 + 1]! += (dy / d) * f\n this.velocity[i * 3 + 2]! += (dz / d) * f\n }\n }\n\n update(dt: number, host: KindPoolHost): void {\n let top = 0\n for (let i = 0; i < this.high; i++) {\n if (!this.alive[i]) continue\n const def = this.defs[this.kind[i]!]!\n const age = (this.age[i]! + dt)\n this.age[i] = age\n const life = this.life[i]!\n if (age >= life) {\n this.kill(i)\n continue\n }\n const t = age / life\n const i3 = i * 3\n let vx = this.velocity[i3]!\n let vy = this.velocity[i3 + 1]!\n let vz = this.velocity[i3 + 2]!\n vy += (def.rise * this.riseMul[i]! - def.gravity * host.gravity) * dt\n if (def.flutter) {\n const phase = age * 7 + this.seed[i]! * 40\n vx += Math.sin(phase) * def.flutter * dt\n vz += Math.cos(phase * 1.3) * def.flutter * dt\n }\n if (def.drag > 0) {\n const k = Math.exp(-def.drag * dt)\n vx *= k\n vy *= k\n vz *= k\n }\n const x = this.position[i3]! + vx * dt\n let y = this.position[i3 + 1]! + vy * dt\n const z = this.position[i3 + 2]! + vz * dt\n if (def.ground !== 'none') {\n const own = this.floor[i]!\n const gy = Number.isNaN(own) ? host.groundAt(x, z) + 0.005 : own\n if (y < gy) {\n if (def.ground === 'land') {\n const tag = this.tag[i]!\n const owner = this.owner[i]!\n if (tag !== 0 && owner >= 0) host.land(this.owners[owner]!, x, gy, z, this.colorHex[i]! >= 0 ? this.colorHex[i]! : def.c0, tag)\n this.kill(i)\n continue\n }\n y = gy\n if (def.ground === 'bounce' && !this.bounced[i]) {\n this.bounced[i] = 1\n vy = Math.abs(vy) * 0.35\n vx *= 0.6\n vz *= 0.6\n } else {\n vx = vy = vz = 0\n }\n }\n }\n this.velocity[i3] = vx\n this.velocity[i3 + 1] = vy\n this.velocity[i3 + 2] = vz\n this.position[i3] = x\n this.position[i3 + 1] = y\n this.position[i3 + 2] = z\n this.size[i] = this.size0[i]! * lerp(1, def.grow, t)\n this.color[i3] = lerp(this.rgbStart[i3]!, this.rgbEnd[i3]!, t)\n this.color[i3 + 1] = lerp(this.rgbStart[i3 + 1]!, this.rgbEnd[i3 + 1]!, t)\n this.color[i3 + 2] = lerp(this.rgbStart[i3 + 2]!, this.rgbEnd[i3 + 2]!, t)\n let envelope = t < def.fadeIn ? t / def.fadeIn : 1 - (t - def.fadeIn) / (1 - def.fadeIn)\n if (def.flicker) envelope *= 1 - def.flicker * (0.5 + 0.5 * Math.sin(age * 38 + this.seed[i]! * 50))\n this.alpha[i] = clamp(envelope, 0, 1) * def.alpha * this.alphaMul[i]!\n top = i + 1\n }\n this.high = top\n this.drawCount = top\n }\n}\n\nexport { rgb as hexToUnitRgb }\n","{\n \"fire\": {\n \"id\": \"fire\",\n \"additive\": true,\n \"life\": [\n 0.45,\n 0.9\n ],\n \"size\": [\n 0.1,\n 0.2\n ],\n \"grow\": 0.3,\n \"gravity\": 0,\n \"rise\": 2.4,\n \"drag\": 1.2,\n \"c0\": 16760896,\n \"c1\": 12591104,\n \"alpha\": 0.9,\n \"fadeIn\": 0.12,\n \"cone\": 0.7,\n \"ground\": \"none\",\n \"flicker\": 0.35\n },\n \"smoke\": {\n \"id\": \"smoke\",\n \"additive\": false,\n \"life\": [\n 1.8,\n 3.2\n ],\n \"size\": [\n 0.14,\n 0.24\n ],\n \"grow\": 3.2,\n \"gravity\": 0,\n \"rise\": 0.7,\n \"drag\": 0.8,\n \"c0\": 6710886,\n \"c1\": 10132122,\n \"alpha\": 0.35,\n \"fadeIn\": 0.15,\n \"cone\": 0.6,\n \"ground\": \"none\"\n },\n \"sparks\": {\n \"id\": \"sparks\",\n \"additive\": true,\n \"life\": [\n 0.25,\n 0.6\n ],\n \"size\": [\n 0.018,\n 0.03\n ],\n \"grow\": 0.5,\n \"gravity\": 1,\n \"rise\": 0,\n \"drag\": 0.3,\n \"c0\": 16773792,\n \"c1\": 16738832,\n \"alpha\": 1,\n \"fadeIn\": 0.02,\n \"cone\": 0.55,\n \"ground\": \"bounce\"\n },\n \"dust\": {\n \"id\": \"dust\",\n \"additive\": false,\n \"life\": [\n 1,\n 2\n ],\n \"size\": [\n 0.08,\n 0.14\n ],\n \"grow\": 2.5,\n \"gravity\": 0.05,\n \"rise\": 0,\n \"drag\": 2.5,\n \"c0\": 9274745,\n \"c1\": 9274745,\n \"alpha\": 0.4,\n \"fadeIn\": 0.1,\n \"cone\": 0.4,\n \"ground\": \"none\"\n },\n \"shards\": {\n \"id\": \"shards\",\n \"additive\": true,\n \"life\": [\n 0.8,\n 1.4\n ],\n \"size\": [\n 0.02,\n 0.035\n ],\n \"grow\": 0.8,\n \"gravity\": 1,\n \"rise\": 0,\n \"drag\": 0.2,\n \"c0\": 14677247,\n \"c1\": 10473727,\n \"alpha\": 0.9,\n \"fadeIn\": 0.03,\n \"cone\": 0.5,\n \"ground\": \"bounce\",\n \"flicker\": 0.5\n },\n \"splinters\": {\n \"id\": \"splinters\",\n \"additive\": false,\n \"life\": [\n 0.8,\n 1.5\n ],\n \"size\": [\n 0.02,\n 0.035\n ],\n \"grow\": 1,\n \"gravity\": 1,\n \"rise\": 0,\n \"drag\": 0.3,\n \"c0\": 10516042,\n \"c1\": 8017200,\n \"alpha\": 1,\n \"fadeIn\": 0.02,\n \"cone\": 0.5,\n \"ground\": \"bounce\"\n },\n \"chips\": {\n \"id\": \"chips\",\n \"additive\": false,\n \"life\": [\n 0.8,\n 1.5\n ],\n \"size\": [\n 0.025,\n 0.04\n ],\n \"grow\": 1,\n \"gravity\": 1,\n \"rise\": 0,\n \"drag\": 0.3,\n \"c0\": 9077624,\n \"c1\": 7235679,\n \"alpha\": 1,\n \"fadeIn\": 0.02,\n \"cone\": 0.5,\n \"ground\": \"bounce\"\n },\n \"fibers\": {\n \"id\": \"fibers\",\n \"additive\": false,\n \"life\": [\n 1.5,\n 2.5\n ],\n \"size\": [\n 0.02,\n 0.03\n ],\n \"grow\": 1,\n \"gravity\": 0.15,\n \"rise\": 0,\n \"drag\": 2,\n \"c0\": 12101768,\n \"c1\": 12101768,\n \"alpha\": 0.9,\n \"fadeIn\": 0.05,\n \"cone\": 0.4,\n \"ground\": \"stop\",\n \"flutter\": 2.5\n },\n \"leaves\": {\n \"id\": \"leaves\",\n \"additive\": false,\n \"life\": [\n 1.5,\n 2.8\n ],\n \"size\": [\n 0.03,\n 0.05\n ],\n \"grow\": 1,\n \"gravity\": 0.15,\n \"rise\": 0,\n \"drag\": 2,\n \"c0\": 6003260,\n \"c1\": 4882224,\n \"alpha\": 0.95,\n \"fadeIn\": 0.05,\n \"cone\": 0.4,\n \"ground\": \"stop\",\n \"flutter\": 2.8\n },\n \"drips\": {\n \"id\": \"drips\",\n \"additive\": false,\n \"life\": [\n 2,\n 3\n ],\n \"size\": [\n 0.025,\n 0.035\n ],\n \"grow\": 1,\n \"gravity\": 1,\n \"rise\": 0,\n \"drag\": 0,\n \"c0\": 6974058,\n \"c1\": 6974058,\n \"alpha\": 1,\n \"fadeIn\": 0.01,\n \"cone\": 0.2,\n \"ground\": \"land\"\n },\n \"ash\": {\n \"id\": \"ash\",\n \"additive\": false,\n \"life\": [\n 2,\n 4\n ],\n \"size\": [\n 0.02,\n 0.04\n ],\n \"grow\": 1.2,\n \"gravity\": 0.08,\n \"rise\": 0,\n \"drag\": 1.5,\n \"c0\": 2762275,\n \"c1\": 1710102,\n \"alpha\": 0.8,\n \"fadeIn\": 0.1,\n \"cone\": 0.5,\n \"ground\": \"stop\",\n \"flutter\": 0.6\n },\n \"steam\": {\n \"id\": \"steam\",\n \"additive\": false,\n \"life\": [\n 1,\n 2\n ],\n \"size\": [\n 0.12,\n 0.2\n ],\n \"grow\": 3,\n \"gravity\": 0,\n \"rise\": 0.9,\n \"drag\": 1,\n \"c0\": 16777215,\n \"c1\": 14739694,\n \"alpha\": 0.35,\n \"fadeIn\": 0.15,\n \"cone\": 0.6,\n \"ground\": \"none\"\n },\n \"flash\": {\n \"id\": \"flash\",\n \"additive\": true,\n \"life\": [\n 0.12,\n 0.2\n ],\n \"size\": [\n 1.2,\n 1.8\n ],\n \"grow\": 1.6,\n \"gravity\": 0,\n \"rise\": 0,\n \"drag\": 0,\n \"c0\": 16774080,\n \"c1\": 16748592,\n \"alpha\": 1,\n \"fadeIn\": 0.1,\n \"cone\": 0,\n \"ground\": \"none\"\n },\n \"foam\": {\n \"id\": \"foam\",\n \"additive\": false,\n \"life\": [\n 1,\n 1.8\n ],\n \"size\": [\n 0.06,\n 0.1\n ],\n \"grow\": 1.5,\n \"gravity\": 0.6,\n \"rise\": 0,\n \"drag\": 1.5,\n \"c0\": 16054527,\n \"c1\": 14542576,\n \"alpha\": 0.8,\n \"fadeIn\": 0.05,\n \"cone\": 0.5,\n \"ground\": \"stop\"\n }\n}\n","import kindData from '../data/sprite-kinds.json'\n\n/** The kinds of sprite the game's pools make: debris, sparks, smoke, steam, drips... (the destruction renderer's old `ParticleKind`). */\nexport type SpriteKind =\n | 'fire' | 'smoke' | 'sparks' | 'dust' | 'shards' | 'splinters' | 'chips' | 'fibers' | 'leaves' | 'drips' | 'ash' | 'steam' | 'flash' | 'foam'\n\n/** What happens to a sprite that reaches the ground. */\nexport type SpriteGround = 'none' | 'bounce' | 'stop' | 'land'\n\nexport interface SpriteKindDef {\n readonly id: SpriteKind\n readonly additive: boolean\n /** Life range in seconds. */\n readonly life: readonly [number, number]\n /** Start size range in metres (billboard diameter). */\n readonly size: readonly [number, number]\n /** Size multiplier reached at the end of the life. */\n readonly grow: number\n /** Gravity multiplier (1 = full gravity). */\n readonly gravity: number\n /** Constant upward acceleration in m/s² (heat, steam). */\n readonly rise: number\n /** Velocity damping per second. */\n readonly drag: number\n /** `0xRRGGBB` at the start and at the end of the life. */\n readonly c0: number\n readonly c1: number\n readonly alpha: number\n /** Fraction of the life spent fading in. */\n readonly fadeIn: number\n /** How much of the emission direction follows the surface normal (1) versus a random direction (0). */\n readonly cone: number\n readonly ground: SpriteGround\n readonly flutter?: number\n readonly flicker?: number\n}\n\nexport const SPRITE_KINDS: Readonly<Record<SpriteKind, SpriteKindDef>> = kindData as unknown as Record<SpriteKind, SpriteKindDef>\n\n/** A copy of the kinds with some changed (a partial replaces the fields it names), added or removed (`null`); a game tunes its sprites without touching the core. */\nexport function patchSpriteKinds(\n base: Readonly<Record<string, SpriteKindDef>>,\n changes: Readonly<Record<string, Partial<SpriteKindDef> | SpriteKindDef | null>>,\n): Record<string, SpriteKindDef> {\n const result: Record<string, SpriteKindDef> = { ...base }\n for (const [id, change] of Object.entries(changes)) {\n if (change === null) delete result[id]\n else result[id] = { ...(base[id] ?? {}), ...change, id } as SpriteKindDef\n }\n return result\n}\n","import { SpriteKindPool, spriteRandom } from './kind-pool'\nimport type { SpriteSpawnOptions } from './kind-pool'\nimport { SPRITE_KINDS } from './kinds'\nimport type { SpriteKind, SpriteKindDef } from './kinds'\n\ntype Vec3 = readonly [number, number, number]\n\nexport interface SpriteKindSystemOptions {\n /** Most live sprites at once (additive takes 40 %, normal the rest). */\n readonly capacity: number\n /** Count multiplier for bursts (0.6 on low quality). */\n readonly density: number\n readonly seed: number\n readonly groundAt: (x: number, z: number) => number\n readonly gravity: number\n readonly kinds?: Readonly<Record<string, SpriteKindDef>>\n}\n\nexport type LandListener = (x: number, y: number, z: number, color: number, tag: number) => void\n\n/**\n * The game's sprites of fixed kinds (debris, sparks, smoke, steam, drips...), burst or spawned one by one: two pooled rings, additive and normal, that\n * a renderer draws as points. This is the destruction renderer's old particle system with the maths unchanged and the drawing taken out.\n */\nexport class SpriteKindSystem {\n readonly additive: SpriteKindPool\n readonly normal: SpriteKindPool\n private readonly random: () => number\n private readonly ids: readonly string[]\n private readonly defs: readonly SpriteKindDef[]\n private readonly index = new Map<string, number>()\n private readonly owners: string[] = []\n private readonly listeners = new Map<string, LandListener>()\n\n constructor(private readonly options: SpriteKindSystemOptions) {\n const kinds: Readonly<Record<string, SpriteKindDef>> = options.kinds ?? SPRITE_KINDS\n this.ids = Object.keys(kinds)\n this.defs = this.ids.map((id) => kinds[id]!)\n this.ids.forEach((id, at) => this.index.set(id, at))\n const additiveCapacity = Math.floor(options.capacity * 0.4)\n this.additive = new SpriteKindPool(additiveCapacity, this.defs, this.owners)\n this.normal = new SpriteKindPool(options.capacity - additiveCapacity, this.defs, this.owners)\n this.random = spriteRandom(options.seed)\n }\n\n get capacity(): number {\n return this.additive.capacity + this.normal.capacity\n }\n\n get live(): number {\n return this.additive.live + this.normal.live\n }\n\n /** Hears the landing of the drips of `owner` (the ones spawned with `{ owner, tag }`). Returns the function that stops listening. */\n onLand(owner: string, listener: LandListener): () => void {\n this.listeners.set(owner, listener)\n return () => {\n if (this.listeners.get(owner) === listener) this.listeners.delete(owner)\n }\n }\n\n private kindOf(kind: string): { index: number; def: SpriteKindDef } {\n const index = this.index.get(kind)\n if (index === undefined) throw new Error(`unknown sprite kind ${kind}`)\n return { index, def: this.defs[index]! }\n }\n\n /** One sprite with an explicit velocity. */\n spawn(kind: SpriteKind | string, point: Vec3, velocity: Vec3, options?: SpriteSpawnOptions): void {\n const { index, def } = this.kindOf(kind)\n ;(def.additive ? this.additive : this.normal).spawn(index, point, velocity, this.random, options)\n }\n\n /** `count` sprites leaving `point` around `normal` at about `speed` m/s. */\n burst(kind: SpriteKind | string, point: Vec3, normal: Vec3, count: number, speed = 1, options?: SpriteSpawnOptions): void {\n const { def } = this.kindOf(kind)\n const n = Math.max(0, Math.round(count * this.options.density))\n const rnd = this.random\n const length = Math.hypot(normal[0], normal[1], normal[2]) || 1\n const nx = normal[0] / length\n const ny = normal[1] / length\n const nz = normal[2] / length\n for (let k = 0; k < n; k++) {\n const u = rnd() * 2 - 1\n const phase = rnd() * Math.PI * 2\n const s = Math.sqrt(1 - u * u)\n const rx = s * Math.cos(phase)\n const ry = u\n const rz = s * Math.sin(phase)\n let dx = nx * def.cone + rx * (1 - def.cone)\n let dy = ny * def.cone + ry * (1 - def.cone)\n let dz = nz * def.cone + rz * (1 - def.cone)\n // a little scatter even for a pure cone, and never push into the surface for debris\n dx += rx * 0.35\n dy += ry * 0.35\n dz += rz * 0.35\n const l = Math.hypot(dx, dy, dz) || 1\n const sp = speed * (0.35 + 0.65 * rnd())\n const jitter = 0.01 + (def.size[1] > 0.5 ? 0.15 : 0.02)\n this.spawn(\n def.id,\n [point[0] + (rnd() - 0.5) * jitter, point[1] + (rnd() - 0.5) * jitter, point[2] + (rnd() - 0.5) * jitter],\n [(dx / l) * sp, (dy / l) * sp, (dz / l) * sp],\n options,\n )\n }\n }\n\n /** Blast wave: shoves the live sprites away from a point. */\n push(point: Vec3, radius: number, speed: number): void {\n this.additive.push(point[0], point[1], point[2], radius, speed)\n this.normal.push(point[0], point[1], point[2], radius, speed)\n }\n\n update(dt: number): void {\n const host = {\n groundAt: this.options.groundAt,\n gravity: this.options.gravity,\n land: (owner: string, x: number, y: number, z: number, color: number, tag: number) => this.listeners.get(owner)?.(x, y, z, color, tag),\n }\n if (this.additive.live > 0 || this.additive.drawCount > 0) this.additive.update(dt, host)\n if (this.normal.live > 0 || this.normal.drawCount > 0) this.normal.update(dt, host)\n }\n\n clear(): void {\n this.additive.clear()\n this.normal.clear()\n }\n}\n","import type { RandomStream, MutableVec3 } from '../random'\nimport { addScaled, normalize } from '../vector'\nimport type { SpriteRange, Vec3Tuple } from './types'\n\nconst TWO_PI_ROUNDED = 6.2832\nconst WIDE_CONE = 3.1\nconst UP: Vec3Tuple = [0, 1, 0]\n\nexport function drawRange(random: RandomStream, range: SpriteRange | undefined): number {\n if (Array.isArray(range)) return random.between(range[0], range[1])\n return (range as number | undefined) || 0\n}\n\n/** `randUnit` (W166): the page rounds a turn to 6.2832, so this is not `RandomStream.direction`. */\nexport function randomUnit(random: RandomStream, out: MutableVec3): MutableVec3 {\n const up = random.next() * 2 - 1\n const angle = random.next() * TWO_PI_ROUNDED\n const ring = Math.sqrt(1 - up * up)\n out.x = ring * Math.cos(angle)\n out.y = up\n out.z = ring * Math.sin(angle)\n return out\n}\n\nconst tangentA: MutableVec3 = { x: 0, y: 0, z: 0 }\nconst tangentB: MutableVec3 = { x: 0, y: 0, z: 0 }\nconst axis: MutableVec3 = { x: 0, y: 0, z: 0 }\n\nfunction cross(out: MutableVec3, a: MutableVec3, b: MutableVec3): MutableVec3 {\n const x = a.y * b.z - a.z * b.y\n const y = a.z * b.x - a.x * b.z\n const z = a.x * b.y - a.y * b.x\n out.x = x\n out.y = y\n out.z = z\n return out\n}\n\n/** `coneDir` (W169-171): a direction inside `cone` radians of `direction`, two draws (a full sphere when the cone is wide). */\nexport function coneDirection(random: RandomStream, direction: Vec3Tuple | undefined, cone: number, out: MutableVec3): MutableVec3 {\n if (cone >= WIDE_CONE) return randomUnit(random, out)\n const [dx, dy, dz] = direction ?? UP\n const cosTilt = 1 - random.next() * (1 - Math.cos(cone))\n const sinTilt = Math.sqrt(1 - cosTilt * cosTilt)\n const phase = random.next() * TWO_PI_ROUNDED\n axis.x = dx\n axis.y = dy\n axis.z = dz\n tangentA.x = 1\n tangentA.y = 0\n tangentA.z = 0\n if (Math.abs(dx) > 0.9) {\n tangentA.x = 0\n tangentA.y = 1\n }\n normalize(cross(tangentB, axis, tangentA))\n normalize(cross(tangentA, tangentB, axis))\n out.x = dx * cosTilt\n out.y = dy * cosTilt\n out.z = dz * cosTilt\n addScaled(out, tangentA, sinTilt * Math.cos(phase))\n addScaled(out, tangentB, sinTilt * Math.sin(phase))\n return out\n}\n","import type { MutableVec3 } from '../random'\nimport { coneDirection, drawRange } from './direction'\nimport type { DebrisSpawn, WorkshopContext } from './types'\n\nconst CAPACITY = 500\nconst GRAVITY = 9.8\nconst QUARTER_TURN = 1.5708\nconst FADE_SECONDS = 0.5\nconst DEFAULT_SIZE = [0.05, 0.05, 0.05] as const\nconst DEFAULT_SPIN = 12\nconst DEFAULT_COLOR = 0x888888\nconst DEFAULT_LIFE = 8\nconst DEFAULT_BOUNCE = 0.3\nconst FULL_TURN_DRAWN = 6\n\nexport interface DebrisPiece {\n on: boolean\n x: number\n y: number\n z: number\n vx: number\n vy: number\n vz: number\n sx: number\n sy: number\n sz: number\n rx: number\n ry: number\n rz: number\n ax: number\n ay: number\n az: number\n life: number\n bounce: number\n rest: boolean\n color: number\n}\n\nfunction createPiece(): DebrisPiece {\n return { on: false, x: 0, y: 0, z: 0, vx: 0, vy: 0, vz: 0, sx: 0, sy: 0, sz: 0, rx: 0, ry: 0, rz: 0, ax: 0, ay: 0, az: 0, life: 0, bounce: 0, rest: false, color: DEFAULT_COLOR }\n}\n\n/** The Workshop's physics debris (W248-263): 500 boxes in a ring buffer, the newest overwriting the oldest, that tumble, bounce, settle on a face and shrink away. */\nexport class DebrisPool {\n readonly pieces: DebrisPiece[] = Array.from({ length: CAPACITY }, createPiece)\n count = 0\n private next = 0\n private readonly direction: MutableVec3 = { x: 0, y: 0, z: 0 }\n\n constructor(private readonly context: WorkshopContext) {}\n\n spawn(options: DebrisSpawn): void {\n const random = this.context.random\n const piece = this.pieces[this.next++ % CAPACITY]!\n coneDirection(random, options.dir, options.cone ?? 3.2, this.direction)\n const speed = drawRange(random, options.sp)\n const size = options.size ?? DEFAULT_SIZE\n const scale = options.k ? drawRange(random, options.k) : 1\n const box = options.box\n const spin = options.spin || DEFAULT_SPIN\n piece.on = true\n piece.x = options.p[0] + (box ? random.between(-box[0], box[0]) : 0)\n piece.y = options.p[1] + (box ? random.between(-box[1], box[1]) : 0)\n piece.z = options.p[2] + (box ? random.between(-box[2], box[2]) : 0)\n piece.vx = this.direction.x * speed + (options.v ? options.v[0] : 0)\n piece.vy = this.direction.y * speed + (options.v ? options.v[1] : 0)\n piece.vz = this.direction.z * speed + (options.v ? options.v[2] : 0)\n piece.sx = (size[0] as number) * scale\n piece.sy = (size[1] as number) * scale\n piece.sz = (size[2] as number) * scale\n piece.rx = random.between(0, FULL_TURN_DRAWN)\n piece.ry = random.between(0, FULL_TURN_DRAWN)\n piece.rz = random.between(0, FULL_TURN_DRAWN)\n piece.ax = random.between(-1, 1) * spin\n piece.ay = random.between(-1, 1) * spin\n piece.az = random.between(-1, 1) * spin\n piece.life = options.life || DEFAULT_LIFE\n piece.bounce = options.bounce ?? DEFAULT_BOUNCE\n piece.rest = false\n piece.color = options.c ?? DEFAULT_COLOR\n }\n\n clear(): void {\n for (const piece of this.pieces) piece.on = false\n this.count = 0\n }\n\n update(dt: number): void {\n this.count = 0\n for (const piece of this.pieces) {\n if (!piece.on) continue\n this.count++\n piece.life -= dt\n if (piece.life <= 0) {\n piece.on = false\n continue\n }\n if (!piece.rest) this.move(piece, dt)\n }\n }\n\n /** Scale at the end of its life, 1 until the last half second (the renderer's `sh`). */\n static fade(piece: DebrisPiece): number {\n return Math.min(1, piece.life / FADE_SECONDS)\n }\n\n private move(piece: DebrisPiece, dt: number): void {\n piece.vy -= GRAVITY * dt\n piece.x += piece.vx * dt\n piece.y += piece.vy * dt\n piece.z += piece.vz * dt\n piece.rx += piece.ax * dt\n piece.ry += piece.ay * dt\n piece.rz += piece.az * dt\n const groundHeight = this.context.groundAt(piece.x, piece.z) + Math.min(piece.sx, piece.sy, piece.sz) * 0.5\n if (piece.y >= groundHeight) return\n piece.y = groundHeight\n piece.vy *= -piece.bounce\n piece.vx *= 0.55\n piece.vz *= 0.55\n piece.ax *= 0.5\n piece.ay *= 0.5\n piece.az *= 0.5\n if (Math.abs(piece.vy) < 0.5 && Math.hypot(piece.vx, piece.vz) < 0.3) {\n piece.rest = true\n piece.rx = Math.round(piece.rx / QUARTER_TURN) * QUARTER_TURN\n piece.rz = Math.round(piece.rz / QUARTER_TURN) * QUARTER_TURN\n }\n }\n}\n","import type { DecalOptions, Vec3Tuple, WorkshopContext } from './types'\n\nconst CAPACITY = 400\nconst UP: Vec3Tuple = [0, 1, 0]\nconst DEFAULT_LIFE = 60\nconst DEFAULT_GROW = 1.2\n\n/** The seconds a fluid decal waits and then takes to evaporate away (W276); blood, bile and oil only darken. */\nconst EVAPORATION = { fuel: [6, 5], water: [40, 20] } as const\n\nexport interface Decal {\n on: boolean\n x: number\n y: number\n z: number\n sx: number\n sy: number\n t: number\n life: number\n grow: number\n color: number\n /** Where the surface faces, and the colour a fluid dries to (`-1`: it does not dry). */\n normal: Vec3Tuple\n fluid: DecalOptions['fluid'] | undefined\n dryColor: number\n dryAt: number\n dryTime: number\n}\n\nfunction createDecal(): Decal {\n return { on: false, x: 0, y: 0, z: 0, sx: 0, sy: 0, t: 0, life: 0, grow: 0, color: 0, normal: UP, fluid: undefined, dryColor: -1, dryAt: 0, dryTime: 0 }\n}\n\n/** The Workshop's decals (W265-306): 400 marks on the ground in a ring buffer. Wet ones spread and evaporate (fuel, water), the rest last their life. */\nexport class DecalPool {\n readonly decals: Decal[] = Array.from({ length: CAPACITY }, createDecal)\n count = 0\n private next = 0\n\n constructor(private readonly context: WorkshopContext) {}\n\n add(x: number, y: number, z: number, size: number, color: number, options: DecalOptions = {}): void {\n const random = this.context.random\n const index = this.next++ % CAPACITY\n const decal = this.decals[index]!\n const normal = options.n ?? UP\n random.between(0, 6.28)\n const offset = 0.004 + (index % 40) * 0.0004\n decal.on = true\n decal.x = x + normal[0] * offset\n decal.y = y + normal[1] * offset\n decal.z = z + normal[2] * offset\n decal.sx = size * (options.ar ? 1 : random.between(0.8, 1.2))\n decal.sy = size * (options.ar || random.between(0.65, 1.1))\n decal.t = -(options.delay || 0)\n decal.grow = options.grow ?? DEFAULT_GROW\n decal.life = options.life || DEFAULT_LIFE\n decal.color = color\n decal.normal = normal\n const fluid = options.fluid ?? (options.dark ? 'blood' : undefined)\n decal.fluid = fluid\n decal.dryAt = options.dark && !options.fluid ? 0 : options.dryAt ?? (fluid === 'blood' || fluid === 'bile' ? 40 : 0)\n decal.dryTime = options.dark && !options.fluid ? options.dark : options.dryT ?? (fluid === 'blood' || fluid === 'bile' ? 30 : 0)\n decal.dryColor = options.dry ?? (fluid === 'bile' ? 0x3e4220 : -1)\n }\n\n clear(): void {\n for (const decal of this.decals) decal.on = false\n this.count = 0\n }\n\n update(dt: number): void {\n this.count = 0\n for (const decal of this.decals) {\n if (!decal.on) continue\n this.count++\n decal.t += dt\n if (decal.t < 0) continue\n decal.life -= dt\n if (decal.life <= 0) {\n decal.on = false\n continue\n }\n const evaporation = decal.fluid === 'fuel' || decal.fluid === 'water' ? EVAPORATION[decal.fluid] : undefined\n if (evaporation && (decal.t - evaporation[0]) / evaporation[1] >= 1) decal.on = false\n }\n }\n}\n","import { hexToRgb } from '../color'\nimport { lerp } from '../vector'\nimport type { MutableVec3, RandomStream } from '../random'\nimport { coneDirection, drawRange, randomUnit } from './direction'\nimport type { SpriteSpawn, WorkshopContext } from './types'\n\n/** Colour, size and alpha of a particle at its current age: what the renderer reads (`col`, `siz`, `alp`). */\nconst DEFAULT_FADE_IN = 0.1\nconst FLOATS_PER_VECTOR = 3\nconst BYTE_MAX = 255\n\nexport interface DecalFromSprite {\n (x: number, y: number, z: number, size: number, color: number, options: { readonly grow: number; readonly fluid: 'blood' }): void\n}\n\n/**\n * The Workshop's `PSys` (W179-220): a pool of sprites in typed arrays, as the page keeps them. A dead record is replaced by the last one, so the order\n * in the arrays is the page's, and the golden hashes the arrays as they are. Additive and normal-blend systems are two instances.\n */\nexport class SpritePool {\n count = 0\n readonly position: Float32Array\n readonly velocity: Float32Array\n readonly colorStart: Float32Array\n readonly colorEnd: Float32Array\n readonly life: Float32Array\n readonly maxLife: Float32Array\n readonly startSize: Float32Array\n readonly growth: Float32Array\n readonly startAlpha: Float32Array\n readonly gravity: Float32Array\n readonly drag: Float32Array\n readonly fadeIn: Float32Array\n readonly groundMode: Uint8Array\n readonly decalSize: Float32Array\n /** Written by `update`: what the renderer draws. */\n readonly color: Float32Array\n readonly size: Float32Array\n readonly alpha: Float32Array\n private readonly vectorArrays: readonly Float32Array[]\n private readonly scalarArrays: readonly (Float32Array | Uint8Array)[]\n private readonly direction: MutableVec3 = { x: 0, y: 0, z: 0 }\n private readonly unit: MutableVec3 = { x: 0, y: 0, z: 0 }\n\n constructor(readonly capacity: number, private readonly context: WorkshopContext) {\n const vectorArray = (): Float32Array => new Float32Array(capacity * FLOATS_PER_VECTOR)\n const scalarArray = (): Float32Array => new Float32Array(capacity)\n this.position = vectorArray()\n this.velocity = vectorArray()\n this.colorStart = vectorArray()\n this.colorEnd = vectorArray()\n this.color = vectorArray()\n this.life = scalarArray()\n this.maxLife = scalarArray()\n this.startSize = scalarArray()\n this.growth = scalarArray()\n this.startAlpha = scalarArray()\n this.gravity = scalarArray()\n this.drag = scalarArray()\n this.fadeIn = scalarArray()\n this.decalSize = scalarArray()\n this.size = scalarArray()\n this.alpha = scalarArray()\n this.groundMode = new Uint8Array(capacity)\n this.vectorArrays = [this.position, this.velocity, this.colorStart, this.colorEnd]\n this.scalarArrays = [this.life, this.maxLife, this.startSize, this.growth, this.startAlpha, this.gravity, this.drag, this.fadeIn, this.groundMode, this.decalSize]\n }\n\n spawn(options: SpriteSpawn): void {\n if (this.count >= this.capacity) return\n const random: RandomStream = this.context.random\n const index = this.count++\n const offset = index * FLOATS_PER_VECTOR\n let [x, y, z] = options.p\n if (options.r) {\n randomUnit(random, this.unit)\n const reach = Math.cbrt(random.next()) * options.r\n x += this.unit.x * reach\n y += this.unit.y * reach * (options.flat ? 0 : 1)\n z += this.unit.z * reach\n }\n if (options.box) {\n x += random.between(-options.box[0], options.box[0])\n y += random.between(-options.box[1], options.box[1])\n z += random.between(-options.box[2], options.box[2])\n }\n this.position[offset] = x\n this.position[offset + 1] = y\n this.position[offset + 2] = z\n coneDirection(random, options.dir, options.cone ?? 3.2, this.direction)\n const speed = drawRange(random, options.sp)\n this.velocity[offset] = this.direction.x * speed + (options.v ? options.v[0] : 0)\n this.velocity[offset + 1] = this.direction.y * speed + (options.v ? options.v[1] : 0)\n this.velocity[offset + 2] = this.direction.z * speed + (options.v ? options.v[2] : 0)\n const start = hexToRgb(options.c0 ?? 0xffffff)\n const end = options.c1 != null ? hexToRgb(options.c1) : start\n for (let channel = 0; channel < FLOATS_PER_VECTOR; channel++) {\n this.colorStart[offset + channel] = start[channel]\n this.colorEnd[offset + channel] = end[channel]\n }\n this.life[index] = this.maxLife[index] = drawRange(random, options.life || 1)\n this.startSize[index] = drawRange(random, options.size || 0.1)\n this.growth[index] = options.grow || 1\n this.startAlpha[index] = options.a ?? 1\n this.gravity[index] = options.g || 0\n this.drag[index] = options.drag || 0\n this.fadeIn[index] = options.fi ?? DEFAULT_FADE_IN\n this.groundMode[index] = options.gnd || 0\n this.decalSize[index] = options.ds ? drawRange(random, options.ds) : 0.1\n }\n\n kill(index: number): void {\n const last = --this.count\n if (index === last) return\n for (const array of this.vectorArrays) {\n for (let component = 0; component < FLOATS_PER_VECTOR; component++) array[index * FLOATS_PER_VECTOR + component] = array[last * FLOATS_PER_VECTOR + component]\n }\n for (const array of this.scalarArrays) array[index] = array[last]\n }\n\n clear(): void {\n this.count = 0\n }\n\n update(dt: number, leaveDecal: DecalFromSprite): void {\n const { position, velocity } = this\n for (let index = this.count - 1; index >= 0; index--) {\n this.life[index] -= dt\n if (this.life[index] <= 0) {\n this.kill(index)\n continue\n }\n const offset = index * FLOATS_PER_VECTOR\n const age = 1 - this.life[index] / this.maxLife[index]\n velocity[offset + 1] -= this.gravity[index] * dt\n const factor = Math.max(0, 1 - this.drag[index] * dt)\n velocity[offset] *= factor\n velocity[offset + 1] *= factor\n velocity[offset + 2] *= factor\n position[offset] += velocity[offset] * dt\n position[offset + 1] += velocity[offset + 1] * dt\n position[offset + 2] += velocity[offset + 2] * dt\n const mode = this.groundMode[index]\n if (mode) {\n const groundHeight = this.context.groundAt(position[offset], position[offset + 2])\n if (position[offset + 1] < groundHeight) {\n if (mode === 1) {\n this.kill(index)\n continue\n }\n if (mode === 2) {\n position[offset + 1] = groundHeight + 0.001\n velocity[offset + 1] *= -0.3\n velocity[offset] *= 0.55\n velocity[offset + 2] *= 0.55\n }\n if (mode === 3) {\n if (this.context.quality.level < 2) {\n const red = (this.colorStart[offset] * BYTE_MAX) << 16\n const green = (this.colorStart[offset + 1] * BYTE_MAX) << 8\n leaveDecal(position[offset], groundHeight, position[offset + 2], this.decalSize[index], red | green | (this.colorStart[offset + 2] * BYTE_MAX), { grow: 0.15, fluid: 'blood' })\n }\n this.kill(index)\n continue\n }\n }\n }\n this.size[index] = this.startSize[index] * (1 + (this.growth[index] - 1) * age)\n const fadeIn = this.fadeIn[index]\n this.alpha[index] = this.startAlpha[index] * (fadeIn > 0 && age < fadeIn ? age / fadeIn : 1 - (age - fadeIn) / (1 - fadeIn))\n for (let channel = 0; channel < FLOATS_PER_VECTOR; channel++) this.color[offset + channel] = lerp(this.colorStart[offset + channel], this.colorEnd[offset + channel], age)\n }\n }\n}\n","import { hexToRgb } from '../color'\nimport type { MutableVec3 } from '../random'\nimport { lerp } from '../vector'\nimport { coneDirection, drawRange } from './direction'\nimport type { SpriteSpawn, WorkshopContext } from './types'\n\nconst FLOATS_PER_VECTOR = 3\nconst SEGMENT_FLOATS = 6\nconst TAIL_BRIGHTNESS = 0.15\n\n/** The Workshop's `Streaks` (W223-245): short lines that follow their own velocity (sparks, tracers, rain-like glints). Same swap-with-last pool as `SpritePool`. */\nexport class StreakPool {\n count = 0\n readonly point: Float32Array\n readonly velocity: Float32Array\n readonly colorStart: Float32Array\n readonly colorEnd: Float32Array\n readonly life: Float32Array\n readonly maxLife: Float32Array\n readonly length: Float32Array\n readonly gravity: Float32Array\n readonly drag: Float32Array\n readonly groundMode: Uint8Array\n readonly startAlpha: Float32Array\n /** Two end points and two colours per line: what the renderer draws. */\n readonly segment: Float32Array\n readonly segmentColor: Float32Array\n private readonly vectorArrays: readonly Float32Array[]\n private readonly scalarArrays: readonly (Float32Array | Uint8Array)[]\n private readonly direction: MutableVec3 = { x: 0, y: 0, z: 0 }\n\n constructor(readonly capacity: number, private readonly context: WorkshopContext) {\n const vectorArray = (): Float32Array => new Float32Array(capacity * FLOATS_PER_VECTOR)\n const scalarArray = (): Float32Array => new Float32Array(capacity)\n this.point = vectorArray()\n this.velocity = vectorArray()\n this.colorStart = vectorArray()\n this.colorEnd = vectorArray()\n this.life = scalarArray()\n this.maxLife = scalarArray()\n this.length = scalarArray()\n this.gravity = scalarArray()\n this.drag = scalarArray()\n this.startAlpha = scalarArray()\n this.groundMode = new Uint8Array(capacity)\n this.segment = new Float32Array(capacity * SEGMENT_FLOATS)\n this.segmentColor = new Float32Array(capacity * SEGMENT_FLOATS)\n this.vectorArrays = [this.point, this.velocity, this.colorStart, this.colorEnd]\n this.scalarArrays = [this.life, this.maxLife, this.length, this.gravity, this.drag, this.groundMode, this.startAlpha]\n }\n\n spawn(options: SpriteSpawn): void {\n if (this.count >= this.capacity) return\n const random = this.context.random\n const index = this.count++\n const offset = index * FLOATS_PER_VECTOR\n let [x, y, z] = options.p\n if (options.box) {\n x += random.between(-options.box[0], options.box[0])\n y += random.between(-options.box[1], options.box[1])\n z += random.between(-options.box[2], options.box[2])\n }\n this.point[offset] = x\n this.point[offset + 1] = y\n this.point[offset + 2] = z\n coneDirection(random, options.dir, options.cone ?? 3.2, this.direction)\n const speed = drawRange(random, options.sp)\n this.velocity[offset] = this.direction.x * speed + (options.v ? options.v[0] : 0)\n this.velocity[offset + 1] = this.direction.y * speed + (options.v ? options.v[1] : 0)\n this.velocity[offset + 2] = this.direction.z * speed + (options.v ? options.v[2] : 0)\n const start = hexToRgb(options.c0 ?? 0xffd080)\n const end = options.c1 != null ? hexToRgb(options.c1) : start\n for (let channel = 0; channel < FLOATS_PER_VECTOR; channel++) {\n this.colorStart[offset + channel] = start[channel]\n this.colorEnd[offset + channel] = end[channel]\n }\n this.life[index] = this.maxLife[index] = drawRange(random, options.life || 0.4)\n this.length[index] = options.len || 0.02\n this.gravity[index] = options.g || 0\n this.drag[index] = options.drag || 0\n this.groundMode[index] = options.gnd || 0\n this.startAlpha[index] = options.a ?? 1\n }\n\n kill(index: number): void {\n const last = --this.count\n if (index === last) return\n for (const array of this.vectorArrays) {\n for (let component = 0; component < FLOATS_PER_VECTOR; component++) array[index * FLOATS_PER_VECTOR + component] = array[last * FLOATS_PER_VECTOR + component]\n }\n for (const array of this.scalarArrays) array[index] = array[last]\n }\n\n clear(): void {\n this.count = 0\n }\n\n update(dt: number): void {\n const { point, velocity } = this\n for (let index = this.count - 1; index >= 0; index--) {\n this.life[index] -= dt\n if (this.life[index] <= 0) {\n this.kill(index)\n continue\n }\n const offset = index * FLOATS_PER_VECTOR\n const age = 1 - this.life[index] / this.maxLife[index]\n velocity[offset + 1] -= this.gravity[index] * dt\n const factor = Math.max(0, 1 - this.drag[index] * dt)\n velocity[offset] *= factor\n velocity[offset + 1] *= factor\n velocity[offset + 2] *= factor\n point[offset] += velocity[offset] * dt\n point[offset + 1] += velocity[offset + 1] * dt\n point[offset + 2] += velocity[offset + 2] * dt\n if (this.groundMode[index]) {\n const groundHeight = this.context.groundAt(point[offset], point[offset + 2])\n if (point[offset + 1] < groundHeight) {\n if (this.groundMode[index] === 1) {\n this.kill(index)\n continue\n }\n point[offset + 1] = groundHeight + 0.001\n velocity[offset + 1] *= -0.35\n velocity[offset] *= 0.6\n velocity[offset + 2] *= 0.6\n }\n }\n const length = this.length[index]\n const alpha = this.startAlpha[index] * (1 - age)\n const at = index * SEGMENT_FLOATS\n this.segment[at] = point[offset]\n this.segment[at + 1] = point[offset + 1]\n this.segment[at + 2] = point[offset + 2]\n this.segment[at + 3] = point[offset] - velocity[offset] * length\n this.segment[at + 4] = point[offset + 1] - velocity[offset + 1] * length\n this.segment[at + 5] = point[offset + 2] - velocity[offset + 2] * length\n for (let channel = 0; channel < FLOATS_PER_VECTOR; channel++) {\n const shade = lerp(this.colorStart[offset + channel], this.colorEnd[offset + channel], age) * alpha\n this.segmentColor[at + channel] = shade\n this.segmentColor[at + 3 + channel] = shade * TAIL_BRIGHTNESS\n }\n }\n }\n}\n","import type { Vec3Tuple, WorkshopContext } from './types'\n\nconst RING_CAPACITY = 120\nconst LIGHT_CAPACITY = 4\nconst UP: Vec3Tuple = [0, 1, 0]\nconst DEFAULT_LIGHT_DISTANCE = 14\n\nexport interface WaveRing {\n on: boolean\n point: Vec3Tuple\n from: number\n to: number\n t: number\n duration: number\n color: number\n normal: Vec3Tuple\n hold: number\n}\n\n/** The Workshop's expanding rings on the ground (W308-315): 120 in a ring buffer. A ring with `hold` stays at that brightness until cleared. */\nexport class WaveRingPool {\n readonly rings: WaveRing[] = Array.from({ length: RING_CAPACITY }, () => ({ on: false, point: UP, from: 0, to: 0, t: 0, duration: 1, color: 0, normal: UP, hold: 0 }))\n count = 0\n private next = 0\n\n add(point: Vec3Tuple, from: number, to: number, duration: number, color: number, normal?: Vec3Tuple, hold?: number): void {\n const ring = this.rings[this.next++ % RING_CAPACITY]!\n ring.on = true\n ring.point = [point[0], point[1], point[2]]\n ring.from = from\n ring.to = to\n ring.t = 0\n ring.duration = duration\n ring.color = color\n ring.normal = normal ?? UP\n ring.hold = hold || 0\n }\n\n clear(): void {\n for (const ring of this.rings) ring.on = false\n this.count = 0\n }\n\n update(dt: number): void {\n this.count = 0\n for (const ring of this.rings) {\n if (!ring.on) continue\n this.count++\n ring.t += dt\n if (ring.t / ring.duration >= 1 && !ring.hold) ring.on = false\n }\n }\n}\n\nexport interface FlashLight {\n x: number\n y: number\n z: number\n color: number\n distance: number\n peak: number\n duration: number\n t: number\n intensity: number\n}\n\nexport interface FireLightSlot {\n x: number\n y: number\n z: number\n intensity: number\n}\n\n/** The four short flashes of the Workshop (W333-337), taken in turn, and its two fire lights. */\nexport class FlashLightPool {\n readonly lights: FlashLight[] = Array.from({ length: LIGHT_CAPACITY }, () => ({ x: 0, y: 0, z: 0, color: 0xffc070, distance: DEFAULT_LIGHT_DISTANCE, peak: 0, duration: 1, t: 0, intensity: 0 }))\n readonly fire: FireLightSlot[] = [\n { x: 0, y: 0, z: 0, intensity: 0 },\n { x: 0, y: 0, z: 0, intensity: 0 },\n ]\n private next = 0\n\n constructor(private readonly context: WorkshopContext) {}\n\n get lit(): number {\n return this.lights.filter((light) => light.intensity > 0).length\n }\n\n flash(point: Vec3Tuple, color: number, intensity: number, duration: number, distance?: number, force?: boolean | 'boom'): void {\n const { level } = this.context.quality\n if (level >= 1 && !force) return\n if (level >= 2 && force !== 'boom') return\n const light = this.lights[this.next++ % LIGHT_CAPACITY]!\n light.x = point[0]\n light.y = point[1]\n light.z = point[2]\n light.color = color\n light.distance = distance || DEFAULT_LIGHT_DISTANCE\n light.peak = intensity\n light.duration = duration\n light.t = 0\n light.intensity = intensity\n }\n\n clear(): void {\n for (const light of this.lights) light.intensity = 0\n for (const light of this.fire) light.intensity = 0\n }\n\n update(dt: number): void {\n for (const light of this.lights) {\n if (light.intensity <= 0) continue\n light.t += dt\n const age = light.t / light.duration\n light.intensity = age >= 1 ? 0 : light.peak * (1 - age) * (1 - age)\n }\n }\n}\n","import { PROFILES } from '../budget'\nimport type { RandomStream } from '../random'\nimport { createScreenState, resetScreenState, updateScreen } from '../screen'\nimport type { ScreenState, VfxProfile } from '../types'\nimport { DebrisPool } from './debris'\nimport { DecalPool } from './decals'\nimport { SpritePool } from './sprites'\nimport { StreakPool } from './streaks'\nimport type { DebrisSpawn, DecalOptions, SpriteSpawn, SpriteSystem, Vec3Tuple, WorkshopContext, WorkshopTrace } from './types'\nimport { FlashLightPool, WaveRingPool } from './waves'\n\nconst QUALITY_MULTIPLIERS = [1, 0.5, 0.2] as const\nconst FIRE_FLICKER_SWAY = 0.25\nconst FIRE_FLICKER_JITTER = 0.08\nconst LENS_FADE = 0.16\n\n/** Water or blood on the camera lens: a soft blob that fades and runs down (W351-352). */\nexport interface LensBlob {\n x: number\n y: number\n radius: number\n color: string\n alpha: number\n fall: number\n}\n\n/** The page's `P0` (W347, W1277): what an effect asks of the post-process for this frame only; it is emptied before every frame's tick. */\nexport interface PostProcess {\n desat?: number\n tunnel?: number\n blur?: number\n cold?: number\n chroma?: number\n hue?: number\n red?: number\n tintAlpha?: number\n tintColor?: string\n}\n\n/**\n * Where an effect is put when it is played at a point instead of on the Workshop's set: what the recipe wrote at `anchor` lands at `origin`, turned\n * about the vertical axis by `yaw`. Positions are moved and turned, directions and velocities only turned.\n */\nexport interface Placement {\n readonly origin: Vec3Tuple\n readonly anchor: Vec3Tuple\n readonly yaw: number\n}\n\nexport interface WorkshopOptions {\n readonly random: RandomStream\n readonly profile?: VfxProfile\n readonly groundAt?: (x: number, z: number) => number\n readonly trace?: WorkshopTrace\n}\n\n/** The default ground of the Workshop scene: a raised kerb 12 cm high between z -4.5 and -2.9 (W29). */\nexport function workshopGround(x: number, z: number): number {\n return z < -2.9 && z > -4.5 && Math.abs(x) < 13 ? 0.12 : 0\n}\n\n/**\n * Everything the Workshop's recipes spawn into and the loop that ages it (W168-371, W1285-1287): sprites, streaks, debris, decals, rings, lights, the\n * screen state and the lens blobs, plus the quality level that thins what a recipe asks for and the accumulators that turn a rate into spawns.\n */\nexport class WorkshopEngine {\n readonly context: WorkshopContext\n readonly add: SpritePool\n readonly normal: SpritePool\n readonly streaks: StreakPool\n readonly debris: DebrisPool\n readonly decals: DecalPool\n readonly rings = new WaveRingPool()\n readonly lights: FlashLightPool\n readonly screen: ScreenState = createScreenState()\n readonly lens: LensBlob[] = []\n post: PostProcess = {}\n /** Set by whoever runs an effect for the time it runs; `undefined` leaves every number as the recipe wrote it. */\n placement: Placement | undefined\n private readonly accumulators = new Map<string, number>()\n\n constructor(options: WorkshopOptions) {\n const profile = options.profile ?? PROFILES.high\n this.context = { random: options.random, groundAt: options.groundAt ?? workshopGround, quality: { level: 0, mult: 1 }, trace: options.trace }\n this.add = new SpritePool(profile.sprites.add, this.context)\n this.normal = new SpritePool(profile.sprites.normal, this.context)\n this.streaks = new StreakPool(profile.streaks, this.context)\n this.debris = new DebrisPool(this.context)\n this.decals = new DecalPool(this.context)\n this.lights = new FlashLightPool(this.context)\n }\n\n get random(): RandomStream {\n return this.context.random\n }\n\n get quality(): number {\n return this.context.quality.level\n }\n\n setQuality(level: number): void {\n this.context.quality.level = level\n this.context.quality.mult = QUALITY_MULTIPLIERS[level] ?? 1\n }\n\n /** `n` (W340): a count thinned by the quality level, never to zero when something was asked. */\n scaled(count: number): number {\n return Math.max(count > 0 ? 1 : 0, Math.round(count * this.context.quality.mult))\n }\n\n /** A point of the recipe's space in the world (`undefined` placement: as is). */\n place(point: Vec3Tuple): Vec3Tuple {\n const placement = this.placement\n if (!placement) return point\n const { anchor, origin, yaw } = placement\n const x = point[0] - anchor[0]\n const z = point[2] - anchor[2]\n const cos = Math.cos(yaw)\n const sin = Math.sin(yaw)\n return [origin[0] + cos * x + sin * z, origin[1] + point[1] - anchor[1], origin[2] - sin * x + cos * z]\n }\n\n /** A direction or velocity of the recipe's space in the world. */\n turn(vector: Vec3Tuple): Vec3Tuple {\n const placement = this.placement\n if (!placement) return vector\n const cos = Math.cos(placement.yaw)\n const sin = Math.sin(placement.yaw)\n return [cos * vector[0] + sin * vector[2], vector[1], -sin * vector[0] + cos * vector[2]]\n }\n\n private placed<T extends { readonly p: Vec3Tuple; readonly dir?: Vec3Tuple; readonly v?: Vec3Tuple }>(options: T): T {\n if (!this.placement) return options\n return { ...options, p: this.place(options.p), ...(options.dir ? { dir: this.turn(options.dir) } : {}), ...(options.v ? { v: this.turn(options.v) } : {}) }\n }\n\n spawn(system: SpriteSystem, recipe: SpriteSpawn): void {\n const options = this.placed(recipe)\n this.context.trace?.(system, [options])\n if (system === 'add') this.add.spawn(options)\n else if (system === 'normal') this.normal.spawn(options)\n else this.streaks.spawn(options)\n }\n\n burst(system: SpriteSystem, count: number, options: SpriteSpawn): void {\n const total = this.scaled(count)\n for (let spawned = 0; spawned < total; spawned++) this.spawn(system, options)\n }\n\n /** `emit` (W343-345): `rate` per second, thinned by the quality, carried over between frames under `key`. `options` is read again for every particle. */\n emit(key: string, system: SpriteSystem, rate: number, dt: number, options: () => SpriteSpawn): void {\n let carried = (this.accumulators.get(key) ?? 0) + rate * this.context.quality.mult * dt\n while (carried >= 1) {\n carried -= 1\n this.spawn(system, options())\n }\n this.accumulators.set(key, carried)\n }\n\n debrisPiece(recipe: DebrisSpawn): void {\n const options = this.placed(recipe)\n this.context.trace?.('debris', [options])\n this.debris.spawn(options)\n }\n\n decal(recipe: readonly [number, number, number, number, number, DecalOptions?]): void {\n let args = recipe\n if (this.placement) {\n const [x, y, z] = this.place([recipe[0], recipe[1], recipe[2]])\n const options = recipe[5]\n args = [x, y, z, recipe[3], recipe[4], options?.n ? { ...options, n: this.turn(options.n) } : options]\n }\n this.context.trace?.('decal', args)\n this.decals.add(args[0], args[1], args[2], args[3], args[4], args[5])\n }\n\n ring(recipe: readonly [Vec3Tuple, number, number, number, number, Vec3Tuple?, number?]): void {\n const args = this.placement ? ([this.place(recipe[0]), recipe[1], recipe[2], recipe[3], recipe[4], recipe[5] && this.turn(recipe[5]), recipe[6]] as const) : recipe\n this.context.trace?.('ring', args)\n this.rings.add(args[0], args[1], args[2], args[3], args[4], args[5], args[6])\n }\n\n flash(recipe: readonly [Vec3Tuple, number, number, number, number?, (boolean | 'boom')?]): void {\n const args = this.placement ? ([this.place(recipe[0]), recipe[1], recipe[2], recipe[3], recipe[4], recipe[5]] as const) : recipe\n this.context.trace?.('flash', args)\n this.lights.flash(args[0], args[1], args[2], args[3], args[4], args[5])\n }\n\n /** `fireLight` (W400): sways with the clock and flickers with one draw. */\n fireLight(slot: 0 | 1, recipe: Vec3Tuple, intensity: number, time: number): void {\n const light = this.lights.fire[slot]!\n const point = this.place(recipe)\n light.x = point[0]\n light.y = point[1]\n light.z = point[2]\n light.intensity = intensity * (0.75 + FIRE_FLICKER_SWAY * Math.sin(time * 23) * Math.sin(time * 7.3) + this.random.between(-FIRE_FLICKER_JITTER, FIRE_FLICKER_JITTER))\n }\n\n lensBlob(x: number, y: number, radius: number, color: string, alpha: number): void {\n this.lens.push({ x, y, radius, color, alpha, fall: this.random.between(2, 8) })\n }\n\n /** The order of the page's loop (W1285-1287): the sprites, streaks, debris, decals, rings and lights, then the screen and its lens. */\n update(dt: number): void {\n const leaveDecal = (x: number, y: number, z: number, size: number, color: number, options: DecalOptions): void => {\n this.decal([x, y, z, size, color, options])\n }\n this.add.update(dt, leaveDecal)\n this.normal.update(dt, leaveDecal)\n this.streaks.update(dt)\n this.debris.update(dt)\n this.decals.update(dt)\n this.rings.update(dt)\n this.lights.update(dt)\n updateScreen(this.screen, dt)\n for (let index = this.lens.length - 1; index >= 0; index--) {\n const blob = this.lens[index]!\n blob.alpha -= dt * LENS_FADE\n blob.y += blob.fall * dt\n if (blob.alpha <= 0) this.lens.splice(index, 1)\n }\n }\n\n clear(): void {\n this.add.clear()\n this.normal.clear()\n this.streaks.clear()\n this.debris.clear()\n this.decals.clear()\n this.rings.clear()\n this.lights.clear()\n this.accumulators.clear()\n resetScreenState(this.screen)\n this.lens.length = 0\n this.post = {}\n }\n}\n","import type { Rgb } from './color'\n\nexport type { Rgb }\n\nexport type Vec3Like = readonly [number, number, number]\n\n/** The six layers of an explosion the viewer can turn off one by one (L478): flash and light, fire, shock wave, smoke and dust, shrapnel and debris, marks on the ground. */\nexport type VfxLayer = 'flash' | 'fire' | 'shock' | 'smoke' | 'shrapnel' | 'mark'\n\nexport const VFX_LAYERS: readonly VfxLayer[] = ['flash', 'fire', 'shock', 'smoke', 'shrapnel', 'mark']\n\n/** A chain of things that move: anything with a position, or a position. */\nexport type Follow = { readonly x: number; readonly y: number; readonly z: number } | { readonly position: { readonly x: number; readonly y: number; readonly z: number } }\n\n/** A residual fire left behind by an explosion (L798 `residual`), or started by `world.fire`. */\nexport interface FireSpec {\n /** Offsets from the blast point. */\n readonly dx?: number\n readonly dz?: number\n readonly radius: number\n readonly life: number\n readonly rate?: number\n readonly smoke?: number\n /** Grows from `startRadius` to `radius` over `grow` seconds. */\n readonly startRadius?: number\n readonly grow?: number\n /** `0xRRGGBB` of the smoke the fire gives (default soot black). */\n readonly smokeColor?: number\n}\n\nexport interface MushroomSpec {\n readonly height: number\n readonly radius: number\n readonly count: number\n readonly stem: number\n readonly life: number\n /** How fast the ring climbs towards `height` (the Lab's `rk`, default .55). */\n readonly riseRate?: number\n readonly roll?: number\n readonly color?: number\n}\n\n/** The Lab's explosion profile with English names; every number is the Lab's (`sources` lists the lines they come from). */\nexport interface ExplosionDef {\n readonly id: string\n /** Radius (m) inside which a body is torn apart, and the one inside which it is hurt. */\n readonly lethal: number\n readonly injury: number\n /** Radius of the fireball (`vis`). */\n readonly visualRadius: number\n readonly fireballs: number\n readonly fuel?: boolean\n readonly heat?: number\n readonly heatLifeScale?: number\n readonly smoke: { readonly color: number; readonly count: number; readonly life: number; readonly kind?: 0 | 3 }\n readonly dust: { readonly color: number; readonly count: number }\n readonly shrapnel: { readonly count: number; readonly kind: 0 | 1 | 2 | 3 | 4; readonly speed: number; readonly width?: number; readonly embers?: number }\n readonly chunks: { readonly count: number; readonly colors: readonly number[]; readonly plate?: boolean; readonly hot: number; readonly speed?: number; readonly size?: number }\n readonly mark: { readonly scorch?: number; readonly crater?: number; readonly dent?: number }\n readonly flash: { readonly light: number; readonly life?: number; readonly color?: number; readonly intensity?: number; readonly blinding?: boolean }\n readonly shock?: false | { readonly duration: number }\n readonly shake: number\n readonly push: number\n readonly mushroom?: MushroomSpec\n readonly residualFires?: readonly FireSpec[]\n /** Radius (m) in which characters catch fire. */\n readonly ignite?: number\n /** The pieces that fly are hot (the characters' parts burn). */\n readonly hotParts?: boolean\n readonly cone?: { readonly angle: number; readonly half: number; readonly length: number }\n readonly directionBias?: number\n readonly sources: readonly string[]\n}\n\n/** What a host does with what an effect cannot do by itself (break the windows, hurt the characters, dig the ground). */\nexport type VfxSideRequest =\n | { readonly type: 'crater'; readonly x: number; readonly z: number; readonly radius: number; readonly depth: number }\n | { readonly type: 'ignite'; readonly point: Vec3Like; readonly radius: number }\n | {\n readonly type: 'blast_front'\n readonly explosion: string\n readonly point: Vec3Like\n /** Where the glass breaks, damage and push reach: the Lab's `min(injury, 20) * .85`. */\n readonly glassRadius: number\n /** Metres per second the front travels (the speed of sound): the host delays what it breaks by `distance / speed`. */\n readonly speed: number\n readonly lethal: number\n readonly injury: number\n readonly push: number\n readonly hotParts: boolean\n readonly blinding: boolean\n readonly cone?: { readonly angle: number; readonly half: number; readonly length: number }\n }\n\nexport interface VfxProfile {\n readonly puffs: number\n readonly sparks: number\n readonly glows: number\n readonly chunks: number\n readonly sprites: { readonly add: number; readonly normal: number }\n readonly streaks: number\n readonly debris: number\n}\n\nexport type VfxProfileName = 'low' | 'medium' | 'high'\n\nexport interface LodBands {\n /** Distances (m) at which the scale drops. */\n readonly edges: readonly [number, number, number]\n /** The scale in each of the four bands. */\n readonly scales: readonly [number, number, number, number]\n /** How many visual radii are taken off the distance: a big blast stays detailed farther away. */\n readonly radiusFactor: number\n}\n\nexport interface AreaCap {\n readonly radius: number\n readonly window: number\n readonly count: number\n readonly excessScale: number\n}\n\nexport interface ScreenState {\n /** Camera shake amplitude (the Lab's `shakeAmp`). */\n shake: number\n /** White flash of the Lab, 0..1, and how fast it fades. */\n flash: number\n flashDecay: number\n white: number\n red: number\n directionLeft: number\n directionRight: number\n chroma: number\n desat: number\n gas: number\n beat: number\n}\n\nexport interface VfxStats {\n readonly puffs: number\n readonly sparks: number\n readonly glows: number\n readonly chunks: number\n readonly fires: number\n readonly shocks: number\n readonly rings: number\n /** Blast lights and fire lights that are lit. */\n readonly blastLights: number\n readonly fireLights: number\n /** What the last explosion made: puffs, sparks, chunks, lights. */\n readonly lastBlast: { readonly puffs: number; readonly sparks: number; readonly chunks: number; readonly lights: number }\n /** The scale the last explosion was made at (LOD times the area cap). */\n readonly lod: number\n /** Seconds since the last explosion, or `-1` before the first. */\n readonly blastTime: number\n}\n","import { AREA_CAP_GAME, createAreaCap } from './area-cap'\nimport { PROFILES } from './budget'\nimport { EXPLOSIONS, EXPLOSION_ALIASES } from './catalog'\nimport { hexToLinearRgb } from './color'\nimport { DEFAULT_GRAVITY, DEFAULT_WIND } from './constants'\nimport type { RandomMode, VfxCore } from './core'\nimport { addFire, createFirePool, updateFires } from './emitters/fire'\nimport type { Fire } from './emitters/fire'\nimport { GAME_EFFECTS } from './effects/catalog'\nimport { createEffectPlayer } from './effects/player'\nimport type { EffectPlayer, PlayOptions } from './effects/player'\nimport type { EffectDef } from './effects/types'\nimport { composeExplosion } from './explosion'\nimport type { GroundField } from './ground'\nimport { createLightPool, resetLightPool, updateLights } from './lights'\nimport type { LightPool } from './lights'\nimport { LOD_GAME } from './lod'\nimport { createMarkStore } from './marks'\nimport type { MarkEvent, MarkStore } from './marks'\nimport type { OrderedPool } from './pool'\nimport { updateChunks } from './pools/chunk-motion'\nimport { createChunkPool } from './pools/chunks'\nimport type { Chunk } from './pools/chunks'\nimport { createGlowPool, updateGlows } from './pools/glows'\nimport type { Glow } from './pools/glows'\nimport { createPuffPool, updatePuffs } from './pools/puffs'\nimport type { Puff, Ring } from './pools/puffs'\nimport { createRingPool, updateRings } from './pools/rings'\nimport { createShockPool, updateShocks } from './pools/shocks'\nimport type { Shock } from './pools/shocks'\nimport { createSparkPool, updateSparks } from './pools/sparks'\nimport type { Spark } from './pools/sparks'\nimport { lehmer, streamFor } from './random'\nimport type { MutableVec3, RandomStream } from './random'\nimport { createSchedule } from './schedule'\nimport { createScreenState, resetScreenState, updateScreen } from './screen'\nimport { SpriteKindSystem } from './sprites/system'\nimport type { LandListener } from './sprites/system'\nimport type { SpriteSpawnOptions } from './sprites/kind-pool'\nimport type { SpriteKind, SpriteKindDef } from './sprites/kinds'\nimport type { VfxTrace } from './trace'\nimport { WorkshopEngine } from './workshop/engine'\nimport type { Vec3Tuple } from './workshop/types'\nimport type { AreaCap, ExplosionDef, Follow, LodBands, ScreenState, VfxLayer, VfxProfile, VfxProfileName, VfxSideRequest, VfxStats, Vec3Like } from './types'\nimport { VFX_LAYERS } from './types'\n\nexport interface VfxOptions {\n readonly seed?: number\n /** `per_event` (default): every explosion draws from a stream of its own seed, so a LAN client repeats it from a few bytes. `shared`: one stream for everything, like the Lab page (the goldens). */\n readonly randomMode?: RandomMode\n readonly profile?: VfxProfileName | VfxProfile\n readonly lod?: LodBands\n /** The cap on explosions in one area; `false` turns it off. Default the game's: 6 in 4 s within 40 m, the extra ones at 0.3. */\n readonly areaCap?: AreaCap | false\n /** Height of the ground for what bounces. Default: the `ground` field, else flat at 0. */\n readonly groundAt?: (x: number, z: number) => number\n /** A deformable ground the craters dig into (see `createGroundField`). */\n readonly ground?: GroundField\n readonly wind?: Vec3Like\n readonly gravity?: number\n /** The catalogue by id (default the Lab's 16 and `generic`); build another with `patchExplosions(EXPLOSIONS, ...)`. */\n readonly explosions?: Readonly<Record<string, ExplosionDef>>\n readonly aliases?: Readonly<Record<string, string>>\n /** The Workshop's recipes by id for `play` (default `GAME_EFFECTS`; build another with `patchEffects`). */\n readonly effects?: Readonly<Record<string, EffectDef>>\n /** The sprites of fixed kinds (`burst`, `spawn`): pool size and count multiplier (default 2500 and 1; a `low` profile halves the pool and uses 0.6) and the kinds by id. */\n readonly sprites?: { readonly capacity?: number; readonly density?: number; readonly kinds?: Readonly<Record<string, SpriteKindDef>> }\n readonly reducedMotion?: boolean\n /** Hears everything that spawns, in order (the goldens use it). */\n readonly trace?: VfxTrace\n readonly onRequest?: (request: VfxSideRequest) => void\n}\n\nexport interface ExplosionOptions {\n readonly direction?: Vec3Like\n /** Repeats the same visual: used in `per_event` mode. */\n readonly seed?: number\n /** Multiplies the size of the effect (radius, marks, mushroom, fires). */\n readonly scale?: number\n /** Seconds from now until it goes off (a chain). */\n readonly delay?: number\n}\n\nexport interface FireOptions {\n readonly point: Vec3Like\n readonly radius: number\n readonly life: number\n /** Flames per second per square-ish metre (the Lab's `rate`, default 22) and smoke puffs (default .6). */\n readonly rate?: number\n readonly smoke?: number\n /** Grows from `startRadius` to `radius` over `grow` seconds. */\n readonly startRadius?: number\n readonly grow?: number\n /** `0xRRGGBB` of the smoke (default soot black). */\n readonly smokeColor?: number\n /** Moves with a thing (a burning part): the fire sits at its position plus `offset`. */\n readonly follow?: Follow\n readonly offset?: Vec3Like\n readonly seed?: number\n}\n\n/** The pools as the renderer reads them: live records, oldest first. */\nexport interface VfxPoolsView {\n readonly puffs: OrderedPool<Puff>\n readonly sparks: OrderedPool<Spark>\n readonly glows: OrderedPool<Glow>\n readonly chunks: OrderedPool<Chunk>\n readonly shocks: OrderedPool<Shock>\n readonly rings: OrderedPool<Ring>\n readonly lights: LightPool\n readonly marks: MarkStore\n readonly ground: GroundField | undefined\n}\n\nexport interface VfxWorld {\n /** Sets off an explosion of the catalogue by id (an explosive id or an alias); `null` when the id is unknown. */\n explosion(id: string, point: Vec3Like, options?: ExplosionOptions): number | null\n /** A lingering area fire (a molotov's pool, a burning part). Returns a handle for `stop`. */\n fire(options: FireOptions): number\n /**\n * Plays a recipe of the Workshop (a blood hit, a muzzle flash, a collapse) at `point`, facing `options.direction`, into the world's Workshop pools\n * (`workshop()`); `null` when the id is unknown. Returns a handle for `stop`.\n */\n play(id: string, point: Vec3Like, options?: PlayOptions): number | null\n /** The Workshop's pools (sprites, streaks, debris, decals, rings, lights, screen): what the renderer draws for `play`. Made on first use. */\n workshop(): WorkshopEngine\n /** The Workshop engine if something already made it (`play`, `workshop()`); never creates it. */\n peekWorkshop(): WorkshopEngine | undefined\n /** The ids `play` accepts. */\n effectIds(): readonly string[]\n /** `count` sprites of a kind leaving `point` around `normal` at about `speed` m/s (debris, sparks, smoke, steam, drips...). */\n burst(kind: SpriteKind | string, point: Vec3Like, normal: Vec3Like, count: number, speed?: number, options?: SpriteSpawnOptions): void\n /** One sprite of a kind with an explicit velocity. */\n spawn(kind: SpriteKind | string, point: Vec3Like, velocity: Vec3Like, options?: SpriteSpawnOptions): void\n /** Hears where the sprites of `owner` that land (drips spawned with `{ owner, tag }`) touch the ground. Returns the function that stops listening. */\n onLand(owner: string, listener: LandListener): () => void\n /** The sprite pools as the renderer reads them. */\n sprites(): SpriteKindSystem\n /** Pushes the smoke and dust within `radius` of `point` outwards, as fast as `speed` at the centre. */\n push(point: Vec3Like, radius: number, speed: number): void\n stop(handle: number): void\n setLayer(layer: VfxLayer, on: boolean): void\n layer(layer: VfxLayer): boolean\n setCamera(position: Vec3Like): void\n /** Forces the scale of every explosion from now on (`null`: back to the distance bands and the cap). */\n forceLod(scale: number | null): void\n update(dt: number): void\n screen(): Readonly<ScreenState>\n stats(): VfxStats\n pools(): VfxPoolsView\n /** The live lingering fires, oldest first. */\n fires(): readonly Fire[]\n /** Hears every mark the moment it is made, and when they are cleared. Returns the function that stops listening. */\n onMarks(listener: (event: MarkEvent) => void): () => void\n /** Seconds the world has run since it was created or cleared. */\n time(): number\n /** The state of the shared stream (`randomMode: 'shared'`): what a golden compares after a run. */\n randomState(): number\n /** Takes everything away (pools, marks, ground, timers, lights, screen) and restarts the clock. */\n clear(): void\n /** The catalogue ids `explosion` accepts, aliases included. */\n catalog(): readonly string[]\n}\n\nconst FIRE_CAPACITY = 64\nconst RING_CAPACITY = 16\nconst SHOCK_CAPACITY = 16\nconst AUTO_SEED = 0x2545f491\n\nfunction resolveProfile(profile: VfxOptions['profile']): VfxProfile {\n if (profile === undefined) return PROFILES.medium\n return typeof profile === 'string' ? PROFILES[profile] : profile\n}\n\nfunction scaleDefinition(def: ExplosionDef, factor: number): ExplosionDef {\n if (factor === 1) return def\n const mushroom = def.mushroom && { ...def.mushroom, height: def.mushroom.height * factor, radius: def.mushroom.radius * factor }\n const residual = def.residualFires?.map((fire) => ({ ...fire, radius: fire.radius * factor, dx: (fire.dx ?? 0) * factor, dz: (fire.dz ?? 0) * factor }))\n return {\n ...def,\n lethal: def.lethal * factor,\n injury: def.injury * factor,\n visualRadius: def.visualRadius * factor,\n mark: { scorch: def.mark.scorch && def.mark.scorch * factor, crater: def.mark.crater && def.mark.crater * factor, dent: def.mark.dent && def.mark.dent * factor },\n ...(mushroom ? { mushroom } : {}),\n ...(residual ? { residualFires: residual } : {}),\n }\n}\n\nexport function createVfx(options: VfxOptions = {}): VfxWorld {\n const profile = resolveProfile(options.profile)\n const sharedRandom = lehmer(options.seed ?? 1)\n const explosions = options.explosions ?? EXPLOSIONS\n const aliases = options.aliases ?? EXPLOSION_ALIASES\n const randomMode = options.randomMode ?? 'per_event'\n const wind: MutableVec3 = { x: DEFAULT_WIND.x, y: DEFAULT_WIND.y, z: DEFAULT_WIND.z }\n if (options.wind) [wind.x, wind.y, wind.z] = options.wind\n const camera: MutableVec3 = { x: 0, y: 1.6, z: 0 }\n const layers = Object.fromEntries(VFX_LAYERS.map((layer) => [layer, true])) as Record<VfxLayer, boolean>\n const ground = options.ground\n const groundAt = options.groundAt ?? (ground ? (x: number, z: number) => ground.heightAt(x, z) : () => 0)\n const handles = { next: 0, events: 0 }\n const low = options.profile === 'low'\n const spriteSystem = new SpriteKindSystem({\n capacity: options.sprites?.capacity ?? (low ? 1250 : 2500),\n density: options.sprites?.density ?? (low ? 0.6 : 1),\n seed: 0x9e3779b1 + (options.seed ?? 0),\n groundAt,\n gravity: options.gravity ?? DEFAULT_GRAVITY,\n ...(options.sprites?.kinds ? { kinds: options.sprites.kinds } : {}),\n })\n const core: VfxCore = {\n profile,\n puffs: createPuffPool(profile.puffs),\n sparks: createSparkPool(profile.sparks),\n glows: createGlowPool(profile.glows),\n chunks: createChunkPool(profile.chunks, sharedRandom),\n fires: createFirePool(FIRE_CAPACITY, sharedRandom),\n shocks: createShockPool(SHOCK_CAPACITY),\n rings: createRingPool(RING_CAPACITY),\n lights: createLightPool(),\n marks: createMarkStore(),\n screen: createScreenState(),\n layers,\n schedule: createSchedule(),\n wind,\n camera,\n ground,\n bands: options.lod ?? LOD_GAME,\n areaCap: options.areaCap === false ? undefined : createAreaCap(options.areaCap ?? AREA_CAP_GAME),\n randomMode,\n sharedRandom,\n reducedMotion: options.reducedMotion ?? false,\n gravity: options.gravity ?? DEFAULT_GRAVITY,\n trace: options.trace,\n spawned: { puffs: 0, sparks: 0, chunks: 0, lights: 0 },\n clock: { time: 0, lastBlast: -1, lod: 1, forcedLod: null },\n groundAt,\n emit: (request) => options.onRequest?.(request),\n }\n\n function definitionOf(id: string): ExplosionDef | undefined {\n return explosions[id] ?? explosions[aliases[id] ?? '']\n }\n\n function streamOf(seed: number | undefined): RandomStream {\n if (randomMode === 'shared') return sharedRandom\n handles.events++\n return streamFor(seed ?? Math.imul(handles.events, AUTO_SEED) + (options.seed ?? 0))\n }\n\n function detonate(def: ExplosionDef, point: Vec3Like, call: { direction: MutableVec3 | undefined; random: RandomStream; handle: number }): void {\n composeExplosion(core, def, { x: point[0], y: point[1], z: point[2], direction: call.direction, random: call.random, handle: call.handle })\n }\n\n let engine: WorkshopEngine | undefined\n let player: EffectPlayer | undefined\n const effects = options.effects ?? GAME_EFFECTS\n const view = { get position(): Vec3Tuple { return [camera.x, camera.y, camera.z] }, forward: (local: readonly number[]): Vec3Tuple => [local[0] ?? 0, local[1] ?? 0, local[2] ?? 0] }\n\n function workshop(): WorkshopEngine {\n return (engine ??= new WorkshopEngine({ random: sharedRandom, profile, groundAt }))\n }\n\n function effectPlayer(): EffectPlayer {\n return (player ??= createEffectPlayer({ engine: workshop(), effects, view }))\n }\n\n return {\n play(id, point, playOptions = {}) {\n if (!(id in effects)) return null\n const seed = playOptions.seed ?? (randomMode === 'shared' ? undefined : Math.imul(++handles.events, AUTO_SEED) + (options.seed ?? 0))\n return effectPlayer().play(id, [point[0], point[1], point[2]], { ...playOptions, ...(seed !== undefined ? { seed } : {}) })\n },\n burst: (kind, point, normal, count, speed, spriteOptions) => spriteSystem.burst(kind, point, normal, count, speed, spriteOptions),\n spawn: (kind, point, velocity, spriteOptions) => spriteSystem.spawn(kind, point, velocity, spriteOptions),\n onLand: (owner, listener) => spriteSystem.onLand(owner, listener),\n sprites: () => spriteSystem,\n workshop,\n peekWorkshop: () => engine,\n effectIds: () => Object.keys(effects),\n explosion(id, point, explosionOptions = {}) {\n const base = definitionOf(id)\n if (!base) return null\n const def = scaleDefinition(base, explosionOptions.scale ?? 1)\n const handle = ++handles.next\n const direction = explosionOptions.direction && { x: explosionOptions.direction[0], y: explosionOptions.direction[1], z: explosionOptions.direction[2] }\n const random = streamOf(explosionOptions.seed)\n if (explosionOptions.delay) core.schedule.at(core.clock.time + explosionOptions.delay, () => detonate(def, point, { direction, random, handle }))\n else detonate(def, point, { direction, random, handle })\n return handle\n },\n fire(fireOptions) {\n const handle = ++handles.next\n const { point, offset, seed, radius, smokeColor, ...rest } = fireOptions\n addFire(core, streamOf(seed), {\n ...rest, r: radius, x: point[0], y: point[1], z: point[2], fx: offset?.[0] ?? 0, fy: offset?.[1] ?? 0, fz: offset?.[2] ?? 0,\n ...(smokeColor !== undefined ? { color: hexToLinearRgb(smokeColor) } : {}),\n }, handle)\n return handle\n },\n push(point, radius, speed) {\n spriteSystem.push(point, radius, speed)\n for (const puff of core.puffs.items) {\n const dx = puff.x - point[0]\n const dy = puff.y - point[1]\n const dz = puff.z - point[2]\n const distance = Math.sqrt(dx * dx + dy * dy + dz * dz)\n if (distance >= radius) continue\n const strength = (speed * (1 - distance / radius)) / (distance + 0.001)\n puff.vx += dx * strength\n puff.vy += dy * strength\n puff.vz += dz * strength\n }\n },\n stop(handle) {\n player?.stop(handle)\n const fires = core.fires\n for (let index = fires.items.length - 1; index >= 0; index--) if (fires.items[index]!.handle === handle) fires.removeAt(index)\n },\n setLayer(layer, on) {\n layers[layer] = on\n },\n layer: (layer) => layers[layer],\n setCamera(position) {\n camera.x = position[0]\n camera.y = position[1]\n camera.z = position[2]\n },\n forceLod(scale) {\n core.clock.forcedLod = scale\n },\n update(dt) {\n core.clock.time += dt\n core.schedule.update(core.clock.time)\n updateRings(core.rings, dt)\n updatePuffs(core.puffs, dt, wind)\n updateSparks(core.sparks, { dt, groundAt })\n updateGlows(core.glows, dt)\n updateShocks(core.shocks, core.puffs.items, dt)\n updateChunks(core, dt)\n updateFires(core, dt)\n updateLights(core.lights, { dt, flashLayer: layers.flash ? 1 : 0, clock: core.clock.time, fires: core.fires.items })\n updateScreen(core.screen, dt)\n spriteSystem.update(dt)\n if (engine) {\n engine.post = {}\n player?.update(dt)\n engine.update(dt)\n }\n },\n screen: () => core.screen,\n stats() {\n const lights = core.lights\n return {\n puffs: core.puffs.count,\n sparks: core.sparks.count,\n glows: core.glows.count,\n chunks: core.chunks.count,\n fires: core.fires.count,\n shocks: core.shocks.count,\n rings: core.rings.count,\n blastLights: lights.blast.filter((light) => light.intensity > 0.05).length,\n fireLights: lights.fire.filter((light) => light.intensity > 0.05).length,\n lastBlast: { ...core.spawned },\n lod: core.clock.lod,\n blastTime: core.clock.lastBlast < 0 ? -1 : core.clock.time - core.clock.lastBlast,\n }\n },\n pools: () => ({\n puffs: core.puffs, sparks: core.sparks, glows: core.glows, chunks: core.chunks, shocks: core.shocks, rings: core.rings, lights: core.lights, marks: core.marks, ground,\n }),\n fires: () => core.fires.items,\n onMarks: (listener) => core.marks.subscribe(listener),\n time: () => core.clock.time,\n randomState: () => sharedRandom.state,\n clear() {\n for (const pool of [core.puffs, core.sparks, core.glows, core.chunks, core.fires, core.shocks, core.rings] as OrderedPool<object>[]) pool.clear()\n core.marks.clear()\n core.schedule.clear()\n core.areaCap?.clear()\n ground?.reset()\n resetLightPool(core.lights)\n resetScreenState(core.screen)\n spriteSystem.clear()\n player?.clear()\n engine?.clear()\n core.clock.time = 0\n core.clock.lastBlast = -1\n core.clock.lod = 1\n core.spawned.puffs = core.spawned.sparks = core.spawned.chunks = core.spawned.lights = 0\n },\n catalog: () => [...new Set([...Object.keys(explosions), ...Object.keys(aliases)])],\n }\n}\n","{\n \"spall\": \"impact_{material}\",\n \"stage\": { \"1\": \"stage_crack\", \"2\": \"breach_dust\", \"3\": \"collapse_dust\" },\n \"expose\": \"rebar_expose\",\n \"collapse\": \"collapse_dust\",\n \"explode\": \"explosion\",\n \"fracture\": { \"glass\": \"glass_burst\" }\n}\n","import eventData from '../data/event-effects.json'\n\n/** What a game event looks like to the map: the kind of event and the little that picks its effect. */\nexport interface GameEvent {\n /** `spall`, `stage`, `expose`, `collapse`, `explode`, `fracture`... (strings, so there is no compile dependency on the destruction package). */\n readonly kind: string\n /** The stage of a `stage` event (1, 2 or 3). */\n readonly stage?: number\n /** The material of a `spall` or `fracture` event. */\n readonly material?: string\n}\n\ntype EventMap = Readonly<Record<string, string | Readonly<Record<string, string>>>>\n\nexport const EVENT_EFFECTS_MAP: EventMap = eventData\n\n/**\n * The effect id that plays for an event (`undefined` when the map has none). A string entry may use `{material}`; an object entry is chosen by the event's\n * stage or material. A game passes its own map to add or change entries.\n */\nexport function effectForEvent(event: GameEvent, map: EventMap = EVENT_EFFECTS_MAP): string | undefined {\n const entry = map[event.kind]\n if (entry === undefined) return undefined\n if (typeof entry === 'string') return event.material || !entry.includes('{material}') ? entry.replace('{material}', event.material ?? '') : undefined\n const key = event.stage !== undefined ? String(event.stage) : event.material\n return key === undefined ? undefined : entry[key]\n}\n","import type { EffectTexts } from '../locale'\n\n/** The Workshop's sheet for each effect, in English (the pt-BR texts are the page's own). */\nexport const en: Readonly<Record<string, EffectTexts>> = {\n blood_hit: {\n name: 'Blood by infection age',\n short: 'A mist on the way in, a jet on the way out, a pool that grows. Age changes colour and amount.',\n cost: '≤ 70 part.',\n costNote: '1 pool · 6 splashes',\n paramLabel: 'Age',\n when: 'Every bullet or blow that hits a zombie. The blood value comes from the bestiary age: 1.0 · 0.7 · 0.35 · 0.1.',\n look: 'Fresh sprays bright red and drips. Recent is burgundy. Old is brown and sparse. Decrepit does not bleed: it gives off black dust and dry flakes. The pool grows for 2 to 4 s and darkens in 60 s.',\n budget: 'Up to 70 particles per hit (Fresh), 1 pool and up to 6 splashes on the ground. Drops that land become decals.',\n network: 'Cosmetic. Goes in the impact event with 2 bits of age and the seed.',\n },\n headshot: {\n name: 'Headshot',\n short: 'The head bursts, the eyes go out on the same frame and the body drops.',\n cost: '≤ 70 part.',\n costNote: '8 debris · 1 pool',\n when: 'A head hit that kills (bestiary rule: a head kills at once).',\n look: 'A red mist around the head, a jet of drops backwards, 6 to 8 pieces of bone and skin, a swap to the burst-neck mesh. The eye glow goes out on the same frame, which is the most readable signal from afar.',\n budget: '20 mist + 35 drops + 8 debris + 1 pool. In the horde only the 12 closest get debris.',\n network: 'Cosmetic. The server already sends \"death by head\".',\n },\n dismember: {\n name: 'Dismemberment',\n short: 'The arm comes off whole with physics and the shoulder gushes for half a second.',\n cost: '≤ 60 part.',\n costNote: '1 limb · 4 splashes',\n when: 'Damage to a limb above the threshold (multipliers by region from the bestiary) or an axe blow.',\n look: 'The limb becomes a loose piece with physics and bounces. A short pulsing jet from the shoulder, drops that form a trail to the pool. The zombie staggers but keeps walking.',\n budget: '1 limb with physics (counts toward the cap of 300), a jet of up to 60 drops in 0.7 s.',\n network: 'The server sends which limb came off. The piece is local; each client simulates its own.',\n },\n bloater_cloud: {\n name: 'Bloater cloud',\n short: 'The belly pulses for 0.5 s, bursts and leaves 4 m of gas for 8 s.',\n cost: '≤ 180 part.',\n costNote: 'server entity',\n when: 'Death of the Bloater, by any cause.',\n look: 'A 0.5 s warning with the belly swelling, a burst with pieces and a greenish jet, and a low moss-green cloud that undulates on the ground for 8 s. Loose green glows inside the cloud mark the area.',\n budget: '40 large sprites at the burst and up to 60 alive in the cloud, at half resolution. Fade near the camera so it does not paint the whole screen.',\n network: 'Server entity (radius 4 m, 8 s): blinds for 5 s, doubles the scent trail and makes people cough. The client only draws.',\n },\n screamer_shriek: {\n name: 'Screamer shriek',\n short: 'A 1.5 s wind-up with steam, a wave on the ground and the horde\\'s eyes light up.',\n cost: '≤ 150 part.',\n costNote: '3 rings · aberration',\n when: 'The Screamer spots the player and starts the shriek (1.5 s wind-up, calls everyone within 150 m).',\n look: 'Head back and steam from the mouth during the wind-up. On the shriek, three pressure rings on the ground, dust raised in the radius, a short chromatic aberration on the screen of whoever is near, and the eyes of nearby zombies flare when they hear.',\n budget: '3 rings, up to 150 dust particles. The screen aberration is a 0.4 s post effect.',\n network: 'The server sends \"shriek at X\". The screen effect depends on local distance.',\n },\n eyes_in_fog: {\n name: 'Eyes in the fog',\n short: 'Eye glow that cuts through the fog; runners leave a red trail.',\n cost: '2 sprites/zombie',\n costNote: '0 lights',\n when: 'Always, on every zombie. It is the bestiary reading system: eye colour tells the category.',\n look: 'Two emissive points with bloom that ignore the fog. The body disappears in the mist at 25 m, the eyes stay readable at 60 m. Runners leave a short trail in the eye colour when they run.',\n budget: '2 sprites per zombie in the same instanced batch as the body. Trail only for runners closer than 30 m. No real light.',\n network: 'Nothing goes over the network: it comes from the zombie category.',\n },\n corpse_decay: {\n name: 'Corpse: darken and dissolve',\n short: '15 minutes in 8 seconds: fall, darken, flies and turn to dust.',\n cost: '≤ 40 part.',\n costNote: 'pool of 400 bodies',\n when: 'Zombie death. Bestiary timeline: ragdoll 3 s, frozen mesh, darken, dissolve between 14 and 15 min.',\n look: 'The skin loses colour until almost black, the pool dries to brown, flies appear from minute 5 and in the last minute the body sinks and gives off dark dust.',\n budget: 'Flies: 6 particles per body, only closer than 10 m. Dust: 40 particles once. The body uses the dissolve shader, no extra particle.',\n network: 'The server sends the time of death; each client works out the state.',\n },\n reanimation: {\n name: 'Reanimation',\n short: 'Veins light up, spasms, the eyes switch on and the body gets up.',\n cost: '≤ 30 part.',\n costNote: 'emissive in the shader',\n when: 'End of infection of a player or NPC: reanimates in 2 to 6 min as Fresh (70% Slow, 30% Runner).',\n look: 'Green pulses through the skin (emissive in the shader, not particles), spasms in the arms and head, the eyes light up from the inside out and the body stands. Breath as steam on the first shriek.',\n budget: 'Only animated emissive and 30 particles. It is rare and important, so it may be expensive.',\n network: 'The server sends \"reanimated\". For the friend who sees a teammate get up, it is the strongest moment of co-op.',\n },\n muzzle_flash: {\n name: 'Muzzle flash',\n short: 'Core, petals and smoke scaled by weapon. The Barrett raises dust.',\n cost: '≤ 60 part.',\n costNote: '1 light · 50 ms',\n paramLabel: 'Weapon',\n when: 'Every shot. In first person it uses the weapon model; from afar it is only the core sprite.',\n look: 'Three layers: a white-yellow core of 1 frame, petals along the barrel and grey smoke that stays 1 to 2 s. The 12-gauge has a wide flash and thick smoke. The Barrett vents two side jets through the muzzle brake and raises a ring of dust from the ground. The light illuminates the room for 50 ms (request from the map brief).',\n budget: 'Up to 60 particles and 1 light per shot. An AK burst uses the same light in a loop. A pistol has no light beyond 15 m.',\n network: 'Cosmetic. It comes from the shot event the server already sends (weapon, origin, direction).',\n },\n impact: {\n name: 'Impact by material',\n short: 'Concrete, metal, wood, glass, flesh and water: each with its own response.',\n cost: '≤ 40 part.',\n costNote: '1 hole',\n paramLabel: 'Material',\n when: 'Every bullet that hits something. The material comes from the mesh surface tag.',\n look: 'Concrete: pale dust in a cone and grey chips. Metal: a shower of bouncing sparks, no dust, with a short light. Wood: thin splinters and brown dust. Glass: glint, shards and a crack. Flesh: blood by age. Water: a crown of drops and two rings.',\n budget: '14 dust + 6 chips + up to 18 sparks + 1 hole. At 40 m only the dust remains.',\n network: '12-byte impact event with the material in 4 bits.',\n },\n penetration: {\n name: 'Bullet through the horde',\n short: 'Barrett tracer passing through 5 zombies, in 10x slow motion.',\n cost: '≤ 250 part.',\n costNote: 'aggregate event',\n when: 'Weapon with penetration: the AK goes through 2, the Remington 700 through 3, the Barrett through 6 and cars.',\n look: 'A tracer with a hot-air trail, blood exiting each body in the order the bullet passes (each in the colour of its age), bodies falling one after another and a final impact on the concrete.',\n budget: 'All hits of a shot go in a single aggregate event with the list of points. 5 bodies = 5 exits + 5 pools.',\n network: 'The server sends the shot and the ordered list of hits.',\n },\n casings_and_magazine: {\n name: 'Casings and magazine',\n short: 'Brass that bounces and stays on the ground, an empty magazine falling, smoke from the hot barrel.',\n cost: '200/player',\n costNote: 'physics 1.5 s',\n when: 'Every shot of a semi-automatic weapon, every reload, and the pump of the 12-gauge or the rifle bolt.',\n look: 'The casing leaves the ejection port spinning, bounces on the asphalt two or three times and stays. The empty magazine drops on reload. After a burst, thin smoke rises from the hot barrel for 2 s.',\n budget: 'Physics only for 1.5 s, then it becomes a static instance. Cap of 200 per player, the oldest goes first.',\n network: 'Cosmetic, each client generates its own. Ground position is not synchronised.',\n },\n molotov: {\n name: 'Molotov cocktail',\n short: 'A lit wick in the arc, glass breaking and a pool of fire that spreads.',\n cost: '≤ 300 part.',\n costNote: '2 lights · entity',\n when: 'A molotov hitting the ground or a zombie.',\n look: 'A bottle in an arc with the wick giving off flame and smoke. On impact, green shards and a splash of fire that opens from 0.5 m to 2.2 m in 0.8 s. Low flames, embers, dark smoke and a flickering light for 6 s, then embers and a burn mark.',\n budget: 'Area fire with up to 300 particles and 1 fire light. Burning zombies use a small emitter attached to the body (40 particles) and only the 8 closest.',\n network: 'Fire is a server entity: area, damage per second, time. The client draws from it.',\n },\n grenade: {\n name: 'Fragmentation grenade',\n short: 'A bounce on the asphalt, flash, shock wave, shrapnel and a ring of dust.',\n cost: '≤ 400 part.',\n costNote: 'aggregate event',\n when: 'A grenade exploding (1.8 s fuse after the throw).',\n look: 'The grenade bounces twice. A layered explosion: a white flash that paints the screen, a short fireball, a shock ring on the ground and another facing the camera, bright shrapnel that bounces, a ring of low dust, grey smoke that rises and a burn mark. Zombies in the radius fall outwards.',\n budget: 'Up to 400 particles, 1 strong light of 0.5 s, 16 debris, 1 mark. The 40 zombies hit go in a single aggregate event.',\n network: 'The server sends the explosion and the list of those hit. Visual is local.',\n },\n explosive_barrel: {\n name: 'Explosive barrel',\n short: 'A mushroom of fire, the lid flying, petrol burning on the ground and a column of smoke.',\n cost: '≤ 450 part.',\n costNote: '2 lights · fire entity',\n when: 'A red barrel with damage above the threshold, a shot or fire touching it.',\n look: 'Everything the grenade does, plus: a fireball that rises into a mushroom, the lid and red pieces flying high, three pools of petrol burning for 5 s and a black smoke column that serves as a landmark from afar.',\n budget: 'Peak of 450 particles. The smoke column stays visible beyond 80 m with 6 large sprites.',\n network: 'Explosion + a fire entity in the pools.',\n },\n shopfront_glass: {\n name: 'Shop window bursting',\n short: 'Two cracks, then the whole pane comes down in shards that stay on the pavement.',\n cost: '≤ 80 debris',\n costNote: '1 crack',\n when: 'Glass with accumulated damage (shot, blow, horde pushing). A 20 m noise that draws the horde.',\n look: 'The first blows open star cracks in the glass texture. At the burst the pane vanishes and becomes 60 to 80 shards that fall spinning, glinting in the light, and stay on the pavement. A veil of fine glass dust in front.',\n budget: 'Shards with physics count toward the cap of 300 and become static in 2 s. 3 crack stages in a single mask.',\n network: 'The server sends the glass state (0 to 3). Shards are local.',\n },\n door_stages: {\n name: 'Door giving way in 3 stages',\n short: 'Blows shake dust from the frame, a plank cracks and the door falls.',\n cost: '≤ 120 part.',\n costNote: '3 stages',\n when: 'The horde hitting a door or barricade (the Brute does 3x damage). Stages: 66%, 33%, 0% health.',\n look: 'Each blow shakes the door and loosens dust from the frame and splinters. At stage 2 the middle plank cracks and falls. At 3 the door topples and raises a cloud of low dust, and the zombie comes out of the dark.',\n budget: 'Dust: 12 particles per blow. Fall: 40 of dust and 6 debris. Blows beyond 15 m have no particles, only sound.',\n network: 'The server sends the door health. The visual stage comes from the value.',\n },\n building_collapse: {\n name: 'Collapse',\n short: 'Dust trickling from the awning, blocks falling and a cloud of dust rolling down the street.',\n cost: '≤ 350 part.',\n costNote: '26 debris',\n when: 'A structure that loses its health: explosion, vehicle impact, a big horde pushing (destruction of the map buildings).',\n look: 'Before: fine dust trickling and pebbles falling for 1 s, as a warning. Then: large and small blocks fall, the camera shakes and a cloud of grey-beige dust rolls along the ground and hangs in the air for 5 s, hiding zombies.',\n budget: '26 debris (6 large), 70 dust sprites at half resolution. A rare event, it fits the budget.',\n network: 'The server sends the collapse and the dust area, which blocks zombie sight for 4 s.',\n },\n block_wall: {\n name: 'Wall brought down with holes',\n short: 'A burst leaves holes and chips; the wall falls in blocks and the holes stay on them.',\n cost: '≤ 300 part.',\n costNote: '30 blocks · attached holes',\n when: 'A concrete wall, low wall or barricade that loses health from shots, explosion, the Brute or a car (map and arsenal destruction).',\n look: 'Each bullet opens a dark hole with a chipped rim, giving off dust and pebbles. When the wall gives way it splits into blocks that topple with physics and a cloud of dust rolls along the ground. The bullet holes are attached to the blocks: they stay on the fallen pieces, turned with them, and do not vanish when the wall breaks.',\n budget: 'Holes are decals that are children of the piece (not of the world), up to 12 per block and 128 in total. Blocks count toward the cap of 300 debris and become static at rest. Dust: 60 sprites at half resolution.',\n network: 'The server sends the wall health and the list of holes (2-byte local position per hole). Whoever joins later sees the same holes on the same pieces. The blocks fall using a common seed.',\n },\n metal_door: {\n name: 'Metal door denting',\n short: 'Each blow sinks the plate, flakes the paint and throws sparks; the hinge bursts.',\n cost: '≤ 160 part.',\n costNote: 'deformable mesh',\n when: 'A door, gate or steel plate taking a zombie blow (Brute 3x), a 12-gauge shot or a car hit.',\n look: 'The plate sinks where it is hit (a deformed mesh, not a model swap), with 3 visible levels. Each blow gives off sparks, flakes of green paint and rust dust. When the health runs out the top hinge bursts in sparks, the door topples and scrapes on the ground giving off more sparks.',\n budget: 'Per-vertex deformation only at the moment of the blow (CPU, 400 vertices). Per blow: 8 sparks, 5 flakes, 6 of rust. Fall: 30 sparks and 40 of dust.',\n network: 'The server keeps 3 bits of dent per door zone (9 zones). Whoever joins later sees the same dented door.',\n },\n iron_railing: {\n name: 'Iron railing bending',\n short: 'The horde pushes, the bars bow, the weld pops in a spark and the railing falls.',\n cost: '≤ 200 part.',\n costNote: '3 deformable bars',\n when: 'A railing, iron gate, handrail or chain-link fence with zombies pushing (horde pressure) or a car hitting it.',\n look: 'The bars bow gradually at the point of pressure and the vertical ones tilt with them. Rust falls in a thread during the strain. At two moments the weld pops with a crackle of sparks. At the end the middle post gives and the railing topples, leaving the bent iron on the ground.',\n budget: 'Curvature worked out only when the pressure changes, up to 180 vertices. Rust: 10 per second during the strain. Weld pop: 20 sparks.',\n network: 'The server sends the pressure (0 to 1) and when it gives. The curve comes from the value, the same on all clients.',\n },\n wooden_fence: {\n name: 'Wooden fence breaking',\n short: 'Planks snap one by one, chips and dust, and the section topples as the horde passes.',\n cost: '≤ 150 part.',\n costNote: '12 planks',\n when: 'A fence, yard gate or plank barricade taking a shot, blow or horde pressure.',\n look: 'Each plank snaps at a random point: the stump stays on the rail and the top piece flies off with chips and wood dust. The zombie appears through the gap. At the end the rail cracks and the whole section topples forward.',\n budget: 'Each plank is a piece: breaking swaps it for a stump + a piece with physics. Per break: 6 chips, 8 of dust. Topple: 35 of dust.',\n network: 'The server sends which planks broke (12 bits) and when the section falls.',\n },\n hit_point_breakage: {\n name: 'Breakage by the point hit',\n short: 'A shot to the leg, a machete on the corner, a bat on the top and a 12-gauge up close: each weapon breaks the part it hit.',\n cost: '≤ 120 part.',\n costNote: 'parts with their own health',\n when: 'Any destructible item. The damage goes to the part that was hit, not to the whole item.',\n look: 'A 9 mm shot to the front leg: the lower part of the leg breaks off and the table tips toward that corner. A machete on the corner of the top: the corner comes off whole in a straight cut, with chips in a line. A bat on the other corner: the top cracks and falls apart in pieces with dust. A 12-gauge up close on what is left: 9 grouped holes that stay in the wood.',\n budget: 'Each part has health and material. A common item: 4 to 12 parts. Particles only from the part hit, by its material.',\n network: 'The server sends which part lost health and how much. A loose part simulates on the client; the final position is sent once.',\n },\n pressure_cylinders: {\n name: 'Extinguisher and oxygen cylinder',\n short: 'A shot to the extinguisher makes a white cloud and a spin; on oxygen the valve breaks and the cylinder becomes a rocket.',\n cost: '≤ 250 part.',\n costNote: 'cloud entity',\n paramLabel: 'Cylinder',\n when: 'A shot, a strong blow or fire on an extinguisher, oxygen cylinder or welding cylinder (extinguisher and oxygen_cylinder keys of the map catalogue).',\n look: 'Extinguisher: the hole gives off a jet of white powder, the extinguisher falls, spins on the ground pushed by the jet and fills a 5 m area with a cloud that blocks sight for 6 s. Oxygen: the valve breaks with a crack, the jet becomes propulsion, the cylinder shoots along the ground scraping and throwing sparks, bounces and stops against a wall. With fire nearby, the oxygen blows up like the barrel.',\n budget: 'Jet: 60 particles per second for 3 s. Extinguisher cloud: up to 40 large sprites. Scraping sparks like the car.',\n network: 'The extinguisher cloud is a server entity (blocks zombie and player sight). The cylinder trajectory uses a common seed and the server corrects the final position.',\n },\n building_fire: {\n name: 'Building on fire',\n short: 'Fire coming out of the windows, licking the facade, embers and smoke with the wind.',\n cost: '≤ 600 part.',\n costNote: '2 lights · reference',\n when: 'A building fire (molotov, explosion, vehicle). It lasts minutes and spreads to the neighbour.',\n look: 'Windows lit orange that flicker, tongues of fire coming out of the top of each window and climbing the facade, embers carried by the wind and a thick slanted column of smoke. The light paints the street.',\n budget: 'Up to 600 particles per building and 2 fire lights. Beyond 80 m only the smoke column and the glow of the windows remain, which become a landmark.',\n network: 'A fire entity on the server per building, with a spread state.',\n },\n scraping_sparks: {\n name: 'Scraping sparks',\n short: 'A bumper dragging on the asphalt: a river of bouncing sparks and a mark on the ground.',\n cost: '≤ 200 sparks/s',\n costNote: '1 light',\n when: 'A car part in contact with the ground or a wall (a fallen bumper, a rim without a tyre, a flipped car sliding).',\n look: 'A jet of sparks behind the movement, bouncing on the asphalt and dying from white to orange. A glow and a short light at the contact point. A dark scratched mark on the ground.',\n budget: 'Up to 200 sparks per second, 1 light. Beyond 40 m it is only the glow of the point.',\n network: 'Cosmetic, from the part state and speed that are already replicated.',\n },\n engine_smoke: {\n name: 'Engine smoke',\n short: 'White steam, grey smoke, then black with fire, depending on condition.',\n cost: '≤ 120 part.',\n costNote: 'continuous',\n paramLabel: 'Condition',\n when: 'Whenever the engine is on or hot, from the part condition (0 to 100) in the fleet brief.',\n look: '100: exhaust only. 60: a thread of white steam from the bonnet. 30: steady grey smoke and the odd spark. 10: thick black smoke, flames at the grille and fire light, a warning that it will explode.',\n budget: 'Continuous, so cheap: up to 40 particles per second and 1 light only in the critical state.',\n network: 'Comes from the engine condition, which is already replicated.',\n },\n fuel_leak: {\n name: 'Tank leak and fire',\n short: 'Dripping petrol, a pool, a spark and the fire running to the tank.',\n cost: '≤ 400 part.',\n costNote: 'fire entity',\n when: 'A tank punctured by a shot or a crash. A spark, a shot or nearby fire lights the pool.',\n look: 'Drops of petrol with an oily sheen forming a growing pool. A shot on the metal throws a spark, the fire lights on the pool, runs to the car and, if nobody puts it out, the car explodes.',\n budget: 'Drops: 12 per second. Pool fire like the molotov. The final explosion is the next effect.',\n network: 'The pool and the fire are server entities (they can burn zombies and players).',\n },\n car_explosion: {\n name: 'Car explosion',\n short: 'A fireball, the car jumping, the bonnet and doors flying, the shell burning.',\n cost: '≤ 600 part.',\n costNote: 'rare event',\n when: 'A tank on fire for more than 4 s, or damage above the threshold on a car with fuel.',\n look: 'A large explosion (grenade scale 1.8), the car jumps and lands crooked, the bonnet, doors and a wheel fly off, the glass bursts into shards. The bodywork goes black and the shell burns with black smoke for minutes.',\n budget: 'Peak of 600 particles and 34 debris. It is a rare event and may go over the per-event cap, never the global cap.',\n network: 'Explosion on the server. The car becomes a wreck (another prefab) for everyone.',\n },\n windshield: {\n name: 'Windshield: blood and cracks',\n short: 'View from inside the car: blows, cracks in 4 stages and a bloody hand.',\n cost: '0 particles',\n costNote: '4-stage mask',\n when: 'A zombie hitting the windshield or a side window (fleet brief: the glass takes 8 blows).',\n look: 'Each blow shakes the camera and adds a star crack. Stages 2 and 4 leave a blood stain with a handprint running down. At stage 4 the glass is white with cracks and the next blow breaks it.',\n budget: 'Texture only: a crack mask with 4 stages and a blood decal on the glass. Glass dust: 6 particles per blow.',\n network: 'The server sends the glass health. Blood on the glass is local.',\n },\n damage_and_bite: {\n name: 'Damage and bite',\n short: 'A red edge on the side of the blow, blood on the lens and the pulse of the bite.',\n cost: 'post-process',\n costNote: 'local only',\n when: 'The player takes a blow (with direction) or a bite (100% infection, characters rule).',\n look: 'Blow: a red edge on the side it came from, a short shake and splashes on the lens. Bite: a red flash over the whole screen, the colour drains for 2 s, a heartbeat pulsing in a dark tunnel and big blood on the lens that runs down.',\n budget: 'A post-process effect and one lens layer. No particles in the world besides the normal blood.',\n network: 'Only on the screen of whoever was hit. The others see the blood in the world.',\n },\n infection_phases: {\n name: 'Infection phases',\n short: 'Malaise, fever with a cold edge, delirium with ghosts and the end.',\n cost: 'post-process',\n costNote: 'local only',\n paramLabel: 'Phase',\n when: 'Phases from the characters brief: 2–12 h malaise, 12–30 h fever, 30–48 h delirium, 48 h death.',\n look: 'Malaise: slightly washed colour and a slowly breathing vignette. Fever: a bluish cold edge only for the infected, waves of blur, a cough that shakes the camera. Delirium: colour aberration, an oscillating hue and fake zombies flickering in the fog. 48 h: almost no colour, a green tone, a dark tunnel and a heartbeat.',\n budget: 'Post-process only. The delirium ghosts are 3 zombie instances without collision, only on the infected client.',\n network: 'Never goes over the network. The other player notices only the cough and the breathing (see \"Cough and breath\").',\n },\n panic: {\n name: 'Panic',\n short: 'The aim shakes, the tunnel closes as the horde arrives and the heart speeds up.',\n cost: 'post-process',\n costNote: 'local only',\n when: 'The characters\\' panic meter (rises with visible zombies, runners and a dead companion). Effects above 60 and above 90.',\n look: 'A fine, constant tremor in the camera (the aim shakes), a dark vignette that closes as the nearest zombie arrives and pulses with the heartbeat, which speeds up from 70 to 150 bpm. Colour loses strength.',\n budget: 'Post-process and camera noise only.',\n network: 'Local. Others\\' panic shows in their aim and in the scream (sound).',\n },\n bloater_blindness: {\n name: 'Bloater blindness',\n short: 'Green gas in the face: a blurred screen for 5 s and mucus on the lens.',\n cost: 'post + 30 part.',\n costNote: 'server entity',\n when: 'A player inside the Bloater cloud (radius 4 m) when it spawns.',\n look: 'A green cloud bursts between the zombie and the camera, the screen goes green and blurred and clears in 5 s, with drops of green mucus on the lens that run down. A cough shakes the camera twice.',\n budget: 'Post-process + 30 gas sprites near the camera, with a fade so particles do not cover the whole screen.',\n network: 'The blindness comes from the server entity; the visual is local.',\n },\n campfire: {\n name: 'Campsite fire',\n short: 'A low flame, embers spiralling up and a flickering warm light.',\n cost: '≤ 120 part.',\n costNote: '1 fire light',\n when: 'A campfire lit by the group or by NPCs. It warms, lights and draws zombies from afar (light).',\n look: 'Small low flames, embers that rise turning and fade, thin smoke and a warm light that flickers and paints the survivor.',\n budget: '120 particles and 1 fire light. Several campfires of the camp share the same light if they are closer than 6 m.',\n network: 'A replicated scene object (lit or out). Visual is local.',\n },\n cough_and_breath: {\n name: 'Cough, breath and flashlight',\n short: 'Breath vapour in the cold, the fever cough visible to others and the flashlight beam.',\n cost: '≤ 40 part.',\n costNote: '1 spot with shadow',\n when: 'A cold night: breath always. Infection fever: a cough every few seconds, audible at 10 m.',\n look: 'A short puff of vapour every 2.4 s. On a cough, three quick bigger puffs and the body jolts. The flashlight shows the beam in the fog and is the only light with a shadow.',\n budget: 'Vapour: 8 particles per puff, only closer than 15 m. Flashlight: 1 spot with a shadow per player, only the local player\\'s casts a shadow.',\n network: 'The cough is the social signal of the infection: the server sends the cough event (without saying it is infection) and the others see the vapour and hear it.',\n },\n}\n","import type { EffectTexts } from '../locale'\n\n/** The Workshop's sheet for each effect (W450-1262), as the page wrote it. */\nexport const ptBR: Readonly<Record<string, EffectTexts>> = {\n blood_hit: {\n name: 'Sangue por idade da infecção',\n short: 'Névoa na entrada, jato na saída, poça que cresce. A idade muda cor e quantidade.',\n cost: '≤ 70 part.',\n costNote: '1 poça · 6 respingos',\n paramLabel: 'Idade',\n when: 'Cada acerto de bala ou golpe em zumbi. O valor de sangue vem da idade do bestiário: 1,0 · 0,7 · 0,35 · 0,1.',\n look: 'Fresco espirra vermelho vivo e pinga. Recente sai bordô. Antigo sai marrom e pouco. Decrépito não sangra: solta pó preto e lascas secas. A poça cresce por 2 a 4 s e escurece em 60 s.',\n budget: 'Até 70 partículas por acerto (Fresco), 1 poça e até 6 respingos no chão. Gotas que caem viram decalque.',\n network: 'Cosmético. Vai no evento de impacto com 2 bits de idade e a semente.',\n },\n headshot: {\n name: 'Headshot',\n short: 'A cabeça estoura, os olhos apagam no mesmo quadro e o corpo cai.',\n cost: '≤ 70 part.',\n costNote: '8 detritos · 1 poça',\n when: 'Acerto na cabeça que mata (regra do bestiário: cabeça mata na hora).',\n look: 'Névoa vermelha em volta da cabeça, jato de gotas para trás, 6 a 8 pedaços de osso e pele, troca pela malha de pescoço estourado. O brilho dos olhos apaga no mesmo quadro, que é o sinal mais legível de longe.',\n budget: '20 de névoa + 35 gotas + 8 detritos + 1 poça. Na horda, só os 12 mais próximos ganham detritos.',\n network: 'Cosmético. O servidor já manda “morte por cabeça”.',\n },\n dismember: {\n name: 'Desmembramento',\n short: 'O braço sai inteiro com física e o ombro jorra por meio segundo.',\n cost: '≤ 60 part.',\n costNote: '1 membro · 4 respingos',\n when: 'Dano no membro acima do limite (multiplicadores por região do bestiário) ou golpe de machado.',\n look: 'O membro vira peça solta com física e quica. Jato curto do ombro em pulsos, gotas que formam um rastro até a poça. O zumbi cambaleia mas continua andando.',\n budget: '1 membro com física (conta no teto de 300), jato de até 60 gotas em 0,7 s.',\n network: 'O servidor manda qual membro saiu. A peça é local; cada cliente simula a sua.',\n },\n bloater_cloud: {\n name: 'Nuvem do Inchado',\n short: 'Barriga pulsa por 0,5 s, estoura e deixa 4 m de gás por 8 s.',\n cost: '≤ 180 part.',\n costNote: 'entidade no servidor',\n when: 'Morte do Inchado, por qualquer causa.',\n look: 'Aviso de 0,5 s com a barriga inchando, estouro com pedaços e jato esverdeado, e uma nuvem baixa verde-musgo que ondula no chão por 8 s. Brilhos verdes soltos dentro da nuvem marcam a área.',\n budget: '40 sprites grandes no estouro e até 60 vivos na nuvem, em meia resolução. Fade perto da câmera para não pintar a tela inteira.',\n network: 'Entidade no servidor (raio 4 m, 8 s): cega por 5 s, dobra o rastro de cheiro e faz tossir. O cliente só desenha.',\n },\n screamer_shriek: {\n name: 'Grito do Gritador',\n short: 'Preparo de 1,5 s com vapor, onda no chão e os olhos da horda acendem.',\n cost: '≤ 150 part.',\n costNote: '3 anéis · aberração',\n when: 'O Gritador avista o jogador e começa o grito (preparo de 1,5 s, chama todos num raio de 150 m).',\n look: 'Cabeça para trás e vapor saindo da boca durante o preparo. No grito, três anéis de pressão no chão, poeira levantada no raio, aberração cromática curta na tela de quem está perto e os olhos dos zumbis próximos piscam forte quando escutam.',\n budget: '3 anéis, até 150 partículas de poeira. A aberração na tela é um efeito de pós de 0,4 s.',\n network: 'O servidor manda “grito em X”. O efeito de tela depende da distância local.',\n },\n eyes_in_fog: {\n name: 'Olhos na neblina',\n short: 'Brilho dos olhos que fura a neblina; os corredores deixam rastro vermelho.',\n cost: '2 sprites/zumbi',\n costNote: '0 luzes',\n when: 'Sempre, em todo zumbi. É o sistema de leitura do bestiário: cor do olho diz a categoria.',\n look: 'Dois pontos emissivos com bloom que ignoram a neblina. O corpo some na névoa a 25 m, os olhos continuam legíveis a 60 m. Corredores deixam um rastro curto da cor do olho quando correm.',\n budget: '2 sprites por zumbi no mesmo lote instanciado do corpo. Rastro só para corredores a menos de 30 m. Nenhuma luz real.',\n network: 'Nada vai pela rede: sai da categoria do zumbi.',\n },\n corpse_decay: {\n name: 'Corpo: escurecer e dissolver',\n short: '15 minutos em 8 segundos: queda, escurece, moscas e vira pó.',\n cost: '≤ 40 part.',\n costNote: 'pool de 400 corpos',\n when: 'Morte do zumbi. Linha do tempo do bestiário: ragdoll 3 s, malha congelada, escurece, dissolve entre 14 e 15 min.',\n look: 'A pele perde a cor até ficar quase preta, a poça seca para marrom, aparecem moscas a partir do minuto 5 e no último minuto o corpo afunda e solta pó escuro.',\n budget: 'Moscas: 6 partículas por corpo, só a menos de 10 m. Pó: 40 partículas uma vez. O corpo usa o shader de dissolve, sem partícula extra.',\n network: 'O servidor manda a hora da morte; cada cliente calcula o estado.',\n },\n reanimation: {\n name: 'Reanimação',\n short: 'Veias acendem, espasmos, os olhos ligam e o corpo levanta.',\n cost: '≤ 30 part.',\n costNote: 'emissivo no shader',\n when: 'Fim da infecção de jogador ou NPC: reanima em 2 a 6 min como Fresco (70% Lento, 30% Corredor).',\n look: 'Pulsos verdes pela pele (emissivo no shader, não partícula), espasmos nos braços e na cabeça, os olhos acendem de dentro para fora e o corpo se levanta. Respiração em vapor no primeiro grito.',\n budget: 'Só emissivo animado e 30 partículas. É raro e importante, então pode ser caro.',\n network: 'O servidor manda “reanimou”. Para o amigo que vê o colega levantar, é o momento mais forte do coop.',\n },\n muzzle_flash: {\n name: 'Clarão de disparo',\n short: 'Núcleo, pétalas e fumaça com escala por arma. A Barrett levanta poeira.',\n cost: '≤ 60 part.',\n costNote: '1 luz · 50 ms',\n paramLabel: 'Arma',\n when: 'Cada disparo. Em primeira pessoa usa o modelo da arma; de longe vira só o sprite do núcleo.',\n look: 'Três camadas: núcleo branco-amarelo de 1 quadro, pétalas na direção do cano e fumaça cinza que fica 1 a 2 s. A 12 tem clarão largo e fumaça grossa. A Barrett solta dois jatos laterais pelo freio de boca e levanta um anel de poeira do chão. A luz ilumina o cômodo por 50 ms (pedido do brief do mapa).',\n budget: 'Até 60 partículas e 1 luz por tiro. Rajada de AK usa a mesma luz em loop. Pistola sem luz a mais de 15 m.',\n network: 'Cosmético. Sai do evento de tiro que o servidor já manda (arma, origem, direção).',\n },\n impact: {\n name: 'Impacto por material',\n short: 'Concreto, metal, madeira, vidro, carne e água: cada um com sua resposta.',\n cost: '≤ 40 part.',\n costNote: '1 furo',\n paramLabel: 'Material',\n when: 'Toda bala que acerta algo. O material vem da tag de superfície da malha.',\n look: 'Concreto: pó claro em cone e lascas cinza. Metal: chuva de faíscas que quicam, sem pó, com luz curta. Madeira: lascas finas e pó marrom. Vidro: brilho, cacos e rachadura. Carne: sangue pela idade. Água: coroa de gotas e dois anéis.',\n budget: '14 de pó + 6 lascas + até 18 faíscas + 1 furo. A 40 m sobra só o pó.',\n network: 'Evento de impacto de 12 bytes com material em 4 bits.',\n },\n penetration: {\n name: 'Bala que atravessa a horda',\n short: 'Traçante da Barrett passando por 5 zumbis, em câmera lenta 10×.',\n cost: '≤ 250 part.',\n costNote: 'evento agregado',\n when: 'Arma com penetração: AK atravessa 2, Remington 700 atravessa 3, Barrett atravessa 6 e carros.',\n look: 'Traçante com rastro de ar quente, saída de sangue em cada corpo na ordem em que a bala passa (cada um com a cor da sua idade), corpos caindo um atrás do outro e impacto final no concreto.',\n budget: 'Todos os acertos de um tiro saem num único evento agregado com a lista de pontos. 5 corpos = 5 saídas + 5 poças.',\n network: 'O servidor manda o tiro e a lista ordenada de acertos.',\n },\n casings_and_magazine: {\n name: 'Cápsulas e carregador',\n short: 'Latão que quica e fica no chão, carregador vazio caindo, fumaça do cano quente.',\n cost: '200/jogador',\n costNote: 'física 1,5 s',\n when: 'Cada disparo de arma semiautomática, recarga, e bombeada da 12 ou ferrolho do rifle.',\n look: 'Cápsula sai pela janela de ejeção girando, quica no asfalto duas ou três vezes e fica. O carregador vazio cai na recarga. Depois da rajada, fumaça fina sobe do cano quente por 2 s.',\n budget: 'Física só por 1,5 s, depois vira instância estática. Teto de 200 por jogador, o mais antigo some primeiro.',\n network: 'Cosmético, cada cliente gera as suas. Não sincroniza posição no chão.',\n },\n molotov: {\n name: 'Coquetel molotov',\n short: 'Pavio aceso no arco, vidro quebrando e poça de fogo que espalha.',\n cost: '≤ 300 part.',\n costNote: '2 luzes · entidade',\n when: 'Molotov atingindo o chão ou um zumbi.',\n look: 'Garrafa em arco com pavio soltando chama e fumaça. Ao bater, cacos verdes e um respingo de fogo que abre de 0,5 m para 2,2 m em 0,8 s. Chamas baixas, brasas, fumaça escura e luz tremendo por 6 s, depois brasa e mancha queimada.',\n budget: 'Fogo de área com até 300 partículas e 1 luz de fogo. Zumbis em chamas usam um emissor pequeno preso ao corpo (40 partículas) e só os 8 mais próximos.',\n network: 'Fogo é entidade no servidor: área, dano por segundo, tempo. O cliente desenha a partir dela.',\n },\n grenade: {\n name: 'Granada de fragmentação',\n short: 'Quique no asfalto, clarão, onda de choque, estilhaços e anel de poeira.',\n cost: '≤ 400 part.',\n costNote: 'evento agregado',\n when: 'Granada explodindo (pavio de 1,8 s após o arremesso).',\n look: 'Granada quica duas vezes. Explosão em camadas: clarão branco que pinta a tela, bola de fogo curta, anel de choque no chão e outro de frente para a câmera, estilhaços brilhantes que quicam, anel de poeira rasteira, fumaça cinza que sobe e marca queimada. Zumbis no raio caem para fora.',\n budget: 'Até 400 partículas, 1 luz forte de 0,5 s, 16 detritos, 1 marca. Os 40 zumbis atingidos entram num único evento agregado.',\n network: 'O servidor manda a explosão e a lista de atingidos. Visual local.',\n },\n explosive_barrel: {\n name: 'Barril explosivo',\n short: 'Cogumelo de fogo, tampa voando, gasolina queimando no chão e coluna de fumaça.',\n cost: '≤ 450 part.',\n costNote: '2 luzes · entidade de fogo',\n when: 'Barril vermelho com dano acima do limite, tiro ou fogo encostado.',\n look: 'Tudo da granada, mais: bola de fogo que sobe em cogumelo, tampa e pedaços vermelhos voando alto, três poças de gasolina queimando por 5 s e coluna de fumaça preta que serve de referência de longe.',\n budget: 'Pico de 450 partículas. A coluna de fumaça fica visível além de 80 m com 6 sprites grandes.',\n network: 'Explosão + entidade de fogo nas poças.',\n },\n shopfront_glass: {\n name: 'Vitrine estourando',\n short: 'Duas trincas, depois a placa inteira desce em cacos que ficam na calçada.',\n cost: '≤ 80 detritos',\n costNote: '1 rachadura',\n when: 'Vidro com dano acumulado (tiro, golpe, horda empurrando). Barulho de 20 m que atrai a horda.',\n look: 'Primeiros golpes abrem trincas em estrela na textura do vidro. No estouro a placa some e vira 60 a 80 cacos que caem girando, brilhando na luz, e ficam na calçada. Um véu de pó de vidro fino na frente.',\n budget: 'Cacos com física contam no teto de 300 e viram estáticos em 2 s. 3 estágios de rachadura numa máscara só.',\n network: 'O servidor manda o estado do vidro (0 a 3). Cacos são locais.',\n },\n door_stages: {\n name: 'Porta cedendo em 3 estágios',\n short: 'Pancadas soltam pó do batente, uma tábua racha e a porta cai.',\n cost: '≤ 120 part.',\n costNote: '3 estágios',\n when: 'Horda batendo em porta ou barricada (o Brutamonte faz 3× dano). Estágios: 66%, 33%, 0% de vida.',\n look: 'Cada pancada sacode a porta e solta pó do batente e lascas. No estágio 2 a tábua do meio racha e cai. No 3 a porta tomba e levanta uma nuvem de poeira rasteira, e o zumbi sai do escuro.',\n budget: 'Pó: 12 partículas por pancada. Queda: 40 de poeira e 6 detritos. Pancadas a mais de 15 m sem partícula, só som.',\n network: 'O servidor manda a vida da porta. Estágio visual sai do valor.',\n },\n building_collapse: {\n name: 'Desabamento',\n short: 'Pó escorrendo da marquise, blocos caindo e nuvem de poeira que rola pela rua.',\n cost: '≤ 350 part.',\n costNote: '26 detritos',\n when: 'Estrutura que perde a vida: explosão, veículo batendo, horda grande empurrando (destruição de prédios do mapa).',\n look: 'Antes: pó fino escorrendo e pedrinhas caindo por 1 s, como aviso. Depois: blocos grandes e pequenos caem, a câmera treme e uma nuvem de poeira cinza-bege rola pelo chão e fica no ar por 5 s, escondendo zumbis.',\n budget: '26 detritos (6 grandes), 70 sprites de poeira em meia resolução. É evento raro, cabe no orçamento.',\n network: 'O servidor manda o colapso e a área de poeira, que bloqueia visão de zumbi por 4 s.',\n },\n block_wall: {\n name: 'Parede derrubada com furos',\n short: 'Rajada deixa furos e lascas; a parede cai em blocos e os furos continuam neles.',\n cost: '≤ 300 part.',\n costNote: '30 blocos · furos presos',\n when: 'Parede, mureta ou barricada de concreto que perde a vida por tiros, explosão, Brutamonte ou carro (destruição do mapa e do arsenal).',\n look: 'Cada bala abre um furo escuro com borda lascada, solta pó e pedrinhas. Quando a parede cede, ela se parte em blocos que tombam com física e uma nuvem de poeira rola pelo chão. Os furos de bala estão presos aos blocos: ficam nos pedaços caídos, girados junto com eles, e não somem quando a parede quebra.',\n budget: 'Furos são decalques filhos do pedaço (não do mundo), até 12 por bloco e 128 no total. Blocos contam no teto de 300 detritos e viram estáticos em repouso. Poeira: 60 sprites em meia resolução.',\n network: 'O servidor manda vida da parede e a lista de furos (posição local de 2 bytes por furo). Quem entra depois vê os mesmos furos nos mesmos pedaços. Queda dos blocos usa semente comum.',\n },\n metal_door: {\n name: 'Porta de metal amassando',\n short: 'Cada pancada afunda a chapa, descasca a tinta e solta faísca; a dobradiça arrebenta.',\n cost: '≤ 160 part.',\n costNote: 'malha deformável',\n when: 'Porta, portão ou chapa de aço levando golpe de zumbi (Brutamonte 3×), tiro de 12 ou batida de carro.',\n look: 'A chapa afunda onde apanha (malha deformada, não troca de modelo), com 3 níveis visíveis. Cada pancada solta faísca, lasca de tinta verde e pó de ferrugem. Quando a vida acaba, a dobradiça de cima estoura em faíscas, a porta tomba e raspa no chão soltando mais faísca.',\n budget: 'Deformação por vértice só na hora do golpe (CPU, 400 vértices). Por golpe: 8 faíscas, 5 lascas, 6 de ferrugem. Queda: 30 faíscas e 40 de poeira.',\n network: 'O servidor guarda 3 bits de amassado por zona da porta (9 zonas). Quem entra depois vê a mesma porta amassada.',\n },\n iron_railing: {\n name: 'Grade de ferro entortando',\n short: 'A horda empurra, as barras envergam, a solda estoura em faísca e a grade tomba.',\n cost: '≤ 200 part.',\n costNote: '3 barras deformáveis',\n when: 'Grade, portão de ferro, corrimão ou alambrado com zumbis empurrando (pressão da horda) ou carro batendo.',\n look: 'As barras envergam aos poucos no ponto de pressão e as verticais inclinam junto. Ferrugem cai em fio durante o esforço. Em dois momentos a solda estoura com um estalo de faíscas. No fim o poste do meio cede e a grade tomba, deixando o ferro torto no chão.',\n budget: 'Curvatura calculada só quando a pressão muda, até 180 vértices. Ferrugem: 10 por segundo durante o esforço. Estouro de solda: 20 faíscas.',\n network: 'O servidor manda a pressão (0 a 1) e quando cede. A curva sai do valor, igual em todos os clientes.',\n },\n wooden_fence: {\n name: 'Cerca de madeira quebrando',\n short: 'Tábuas partem uma a uma, lascas e pó, e a seção tomba com a horda passando.',\n cost: '≤ 150 part.',\n costNote: '12 tábuas',\n when: 'Cerca, portão de quintal ou barricada de tábuas recebendo tiro, golpe ou pressão da horda.',\n look: 'Cada tábua parte num ponto aleatório: o toco fica preso no trilho e o pedaço de cima voa com lascas e pó de madeira. O zumbi aparece pelo vão. No fim o trilho racha e a seção inteira tomba para frente.',\n budget: 'Cada tábua é uma peça: quebra troca para toco + pedaço com física. Por quebra: 6 lascas, 8 de pó. Tombo: 35 de poeira.',\n network: 'O servidor manda quais tábuas quebraram (12 bits) e quando a seção cai.',\n },\n hit_point_breakage: {\n name: 'Quebra pelo ponto atingido',\n short: 'Tiro na perna, facão na quina, taco no tampo e 12 de perto: cada arma quebra a parte que pegou.',\n cost: '≤ 120 part.',\n costNote: 'peças com vida própria',\n when: 'Qualquer item destrutível. O dano vai para a peça que foi atingida, não para o item inteiro.',\n look: 'Tiro de 9 mm na perna da frente: a parte de baixo da perna parte e a mesa tomba para aquele canto. Facão na quina do tampo: a quina sai inteira num corte reto, com lascas em linha. Taco no outro canto: o tampo racha e se desfaz em pedaços com pó. 12 de perto no que sobrou: 9 furos agrupados que ficam na madeira.',\n budget: 'Cada peça tem vida e material. Item comum: 4 a 12 peças. Partículas só da peça atingida, pelo material dela.',\n network: 'O servidor manda qual peça perdeu vida e quanto. Peça solta simula no cliente; a posição final é mandada uma vez.',\n },\n pressure_cylinders: {\n name: 'Extintor e cilindro de oxigênio',\n short: 'Tiro no extintor faz nuvem branca e giro; no oxigênio a válvula quebra e o cilindro vira foguete.',\n cost: '≤ 250 part.',\n costNote: 'entidade de nuvem',\n paramLabel: 'Cilindro',\n when: 'Tiro, golpe forte ou fogo em extintor, cilindro de oxigênio ou de solda (chaves extintor e cilindro_oxigenio do catálogo do mapa).',\n look: 'Extintor: o furo solta um jato de pó branco, o extintor cai, gira no chão empurrado pelo jato e enche uma área de 5 m de nuvem que tapa a visão por 6 s. Oxigênio: a válvula quebra com estalo, o jato vira propulsão, o cilindro dispara rente ao chão raspando e soltando faísca, quica e para numa parede. Com fogo por perto, o oxigênio estoura como o barril.',\n budget: 'Jato: 60 partículas por segundo por 3 s. Nuvem do extintor: até 40 sprites grandes. Faísca de raspagem como a do carro.',\n network: 'Nuvem do extintor é entidade no servidor (bloqueia visão de zumbi e de jogador). A trajetória do cilindro usa semente comum e o servidor corrige a posição final.',\n },\n building_fire: {\n name: 'Prédio em chamas',\n short: 'Fogo saindo pelas janelas, lambendo a fachada, brasas e fumaça com vento.',\n cost: '≤ 600 part.',\n costNote: '2 luzes · referência',\n when: 'Incêndio de prédio (molotov, explosão, veículo). Dura minutos e espalha para o vizinho.',\n look: 'Janelas acesas em laranja que tremem, línguas de fogo saindo pela parte de cima de cada janela e subindo pela fachada, brasas levadas pelo vento e uma coluna de fumaça grossa inclinada. A luz pinta a rua.',\n budget: 'Até 600 partículas por prédio e 2 luzes de fogo. Além de 80 m fica só a coluna de fumaça e o brilho das janelas, que viram ponto de referência.',\n network: 'Entidade de fogo no servidor por prédio, com estado de espalhar.',\n },\n scraping_sparks: {\n name: 'Faíscas de raspagem',\n short: 'Para-choque arrastando no asfalto: rio de faíscas que quicam e marca no chão.',\n cost: '≤ 200 faíscas/s',\n costNote: '1 luz',\n when: 'Peça de carro em contato com o chão ou com parede (para-choque caído, roda sem pneu, carro capotado deslizando).',\n look: 'Jato de faíscas para trás do movimento, que quicam no asfalto e apagam do branco para o laranja. Brilho e luz curta no ponto de contato. Marca escura riscada no chão.',\n budget: 'Até 200 faíscas por segundo, 1 luz. A mais de 40 m vira só o brilho do ponto.',\n network: 'Cosmético, a partir do estado da peça e da velocidade que já são replicados.',\n },\n engine_smoke: {\n name: 'Fumaça do motor',\n short: 'Vapor branco, fumaça cinza, depois preta com fogo, conforme a condição.',\n cost: '≤ 120 part.',\n costNote: 'contínuo',\n paramLabel: 'Condição',\n when: 'Sempre que o motor está ligado ou quente, a partir da condição da peça (0 a 100) do brief da frota.',\n look: '100: só escapamento. 60: fio de vapor branco saindo pelo capô. 30: fumaça cinza constante e faísca de vez em quando. 10: fumaça preta grossa, chamas na grade e luz de fogo, aviso de que vai explodir.',\n budget: 'Contínuo, então barato: até 40 partículas por segundo e 1 luz só no estado crítico.',\n network: 'Sai da condição do motor, que já é replicada.',\n },\n fuel_leak: {\n name: 'Vazamento e incêndio do tanque',\n short: 'Gasolina pingando, poça, uma faísca e o fogo correndo até o tanque.',\n cost: '≤ 400 part.',\n costNote: 'entidade de fogo',\n when: 'Tanque furado por tiro ou batida. Faísca, tiro ou fogo próximo acende a poça.',\n look: 'Pingos de gasolina com brilho oleoso formando uma poça que cresce. Um tiro no metal solta faísca, o fogo acende na poça, corre até o carro e, se ninguém apagar, o carro explode.',\n budget: 'Pingos: 12 por segundo. Fogo de poça como o do molotov. A explosão final é o efeito seguinte.',\n network: 'Poça e fogo são entidades no servidor (podem queimar zumbis e jogadores).',\n },\n car_explosion: {\n name: 'Explosão do carro',\n short: 'Bola de fogo, carro pulando, capô e portas voando, carcaça queimando.',\n cost: '≤ 600 part.',\n costNote: 'evento raro',\n when: 'Tanque em chamas por mais de 4 s, ou dano acima do limite em carro com combustível.',\n look: 'Explosão grande (escala 1,8 da granada), o carro pula e cai torto, capô, portas e uma roda saem voando, vidro estoura em cacos. A lataria fica preta e a carcaça queima com fumaça preta por minutos.',\n budget: 'Pico de 600 partículas e 34 detritos. É evento raro e pode passar do teto por evento, nunca do teto global.',\n network: 'Explosão no servidor. O carro vira carcaça (outro prefab) para todos.',\n },\n windshield: {\n name: 'Para-brisa: sangue e rachadura',\n short: 'Visão de dentro do carro: pancadas, trincas em 4 estágios e mão de sangue.',\n cost: '0 partículas',\n costNote: 'máscara 4 estágios',\n when: 'Zumbi batendo no para-brisa ou no vidro lateral (brief da frota: vidro aguenta 8 golpes).',\n look: 'Cada golpe treme a câmera e soma uma trinca em estrela. Estágio 2 e 4 deixam mancha de sangue com marca de mão escorrendo. No estágio 4 o vidro está branco de rachadura e o próximo golpe quebra.',\n budget: 'Só textura: uma máscara de rachadura com 4 estágios e um decal de sangue no vidro. Pó de vidro: 6 partículas por golpe.',\n network: 'O servidor manda a vida do vidro. Sangue no vidro é local.',\n },\n damage_and_bite: {\n name: 'Dano e mordida',\n short: 'Borda vermelha do lado do golpe, sangue na lente e o pulso da mordida.',\n cost: 'pós-processo',\n costNote: 'só local',\n when: 'Jogador recebe golpe (com direção) ou mordida (infecção 100%, regra dos personagens).',\n look: 'Golpe: borda vermelha do lado de onde veio, tremor curto e respingos na lente. Mordida: clarão vermelho na tela inteira, a cor some por 2 s, batimento pulsando no túnel escuro e sangue grande na lente que escorre.',\n budget: 'Efeito de pós-processo e uma camada de lente. Nenhuma partícula no mundo além do sangue normal.',\n network: 'Só na tela de quem levou. Os outros veem o sangue no mundo.',\n },\n infection_phases: {\n name: 'Fases da infecção',\n short: 'Mal-estar, febre com borda fria, delírio com fantasmas e o fim.',\n cost: 'pós-processo',\n costNote: 'só local',\n paramLabel: 'Fase',\n when: 'Fases do brief de personagens: 2–12 h mal-estar, 12–30 h febre, 30–48 h delírio, 48 h morte.',\n look: 'Mal-estar: cor um pouco lavada e vinheta que respira devagar. Febre: borda fria azulada só para o infectado, desfoque em ondas, tosse que treme a câmera. Delírio: aberração de cor, matiz que oscila e zumbis falsos que piscam na neblina. 48 h: quase sem cor, tom verde, túnel escuro e batimento.',\n budget: 'Só pós-processo. Os fantasmas do delírio são 3 instâncias do zumbi sem colisão, só no cliente infectado.',\n network: 'Nunca vai pela rede. O outro jogador percebe só a tosse e a respiração (ver “Tosse e respiração”).',\n },\n panic: {\n name: 'Pânico',\n short: 'A mira treme, o túnel fecha conforme a horda chega e o coração acelera.',\n cost: 'pós-processo',\n costNote: 'só local',\n when: 'Medidor de pânico dos personagens (sobe com zumbis visíveis, corredores e companheiro morto). Efeitos acima de 60 e de 90.',\n look: 'Tremor fino e constante na câmera (a mira treme), vinheta escura que fecha conforme o zumbi mais próximo chega e pulsa com o batimento, que acelera de 70 para 150 bpm. A cor perde força.',\n budget: 'Só pós-processo e ruído de câmera.',\n network: 'Local. O pânico dos outros aparece na mira deles e no grito (som).',\n },\n bloater_blindness: {\n name: 'Cegueira do Inchado',\n short: 'Gás verde na cara: tela turva por 5 s e muco na lente.',\n cost: 'pós + 30 part.',\n costNote: 'entidade do servidor',\n when: 'Jogador dentro da nuvem do Inchado (raio 4 m) quando ela nasce.',\n look: 'Nuvem verde explode entre o zumbi e a câmera, a tela fica verde e desfocada e clareia em 5 s, com gotas de muco verde na lente que escorrem. Tosse treme a câmera duas vezes.',\n budget: 'Pós-processo + 30 sprites de gás perto da câmera, com fade para não cobrir a tela inteira de partícula.',\n network: 'A cegueira vem da entidade no servidor; o visual é local.',\n },\n campfire: {\n name: 'Fogueira do acampamento',\n short: 'Chama baixa, brasas subindo em espiral e luz quente tremendo.',\n cost: '≤ 120 part.',\n costNote: '1 luz de fogo',\n when: 'Fogueira acesa pelo grupo ou por NPCs. Esquenta, ilumina e atrai zumbis de longe (luz).',\n look: 'Chamas pequenas e baixas, brasas que sobem girando e somem, fumaça fina e uma luz quente que treme e pinta o sobrevivente.',\n budget: '120 partículas e 1 luz de fogo. Várias fogueiras do acampamento dividem a mesma luz se estiverem a menos de 6 m.',\n network: 'Objeto de cena replicado (aceso ou apagado). Visual local.',\n },\n cough_and_breath: {\n name: 'Tosse, respiração e lanterna',\n short: 'Vapor da respiração no frio, tosse da febre visível para os outros e feixe da lanterna.',\n cost: '≤ 40 part.',\n costNote: '1 spot com sombra',\n when: 'Noite fria: respiração sempre. Febre da infecção: tosse a cada poucos segundos, audível a 10 m.',\n look: 'Sopro curto de vapor a cada 2,4 s. Na tosse, três sopros rápidos e maiores e o corpo dá um tranco. A lanterna mostra o feixe na neblina e é a única luz com sombra.',\n budget: 'Vapor: 8 partículas por sopro, só a menos de 15 m. Lanterna: 1 spot com sombra por jogador, só a do jogador local projeta sombra.',\n network: 'A tosse é o sinal social da infecção: o servidor manda o evento de tosse (sem dizer que é infecção) e os outros veem o vapor e ouvem.',\n },\n}\n","import { en } from './locales/en'\nimport { ptBR } from './locales/pt-BR'\n\n/** Locales shipped with the package. Unknown locales fall back to `en`. */\nexport type VfxLocale = 'en' | 'pt-BR'\n\n/** The texts of one Workshop effect: its card in the list and its sheet (when, how it looks, what it costs, what goes over the network). */\nexport interface EffectTexts {\n readonly name: string\n readonly short: string\n /** The cost shown on the card, and what comes with it. */\n readonly cost: string\n readonly costNote: string\n /** The label of the effect's parameter, when it has one. */\n readonly paramLabel?: string\n readonly when: string\n readonly look: string\n readonly budget: string\n readonly network: string\n}\n\n/** The words around the sheet (`renderSheet`, W1266). */\nexport interface SheetLabels {\n readonly when: string\n readonly look: string\n readonly budget: string\n readonly network: string\n readonly quality: string\n readonly replay: string\n readonly auto: string\n}\n\nexport interface VfxStrings {\n readonly effects: Readonly<Record<string, EffectTexts>>\n readonly sheet: SheetLabels\n readonly groups: Readonly<Record<string, string>>\n}\n\nexport const locales: Record<VfxLocale, VfxStrings> = {\n en: {\n effects: en,\n sheet: { when: 'When', look: 'Look', budget: 'Budget', network: 'Network', quality: 'Quality', replay: 'Replay', auto: 'Auto replay' },\n groups: { explosions: 'Explosions', creatures: 'Zombies', weapons: 'Weapons and explosives', world: 'Map, weather and destruction', destruction: 'Scenery destruction', vehicles: 'Vehicles', screen: 'Characters and screen effects' },\n },\n 'pt-BR': {\n effects: ptBR,\n sheet: { when: 'Quando', look: 'Visual', budget: 'Orçamento', network: 'Rede', quality: 'Qualidade', replay: 'Repetir', auto: 'Repetição automática' },\n groups: { explosions: 'Explosões', creatures: 'Zumbis', weapons: 'Armas e explosivos', world: 'Mapa, clima e destruição', destruction: 'Destruição de cenário', vehicles: 'Veículos', screen: 'Personagens e efeitos de tela' },\n },\n}\n\nexport const resolveStrings = (locale: string | undefined): VfxStrings => locales[(locale as VfxLocale) ?? 'en'] ?? locales.en\n","import type { Vec3Tuple } from '../workshop/types'\n\nconst ANIMATION_SPEED = 1.4\nconst START_POSITION: Vec3Tuple = [4, 2.4, 7]\nconst START_TARGET: Vec3Tuple = [0, 1, 0]\n\ntype Vector = [number, number, number]\n\nfunction subtract(a: Vec3Tuple, b: Vec3Tuple): Vector {\n return [a[0] - b[0], a[1] - b[1], a[2] - b[2]]\n}\n\nfunction unit(vector: Vector): Vector {\n const length = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1] + vector[2] * vector[2]) || 1\n const inverse = 1 / length\n return [vector[0] * inverse, vector[1] * inverse, vector[2] * inverse]\n}\n\nfunction crossProduct(a: Vector, b: Vector): Vector {\n return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]]\n}\n\nfunction ease(value: number): number {\n return value < 0 ? 0 : value > 1 ? 1 : value * value * (3 - 2 * value)\n}\n\nfunction mix(from: Vec3Tuple, to: Vec3Tuple, amount: number): Vec3Tuple {\n return [from[0] + (to[0] - from[0]) * amount, from[1] + (to[1] - from[1]) * amount, from[2] + (to[2] - from[2]) * amount]\n}\n\n/** Rotates a vector by the quaternion `(x, y, z, w)` as three.js `applyQuaternion` does. */\nfunction rotate(local: readonly number[], x: number, y: number, z: number, w: number): Vec3Tuple {\n const [vx, vy, vz] = local as Vec3Tuple\n const ix = w * vx + y * vz - z * vy\n const iy = w * vy + z * vx - x * vz\n const iz = w * vz + x * vy - y * vx\n const iw = -x * vx - y * vy - z * vz\n return [ix * w + iw * -x + iy * -z - iz * -y, iy * w + iw * -y + iz * -x - ix * -z, iz * w + iw * -z + ix * -y - iy * -x]\n}\n\n/** The quaternion three.js builds from `lookAt(eye, target)` with the y axis up (`setFromRotationMatrix`). */\nfunction lookAtQuaternion(eye: Vec3Tuple, target: Vec3Tuple): readonly [number, number, number, number] {\n const z = unit(subtract(eye, target))\n const x = unit(crossProduct([0, 1, 0], z))\n const y = crossProduct(z, x)\n const [m11, m21, m31, m12, m22, m32, m13, m23, m33] = [x[0], x[1], x[2], y[0], y[1], y[2], z[0], z[1], z[2]]\n const trace = m11 + m22 + m33\n if (trace > 0) {\n const scale = 0.5 / Math.sqrt(trace + 1)\n return [(m32 - m23) * scale, (m13 - m31) * scale, (m21 - m12) * scale, 0.25 / scale]\n }\n if (m11 > m22 && m11 > m33) {\n const scale = 2 * Math.sqrt(1 + m11 - m22 - m33)\n return [0.25 * scale, (m12 + m21) / scale, (m13 + m31) / scale, (m32 - m23) / scale]\n }\n if (m22 > m33) {\n const scale = 2 * Math.sqrt(1 + m22 - m11 - m33)\n return [(m12 + m21) / scale, 0.25 * scale, (m23 + m32) / scale, (m13 - m31) / scale]\n }\n const scale = 2 * Math.sqrt(1 + m33 - m11 - m22)\n return [(m13 + m31) / scale, (m23 + m32) / scale, 0.25 * scale, (m21 - m12) / scale]\n}\n\n/**\n * The Workshop's view (W1259-1262): it glides to each effect's `view` (`camTo`) in about 0.7 s and persists between effects, so an effect that reads the\n * camera (a ring that faces the viewer, a mist at the lens) sees where the previous one left it.\n */\nexport class EffectCamera {\n position: Vec3Tuple = START_POSITION\n target: Vec3Tuple = START_TARGET\n private animation: { from: Vec3Tuple; fromTarget: Vec3Tuple; to: Vec3Tuple; toTarget: Vec3Tuple; progress: number } | undefined\n\n goTo(eye: Vec3Tuple, target: Vec3Tuple): void {\n this.animation = { from: this.position, fromTarget: this.target, to: eye, toTarget: target, progress: 0 }\n }\n\n place(eye: Vec3Tuple, target: Vec3Tuple): void {\n this.position = eye\n this.target = target\n this.animation = undefined\n }\n\n hold(): void {\n this.animation = undefined\n }\n\n update(dt: number): void {\n const animation = this.animation\n if (!animation) return\n animation.progress = Math.min(1, animation.progress + dt * ANIMATION_SPEED)\n const eased = ease(animation.progress)\n this.position = mix(animation.from, animation.to, eased)\n this.target = mix(animation.fromTarget, animation.toTarget, eased)\n if (animation.progress >= 1) this.animation = undefined\n }\n\n /** A direction given in the camera's own axes, in world axes. */\n forward(local: readonly number[]): Vec3Tuple {\n const [x, y, z, w] = lookAtQuaternion(this.position, this.target)\n return rotate(local, x, y, z, w)\n }\n}\n","import type { RandomStream } from '../random'\nimport type { WorkshopEngine } from '../workshop/engine'\nimport type { Vec3Tuple } from '../workshop/types'\nimport type { Behavior } from './behavior'\nimport { EffectCamera } from './camera'\nimport { EffectInstance } from './instance'\nimport type { CueRequest, EffectDef, EffectStep } from './types'\n\nconst SHAKE_SCALE = 0.06\nconst SHAKE_DEPTH = 0.5\n\nexport interface EffectRunnerOptions {\n readonly engine: WorkshopEngine\n readonly effects: Readonly<Record<string, EffectDef>>\n readonly behaviors?: Readonly<Record<string, Behavior>>\n readonly onCue?: (cue: CueRequest) => void\n}\n\n/**\n * The Workshop's one-effect-at-a-time view (W1253-1295): choosing an effect clears the set and runs it; each frame fires the timers due, runs the tick, ages\n * the pools, glides the camera and draws the camera shake. This is what the bench shows and what the golden replays; `createEffectPlayer` plays many at once.\n */\nexport class EffectRunner {\n readonly camera = new EffectCamera()\n readonly instance: EffectInstance\n /** The camera shake of the last frame, three draws per frame as in the page. */\n shake: Vec3Tuple = [0, 0, 0]\n\n constructor(private readonly options: EffectRunnerOptions) {\n const behaviors = options.behaviors ?? {}\n this.instance = new EffectInstance({\n engine: options.engine,\n view: this.camera,\n behavior: (id) => behaviors[id]!,\n behaviors: () => Object.values(behaviors),\n ...(options.onCue ? { onCue: options.onCue } : {}),\n })\n }\n\n get current(): EffectDef | undefined {\n return this.instance.def\n }\n\n get variables(): Map<string, unknown> {\n return this.instance.variables\n }\n\n get param(): number {\n return this.instance.param\n }\n\n get fresh(): boolean {\n return this.instance.fresh\n }\n\n get time(): number {\n return this.instance.time\n }\n\n /** Seconds since the effect was chosen: unlike `time`, a restart of the loop does not reset it (`el`). */\n get elapsed(): number {\n return this.instance.elapsed\n }\n\n get random(): RandomStream {\n return this.options.engine.random\n }\n\n /** `select` + `startLoop` (W1253-1257): clears everything, then runs the effect's setup and timeline. */\n start(id: string, param?: number): boolean {\n const def = this.options.effects[id]\n if (!def) return false\n if (def.view) this.camera.goTo(def.view.eye, def.view.target)\n else this.camera.hold()\n this.options.engine.clear()\n this.instance.begin(def, param)\n return true\n }\n\n /** `startLoop` again: the same effect from its start (the replay button and the end of the loop). */\n restart(): void {\n if (!this.instance.def) return\n this.options.engine.clear()\n this.instance.restart()\n }\n\n /** One frame of the page (W1280-1295). */\n update(dt: number): void {\n const engine = this.options.engine\n engine.post = {}\n this.instance.step(dt)\n engine.update(dt)\n this.camera.update(dt)\n const amplitude = engine.screen.shake * SHAKE_SCALE + this.instance.tremor\n const random = this.random\n this.shake = [random.between(-1, 1) * amplitude, random.between(-1, 1) * amplitude, random.between(-1, 1) * amplitude * SHAKE_DEPTH]\n }\n\n execute(steps: readonly EffectStep[]): void {\n this.instance.execute(steps)\n }\n}\n","import type { RandomStream } from './random'\n\nexport interface GroundFieldOptions {\n /** Side of the deformable patch in metres (the Lab's 60). */\n readonly size?: number\n /** Cells per side (the Lab's 180: 33 cm cells). */\n readonly segments?: number\n}\n\n/** The deformable ground the Lab's craters dig into (L60-83): a height field, in float32 like the page, centred on the origin. */\nexport interface GroundField {\n readonly size: number\n readonly segments: number\n /** Vertices per side. */\n readonly stride: number\n readonly cellSize: number\n /** World coordinate of the first row and column. */\n readonly origin: number\n readonly heights: Float32Array\n /** Goes up on every dent or reset: a renderer redraws its mesh when it changes. */\n readonly version: number\n heightAt(x: number, z: number): number\n /** A bowl with an uneven floor and a raised rim, drawing from `random` like the Lab's `dent`. */\n dent(random: RandomStream, x: number, z: number, radius: number, depth: number): void\n reset(): void\n}\n\nconst DEFAULT_SIZE = 60\nconst DEFAULT_SEGMENTS = 180\nconst MAX_DEPTH = 3\nconst RIM_REACH = 1.45\nconst REACH_FACTOR = 1.5\n\nexport function createGroundField(options: GroundFieldOptions = {}): GroundField {\n const size = options.size ?? DEFAULT_SIZE\n const segments = options.segments ?? DEFAULT_SEGMENTS\n const stride = segments + 1\n const cellSize = size / segments\n const origin = -size / 2\n const heights = new Float32Array(stride * stride)\n let version = 0\n\n function heightAt(x: number, z: number): number {\n const fx = (x - origin) / cellSize\n const fz = (z - origin) / cellSize\n if (fx < 0 || fz < 0 || fx >= segments || fz >= segments) return 0\n const column = fx | 0\n const row = fz | 0\n const tx = fx - column\n const tz = fz - row\n const index = row * stride + column\n return (heights[index]! * (1 - tx) + heights[index + 1]! * tx) * (1 - tz) + (heights[index + stride]! * (1 - tx) + heights[index + stride + 1]! * tx) * tz\n }\n\n function dent(random: RandomStream, x: number, z: number, radius: number, depth: number): void {\n const reach = radius * REACH_FACTOR\n const column0 = Math.max(0, Math.floor((x - reach - origin) / cellSize))\n const column1 = Math.min(segments, Math.ceil((x + reach - origin) / cellSize))\n const row0 = Math.max(0, Math.floor((z - reach - origin) / cellSize))\n const row1 = Math.min(segments, Math.ceil((z + reach - origin) / cellSize))\n const phaseA = random.next() * 6\n const phaseB = random.next() * 6\n for (let row = row0; row <= row1; row++) {\n for (let column = column0; column <= column1; column++) {\n const px = origin + column * cellSize\n const pz = origin + row * cellSize\n const distance = Math.hypot(px - x, pz - z)\n const angle = Math.atan2(pz - z, px - x)\n const q = distance / (radius * (1 + 0.13 * Math.sin(angle * 5 + phaseA) + 0.07 * Math.sin(angle * 11 + phaseB)))\n let change = 0\n if (q < 1) change = -depth * (1 - q * q) * (0.85 + random.next() * 0.3)\n else if (q < RIM_REACH) {\n const k = (q - 1) / 0.45\n change = depth * 0.3 * Math.sin(k * Math.PI) * (1 - k * 0.3) * (0.6 + random.next() * 0.8)\n }\n if (!change) continue\n const index = row * stride + column\n heights[index] = Math.max(-MAX_DEPTH, heights[index]! + change)\n }\n }\n version++\n }\n\n function reset(): void {\n heights.fill(0)\n version++\n }\n\n return {\n size,\n segments,\n stride,\n cellSize,\n origin,\n heights,\n get version() {\n return version\n },\n heightAt,\n dent,\n reset,\n }\n}\n","import type * as THREE_NS from 'three'\nimport type { ControlValue } from '@adatechnology/game-contracts'\nimport { colorOfChunk, createVfx, hexToLinearRgb } from '@adatechnology/game-vfx'\nimport type { ScreenState, VfxWorld, WorkshopEngine } from '@adatechnology/game-vfx'\nimport { createChunkLayer, createGlowLayer, createPuffLayer, createSparkLayer } from './instanced'\nimport type { PuffUniforms } from './instanced'\nimport { createLightLayer } from './lights'\nimport { createMarkLayer } from './marks'\nimport { createScreenOverlay } from './screen-overlay'\nimport { createShockLayer } from './shocks'\nimport { createSpriteLayer } from './sprites'\nimport type { ThreeLib, VfxRenderer, VfxRendererOptions } from './types'\nimport { createWorkshopLayers } from './workshop-layers'\nimport type { WorkshopLayers } from './workshop-layers'\n\nconst LAB_LIGHT_DIRECTION = [-14, 26, 12] as const\nconst SKY_COLOR = 0x6f7c92\nconst GROUND_COLOR = 0x2a241f\nconst LOD_BANDS: Readonly<Record<string, number | null>> = { auto: null, near: 1, middle: 0.5, far: 0.2 }\n/** The quality names the bench offers, as the Workshop's levels (0 keeps every particle). */\nconst QUALITY_LEVELS: Readonly<Record<string, number>> = { high: 0, medium: 1, low: 2 }\n/** `configure({ layer_<id>: false })` hides a layer of the renderer: `puffs`, `sparks`, `glows`, `debris`, `shock`, `sprites`. */\nexport const VFX_LAYERS = ['puffs', 'sparks', 'glows', 'debris', 'shock', 'sprites'] as const\nconst LAYER_PREFIX = 'layer_'\nconst SHAKE_REACH = 0.18\nconst SHAKE_FLOOR = 0.001\n\nfunction isWorld(value: unknown): value is VfxWorld {\n return typeof value === 'object' && value !== null && 'explosion' in value && typeof (value as VfxWorld).explosion === 'function'\n}\n\n/** The overlay shows the Lab's screen and the Workshop's together: the stronger of each field. */\nfunction mergeScreens(lab: ScreenState, workshop: ScreenState | undefined): ScreenState {\n if (!workshop) return lab\n const merged = { ...lab }\n for (const key of Object.keys(lab) as (keyof ScreenState)[]) merged[key] = Math.max(lab[key], workshop[key])\n return merged\n}\n\nfunction prefersReducedMotion(): boolean {\n return typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches\n}\n\n/** Draws a `VfxWorld`: the Lab's shaders, debris, shock, lights, marks and the screen overlay, and plays effects asked of it by id (`EffectSink`). */\nexport function createVfxRenderer(THREE: ThreeLib, options: VfxRendererOptions): VfxRenderer {\n const reducedMotion = options.reducedMotion ?? prefersReducedMotion()\n const requested = options.vfx\n const world = isWorld(requested) ? requested : createVfx({ profile: options.quality ?? 'medium', reducedMotion, ...requested })\n const pools = world.pools()\n const root = new THREE.Group()\n options.scene.add(root)\n\n const direction = new THREE.Vector3(...(options.lightDirection ?? LAB_LIGHT_DIRECTION)).normalize()\n const sky = hexToLinearRgb(SKY_COLOR)\n const ground = hexToLinearRgb(GROUND_COLOR)\n const uniforms: PuffUniforms = {\n uLight: { value: direction },\n uSky: { value: new THREE.Vector3(sky[0] * 0.55, sky[1] * 0.55, sky[2] * 0.55) },\n uGround: { value: new THREE.Vector3(ground[0] * 0.6, ground[1] * 0.6, ground[2] * 0.6) },\n uGlowPos: { value: new THREE.Vector3() },\n uGlowCol: { value: new THREE.Vector3(1, 0.5, 0.2) },\n uGlowI: { value: 0 },\n uTime: { value: 0 },\n }\n const puffs = createPuffLayer(THREE, pools.puffs, uniforms)\n const sparks = createSparkLayer(THREE, pools.sparks)\n const glows = createGlowLayer(THREE, pools.glows)\n const rockGeometry = new THREE.DodecahedronGeometry(0.5, 0)\n const plateGeometry = new THREE.BoxGeometry(1, 1, 1)\n const chunks = createChunkLayer(THREE, pools.chunks, { rock: rockGeometry, plate: plateGeometry }, colorOfChunk)\n const shocks = createShockLayer(THREE, pools.shocks)\n const sprites = createSpriteLayer(THREE, world.sprites(), options.pointScale ?? 700)\n const lights = createLightLayer(THREE, pools.lights, options.lightScale ?? Math.PI)\n const marks = createMarkLayer(THREE, { ground: pools.ground, createCanvas: options.createCanvas, rockGeometry, plateGeometry })\n root.add(puffs.mesh, sparks.mesh, glows.mesh, chunks.rocks, chunks.plates, shocks.group, lights.group, sprites.additive, sprites.normal, marks.group)\n const stopMarks = world.onMarks((event) => marks.handle(event))\n const overlay = options.overlay ? createScreenOverlay(options.overlay) : undefined\n let drawCalls = 0\n let workshopLayers: WorkshopLayers | undefined\n let workshopEngine: WorkshopEngine | undefined\n const layerOn = new Map<string, boolean>()\n const layerObjects = (): Record<string, THREE_NS.Object3D[]> => ({\n puffs: [puffs.mesh],\n sparks: [sparks.mesh],\n glows: [glows.mesh],\n debris: [chunks.rocks, chunks.plates],\n shock: [shocks.group],\n sprites: [sprites.additive, sprites.normal, ...(workshopLayers ? [workshopLayers.group] : [])],\n })\n const applyLayers = (): void => {\n for (const [id, objects] of Object.entries(layerObjects())) for (const object of objects) object.visible = layerOn.get(id) !== false\n }\n const screenNow = (): ScreenState => mergeScreens(world.screen(), world.peekWorkshop()?.screen)\n let clock = 0\n const lastDrawn = { puffs: 0, sparks: 0, glows: 0, chunks: 0, shocks: 0, sprites: 0 }\n\n function followCamera(): void {\n const position = options.camera.position\n world.setCamera([position.x, position.y, position.z])\n }\n\n function syncAll(): void {\n lastDrawn.puffs = puffs.sync()\n lastDrawn.sparks = sparks.sync()\n lastDrawn.glows = glows.sync()\n lastDrawn.chunks = chunks.sync()\n lastDrawn.shocks = shocks.sync()\n lastDrawn.sprites = sprites.sync()\n lights.sync()\n const engine = world.peekWorkshop()\n if (engine && engine !== workshopEngine) {\n workshopLayers?.dispose()\n if (workshopLayers) root.remove(workshopLayers.group)\n workshopEngine = engine\n workshopLayers = createWorkshopLayers(THREE, engine, options.pointScale ?? 700, 1)\n root.add(workshopLayers.group)\n applyLayers()\n }\n const workshopCalls = workshopLayers?.sync() ?? 0\n const glow = pools.lights.glow\n uniforms.uGlowPos.value.set(glow.x, glow.y, glow.z)\n uniforms.uGlowCol.value.set(glow.color[0], glow.color[1], glow.color[2])\n uniforms.uGlowI.value = glow.intensity\n drawCalls = Number(lastDrawn.puffs > 0) + Number(lastDrawn.sparks > 0) + Number(lastDrawn.glows > 0) + Number(chunks.rocks.count > 0) + Number(chunks.plates.count > 0) + Number(sprites.additive.geometry.drawRange.count > 0) + Number(sprites.normal.geometry.drawRange.count > 0) + lastDrawn.shocks * 2 + marks.count() + workshopCalls\n }\n\n return {\n world,\n play(request) {\n followCamera()\n const explosion = world.explosion(request.effect, request.point, {\n ...(request.direction ? { direction: request.direction } : {}),\n ...(request.seed !== undefined ? { seed: request.seed } : {}),\n ...(request.scale !== undefined ? { scale: request.scale } : {}),\n })\n if (explosion !== null) return explosion\n return world.play(request.effect, request.point, {\n ...(request.direction ? { direction: request.direction } : {}),\n ...(request.normal ? { normal: request.normal } : {}),\n ...(request.seed !== undefined ? { seed: request.seed } : {}),\n ...(request.param !== undefined ? { param: request.param } : {}),\n })\n },\n stop: (handle) => world.stop(handle),\n configure(settings: Readonly<Record<string, ControlValue>>) {\n const band = settings.lod_band\n if (typeof band === 'string' && band in LOD_BANDS) world.forceLod(LOD_BANDS[band] ?? null)\n const quality = settings.quality\n if (typeof quality === 'string' && quality in QUALITY_LEVELS) world.workshop().setQuality(QUALITY_LEVELS[quality] ?? 0)\n let layersChanged = false\n for (const [key, value] of Object.entries(settings)) {\n if (!key.startsWith(LAYER_PREFIX) || typeof value !== 'boolean') continue\n layerOn.set(key.slice(LAYER_PREFIX.length), value)\n layersChanged = true\n }\n if (layersChanged) applyLayers()\n },\n catalog: () => [...world.catalog(), ...world.effectIds()],\n update(dt) {\n clock += dt\n uniforms.uTime.value = clock\n followCamera()\n world.update(dt)\n marks.update(dt)\n syncAll()\n overlay?.update(screenNow())\n },\n applyCamera() {\n const shake = screenNow().shake\n if (reducedMotion || !(shake > SHAKE_FLOOR)) return () => {}\n const camera = options.camera as THREE_NS.Camera\n const base = camera.position.clone()\n const reach = shake * SHAKE_REACH\n camera.position.x += Math.sin(clock * 71) * reach\n camera.position.y += Math.sin(clock * 53 + 1) * reach * 0.7\n camera.position.z += Math.sin(clock * 67 + 2) * reach\n return () => camera.position.copy(base)\n },\n stats() {\n const { lastBlast, ...counters } = world.stats()\n return { ...counters, last_puffs: lastBlast.puffs, last_sparks: lastBlast.sparks, last_chunks: lastBlast.chunks, last_lights: lastBlast.lights, drawCalls }\n },\n screen: screenNow,\n dispose() {\n stopMarks()\n overlay?.dispose()\n workshopLayers?.dispose()\n for (const layer of [puffs, sparks, glows, chunks, shocks, lights, marks, sprites]) layer.dispose()\n rockGeometry.dispose()\n plateGeometry.dispose()\n options.scene.remove(root)\n },\n }\n}\n","/** The additive halo (Lab L297-304), verbatim except for the r160 colour-space chunk. */\nexport const GLOW_VERTEX = `attribute vec4 aP;attribute vec4 aC;varying vec2 vU;varying vec4 vC;\nvoid main(){vec3 R=vec3(viewMatrix[0][0],viewMatrix[1][0],viewMatrix[2][0]);vec3 Up=vec3(viewMatrix[0][1],viewMatrix[1][1],viewMatrix[2][1]);\nvec3 wp=aP.xyz+(R*position.x+Up*position.y)*aP.w;vU=position.xy;vC=aC;gl_Position=projectionMatrix*viewMatrix*vec4(wp,1.);}`\n\nexport const GLOW_FRAGMENT = `varying vec2 vU;varying vec4 vC;void main(){float r=dot(vU,vU);if(r>1.)discard;float f=exp(-r*4.2)-.015;gl_FragColor=vec4(vC.rgb*vC.a*max(f,0.),1.);\n #include <tonemapping_fragment>\n #include <colorspace_fragment>\n}`\n","/** The value noise the puff shader builds its dissolve from (Lab L116-122), verbatim. */\nexport const GL_NOISE = `\nfloat h3(vec3 p){p=fract(p*.3183099+.1);p*=17.0;return fract(p.x*p.y*p.z*(p.x+p.y+p.z));}\nfloat vn(vec3 x){vec3 i=floor(x),f=fract(x);f=f*f*(3.-2.*f);\n return mix(mix(mix(h3(i),h3(i+vec3(1,0,0)),f.x),mix(h3(i+vec3(0,1,0)),h3(i+vec3(1,1,0)),f.x),f.y),\n mix(mix(h3(i+vec3(0,0,1)),h3(i+vec3(1,0,1)),f.x),mix(h3(i+vec3(0,1,1)),h3(i+vec3(1,1,1)),f.x),f.y),f.z);}\n`\n","import { GL_NOISE } from './noise'\n\n/**\n * The puff shader of the Lab (L130-192), verbatim: a faceted icosahedron per puff, a five-band posterised heat ramp, a noise dissolve and banded light.\n * The only change is the r160 colour-space chunk: `encodings_fragment` (r128) is `colorspace_fragment` now.\n */\nexport const PUFF_VERTEX = `${GL_NOISE}\nattribute vec4 aP;attribute vec4 aD;attribute vec3 aC;\nvarying vec3 vWP;varying vec3 vL;varying vec4 vD;varying vec3 vC;\nvoid main(){\n vec3 p=position;\n float s=aD.z;\n float n=vn(p*1.6+vec3(s*11.0,s*7.0,s*3.0));\n p*=0.78+0.5*n;\n float a=s*6.2831;float c=cos(a),si=sin(a);p.xz=mat2(c,-si,si,c)*p.xz;\n vL=p;\n vec3 wp=aP.xyz+p*aP.w;\n vWP=wp;vD=aD;vC=aC;\n gl_Position=projectionMatrix*viewMatrix*vec4(wp,1.0);\n}`\n\nexport const PUFF_FRAGMENT = `${GL_NOISE}\nuniform vec3 uLight;uniform vec3 uSky;uniform vec3 uGround;uniform vec3 uGlowPos;uniform vec3 uGlowCol;uniform float uGlowI;\nvarying vec3 vWP;varying vec3 vL;varying vec4 vD;varying vec3 vC;\nvec3 L(vec3 c){return pow(c,vec3(2.2));}\nvec3 ramp(float h){\n vec3 c0=L(vec3(.36,.07,.03)),c1=L(vec3(.76,.21,.1)),c2=L(vec3(1.,.54,.16)),c3=L(vec3(1.,.83,.42)),c4=L(vec3(1.,.96,.84));\n if(h<.25)return mix(c0,c1,h/.25);\n if(h<.5)return mix(c1,c2,(h-.25)/.25);\n if(h<.8)return mix(c2,c3,(h-.5)/.3);\n return mix(c3,c4,clamp((h-.8)/.4,0.,1.));\n}\nvoid main(){\n vec3 N=normalize(cross(dFdx(vWP),dFdy(vWP)));\n float age=vD.x,heat=vD.y,kind=vD.w;\n float n=vn(vL*2.4+vD.z*13.0);\n float diss=smoothstep(.5,1.,age);\n if(n<diss*1.08-.04)discard;\n float lam=max(dot(N,uLight),0.);\n float band=lam>.55?1.:lam>.15?.68:.42;\n vec3 col=vC*(vec3(.5,.56,.7)*band*1.05+vec3(.2,.19,.18)+uGround*(.4-.4*N.y));\n vec3 tg=uGlowPos-vWP;float dg=length(tg)+.001;\n float gi=uGlowI/(1.+dg*dg*.05)*(.3+.7*max(dot(N,tg/dg),0.));\n col+=uGlowCol*gi*vC*1.6;\n float h=heat*(.72+.56*n)+heat*(-N.y)*.18;\n if(h>.05){\n float hq=floor(h*5.+.5)/5.;\n vec3 fc=ramp(clamp(hq,0.,1.2))*(1.1+3.1*hq);\n col=mix(col,fc,smoothstep(.04,.2,h));\n }\n if(kind>2.5&&kind<3.5){col+=vec3(.02,.025,.03);}\n gl_FragColor=vec4(col,1.);\n #include <tonemapping_fragment>\n #include <colorspace_fragment>\n}`\n","/** The streak shader of shrapnel, sparks and embers (Lab L255-275), verbatim except for the r160 colour-space chunk. */\nexport const SPARK_VERTEX = `\nattribute vec3 aPos;attribute vec3 aVel;attribute vec4 aE;varying vec2 vU;varying vec4 vE;\nvoid main(){\n float sp=length(aVel)+1e-4;vec3 d=aVel/sp;\n vec3 tc=normalize(cameraPosition-aPos);\n vec3 sd=normalize(cross(d,tc)+vec3(1e-5));\n float len=max(aE.y*2.5,min(sp*aE.z,2.2));\n vec3 wp=aPos+sd*position.x*aE.y-d*position.y*len;\n vU=position.xy;vE=aE;\n gl_Position=projectionMatrix*viewMatrix*vec4(wp,1.);\n}`\n\nexport const SPARK_FRAGMENT = `\nvarying vec2 vU;varying vec4 vE;\nvoid main(){\n float age=vE.x,k=vE.w;\n float w=1.-abs(vU.x)*2.;w=w*w;\n float tail=1.-vU.y;\n vec3 c;\n if(k<.5){c=mix(vec3(4.,3.3,2.2),vec3(2.4,.7,.12),smoothstep(0.,.55,age));c=mix(c,vec3(.5,.06,.02),smoothstep(.55,1.,age));}\n else if(k<1.5){c=mix(vec3(1.6,1.6,1.6),vec3(.35,.35,.36),smoothstep(0.,.4,age));}\n else if(k<2.5){c=mix(vec3(5.,5.,6.),vec3(1.,1.3,2.),smoothstep(0.,.5,age));}\n else if(k<3.5){c=vec3(.9,1.2,1.4)*(1.-age);}\n else {c=mix(vec3(3.2,1.4,.3),vec3(1.2,.25,.05),age);}\n float a=w*(.35+.65*tail)*(1.-smoothstep(.75,1.,age));\n gl_FragColor=vec4(c*a,1.);\n #include <tonemapping_fragment>\n #include <colorspace_fragment>\n}`\n","import type * as THREE_NS from 'three'\nimport type { Chunk, Glow, OrderedPool, Puff, Spark } from '@adatechnology/game-vfx'\nimport { GLOW_FRAGMENT, GLOW_VERTEX } from './shaders/glow'\nimport { PUFF_FRAGMENT, PUFF_VERTEX } from './shaders/puff'\nimport { SPARK_FRAGMENT, SPARK_VERTEX } from './shaders/spark'\nimport type { ThreeLib } from './types'\n\nexport interface DrawnLayer {\n readonly mesh: THREE_NS.Mesh\n /** Copies the pool into the instance buffers and answers how many are drawn. */\n sync(): number\n dispose(): void\n}\n\ntype Attribute = THREE_NS.InstancedBufferAttribute\n\nfunction dynamic(THREE: ThreeLib, array: Float32Array, size: number): Attribute {\n return new THREE.InstancedBufferAttribute(array, size).setUsage(THREE.DynamicDrawUsage)\n}\n\nexport interface PuffUniforms {\n uLight: { value: THREE_NS.Vector3 }\n uSky: { value: THREE_NS.Vector3 }\n uGround: { value: THREE_NS.Vector3 }\n uGlowPos: { value: THREE_NS.Vector3 }\n uGlowCol: { value: THREE_NS.Vector3 }\n uGlowI: { value: number }\n uTime: { value: number }\n}\n\nexport function createPuffLayer(THREE: ThreeLib, pool: OrderedPool<Puff>, uniforms: PuffUniforms): DrawnLayer {\n const capacity = pool.capacity\n const geometry = new THREE.InstancedBufferGeometry()\n geometry.setAttribute('position', new THREE.IcosahedronGeometry(1, 1).getAttribute('position'))\n const a = new Float32Array(capacity * 4)\n const d = new Float32Array(capacity * 4)\n const c = new Float32Array(capacity * 3)\n const attributes = [dynamic(THREE, a, 4), dynamic(THREE, d, 4), dynamic(THREE, c, 3)] as const\n geometry.setAttribute('aP', attributes[0])\n geometry.setAttribute('aD', attributes[1])\n geometry.setAttribute('aC', attributes[2])\n geometry.instanceCount = 0\n const material = new THREE.ShaderMaterial({ uniforms: uniforms as never, vertexShader: PUFF_VERTEX, fragmentShader: PUFF_FRAGMENT })\n const mesh = new THREE.Mesh(geometry, material)\n mesh.name = 'vfx-puffs'\n mesh.frustumCulled = false\n return {\n mesh,\n sync() {\n let n = 0\n for (const p of pool.items) {\n a[n * 4] = p.x\n a[n * 4 + 1] = p.y\n a[n * 4 + 2] = p.z\n a[n * 4 + 3] = p.size\n d[n * 4] = p.age\n d[n * 4 + 1] = p.heat\n d[n * 4 + 2] = p.seed\n d[n * 4 + 3] = p.kind\n c[n * 3] = p.color[0]\n c[n * 3 + 1] = p.color[1]\n c[n * 3 + 2] = p.color[2]\n n++\n }\n geometry.instanceCount = n\n for (const attribute of attributes) attribute.needsUpdate = true\n return n\n },\n dispose() {\n geometry.dispose()\n material.dispose()\n },\n }\n}\n\nexport function createSparkLayer(THREE: ThreeLib, pool: OrderedPool<Spark>): DrawnLayer {\n const capacity = pool.capacity\n const geometry = new THREE.InstancedBufferGeometry()\n geometry.setAttribute('position', new THREE.Float32BufferAttribute([-0.5, 0, 0, 0.5, 0, 0, 0.5, 1, 0, -0.5, 1, 0], 3))\n geometry.setIndex([0, 1, 2, 0, 2, 3])\n const position = new Float32Array(capacity * 3)\n const velocity = new Float32Array(capacity * 3)\n const extra = new Float32Array(capacity * 4)\n const attributes = [dynamic(THREE, position, 3), dynamic(THREE, velocity, 3), dynamic(THREE, extra, 4)] as const\n geometry.setAttribute('aPos', attributes[0])\n geometry.setAttribute('aVel', attributes[1])\n geometry.setAttribute('aE', attributes[2])\n geometry.instanceCount = 0\n const material = new THREE.ShaderMaterial({ transparent: true, depthWrite: false, blending: THREE.AdditiveBlending, vertexShader: SPARK_VERTEX, fragmentShader: SPARK_FRAGMENT })\n const mesh = new THREE.Mesh(geometry, material)\n mesh.name = 'vfx-sparks'\n mesh.frustumCulled = false\n mesh.renderOrder = 5\n return {\n mesh,\n sync() {\n let n = 0\n for (const s of pool.items) {\n position[n * 3] = s.x\n position[n * 3 + 1] = s.y\n position[n * 3 + 2] = s.z\n velocity[n * 3] = s.vx\n velocity[n * 3 + 1] = s.vy\n velocity[n * 3 + 2] = s.vz\n extra[n * 4] = s.age\n extra[n * 4 + 1] = s.w\n extra[n * 4 + 2] = s.st\n extra[n * 4 + 3] = s.kind\n n++\n }\n geometry.instanceCount = n\n for (const attribute of attributes) attribute.needsUpdate = true\n return n\n },\n dispose() {\n geometry.dispose()\n material.dispose()\n },\n }\n}\n\nexport function createGlowLayer(THREE: ThreeLib, pool: OrderedPool<Glow>): DrawnLayer {\n const capacity = pool.capacity\n const geometry = new THREE.InstancedBufferGeometry()\n geometry.setAttribute('position', new THREE.Float32BufferAttribute([-1, -1, 0, 1, -1, 0, 1, 1, 0, -1, 1, 0], 3))\n geometry.setIndex([0, 1, 2, 0, 2, 3])\n const where = new Float32Array(capacity * 4)\n const color = new Float32Array(capacity * 4)\n const attributes = [dynamic(THREE, where, 4), dynamic(THREE, color, 4)] as const\n geometry.setAttribute('aP', attributes[0])\n geometry.setAttribute('aC', attributes[1])\n geometry.instanceCount = 0\n const material = new THREE.ShaderMaterial({ transparent: true, depthWrite: false, blending: THREE.AdditiveBlending, vertexShader: GLOW_VERTEX, fragmentShader: GLOW_FRAGMENT })\n const mesh = new THREE.Mesh(geometry, material)\n mesh.name = 'vfx-glows'\n mesh.frustumCulled = false\n mesh.renderOrder = 6\n return {\n mesh,\n sync() {\n let n = 0\n for (const g of pool.items) {\n where[n * 4] = g.outX\n where[n * 4 + 1] = g.outY\n where[n * 4 + 2] = g.outZ\n where[n * 4 + 3] = g.outSize\n color[n * 4] = g.color[0]\n color[n * 4 + 1] = g.color[1]\n color[n * 4 + 2] = g.color[2]\n color[n * 4 + 3] = g.outIntensity\n n++\n }\n geometry.instanceCount = n\n for (const attribute of attributes) attribute.needsUpdate = true\n return n\n },\n dispose() {\n geometry.dispose()\n material.dispose()\n },\n }\n}\n\nexport interface ChunkLayer {\n readonly rocks: THREE_NS.InstancedMesh\n readonly plates: THREE_NS.InstancedMesh\n sync(): number\n dispose(): void\n}\n\nexport function createChunkLayer(THREE: ThreeLib, pool: OrderedPool<Chunk>, shapes: { rock: THREE_NS.BufferGeometry; plate: THREE_NS.BufferGeometry }, colorOf: (chunk: Chunk) => readonly [number, number, number]): ChunkLayer {\n const capacity = pool.capacity\n const rockMaterial = new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 0.85, flatShading: true })\n const plateMaterial = rockMaterial.clone()\n const make = (geometry: THREE_NS.BufferGeometry, material: THREE_NS.Material): THREE_NS.InstancedMesh => {\n const mesh = new THREE.InstancedMesh(geometry, material, capacity)\n mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage)\n mesh.castShadow = true\n mesh.receiveShadow = true\n mesh.frustumCulled = false\n mesh.count = 0\n mesh.setColorAt(0, new THREE.Color(1, 1, 1))\n return mesh\n }\n const rocks = make(shapes.rock, rockMaterial)\n const plates = make(shapes.plate, plateMaterial)\n const matrix = new THREE.Matrix4()\n const quaternion = new THREE.Quaternion()\n const euler = new THREE.Euler()\n const position = new THREE.Vector3()\n const scale = new THREE.Vector3()\n return {\n rocks,\n plates,\n sync() {\n let rockCount = 0\n let plateCount = 0\n for (const chunk of pool.items) {\n euler.set(chunk.rx, chunk.ry, chunk.rz)\n quaternion.setFromEuler(euler)\n matrix.compose(position.set(chunk.x, chunk.y, chunk.z), quaternion, scale.set(chunk.sx, chunk.sy, chunk.sz))\n const mesh = chunk.plate ? plates : rocks\n const index = chunk.plate ? plateCount++ : rockCount++\n mesh.setMatrixAt(index, matrix)\n const color = colorOf(chunk)\n const colors = mesh.instanceColor!.array as Float32Array\n colors[index * 3] = color[0]\n colors[index * 3 + 1] = color[1]\n colors[index * 3 + 2] = color[2]\n }\n rocks.count = rockCount\n plates.count = plateCount\n for (const mesh of [rocks, plates]) {\n mesh.instanceMatrix.needsUpdate = true\n mesh.instanceColor!.needsUpdate = true\n }\n return rockCount + plateCount\n },\n dispose() {\n rockMaterial.dispose()\n plateMaterial.dispose()\n rocks.dispose()\n plates.dispose()\n },\n }\n}\n","import type * as THREE_NS from 'three'\nimport type { LightPool } from '@adatechnology/game-vfx'\nimport type { ThreeLib } from './types'\n\nexport interface LightLayer {\n readonly group: THREE_NS.Group\n /** Moves and lights the 4 blast lights and 2 fire lights from the pool; answers how many are lit. */\n sync(): number\n dispose(): void\n}\n\n/** The Lab's light pool as real point lights (L349-365). Colours are the raw numbers the Lab used, not converted from sRGB. */\nexport function createLightLayer(THREE: ThreeLib, pool: LightPool, scale: number): LightLayer {\n const group = new THREE.Group()\n const blast = pool.blast.map(() => {\n const light = new THREE.PointLight(0xffa050, 0, 30, 2)\n light.position.set(0, -50, 0)\n group.add(light)\n return light\n })\n const fire = pool.fire.map(() => {\n const light = new THREE.PointLight(0xff7a2a, 0, 16, 2)\n light.position.set(0, -50, 0)\n group.add(light)\n return light\n })\n return {\n group,\n sync() {\n let lit = 0\n pool.blast.forEach((entry, index) => {\n const light = blast[index]!\n light.position.set(entry.x, entry.y, entry.z)\n light.distance = entry.distance\n light.color.setRGB(entry.color[0], entry.color[1], entry.color[2], THREE.LinearSRGBColorSpace)\n light.intensity = entry.intensity * scale\n if (entry.intensity > 0.05) lit++\n })\n pool.fire.forEach((entry, index) => {\n const light = fire[index]!\n light.position.set(entry.x, entry.y, entry.z)\n light.distance = entry.distance\n light.intensity = entry.intensity * scale\n if (entry.intensity > 0.05) lit++\n })\n return lit\n },\n dispose() {\n for (const light of [...blast, ...fire]) light.dispose()\n },\n }\n}\n","import { lehmer } from '@adatechnology/game-vfx'\nimport type { RandomStream } from '@adatechnology/game-vfx'\nimport type { CanvasContextLike, CanvasLike, ThreeLib } from './types'\nimport type * as THREE_NS from 'three'\n\nconst SCORCH_SEED = 5\nconst TEXTURE_SIZE = 256\nconst FULL_TURN = Math.PI * 2\n\n/** Where a texture draws its randomness: the Lab seeded the page with 5 before it made the marks, so the textures do not touch the effect's own stream. */\nexport function createTextureStream(): RandomStream {\n return lehmer(SCORCH_SEED)\n}\n\n/** Without a DOM (a server, a test in Node) there is nothing to draw on: the textures stay blank and the marks still lay down their geometry. */\nfunction blankCanvas(): CanvasLike {\n return { width: 0, height: 0, getContext: () => null }\n}\n\nfunction defaultCanvas(): CanvasLike {\n if (typeof document === 'undefined') return blankCanvas()\n return document.createElement('canvas') as unknown as CanvasLike\n}\n\nfunction paint(createCanvas: () => CanvasLike, draw: (context: CanvasContextLike, size: number) => void): CanvasLike {\n const canvas = createCanvas()\n canvas.width = TEXTURE_SIZE\n canvas.height = TEXTURE_SIZE\n const context = canvas.getContext('2d')\n if (context) draw(context, TEXTURE_SIZE)\n return canvas\n}\n\nfunction drawScorch(context: CanvasContextLike, size: number, random: RandomStream): void {\n context.clearRect(0, 0, size, size)\n const center = size / 2\n for (let ray = 0; ray < 60; ray++) {\n const angle = random.next() * FULL_TURN\n const length = center * (0.55 + random.next() * 0.42)\n context.strokeStyle = `rgba(8,6,5,${0.25 + random.next() * 0.35})`\n context.lineWidth = 2 + random.next() * 6\n context.beginPath()\n context.moveTo(center, center)\n context.lineTo(center + Math.cos(angle) * length, center + Math.sin(angle) * length)\n context.stroke()\n }\n const gradient = context.createRadialGradient(center, center, 0, center, center, center * 0.72)\n gradient.addColorStop(0, 'rgba(4,3,3,.96)')\n gradient.addColorStop(0.55, 'rgba(10,8,7,.85)')\n gradient.addColorStop(1, 'rgba(14,11,9,0)')\n context.fillStyle = gradient\n context.fillRect(0, 0, size, size)\n for (let spot = 0; spot < 26; spot++) {\n context.fillStyle = `rgba(60,40,28,${0.15 + random.next() * 0.2})`\n context.beginPath()\n context.arc(center + random.between(-0.4, 0.4) * center, center + random.between(-0.4, 0.4) * center, 2 + random.next() * 7, 0, 7)\n context.fill()\n }\n}\n\nfunction drawDirt(context: CanvasContextLike, size: number, random: RandomStream): void {\n const center = size / 2\n context.clearRect(0, 0, size, size)\n const gradient = context.createRadialGradient(center, center, 0, center, center, center)\n gradient.addColorStop(0, 'rgba(92,72,52,1)')\n gradient.addColorStop(0.6, 'rgba(118,92,64,1)')\n gradient.addColorStop(0.82, 'rgba(80,64,50,.8)')\n gradient.addColorStop(1, 'rgba(40,36,34,0)')\n context.fillStyle = gradient\n context.fillRect(0, 0, size, size)\n for (let speck = 0; speck < 420; speck++) {\n const angle = random.next() * FULL_TURN\n const distance = Math.sqrt(random.next()) * center * 0.8\n context.fillStyle = random.next() < 0.5 ? 'rgba(40,30,22,.5)' : 'rgba(160,132,96,.5)'\n context.beginPath()\n context.arc(center + Math.cos(angle) * distance, center + Math.sin(angle) * distance, 1 + random.next() * 3.5, 0, 7)\n context.fill()\n }\n}\n\nexport interface MarkTextures {\n readonly scorch: readonly THREE_NS.CanvasTexture[]\n readonly dirt: THREE_NS.CanvasTexture\n}\n\n/** The three scorch textures and the dirt one (L443-457), drawn once from a stream seeded like the page's. */\nexport function createMarkTextures(THREE: ThreeLib, createCanvas: (() => CanvasLike) | undefined): MarkTextures {\n const make = createCanvas ?? defaultCanvas\n const random = createTextureStream()\n const texture = (canvas: CanvasLike): THREE_NS.CanvasTexture => {\n const result = new THREE.CanvasTexture(canvas as unknown as HTMLCanvasElement)\n result.colorSpace = THREE.SRGBColorSpace\n result.anisotropy = 4\n return result\n }\n const scorch = [0, 1, 2].map(() => texture(paint(make, (context, size) => drawScorch(context, size, random))))\n const dirt = texture(paint(make, (context, size) => drawDirt(context, size, random)))\n return { scorch, dirt }\n}\n","import type * as THREE_NS from 'three'\nimport type { BloodMark, DirtMark, GroundField, Mark, MarkEvent, RockMark, ScorchMark } from '@adatechnology/game-vfx'\nimport { createMarkTextures } from './canvas'\nimport type { MarkTextures } from './canvas'\nimport type { CanvasLike, ThreeLib } from './types'\n\nconst MAX_CONFORM_STEPS = 48\nconst MIN_CONFORM_STEPS = 16\nconst BLOOD_GROW_RATE = 1.2\n\ninterface BloodDrawn {\n readonly mesh: THREE_NS.Mesh\n readonly scaleX: number\n readonly scaleZ: number\n age: number\n}\n\nexport interface MarkLayer {\n readonly group: THREE_NS.Group\n /** The shared rock and plate shapes, so the debris and the rim rocks use the same geometry. */\n handle(event: MarkEvent): void\n update(dt: number): void\n /** The meshes in the group right now. */\n count(): number\n dispose(): void\n}\n\nexport interface MarkLayerOptions {\n readonly ground: GroundField | undefined\n readonly createCanvas: (() => CanvasLike) | undefined\n readonly rockGeometry: THREE_NS.BufferGeometry\n readonly plateGeometry: THREE_NS.BufferGeometry\n}\n\n/**\n * The scorch, dirt, rim rocks and blood of the Lab as meshes (L443-476). A scorch or dirt mesh follows the ground as it was the moment the mark was\n * made (`conform`), so the renderer builds it from the mark event, not later.\n */\nexport function createMarkLayer(THREE: ThreeLib, options: MarkLayerOptions): MarkLayer {\n const group = new THREE.Group()\n const textures: MarkTextures = createMarkTextures(THREE, options.createCanvas)\n const owned: { dispose(): void }[] = [...textures.scorch, textures.dirt]\n const scorchMaterials = textures.scorch.map((map) => {\n const material = new THREE.MeshStandardMaterial({ map, transparent: true, depthWrite: false, roughness: 1, polygonOffset: true, polygonOffsetFactor: -2, polygonOffsetUnits: -2 })\n owned.push(material)\n return material\n })\n const dirtMaterial = new THREE.MeshStandardMaterial({\n map: textures.dirt, transparent: true, depthWrite: false, roughness: 1, flatShading: true, polygonOffset: true, polygonOffsetFactor: -1, polygonOffsetUnits: -1,\n })\n owned.push(dirtMaterial)\n const bloodMaterial = new THREE.MeshStandardMaterial({ color: 0x5c0a0a, roughness: 0.15, metalness: 0.1, polygonOffset: true, polygonOffsetFactor: -3, polygonOffsetUnits: -3 })\n const bloodGeometry = new THREE.CircleGeometry(0.5, 9).rotateX(-Math.PI / 2)\n owned.push(bloodMaterial, bloodGeometry)\n const rockMaterials = new Map<number, THREE_NS.MeshStandardMaterial>()\n const blood: BloodDrawn[] = []\n let meshCount = 0\n\n function rockMaterial(color: number): THREE_NS.MeshStandardMaterial {\n let material = rockMaterials.get(color)\n if (!material) {\n material = new THREE.MeshStandardMaterial({ color, roughness: 0.9, metalness: 0, flatShading: true })\n rockMaterials.set(color, material)\n owned.push(material)\n }\n return material\n }\n\n function conform(mark: ScorchMark | DirtMark, material: THREE_NS.Material): THREE_NS.Mesh {\n const radius = mark.radius\n const steps = Math.max(MIN_CONFORM_STEPS, Math.min(MAX_CONFORM_STEPS, Math.round(radius * 8)))\n const geometry = new THREE.PlaneGeometry(2, 2, steps, steps).rotateX(-Math.PI / 2)\n const position = geometry.attributes.position!\n const cos = Math.cos(mark.rotation)\n const sin = Math.sin(mark.rotation)\n for (let index = 0; index < position.count; index++) {\n const localX = position.getX(index) * radius\n const localZ = position.getZ(index) * radius\n const worldX = mark.x + localX * cos - localZ * sin\n const worldZ = mark.z + localX * sin + localZ * cos\n position.setXYZ(index, worldX, (options.ground?.heightAt(worldX, worldZ) ?? 0) + mark.lift, worldZ)\n }\n geometry.computeVertexNormals()\n const mesh = new THREE.Mesh(geometry, material as THREE_NS.MeshStandardMaterial)\n mesh.receiveShadow = true\n return mesh\n }\n\n function addRock(mark: RockMark): THREE_NS.Mesh {\n const mesh = new THREE.Mesh(mark.plate ? options.plateGeometry : options.rockGeometry, rockMaterial(mark.color))\n mesh.position.set(mark.x, mark.y, mark.z)\n mesh.scale.set(mark.scale[0], mark.scale[1], mark.scale[2])\n mesh.rotation.set(mark.rotation[0], mark.rotation[1], mark.rotation[2])\n mesh.castShadow = true\n mesh.receiveShadow = true\n return mesh\n }\n\n function addBlood(mark: BloodMark): THREE_NS.Mesh {\n const mesh = new THREE.Mesh(bloodGeometry, bloodMaterial)\n mesh.position.set(mark.x, mark.y, mark.z)\n mesh.scale.set(mark.scaleX, 1, mark.scaleZ)\n mesh.rotation.y = mark.rotation\n mesh.receiveShadow = true\n mesh.scale.multiplyScalar(0.05)\n blood.push({ mesh, scaleX: mark.scaleX, scaleZ: mark.scaleZ, age: 0 })\n return mesh\n }\n\n function build(mark: Mark): THREE_NS.Mesh {\n if (mark.kind === 'scorch') return conform(mark, scorchMaterials[mark.variant]!)\n if (mark.kind === 'dirt') return conform(mark, dirtMaterial)\n if (mark.kind === 'rock') return addRock(mark)\n return addBlood(mark)\n }\n\n function clearMeshes(): void {\n for (const child of [...group.children]) {\n group.remove(child)\n const geometry = (child as THREE_NS.Mesh).geometry\n if (geometry !== options.rockGeometry && geometry !== options.plateGeometry && geometry !== bloodGeometry) geometry.dispose()\n }\n blood.length = 0\n meshCount = 0\n }\n\n return {\n group,\n handle(event) {\n if (event.type === 'clear') {\n clearMeshes()\n return\n }\n group.add(build(event.mark))\n meshCount++\n },\n update(dt) {\n for (const drawn of blood) {\n if (drawn.age > 1) continue\n drawn.age += dt * BLOOD_GROW_RATE\n const ease = 1 - Math.pow(1 - Math.min(1, drawn.age), 2.5)\n drawn.mesh.scale.set(drawn.scaleX * Math.max(0.05, ease), 1, drawn.scaleZ * Math.max(0.05, ease))\n }\n },\n count: () => meshCount,\n dispose() {\n clearMeshes()\n for (const item of owned) item.dispose()\n },\n }\n}\n","import type { ScreenState } from '@adatechnology/game-vfx'\n\nexport interface ScreenOverlay {\n update(state: Readonly<ScreenState>): void\n dispose(): void\n}\n\nconst clamp = (value: number, min: number, max: number): number => (value < min ? min : value > max ? max : value)\n\nfunction layer(container: HTMLElement, name: string, blend?: string): HTMLDivElement {\n const element = container.ownerDocument.createElement('div')\n element.dataset.vfxOverlay = name\n Object.assign(element.style, { position: 'absolute', inset: '0', pointerEvents: 'none', opacity: '0' })\n if (blend) element.style.mixBlendMode = blend\n container.appendChild(element)\n return element\n}\n\n/**\n * The screen effects as overlays inside the host's container: the Lab's white flash (L1087, `mix-blend-mode: screen`) and the Workshop's white,\n * red edge, the two sides and the gas tint (W356-366), with the same colours. Nothing is drawn while the state is at rest.\n */\nexport function createScreenOverlay(container: HTMLElement): ScreenOverlay {\n const flash = layer(container, 'flash', 'screen')\n flash.style.background = '#fff'\n const white = layer(container, 'white')\n const red = layer(container, 'red')\n const sides = layer(container, 'sides')\n const gas = layer(container, 'gas')\n const elements = [flash, white, red, sides, gas]\n return {\n update(state) {\n flash.style.opacity = clamp(state.flash, 0, 1).toFixed(3)\n white.style.opacity = '1'\n white.style.background = `rgba(235,240,255,${clamp(state.white, 0, 1).toFixed(2)})`\n red.style.opacity = '1'\n red.style.background = `radial-gradient(ellipse at center, transparent 40%, rgba(170,16,12,${(clamp(state.red, 0, 1) * 0.85).toFixed(2)}) 100%)`\n sides.style.opacity = '1'\n sides.style.background = `linear-gradient(90deg, rgba(190,20,14,${(state.directionLeft * 0.8).toFixed(2)}) 0%, transparent 35%, transparent 65%, rgba(190,20,14,${(state.directionRight * 0.8).toFixed(2)}) 100%)`\n gas.style.opacity = '1'\n gas.style.background = state.gas > 0.01 ? `rgba(90,130,40,${(state.gas * 0.55).toFixed(2)})` : 'transparent'\n },\n dispose() {\n for (const element of elements) element.remove()\n },\n }\n}\n","/** The shock shell (fresnel edge, Lab L322-325) and the dust ring on the ground (L326-333), verbatim except for the r160 colour-space chunk. */\nexport const SHELL_VERTEX = `varying vec3 vN;varying vec3 vV;void main(){vec4 wp=modelMatrix*vec4(position,1.);vN=normalize(mat3(modelMatrix)*normal);vV=normalize(cameraPosition-wp.xyz);gl_Position=projectionMatrix*viewMatrix*wp;}`\n\nexport const SHELL_FRAGMENT = `uniform float uA;varying vec3 vN;varying vec3 vV;void main(){float f=1.-abs(dot(vN,vV));f=pow(f,5.);gl_FragColor=vec4(vec3(1.,.93,.82)*f*uA*.45,1.);\n #include <tonemapping_fragment>\n #include <colorspace_fragment>\n}`\n\nexport const RING_VERTEX = `varying vec2 vU;void main(){vU=position.xz;gl_Position=projectionMatrix*viewMatrix*modelMatrix*vec4(position,1.);}`\n\nexport const RING_FRAGMENT = `uniform float uA;uniform float uW;uniform vec3 uC;varying vec2 vU;\n float h(vec2 p){return fract(sin(dot(p,vec2(12.9898,78.233)))*43758.5453);}\n void main(){float r=length(vU);float b=smoothstep(1.,1.-uW,r)*smoothstep(1.-uW*3.5,1.-uW,r);float a=atan(vU.y,vU.x);float n=.7+.3*h(vec2(floor(a*14.),1.));gl_FragColor=vec4(uC,b*uA*n);\n #include <tonemapping_fragment>\n #include <colorspace_fragment>\n}`\n","import type * as THREE_NS from 'three'\nimport type { OrderedPool, Shock } from '@adatechnology/game-vfx'\nimport { RING_FRAGMENT, RING_VERTEX, SHELL_FRAGMENT, SHELL_VERTEX } from './shaders/shock'\nimport type { ThreeLib } from './types'\n\ninterface Pair {\n readonly shell: THREE_NS.Mesh\n readonly ring: THREE_NS.Mesh\n readonly shellAlpha: { value: number }\n readonly ringAlpha: { value: number }\n}\n\nconst RING_HEIGHT = 0.05\n\nexport interface ShockLayer {\n readonly group: THREE_NS.Group\n sync(): number\n dispose(): void\n}\n\n/** The shock shell and ground ring of each live shock (L319-348): one pair of meshes per slot, shown while the slot has a shock. */\nexport function createShockLayer(THREE: ThreeLib, pool: OrderedPool<Shock>): ShockLayer {\n const group = new THREE.Group()\n const shellGeometry = new THREE.SphereGeometry(1, 28, 18)\n const ringGeometry = new THREE.PlaneGeometry(2, 2).rotateX(-Math.PI / 2)\n const pairs: Pair[] = []\n\n function makePair(): Pair {\n const shellAlpha = { value: 0 }\n const ringAlpha = { value: 0 }\n const shellMaterial = new THREE.ShaderMaterial({ transparent: true, depthWrite: false, blending: THREE.AdditiveBlending, uniforms: { uA: shellAlpha }, vertexShader: SHELL_VERTEX, fragmentShader: SHELL_FRAGMENT })\n const ringMaterial = new THREE.ShaderMaterial({\n transparent: true, depthWrite: false, uniforms: { uA: ringAlpha, uW: { value: 0.12 }, uC: { value: new THREE.Color(0.55, 0.5, 0.44) } }, vertexShader: RING_VERTEX, fragmentShader: RING_FRAGMENT,\n })\n const shell = new THREE.Mesh(shellGeometry, shellMaterial)\n shell.renderOrder = 4\n const ring = new THREE.Mesh(ringGeometry, ringMaterial)\n ring.renderOrder = 3\n group.add(shell, ring)\n return { shell, ring, shellAlpha, ringAlpha }\n }\n\n return {\n group,\n sync() {\n const shocks = pool.items\n for (let index = 0; index < shocks.length; index++) {\n const pair = (pairs[index] ??= makePair())\n const shock = shocks[index]!\n pair.shell.visible = pair.ring.visible = true\n pair.shell.position.set(shock.x, shock.y, shock.z)\n pair.shell.scale.setScalar(shock.shellScale)\n pair.shellAlpha.value = shock.shellAlpha\n pair.ring.position.set(shock.x, RING_HEIGHT, shock.z)\n pair.ring.scale.setScalar(shock.ringScale)\n pair.ringAlpha.value = shock.ringAlpha\n }\n for (let index = shocks.length; index < pairs.length; index++) pairs[index]!.shell.visible = pairs[index]!.ring.visible = false\n return shocks.length\n },\n dispose() {\n for (const pair of pairs) {\n ;(pair.shell.material as THREE_NS.Material).dispose()\n ;(pair.ring.material as THREE_NS.Material).dispose()\n }\n shellGeometry.dispose()\n ringGeometry.dispose()\n },\n }\n}\n","/** The soft round point sprite of the game's sprite kinds (moved verbatim from the destruction renderer's particles). */\nexport const SPRITE_VERTEX = `\nuniform float uScale;\nattribute float size;\nattribute float alpha;\nattribute vec3 pcolor;\nvarying float vAlpha;\nvarying vec3 vColor;\nvoid main() {\n vAlpha = alpha;\n vColor = pcolor;\n vec4 mv = modelViewMatrix * vec4(position, 1.0);\n gl_PointSize = min(size * uScale / max(0.1, -mv.z), 256.0);\n gl_Position = projectionMatrix * mv;\n}`\n\nexport const SPRITE_FRAGMENT = `\nvarying float vAlpha;\nvarying vec3 vColor;\nvoid main() {\n float d = length(gl_PointCoord - vec2(0.5)) * 2.0;\n float a = (1.0 - smoothstep(0.35, 1.0, d)) * vAlpha;\n if (a < 0.004) discard;\n gl_FragColor = vec4(vColor, a);\n}`\n","import type * as THREE_NS from 'three'\nimport type { SpriteKindPool, SpriteKindSystem } from '@adatechnology/game-vfx'\nimport { SPRITE_FRAGMENT, SPRITE_VERTEX } from './shaders/sprite'\nimport type { ThreeLib } from './types'\n\nexport interface SpriteLayer {\n readonly additive: THREE_NS.Points\n readonly normal: THREE_NS.Points\n /** Tells the GPU what changed and answers how many sprites are drawn. */\n sync(): number\n dispose(): void\n}\n\ninterface Drawn {\n readonly points: THREE_NS.Points\n sync(): number\n dispose(): void\n}\n\nfunction drawPool(THREE: ThreeLib, pool: SpriteKindPool, additive: boolean, pointScale: number): Drawn {\n const geometry = new THREE.BufferGeometry()\n const attributes: [string, Float32Array, number][] = [['position', pool.position, 3], ['pcolor', pool.color, 3], ['alpha', pool.alpha, 1], ['size', pool.size, 1]]\n const buffers = attributes.map(([name, array, size]) => {\n const attribute = new THREE.BufferAttribute(array, size).setUsage(THREE.DynamicDrawUsage)\n geometry.setAttribute(name, attribute)\n return attribute\n })\n geometry.setDrawRange(0, 0)\n const material = new THREE.ShaderMaterial({\n uniforms: { uScale: { value: pointScale } },\n vertexShader: SPRITE_VERTEX,\n fragmentShader: SPRITE_FRAGMENT,\n transparent: true,\n depthWrite: false,\n blending: additive ? THREE.AdditiveBlending : THREE.NormalBlending,\n })\n const points = new THREE.Points(geometry, material)\n points.frustumCulled = false\n points.name = additive ? 'vfx-sprites-additive' : 'vfx-sprites'\n points.renderOrder = additive ? 11 : 10\n return {\n points,\n sync() {\n geometry.setDrawRange(0, pool.drawCount)\n for (const buffer of buffers) buffer.needsUpdate = true\n return pool.drawCount\n },\n dispose() {\n geometry.dispose()\n material.dispose()\n },\n }\n}\n\n/** Draws the game's sprite kinds (debris, sparks, smoke, steam, drips...) as two soft point layers, additive and normal. */\nexport function createSpriteLayer(THREE: ThreeLib, system: SpriteKindSystem, pointScale: number): SpriteLayer {\n const additive = drawPool(THREE, system.additive, true, pointScale)\n const normal = drawPool(THREE, system.normal, false, pointScale)\n return {\n additive: additive.points,\n normal: normal.points,\n sync: () => additive.sync() + normal.sync(),\n dispose() {\n additive.dispose()\n normal.dispose()\n },\n }\n}\n","/** The Workshop's soft point sprite (W168-171): the size is in metres, clamped to 1..512 px; `uSoft` is 1 for the soft (normal blend) pool. */\nexport const WORKSHOP_SPRITE_VERTEX = `attribute float size;attribute float alpha;attribute vec3 pcol;varying vec3 vC;varying float vA;uniform float uScale;\nvoid main(){vC=pcol;vA=alpha;vec4 mv=modelViewMatrix*vec4(position,1.);gl_PointSize=clamp(size*uScale/-mv.z,1.,512.);gl_Position=projectionMatrix*mv;}`\n\nexport const WORKSHOP_SPRITE_FRAGMENT = `uniform float uSoft;varying vec3 vC;varying float vA;\nvoid main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a=mix(smoothstep(1.,.78,r),pow(1.-r,1.5),uSoft);gl_FragColor=vec4(vC,vA*a);}`\n","import type * as THREE_NS from 'three'\nimport type { Decal, WorkshopEngine } from '@adatechnology/game-vfx'\nimport { WORKSHOP_SPRITE_FRAGMENT, WORKSHOP_SPRITE_VERTEX } from './shaders/workshop-sprite'\nimport type { ThreeLib } from './types'\n\nconst DEBRIS_CAPACITY = 500\nconst DECAL_CAPACITY = 400\nconst RING_CAPACITY = 120\nconst RIPPLE_SEGMENTS = 12\nconst FADE_SECONDS = 0.5\nconst FIRE_LIGHT_COLOR = 0xff7a2a\nconst FIRE_LIGHT_DISTANCE = 14\nconst GLOSS_DRIED_AT = 0.85\nconst BROWN = 0x2a1610\nconst GROUND_COLOR = 0x888888\nconst FUEL_DRIED = 0x888888\n\nexport interface WorkshopLayers {\n readonly group: THREE_NS.Group\n /** Copies the pools into the draw buffers; answers how many draw calls have something to draw. */\n sync(): number\n dispose(): void\n}\n\ninterface Drawn {\n readonly object: THREE_NS.Object3D\n sync(): boolean\n dispose(): void\n}\n\nconst clamp = (v: number, lo: number, hi: number): number => (v < lo ? lo : v > hi ? hi : v)\n\nfunction spriteLayer(THREE: ThreeLib, pool: WorkshopEngine['add'], additive: boolean, soft: number, scale: number): Drawn {\n const geometry = new THREE.BufferGeometry()\n const arrays: [string, Float32Array, number][] = [['position', pool.position, 3], ['pcol', pool.color, 3], ['size', pool.size, 1], ['alpha', pool.alpha, 1]]\n const buffers = arrays.map(([name, array, size]) => {\n const attribute = new THREE.BufferAttribute(array, size).setUsage(THREE.DynamicDrawUsage)\n geometry.setAttribute(name, attribute)\n return attribute\n })\n geometry.setDrawRange(0, 0)\n const material = new THREE.ShaderMaterial({\n uniforms: { uScale: { value: scale }, uSoft: { value: soft } },\n vertexShader: WORKSHOP_SPRITE_VERTEX,\n fragmentShader: WORKSHOP_SPRITE_FRAGMENT,\n transparent: true,\n depthWrite: false,\n blending: additive ? THREE.AdditiveBlending : THREE.NormalBlending,\n })\n const points = new THREE.Points(geometry, material)\n points.frustumCulled = false\n points.renderOrder = additive ? 3 : 2\n points.name = additive ? 'workshop-add' : 'workshop-normal'\n return {\n object: points,\n sync() {\n geometry.setDrawRange(0, pool.count)\n for (const buffer of buffers) buffer.needsUpdate = true\n return pool.count > 0\n },\n dispose() {\n geometry.dispose()\n material.dispose()\n },\n }\n}\n\nfunction streakLayer(THREE: ThreeLib, pool: WorkshopEngine['streaks']): Drawn {\n const geometry = new THREE.BufferGeometry()\n const position = new THREE.BufferAttribute(pool.segment, 3).setUsage(THREE.DynamicDrawUsage)\n const color = new THREE.BufferAttribute(pool.segmentColor, 3).setUsage(THREE.DynamicDrawUsage)\n geometry.setAttribute('position', position)\n geometry.setAttribute('color', color)\n geometry.setDrawRange(0, 0)\n const material = new THREE.LineBasicMaterial({ vertexColors: true, transparent: true, blending: THREE.AdditiveBlending, depthWrite: false })\n const lines = new THREE.LineSegments(geometry, material)\n lines.frustumCulled = false\n lines.renderOrder = 4\n lines.name = 'workshop-streaks'\n return {\n object: lines,\n sync() {\n geometry.setDrawRange(0, pool.count * 2)\n position.needsUpdate = color.needsUpdate = true\n return pool.count > 0\n },\n dispose() {\n geometry.dispose()\n material.dispose()\n },\n }\n}\n\nfunction debrisLayer(THREE: ThreeLib, pool: WorkshopEngine['debris']): Drawn {\n const geometry = new THREE.BoxGeometry(1, 1, 1)\n const material = new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 0.45, metalness: 0.25, flatShading: true })\n const mesh = new THREE.InstancedMesh(geometry, material, DEBRIS_CAPACITY)\n mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage)\n mesh.frustumCulled = false\n mesh.name = 'workshop-debris'\n const euler = new THREE.Euler()\n const quaternion = new THREE.Quaternion()\n const position = new THREE.Vector3()\n const scale = new THREE.Vector3()\n const matrix = new THREE.Matrix4()\n const colour = new THREE.Color()\n const hidden = new THREE.Matrix4().makeScale(0, 0, 0)\n for (let index = 0; index < DEBRIS_CAPACITY; index++) mesh.setMatrixAt(index, hidden)\n mesh.setColorAt(0, colour.setHex(0xffffff))\n return {\n object: mesh,\n sync() {\n let drawn = false\n for (let index = 0; index < DEBRIS_CAPACITY; index++) {\n const piece = pool.pieces[index]!\n if (!piece.on) {\n mesh.setMatrixAt(index, hidden)\n continue\n }\n drawn = true\n const shrink = Math.min(1, piece.life / FADE_SECONDS)\n euler.set(piece.rx, piece.ry, piece.rz)\n quaternion.setFromEuler(euler)\n matrix.compose(position.set(piece.x, piece.y, piece.z), quaternion, scale.set(piece.sx * shrink, piece.sy * shrink, piece.sz * shrink))\n mesh.setMatrixAt(index, matrix)\n mesh.setColorAt(index, colour.setHex(piece.color))\n }\n mesh.instanceMatrix.needsUpdate = true\n if (mesh.instanceColor) mesh.instanceColor.needsUpdate = true\n return drawn\n },\n dispose() {\n geometry.dispose()\n material.dispose()\n },\n }\n}\n\n/** A decal's current look from its fields (W276-306): it grows, a wet one creeps, dries towards its dry colour or evaporates. */\nfunction decalLook(decal: Decal, colour: THREE_NS.Color, dryColour: THREE_NS.Color, brown: THREE_NS.Color, ground: THREE_NS.Color, index: number): { sx: number; sy: number; dried: boolean } | null {\n const grown = decal.grow > 0 ? 1 - Math.pow(1 - Math.min(1, decal.t / decal.grow), 2.4) : 1\n let sx = decal.sx * grown\n let sy = decal.sy * grown\n colour.setHex(decal.color)\n let dried = false\n const fluid = decal.fluid\n if (fluid) {\n const creep = fluid === 'oil' ? 0.5 : fluid === 'fuel' ? 0.7 : fluid === 'bile' ? 0.25 : fluid === 'water' ? 0.2 : 0.3\n const age = decal.t\n if (decal.dryTime > 0) {\n const k = clamp((age - decal.dryAt) / decal.dryTime, 0, 1)\n if (decal.dryColor >= 0) dryColour.setHex(decal.dryColor)\n else dryColour.copy(colour).multiplyScalar(0.45).lerp(brown, 0.45)\n dried = k > GLOSS_DRIED_AT\n const spread = dried ? 1 + creep : 1 + creep * (1 - Math.exp(-age / 6))\n sx *= dried ? spread : spread * (1 + 0.04 * Math.sin(age * 1.9 + index))\n sy *= dried ? spread : spread * (1 + 0.04 * Math.cos(age * 1.5 + index * 0.7))\n colour.lerp(dryColour, k)\n } else {\n const spread = 1 + creep * (1 - Math.exp(-age / 6))\n sx *= spread * (1 + 0.04 * Math.sin(age * 1.9 + index))\n sy *= spread * (1 + 0.04 * Math.cos(age * 1.5 + index * 0.7))\n if (fluid === 'fuel' || fluid === 'water') {\n const [wait, span] = fluid === 'fuel' ? [6, 5] : [40, 20]\n const gone = clamp((age - wait) / span, 0, 1)\n sx *= 1 - gone * 0.85\n sy *= 1 - gone * 0.85\n colour.lerp(ground.setHex(fluid === 'fuel' ? FUEL_DRIED : GROUND_COLOR), gone * 0.8)\n if (gone >= 1) return null\n }\n }\n }\n return { sx, sy, dried }\n}\n\nfunction decalLayer(THREE: ThreeLib, pool: WorkshopEngine['decals']): Drawn {\n const geometry = new THREE.CircleGeometry(0.5, RIPPLE_SEGMENTS)\n const positions = geometry.getAttribute('position')\n // a blob, not a disc: each rim vertex is pulled in by a fixed amount between .72 and 1.12\n for (let index = 1; index < positions.count; index++) {\n const k = 0.72 + (((index * 2654435761) >>> 0) / 4294967296) * 0.4\n positions.setXY(index, positions.getX(index) * k, positions.getY(index) * k)\n }\n const wet = new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 0.1, metalness: 0.3, polygonOffset: true, polygonOffsetFactor: -4, polygonOffsetUnits: -4 })\n const dry = new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 1, metalness: 0, flatShading: true, polygonOffset: true, polygonOffsetFactor: -4, polygonOffsetUnits: -4 })\n const wetMesh = new THREE.InstancedMesh(geometry, wet, DECAL_CAPACITY)\n const dryMesh = new THREE.InstancedMesh(geometry, dry, DECAL_CAPACITY)\n const group = new THREE.Group()\n group.name = 'workshop-decals'\n for (const mesh of [wetMesh, dryMesh]) {\n mesh.frustumCulled = false\n mesh.renderOrder = 1\n mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage)\n group.add(mesh)\n }\n const hidden = new THREE.Matrix4().makeScale(0, 0, 0)\n const colour = new THREE.Color()\n const dryColour = new THREE.Color()\n const brown = new THREE.Color(BROWN)\n const ground = new THREE.Color()\n const normal = new THREE.Vector3()\n const forward = new THREE.Vector3(0, 0, 1)\n const turn = new THREE.Quaternion()\n const quaternion = new THREE.Quaternion()\n const position = new THREE.Vector3()\n const scale = new THREE.Vector3()\n const matrix = new THREE.Matrix4()\n wetMesh.setColorAt(0, colour.setHex(0))\n dryMesh.setColorAt(0, colour.setHex(0))\n for (let index = 0; index < DECAL_CAPACITY; index++) {\n wetMesh.setMatrixAt(index, hidden)\n dryMesh.setMatrixAt(index, hidden)\n }\n return {\n object: group,\n sync() {\n let drawn = false\n for (let index = 0; index < DECAL_CAPACITY; index++) {\n const decal = pool.decals[index]!\n wetMesh.setMatrixAt(index, hidden)\n dryMesh.setMatrixAt(index, hidden)\n if (!decal.on || decal.t < 0) continue\n const look = decalLook(decal, colour, dryColour, brown, ground, index)\n if (!look) continue\n drawn = true\n normal.set(decal.normal[0], decal.normal[1], decal.normal[2])\n quaternion.setFromUnitVectors(forward, normal)\n quaternion.multiply(turn.setFromAxisAngle(forward, index * 2.399963))\n const offset = 0.004 + (index % 40) * 0.0004\n matrix.compose(position.set(decal.x + normal.x * offset, decal.y + normal.y * offset, decal.z + normal.z * offset), quaternion, scale.set(look.sx, look.sy, 1))\n const mesh = look.dried ? dryMesh : wetMesh\n mesh.setMatrixAt(index, matrix)\n mesh.setColorAt(index, colour)\n }\n for (const mesh of [wetMesh, dryMesh]) {\n mesh.instanceMatrix.needsUpdate = true\n if (mesh.instanceColor) mesh.instanceColor.needsUpdate = true\n }\n return drawn\n },\n dispose() {\n geometry.dispose()\n wet.dispose()\n dry.dispose()\n },\n }\n}\n\nfunction ringLayer(THREE: ThreeLib, pool: WorkshopEngine['rings']): Drawn {\n const geometry = new THREE.RingGeometry(0.86, 1, 40)\n const material = new THREE.MeshBasicMaterial({ color: 0xffffff, transparent: true, blending: THREE.AdditiveBlending, depthWrite: false, side: THREE.DoubleSide })\n const mesh = new THREE.InstancedMesh(geometry, material, RING_CAPACITY)\n mesh.frustumCulled = false\n mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage)\n mesh.name = 'workshop-rings'\n const hidden = new THREE.Matrix4().makeScale(0, 0, 0)\n const colour = new THREE.Color()\n const normal = new THREE.Vector3()\n const forward = new THREE.Vector3(0, 0, 1)\n const quaternion = new THREE.Quaternion()\n const position = new THREE.Vector3()\n const scale = new THREE.Vector3()\n const matrix = new THREE.Matrix4()\n mesh.setColorAt(0, colour.setHex(0))\n for (let index = 0; index < RING_CAPACITY; index++) mesh.setMatrixAt(index, hidden)\n return {\n object: mesh,\n sync() {\n let drawn = false\n for (let index = 0; index < RING_CAPACITY; index++) {\n const ring = pool.rings[index]!\n if (!ring.on) {\n mesh.setMatrixAt(index, hidden)\n continue\n }\n drawn = true\n const k = Math.min(1, ring.t / ring.duration)\n const radius = ring.from + (ring.to - ring.from) * (1 - Math.pow(1 - k, 2))\n quaternion.setFromUnitVectors(forward, normal.set(ring.normal[0], ring.normal[1], ring.normal[2]))\n matrix.compose(position.set(ring.point[0], ring.point[1], ring.point[2]), quaternion, scale.set(radius, radius, 1))\n mesh.setMatrixAt(index, matrix)\n mesh.setColorAt(index, colour.setHex(ring.color).multiplyScalar(ring.hold ? ring.hold : 1 - k))\n }\n mesh.instanceMatrix.needsUpdate = true\n if (mesh.instanceColor) mesh.instanceColor.needsUpdate = true\n return drawn\n },\n dispose() {\n geometry.dispose()\n material.dispose()\n },\n }\n}\n\n/**\n * Draws the pools of the world's Workshop engine (what `world.play` fills): the two sprite systems, the streaks, the physics debris, the decals, the\n * expanding rings and the flash and fire lights, with the page's shaders and materials.\n */\nexport function createWorkshopLayers(THREE: ThreeLib, engine: WorkshopEngine, pointScale: number, lightScale: number): WorkshopLayers {\n const group = new THREE.Group()\n group.name = 'workshop'\n const layers: Drawn[] = [\n spriteLayer(THREE, engine.add, true, 1, pointScale),\n spriteLayer(THREE, engine.normal, false, 0.55, pointScale),\n streakLayer(THREE, engine.streaks),\n debrisLayer(THREE, engine.debris),\n decalLayer(THREE, engine.decals),\n ringLayer(THREE, engine.rings),\n ]\n for (const layer of layers) group.add(layer.object)\n const flashLights = engine.lights.lights.map(() => new THREE.PointLight(0xffc070, 0, 14))\n const fireLights = engine.lights.fire.map(() => new THREE.PointLight(FIRE_LIGHT_COLOR, 0, FIRE_LIGHT_DISTANCE))\n for (const light of [...flashLights, ...fireLights]) group.add(light)\n return {\n group,\n sync() {\n let drawCalls = 0\n for (const layer of layers) if (layer.sync()) drawCalls++\n engine.lights.lights.forEach((source, index) => {\n const light = flashLights[index]!\n light.position.set(source.x, source.y, source.z)\n light.color.setHex(source.color)\n light.distance = source.distance\n light.intensity = source.intensity * lightScale\n })\n engine.lights.fire.forEach((source, index) => {\n const light = fireLights[index]!\n light.position.set(source.x, source.y, source.z)\n light.intensity = source.intensity * lightScale\n })\n return drawCalls\n },\n dispose() {\n for (const layer of layers) layer.dispose()\n group.clear()\n },\n }\n}\n","import type * as THREE_NS from \"three\";\nimport type {\n BenchChange,\n BenchSheet,\n ChoiceOption,\n EffectHandle,\n EffectSink,\n ModuleControl,\n ModuleHost,\n ModuleLayer,\n ModuleMetric,\n TestableModule,\n} from \"@adatechnology/game-contracts\";\nimport {\n EVENT_BUDGET,\n EXPLOSIONS,\n GAME_EFFECTS,\n estimateCost,\n resolveStrings,\n} from \"@adatechnology/game-vfx\";\nimport type { EffectDef, ExplosionDef } from \"@adatechnology/game-vfx\";\nimport { createVfxRenderer, VFX_LAYERS } from \"../renderer\";\nimport type { ThreeLib, VfxRenderer } from \"../types\";\nimport { benchText } from \"./text\";\n\nexport interface VfxTestModuleOptions {\n readonly locale?: string;\n /** The recipes the bench lists (default `GAME_EFFECTS`) and the explosions (default `EXPLOSIONS`). */\n readonly effects?: Readonly<Record<string, EffectDef>>;\n readonly explosions?: Readonly<Record<string, ExplosionDef>>;\n /** Seconds between two replays while `auto_repeat` is on. */\n readonly repeatSeconds?: number;\n}\n\nconst DEFAULT_EFFECT = \"m67\";\n/** The profile the Lab falls back to for an unknown explosive: not something to pick. */\nconst FALLBACK_EXPLOSION = \"generic\";\nconst NO_VARIANT = \"none\";\nconst BLAST_POINT: readonly [number, number, number] = [0, 0.1, 0];\nconst DEFAULT_DISTANCE = 14;\nconst DEFAULT_REPEAT_SECONDS = 4;\nconst LIGHT_BUDGET = EVENT_BUDGET.blastLights + EVENT_BUDGET.fireLights;\nconst SEGMENT_COLORS = [\"#ffb347\", \"#ff7a2a\", \"#8a8f98\", \"#5c6370\"] as const;\n\nconst TEAMS = [\n \"creatures\",\n \"weapons\",\n \"destruction\",\n \"world\",\n \"vehicles\",\n \"screen\",\n] as const;\nconst LAYER_ID = \"show_\";\n\nconst humanize = (id: string): string => id.replace(/_/g, \" \");\n\n/** Where the bench stands for a distance (m): a three-quarter view that keeps the blast in frame. */\nconst eyeFor = (distance: number): [number, number, number] => [\n distance * 0.6,\n distance * 0.35 + 1.4,\n distance * 0.8,\n];\n\n/** The tick marks and segments of a sheet's timeline: an explosion's flash, fireball and smoke, or a recipe's time. */\nfunction timelineOf(\n explosion: ExplosionDef | undefined,\n recipe: EffectDef | undefined,\n metric: string,\n): NonNullable<BenchSheet[\"timeline\"]> {\n if (explosion) {\n const flash = explosion.flash.life ?? 0.2;\n const smoke = Math.max(explosion.smoke.life, 1);\n return {\n segments: [\n { name: \"flash\", start: 0, end: flash, color: SEGMENT_COLORS[0] },\n {\n name: \"fireball\",\n start: 0,\n end: Math.min(smoke, 2.4),\n color: SEGMENT_COLORS[1],\n },\n { name: \"smoke\", start: 0.4, end: smoke, color: SEGMENT_COLORS[2] },\n ],\n ticks: Array.from({ length: Math.ceil(smoke) }, (_, index) => index + 1),\n metric,\n };\n }\n const span = recipe?.loop && recipe.loop > 0 ? recipe.loop : 4;\n return {\n segments: [\n { name: recipe?.id ?? \"\", start: 0, end: span, color: SEGMENT_COLORS[1] },\n ],\n ticks: Array.from({ length: Math.floor(span) }, (_, index) => index + 1),\n metric,\n };\n}\n\n/**\n * The effects bench: pick an explosion or a recipe (listed by team), its variant, the distance, the quality and the layers, replay it or let it repeat,\n * and watch the particle and light counters against the budget. It plays through `host.effects` (the room's world) and, in a host without one, draws\n * into a renderer of its own.\n */\nexport function createVfxTestModule(\n options: VfxTestModuleOptions = {},\n): TestableModule {\n const locale = options.locale ?? \"en\";\n const recipes = options.effects ?? GAME_EFFECTS;\n const explosions = options.explosions ?? EXPLOSIONS;\n const text = benchText(locale);\n const strings = resolveStrings(locale);\n\n const effectOptions = (): ChoiceOption[] => [\n ...Object.keys(explosions)\n .filter((id) => id !== FALLBACK_EXPLOSION)\n .map((id) => ({\n id,\n name: humanize(id),\n group: strings.groups.explosions ?? text.sheet.explosion,\n })),\n // an id the explosions already use is the explosion's: the sink tries the explosions first, so the recipe of the same name could not be reached\n ...TEAMS.flatMap((team) =>\n Object.values(recipes)\n .filter((def) => def.team === team && !(def.id in explosions))\n .map((def) => ({\n id: def.id,\n name: strings.effects[def.id]?.name ?? humanize(def.id),\n group: strings.groups[def.team] ?? def.team,\n })),\n ),\n ];\n\n const sheetFor = (id: string): BenchSheet => {\n const explosion = explosions[id];\n if (explosion) {\n return {\n kicker: strings.groups.explosions ?? text.sheet.explosion,\n title: humanize(id),\n facts: [\n { label: text.sheet.lethal, value: `${explosion.lethal} m` },\n { label: text.sheet.injury, value: `${explosion.injury} m` },\n { label: text.sheet.radius, value: `${explosion.visualRadius} m` },\n { label: text.sheet.fireballs, value: String(explosion.fireballs) },\n {\n label: text.sheet.particles,\n value: `${estimateCost(explosion)} / ${EVENT_BUDGET.particles}`,\n },\n ],\n timeline: timelineOf(explosion, undefined, \"blast_time\"),\n };\n }\n const recipe = recipes[id];\n const words = strings.effects[id];\n const labels = strings.sheet;\n return {\n kicker: recipe ? (strings.groups[recipe.team] ?? recipe.team) : undefined,\n title: words?.name ?? humanize(id),\n ...(words ? { body: words.short } : {}),\n facts: words\n ? [{ label: labels.budget, value: `${words.cost} · ${words.costNote}` }]\n : [],\n steps: words\n ? [\n { at: labels.when, text: words.when },\n { at: labels.look, text: words.look },\n { at: labels.budget, text: words.budget },\n { at: labels.network, text: words.network },\n ]\n : [],\n timeline: timelineOf(undefined, recipe, \"phase\"),\n };\n };\n\n const controls = (): ModuleControl[] => [\n {\n kind: \"choice\",\n id: \"effect\",\n name: text.controls.effect,\n options: effectOptions(),\n default:\n DEFAULT_EFFECT in explosions ? DEFAULT_EFFECT : effectOptions()[0]!.id,\n },\n {\n kind: \"choice\",\n id: \"variant\",\n name: text.controls.variant,\n options: [{ id: NO_VARIANT, name: text.none }],\n default: NO_VARIANT,\n hidden: true,\n },\n { kind: \"action\", id: \"replay\", name: text.controls.replay },\n {\n kind: \"boolean\",\n id: \"auto_repeat\",\n name: text.controls.auto_repeat,\n default: false,\n },\n {\n kind: \"number\",\n id: \"distance\",\n name: text.controls.distance,\n min: 4,\n max: 120,\n step: 1,\n unit: \"m\",\n default: DEFAULT_DISTANCE,\n },\n {\n kind: \"choice\",\n id: \"lod_band\",\n name: text.controls.lod_band,\n options: ([\"auto\", \"near\", \"middle\", \"far\"] as const).map((id) => ({\n id,\n name: text.lod[id],\n })),\n default: \"auto\",\n },\n {\n kind: \"choice\",\n id: \"quality\",\n name: text.controls.quality,\n options: ([\"high\", \"medium\", \"low\"] as const).map((id) => ({\n id,\n name: text.quality[id],\n })),\n default: \"high\",\n },\n ];\n\n const layers = (): ModuleLayer[] =>\n VFX_LAYERS.map((id) => ({ id: `${LAYER_ID}${id}`, name: text.layers[id] }));\n\n /** The live counters: shared by `metrics` and filled by `attach` (a module without a host reads zeros). */\n let read: () => {\n stats: Readonly<Record<string, number>>;\n phase: number;\n budget: number;\n } = () => ({ stats: {}, phase: 0, budget: 0 });\n\n const metrics = (): ModuleMetric[] => [\n {\n id: \"puffs\",\n name: text.metrics.puffs,\n digits: 0,\n read: () => read().stats.puffs ?? 0,\n },\n {\n id: \"sparks\",\n name: text.metrics.sparks,\n digits: 0,\n read: () => read().stats.sparks ?? 0,\n },\n {\n id: \"debris\",\n name: text.metrics.debris,\n digits: 0,\n max: EVENT_BUDGET.debris,\n read: () => read().stats.chunks ?? 0,\n },\n {\n id: \"lights\",\n name: text.metrics.lights,\n digits: 0,\n max: LIGHT_BUDGET,\n read: () =>\n (read().stats.blastLights ?? 0) + (read().stats.fireLights ?? 0),\n },\n {\n id: \"budget_particles\",\n name: text.metrics.budget_particles,\n digits: 0,\n max: EVENT_BUDGET.particles,\n read: () => read().budget,\n },\n {\n id: \"lod\",\n name: text.metrics.lod,\n digits: 2,\n read: () => read().stats.lod ?? 1,\n },\n {\n id: \"phase\",\n name: text.metrics.phase,\n unit: \"s\",\n digits: 2,\n read: () => read().phase,\n },\n {\n id: \"blast_time\",\n name: text.metrics.blast_time,\n unit: \"s\",\n digits: 2,\n read: () => Math.max(0, read().stats.blastTime ?? 0),\n },\n ];\n\n return {\n id: \"vfx\",\n name: text.module,\n group: \"effects\",\n items: () => [],\n controls,\n layers,\n metrics,\n attach(host: ModuleHost) {\n const bench = host.bench;\n let own: VfxRenderer | undefined;\n let sink: EffectSink | undefined = host.effects;\n if (!sink) {\n own = createVfxRenderer(host.THREE as ThreeLib, {\n scene: host.scene as THREE_NS.Scene,\n camera: host.camera as THREE_NS.PerspectiveCamera,\n });\n sink = own;\n }\n const effects = sink;\n let effect = \"m67\";\n let variant = NO_VARIANT;\n let distance = DEFAULT_DISTANCE;\n let auto = false;\n let phase = 0;\n let handle: EffectHandle | null = null;\n let sinceRepeat = 0;\n\n read = () => {\n const def = explosions[effect];\n const recipe = recipes[effect];\n const budget = def\n ? estimateCost(def)\n : (recipe?.budget.particles ?? 0);\n return { stats: effects.stats?.() ?? {}, phase, budget };\n };\n\n function replay(): void {\n if (handle !== null) effects.stop(handle);\n phase = 0;\n sinceRepeat = 0;\n handle = effects.play({\n effect,\n point: [BLAST_POINT[0], BLAST_POINT[1], BLAST_POINT[2]],\n direction: [1, 0.2, 0.4],\n normal: [0, 1, 0],\n ...(variant !== NO_VARIANT &&\n recipes[effect]?.params?.options.includes(variant)\n ? { param: variant }\n : {}),\n });\n }\n\n function choose(id: string): void {\n effect = id;\n const params = recipes[id]?.params;\n variant = params\n ? (params.options[params.default] ?? params.options[0] ?? NO_VARIANT)\n : NO_VARIANT;\n bench?.patch(\n \"variant\",\n params\n ? {\n hidden: false,\n options: params.options.map((option) => ({\n id: option,\n name: option,\n })),\n value: variant,\n }\n : {\n hidden: true,\n options: [{ id: NO_VARIANT, name: text.none }],\n value: NO_VARIANT,\n },\n );\n bench?.sheet?.(sheetFor(id));\n replay();\n }\n\n function look(): void {\n const eye = eyeFor(distance);\n bench?.look(eye, [0, 1.2, 0]);\n }\n\n const apply = (change: BenchChange): void => {\n if (change.kind === \"layer\")\n effects.configure?.({\n [`layer_${change.id.slice(LAYER_ID.length)}`]: change.on,\n });\n else if (change.kind === \"action\") {\n if (change.id === \"replay\") replay();\n } else if (change.id === \"effect\" && typeof change.value === \"string\")\n choose(change.value);\n else if (change.id === \"variant\" && typeof change.value === \"string\") {\n variant = change.value;\n if (recipes[effect]?.params) replay();\n } else if (change.id === \"auto_repeat\") auto = change.value === true;\n else if (change.id === \"distance\" && typeof change.value === \"number\") {\n distance = change.value;\n look();\n } else if (change.id === \"lod_band\" || change.id === \"quality\")\n effects.configure?.({ [change.id]: change.value });\n };\n const offChange = bench?.onChange(apply);\n const offFrame = host.onFrame((dt) => {\n phase += dt;\n sinceRepeat += dt;\n own?.update(dt);\n if (\n auto &&\n sinceRepeat >= (options.repeatSeconds ?? DEFAULT_REPEAT_SECONDS)\n )\n replay();\n });\n return () => {\n offChange?.();\n offFrame();\n if (handle !== null) effects.stop(handle);\n bench?.sheet?.(null);\n own?.dispose();\n read = () => ({ stats: {}, phase: 0, budget: 0 });\n };\n },\n };\n}\n","/** The words of the effects bench, per locale (the effects' own texts live in `@adatechnology/game-vfx`). */\nexport interface BenchText {\n readonly module: string\n readonly controls: Readonly<Record<'effect' | 'variant' | 'replay' | 'auto_repeat' | 'distance' | 'lod_band' | 'quality', string>>\n readonly lod: Readonly<Record<'auto' | 'near' | 'middle' | 'far', string>>\n readonly quality: Readonly<Record<'high' | 'medium' | 'low', string>>\n readonly layers: Readonly<Record<'puffs' | 'sparks' | 'glows' | 'debris' | 'shock' | 'sprites', string>>\n readonly metrics: Readonly<Record<'puffs' | 'sparks' | 'debris' | 'lights' | 'budget_particles' | 'lod' | 'phase' | 'blast_time', string>>\n readonly sheet: Readonly<Record<'explosion' | 'lethal' | 'injury' | 'radius' | 'fireballs' | 'particles' | 'scale', string>>\n readonly none: string\n}\n\nexport const benchTexts: Readonly<Record<string, BenchText>> = {\n en: {\n module: 'Effects',\n controls: { effect: 'Effect', variant: 'Variant', replay: 'Replay', auto_repeat: 'Auto replay', distance: 'Distance', lod_band: 'LOD band', quality: 'Quality' },\n lod: { auto: 'Auto', near: 'Near', middle: 'Middle', far: 'Far' },\n quality: { high: 'High', medium: 'Medium', low: 'Low' },\n layers: { puffs: 'Smoke and fire', sparks: 'Sparks', glows: 'Glows', debris: 'Debris', shock: 'Shock wave', sprites: 'Droplets and recipes' },\n metrics: { puffs: 'Puffs', sparks: 'Sparks', debris: 'Debris', lights: 'Lights', budget_particles: 'Particles of the effect', lod: 'LOD scale', phase: 'Time since replay', blast_time: 'Time since the blast' },\n sheet: { explosion: 'Explosion', lethal: 'Lethal radius', injury: 'Injury radius', radius: 'Fireball', fireballs: 'Fireballs', particles: 'Particles', scale: 'Scale' },\n none: 'None',\n },\n 'pt-BR': {\n module: 'Efeitos',\n controls: { effect: 'Efeito', variant: 'Variante', replay: 'Repetir', auto_repeat: 'Repetição automática', distance: 'Distância', lod_band: 'Faixa de LOD', quality: 'Qualidade' },\n lod: { auto: 'Automático', near: 'Perto', middle: 'Meio', far: 'Longe' },\n quality: { high: 'Alta', medium: 'Média', low: 'Baixa' },\n layers: { puffs: 'Fumaça e fogo', sparks: 'Faíscas', glows: 'Brilhos', debris: 'Destroços', shock: 'Onda de choque', sprites: 'Gotas e receitas' },\n metrics: { puffs: 'Nuvens', sparks: 'Faíscas', debris: 'Destroços', lights: 'Luzes', budget_particles: 'Partículas do efeito', lod: 'Escala de LOD', phase: 'Tempo desde a repetição', blast_time: 'Tempo desde a explosão' },\n sheet: { explosion: 'Explosão', lethal: 'Raio letal', injury: 'Raio de ferimento', radius: 'Bola de fogo', fireballs: 'Bolas de fogo', particles: 'Partículas', scale: 'Escala' },\n none: 'Nenhuma',\n },\n}\n\nexport const benchText = (locale: string): BenchText => benchTexts[locale] ?? benchTexts.en!\n","import type * as THREE_NS from 'three'\nimport type { GroundField } from '@adatechnology/game-vfx'\nimport type { ThreeLib } from './types'\n\nexport interface GroundPatchOptions {\n /** The height field the craters dig; the mesh follows it. */\n readonly field: GroundField\n /** What the patch is made of (default a plain flat-shaded grey standard material). */\n readonly material?: THREE_NS.Material\n /** Texture coordinates repeat every this many metres (the Lab's 17). */\n readonly textureMetres?: number\n}\n\nexport interface GroundPatch {\n readonly mesh: THREE_NS.Mesh\n /** Redraws the vertices when the field changed since the last call. Returns whether it did. */\n sync(): boolean\n dispose(): void\n}\n\nconst DEFAULT_TEXTURE_METRES = 17\n\n/** The Lab's 60 m deformable patch with 33 cm cells (L60-83), as a mesh that follows a `GroundField`. */\nexport function createGroundPatch(THREE: ThreeLib, options: GroundPatchOptions): GroundPatch {\n const field = options.field\n const metres = options.textureMetres ?? DEFAULT_TEXTURE_METRES\n const geometry = new THREE.PlaneGeometry(field.size, field.size, field.segments, field.segments).rotateX(-Math.PI / 2)\n const position = geometry.attributes.position!\n const uv = geometry.attributes.uv!\n for (let index = 0; index < position.count; index++) uv.setXY(index, position.getX(index) / metres, position.getZ(index) / metres)\n const material = options.material ?? new THREE.MeshStandardMaterial({ color: 0x2b2c2f, roughness: 0.93, flatShading: true })\n const mesh = new THREE.Mesh(geometry, material)\n mesh.receiveShadow = true\n let version = -1\n\n function sync(): boolean {\n if (field.version === version) return false\n version = field.version\n for (let index = 0; index < position.count; index++) position.setY(index, field.heights[index]!)\n position.needsUpdate = true\n geometry.computeBoundingSphere()\n return true\n }\n\n return {\n mesh,\n sync,\n dispose() {\n geometry.dispose()\n if (!options.material) material.dispose()\n },\n }\n}\n","/**\n * Convex solids (a cylinder or a box, in container space) clipped by the liquid surface plane `u · p <= d`.\n * This is robust for any tilt (upright, lying on its side, upside down): the result is exactly the part of the container the liquid fills,\n * with the surface cap lying on the plane. Written into preallocated buffers, no per-frame geometry is created.\n */\nexport type V3 = [number, number, number]\n\nexport interface SolidFace {\n p: V3[]\n n: V3[]\n}\n\nexport interface Solid {\n faces: SolidFace[]\n /** Upper bound of the vertices a clip can write (non-indexed triangles). */\n maxVertices: number\n}\n\nconst sub = (a: V3, b: V3): V3 => [a[0] - b[0], a[1] - b[1], a[2] - b[2]]\nconst cross = (a: V3, b: V3): V3 => [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]]\nconst dot = (a: V3, b: V3): number => a[0] * b[0] + a[1] * b[1] + a[2] * b[2]\nconst norm = (a: V3): V3 => {\n const l = Math.hypot(a[0], a[1], a[2]) || 1\n return [a[0] / l, a[1] / l, a[2] / l]\n}\n\n/** Reverse the winding when the face does not look outward. */\nconst orient = (p: V3[], n: V3[], outward: V3): SolidFace => {\n if (dot(cross(sub(p[1], p[0]), sub(p[2], p[0])), outward) < 0) return { p: [...p].reverse(), n: [...n].reverse() }\n return { p, n }\n}\n\nconst finish = (faces: SolidFace[]): Solid => {\n let maxVertices = 0\n for (const f of faces) maxVertices += (f.p.length * 2 + 2) * 3\n // The surface cap: at most one point per face edge cut.\n maxVertices += faces.length * 3 * 2 + 64\n return { faces, maxVertices }\n}\n\nexport const cylinderSolid = (radius: number, height: number, segments: number): Solid => {\n const faces: SolidFace[] = []\n const ring = (y: number, i: number): V3 => {\n const a = (i / segments) * Math.PI * 2\n return [Math.cos(a) * radius, y, Math.sin(a) * radius]\n }\n const radial = (i: number): V3 => {\n const a = (i / segments) * Math.PI * 2\n return [Math.cos(a), 0, Math.sin(a)]\n }\n for (let i = 0; i < segments; i++) {\n const j = (i + 1) % segments\n const mid = norm([radial(i)[0] + radial(j)[0], 0, radial(i)[2] + radial(j)[2]])\n faces.push(orient([ring(0, i), ring(0, j), ring(height, j), ring(height, i)], [radial(i), radial(j), radial(j), radial(i)], mid))\n }\n const bottom: V3[] = []\n const top: V3[] = []\n for (let i = 0; i < segments; i++) {\n bottom.push(ring(0, i))\n top.push(ring(height, i))\n }\n faces.push(orient(bottom, bottom.map((): V3 => [0, -1, 0]), [0, -1, 0]))\n faces.push(orient(top, top.map((): V3 => [0, 1, 0]), [0, 1, 0]))\n return finish(faces)\n}\n\nexport const boxSolid = (size: V3): Solid => {\n const hx = size[0] / 2\n const hz = size[2] / 2\n const hy = size[1]\n const c = (x: number, y: number, z: number): V3 => [x * hx, y * hy, z * hz]\n const quad = (a: V3, b: V3, cc: V3, d: V3, n: V3): SolidFace => orient([a, b, cc, d], [n, n, n, n], n)\n return finish([\n quad(c(1, 0, -1), c(1, 1, -1), c(1, 1, 1), c(1, 0, 1), [1, 0, 0]),\n quad(c(-1, 0, -1), c(-1, 1, -1), c(-1, 1, 1), c(-1, 0, 1), [-1, 0, 0]),\n quad(c(-1, 1, -1), c(1, 1, -1), c(1, 1, 1), c(-1, 1, 1), [0, 1, 0]),\n quad(c(-1, 0, -1), c(1, 0, -1), c(1, 0, 1), c(-1, 0, 1), [0, -1, 0]),\n quad(c(-1, 0, 1), c(1, 0, 1), c(1, 1, 1), c(-1, 1, 1), [0, 0, 1]),\n quad(c(-1, 0, -1), c(1, 0, -1), c(1, 1, -1), c(-1, 1, -1), [0, 0, -1]),\n ])\n}\n\nconst polyP: number[] = []\nconst polyN: number[] = []\nconst cut: number[] = []\n\n/**\n * Writes the triangles of `solid ∩ { u · p <= d }` into `pos` / `nor` (non-indexed, 3 floats per vertex) and returns the vertex count.\n * The cap on the plane has normal `u`.\n */\nexport const clipSolid = (solid: Solid, u: V3, d: number, pos: Float32Array, nor: Float32Array): number => {\n let count = 0\n const capacity = Math.floor(pos.length / 3)\n cut.length = 0\n const put = (px: number, py: number, pz: number, nx: number, ny: number, nz: number): void => {\n if (count >= capacity) return\n pos[count * 3] = px\n pos[count * 3 + 1] = py\n pos[count * 3 + 2] = pz\n nor[count * 3] = nx\n nor[count * 3 + 1] = ny\n nor[count * 3 + 2] = nz\n count++\n }\n\n for (const face of solid.faces) {\n const m = face.p.length\n polyP.length = 0\n polyN.length = 0\n for (let i = 0; i < m; i++) {\n const a = face.p[i]\n const b = face.p[(i + 1) % m]\n const na = face.n[i]\n const nb = face.n[(i + 1) % m]\n const da = u[0] * a[0] + u[1] * a[1] + u[2] * a[2] - d\n const db = u[0] * b[0] + u[1] * b[1] + u[2] * b[2] - d\n const ina = da <= 0\n const inb = db <= 0\n if (ina) {\n polyP.push(a[0], a[1], a[2])\n polyN.push(na[0], na[1], na[2])\n if (da > -1e-7) cut.push(a[0], a[1], a[2])\n }\n if (ina !== inb) {\n const t = da / (da - db)\n const x = a[0] + (b[0] - a[0]) * t\n const y = a[1] + (b[1] - a[1]) * t\n const z = a[2] + (b[2] - a[2]) * t\n polyP.push(x, y, z)\n polyN.push(na[0] + (nb[0] - na[0]) * t, na[1] + (nb[1] - na[1]) * t, na[2] + (nb[2] - na[2]) * t)\n cut.push(x, y, z)\n }\n }\n const n = polyP.length / 3\n for (let i = 1; i < n - 1; i++) {\n for (const k of [0, i, i + 1]) put(polyP[k * 3], polyP[k * 3 + 1], polyP[k * 3 + 2], polyN[k * 3], polyN[k * 3 + 1], polyN[k * 3 + 2])\n }\n }\n\n // Cap on the plane: unique cut points sorted around their centre.\n const pts: V3[] = []\n for (let i = 0; i < cut.length; i += 3) {\n let dup = false\n for (const q of pts) {\n if (Math.abs(q[0] - cut[i]) + Math.abs(q[1] - cut[i + 1]) + Math.abs(q[2] - cut[i + 2]) < 1e-6) {\n dup = true\n break\n }\n }\n if (!dup) pts.push([cut[i], cut[i + 1], cut[i + 2]])\n }\n if (pts.length >= 3) {\n const c: V3 = [0, 0, 0]\n for (const q of pts) {\n c[0] += q[0] / pts.length\n c[1] += q[1] / pts.length\n c[2] += q[2] / pts.length\n }\n const e1 = norm(cross(u, Math.abs(u[0]) < 0.9 ? [1, 0, 0] : [0, 1, 0]))\n const e2 = cross(u, e1)\n const keyed = pts.map((q) => ({ q, a: Math.atan2(dot(sub(q, c), e2), dot(sub(q, c), e1)) })).sort((p, r) => p.a - r.a)\n for (let i = 0; i < keyed.length; i++) {\n for (const q of [c, keyed[i].q, keyed[(i + 1) % keyed.length].q]) put(q[0], q[1], q[2], u[0], u[1], u[2])\n }\n }\n return count\n}\n","import type * as THREE_NS from 'three'\nimport type { ContainerState, FluidWorld, Vec3 } from '@adatechnology/game-fluids'\nimport { boxSolid, clipSolid, cylinderSolid } from './clip'\nimport type { Solid, V3 } from './clip'\nimport type { MaterialCache } from './materials'\nimport type { FluidSprites } from './sprites'\nimport type { QualityProfile } from './quality'\nimport type { ThreeLib } from './types'\n\n/** How far the liquid is kept inside the container walls so it does not fight with them. */\nconst INSET = 0.985\n\n/**\n * The liquid inside one bound container: the container's inner shape clipped by the world surface plane, expressed in the bound object's space,\n * so the cap always lies on the plane whatever the tilt. Plus steam puffs above a hot surface.\n */\nexport class LiquidBody {\n readonly group: THREE_NS.Group\n private mesh: THREE_NS.Mesh | null = null\n private geometry: THREE_NS.BufferGeometry | null = null\n private pos: Float32Array = new Float32Array(0)\n private nor: Float32Array = new Float32Array(0)\n private solid: Solid | null = null\n private shapeKey = ''\n private steamAcc = 0\n private readonly inverse: THREE_NS.Matrix4\n private readonly tmpN: THREE_NS.Vector3\n private readonly tmpP: THREE_NS.Vector3\n vertices = 0\n\n constructor(\n private readonly THREE: ThreeLib,\n readonly containerId: string,\n readonly object: THREE_NS.Object3D,\n offset: Vec3,\n ) {\n this.group = new THREE.Group()\n this.group.name = 'fluid_liquid_body'\n this.group.position.set(offset[0], offset[1], offset[2])\n this.inverse = new THREE.Matrix4()\n this.tmpN = new THREE.Vector3()\n this.tmpP = new THREE.Vector3()\n object.add(this.group)\n }\n\n get visible(): boolean {\n return !!this.mesh && this.mesh.visible\n }\n\n private build(state: ContainerState, quality: QualityProfile): void {\n const shape = state.shape\n if (!shape) return\n const key = JSON.stringify(shape) + quality.bodySegments\n if (key === this.shapeKey) return\n this.shapeKey = key\n this.solid =\n shape.kind === 'cylinder'\n ? cylinderSolid(shape.radius * INSET, shape.height * INSET, quality.bodySegments)\n : boxSolid([shape.size[0] * INSET, shape.size[1] * INSET, shape.size[2] * INSET])\n const T = this.THREE\n this.geometry?.dispose()\n this.pos = new Float32Array(this.solid.maxVertices * 3)\n this.nor = new Float32Array(this.solid.maxVertices * 3)\n this.geometry = new T.BufferGeometry()\n const p = new T.BufferAttribute(this.pos, 3)\n const n = new T.BufferAttribute(this.nor, 3)\n p.setUsage(T.DynamicDrawUsage)\n n.setUsage(T.DynamicDrawUsage)\n this.geometry.setAttribute('position', p)\n this.geometry.setAttribute('normal', n)\n if (!this.mesh) {\n this.mesh = new T.Mesh(this.geometry)\n this.mesh.name = 'fluid_liquid'\n this.mesh.frustumCulled = false\n this.mesh.renderOrder = 3\n this.group.add(this.mesh)\n } else this.mesh.geometry = this.geometry\n }\n\n update(dt: number, world: FluidWorld, materials: MaterialCache, quality: QualityProfile, particles: FluidSprites): void {\n const state = world.container(this.containerId)\n const def = state?.liquid ? world.liquids.get(state.liquid) : undefined\n if (!state || !state.shape || !def || state.liters <= 1e-6) {\n if (this.mesh) this.mesh.visible = false\n this.vertices = 0\n return\n }\n this.build(state, quality)\n if (!this.mesh || !this.solid) return\n this.mesh.material = materials.liquidBody(def)\n\n // The surface plane in the group's space.\n this.object.updateWorldMatrix(true, false)\n this.group.updateWorldMatrix(true, false)\n this.inverse.copy(this.group.matrixWorld).invert()\n this.tmpN.set(state.surfaceNormal[0], state.surfaceNormal[1], state.surfaceNormal[2]).transformDirection(this.inverse)\n this.tmpP.set(state.surfacePoint[0], state.surfacePoint[1], state.surfacePoint[2]).applyMatrix4(this.inverse)\n const u: V3 = [this.tmpN.x, this.tmpN.y, this.tmpN.z]\n const d = u[0] * this.tmpP.x + u[1] * this.tmpP.y + u[2] * this.tmpP.z\n\n this.vertices = clipSolid(this.solid, u, d, this.pos, this.nor)\n const geometry = this.geometry as THREE_NS.BufferGeometry\n geometry.setDrawRange(0, this.vertices)\n ;(geometry.getAttribute('position') as THREE_NS.BufferAttribute).needsUpdate = true\n ;(geometry.getAttribute('normal') as THREE_NS.BufferAttribute).needsUpdate = true\n this.mesh.visible = this.vertices > 0\n\n // Steam above a hot open surface.\n if (state.steaming && this.vertices > 0) {\n this.steamAcc += dt * quality.steamRate\n while (this.steamAcc >= 1) {\n this.steamAcc -= 1\n const o = this.tmpP.set(state.surfacePoint[0], state.surfacePoint[1], state.surfacePoint[2])\n particles.steam([o.x, o.y + 0.01, o.z])\n }\n } else this.steamAcc = 0\n }\n\n get triangles(): number {\n return this.visible ? Math.floor(this.vertices / 3) : 0\n }\n\n dispose(): void {\n this.object.remove(this.group)\n this.geometry?.dispose()\n this.geometry = null\n this.mesh = null\n }\n}\n","export const clamp = (v: number, lo: number, hi: number): number => (v < lo ? lo : v > hi ? hi : v)\nexport const lerp = (a: number, b: number, t: number): number => a + (b - a) * t\n\n/** Small seeded generator for cosmetic randomness (droplet directions): the same input gives the same droplets. */\nexport class Rng {\n private state: number\n constructor(seed: number) {\n this.state = (seed >>> 0) || 1\n }\n next(): number {\n this.state = (this.state + 0x6d2b79f5) >>> 0\n let t = this.state\n t = Math.imul(t ^ (t >>> 15), t | 1)\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61)\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296\n }\n}\n\n/** Integer hash of a point, to seed cosmetic randomness from world state. */\nexport const hashPoint = (x: number, y: number, z: number): number => {\n let h = 2166136261\n for (const v of [x, y, z]) h = Math.imul(h ^ Math.round(v * 1000), 16777619)\n return h >>> 0\n}\n","import type * as THREE_NS from 'three'\nimport type { LiquidDef } from '@adatechnology/game-fluids'\nimport { clamp } from './util'\nimport type { ThreeLib } from './types'\n\n/** Colour of a stain: the liquid's `stainColor`, else a darker `color`. */\nexport const stainColorOf = (def: LiquidDef): number => {\n if (def.stainColor !== undefined) return def.stainColor\n const r = ((def.color >> 16) & 255) * 0.65\n const g = ((def.color >> 8) & 255) * 0.65\n const b = (def.color & 255) * 0.65\n return (Math.round(r) << 16) | (Math.round(g) << 8) | Math.round(b)\n}\n\n/**\n * One material per liquid and role, created on demand and shared by every mesh: each liquid is its own material (colour, opacity, gloss).\n * Wet surfaces use a low roughness from `gloss` so they reflect the scene's environment.\n */\nexport class MaterialCache {\n private body = new Map<string, THREE_NS.MeshStandardMaterial>()\n private stream = new Map<string, THREE_NS.MeshStandardMaterial>()\n private film = new Map<string, THREE_NS.MeshStandardMaterial>()\n private wet = new Map<string, THREE_NS.MeshStandardMaterial>()\n\n constructor(\n private readonly THREE: ThreeLib,\n private readonly envMap: THREE_NS.Texture | null,\n ) {}\n\n private make(params: THREE_NS.MeshStandardMaterialParameters): THREE_NS.MeshStandardMaterial {\n const m = new this.THREE.MeshStandardMaterial({ metalness: 0, ...params })\n if (this.envMap) m.envMap = this.envMap\n return m\n }\n\n /** The volume of liquid inside a container. */\n liquidBody(def: LiquidDef): THREE_NS.MeshStandardMaterial {\n let m = this.body.get(def.id)\n if (!m) {\n m = this.make({\n color: def.color,\n opacity: def.opacity,\n transparent: def.opacity < 0.999,\n roughness: clamp(1 - def.gloss * 0.9, 0.04, 1),\n envMapIntensity: 0.5 + def.gloss * 0.9,\n depthWrite: def.opacity >= 0.999,\n })\n this.body.set(def.id, m)\n }\n return m\n }\n\n /** A jet or a drop in the air. */\n liquidStream(def: LiquidDef): THREE_NS.MeshStandardMaterial {\n let m = this.stream.get(def.id)\n if (!m) {\n m = this.make({\n color: def.color,\n opacity: clamp(def.opacity + 0.1, 0.35, 1),\n transparent: true,\n roughness: clamp(1 - def.gloss * 0.95, 0.03, 1),\n envMapIntensity: 0.8 + def.gloss,\n side: this.THREE.DoubleSide,\n depthWrite: false,\n })\n this.stream.set(def.id, m)\n }\n return m\n }\n\n /** The thin outer film of a stain (and the permanent mark): tinted by vertex colour, soft rim by vertex alpha, mostly matte. */\n stainFilm(def: LiquidDef): THREE_NS.MeshStandardMaterial {\n let m = this.film.get(def.id)\n if (!m) {\n m = this.make({\n color: 0xffffff,\n vertexColors: true,\n transparent: true,\n roughness: clamp(0.95 - def.gloss * 0.45, 0.3, 1),\n envMapIntensity: 0.4 + def.gloss * 0.4,\n depthWrite: false,\n polygonOffset: true,\n polygonOffsetFactor: -2,\n polygonOffsetUnits: -2,\n })\n this.film.set(def.id, m)\n }\n return m\n }\n\n /** The glossy wet part of a stain, which shrinks while it dries. */\n stainWet(def: LiquidDef): THREE_NS.MeshStandardMaterial {\n let m = this.wet.get(def.id)\n if (!m) {\n m = this.make({\n color: 0xffffff,\n vertexColors: true,\n transparent: true,\n roughness: clamp(1 - def.gloss, 0.02, 0.7),\n envMapIntensity: 0.9 + def.gloss * 1.4,\n depthWrite: false,\n polygonOffset: true,\n polygonOffsetFactor: -3,\n polygonOffsetUnits: -3,\n })\n this.wet.set(def.id, m)\n }\n return m\n }\n\n dispose(): void {\n for (const map of [this.body, this.stream, this.film, this.wet]) {\n for (const m of map.values()) m.dispose()\n map.clear()\n }\n }\n}\n","import type { FluidQuality } from './types'\n\nexport interface QualityProfile {\n /** Radial segments of the container liquid volume. */\n bodySegments: number\n /** Samples along a stream and sides of its tube. */\n streamSamples: number\n streamSides: number\n /** Drops of a thin stream. */\n thinDrops: number\n /** Subdivisions per front sample of a stain outline (16 samples). */\n stainSubdiv: number\n maxStains: number\n maxStreams: number\n maxParticles: number\n /** Steam puffs per second of a steaming container. */\n steamRate: number\n /** Droplets of a full-intensity splash. */\n splashDroplets: number\n}\n\nexport const PROFILES: Record<FluidQuality, QualityProfile> = {\n low: { bodySegments: 10, streamSamples: 8, streamSides: 4, thinDrops: 4, stainSubdiv: 1, maxStains: 24, maxStreams: 12, maxParticles: 96, steamRate: 4, splashDroplets: 5 },\n medium: { bodySegments: 14, streamSamples: 12, streamSides: 5, thinDrops: 6, stainSubdiv: 2, maxStains: 48, maxStreams: 24, maxParticles: 256, steamRate: 8, splashDroplets: 9 },\n high: { bodySegments: 20, streamSamples: 20, streamSides: 8, thinDrops: 8, stainSubdiv: 4, maxStains: 96, maxStreams: 48, maxParticles: 512, steamRate: 14, splashDroplets: 14 },\n}\n\nexport const profileOf = (q: FluidQuality | undefined): QualityProfile => PROFILES[q ?? 'medium'] ?? PROFILES.medium\n","import type { VfxWorld } from '@adatechnology/game-vfx'\nimport { Rng, clamp, hashPoint } from './util'\n\n/**\n * Splash droplets and steam, made of the shared sprite kinds of the effects world (`drips` and `steam`) instead of a pool of their own. The droplets are\n * ballistic and stop at the height they were thrown from; the steam rises slowly.\n */\nexport class FluidSprites {\n private readonly rng = new Rng(1234567)\n private thrown = 0\n\n constructor(\n private readonly vfx: VfxWorld,\n private readonly owner: string,\n ) {}\n\n /** Droplets thrown up by an impact; they stop at `point[1]`. */\n splash(point: [number, number, number], intensity: number, hex: number, opacity: number, count: number): void {\n const rng = new Rng(hashPoint(point[0], point[1], point[2]) ^ this.thrown)\n const n = Math.max(2, Math.round(count * (0.35 + 0.65 * clamp(intensity, 0, 1))))\n this.thrown += n\n for (let k = 0; k < n; k++) {\n const a = rng.next() * Math.PI * 2\n const h = (0.25 + rng.next() * 0.9) * (0.4 + intensity)\n const up = (0.7 + rng.next() * 1.5) * (0.5 + intensity)\n const life = 0.45 + rng.next() * 0.45\n const size = 0.012 + rng.next() * 0.014\n this.vfx.spawn('drips', [point[0], point[1] + 0.01, point[2]], [Math.cos(a) * h, up, Math.sin(a) * h], {\n color: hex,\n life: life / DRIP_LIFE,\n size: size / DRIP_SIZE,\n alpha: clamp(opacity + 0.2, 0.35, 1),\n floor: point[1],\n })\n }\n }\n\n /** One puff of steam rising from `point`. */\n steam(point: [number, number, number]): void {\n const r = this.rng\n this.vfx.spawn('steam', [point[0] + (r.next() - 0.5) * 0.04, point[1], point[2] + (r.next() - 0.5) * 0.04], [(r.next() - 0.5) * 0.06, 0.22 + r.next() * 0.12, (r.next() - 0.5) * 0.06], {\n color: STEAM_COLOR,\n life: (1.2 + r.next() * 0.6) / STEAM_LIFE,\n size: STEAM_SIZE_FACTOR,\n alpha: STEAM_ALPHA,\n rise: STEAM_RISE,\n })\n }\n\n /** The sprites alive in the world (a shared world counts everyone's). */\n get count(): number {\n return this.vfx.sprites().live\n }\n}\n\n// The `drips` kind lives 2 to 3 s at 0.025 to 0.035 m, the `steam` kind 1 to 2 s at 0.12 to 0.2 m with 0.35 alpha and a rise of 0.9 m/s²: the\n// fluids pool's old droplets (0.45 to 0.9 s, 0.012 to 0.026 m) and steam (1.2 to 1.8 s, 0.03 m growing to 0.1 m, 0.24 alpha) are those scaled.\nconst DRIP_LIFE = 2.5\nconst DRIP_SIZE = 0.03\nconst STEAM_LIFE = 1.5\nconst STEAM_COLOR = 0xf4f8ff\nconst STEAM_SIZE_FACTOR = 0.2\nconst STEAM_ALPHA = 0.24 / 0.35\nconst STEAM_RISE = 0.3\n","[\n {\n \"id\": \"water\",\n \"color\": 11064558,\n \"opacity\": 0.35,\n \"stainColor\": 7309206,\n \"viscosity\": 1,\n \"density\": 1000,\n \"dryTime\": 180,\n \"gloss\": 0.9,\n \"drink\": {\n \"thirst\": 100,\n \"effects\": []\n },\n \"tags\": [\n \"drink\",\n \"clear\"\n ]\n },\n {\n \"id\": \"coffee\",\n \"color\": 3875604,\n \"opacity\": 0.95,\n \"stainColor\": 2824202,\n \"viscosity\": 1.5,\n \"density\": 1005,\n \"dryTime\": 900,\n \"gloss\": 0.8,\n \"leavesMark\": true,\n \"steamC\": 60,\n \"drink\": {\n \"thirst\": 80,\n \"effects\": [\n \"caffeine\"\n ]\n },\n \"tags\": [\n \"drink\",\n \"hot\"\n ]\n },\n {\n \"id\": \"tea\",\n \"color\": 9063188,\n \"opacity\": 0.8,\n \"stainColor\": 6961682,\n \"viscosity\": 1.4,\n \"density\": 1005,\n \"dryTime\": 800,\n \"gloss\": 0.8,\n \"leavesMark\": true,\n \"steamC\": 60,\n \"drink\": {\n \"thirst\": 90,\n \"effects\": []\n },\n \"tags\": [\n \"drink\",\n \"hot\"\n ]\n },\n {\n \"id\": \"milk\",\n \"color\": 16052712,\n \"opacity\": 0.98,\n \"stainColor\": 14473160,\n \"viscosity\": 2,\n \"density\": 1030,\n \"dryTime\": 900,\n \"gloss\": 0.5,\n \"leavesMark\": true,\n \"drink\": {\n \"thirst\": 70,\n \"effects\": []\n },\n \"tags\": [\n \"drink\"\n ]\n },\n {\n \"id\": \"soda\",\n \"color\": 3808530,\n \"opacity\": 0.85,\n \"stainColor\": 2757901,\n \"viscosity\": 1.3,\n \"density\": 1040,\n \"dryTime\": 1200,\n \"gloss\": 0.85,\n \"leavesMark\": true,\n \"drink\": {\n \"thirst\": 60,\n \"effects\": []\n },\n \"tags\": [\n \"drink\",\n \"sticky\"\n ]\n },\n {\n \"id\": \"juice\",\n \"color\": 15239467,\n \"opacity\": 0.9,\n \"stainColor\": 12741916,\n \"viscosity\": 2,\n \"density\": 1045,\n \"dryTime\": 1100,\n \"gloss\": 0.8,\n \"leavesMark\": true,\n \"drink\": {\n \"thirst\": 75,\n \"effects\": []\n },\n \"tags\": [\n \"drink\",\n \"sticky\"\n ]\n },\n {\n \"id\": \"beer\",\n \"color\": 14259740,\n \"opacity\": 0.8,\n \"stainColor\": 11039764,\n \"viscosity\": 1.4,\n \"density\": 1010,\n \"dryTime\": 900,\n \"gloss\": 0.8,\n \"drink\": {\n \"thirst\": 50,\n \"effects\": [\n \"alcohol\"\n ]\n },\n \"tags\": [\n \"drink\",\n \"alcohol\"\n ]\n },\n {\n \"id\": \"wine\",\n \"color\": 7016223,\n \"opacity\": 0.9,\n \"stainColor\": 5048854,\n \"viscosity\": 1.6,\n \"density\": 990,\n \"dryTime\": 800,\n \"gloss\": 0.85,\n \"leavesMark\": true,\n \"drink\": {\n \"thirst\": 40,\n \"effects\": [\n \"alcohol\"\n ]\n },\n \"tags\": [\n \"drink\",\n \"alcohol\"\n ]\n },\n {\n \"id\": \"spirits\",\n \"color\": 13214042,\n \"opacity\": 0.6,\n \"stainColor\": 10320959,\n \"viscosity\": 1.8,\n \"density\": 940,\n \"dryTime\": 200,\n \"gloss\": 0.85,\n \"flammable\": true,\n \"drink\": {\n \"thirst\": 5,\n \"effects\": [\n \"alcohol\"\n ]\n },\n \"tags\": [\n \"drink\",\n \"alcohol\"\n ]\n },\n {\n \"id\": \"fuel\",\n \"color\": 14926682,\n \"opacity\": 0.6,\n \"stainColor\": 7232298,\n \"viscosity\": 0.6,\n \"density\": 740,\n \"dryTime\": 60,\n \"gloss\": 0.95,\n \"leavesMark\": false,\n \"flammable\": true,\n \"drink\": {\n \"thirst\": 0,\n \"effects\": [\n \"poison\"\n ]\n },\n \"tags\": [\n \"fuel\",\n \"gasoline\"\n ]\n },\n {\n \"id\": \"diesel\",\n \"color\": 12098094,\n \"opacity\": 0.8,\n \"stainColor\": 5917208,\n \"viscosity\": 3,\n \"density\": 830,\n \"dryTime\": 3600,\n \"gloss\": 0.95,\n \"leavesMark\": true,\n \"flammable\": true,\n \"drink\": {\n \"thirst\": 0,\n \"effects\": [\n \"poison\"\n ]\n },\n \"tags\": [\n \"fuel\"\n ]\n },\n {\n \"id\": \"motor_oil\",\n \"color\": 3877388,\n \"opacity\": 0.95,\n \"stainColor\": 1840134,\n \"viscosity\": 100,\n \"density\": 880,\n \"dryTime\": 0,\n \"gloss\": 1,\n \"leavesMark\": true,\n \"flammable\": true,\n \"drink\": {\n \"thirst\": 0,\n \"effects\": [\n \"poison\"\n ]\n },\n \"tags\": [\n \"oil\"\n ]\n },\n {\n \"id\": \"cooking_oil\",\n \"color\": 14267708,\n \"opacity\": 0.85,\n \"stainColor\": 11110698,\n \"viscosity\": 60,\n \"density\": 920,\n \"dryTime\": 0,\n \"gloss\": 1,\n \"leavesMark\": true,\n \"flammable\": true,\n \"drink\": {\n \"thirst\": 0,\n \"effects\": [\n \"poison\"\n ]\n },\n \"tags\": [\n \"oil\",\n \"food\"\n ]\n },\n {\n \"id\": \"blood\",\n \"color\": 7997964,\n \"opacity\": 0.92,\n \"stainColor\": 3874068,\n \"viscosity\": 4,\n \"density\": 1060,\n \"dryTime\": 600,\n \"gloss\": 0.75,\n \"leavesMark\": true,\n \"drink\": {\n \"thirst\": 10,\n \"effects\": [\n \"disgust\"\n ]\n },\n \"tags\": [\n \"organic\"\n ]\n },\n {\n \"id\": \"old_blood\",\n \"color\": 7085078,\n \"opacity\": 0.92,\n \"stainColor\": 2758158,\n \"viscosity\": 4,\n \"density\": 1060,\n \"dryTime\": 360,\n \"gloss\": 0.7,\n \"leavesMark\": true,\n \"drink\": {\n \"thirst\": 10,\n \"effects\": [\n \"disgust\"\n ]\n },\n \"tags\": [\n \"organic\"\n ]\n },\n {\n \"id\": \"bile\",\n \"color\": 9083434,\n \"opacity\": 0.9,\n \"stainColor\": 4080160,\n \"viscosity\": 6,\n \"density\": 1020,\n \"dryTime\": 900,\n \"gloss\": 0.75,\n \"leavesMark\": true,\n \"drink\": {\n \"thirst\": 0,\n \"effects\": [\n \"disgust\"\n ]\n },\n \"tags\": [\n \"organic\"\n ]\n },\n {\n \"id\": \"sap\",\n \"color\": 13146940,\n \"opacity\": 0.8,\n \"stainColor\": 10122282,\n \"viscosity\": 200,\n \"density\": 1100,\n \"dryTime\": 3600,\n \"gloss\": 0.8,\n \"leavesMark\": true,\n \"drink\": {\n \"thirst\": 20,\n \"effects\": []\n },\n \"tags\": [\n \"organic\",\n \"sticky\"\n ]\n },\n {\n \"id\": \"acid\",\n \"color\": 10146378,\n \"opacity\": 0.6,\n \"stainColor\": 7313970,\n \"viscosity\": 1.5,\n \"density\": 1200,\n \"dryTime\": 900,\n \"gloss\": 0.9,\n \"leavesMark\": true,\n \"drink\": {\n \"thirst\": 0,\n \"effects\": [\n \"poison\"\n ]\n },\n \"tags\": [\n \"corrosive\"\n ]\n },\n {\n \"id\": \"paint\",\n \"color\": 11740715,\n \"opacity\": 0.98,\n \"stainColor\": 9050655,\n \"viscosity\": 300,\n \"density\": 1300,\n \"dryTime\": 1800,\n \"gloss\": 0.6,\n \"leavesMark\": true,\n \"drink\": {\n \"thirst\": 0,\n \"effects\": [\n \"poison\"\n ]\n },\n \"tags\": [\n \"paint\"\n ]\n },\n {\n \"id\": \"bleach\",\n \"color\": 15134688,\n \"opacity\": 0.7,\n \"stainColor\": 13621446,\n \"viscosity\": 1.2,\n \"density\": 1100,\n \"dryTime\": 900,\n \"gloss\": 0.85,\n \"leavesMark\": true,\n \"drink\": {\n \"thirst\": 0,\n \"effects\": [\n \"poison\"\n ]\n },\n \"tags\": [\n \"corrosive\"\n ]\n },\n {\n \"id\": \"honey\",\n \"color\": 14193162,\n \"opacity\": 0.9,\n \"stainColor\": 12089350,\n \"viscosity\": 3000,\n \"density\": 1420,\n \"dryTime\": 14400,\n \"gloss\": 0.95,\n \"leavesMark\": true,\n \"drink\": {\n \"thirst\": 10,\n \"effects\": []\n },\n \"tags\": [\n \"drink\",\n \"sticky\"\n ]\n }\n]","import liquidsData from './data/liquids.json'\nimport type { LiquidDef } from './types'\n\n/**\n * Built-in liquids (data in `data/liquids.json`; colours are `0xRRGGBB` stored as integers).\n * Viscosity is relative to water, density in kg/m³, `dryTime` in seconds for a thin film at 20 °C (0 = never dries).\n */\nexport const LIQUIDS: LiquidDef[] = liquidsData as unknown as LiquidDef[]\n","import type { Quat, Vec3 } from './types'\n\nexport const GRAVITY = 9.81\nexport const EPS = 1e-7\n\nexport const clamp = (v: number, lo: number, hi: number): number => (v < lo ? lo : v > hi ? hi : v)\nexport const lerp = (a: number, b: number, t: number): number => a + (b - a) * t\n\nexport const add = (a: Vec3, b: Vec3): Vec3 => [a[0] + b[0], a[1] + b[1], a[2] + b[2]]\nexport const sub = (a: Vec3, b: Vec3): Vec3 => [a[0] - b[0], a[1] - b[1], a[2] - b[2]]\nexport const scale = (a: Vec3, s: number): Vec3 => [a[0] * s, a[1] * s, a[2] * s]\nexport const dot = (a: Vec3, b: Vec3): number => a[0] * b[0] + a[1] * b[1] + a[2] * b[2]\nexport const cross = (a: Vec3, b: Vec3): Vec3 => [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]]\nexport const length = (a: Vec3): number => Math.hypot(a[0], a[1], a[2])\nexport const distance = (a: Vec3, b: Vec3): number => Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2])\nexport const normalize = (a: Vec3, fallback: Vec3 = [0, 1, 0]): Vec3 => {\n const l = length(a)\n return l < 1e-9 ? [fallback[0], fallback[1], fallback[2]] : [a[0] / l, a[1] / l, a[2] / l]\n}\nexport const copy3 = (a: Vec3): Vec3 => [a[0], a[1], a[2]]\nexport const lerp3 = (a: Vec3, b: Vec3, t: number): Vec3 => [lerp(a[0], b[0], t), lerp(a[1], b[1], t), lerp(a[2], b[2], t)]\n\n/** Rotate `v` by the unit quaternion `q` (x, y, z, w). */\nexport const quatRotate = (q: Quat, v: Vec3): Vec3 => {\n const u: Vec3 = [q[0], q[1], q[2]]\n const c1 = cross(u, v)\n const c2 = cross(u, c1)\n return [v[0] + 2 * (q[3] * c1[0] + c2[0]), v[1] + 2 * (q[3] * c1[1] + c2[1]), v[2] + 2 * (q[3] * c1[2] + c2[2])]\n}\nexport const quatConjugate = (q: Quat): Quat => [-q[0], -q[1], -q[2], q[3]]\nexport const quatMultiply = (a: Quat, b: Quat): Quat => [\n a[3] * b[0] + a[0] * b[3] + a[1] * b[2] - a[2] * b[1],\n a[3] * b[1] - a[0] * b[2] + a[1] * b[3] + a[2] * b[0],\n a[3] * b[2] + a[0] * b[1] - a[1] * b[0] + a[2] * b[3],\n a[3] * b[3] - a[0] * b[0] - a[1] * b[1] - a[2] * b[2],\n]\nexport const quatNormalize = (q: Quat): Quat => {\n const l = Math.hypot(q[0], q[1], q[2], q[3])\n return l < 1e-9 ? [0, 0, 0, 1] : [q[0] / l, q[1] / l, q[2] / l, q[3] / l]\n}\n/** Quaternion from Euler angles in XYZ order (the same convention as three.js). */\nexport const quatFromEuler = (x: number, y: number, z: number): Quat => {\n const c1 = Math.cos(x / 2)\n const c2 = Math.cos(y / 2)\n const c3 = Math.cos(z / 2)\n const s1 = Math.sin(x / 2)\n const s2 = Math.sin(y / 2)\n const s3 = Math.sin(z / 2)\n return [s1 * c2 * c3 + c1 * s2 * s3, c1 * s2 * c3 - s1 * c2 * s3, c1 * c2 * s3 + s1 * s2 * c3, c1 * c2 * c3 - s1 * s2 * s3]\n}\n\n/** Small seeded generator (mulberry32). Its whole state is one integer, so it serialises trivially. */\nexport class Rng {\n state: number\n constructor(seed: number) {\n this.state = (Math.imul((seed | 0) ^ 0x9e3779b9, 0x85ebca6b) >>> 0) || 1\n }\n next(): number {\n this.state = (this.state + 0x6d2b79f5) >>> 0\n let t = this.state\n t = Math.imul(t ^ (t >>> 15), t | 1)\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61)\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296\n }\n}\n","import type { StreamState, Vec3 } from './types'\nimport { dot, sub } from './math'\nimport type { Basis } from './stains'\n\nexport const GROUND_ID = 'ground'\n/** Longest flight simulated for a stream before it counts as landing nowhere. */\nexport const MAX_FLIGHT = 3\n\n/** Position of a ballistic jet `t` seconds after leaving its origin. */\nexport const ballistic = (origin: Vec3, velocity: Vec3, gravity: number, t: number): Vec3 => [\n origin[0] + velocity[0] * t,\n origin[1] + velocity[1] * t - 0.5 * gravity * t * t,\n origin[2] + velocity[2] * t,\n]\n\n/** Point of a stream's path `t` seconds after its origin (use with `StreamState.flightTime` and the world's `gravity`). */\nexport const sampleStream = (stream: Pick<StreamState, 'origin' | 'velocity'>, gravity: number, t: number): Vec3 =>\n ballistic(stream.origin, stream.velocity, gravity, t)\n\nexport interface LandingSurface extends Basis {\n id: string\n point: Vec3\n half: [number, number]\n}\n\nexport interface Hit {\n t: number\n point: Vec3\n normal: Vec3\n surface: string\n /** Velocity of the jet at impact. */\n velocity: Vec3\n}\n\nconst velocityAt = (velocity: Vec3, gravity: number, t: number): Vec3 => [velocity[0], velocity[1] - gravity * t, velocity[2]]\n\n/** First thing a jet hits: an authored surface (plane with half extents) or else the ground. */\nexport const trace = (\n surfaces: Iterable<LandingSurface>,\n groundAt: (x: number, z: number) => number,\n origin: Vec3,\n velocity: Vec3,\n gravity: number,\n): Hit | null => {\n let best: Hit | null = null\n for (const s of surfaces) {\n const n = s.normal\n const c = dot(n, sub(origin, s.point))\n if (c < -0.005) continue\n const a = -0.5 * gravity * n[1]\n const b = dot(n, velocity)\n let t = Infinity\n if (Math.abs(a) < 1e-9) {\n if (b < -1e-9) t = -c / b\n } else {\n const disc = b * b - 4 * a * c\n if (disc >= 0) {\n const sq = Math.sqrt(disc)\n for (const root of [(-b - sq) / (2 * a), (-b + sq) / (2 * a)]) {\n if (root >= -1e-6 && b + 2 * a * root < 0 && root < t) t = root\n }\n }\n }\n if (!Number.isFinite(t) || t > MAX_FLIGHT) continue\n t = Math.max(0, t)\n if (best && t >= best.t) continue\n const p = ballistic(origin, velocity, gravity, t)\n const rel = sub(p, s.point)\n if (Math.abs(dot(rel, s.t1)) > s.half[0] || Math.abs(dot(rel, s.t2)) > s.half[1]) continue\n best = { t, point: p, normal: n, surface: s.id, velocity: velocityAt(velocity, gravity, t) }\n }\n\n // Ground: march the path and refine the crossing.\n const step = 0.02\n let prevT = 0\n const startOver = origin[1] - groundAt(origin[0], origin[2])\n const limit = best ? best.t : MAX_FLIGHT\n if (startOver <= 0) {\n return best && best.t <= 0 ? best : { t: 0, point: [origin[0], groundAt(origin[0], origin[2]), origin[2]], normal: [0, 1, 0], surface: GROUND_ID, velocity }\n }\n for (let t = step; t <= limit + step; t += step) {\n const tt = Math.min(t, limit)\n const p = ballistic(origin, velocity, gravity, tt)\n const over = p[1] - groundAt(p[0], p[2])\n if (over <= 0) {\n let lo = prevT\n let hi = tt\n for (let i = 0; i < 18; i++) {\n const mid = (lo + hi) / 2\n const q = ballistic(origin, velocity, gravity, mid)\n if (q[1] - groundAt(q[0], q[2]) > 0) lo = mid\n else hi = mid\n }\n if (best && hi >= best.t - 1e-3) return best\n const q = ballistic(origin, velocity, gravity, hi)\n return { t: hi, point: [q[0], groundAt(q[0], q[2]), q[2]], normal: [0, 1, 0], surface: GROUND_ID, velocity: velocityAt(velocity, gravity, hi) }\n }\n prevT = tt\n if (tt >= limit) break\n }\n return best\n}\n","import type { FluidEvent, LiquidId } from './types'\n\n/** Locales shipped with the package. Unknown locales fall back to `en`. */\nexport type FluidLocale = 'en' | 'pt-BR'\n\nexport interface FluidStrings {\n /** Display name per liquid id (unknown ids fall back to the id itself). */\n liquids: Record<LiquidId, string>\n /** Short label per event type. */\n events: Record<FluidEvent['type'], string>\n on: string\n off: string\n}\n\nexport const locales: Record<FluidLocale, FluidStrings> = {\n en: {\n liquids: {\n water: 'water',\n coffee: 'coffee',\n tea: 'tea',\n milk: 'milk',\n soda: 'soda',\n juice: 'juice',\n beer: 'beer',\n wine: 'wine',\n spirits: 'spirits',\n fuel: 'gasoline',\n diesel: 'diesel',\n motor_oil: 'motor oil',\n cooking_oil: 'cooking oil',\n blood: 'blood',\n old_blood: 'old blood',\n bile: 'bile',\n sap: 'sap',\n acid: 'acid',\n paint: 'paint',\n bleach: 'bleach',\n honey: 'honey',\n },\n events: {\n fill: 'filled',\n pour: 'poured',\n spill: 'spilled',\n leak: 'leaking',\n drink: 'drank',\n empty: 'emptied',\n stream_end: 'stream ended',\n splash: 'splash',\n stain: 'stain',\n dried: 'dried',\n steam: 'steam',\n mix: 'mixed',\n tap: 'tap',\n },\n on: 'on',\n off: 'off',\n },\n 'pt-BR': {\n liquids: {\n water: 'água',\n coffee: 'café',\n tea: 'chá',\n milk: 'leite',\n soda: 'refrigerante',\n juice: 'suco',\n beer: 'cerveja',\n wine: 'vinho',\n spirits: 'destilado',\n fuel: 'gasolina',\n diesel: 'diesel',\n motor_oil: 'óleo de motor',\n cooking_oil: 'óleo de cozinha',\n blood: 'sangue',\n old_blood: 'sangue antigo',\n bile: 'bile',\n sap: 'seiva',\n acid: 'ácido',\n paint: 'tinta',\n bleach: 'água sanitária',\n honey: 'mel',\n },\n events: {\n fill: 'encheu',\n pour: 'despejou',\n spill: 'derramou',\n leak: 'vazando',\n drink: 'bebeu',\n empty: 'esvaziou',\n stream_end: 'parou de escorrer',\n splash: 'respingo',\n stain: 'mancha',\n dried: 'secou',\n steam: 'vapor',\n mix: 'misturou',\n tap: 'torneira',\n },\n on: 'ligado',\n off: 'desligado',\n },\n}\n\nexport const resolveStrings = (locale: string | undefined): FluidStrings => locales[(locale as FluidLocale) ?? 'en'] ?? locales.en\n\nconst fmt = (n: number): string => `${(Math.round(n * 1000) / 1000).toString()} l`\nconst pt = (p: [number, number, number]): string => `(${p.map((v) => Math.round(v * 100) / 100).join(', ')})`\n\n/** One-line description of an event for logs and debug panels, e.g. `drank: coffee 0.03 l`. */\nexport const describeEvent = (strings: FluidStrings, event: FluidEvent): string => {\n const label = strings.events[event.type]\n const name = (id: LiquidId) => strings.liquids[id] ?? id\n switch (event.type) {\n case 'fill':\n return `${label}: ${event.container} ${name(event.liquid)} ${fmt(event.liters)}`\n case 'pour':\n case 'spill':\n return `${label}: ${event.container} ${name(event.liquid)} ${fmt(event.rate)}/s`\n case 'leak':\n return `${label}: ${event.container} ${name(event.liquid)} ${fmt(event.rate)}/s ${pt(event.point)}`\n case 'drink':\n return `${label}: ${event.container} ${name(event.liquid)} ${fmt(event.liters)}${event.effects.length ? ` [${event.effects.join(', ')}]` : ''}${event.burn ? ' !' : ''}`\n case 'empty':\n return `${label}: ${event.container}`\n case 'stream_end':\n return `${label}: ${event.stream}`\n case 'splash':\n return `${label}: ${name(event.liquid)} ${pt(event.point)}`\n case 'stain':\n return `${label}: ${event.stain} ${name(event.liquid)} ${event.surface} ${pt(event.point)}`\n case 'dried':\n return `${label}: ${event.stain}${event.mark ? ' (mark)' : ''}`\n case 'steam':\n return `${label}: ${event.container} ${event.on ? strings.on : strings.off}`\n case 'mix':\n return `${label}: ${event.container} ${name(event.from)} -> ${name(event.into)}`\n case 'tap':\n return `${label}: ${event.tap} ${event.on ? strings.on : strings.off}`\n }\n}\n","import type { ContainerSpec, Vec3 } from './types'\nimport { clamp, normalize } from './math'\n\nexport type Shape = ContainerSpec['shape']\n\n/** Sample columns of a container's inner volume: equal-weight (x, z) positions with the full height `h`. */\nexport interface Columns {\n x: Float64Array\n z: Float64Array\n h: number\n /** Radius (cylinder) or half extents (box) used for the extent of a plane. */\n kind: 'cylinder' | 'box'\n r: number\n hx: number\n hz: number\n}\n\nconst RINGS = 8\nconst SEGMENTS = 16\nconst GRID = 12\n\nexport const makeColumns = (shape: Shape): Columns => {\n if (shape.kind === 'cylinder') {\n const x = new Float64Array(RINGS * SEGMENTS)\n const z = new Float64Array(RINGS * SEGMENTS)\n let n = 0\n for (let k = 0; k < RINGS; k++) {\n const rr = shape.radius * Math.sqrt((k + 0.5) / RINGS)\n for (let m = 0; m < SEGMENTS; m++) {\n // Offset every other ring so the samples do not line up radially.\n const a = ((m + (k % 2) * 0.5) / SEGMENTS) * Math.PI * 2\n x[n] = Math.cos(a) * rr\n z[n] = Math.sin(a) * rr\n n++\n }\n }\n return { x, z, h: shape.height, kind: 'cylinder', r: shape.radius, hx: shape.radius, hz: shape.radius }\n }\n const [sx, sy, sz] = shape.size\n const x = new Float64Array(GRID * GRID)\n const z = new Float64Array(GRID * GRID)\n let n = 0\n for (let i = 0; i < GRID; i++) {\n for (let j = 0; j < GRID; j++) {\n x[n] = ((i + 0.5) / GRID - 0.5) * sx\n z[n] = ((j + 0.5) / GRID - 0.5) * sz\n n++\n }\n }\n return { x, z, h: sy, kind: 'box', r: Math.hypot(sx, sz) / 2, hx: sx / 2, hz: sz / 2 }\n}\n\n/** Range of `u · p` over the container: below `lo` it is empty, above `hi` it is full. */\nexport const planeRange = (c: Columns, u: Vec3): [number, number] => {\n const horizontal = c.kind === 'cylinder' ? c.r * Math.hypot(u[0], u[2]) : Math.abs(u[0]) * c.hx + Math.abs(u[2]) * c.hz\n const top = u[1] * c.h\n return [-horizontal + Math.min(0, top), horizontal + Math.max(0, top)]\n}\n\n/** Fraction (0..1) of the inner volume with `u · p <= d` (the liquid for a surface plane with normal `u` at offset `d`). */\nexport const planeFraction = (c: Columns, u: Vec3, d: number): number => {\n const uy = u[1]\n let total = 0\n const n = c.x.length\n for (let i = 0; i < n; i++) {\n const s0 = u[0] * c.x[i] + u[2] * c.z[i]\n let len: number\n if (uy > 1e-6) len = clamp((d - s0) / uy, 0, c.h)\n else if (uy < -1e-6) len = c.h - clamp((d - s0) / uy, 0, c.h)\n else len = s0 <= d ? c.h : 0\n total += len\n }\n return total / (n * c.h)\n}\n\n/** Plane offset `d` along unit normal `u` for which the liquid takes the volume fraction `f`. */\nexport const levelFor = (c: Columns, u: Vec3, f: number): number => {\n const [lo0, hi0] = planeRange(c, u)\n if (f <= 0) return lo0\n if (f >= 1) return hi0\n if (Math.abs(u[0]) + Math.abs(u[2]) < 1e-9) {\n return u[1] > 0 ? f * c.h * u[1] : u[1] * c.h - f * c.h * u[1]\n }\n let lo = lo0\n let hi = hi0\n for (let i = 0; i < 28; i++) {\n const mid = (lo + hi) / 2\n if (planeFraction(c, u, mid) < f) lo = mid\n else hi = mid\n }\n return (lo + hi) / 2\n}\n\n/** Radius of the lip circle (the opening); a closed spec (`opening` 0) uses half the container width once opened. */\nexport const lipRadius = (shape: Shape, opening: number): number => {\n const limit = shape.kind === 'cylinder' ? shape.radius : Math.min(shape.size[0], shape.size[2]) / 2\n return opening > 0 ? Math.min(opening, limit) : limit * 0.5\n}\n\nexport const shapeHeight = (shape: Shape): number => (shape.kind === 'cylinder' ? shape.height : shape.size[1])\n\nexport interface Lip {\n /** Lowest lip point along the surface normal, container space. */\n point: Vec3\n /** Centre of the lip circle, container space. */\n center: Vec3\n /** Outward direction (container space, horizontal in the container frame) the liquid leaves along, or null when upright. */\n outward: Vec3 | null\n /** `u · point`: the surface offset above which the liquid spills. */\n level: number\n}\n\nexport const lipOf = (shape: Shape, opening: number, u: Vec3): Lip => {\n const h = shapeHeight(shape)\n const ro = lipRadius(shape, opening)\n const center: Vec3 = [0, h, 0]\n const uh = Math.hypot(u[0], u[2])\n if (uh < 1e-6) return { point: center, center, outward: null, level: u[1] * h }\n const out: Vec3 = normalize([-u[0], 0, -u[2]])\n const point: Vec3 = [out[0] * ro, h, out[2] * ro]\n return { point, center, outward: out, level: u[0] * point[0] + u[1] * point[1] + u[2] * point[2] }\n}\n","import type { LiquidDef, StainState, Vec3 } from './types'\nimport { Rng, add, clamp, cross, dot, length, normalize, scale, sub } from './math'\n\n/** Number of radial samples of the flow front. */\nexport const SAMPLES = 16\n\nconst COS: number[] = []\nconst SIN: number[] = []\nfor (let i = 0; i < SAMPLES; i++) {\n COS.push(Math.cos((i / SAMPLES) * Math.PI * 2))\n SIN.push(Math.sin((i / SAMPLES) * Math.PI * 2))\n}\n\n/** Orthonormal frame of a surface: `normal`, `t1` (tangent) and `t2 = normal × t1`. */\nexport interface Basis {\n normal: Vec3\n t1: Vec3\n t2: Vec3\n}\n\nexport const makeBasis = (normalIn: Vec3, tangentIn?: Vec3): Basis => {\n const normal = normalize(normalIn)\n const ref: Vec3 = tangentIn && length(tangentIn) > 1e-6 ? tangentIn : Math.abs(normal[1]) > 0.9 ? [1, 0, 0] : [0, 1, 0]\n const t1 = normalize(sub(ref, scale(normal, dot(ref, normal))), [1, 0, 0])\n return { normal, t1, t2: cross(normal, t1) }\n}\n\n/** What a stain needs to know about the surface it lies on. */\nexport interface StainSurface extends Basis {\n point: Vec3\n half: [number, number]\n porosity: number\n}\n\nexport interface StainInt extends StainState {\n normal: Vec3\n tangent: Vec3\n noise: number[]\n /** Seconds since the last inflow. */\n sinceInflow: number\n /** Drying progress 0..1. */\n dry: number\n mark: boolean\n age: number\n}\n\n/** Direction (world space) of radial sample `i` on a surface frame. */\nexport const sampleDir = (b: Basis, i: number): Vec3 => [\n b.t1[0] * COS[i] + b.t2[0] * SIN[i],\n b.t1[1] * COS[i] + b.t2[1] * SIN[i],\n b.t1[2] * COS[i] + b.t2[2] * SIN[i],\n]\n\nexport const stainArea = (front: number[]): number => {\n let a = 0\n for (let i = 0; i < SAMPLES; i++) a += front[i] * front[(i + 1) % SAMPLES]\n return 0.5 * Math.sin((Math.PI * 2) / SAMPLES) * a\n}\n\nexport const meanRadius = (front: number[]): number => front.reduce((s, v) => s + v, 0) / front.length\n\n/** Thickness (m) a puddle of this liquid cannot get thinner than: 1 mm for water, thicker for viscous liquids. */\nexport const minFilm = (def: LiquidDef): number => 0.001 * (1 + 0.8 * Math.log10(Math.max(1, def.viscosity)))\n\n/** Per-direction growth multipliers: low harmonics plus smoothed seeded noise, so the outline is never a disc. */\nexport const makeNoise = (rng: Rng): number[] => {\n const amp = [0.12 + rng.next() * 0.18, 0.08 + rng.next() * 0.14, 0.05 + rng.next() * 0.08]\n const phase = [rng.next() * Math.PI * 2, rng.next() * Math.PI * 2, rng.next() * Math.PI * 2]\n const raw: number[] = []\n for (let i = 0; i < SAMPLES; i++) {\n const a = (i / SAMPLES) * Math.PI * 2\n let v = 1 + (rng.next() - 0.5) * 0.3\n for (let k = 0; k < 3; k++) v += amp[k] * Math.sin((k + 2) * a + phase[k])\n raw.push(v)\n }\n const out = raw.map((v, i) => 0.25 * raw[(i + SAMPLES - 1) % SAMPLES] + 0.5 * v + 0.25 * raw[(i + 1) % SAMPLES])\n return out.map((v) => clamp(v, 0.45, 1.7))\n}\n\n/** Horizontal run direction on a surface (downhill, unit, in the plane) and the slope strength 0..1 (sin of the angle). */\nexport const downhill = (normal: Vec3): { dir: Vec3; slope: number } => {\n const d = add([0, -1, 0], scale(normal, normal[1]))\n const slope = length(d)\n return slope < 1e-6 ? { dir: [0, 0, 0], slope: 0 } : { dir: scale(d, 1 / slope), slope }\n}\n\n/** Creates a stain at `center` (on the surface) with a small starting front. */\nexport const createStain = (\n id: string,\n surfaceId: string,\n def: LiquidDef,\n basis: Basis,\n center: Vec3,\n liters: number,\n rng: Rng,\n): StainInt => {\n const r0 = clamp(Math.sqrt(liters / 1000 / minFilm(def) / Math.PI) * 0.4, 0.012, 0.12)\n return {\n id,\n surface: surfaceId,\n liquid: def.id,\n center: [center[0], center[1], center[2]],\n front: new Array(SAMPLES).fill(r0),\n liters,\n soaked: 0,\n wetness: 1,\n wetRadius: 1,\n normal: basis.normal,\n tangent: basis.t1,\n noise: makeNoise(rng),\n sinceInflow: 0,\n dry: 0,\n mark: false,\n age: 0,\n }\n}\n\n/** Adds liquid to a stain (rewets a dried mark). `velocity` makes the front stretch the way the liquid came in. */\nexport const addInflow = (s: StainInt, liters: number, velocity: Vec3 | null): void => {\n s.liters += liters\n s.sinceInflow = 0\n if (s.dry > 0 || s.mark) {\n s.dry = 0\n s.mark = false\n s.wetness = 1\n s.wetRadius = 1\n }\n if (velocity) {\n const n = s.normal\n const tangential = sub(velocity, scale(n, dot(velocity, n)))\n if (length(tangential) > 0.2) s.flow = normalize(tangential)\n }\n}\n\n/** Moves the centre towards an inflow point without moving the outline. */\nexport const nudgeCenter = (s: StainInt, point: Vec3, weight: number, basis: Basis): void => {\n const delta = scale(sub(point, s.center), weight)\n if (length(delta) < 1e-6) return\n s.center = add(s.center, delta)\n for (let i = 0; i < SAMPLES; i++) s.front[i] = Math.max(0.008, s.front[i] - dot(delta, sampleDir(basis, i)))\n}\n\nexport type StainStatus = 'alive' | 'dried_mark' | 'dried_gone'\n\n/** Largest radius along each sample direction before the centre's surface rectangle ends. */\nconst edgeLimit = (s: StainInt, surface: StainSurface, i: number): number => {\n if (!Number.isFinite(surface.half[0]) && !Number.isFinite(surface.half[1])) return Infinity\n const rel = sub(s.center, surface.point)\n const cx = dot(rel, surface.t1)\n const cy = dot(rel, surface.t2)\n let limit = Infinity\n if (COS[i] > 1e-6) limit = Math.min(limit, (surface.half[0] - cx) / COS[i])\n else if (COS[i] < -1e-6) limit = Math.min(limit, (-surface.half[0] - cx) / COS[i])\n if (SIN[i] > 1e-6) limit = Math.min(limit, (surface.half[1] - cy) / SIN[i])\n else if (SIN[i] < -1e-6) limit = Math.min(limit, (-surface.half[1] - cy) / SIN[i])\n return Math.max(0.01, limit)\n}\n\n/**\n * Advances a stain: the front grows (faster downhill, with seeded noise, tapering as the area approaches what the liquid can cover at its\n * minimum film thickness), porous surfaces soak the pool, and once the inflow stopped the stain dries from the edge in.\n */\nexport const stepStain = (s: StainInt, def: LiquidDef, surface: StainSurface, ambientC: number, dt: number): StainStatus => {\n s.age += dt\n s.sinceInflow += dt\n const basis: Basis = surface\n const { dir: down, slope } = downhill(s.normal)\n\n // Soak into porous surfaces.\n if (s.liters > 0 && surface.porosity > 0) {\n const soak = Math.min(s.liters, surface.porosity * 0.2 * stainArea(s.front) * dt)\n s.liters -= soak\n s.soaked += soak\n }\n\n // Grow the flow front.\n if (s.liters > 1e-7 && s.wetness > 0) {\n const area = stainArea(s.front)\n const cover = s.liters / 1000 / (minFilm(def) * (1 - 0.7 * slope))\n if (area < cover) {\n const taper = clamp(1 - area / cover, 0, 1)\n const v0 = (0.02 + 0.2 * Math.sqrt(s.liters)) / Math.sqrt(Math.max(1, def.viscosity))\n const delta: number[] = []\n for (let i = 0; i < SAMPLES; i++) {\n let m = s.noise[i]\n if (s.flow) m *= 1 + 0.5 * Math.max(0, dot(sampleDir(basis, i), s.flow))\n if (slope > 0.02) {\n const a = dot(sampleDir(basis, i), down)\n m *= a > 0 ? 1 + 14 * slope * a : Math.max(0.1, 1 + 2.5 * slope * a)\n }\n delta.push(Math.max(0, Math.min(s.front[i] + v0 * m * taper * dt, edgeLimit(s, surface, i)) - s.front[i]))\n }\n // Never overshoot the area the liquid can cover: scale the growth (not the outline) down to fit.\n let lambda = 1\n const apply = (k: number) => s.front.map((r, i) => r + delta[i] * k)\n if (stainArea(apply(1)) > cover) {\n let lo = 0\n let hi = 1\n for (let i = 0; i < 14; i++) {\n const mid = (lo + hi) / 2\n if (stainArea(apply(mid)) > cover) hi = mid\n else lo = mid\n }\n lambda = lo\n }\n s.front = apply(lambda)\n }\n }\n\n // Dry from the edge in once the inflow stopped.\n if (s.sinceInflow > 1 && def.dryTime > 0 && s.dry < 1) {\n const warm = Math.pow(2, (ambientC - 20) / 12)\n const ds = (dt * warm * (1 + 0.5 * surface.porosity)) / def.dryTime\n const before = s.dry\n s.dry = Math.min(1, s.dry + ds)\n if (before < 1) s.liters *= 1 - (s.dry - before) / (1 - before)\n s.wetness = 1 - s.dry\n s.wetRadius = Math.sqrt(Math.max(0, 1 - s.dry))\n if (s.dry >= 1) {\n s.liters = 0\n s.wetness = 0\n s.wetRadius = 0\n if (def.leavesMark) {\n s.mark = true\n return 'dried_mark'\n }\n return 'dried_gone'\n }\n }\n return 'alive'\n}\n\n/** Unit direction (world space) of front sample `i` of a stain, from its `normal` and `tangent`. */\nexport const stainDirection = (s: Pick<StainState, 'normal' | 'tangent'>, i: number): Vec3 => sampleDir(makeBasis(s.normal ?? [0, 1, 0], s.tangent), i)\n","import { LIQUIDS } from './liquids'\nimport { GROUND_ID, MAX_FLIGHT, ballistic, trace } from './landing'\nimport type { Hit, LandingSurface } from './landing'\nimport { describeEvent, resolveStrings } from './locale'\nimport { GRAVITY, Rng, add, clamp, copy3, cross, distance, dot, length, normalize, quatConjugate, quatRotate, scale } from './math'\nimport { levelFor, lipOf, lipRadius, makeColumns, planeFraction, planeRange, shapeHeight } from './shapes'\nimport type { Columns } from './shapes'\nimport { addInflow, createStain, makeBasis, meanRadius, nudgeCenter, stepStain } from './stains'\nimport type { Basis, StainInt, StainSurface } from './stains'\nimport type {\n ContainerSpec,\n ContainerState,\n FluidEvent,\n FluidOptions,\n FluidWorld,\n LiquidDef,\n LiquidId,\n Quat,\n StainState,\n StainTint,\n StreamCause,\n StreamState,\n SurfaceSpec,\n Vec3,\n} from './types'\n\ninterface Hole {\n point: Vec3\n normal: Vec3\n diameter: number\n}\n\ninterface ContainerInt {\n id: string\n spec: ContainerSpec\n cols: Columns\n liquid: LiquidId | null\n liters: number\n temperature: number\n closed: boolean\n position: Vec3\n rotation: Quat\n velocity: Vec3\n prevVelocity: Vec3\n hasPrev: boolean\n holes: Hole[]\n /** Slosh of the liquid surface: tilt offsets and their rates (x, z, vx, vz). */\n slosh: [number, number, number, number]\n /** Surface normal in world space (up plus slosh) and in container space, and the surface offset along it. */\n normal: Vec3\n u: Vec3\n d: number\n steaming: boolean\n outflow: number\n /** Stream key (`spill`, `pour`, `leak:<n>`) to stream id. */\n streams: Record<string, string>\n pourRate: number\n pourTTL: number\n pourVisual: boolean\n lastMix: number\n}\n\ninterface TapInt {\n id: string\n position: Vec3\n direction: Vec3\n liquid: LiquidId\n rate: number\n temperature: number\n on: boolean\n stream?: string\n}\n\ninterface StreamInt extends StreamState {\n key: string\n lastSplash: number\n visualOnly: boolean\n temperature: number\n}\n\ninterface SurfaceInt extends SurfaceSpec, StainSurface {}\n\ninterface Snapshot {\n version: 1\n time: number\n rng: number\n counters: { container: number; stream: number; stain: number; tap?: number }\n taps?: TapInt[]\n containers: Array<Omit<ContainerInt, 'cols'>>\n surfaces: SurfaceSpec[]\n streams: StreamInt[]\n stains: StainInt[]\n}\n\nconst GROUND_POROSITY = 0.2\nconst MAX_STEP = 0.1\nconst MAX_SUBSTEPS = 50\nconst MAX_HOLES = 32\n\nconst clone = <T>(v: T): T => JSON.parse(JSON.stringify(v)) as T\n\nexport function createFluids(options: FluidOptions = {}): FluidWorld {\n const strings = resolveStrings(options.locale)\n const liquids = new Map<LiquidId, LiquidDef>()\n for (const def of LIQUIDS) liquids.set(def.id, def)\n for (const def of options.liquids ?? []) liquids.set(def.id, def)\n const ambientC = options.ambientC ?? 20\n const gravity = options.gravity ?? GRAVITY\n const groundAt = options.groundAt ?? (() => 0)\n const mergeDistance = options.mergeDistance ?? 0.15\n const maxStains = options.maxStains ?? 64\n\n let rng = new Rng(options.seed ?? 1)\n let time = 0\n const counters = { container: 0, stream: 0, stain: 0, tap: 0 }\n let containers = new Map<string, ContainerInt>()\n let surfaces = new Map<string, SurfaceInt>()\n let streams = new Map<string, StreamInt>()\n let taps = new Map<string, TapInt>()\n let stains = new Map<string, StainInt>()\n const listeners = new Map<string, Set<(e: FluidEvent) => void>>()\n let queue: FluidEvent[] = []\n /** Only events produced by `update` are queued and returned by it; direct calls just notify the listeners. */\n let updating = false\n\n const groundSurface: StainSurface = {\n normal: [0, 1, 0],\n t1: [1, 0, 0],\n t2: [0, 0, -1],\n point: [0, 0, 0],\n half: [Infinity, Infinity],\n porosity: GROUND_POROSITY,\n }\n\n // ---- events ----\n\n const emit = (e: FluidEvent): void => {\n if (updating) queue.push(e)\n options.onEvent?.(e)\n listeners.get(e.type)?.forEach((fn) => fn(e))\n listeners.get('*')?.forEach((fn) => fn(e))\n }\n\n // ---- helpers ----\n\n const toWorld = (c: ContainerInt, p: Vec3): Vec3 => add(c.position, quatRotate(c.rotation, p))\n const defOf = (c: ContainerInt): LiquidDef | null => (c.liquid ? (liquids.get(c.liquid) ?? null) : null)\n const viscScale = (def: LiquidDef): number => 1 / Math.sqrt(Math.max(1, def.viscosity))\n\n const refreshLevel = (c: ContainerInt): void => {\n c.normal = normalize([c.slosh[0], 1, c.slosh[1]])\n c.u = normalize(quatRotate(quatConjugate(c.rotation), c.normal))\n c.d = levelFor(c.cols, c.u, c.spec.capacity > 0 ? c.liters / c.spec.capacity : 0)\n }\n\n const surfaceFor = (id: string): StainSurface => (id === GROUND_ID ? groundSurface : (surfaces.get(id) ?? groundSurface))\n\n const landingSurfaces = (): LandingSurface[] => {\n const list: LandingSurface[] = []\n for (const [id, s] of surfaces) list.push({ id, normal: s.normal, t1: s.t1, t2: s.t2, point: s.point, half: s.half })\n return list\n }\n\n const stopStream = (id: string): void => {\n const s = streams.get(id)\n if (!s) return\n streams.delete(id)\n const c = s.container ? containers.get(s.container) : undefined\n if (c) delete c.streams[s.key]\n const tap = s.tap ? taps.get(s.tap) : undefined\n if (tap && tap.stream === id) tap.stream = undefined\n emit({ type: 'stream_end', stream: id })\n }\n\n const endStreamKey = (c: ContainerInt, key: string): void => {\n const id = c.streams[key]\n if (id) stopStream(id)\n }\n\n /** Create or update the stream under `key` of a container; creating one emits the matching cause event. */\n const syncStream = (\n c: ContainerInt,\n key: string,\n cause: StreamCause,\n rate: number,\n origin: Vec3,\n velocity: Vec3,\n visualOnly = false,\n ): void => {\n if (rate < 1e-5 || !c.liquid) {\n endStreamKey(c, key)\n return\n }\n const existing = c.streams[key] ? streams.get(c.streams[key]) : undefined\n if (existing) {\n existing.rate = rate\n existing.origin = origin\n existing.velocity = velocity\n existing.thin = rate < 0.04\n existing.visualOnly = visualOnly\n existing.temperature = c.temperature\n return\n }\n const id = `stream_${++counters.stream}`\n const stream: StreamInt = {\n id,\n liquid: c.liquid,\n cause,\n container: c.id,\n origin,\n velocity,\n rate,\n landing: null,\n thin: rate < 0.04,\n flightTime: MAX_FLIGHT,\n key,\n lastSplash: -1,\n visualOnly,\n temperature: c.temperature,\n }\n streams.set(id, stream)\n c.streams[key] = id\n if (cause === 'leak') emit({ type: 'leak', container: c.id, liquid: c.liquid, rate, stream: id, point: copy3(origin) })\n else if (cause === 'spill') emit({ type: 'spill', container: c.id, liquid: c.liquid, rate, stream: id })\n else emit({ type: 'pour', container: c.id, liquid: c.liquid, rate, stream: id })\n }\n\n /** Speed (m/s) at which a tap's liquid leaves the nozzle. */\n const TAP_SPEED = 1.2\n\n /** Create or update the `tap` stream of a tap that is on; end it when it is off. */\n const syncTap = (t: TapInt): void => {\n if (!t.on || t.rate < 1e-5 || !liquids.has(t.liquid)) {\n if (t.stream) stopStream(t.stream)\n return\n }\n const velocity: Vec3 = [t.direction[0] * TAP_SPEED, t.direction[1] * TAP_SPEED, t.direction[2] * TAP_SPEED]\n const existing = t.stream ? streams.get(t.stream) : undefined\n if (existing) {\n existing.rate = t.rate\n existing.thin = t.rate < 0.04\n existing.temperature = t.temperature\n existing.velocity = velocity\n return\n }\n const id = `stream_${++counters.stream}`\n streams.set(id, {\n id,\n liquid: t.liquid,\n cause: 'tap',\n tap: t.id,\n origin: copy3(t.position),\n velocity,\n rate: t.rate,\n landing: null,\n thin: t.rate < 0.04,\n flightTime: MAX_FLIGHT,\n key: `tap:${t.id}`,\n lastSplash: -1,\n visualOnly: false,\n temperature: t.temperature,\n })\n t.stream = id\n }\n\n /** Where and how fast the liquid leaves over the lowest lip point. */\n const lipFlow = (c: ContainerInt, head: number): { origin: Vec3; velocity: Vec3 } => {\n const lip = lipOf(c.spec.shape, c.spec.opening, c.u)\n const origin = toWorld(c, lip.point)\n let dir: Vec3 = [1, 0, 0]\n const horizontal = (v: Vec3): Vec3 | null => (Math.hypot(v[0], v[2]) > 1e-3 ? normalize([v[0], 0, v[2]]) : null)\n if (lip.outward) dir = horizontal(quatRotate(c.rotation, lip.outward)) ?? horizontal(quatRotate(c.rotation, [1, 0, 0])) ?? dir\n else dir = horizontal(quatRotate(c.rotation, [1, 0, 0])) ?? dir\n const speed = clamp(0.3 + 0.5 * Math.sqrt(2 * gravity * Math.max(0, head)), 0.2, 2.5)\n return { origin, velocity: [dir[0] * speed, -0.1, dir[2] * speed] }\n }\n\n const removeLiters = (c: ContainerInt, amount: number): void => {\n c.liters = Math.max(0, c.liters - amount)\n }\n\n /** Clear a container that ran out and emit `empty` once. */\n const settle = (c: ContainerInt): void => {\n if (c.liquid && c.liters <= 1e-6) {\n c.liters = 0\n c.liquid = null\n emit({ type: 'empty', container: c.id })\n }\n }\n\n const fillInternal = (c: ContainerInt, liquid: LiquidId, liters: number, temperature: number | undefined, announce: boolean): number => {\n if (!(liters > 0) || !liquids.has(liquid)) return 0\n const room = c.spec.capacity - c.liters\n const accepted = Math.min(liters, Math.max(0, room))\n if (accepted <= 0) return 0\n const t = temperature ?? ambientC\n if (c.liquid && c.liquid !== liquid && c.liters > 1e-6) {\n const incomingWins = accepted > c.liters\n const into = incomingWins ? liquid : c.liquid\n const from = incomingWins ? c.liquid : liquid\n if (announce || time - c.lastMix > 0.5) {\n c.lastMix = time\n emit({ type: 'mix', container: c.id, from, into })\n }\n c.liquid = into\n } else if (!c.liquid || c.liters <= 1e-6) {\n c.liquid = liquid\n }\n c.temperature = c.liters > 1e-6 ? (c.temperature * c.liters + t * accepted) / (c.liters + accepted) : t\n c.liters += accepted\n if (announce) emit({ type: 'fill', container: c.id, liquid, liters: accepted })\n return accepted\n }\n\n // ---- stains ----\n\n const stainAt = (surfaceId: string, liquid: LiquidId, point: Vec3): StainInt | null => {\n let best: StainInt | null = null\n let bestD = Infinity\n for (const s of stains.values()) {\n if (s.surface !== surfaceId || s.liquid !== liquid) continue\n const d = distance(point, s.center)\n if (d < mergeDistance + 0.5 * meanRadius(s.front) && d < bestD) {\n best = s\n bestD = d\n }\n }\n return best\n }\n\n const evictStain = (): void => {\n let victim: StainInt | null = null\n for (const s of stains.values()) {\n const rank = (x: StainInt) => (x.wetness <= 0 ? 0 : 1) * 1e9 - x.age\n if (!victim || rank(s) < rank(victim)) victim = s\n }\n if (victim) stains.delete(victim.id)\n }\n\n const deposit = (liquid: LiquidId, hit: Pick<Hit, 'point' | 'surface' | 'normal'>, liters: number, velocity: Vec3 | null, tint?: StainTint): void => {\n const def = liquids.get(liquid)\n if (!def || liters <= 0) return\n let s = stainAt(hit.surface, liquid, hit.point)\n if (s) {\n const basis: Basis = { normal: s.normal, t1: s.tangent, t2: cross(s.normal, s.tangent) }\n nudgeCenter(s, hit.point, clamp((liters / (s.liters + liters)) * 0.5, 0, 0.5), basis)\n addInflow(s, liters, velocity)\n if (tint) s.tint = { ...tint }\n return\n }\n while (stains.size >= maxStains) evictStain()\n const surface = surfaceFor(hit.surface)\n const basis: Basis = hit.surface === GROUND_ID ? makeBasis(hit.normal) : surface\n s = createStain(`stain_${++counters.stain}`, hit.surface, def, basis, hit.point, liters, rng)\n if (velocity) addInflow(s, 0, velocity)\n if (tint) s.tint = { ...tint }\n s.sinceInflow = 0\n stains.set(s.id, s)\n emit({ type: 'stain', stain: s.id, surface: hit.surface, liquid, point: copy3(hit.point) })\n }\n\n // ---- streams ----\n\n const lipCenterWorld = (c: ContainerInt): Vec3 => toWorld(c, [0, shapeHeight(c.spec.shape), 0])\n\n /** A container whose opening the jet passes through before it lands (pouring into a cup). */\n const catcher = (s: StreamInt, landingT: number): { container: ContainerInt; t: number; point: Vec3 } | null => {\n let best: { container: ContainerInt; t: number; point: Vec3 } | null = null\n for (const oc of containers.values()) {\n if (oc.id === s.container || oc.closed || oc.liters >= oc.spec.capacity) continue\n const lip = lipCenterWorld(oc)\n const disc = s.velocity[1] * s.velocity[1] - 2 * gravity * (lip[1] - s.origin[1])\n if (disc < 0) continue\n const t = (s.velocity[1] + Math.sqrt(disc)) / gravity\n if (t <= 0 || t >= landingT || (best && t >= best.t)) continue\n const p = ballistic(s.origin, s.velocity, gravity, t)\n if (Math.hypot(p[0] - lip[0], p[2] - lip[2]) <= lipRadius(oc.spec.shape, oc.spec.opening) + 0.01) best = { container: oc, t, point: p }\n }\n return best\n }\n\n const stepStream = (s: StreamInt, dt: number): void => {\n const hit = trace(landingSurfaces(), groundAt, s.origin, s.velocity, gravity)\n const caught = catcher(s, hit ? hit.t : MAX_FLIGHT)\n if (caught) {\n s.landing = caught.point\n s.flightTime = caught.t\n if (!s.visualOnly) fillInternal(caught.container, s.liquid, s.rate * dt, s.temperature, false)\n return\n }\n s.landing = hit ? copy3(hit.point) : null\n s.flightTime = hit ? hit.t : MAX_FLIGHT\n if (!hit || s.visualOnly) return\n const speed = length(hit.velocity)\n if (time - s.lastSplash >= 0.12) {\n s.lastSplash = time\n emit({ type: 'splash', liquid: s.liquid, point: copy3(hit.point), intensity: clamp(0.15 + s.rate * 1.2 + speed * 0.05, 0, 1) })\n }\n deposit(s.liquid, hit, s.rate * dt, hit.velocity)\n }\n\n // ---- containers ----\n\n const spillRate = (c: ContainerInt, def: LiquidDef, h: number): number => {\n const ro = lipRadius(c.spec.shape, c.spec.opening)\n const area = 2 * ro * Math.min(h, ro)\n return Math.max(0.6 * area * Math.sqrt(2 * gravity * h) * 1000 * viscScale(def), 0.004)\n }\n\n const steamThreshold = (def: LiquidDef): number => def.steamC ?? (def.drink && def.drink.thirst > 0 ? 60 : Infinity)\n\n const stepContainer = (c: ContainerInt, dt: number): void => {\n // Slosh: the surface tilts towards the horizontal acceleration and settles with a damped spring.\n if (c.hasPrev && dt > 0) {\n const ax = (c.velocity[0] - c.prevVelocity[0]) / dt\n const az = (c.velocity[2] - c.prevVelocity[2]) / dt\n const tx = clamp(ax / gravity, -0.35, 0.35)\n const tz = clamp(az / gravity, -0.35, 0.35)\n const w = 9\n const z = 0.12\n c.slosh[2] += (w * w * (tx - c.slosh[0]) - 2 * z * w * c.slosh[2]) * dt\n c.slosh[3] += (w * w * (tz - c.slosh[1]) - 2 * z * w * c.slosh[3]) * dt\n } else {\n c.slosh[2] *= 0.5\n c.slosh[3] *= 0.5\n }\n c.slosh[0] += c.slosh[2] * dt\n c.slosh[1] += c.slosh[3] * dt\n if (Math.abs(c.slosh[0]) + Math.abs(c.slosh[1]) + Math.abs(c.slosh[2]) + Math.abs(c.slosh[3]) < 1e-5) c.slosh = [0, 0, 0, 0]\n c.prevVelocity = copy3(c.velocity)\n c.hasPrev = true\n\n // Cooling towards ambient (insulated containers 10× slower).\n if (c.temperature !== ambientC) {\n const tau = (600 + 2400 * c.liters) * (c.spec.insulated ? 10 : 1)\n c.temperature = ambientC + (c.temperature - ambientC) * Math.exp(-dt / tau)\n if (Math.abs(c.temperature - ambientC) < 0.01) c.temperature = ambientC\n }\n\n refreshLevel(c)\n const def = defOf(c)\n\n const steaming = !!def && c.liters > 1e-6 && !c.closed && c.temperature > steamThreshold(def)\n if (steaming !== c.steaming) {\n c.steaming = steaming\n emit({ type: 'steam', container: c.id, on: steaming })\n }\n\n c.outflow = 0\n if (!def || c.liters <= 1e-6) {\n for (const key of Object.keys(c.streams)) endStreamKey(c, key)\n c.pourTTL = 0\n settle(c)\n return\n }\n\n // Pour (driven by `pour()`), else spill over the lip when the tilted surface rises above it.\n if (c.pourTTL > 0) {\n c.pourTTL -= dt\n if (c.pourTTL > 0) {\n const flow = lipFlow(c, 0.05)\n syncStream(c, 'pour', 'pour', c.pourRate, flow.origin, flow.velocity, c.pourVisual)\n c.outflow += c.pourRate\n } else endStreamKey(c, 'pour')\n endStreamKey(c, 'spill')\n } else {\n endStreamKey(c, 'pour')\n const lip = lipOf(c.spec.shape, c.spec.opening, c.u)\n const h = c.d - lip.level\n let handled = false\n if (!c.closed && h > 1e-5) {\n const excess = c.liters - c.spec.capacity * planeFraction(c.cols, c.u, lip.level)\n if (excess > 1e-6) {\n const rate = Math.min(spillRate(c, def, h), excess / Math.max(dt, 1e-6))\n removeLiters(c, Math.min(rate * dt, excess, c.liters))\n const flow = lipFlow(c, h)\n syncStream(c, 'spill', 'spill', rate, flow.origin, flow.velocity)\n c.outflow += rate\n handled = true\n }\n }\n if (!handled) endStreamKey(c, 'spill')\n }\n\n // Holes leak by Torricelli with the head above each hole.\n for (let i = 0; i < c.holes.length; i++) {\n const hole = c.holes[i]\n const key = `leak:${i}`\n const hh = dot(c.u, hole.point)\n const head = c.d - hh\n if (c.liters > 1e-6 && head > 1e-4) {\n const area = Math.PI * (hole.diameter / 2) ** 2\n const natural = 0.6 * area * Math.sqrt(2 * gravity * head) * 1000 * viscScale(def)\n const avail = c.liters - c.spec.capacity * planeFraction(c.cols, c.u, hh)\n if (avail > 1e-7) {\n const rate = Math.min(natural, avail / Math.max(dt, 1e-6))\n removeLiters(c, Math.min(rate * dt, avail))\n const nW = normalize(quatRotate(c.rotation, hole.normal))\n const v = Math.min(3, 0.6 * Math.sqrt(2 * gravity * head))\n syncStream(c, key, 'leak', rate, toWorld(c, hole.point), [nW[0] * v, nW[1] * v - 0.05, nW[2] * v])\n c.outflow += rate\n continue\n }\n }\n endStreamKey(c, key)\n }\n if (c.holes.length > 0 || c.outflow > 0) refreshLevel(c)\n settle(c)\n }\n\n const step = (dt: number): void => {\n time += dt\n for (const c of [...containers.values()]) stepContainer(c, dt)\n for (const s of [...streams.values()]) stepStream(s, dt)\n for (const st of [...stains.values()]) {\n const def = liquids.get(st.liquid)\n if (!def) continue\n const status = stepStain(st, def, surfaceFor(st.surface), ambientC, dt)\n if (status === 'dried_mark') emit({ type: 'dried', stain: st.id, mark: true })\n else if (status === 'dried_gone') {\n stains.delete(st.id)\n emit({ type: 'dried', stain: st.id, mark: false })\n }\n }\n }\n\n // ---- public state views ----\n\n const stateOf = (c: ContainerInt): ContainerState => ({\n id: c.id,\n liquid: c.liquid,\n liters: c.liters,\n capacity: c.spec.capacity,\n temperature: c.temperature,\n closed: c.closed,\n surfaceHeight: c.liters > 0 ? c.d : 0,\n surfacePoint: add(c.position, scale(c.normal, c.liters > 0 ? c.d : 0)),\n surfaceNormal: copy3(c.normal),\n holes: c.holes.map((h) => ({ point: copy3(h.point), normal: copy3(h.normal), diameter: h.diameter })),\n outflow: c.outflow,\n steaming: c.steaming,\n shape: c.spec.shape,\n lipRadius: lipRadius(c.spec.shape, c.spec.opening),\n position: copy3(c.position),\n rotation: [...c.rotation] as Quat,\n })\n\n const streamView = (s: StreamInt): StreamState => ({\n id: s.id,\n liquid: s.liquid,\n cause: s.cause,\n container: s.container,\n tap: s.tap,\n origin: copy3(s.origin),\n velocity: copy3(s.velocity),\n rate: s.rate,\n landing: s.landing ? copy3(s.landing) : null,\n thin: s.thin,\n flightTime: s.flightTime,\n })\n\n const stainView = (s: StainInt): StainState => ({\n id: s.id,\n surface: s.surface,\n liquid: s.liquid,\n center: copy3(s.center),\n front: [...s.front],\n liters: s.liters,\n soaked: s.soaked,\n wetness: s.wetness,\n wetRadius: s.wetRadius,\n normal: copy3(s.normal),\n tangent: copy3(s.tangent),\n flow: s.flow ? copy3(s.flow) : undefined,\n mark: s.mark,\n age: s.age,\n tint: s.tint ? { ...s.tint } : undefined,\n })\n\n // ---- world API ----\n\n const world: FluidWorld = {\n liquids,\n gravity,\n describe: (event) => describeEvent(strings, event),\n\n addContainer(spec, opts = {}) {\n const id = opts.id ?? `container_${++counters.container}`\n if (containers.has(id)) world.removeContainer(id)\n const liquid = spec.liquid && liquids.has(spec.liquid) ? spec.liquid : null\n const liters = liquid ? clamp(spec.liters ?? 0, 0, spec.capacity) : 0\n const c: ContainerInt = {\n id,\n spec: clone(spec),\n cols: makeColumns(spec.shape),\n liquid: liters > 0 ? liquid : null,\n liters,\n temperature: spec.temperature ?? ambientC,\n closed: spec.closed ?? spec.opening <= 0,\n position: copy3(opts.position ?? [0, 0, 0]),\n rotation: opts.rotation ? ([...opts.rotation] as Quat) : [0, 0, 0, 1],\n velocity: [0, 0, 0],\n prevVelocity: [0, 0, 0],\n hasPrev: false,\n holes: [],\n slosh: [0, 0, 0, 0],\n normal: [0, 1, 0],\n u: [0, 1, 0],\n d: 0,\n steaming: false,\n outflow: 0,\n streams: {},\n pourRate: 0,\n pourTTL: 0,\n pourVisual: false,\n lastMix: -10,\n }\n containers.set(id, c)\n refreshLevel(c)\n return id\n },\n\n addTap(spec) {\n const id = spec.id ?? `tap_${++counters.tap}`\n if (taps.has(id)) world.removeTap(id)\n const d = spec.direction ?? [0, -1, 0]\n const len = Math.hypot(d[0], d[1], d[2]) || 1\n const t: TapInt = {\n id,\n position: copy3(spec.position),\n direction: [d[0] / len, d[1] / len, d[2] / len],\n liquid: spec.liquid,\n rate: Math.max(0, spec.rate),\n temperature: spec.temperature ?? ambientC,\n on: false,\n }\n taps.set(id, t)\n if (spec.on ?? true) world.setTap(id, true)\n return id\n },\n\n setTap(id, on, rate) {\n const t = taps.get(id)\n if (!t) return\n if (rate !== undefined) t.rate = Math.max(0, rate)\n const changed = t.on !== on\n t.on = on\n syncTap(t)\n if (changed) emit({ type: 'tap', tap: id, on })\n },\n\n removeTap(id) {\n const t = taps.get(id)\n if (!t) return\n t.on = false\n syncTap(t)\n taps.delete(id)\n },\n\n taps: () => [...taps.values()].map((t) => ({ id: t.id, position: copy3(t.position), direction: copy3(t.direction), liquid: t.liquid, rate: t.rate, temperature: t.temperature, on: t.on, stream: t.stream })),\n\n removeContainer(id) {\n const c = containers.get(id)\n if (!c) return\n for (const key of Object.keys(c.streams)) endStreamKey(c, key)\n containers.delete(id)\n },\n\n setTransform(id, position, rotation, velocity) {\n const c = containers.get(id)\n if (!c) return\n c.position = copy3(position)\n c.rotation = [...rotation] as Quat\n if (velocity) c.velocity = copy3(velocity)\n refreshLevel(c)\n },\n\n open(id) {\n const c = containers.get(id)\n if (c) c.closed = false\n },\n\n close(id) {\n const c = containers.get(id)\n if (!c) return\n c.closed = true\n for (const key of ['pour', 'spill']) endStreamKey(c, key)\n c.pourTTL = 0\n },\n\n fill(id, liquid, liters, temperature) {\n const c = containers.get(id)\n if (!c || c.closed) return 0\n const accepted = fillInternal(c, liquid, liters, temperature, true)\n refreshLevel(c)\n return accepted\n },\n\n pour(id, dt, into) {\n const c = containers.get(id)\n const def = c ? defOf(c) : null\n if (!c || !def || c.closed || c.liters <= 1e-6 || !(dt > 0)) return 0\n refreshLevel(c)\n const lip = lipOf(c.spec.shape, c.spec.opening, c.u)\n const depth = Math.max(0, c.d - planeRange(c.cols, c.u)[0])\n const head = Math.max(c.d - lip.level, 0.02 + 0.5 * depth)\n const ro = lipRadius(c.spec.shape, c.spec.opening)\n const natural = 0.6 * Math.PI * ro * ro * Math.sqrt(2 * gravity * head) * 1000\n const rate = Math.min(natural, 0.15 + 12 * ro) * viscScale(def)\n const out = Math.min(rate * dt, c.liters)\n const target = into ? containers.get(into) : undefined\n let delivered = out\n if (target) {\n delivered = fillInternal(target, c.liquid as LiquidId, out, c.temperature, false)\n refreshLevel(target)\n const overflow = out - delivered\n if (overflow > 1e-6) {\n const flow = lipFlow(c, head)\n const hit = trace(landingSurfaces(), groundAt, flow.origin, flow.velocity, gravity)\n if (hit) deposit(c.liquid as LiquidId, hit, overflow, hit.velocity)\n }\n }\n removeLiters(c, out)\n c.pourRate = out / dt\n c.pourTTL = Math.max(0.12, dt * 1.5)\n c.pourVisual = !!target\n const flow = lipFlow(c, head)\n syncStream(c, 'pour', 'pour', c.pourRate, flow.origin, flow.velocity, c.pourVisual)\n refreshLevel(c)\n settle(c)\n return out\n },\n\n drink(id, liters) {\n const c = containers.get(id)\n const def = c ? defOf(c) : null\n if (!c || !def || c.closed || c.liters <= 1e-6 || !(liters > 0)) return null\n const amount = Math.min(liters, c.liters)\n removeLiters(c, amount)\n const event: Extract<FluidEvent, { type: 'drink' }> = {\n type: 'drink',\n container: c.id,\n liquid: def.id,\n liters: amount,\n temperature: c.temperature,\n thirst: (def.drink?.thirst ?? 0) * amount,\n effects: [...(def.drink?.effects ?? [])],\n burn: c.temperature > 65,\n }\n emit(event)\n refreshLevel(c)\n settle(c)\n return event\n },\n\n puncture(id, point, normal, diameter) {\n const c = containers.get(id)\n if (!c || !(diameter > 0) || c.holes.length >= MAX_HOLES) return\n c.holes.push({ point: copy3(point), normal: normalize(normal), diameter })\n },\n\n heat(id, deltaC) {\n const c = containers.get(id)\n if (c) c.temperature = clamp(c.temperature + deltaC, -50, 1000)\n },\n\n addSurface(spec) {\n const basis = makeBasis(spec.normal, spec.tangent)\n surfaces.set(spec.id, {\n ...clone(spec),\n ...basis,\n point: copy3(spec.point),\n half: [spec.halfSize[0], spec.halfSize[1]],\n })\n },\n\n removeSurface(id) {\n surfaces.delete(id)\n for (const s of [...stains.values()]) if (s.surface === id) stains.delete(s.id)\n },\n\n splash(liquid, point, liters, velocity, options) {\n if (!liquids.has(liquid) || !(liters > 0)) return\n const v: Vec3 = velocity ?? [0, -0.1, 0]\n const origin = add(point, [0, 0.02, 0])\n const hit = trace(landingSurfaces(), groundAt, origin, v, gravity)\n if (!hit) return\n emit({ type: 'splash', liquid, point: copy3(hit.point), intensity: clamp(0.3 + liters * 2, 0, 1) })\n deposit(liquid, hit, liters, length(v) > 0.3 ? hit.velocity : null, options?.tint)\n },\n\n clearStains(point, radius) {\n for (const s of [...stains.values()]) {\n if (distance(point, s.center) <= radius + 0.5 * meanRadius(s.front)) {\n stains.delete(s.id)\n emit({ type: 'dried', stain: s.id, mark: false })\n }\n }\n },\n\n update(dt) {\n if (dt > 0) {\n const n = clamp(Math.ceil(dt / MAX_STEP), 1, MAX_SUBSTEPS)\n updating = true\n try {\n for (let i = 0; i < n; i++) step(dt / n)\n } finally {\n updating = false\n }\n }\n const out = queue\n queue = []\n return out\n },\n\n container: (id) => {\n const c = containers.get(id)\n return c ? stateOf(c) : null\n },\n containers: () => [...containers.values()].map(stateOf),\n streams: () => [...streams.values()].map(streamView),\n stains: () => [...stains.values()].map(stainView),\n\n on(type, fn) {\n let set = listeners.get(type)\n if (!set) listeners.set(type, (set = new Set()))\n set.add(fn)\n return () => {\n set?.delete(fn)\n }\n },\n\n serialize() {\n const snapshot: Snapshot = {\n version: 1,\n time,\n rng: rng.state,\n counters: { ...counters },\n containers: [...containers.values()].map((c) => {\n const { cols: _cols, ...rest } = c\n return clone(rest)\n }),\n surfaces: [...surfaces.values()].map((s) => clone({ id: s.id, point: s.point, normal: s.normal, halfSize: s.half, tangent: s.t1, porosity: s.porosity })),\n streams: [...streams.values()].map((s) => clone(s)),\n taps: [...taps.values()].map((t) => clone(t)),\n stains: [...stains.values()].map((s) => clone(s)),\n }\n return snapshot\n },\n\n load(input) {\n const snap = clone(input) as Snapshot\n if (!snap || snap.version !== 1) throw new Error('game-fluids: unsupported snapshot')\n time = snap.time\n rng = new Rng(0)\n rng.state = snap.rng\n counters.container = snap.counters.container\n counters.stream = snap.counters.stream\n counters.stain = snap.counters.stain\n containers = new Map()\n for (const raw of snap.containers) containers.set(raw.id, { ...raw, cols: makeColumns(raw.spec.shape) })\n surfaces = new Map()\n for (const s of snap.surfaces) world.addSurface(s)\n counters.tap = snap.counters.tap ?? 0\n streams = new Map(snap.streams.map((s) => [s.id, s]))\n taps = new Map((snap.taps ?? []).map((t) => [t.id, t]))\n stains = new Map(snap.stains.map((s) => [s.id, s]))\n queue = []\n },\n }\n\n return world\n}\n","[\n {\n \"id\": \"sip\",\n \"duration\": 1.2,\n \"keys\": [\n {\n \"t\": 0,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 0.35,\n \"position\": [\n 0.0,\n 0.02,\n -0.2\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 0.45,\n \"position\": [\n 0.0,\n 0.02,\n -0.2\n ],\n \"rotation\": [\n -0.9,\n 0,\n 0\n ],\n \"action\": \"drink\",\n \"rate\": 0.06\n },\n {\n \"t\": 0.95,\n \"position\": [\n 0.0,\n 0.02,\n -0.2\n ],\n \"rotation\": [\n -0.9,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 1.2,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n }\n ]\n },\n {\n \"id\": \"drink\",\n \"duration\": 2.5,\n \"keys\": [\n {\n \"t\": 0,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 0.5,\n \"position\": [\n 0.0,\n 0.02,\n -0.2\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 0.8,\n \"position\": [\n 0.0,\n 0.02,\n -0.2\n ],\n \"rotation\": [\n -1.22,\n 0,\n 0\n ],\n \"action\": \"drink\",\n \"rate\": 0.1667\n },\n {\n \"t\": 2.0,\n \"position\": [\n 0.0,\n 0.02,\n -0.2\n ],\n \"rotation\": [\n -1.22,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 2.5,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n }\n ]\n },\n {\n \"id\": \"chug\",\n \"duration\": 4,\n \"keys\": [\n {\n \"t\": 0,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 0.5,\n \"position\": [\n 0.0,\n 0.02,\n -0.2\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 0.9,\n \"position\": [\n 0.0,\n 0.02,\n -0.2\n ],\n \"rotation\": [\n -1.45,\n 0,\n 0\n ],\n \"action\": \"drink\",\n \"rate\": 0.2\n },\n {\n \"t\": 3.4,\n \"position\": [\n 0.0,\n 0.02,\n -0.2\n ],\n \"rotation\": [\n -1.45,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 4,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n }\n ]\n },\n {\n \"id\": \"pour\",\n \"duration\": 3,\n \"keys\": [\n {\n \"t\": 0,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 0.4,\n \"position\": [\n 0.1,\n 0.0,\n -0.35\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 0.8,\n \"position\": [\n 0.1,\n 0.0,\n -0.35\n ],\n \"rotation\": [\n -1.745,\n 0,\n 0\n ],\n \"action\": \"pour\",\n \"rate\": 1\n },\n {\n \"t\": 2.4,\n \"position\": [\n 0.1,\n 0.0,\n -0.35\n ],\n \"rotation\": [\n -1.745,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 3,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n }\n ]\n },\n {\n \"id\": \"pour_out\",\n \"duration\": 4,\n \"keys\": [\n {\n \"t\": 0,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 0.5,\n \"position\": [\n 0.1,\n 0.1,\n -0.35\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 1.0,\n \"position\": [\n 0.1,\n 0.1,\n -0.35\n ],\n \"rotation\": [\n -2.2,\n 0,\n 0\n ],\n \"action\": \"pour\",\n \"rate\": 40\n },\n {\n \"t\": 3.2,\n \"position\": [\n 0.1,\n 0.1,\n -0.35\n ],\n \"rotation\": [\n -2.2,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 4,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n }\n ]\n },\n {\n \"id\": \"open\",\n \"duration\": 1,\n \"keys\": [\n {\n \"t\": 0,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 0.25,\n \"position\": [\n 0.1,\n 0,\n -0.3\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"open\"\n },\n {\n \"t\": 0.7,\n \"position\": [\n 0.1,\n 0,\n -0.3\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 1,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n }\n ]\n },\n {\n \"id\": \"close\",\n \"duration\": 1,\n \"keys\": [\n {\n \"t\": 0,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 0.25,\n \"position\": [\n 0.1,\n 0,\n -0.3\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"close\"\n },\n {\n \"t\": 0.7,\n \"position\": [\n 0.1,\n 0,\n -0.3\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n },\n {\n \"t\": 1,\n \"position\": [\n 0.22,\n -0.2,\n -0.4\n ],\n \"rotation\": [\n 0,\n 0,\n 0\n ],\n \"action\": \"none\"\n }\n ]\n }\n]","import itemUsesData from './data/item-uses.json'\nimport { lerp3, quatFromEuler, quatMultiply, quatRotate, add } from './math'\nimport type { FluidEvent, FluidWorld, ItemUseDef, ItemUsePlayer, Quat, Vec3 } from './types'\n\n/** Built-in scripted uses of a held item (data in `data/item-uses.json`): `sip`, `drink`, `chug`, `pour`, `pour_out`, `open`, `close`. */\nexport const ITEM_USES: ItemUseDef[] = itemUsesData as unknown as ItemUseDef[]\n\n/** World transform of the hand (or any holder) the item pose is relative to. */\nexport type HandTransform = () => { position: Vec3; rotation: Quat }\n\nconst DEFAULT_DRINK_RATE = 0.1\n\n/**\n * Plays an item use on a container: interpolates the keys, applies `drink` (rate × dt litres) and `pour` (`world.pour`, `rate` multiplies the\n * time) while the current key has that action, and fires `open` / `close` once when their key is reached. When `getHandTransform` is given, the\n * container's world transform follows hand ∘ pose every update; otherwise the host animates the viewmodel from `pose()`.\n */\nexport function createItemUse(world: FluidWorld, containerId: string, def: ItemUseDef, getHandTransform?: HandTransform): ItemUsePlayer {\n const keys = [...def.keys].sort((a, b) => a.t - b.t)\n let time = 0\n let done = keys.length === 0 || def.duration <= 0\n let checked = false\n let lastPos: Vec3 | null = null\n const fired = new Set<number>()\n\n const pose = (): { position: Vec3; rotation: Vec3 } => {\n if (keys.length === 0) return { position: [0, 0, 0], rotation: [0, 0, 0] }\n if (time <= keys[0].t) return { position: [...keys[0].position], rotation: [...keys[0].rotation] }\n for (let i = 0; i < keys.length - 1; i++) {\n const a = keys[i]\n const b = keys[i + 1]\n if (time <= b.t) {\n const k = b.t > a.t ? (time - a.t) / (b.t - a.t) : 1\n return { position: lerp3(a.position, b.position, k), rotation: lerp3(a.rotation, b.rotation, k) }\n }\n }\n const last = keys[keys.length - 1]\n return { position: [...last.position], rotation: [...last.rotation] }\n }\n\n const follow = (dt: number): void => {\n if (!getHandTransform) return\n const hand = getHandTransform()\n const p = pose()\n const position = add(hand.position, quatRotate(hand.rotation, p.position))\n const rotation = quatMultiply(hand.rotation, quatFromEuler(p.rotation[0], p.rotation[1], p.rotation[2]))\n const velocity: Vec3 | undefined =\n lastPos && dt > 0 ? [(position[0] - lastPos[0]) / dt, (position[1] - lastPos[1]) / dt, (position[2] - lastPos[2]) / dt] : undefined\n lastPos = position\n world.setTransform(containerId, position, rotation, velocity)\n }\n\n const player: ItemUsePlayer = {\n def,\n get time() {\n return time\n },\n get done() {\n return done\n },\n pose,\n update(dt) {\n if (done) return []\n const events: FluidEvent[] = []\n const off = world.on('*', (e) => events.push(e))\n try {\n if (!checked) {\n checked = true\n const state = world.container(containerId)\n if (!state || (def.liquids && def.liquids.length > 0 && (!state.liquid || !def.liquids.includes(state.liquid)))) {\n done = true\n return events\n }\n }\n const start = time\n const end = Math.min(def.duration, time + Math.max(0, dt))\n for (let i = 0; i < keys.length && !done; i++) {\n const key = keys[i]\n const spanStart = key.t\n const spanEnd = i + 1 < keys.length ? keys[i + 1].t : def.duration\n if ((key.action === 'open' || key.action === 'close') && !fired.has(i) && end >= spanStart) {\n fired.add(i)\n if (key.action === 'open') world.open(containerId)\n else world.close(containerId)\n }\n const overlap = Math.min(end, spanEnd) - Math.max(start, spanStart)\n if (overlap <= 0) continue\n if (key.action === 'drink') {\n const ev = world.drink(containerId, (key.rate ?? DEFAULT_DRINK_RATE) * overlap)\n const left = world.container(containerId)\n if (!ev || !left || left.liters <= 1e-6) {\n time = def.duration\n done = true\n }\n } else if (key.action === 'pour') {\n world.pour(containerId, overlap * (key.rate ?? 1))\n }\n }\n if (!done) {\n time = end\n done = time >= def.duration\n }\n follow(dt)\n } finally {\n off()\n }\n return events\n },\n cancel() {\n done = true\n },\n }\n return player\n}\n","import type { FilmField, FilmProfile, FilmRandom } from './types'\n\nexport interface DepositContext {\n readonly field: FilmField\n readonly random: FilmRandom\n readonly profile: () => FilmProfile\n}\n\nconst VISIBLE_THICKNESS = 0.035\n\nexport function depositCell(context: DepositContext, rowIn: number, columnIn: number, amount: number): void {\n const { field } = context\n const row = Math.round(rowIn)\n const column = Math.round(columnIn)\n if (row < 0 || column < 0 || row >= field.height || column >= field.width || !(amount > 0)) return\n const index = row * field.width + column\n field.thickness[index]! += amount\n if (amount > 0.02) field.dryness[index]! *= 0.15\n field.dampness[index] = 1\n}\n\nexport function wetCellCount(field: FilmField): number {\n let cells = 0\n for (let index = 0; index < field.thickness.length; index++) if (field.thickness[index]! > VISIBLE_THICKNESS) cells++\n return cells\n}\n\n/** A filled ellipse with a noisy outline; `along` runs along (directionColumn, directionRow) and `across` is the other half axis, `amount` is the thickness at the centre in mm. */\nexport function depositBlob(context: DepositContext, centerRow: number, centerColumn: number, along: number, across: number, directionColumn: number, directionRow: number, amount: number, roughness: number): void {\n const random = context.random\n const phaseA = random() * 6.28\n const phaseB = random() * 6.28\n const phaseC = random() * 6.28\n const phaseD = random() * 6.28\n const reach = Math.ceil(Math.max(along, across) * (1 + roughness) + 1.5)\n for (let row = Math.floor(centerRow - reach); row <= centerRow + reach; row++) {\n for (let column = Math.floor(centerColumn - reach); column <= centerColumn + reach; column++) {\n const dx = column + 0.5 - centerColumn\n const dy = row + 0.5 - centerRow\n const alongDistance = dx * directionColumn + dy * directionRow\n const acrossDistance = -dx * directionRow + dy * directionColumn\n const angle = Math.atan2(acrossDistance / across, alongDistance / along)\n const edge = 1 + roughness * (0.45 * Math.sin(3 * angle + phaseA) + 0.3 * Math.sin(5 * angle + phaseB) + 0.18 * Math.sin(8 * angle + phaseC) + 0.12 * Math.sin(13 * angle + phaseD))\n const squared = ((alongDistance / along) * (alongDistance / along) + (acrossDistance / across) * (acrossDistance / across)) / (edge * edge)\n if (squared < 1) depositCell(context, row, column, amount * Math.sqrt(1 - squared))\n }\n }\n}\n\n/** A tapered stroke from one cell to another. */\nexport function depositStroke(context: DepositContext, fromRow: number, fromColumn: number, toRow: number, toColumn: number, startWidth: number, endWidth: number, amount: number): void {\n const { field } = context\n const length = Math.hypot(toRow - fromRow, toColumn - fromColumn)\n const samples = Math.max(2, Math.ceil(length * 2.5))\n for (let sample = 0; sample <= samples; sample++) {\n const progress = sample / samples\n const row = fromRow + (toRow - fromRow) * progress\n const column = fromColumn + (toColumn - fromColumn) * progress\n const width = startWidth + (endWidth - startWidth) * progress\n const reach = Math.ceil(width + 1)\n for (let cellRow = Math.floor(row - reach); cellRow <= row + reach; cellRow++) {\n for (let cellColumn = Math.floor(column - reach); cellColumn <= column + reach; cellColumn++) {\n if (cellRow < 0 || cellColumn < 0 || cellRow >= field.height || cellColumn >= field.width) continue\n const distance = Math.hypot(cellRow + 0.5 - row, cellColumn + 0.5 - column)\n if (distance >= width) continue\n const index = cellRow * field.width + cellColumn\n const target = amount * (1 - distance / width) * 0.5\n if (field.thickness[index]! < target) depositCell(context, cellRow, cellColumn, target - field.thickness[index]!)\n }\n }\n }\n}\n\n/**\n * The impact: the body is stretched by 1 / sin(angle), with spikes (long only along the travel), satellites and tails.\n * `size` is the radius in cells of the main body; `profile.spikes` below 1 turns the spikes short and the blobs thick.\n */\nexport function depositSplat(context: DepositContext, row: number, column: number, size: number, directionColumn: number, directionRow: number, sinAngleIn: number, amount: number): void {\n const random = context.random\n const spikes = context.profile().spikes\n const sinAngle = Math.max(0.17, Math.min(1, sinAngleIn))\n const stretch = Math.min(5.5, 1 / sinAngle)\n const along = size * Math.pow(stretch, 0.75)\n const across = size / Math.pow(stretch, 0.25)\n const frontRow = row + directionRow * along * 0.25\n const frontColumn = column + directionColumn * along * 0.25\n depositBlob(context, frontRow, frontColumn, along, across, directionColumn, directionRow, amount, 0.1 + 0.12 * (1 - sinAngle) + (size < 1.4 ? 0 : 0.06))\n const sideColumn = -directionRow\n const sideRow = directionColumn\n const spikeCount = Math.round(Math.min(18, (size < 1.2 ? 1 : 4) + size * 1.6 + 5 * (1 - sinAngle)) * spikes)\n for (let spike = 0; spike < spikeCount; spike++) {\n const spread = Math.PI * (0.25 + 0.75 * sinAngle * sinAngle)\n let theta = (random() * 2 - 1) * spread\n if (sinAngle > 0.9) theta = random() * 6.283\n const cosine = Math.cos(theta)\n const sine = Math.sin(theta)\n const dx = cosine * directionColumn + sine * sideColumn\n const dy = cosine * directionRow + sine * sideRow\n const length = size * (0.25 + random() * 0.55) * (1 + 2.6 * (1 - sinAngle) * Math.max(0, cosine)) * (sinAngle > 0.9 ? 0.6 : 1) * (0.5 + 0.5 * spikes)\n const startRow = frontRow + (along * cosine * directionRow + across * sine * sideRow) * 0.75\n const startColumn = frontColumn + (along * cosine * directionColumn + across * sine * sideColumn) * 0.75\n const endRow = startRow + dy * length\n const endColumn = startColumn + dx * length\n depositStroke(context, startRow, startColumn, endRow, endColumn, Math.max(0.55, size * 0.22), 0.42, amount * 0.75)\n if (random() < 0.5) {\n const gap = 1 + random() * 2.2\n depositBlob(context, endRow + dy * gap, endColumn + dx * gap, 0.55 + random() * 0.7, 0.5 + random() * 0.4, dx, dy, amount * 0.55, 0.08)\n }\n }\n const satelliteCount = Math.round((1 + size * 0.9) * (1.4 - sinAngle * 0.6) * (0.5 + 0.5 * spikes))\n for (let satellite = 0; satellite < satelliteCount; satellite++) {\n const cone = sinAngle > 0.9 ? Math.PI : 0.18 + 0.6 * sinAngle\n const theta = (random() * 2 - 1) * cone\n const cosine = Math.cos(theta)\n const sine = Math.sin(theta)\n const dx = cosine * directionColumn + sine * sideColumn\n const dy = cosine * directionRow + sine * sideRow\n const distance = along + size * (0.8 + random() * 3.2)\n const radius = 0.45 + random() * Math.min(1.2, size * 0.22)\n const satelliteRow = frontRow + dy * distance\n const satelliteColumn = frontColumn + dx * distance\n depositBlob(context, satelliteRow, satelliteColumn, radius * Math.sqrt(stretch), radius, dx, dy, amount * 0.6, 0.08)\n if (sinAngle < 0.8) depositStroke(context, satelliteRow + dy * radius * stretch * 0.7, satelliteColumn + dx * radius * stretch * 0.7, satelliteRow + dy * radius * stretch * 1.8, satelliteColumn + dx * radius * stretch * 1.8, 0.5, 0.3, amount * 0.35)\n }\n}\n","import type { Vec3 } from '../types'\nimport { depositBlob, depositSplat } from './deposit'\nimport type { DepositContext } from './deposit'\nimport type { FilmDropState, FilmLayout } from './types'\n\nconst GRAVITY = 9.8\nconst AIR_DRAG = 0.25\nconst MIN_SIZE_CELLS = 0.6\n\nexport interface DropPool {\n readonly state: FilmDropState\n launch(position: Vec3, velocity: Vec3, radius: number): void\n update(dt: number): void\n clear(): void\n}\n\n/** Drops in flight: a ring of slots (the oldest is overwritten), a ballistic flight and a landing that makes an impact shaped by the angle and direction (F250-278). */\nexport function createDropPool(layout: FilmLayout, context: DepositContext): DropPool {\n const capacity = layout.rules.dropCapacity\n let cursor = 0\n const state: FilmDropState = {\n capacity,\n get launched() {\n return cursor\n },\n active: new Uint8Array(capacity),\n position: new Float64Array(capacity * 3),\n velocity: new Float64Array(capacity * 3),\n radius: new Float64Array(capacity),\n }\n const { active, position, velocity, radius } = state\n const cellMillimetres = layout.cellSize * 1000\n const previous: Vec3 = [0, 0, 0]\n const point: Vec3 = [0, 0, 0]\n const speed: Vec3 = [0, 0, 0]\n\n function land(region: number, dropRadius: number): void {\n const profile = context.profile()\n const impact = layout.impact(region, point, speed)\n const [alongColumn, alongRow] = impact.along\n const travel = Math.hypot(alongColumn, alongRow) || 1e-6\n const sinAngle = Math.abs(impact.normalSpeed) / Math.hypot(travel, impact.normalSpeed)\n const volume = ((4 / 3) * Math.PI * dropRadius * dropRadius * dropRadius) / (cellMillimetres * cellMillimetres) * (dropRadius > 3 ? 7 : 4) * profile.volume\n const totalSpeed = Math.sqrt(speed[0] * speed[0] + speed[1] * speed[1] + speed[2] * speed[2])\n const size = Math.max(MIN_SIZE_CELLS, (dropRadius * 2) / cellMillimetres * (1 + 0.2 * totalSpeed))\n const amount = Math.min(profile.maxAmount, Math.max(0.25, volume / ((Math.PI * size * size * 1.6) / (sinAngle < 0.3 ? 0.6 : 1))))\n if (profile.landsAsBlob) {\n depositBlob(context, impact.row, impact.column, size * 1.3, size * 1.1, alongColumn / travel, alongRow / travel, amount, 0.2)\n return\n }\n depositSplat(context, impact.row, impact.column, size, alongColumn / travel, alongRow / travel, sinAngle, amount)\n }\n\n return {\n state,\n launch(origin, direction, dropRadius) {\n const slot = cursor++ % capacity\n const offset = slot * 3\n active[slot] = 1\n position[offset] = origin[0]\n position[offset + 1] = origin[1]\n position[offset + 2] = origin[2]\n velocity[offset] = direction[0]\n velocity[offset + 1] = direction[1]\n velocity[offset + 2] = direction[2]\n radius[slot] = dropRadius\n },\n update(dt) {\n for (let slot = 0; slot < capacity; slot++) {\n if (!active[slot]) continue\n const offset = slot * 3\n previous[0] = position[offset]!\n previous[1] = position[offset + 1]!\n previous[2] = position[offset + 2]!\n velocity[offset + 1]! -= GRAVITY * dt\n const drag = 1 - dt * AIR_DRAG\n velocity[offset]! *= drag\n velocity[offset + 1]! *= drag\n velocity[offset + 2]! *= drag\n point[0] = position[offset]! + velocity[offset]! * dt\n point[1] = position[offset + 1]! + velocity[offset + 1]! * dt\n point[2] = position[offset + 2]! + velocity[offset + 2]! * dt\n const region = layout.collide(previous, point)\n position[offset] = point[0]\n position[offset + 1] = point[1]\n position[offset + 2] = point[2]\n if (region >= 0) {\n if (layout.contains(point)) {\n speed[0] = velocity[offset]!\n speed[1] = velocity[offset + 1]!\n speed[2] = velocity[offset + 2]!\n land(region, radius[slot]!)\n }\n active[slot] = 0\n continue\n }\n if (layout.escaped(point)) active[slot] = 0\n }\n },\n clear() {\n active.fill(0)\n },\n }\n}\n","import type { FilmAdvanceInput, FilmBounds, FilmField, FilmLayout, FilmProfile, FilmSimulator, FilmWeather } from './types'\n\nexport interface CpuSimulatorOptions {\n /** Flow steps per second of real time (the pages use 180). */\n stepRate: number\n /** Most flow steps in one advance (6 in the Fluid Standard, 9 in the Workshop). */\n maxSteps: number\n weather: () => FilmWeather\n}\n\nconst RAIN_SPREAD = 0.5\nconst BOUNDS_MARGIN = 8\n\n/**\n * The thin-film step of the Fluid Standard (F94-114), literal: only film above the pin moves, a dry cell needs the front thickness,\n * walls run sideways at 0.22 and the flow is limited to the wet bounding box. Drying starts from the thin edge.\n */\nexport function createCpuSimulator(field: FilmField, layout: FilmLayout, profileOf: () => FilmProfile, options: CpuSimulatorOptions): FilmSimulator {\n const { width, height, thickness, dryness, dampness } = field\n const { bed, pin: pinning, vertical } = layout\n const delta = new Float32Array(width * height)\n const bounds: FilmBounds = { firstRow: 0, lastRow: -1, firstColumn: 0, lastColumn: -1 }\n let accumulated = 0\n\n function refreshBounds(): void {\n let firstRow = height\n let lastRow = -1\n let firstColumn = width\n let lastColumn = -1\n for (let row = 0; row < height; row++) {\n const offset = row * width\n let isWet = false\n for (let column = 0; column < width; column++) {\n if (thickness[offset + column]! > 0) {\n isWet = true\n if (column < firstColumn) firstColumn = column\n if (column > lastColumn) lastColumn = column\n }\n }\n if (isWet) {\n if (row < firstRow) firstRow = row\n lastRow = row\n }\n }\n if (lastRow < 0) {\n bounds.firstRow = 0\n bounds.lastRow = -1\n bounds.firstColumn = 0\n bounds.lastColumn = -1\n return\n }\n bounds.firstRow = Math.max(0, firstRow - BOUNDS_MARGIN)\n bounds.lastRow = Math.min(height - 1, lastRow + BOUNDS_MARGIN)\n bounds.firstColumn = Math.max(0, firstColumn - BOUNDS_MARGIN)\n bounds.lastColumn = Math.min(width - 1, lastColumn + BOUNDS_MARGIN)\n }\n\n const mobility = (index: number): number => {\n const dry = dryness[index]!\n return dry < 0.25 ? 1 : dry > 0.6 ? 0 : 1 - (dry - 0.25) / 0.35\n }\n\n function flowStep(): void {\n const { firstRow, lastRow, firstColumn, lastColumn } = bounds\n if (lastRow < 0) return\n const profile = profileOf()\n const flow = profile.flow * (1 + RAIN_SPREAD * options.weather().rain)\n const referenceSquared = profile.referenceThickness * profile.referenceThickness\n const slopeCap = profile.slopeCap\n const pinLevel = profile.pin\n const front = profile.front\n for (let row = firstRow; row <= lastRow; row++) {\n const offset = row * width\n for (let column = firstColumn; column <= lastColumn; column++) delta[offset + column] = 0\n }\n for (let row = firstRow; row <= lastRow; row++) {\n const offset = row * width\n for (let column = firstColumn; column <= lastColumn; column++) {\n const index = offset + column\n const own = thickness[index]!\n for (let edge = 0; edge < 2; edge++) {\n let neighbour: number\n if (edge === 0) {\n if (column >= lastColumn) continue\n neighbour = index + 1\n } else {\n if (row >= lastRow) continue\n neighbour = index + width\n }\n const other = thickness[neighbour]!\n if (own < 0.005 && other < 0.005) continue\n let bedDrop = bed[index]! - bed[neighbour]!\n if (bedDrop > slopeCap) bedDrop = slopeCap\n else if (bedDrop < -slopeCap) bedDrop = -slopeCap\n let head = bedDrop + own - other\n let source = index\n let destination = neighbour\n if (head < 0) {\n head = -head\n source = neighbour\n destination = index\n }\n const sourceThickness = thickness[source]!\n const verticalFactor = vertical[source]!\n const pinThreshold = pinLevel * pinning[source]! * (verticalFactor < 1 ? 0.5 : 1)\n if (sourceThickness <= pinThreshold) continue\n if (thickness[destination]! < 0.03 && sourceThickness < front * pinning[source]! * verticalFactor) continue\n const mobile = mobility(source)\n if (mobile <= 0) continue\n let moved = flow * mobile * ((sourceThickness * sourceThickness) / (sourceThickness * sourceThickness + referenceSquared)) * head * (verticalFactor < 1 ? (edge === 0 ? 0.22 : 1) : 4)\n const limit = (sourceThickness - pinThreshold) * 0.24\n if (moved > limit) moved = limit\n delta[source]! -= moved\n delta[destination]! += moved\n }\n }\n }\n for (let row = firstRow; row <= lastRow; row++) {\n const offset = row * width\n for (let column = firstColumn; column <= lastColumn; column++) {\n const index = offset + column\n let next = thickness[index]! + delta[index]!\n if (next < 1e-4) next = 0\n else dampness[index] = 1\n thickness[index] = next\n }\n }\n }\n\n function age(dryingSeconds: number): void {\n const { firstRow, lastRow, firstColumn, lastColumn } = bounds\n if (lastRow < 0) return\n const profile = profileOf()\n const rainScale = 1 - options.weather().rain\n const seconds = dryingSeconds * rainScale\n for (let row = firstRow; row <= lastRow; row++) {\n const offset = row * width\n for (let column = firstColumn; column <= lastColumn; column++) {\n const index = offset + column\n const own = thickness[index]!\n if (own > 0) {\n const thin = 1 - Math.min(1, own / 1.6)\n if (profile.dryTime > 0 && dryness[index]! < 1) dryness[index] = Math.min(1, dryness[index]! + (seconds / profile.dryTime) * (0.45 + 1.7 * thin))\n if (profile.evaporation > 0) {\n const next = own - profile.evaporation * seconds * (0.5 + 1.5 * thin)\n thickness[index] = next > 0.004 ? next : 0\n }\n } else if (dampness[index]! > 0) {\n dampness[index] = Math.max(0, dampness[index]! - seconds / (profile.evaporation > 0 ? 25 : 1e9))\n }\n }\n }\n }\n\n return {\n bounds,\n refreshBounds,\n advance({ dt, dryingSeconds }: FilmAdvanceInput): number {\n accumulated += dt\n let steps = 0\n refreshBounds()\n while (accumulated >= 1 / options.stepRate && steps < options.maxSteps) {\n flowStep()\n accumulated -= 1 / options.stepRate\n steps++\n }\n if (steps === options.maxSteps) accumulated = 0\n age(dryingSeconds)\n return steps\n },\n resetClock() {\n accumulated = 0\n },\n }\n}\n","import type { Vec3 } from '../types'\nimport { depositCell } from './deposit'\nimport type { DepositContext } from './deposit'\nimport type { FilmBead, FilmDrag, FilmDrip, FilmLayout, FilmRandom, FilmSource } from './types'\n\ntype Launch = (position: Vec3, velocity: Vec3, radius: number) => void\n\nconst range = (random: FilmRandom, from: number, to: number): number => from + random() * (to - from)\n\n/** The drip (F282, W-style): a bead swells at the fingertip, detaches and falls. Updates `bead` for the renderer and returns nothing. */\nexport function updateDrip(drip: FilmDrip | undefined, bead: FilmBead, layout: FilmLayout, random: FilmRandom, launch: Launch, dt: number): void {\n if (!drip) {\n bead.visible = false\n return\n }\n drip.elapsed += dt\n const swell = Math.min(1, drip.elapsed / drip.every)\n const radius = drip.radius * (0.35 + 0.65 * swell)\n const isWorkshop = layout.rules.dripStyle === 'workshop'\n bead.visible = true\n bead.swell = swell\n bead.radius = radius\n bead.position[0] = drip.position[0]\n bead.position[1] = drip.position[1] + (isWorkshop ? -radius * 0.0012 * (1 + swell * 0.7) : -radius * 0.0012)\n bead.position[2] = drip.position[2]\n if (drip.elapsed < drip.every) return\n const origin: Vec3 = [bead.position[0], bead.position[1] + -radius * 0.001, bead.position[2]]\n if (isWorkshop) {\n const sideX = range(random, -0.025, 0.025)\n const sideZ = range(random, -0.025, 0.025)\n launch(origin, [sideX, -0.2, sideZ], drip.radius * range(random, 0.85, 1.15))\n drip.elapsed = 0\n drip.every = drip.base * range(random, 0.75, 1.25)\n return\n }\n const sideX = (random() - 0.5) * 0.05\n const sideZ = (random() - 0.5) * 0.05\n launch(origin, [sideX, -0.2, sideZ], drip.radius * (0.85 + random() * 0.3))\n drip.elapsed = 0\n drip.every = drip.base * (0.75 + random() * 0.5)\n}\n\n/** Continuous sources (a wound at the feet, a tipped can): a pulsing thickness added over a small disc (F288). */\nexport function updateSources(sources: readonly FilmSource[], context: DepositContext, dt: number): void {\n const random = context.random\n for (const source of sources) {\n source.elapsed += dt\n if (source.elapsed < 0 || source.elapsed > source.duration) continue\n const pulse = 0.6 + 0.4 * Math.max(0, Math.sin(source.elapsed * 5.2))\n const amount = source.rate * dt * pulse\n for (let part = 0; part < 3; part++) {\n const angle = random() * 6.28\n const distance = random() * source.radius\n depositCell(context, source.row + Math.sin(angle) * distance, source.column + Math.cos(angle) * distance, amount / 3)\n }\n }\n}\n\n/** Drag smears (F291): a striped band that loses load and crumbles at the end, with side drops stretched along the travel. */\nexport function updateDrags(drags: readonly FilmDrag[], context: DepositContext, layout: FilmLayout, launch: Launch, dt: number): void {\n const random = context.random\n for (const drag of drags) {\n if (drag.done) continue\n drag.elapsed += dt\n const progress = Math.min(1, drag.elapsed / drag.duration)\n const here = drag.path(progress)\n const ahead = drag.path(Math.min(1, progress + 0.01))\n let directionColumn = ahead.column - here.column\n let directionRow = ahead.row - here.row\n const length = Math.hypot(directionColumn, directionRow) || 1\n directionColumn /= length\n directionRow /= length\n const sideColumn = -directionRow\n const sideRow = directionColumn\n const load = Math.pow(1 - progress, 1.3)\n for (let offset = -drag.width; offset <= drag.width; offset += 0.5) {\n const across = offset / drag.width\n const stripe = drag.stripes[Math.round((offset + drag.width) * 2)]!\n const edge = 1 - across * across\n const amount = drag.amount * load * (0.25 + 0.75 * stripe) * edge\n if (load < 0.45 && random() > load * 2.2) continue\n depositCell(context, Math.round(here.row + sideRow * offset), Math.round(here.column + sideColumn * offset), amount * dt * 30)\n }\n if (drag.hand && random() < dt * 14 * load + 0.02) {\n const world = layout.cellToWorld(here.row, here.column)\n const origin: Vec3 = [world[0] + (random() - 0.5) * 0.06, drag.hand.height + random() * 0.3, world[2] + (random() - 0.5) * 0.06]\n const velocity: Vec3 = [directionColumn * drag.hand.speed, 0 * drag.hand.speed, directionRow * drag.hand.speed]\n velocity[0] += (random() - 0.5) * 0.3\n velocity[1] += 0.2\n velocity[2] += (random() - 0.5) * 0.3\n launch(origin, velocity, 2 + random() * 2.2)\n }\n if (progress >= 1) drag.done = true\n }\n}\n","import { depositBlob, depositCell, depositSplat, depositStroke, wetCellCount } from './deposit'\nimport type { DepositContext } from './deposit'\nimport { createDropPool } from './drops'\nimport { createCpuSimulator } from './simulate'\nimport { updateDrags, updateDrip, updateSources } from './sources'\nimport type { Film, FilmField, FilmOptions, FilmProfile, FilmWeather } from './types'\n\nconst DEFAULT_STEP_RATE = 180\nconst DEFAULT_MAX_STEPS = 6\n\n/**\n * A running film of one liquid over one layout: drops land as impacts, a drip, sources and drag smears add to it, and a 180 Hz\n * thin-film step spreads it limited to the wet box, at most `maxSteps` per frame. It never calls `Math.random`: the host gives the random stream.\n */\nexport function createFilm(options: FilmOptions): Film {\n const { layout, random } = options\n const cellCount = layout.width * layout.height\n const field: FilmField = {\n width: layout.width,\n height: layout.height,\n thickness: new Float32Array(cellCount),\n dryness: new Float32Array(cellCount),\n dampness: new Float32Array(cellCount),\n }\n let weatherState: FilmWeather = { rain: 0 }\n let currentProfile: FilmProfile = options.profile\n const context: DepositContext = { field, random, profile: () => currentProfile }\n const drops = createDropPool(layout, context)\n const createSimulator = options.simulator ?? createCpuSimulator\n const simulator = createSimulator(field, layout, () => currentProfile, {\n stepRate: options.stepRate ?? DEFAULT_STEP_RATE,\n maxSteps: options.maxSteps ?? DEFAULT_MAX_STEPS,\n weather: () => weatherState,\n })\n\n const film: Film = {\n layout,\n field,\n drops: drops.state,\n bead: { visible: false, position: [0, 0, 0], radius: 0, swell: 0 },\n simulator,\n get profile() {\n return currentProfile\n },\n set profile(profile: FilmProfile) {\n currentProfile = profile\n },\n drip: undefined,\n sources: [],\n drags: [],\n launch: (position, velocity, radius) => drops.launch(position, velocity, radius),\n splat: (row, column, size, directionColumn, directionRow, sinAngle, amount) => depositSplat(context, row, column, size, directionColumn, directionRow, sinAngle, amount),\n blob: (row, column, along, across, directionColumn, directionRow, amount, roughness) => depositBlob(context, row, column, along, across, directionColumn, directionRow, amount, roughness),\n stroke: (fromRow, fromColumn, toRow, toColumn, startWidth, endWidth, amount) => depositStroke(context, fromRow, fromColumn, toRow, toColumn, startWidth, endWidth, amount),\n deposit: (row, column, amount) => depositCell(context, row, column, amount),\n update({ dt, dryingSeconds }) {\n drops.update(dt)\n updateDrip(film.drip, film.bead, layout, random, drops.launch, dt)\n updateSources(film.sources, context, dt)\n updateDrags(film.drags, context, layout, drops.launch, dt)\n simulator.advance({ dt, dryingSeconds })\n },\n setWeather(weather) {\n weatherState = { rain: Math.min(1, Math.max(0, weather.rain ?? weatherState.rain)) }\n },\n weather: () => ({ ...weatherState }),\n clear() {\n field.thickness.fill(0)\n field.dryness.fill(0)\n field.dampness.fill(0)\n drops.clear()\n film.drip = undefined\n film.bead.visible = false\n film.sources.length = 0\n film.drags.length = 0\n simulator.refreshBounds()\n },\n wetCells: () => wetCellCount(field),\n }\n return film\n}\n","import type { FilmRandom } from './types'\n\nexport type FilmLcg = FilmRandom & { seed: number }\n\n/** The Fluid Standard's generator (LCG 1664525 / 1013904223). `seed` can be read and set, so a scene can be restarted from a number. */\nexport function createFilmLcg(seed: number): FilmLcg {\n const lcg = (() => {\n lcg.seed = (lcg.seed * 1664525 + 1013904223) >>> 0\n return lcg.seed / 4294967296\n }) as FilmLcg\n lcg.seed = seed >>> 0\n return lcg\n}\n","// The product is taken in doubles on purpose (not `Math.imul`): the recorded pages round it the same way, and the grids depend on it.\nexport function hashCell(x: number, y: number): number {\n let value = (x * 374761393 + y * 668265263) | 0\n value = ((value ^ (value >>> 13)) * 1274126177) | 0\n return ((value ^ (value >>> 16)) >>> 0) / 4294967296\n}\n\nexport function valueNoise(x: number, y: number): number {\n const xi = Math.floor(x)\n const yi = Math.floor(y)\n const xf = x - xi\n const yf = y - yi\n const u = xf * xf * (3 - 2 * xf)\n const v = yf * yf * (3 - 2 * yf)\n const a = hashCell(xi, yi)\n const b = hashCell(xi + 1, yi)\n const c = hashCell(xi, yi + 1)\n const d = hashCell(xi + 1, yi + 1)\n return a + (b - a) * u + (c - a) * v + (a - b - c + d) * u * v\n}\n","import type { Vec3 } from '../types'\nimport { hashCell, valueNoise } from './noise'\nimport type { FilmImpact, FilmLayout, FilmRegion, FilmRules } from './types'\n\nexport interface FoldedLayoutParams {\n id: string\n columns: number\n /** Metres per cell. */\n cellSize: number\n /** World x of the left edge. */\n originX: number\n wallRows: number\n sidewalkRows: number\n curbRows: number\n roadRows: number\n /** World y of the top of the wall strip and of the sidewalk and road planes. */\n wallTopY: number\n sidewalkY: number\n roadY: number\n /** World z of the wall plane (the sidewalk starts here and runs towards +z). */\n wallZ: number\n /** Metres the curb face drops per row. Default `(sidewalkY - roadY) / curbRows`. */\n curbStep?: number\n /** x that the road's cross slope is measured from (the road falls `5 mm / m` towards +x from here). */\n roadSlopeAnchorX: number\n rules: FilmRules\n}\n\nconst REGION_NAMES = ['wall', 'sidewalk', 'curb', 'road'] as const\n\nconst dot3 = (a: Vec3, b: Vec3): number => a[0] * b[0] + a[1] * b[1] + a[2] * b[2]\n\nfunction impactOf(region: FilmRegion, position: Vec3, velocity: Vec3, cellSize: number): FilmImpact {\n const local: Vec3 = [position[0] - region.origin[0], position[1] - region.origin[1], position[2] - region.origin[2]]\n return {\n row: region.firstRow + dot3(local, region.rowAxis) / cellSize,\n column: dot3(local, region.columnAxis) / cellSize,\n along: [dot3(velocity, region.columnAxis), dot3(velocity, region.rowAxis)],\n normalSpeed: -dot3(velocity, region.normal),\n }\n}\n\nfunction cellToWorldOf(regions: readonly FilmRegion[], cellSize: number, row: number, column: number): Vec3 {\n let region = regions[regions.length - 1]!\n for (const candidate of regions) {\n if (row < candidate.firstRow + candidate.rowCount) {\n region = candidate\n break\n }\n }\n const across = column * cellSize\n const down = (row - region.firstRow) * cellSize\n return [\n region.origin[0] + region.columnAxis[0] * across + region.rowAxis[0] * down,\n region.origin[1] + region.columnAxis[1] * across + region.rowAxis[1] * down,\n region.origin[2] + region.columnAxis[2] * across + region.rowAxis[2] * down,\n ]\n}\n\n/**\n * One strip folded over a wall, the sidewalk in front of it, the curb face and the road: the Fluid Standard's grid (F12-42) and the\n * Workshop's (W562-582) are two settings of it. Bed height (mm), pinning noise and the vertical flag are filled per cell.\n */\nexport function createFoldedLayout(params: FoldedLayoutParams): FilmLayout {\n const { columns, cellSize, originX, wallRows, sidewalkRows, curbRows, roadRows, wallTopY, sidewalkY, roadY, wallZ, rules } = params\n const curbStep = params.curbStep ?? (sidewalkY - roadY) / curbRows\n const height = wallRows + sidewalkRows + curbRows + roadRows\n const cellCount = columns * height\n const bed = new Float32Array(cellCount)\n const pin = new Float32Array(cellCount)\n const vertical = new Float32Array(cellCount)\n const sidewalkEnd = wallRows + sidewalkRows\n const curbEnd = sidewalkEnd + curbRows\n const regionOfRow = (row: number): number => (row < wallRows ? 0 : row < sidewalkEnd ? 1 : row < curbEnd ? 2 : 3)\n\n for (let row = 0; row < height; row++) {\n for (let column = 0; column < columns; column++) {\n const index = row * columns + column\n const region = regionOfRow(row)\n const x = originX + (column + 0.5) * cellSize\n let bedHeight: number\n let roughness = 0.2\n if (region === 0) {\n bedHeight = (wallTopY - (row + 0.5) * cellSize) * 1000\n roughness = 0.3\n } else if (region === 1) {\n const z = (row - wallRows + 0.5) * cellSize\n bedHeight = sidewalkY * 1000 - z * 6\n const jointX = Math.abs((((x - originX) % 0.5) + 0.5) % 0.5 - 0.25)\n const jointZ = Math.abs(((z % 0.5) + 0.5) % 0.5 - 0.25)\n if (jointX > 0.25 - cellSize * 0.6 || jointZ > 0.25 - cellSize * 0.6) bedHeight -= 0.9\n roughness = 0.18\n } else if (region === 2) {\n bedHeight = sidewalkY * 1000 - (row - sidewalkEnd + 0.5) * curbStep * 1000\n } else {\n const z = (row - curbEnd + 0.5) * cellSize\n bedHeight = roadY * 1000 + z * 14 - (x - params.roadSlopeAnchorX) * 5\n roughness = 0.55\n }\n const noise = valueNoise(column * 0.35, row * 0.35) * 0.6 + valueNoise(column * 1.3, row * 1.3) * 0.4\n bed[index] = bedHeight + (noise - 0.5) * roughness * 2\n vertical[index] = region === 0 || region === 2 ? 0.95 : 1\n pin[index] = 0.62 + 0.76 * (valueNoise(column * 0.21 + 40, row * 0.21) * 0.5 + hashCell(column, row) * 0.5) * (region === 3 ? 1.25 : 1)\n }\n }\n\n const curbZ = wallZ + sidewalkRows * cellSize\n const roadEndZ = curbZ + roadRows * cellSize\n const right = [1, 0, 0] as Vec3\n const regions: FilmRegion[] = [\n { id: REGION_NAMES[0], firstRow: 0, rowCount: wallRows, origin: [originX, wallTopY, wallZ], columnAxis: right, rowAxis: [0, -1, 0], normal: [0, 0, 1], isVertical: true },\n { id: REGION_NAMES[1], firstRow: wallRows, rowCount: sidewalkRows, origin: [originX, sidewalkY, wallZ], columnAxis: right, rowAxis: [0, 0, 1], normal: [0, 1, 0], isVertical: false },\n { id: REGION_NAMES[2], firstRow: sidewalkEnd, rowCount: curbRows, origin: [originX, sidewalkY, curbZ], columnAxis: right, rowAxis: [0, -1, 0], normal: [0, 0, 1], isVertical: true },\n { id: REGION_NAMES[3], firstRow: curbEnd, rowCount: roadRows, origin: [originX, roadY, curbZ], columnAxis: right, rowAxis: [0, 0, 1], normal: [0, 1, 0], isVertical: false },\n ]\n const maxX = originX + columns * cellSize\n\n return {\n id: params.id,\n width: columns,\n height,\n cellSize,\n bed,\n pin,\n vertical,\n regions,\n rules,\n cellToWorld: (row, column) => cellToWorldOf(regions, cellSize, row, column),\n collide(previous, position) {\n if (position[2] <= wallZ && position[1] > sidewalkY) {\n position[2] = wallZ\n return 0\n }\n if (position[2] < curbZ && (!rules.guardBehindWall || position[2] >= wallZ) && position[1] <= sidewalkY) {\n position[1] = sidewalkY\n return 1\n }\n if (rules.curbCatch && position[2] >= curbZ && previous[2] < curbZ && position[1] < sidewalkY) {\n position[2] = curbZ\n return 2\n }\n if (position[2] >= curbZ && position[1] <= roadY) {\n position[1] = roadY\n return 3\n }\n return -1\n },\n impact: (region, position, velocity) => impactOf(regions[region]!, position, velocity, cellSize),\n contains: (position) => position[0] > originX && position[0] < maxX && (!rules.clipRoadEnd || position[2] < roadEndZ),\n escaped(position) {\n if (position[1] < -1) return true\n const box = rules.escapeBox\n return box !== undefined && (position[0] < box.minX || position[0] > box.maxX || position[2] > box.maxZ)\n },\n }\n}\n\nconst PADRAO_CELL = 0.0125\nconst PADRAO_WALL_Z = 0\n\n/** The Fluid Standard's grid: 256 x 396 cells of 1.25 cm over a 1.95 m wall, a 1.6 m sidewalk, a 15 cm curb and a 1.4 m road. */\nexport const PADRAO_LAYOUT: FilmLayout = createFoldedLayout({\n id: 'padrao',\n columns: 256,\n cellSize: PADRAO_CELL,\n originX: -1.6,\n wallRows: 144,\n sidewalkRows: 128,\n curbRows: 12,\n roadRows: 112,\n wallTopY: 1.95,\n sidewalkY: 0.15,\n roadY: 0,\n wallZ: PADRAO_WALL_Z,\n curbStep: PADRAO_CELL,\n roadSlopeAnchorX: 0,\n rules: {\n dropCapacity: 600,\n curbCatch: true,\n guardBehindWall: true,\n clipRoadEnd: true,\n escapeBox: { minX: -3, maxX: 3, maxZ: PADRAO_WALL_Z + 128 * PADRAO_CELL + 112 * PADRAO_CELL + 1 },\n dripStyle: 'padrao',\n },\n})\n\nconst WORKSHOP_CELL = 0.0125\n\n/** The Workshop's grid: 160 x 448 cells (wall 160, sidewalk 128, curb 9, road 151) at the foot of the workshop's wall. */\nexport const WORKSHOP_LAYOUT: FilmLayout = createFoldedLayout({\n id: 'workshop',\n columns: 160,\n cellSize: WORKSHOP_CELL,\n originX: -2.2,\n wallRows: 160,\n sidewalkRows: 128,\n curbRows: 9,\n roadRows: 151,\n wallTopY: 2.12,\n sidewalkY: 0.1235,\n roadY: 0.009,\n wallZ: -4.497,\n roadSlopeAnchorX: -1.2,\n rules: { dropCapacity: 400, curbCatch: false, guardBehindWall: false, clipRoadEnd: false, dripStyle: 'workshop' },\n})\n","import { add, cross, dot, normalize, scale, sub } from '../math'\nimport { makeBasis } from '../stains'\nimport type { SurfaceSpec, Vec3 } from '../types'\nimport { valueNoise } from './noise'\nimport type { FilmLayout, FilmRegion, FilmRules } from './types'\n\nexport interface SurfaceLayoutOptions {\n /** The surface the film lies on (a floor, a table, a ramp). */\n floor: SurfaceSpec\n /** A wall that meets the floor: the film folds up it, so a splash on the wall runs down onto the floor. */\n wall?: SurfaceSpec\n /** The lower surface beyond the floor's far edge: the film folds down a curb face to it. Needs the wall or the floor's tangent to say where the edge is. */\n curb?: SurfaceSpec\n /** World point the grid is centred on (sideways; and along the floor when there is no wall). */\n center: Vec3\n /** Cells across (default 160: 2 m of 1.25 cm cells, the grid of the near-player manager). */\n columns?: number\n /** Cells along all strips together (default 160). */\n rows?: number\n /** Metres per cell (default 0.0125). */\n cellSize?: number\n /** Rows of the wall strip (default 48 with a wall, else 0) and cap of the curb face rows (default 12). */\n wallRows?: number\n curbRows?: number\n}\n\nconst DEFAULT_COLUMNS = 160\nconst DEFAULT_ROWS = 160\nconst DEFAULT_CELL = 0.0125\nconst DEFAULT_WALL_ROWS = 48\nconst DEFAULT_CURB_ROWS = 12\nconst RULES: FilmRules = { dropCapacity: 160, curbCatch: false, guardBehindWall: false, clipRoadEnd: false, dripStyle: 'padrao' }\n\nconst along3 = (origin: Vec3, axis: Vec3, distance: number): Vec3 => add(origin, scale(axis, distance))\n\n/** Removes the part of `vector` along the unit `axis`. */\nconst reject = (vector: Vec3, axis: Vec3): Vec3 => sub(vector, scale(axis, dot(vector, axis)))\n\ninterface Strip {\n id: string\n rows: number\n origin: Vec3\n rowAxis: Vec3\n normal: Vec3\n isVertical: boolean\n roughness: number\n}\n\n/**\n * Builds the folded grid from the host's surfaces: `wall` (optional) hangs above the fold line, `floor` runs away from the wall, and\n * `curb` (optional) is the lower surface the floor drops to over a curb face. Without a wall the grid is a flat patch of the floor around `center`.\n * Bed heights come from world y, so a tilted floor makes the film run downhill.\n */\nexport function layoutFromSurfaces(options: SurfaceLayoutOptions): FilmLayout {\n const columns = options.columns ?? DEFAULT_COLUMNS\n const totalRows = options.rows ?? DEFAULT_ROWS\n const cell = options.cellSize ?? DEFAULT_CELL\n const floorBasis = makeBasis(options.floor.normal, options.floor.tangent)\n const floorNormal = floorBasis.normal\n const projected = sub(options.center, scale(floorNormal, dot(sub(options.center, options.floor.point), floorNormal)))\n const wallNormal = options.wall ? normalize(reject(options.wall.normal, floorNormal), floorBasis.t2) : undefined\n const awayAxis = wallNormal ?? floorBasis.t2\n const columnAxis = wallNormal ? normalize(cross(floorNormal, wallNormal), floorBasis.t1) : floorBasis.t1\n const wallRows = options.wall ? Math.min(totalRows - 1, options.wallRows ?? DEFAULT_WALL_ROWS) : 0\n\n let fold = projected\n if (options.wall && wallNormal) {\n const toWall = dot(sub(projected, options.wall.point), wallNormal) / (dot(awayAxis, wallNormal) || 1)\n fold = sub(projected, scale(awayAxis, toWall))\n } else {\n fold = along3(projected, awayAxis, -(totalRows * cell) / 2)\n }\n const lateral = dot(sub(projected, fold), columnAxis) - (columns * cell) / 2\n const base = along3(fold, columnAxis, lateral)\n\n const strips: Strip[] = []\n const roughness = 0.18 + 0.4 * options.floor.porosity\n if (options.wall && wallNormal) {\n strips.push({ id: 'wall', rows: wallRows, origin: along3(base, floorNormal, wallRows * cell), rowAxis: scale(floorNormal, -1), normal: wallNormal, isVertical: true, roughness: 0.3 })\n }\n const lowerRows = totalRows - wallRows\n const dropMetres = options.curb ? Math.max(0, dot(sub(options.floor.point, options.curb.point), floorNormal)) : 0\n const curbRows = options.curb ? Math.min(options.curbRows ?? DEFAULT_CURB_ROWS, Math.max(1, Math.ceil(dropMetres / cell)), lowerRows - 2) : 0\n const floorRows = options.curb ? Math.max(1, Math.floor((lowerRows - curbRows) / 2)) : lowerRows\n strips.push({ id: 'floor', rows: floorRows, origin: base, rowAxis: awayAxis, normal: floorNormal, isVertical: false, roughness })\n if (options.curb) {\n const edge = along3(base, awayAxis, floorRows * cell)\n strips.push({ id: 'curb', rows: curbRows, origin: edge, rowAxis: scale(floorNormal, -1), normal: awayAxis, isVertical: true, roughness: 0.2 })\n const lowerOrigin = along3(edge, floorNormal, -curbRows * cell)\n strips.push({ id: 'low', rows: lowerRows - floorRows - curbRows, origin: lowerOrigin, rowAxis: awayAxis, normal: floorNormal, isVertical: false, roughness: 0.55 })\n }\n\n const regions: FilmRegion[] = []\n let firstRow = 0\n for (const strip of strips) {\n regions.push({ id: strip.id, firstRow, rowCount: strip.rows, origin: strip.origin, columnAxis, rowAxis: strip.rowAxis, normal: strip.normal, isVertical: strip.isVertical })\n firstRow += strip.rows\n }\n\n const height = firstRow\n const bed = new Float32Array(columns * height)\n const pin = new Float32Array(columns * height)\n const vertical = new Float32Array(columns * height)\n regions.forEach((region, regionIndex) => {\n const strip = strips[regionIndex]!\n for (let row = 0; row < region.rowCount; row++) {\n for (let column = 0; column < columns; column++) {\n const index = (region.firstRow + row) * columns + column\n const centre = add(add(region.origin, scale(columnAxis, (column + 0.5) * cell)), scale(region.rowAxis, (row + 0.5) * cell))\n const noise = valueNoise(column * 0.35, (region.firstRow + row) * 0.35) * 0.6 + valueNoise(column * 1.3, (region.firstRow + row) * 1.3) * 0.4\n bed[index] = centre[1] * 1000 + (noise - 0.5) * strip.roughness * 2\n vertical[index] = region.isVertical ? 0.95 : 1\n pin[index] = 0.62 + 0.76 * (valueNoise(column * 0.21 + 40, (region.firstRow + row) * 0.21) * 0.5 + valueNoise(column * 0.9, row * 0.9) * 0.5)\n }\n }\n })\n\n const lowestY = Math.min(...regions.map((region) => region.origin[1] - (region.rowAxis[1] > 0 ? 0 : region.rowCount * cell * Math.abs(region.rowAxis[1])))) - 1\n const width = columns * cell\n const cellToWorld = (row: number, column: number): Vec3 => {\n const region = regions.find((candidate) => row < candidate.firstRow + candidate.rowCount) ?? regions[regions.length - 1]!\n return add(add(region.origin, scale(region.columnAxis, column * cell)), scale(region.rowAxis, (row - region.firstRow) * cell))\n }\n\n return {\n id: 'surfaces',\n width: columns,\n height,\n cellSize: cell,\n bed,\n pin,\n vertical,\n regions,\n rules: RULES,\n cellToWorld,\n collide(previous, position) {\n for (let index = 0; index < regions.length; index++) {\n const region = regions[index]!\n const before = dot(sub(previous, region.origin), region.normal)\n const after = dot(sub(position, region.origin), region.normal)\n if (before < 0 || after > 0) continue\n const local = sub(position, region.origin)\n const sideways = dot(local, region.columnAxis)\n const downward = dot(local, region.rowAxis)\n if (sideways < 0 || sideways >= width || downward < 0 || downward >= region.rowCount * cell) continue\n position[0] -= region.normal[0] * after\n position[1] -= region.normal[1] * after\n position[2] -= region.normal[2] * after\n return index\n }\n return -1\n },\n impact(regionIndex, position, velocity) {\n const region = regions[regionIndex]!\n const local = sub(position, region.origin)\n return {\n row: region.firstRow + dot(local, region.rowAxis) / cell,\n column: dot(local, region.columnAxis) / cell,\n along: [dot(velocity, region.columnAxis), dot(velocity, region.rowAxis)],\n normalSpeed: -dot(velocity, region.normal),\n }\n },\n contains: () => true,\n escaped: (position) => position[1] < lowestY,\n }\n}\n","[\n {\n \"id\": \"blood\",\n \"look\": \"blood\",\n \"flow\": 0.05,\n \"pin\": 0.33,\n \"front\": 0.58,\n \"referenceThickness\": 0.6,\n \"slopeCap\": 1.1,\n \"dryTime\": 600,\n \"evaporation\": 0,\n \"opacityRate\": 3.2,\n \"reflectance\": 0.045,\n \"normalScale\": 16,\n \"thin\": 7997966,\n \"thick\": 2753028,\n \"dry\": 3874068,\n \"dryThin\": 6960678,\n \"dropColor\": 7997965,\n \"dropOpacity\": 1,\n \"volume\": 1,\n \"spikes\": 1,\n \"landsAsBlob\": false,\n \"maxAmount\": 2.6,\n \"sources\": {\n \"padrao\": \"F46\",\n \"oficina\": \"W583\"\n }\n },\n {\n \"id\": \"old_blood\",\n \"look\": \"blood\",\n \"flow\": 0.032,\n \"pin\": 0.42,\n \"front\": 0.7,\n \"referenceThickness\": 0.7,\n \"slopeCap\": 1,\n \"dryTime\": 360,\n \"evaporation\": 0,\n \"opacityRate\": 3.8,\n \"reflectance\": 0.03,\n \"normalScale\": 14,\n \"thin\": 7085078,\n \"thick\": 2888717,\n \"dry\": 2758158,\n \"dryThin\": 4860444,\n \"dropColor\": 4854286,\n \"dropOpacity\": 1,\n \"volume\": 0.6,\n \"spikes\": 1,\n \"landsAsBlob\": false,\n \"maxAmount\": 2.6,\n \"sources\": {\n \"padrao\": \"F48\"\n }\n },\n {\n \"id\": \"bile\",\n \"look\": \"bile\",\n \"flow\": 0.02,\n \"pin\": 0.8,\n \"front\": 1.2,\n \"referenceThickness\": 1.2,\n \"slopeCap\": 0.9,\n \"dryTime\": 900,\n \"evaporation\": 0,\n \"opacityRate\": 7,\n \"reflectance\": 0.04,\n \"normalScale\": 12,\n \"thin\": 11121724,\n \"thick\": 7174171,\n \"dry\": 4080160,\n \"dryThin\": 5659180,\n \"dropColor\": 9083434,\n \"dropOpacity\": 1,\n \"volume\": 1.3,\n \"spikes\": 0.3,\n \"landsAsBlob\": false,\n \"maxAmount\": 4,\n \"sources\": {\n \"padrao\": \"F50\",\n \"oficina\": \"W584 (spikes 0.3 W578, cap 4 W691)\"\n }\n },\n {\n \"id\": \"motor_oil\",\n \"look\": \"oil\",\n \"flow\": 0.03,\n \"pin\": 0.12,\n \"front\": 0.22,\n \"referenceThickness\": 0.5,\n \"slopeCap\": 1,\n \"dryTime\": 0,\n \"evaporation\": 0,\n \"opacityRate\": 4.5,\n \"reflectance\": 0.06,\n \"normalScale\": 18,\n \"thin\": 1381135,\n \"thick\": 263173,\n \"dry\": 263173,\n \"dryThin\": 1381135,\n \"dropColor\": 657932,\n \"dropOpacity\": 1,\n \"volume\": 1,\n \"spikes\": 1,\n \"landsAsBlob\": true,\n \"maxAmount\": 2.6,\n \"sources\": {\n \"padrao\": \"F52\",\n \"oficina\": \"W585 (blob W691)\"\n }\n },\n {\n \"id\": \"fuel\",\n \"look\": \"fuel\",\n \"flow\": 0.2,\n \"pin\": 0.035,\n \"front\": 0.07,\n \"referenceThickness\": 0.2,\n \"slopeCap\": 1.2,\n \"dryTime\": 0,\n \"evaporation\": 0.02,\n \"opacityRate\": 0.25,\n \"reflectance\": 0.04,\n \"normalScale\": 0,\n \"thin\": 10129520,\n \"thick\": 7234630,\n \"dry\": 0,\n \"dryThin\": 0,\n \"dropColor\": 12103824,\n \"dropOpacity\": 0.45,\n \"volume\": 1.4,\n \"spikes\": 1,\n \"landsAsBlob\": true,\n \"maxAmount\": 2.6,\n \"sources\": {\n \"padrao\": \"F54\",\n \"oficina\": \"W586 (blob W691)\"\n }\n }\n]\n","import profilesData from '../data/film-profiles.json'\nimport type { LiquidId } from '../types'\nimport type { FilmProfile } from './types'\n\n/** Film profiles of the liquids that run as a film (F46-55, plus the Workshop's `spikes` and `landsAsBlob`). Data in `data/film-profiles.json`. */\nexport const FILM_PROFILES: FilmProfile[] = profilesData as unknown as FilmProfile[]\n\n/** The profile of a liquid with the given overrides, or undefined when the liquid has no film. */\nexport function filmProfileOf(id: LiquidId, overrides: Partial<FilmProfile> = {}): FilmProfile | undefined {\n const profile = FILM_PROFILES.find((entry) => entry.id === id)\n return profile ? { ...profile, ...overrides } : undefined\n}\n","import type * as THREE_NS from 'three'\nimport { STAIN_SAMPLES } from '@adatechnology/game-fluids'\nimport type { LiquidDef, StainState, Vec3 } from '@adatechnology/game-fluids'\nimport type { MaterialCache } from './materials'\nimport { stainColorOf } from './materials'\nimport type { QualityProfile } from './quality'\nimport { clamp } from './util'\nimport type { ThreeLib } from './types'\n\n/** Periodic Catmull-Rom of the 16 front radii: a smooth outline with `subdiv` points per sample. */\nexport const smoothOutline = (front: number[], subdiv: number, out?: number[]): number[] => {\n const n = front.length\n const res = out ?? []\n res.length = n * subdiv\n for (let i = 0; i < n; i++) {\n const p0 = front[(i + n - 1) % n]\n const p1 = front[i]\n const p2 = front[(i + 1) % n]\n const p3 = front[(i + 2) % n]\n for (let j = 0; j < subdiv; j++) {\n const t = j / subdiv\n const r = 0.5 * (2 * p1 + (-p0 + p2) * t + (2 * p0 - 5 * p1 + 4 * p2 - p3) * t * t + (-p0 + 3 * p1 - 3 * p2 + p3) * t * t * t)\n res[i * subdiv + j] = Math.max(0.004, r)\n }\n }\n return res\n}\n\ninterface Fan {\n mesh: THREE_NS.Mesh\n position: Float32Array\n color: Float32Array\n}\n\n/**\n * One stain: an outer film (the whole flow-front polygon, soft at the rim) and an inner glossy fan at `wetRadius` that recedes while it dries.\n * Both are triangle fans with preallocated buffers, lying on the surface plane a hair above it.\n */\nexport class StainVisual {\n readonly group: THREE_NS.Group\n private readonly outer: Fan\n private readonly inner: Fan\n private readonly ring: number\n private readonly radii: number[] = []\n private readonly tint: THREE_NS.Color\n stainId = ''\n signature = Number.NaN\n triangles = 0\n\n constructor(\n private readonly THREE: ThreeLib,\n profile: QualityProfile,\n readonly slot: number,\n ) {\n this.ring = STAIN_SAMPLES * profile.stainSubdiv\n this.group = new THREE.Group()\n this.group.name = 'fluid_stain'\n this.tint = new THREE.Color()\n this.outer = this.makeFan('fluid_stain_film', 1)\n this.inner = this.makeFan('fluid_stain_wet', 2)\n this.group.add(this.outer.mesh, this.inner.mesh)\n this.group.visible = false\n }\n\n private makeFan(name: string, order: number): Fan {\n const T = this.THREE\n const count = 1 + this.ring\n const position = new Float32Array(count * 3)\n const color = new Float32Array(count * 4)\n const normal = new Float32Array(count * 3)\n const geometry = new T.BufferGeometry()\n const p = new T.BufferAttribute(position, 3)\n const c = new T.BufferAttribute(color, 4)\n p.setUsage(T.DynamicDrawUsage)\n c.setUsage(T.DynamicDrawUsage)\n geometry.setAttribute('position', p)\n geometry.setAttribute('normal', new T.BufferAttribute(normal, 3))\n geometry.setAttribute('color', c)\n const index: number[] = []\n for (let i = 0; i < this.ring; i++) index.push(0, 1 + i, 1 + ((i + 1) % this.ring))\n geometry.setIndex(index)\n const mesh = new T.Mesh(geometry)\n mesh.name = name\n mesh.frustumCulled = false\n mesh.renderOrder = order\n return { mesh, position, color }\n }\n\n assign(def: LiquidDef, materials: MaterialCache): void {\n this.outer.mesh.material = materials.stainFilm(def)\n this.inner.mesh.material = materials.stainWet(def)\n }\n\n hide(): void {\n this.group.visible = false\n this.stainId = ''\n this.signature = Number.NaN\n this.triangles = 0\n }\n\n /** Rebuild the vertices from the stain state (skipped when nothing changed). */\n update(stain: StainState, def: LiquidDef, profile: QualityProfile): void {\n const sig =\n stain.center[0] * 3 +\n stain.center[1] * 5 +\n stain.center[2] * 7 +\n stain.front.reduce((s, v, i) => s + v * (i + 1), 0) * 11 +\n stain.wetness * 13 +\n stain.wetRadius * 17 +\n (stain.soaked / (stain.soaked + stain.liters + 1e-6)) * 19\n if (sig === this.signature && this.group.visible) return\n this.signature = sig\n\n const n: Vec3 = stain.normal ?? [0, 1, 0]\n const t1: Vec3 = stain.tangent ?? [1, 0, 0]\n const t2: Vec3 = [n[1] * t1[2] - n[2] * t1[1], n[2] * t1[0] - n[0] * t1[2], n[0] * t1[1] - n[1] * t1[0]]\n smoothOutline(stain.front, profile.stainSubdiv, this.radii)\n const ring = this.ring\n const lift = 0.0015 + 0.0004 * (this.slot % 6)\n const mean = stain.front.reduce((s, v) => s + v, 0) / stain.front.length\n const wet = clamp(stain.wetness, 0, 1)\n const soakRatio = stain.soaked / (stain.soaked + stain.liters + 1e-6)\n\n // Outer film: tinted by the liquid, darker while wet and where it soaked in, fainter once only the mark is left.\n this.tint.setHex(stain.tint?.dry ?? stainColorOf(def))\n const dark = 1 - 0.3 * wet - 0.2 * soakRatio\n const markAlpha = def.leavesMark ? 0.34 : 0.1\n const aMid = (markAlpha + (0.62 - markAlpha) * wet) * (0.75 + 0.25 * Math.min(1, stain.liters * 20 + soakRatio))\n this.writeFan(this.outer, stain.center, n, t1, t2, lift, 1, this.tint.r * dark, this.tint.g * dark, this.tint.b * dark, aMid, aMid * 0.45)\n this.outer.mesh.visible = true\n\n // Inner glossy wet fan, centred where the liquid came in, shrinking as it dries.\n const wr = clamp(stain.wetRadius, 0, 1)\n const showInner = wr > 0.05 && wet > 0.03\n this.inner.mesh.visible = showInner\n if (showInner) {\n this.tint.setHex(stain.tint?.fresh ?? def.color)\n const shift = stain.flow ? -mean * 0.1 * (1 - wr) : 0\n const centre: Vec3 = stain.flow\n ? [stain.center[0] + stain.flow[0] * shift, stain.center[1] + stain.flow[1] * shift, stain.center[2] + stain.flow[2] * shift]\n : stain.center\n const a = clamp(def.opacity, 0.2, 0.85) * wet * 0.8\n this.writeFan(this.inner, centre, n, t1, t2, lift + 0.0007, 0.9 * wr, this.tint.r, this.tint.g, this.tint.b, a, a * 0.5)\n }\n this.triangles = ring * (showInner ? 2 : 1)\n this.group.visible = true\n }\n\n private writeFan(\n fan: Fan,\n center: Vec3,\n n: Vec3,\n t1: Vec3,\n t2: Vec3,\n lift: number,\n scale: number,\n r: number,\n g: number,\n b: number,\n aCentre: number,\n aRim: number,\n ): void {\n const ring = this.ring\n const px = center[0] + n[0] * lift\n const py = center[1] + n[1] * lift\n const pz = center[2] + n[2] * lift\n fan.position[0] = px\n fan.position[1] = py\n fan.position[2] = pz\n fan.color[0] = r * 0.85\n fan.color[1] = g * 0.85\n fan.color[2] = b * 0.85\n fan.color[3] = aCentre\n const normalAttr = fan.mesh.geometry.getAttribute('normal') as THREE_NS.BufferAttribute\n for (let i = 0; i <= ring; i++) {\n normalAttr.setXYZ(i, n[0], n[1], n[2])\n }\n normalAttr.needsUpdate = true\n for (let i = 0; i < ring; i++) {\n const a = (i / ring) * Math.PI * 2\n const c = Math.cos(a) * this.radii[i] * scale\n const s = Math.sin(a) * this.radii[i] * scale\n const o = (1 + i) * 3\n fan.position[o] = px + t1[0] * c + t2[0] * s\n fan.position[o + 1] = py + t1[1] * c + t2[1] * s\n fan.position[o + 2] = pz + t1[2] * c + t2[2] * s\n const k = (1 + i) * 4\n fan.color[k] = r\n fan.color[k + 1] = g\n fan.color[k + 2] = b\n fan.color[k + 3] = aRim\n }\n const geometry = fan.mesh.geometry\n ;(geometry.getAttribute('position') as THREE_NS.BufferAttribute).needsUpdate = true\n ;(geometry.getAttribute('color') as THREE_NS.BufferAttribute).needsUpdate = true\n }\n\n dispose(): void {\n for (const fan of [this.outer, this.inner]) fan.mesh.geometry.dispose()\n }\n}\n","import type * as THREE_NS from 'three'\nimport { sampleStream } from '@adatechnology/game-fluids'\nimport type { StreamState } from '@adatechnology/game-fluids'\nimport type { QualityProfile } from './quality'\nimport { clamp } from './util'\nimport type { ThreeLib } from './types'\n\n/** Seconds a stream takes to break up into drops after the world ended it. */\nconst GHOST_TIME = 0.4\n\n/**\n * One stream: a thread along the ballistic path (a tube updated in place) with a growing drop at its leading tip, a trailing drop while it ends,\n * and for thin streams only a sequence of falling drops. The jet narrows as it accelerates.\n */\nexport class StreamVisual {\n readonly tube: THREE_NS.Mesh\n readonly drops: THREE_NS.InstancedMesh\n private readonly positions: Float32Array\n private readonly normals: Float32Array\n private readonly samples: number\n private readonly sides: number\n private readonly matrix: THREE_NS.Matrix4\n private readonly maxDrops: number\n id = ''\n age = 0\n ghostAge = -1\n /** Last known state, kept while the stream breaks into drops. */\n state: StreamState | null = null\n triangles = 0\n\n constructor(\n private readonly THREE: ThreeLib,\n profile: QualityProfile,\n dropGeometry: THREE_NS.SphereGeometry,\n ) {\n const T = THREE\n this.samples = profile.streamSamples\n this.sides = profile.streamSides\n this.maxDrops = Math.max(profile.thinDrops, 2) + 2\n this.positions = new Float32Array(this.samples * this.sides * 3)\n this.normals = new Float32Array(this.samples * this.sides * 3)\n const geometry = new T.BufferGeometry()\n const p = new T.BufferAttribute(this.positions, 3)\n const n = new T.BufferAttribute(this.normals, 3)\n p.setUsage(T.DynamicDrawUsage)\n n.setUsage(T.DynamicDrawUsage)\n geometry.setAttribute('position', p)\n geometry.setAttribute('normal', n)\n const index: number[] = []\n for (let i = 0; i < this.samples - 1; i++) {\n for (let j = 0; j < this.sides; j++) {\n const a = i * this.sides + j\n const b = i * this.sides + ((j + 1) % this.sides)\n const c = (i + 1) * this.sides + j\n const d = (i + 1) * this.sides + ((j + 1) % this.sides)\n index.push(a, c, b, b, c, d)\n }\n }\n geometry.setIndex(index)\n this.tube = new T.Mesh(geometry)\n this.tube.name = 'fluid_stream'\n this.tube.frustumCulled = false\n this.tube.renderOrder = 2\n this.drops = new T.InstancedMesh(dropGeometry, undefined as unknown as THREE_NS.Material, this.maxDrops)\n this.drops.name = 'fluid_stream_drops'\n this.drops.frustumCulled = false\n this.drops.renderOrder = 2\n this.drops.instanceMatrix.setUsage(T.DynamicDrawUsage)\n this.drops.count = 0\n this.matrix = new T.Matrix4()\n }\n\n setMaterial(material: THREE_NS.Material): void {\n this.tube.material = material\n this.drops.material = material\n }\n\n hide(): void {\n this.tube.visible = false\n this.drops.visible = false\n this.drops.count = 0\n this.triangles = 0\n }\n\n private dropAt(slot: number, x: number, y: number, z: number, radius: number): void {\n this.matrix.makeScale(radius, radius, radius).setPosition(x, y, z)\n this.drops.setMatrixAt(slot, this.matrix)\n }\n\n /**\n * Draw the stream. `state` is the world's stream while it lives, or the last known one while ghost (`ghostAge >= 0`).\n */\n update(state: StreamState, gravity: number, dt: number, profile: QualityProfile, time: number): void {\n this.state = state\n this.age += dt\n if (this.ghostAge >= 0) this.ghostAge += dt\n\n const flight = Math.max(0.02, state.flightTime ?? 1)\n const [vx, vy, vz] = state.velocity\n const speed0 = Math.max(0.3, Math.hypot(vx, vy, vz))\n const q = Math.max(state.rate, 0) / 1000\n const r0 = clamp(Math.sqrt(q / (Math.PI * speed0)), 0.0015, 0.03)\n\n const tB = Math.min(flight, this.age)\n // The tail leaves the origin at free fall once the world stopped the stream.\n const tA = this.ghostAge >= 0 ? Math.min(flight, this.ghostAge) : 0\n\n let drops = 0\n const thin = state.thin && this.ghostAge < 0\n\n if (thin) {\n this.tube.visible = false\n this.triangles = 0\n const n = profile.thinDrops\n const phase = time * 1.3\n for (let k = 0; k < n && drops < this.maxDrops; k++) {\n const tau = ((phase + k / n) % 1) * flight\n if (tau > tB) continue\n const p = sampleStream(state, gravity, tau)\n const grow = Math.min(1, tau / (0.12 * flight + 0.02))\n this.dropAt(drops++, p[0], p[1], p[2], Math.max(0.002, r0 * 1.8) * (0.4 + 0.6 * grow))\n }\n } else if (tB - tA > 0.015) {\n this.writeTube(state, gravity, tA, tB, r0, speed0)\n this.tube.visible = true\n this.triangles = (this.samples - 1) * this.sides * 2\n // Leading drop that grows and detaches while the jet is still advancing; trailing drop while it ends.\n if (tB < flight) {\n const p = sampleStream(state, gravity, tB)\n const w = this.radiusAt(state, gravity, tB, r0, speed0)\n this.dropAt(drops++, p[0], p[1], p[2], w * 1.35)\n }\n if (this.ghostAge >= 0) {\n const p = sampleStream(state, gravity, tA)\n const w = this.radiusAt(state, gravity, tA, r0, speed0)\n this.dropAt(drops++, p[0], p[1], p[2], w * 1.4 * (1 - this.ghostAge / GHOST_TIME * 0.4))\n }\n } else {\n this.tube.visible = false\n this.triangles = 0\n if (tA < flight) {\n // Only the drop is left.\n const p = sampleStream(state, gravity, this.ghostAge >= 0 ? tA : tB)\n this.dropAt(drops++, p[0], p[1], p[2], Math.max(0.003, r0 * 1.3))\n }\n }\n\n this.drops.count = drops\n this.drops.visible = drops > 0\n this.drops.instanceMatrix.needsUpdate = true\n this.triangles += drops * 24\n }\n\n /** The ghost is over once its tail reached the landing. */\n get finished(): boolean {\n return this.ghostAge >= 0 && (this.ghostAge >= GHOST_TIME || this.ghostAge >= (this.state?.flightTime ?? 0))\n }\n\n private radiusAt(state: StreamState, gravity: number, t: number, r0: number, speed0: number): number {\n const vy = state.velocity[1] - gravity * t\n const speed = Math.max(0.3, Math.hypot(state.velocity[0], vy, state.velocity[2]))\n const fade = this.ghostAge >= 0 ? clamp(1 - this.ghostAge / GHOST_TIME, 0.15, 1) : 1\n return r0 * Math.sqrt(speed0 / speed) * fade\n }\n\n private writeTube(state: StreamState, gravity: number, tA: number, tB: number, r0: number, speed0: number): void {\n const n = this.samples\n const sides = this.sides\n for (let i = 0; i < n; i++) {\n const k = i / (n - 1)\n const t = tA + (tB - tA) * k\n const p = sampleStream(state, gravity, t)\n const vx = state.velocity[0]\n const vy = state.velocity[1] - gravity * t\n const vz = state.velocity[2]\n const l = Math.hypot(vx, vy, vz) || 1\n const tx = vx / l\n const ty = vy / l\n const tz = vz / l\n // Any vector not parallel to the tangent gives the first side axis.\n const ax = Math.abs(tz) < 0.9 ? 0 : 1\n const az = Math.abs(tz) < 0.9 ? 1 : 0\n let b1x = ty * az - tz * 0\n let b1y = tz * ax - tx * az\n let b1z = tx * 0 - ty * ax\n const bl = Math.hypot(b1x, b1y, b1z) || 1\n b1x /= bl\n b1y /= bl\n b1z /= bl\n const b2x = ty * b1z - tz * b1y\n const b2y = tz * b1x - tx * b1z\n const b2z = tx * b1y - ty * b1x\n // Rounded ends: the radius tapers to a bead at both ends.\n const end = Math.min(1, 0.35 + Math.min(k, 1 - k) * 8)\n const r = this.radiusAt(state, gravity, t, r0, speed0) * end\n for (let j = 0; j < sides; j++) {\n const a = (j / sides) * Math.PI * 2\n const c = Math.cos(a)\n const s = Math.sin(a)\n const nx = b1x * c + b2x * s\n const ny = b1y * c + b2y * s\n const nz = b1z * c + b2z * s\n const o = (i * sides + j) * 3\n this.positions[o] = p[0] + nx * r\n this.positions[o + 1] = p[1] + ny * r\n this.positions[o + 2] = p[2] + nz * r\n this.normals[o] = nx\n this.normals[o + 1] = ny\n this.normals[o + 2] = nz\n }\n }\n const g = this.tube.geometry\n ;(g.getAttribute('position') as THREE_NS.BufferAttribute).needsUpdate = true\n ;(g.getAttribute('normal') as THREE_NS.BufferAttribute).needsUpdate = true\n }\n\n start(id: string): void {\n this.id = id\n this.age = 0\n this.ghostAge = -1\n this.state = null\n }\n\n dispose(): void {\n this.tube.geometry.dispose()\n this.drops.dispose()\n }\n}\n","import type * as THREE_NS from 'three'\nimport type { FluidEvent, StainState, StreamState } from '@adatechnology/game-fluids'\nimport { createVfx } from '@adatechnology/game-vfx'\nimport { createVfxRenderer } from '@adatechnology/game-vfx-three'\nimport { LiquidBody } from './liquid-body'\nimport { MaterialCache } from './materials'\nimport { profileOf } from './quality'\nimport { FluidSprites } from './sprites'\nimport { StainVisual } from './stains'\nimport { StreamVisual } from './streams'\nimport type { BindOptions, FluidQuality, FluidRenderer, FluidRendererOptions, FluidRendererStats, ThreeLib } from './types'\n\nlet rendererCount = 0\n\n/**\n * Draws a `FluidWorld` with three.js. It only reads the world state (plus `splash` events for droplets), so a LAN client that receives the\n * same world state draws the same liquids.\n */\nexport function createFluidRenderer(THREE: ThreeLib, options: FluidRendererOptions): FluidRenderer {\n const { scene, fluids } = options\n const quality: FluidQuality = options.quality ?? 'medium'\n const profile = profileOf(quality)\n const materials = new MaterialCache(THREE, options.envMap ?? null)\n const root = new THREE.Group()\n root.name = 'fluid_renderer'\n scene.add(root)\n // The droplets and steam are sprites of the effects world: the host's shared one, or one made here and stepped in `update`.\n const vfx = options.vfx ?? createVfx({ profile: quality, gravity: fluids.gravity, sprites: { capacity: profile.maxParticles } })\n const ownVfx = options.vfx ? undefined : createVfxRenderer(THREE, { scene, camera: options.camera ?? new THREE.PerspectiveCamera(), vfx, pointScale: options.pointScale ?? 600 })\n const particles = new FluidSprites(vfx, `fluids-${++rendererCount}`)\n const dropGeometry = new THREE.SphereGeometry(1, 8, 6)\n\n const bodies = new Map<string, LiquidBody>()\n const streamsActive = new Map<string, StreamVisual>()\n const streamsFree: StreamVisual[] = []\n let streamsCreated = 0\n const stainsActive = new Map<string, StainVisual>()\n const stainsFree: StainVisual[] = []\n let stainsCreated = 0\n let time = 0\n let disposed = false\n\n const offSplash = fluids.on('splash', (e: FluidEvent) => {\n if (e.type !== 'splash' || disposed) return\n const def = fluids.liquids.get(e.liquid)\n if (!def) return\n particles.splash(e.point, e.intensity, def.color, def.opacity, profile.splashDroplets)\n })\n\n // ---- streams ----\n\n const acquireStream = (id: string): StreamVisual | null => {\n let v = streamsFree.pop() ?? null\n if (!v) {\n if (streamsCreated >= profile.maxStreams) return null\n streamsCreated++\n v = new StreamVisual(THREE, profile, dropGeometry)\n root.add(v.tube, v.drops)\n }\n v.start(id)\n return v\n }\n\n const releaseStream = (v: StreamVisual): void => {\n v.hide()\n streamsActive.delete(v.id)\n streamsFree.push(v)\n }\n\n const syncStreams = (dt: number): void => {\n const live = fluids.streams()\n const seen = new Set<string>()\n // Biggest first, so the budget keeps the visible ones.\n live.sort((a: StreamState, b: StreamState) => b.rate - a.rate)\n for (const s of live) {\n seen.add(s.id)\n let v = streamsActive.get(s.id)\n if (!v) {\n const fresh = acquireStream(s.id)\n if (!fresh) continue\n v = fresh\n streamsActive.set(s.id, v)\n }\n const def = fluids.liquids.get(s.liquid)\n if (!def) continue\n v.ghostAge = -1\n v.setMaterial(materials.liquidStream(def))\n v.update(s, fluids.gravity, dt, profile, time)\n }\n for (const v of [...streamsActive.values()]) {\n if (seen.has(v.id)) continue\n // The world ended it: let it break into drops.\n if (v.ghostAge < 0) v.ghostAge = 0\n if (!v.state || v.finished) {\n releaseStream(v)\n continue\n }\n v.update(v.state, fluids.gravity, dt, profile, time)\n if (v.finished) releaseStream(v)\n }\n }\n\n // ---- stains ----\n\n const syncStains = (): void => {\n const all = fluids.stains()\n // Keep the newest stains when the budget is smaller than the world.\n const wanted = all.length > profile.maxStains ? [...all].sort((a: StainState, b: StainState) => (a.age ?? 0) - (b.age ?? 0)).slice(0, profile.maxStains) : all\n const ids = new Set(wanted.map((s: StainState) => s.id))\n for (const [id, v] of [...stainsActive]) {\n if (ids.has(id)) continue\n v.hide()\n stainsActive.delete(id)\n stainsFree.push(v)\n }\n for (const s of wanted) {\n const def = fluids.liquids.get(s.liquid)\n if (!def) continue\n let v = stainsActive.get(s.id)\n if (!v) {\n v = stainsFree.pop()\n if (!v) {\n if (stainsCreated >= profile.maxStains) continue\n v = new StainVisual(THREE, profile, stainsCreated++)\n root.add(v.group)\n }\n v.stainId = s.id\n v.signature = Number.NaN\n v.assign(def, materials)\n stainsActive.set(s.id, v)\n }\n v.update(s, def, profile)\n }\n }\n\n // ---- public ----\n\n const renderer: FluidRenderer = {\n bindContainer(containerId: string, object: THREE_NS.Object3D, opts: BindOptions = {}) {\n renderer.unbindContainer(containerId)\n bodies.set(containerId, new LiquidBody(THREE, containerId, object, opts.innerOffset ?? [0, 0, 0]))\n },\n\n unbindContainer(containerId: string) {\n const body = bodies.get(containerId)\n if (!body) return\n body.dispose()\n bodies.delete(containerId)\n },\n\n update(dt: number) {\n if (disposed) return\n time += dt\n for (const body of bodies.values()) body.update(dt, fluids, materials, profile, particles)\n syncStreams(dt)\n syncStains()\n ownVfx?.update(dt)\n },\n\n clear() {\n for (const v of [...streamsActive.values()]) releaseStream(v)\n for (const [id, v] of [...stainsActive]) {\n v.hide()\n stainsActive.delete(id)\n stainsFree.push(v)\n }\n if (ownVfx) vfx.clear()\n else vfx.sprites().clear()\n },\n\n dispose() {\n if (disposed) return\n disposed = true\n offSplash()\n for (const body of bodies.values()) body.dispose()\n bodies.clear()\n for (const v of [...streamsActive.values(), ...streamsFree]) v.dispose()\n for (const v of [...stainsActive.values(), ...stainsFree]) v.dispose()\n streamsActive.clear()\n stainsActive.clear()\n streamsFree.length = 0\n stainsFree.length = 0\n ownVfx?.dispose()\n dropGeometry.dispose()\n materials.dispose()\n scene.remove(root)\n root.clear()\n },\n\n stats(): FluidRendererStats {\n let triangles = 0\n let containers = 0\n for (const body of bodies.values()) {\n triangles += body.triangles\n if (body.visible) containers++\n }\n for (const v of streamsActive.values()) triangles += v.triangles\n for (const v of stainsActive.values()) triangles += v.triangles\n return {\n quality,\n containers,\n streams: streamsActive.size,\n stains: stainsActive.size,\n particles: particles.count,\n triangles,\n budget: { stains: profile.maxStains, streams: profile.maxStreams, particles: profile.maxParticles },\n }\n },\n }\n\n return renderer\n}\n\n","import type * as THREE_NS from 'three'\nimport type { Film, FilmProfile } from '@adatechnology/game-fluids'\nimport type { ThreeLib } from '../types'\n\n/** The page's drop size: a radius in millimetres drawn as a sphere of `radius * 0.0013` m, stretched along its flight by its speed (F258-262). */\nconst METRES_PER_MM = 0.0013\nconst STRETCH_PER_SPEED = 0.35\nconst MAX_STRETCH = 3.5\nconst BEAD_HANG = 0.0012\nconst BEAD_SCALE = 0.0011\n\nexport interface FilmDrops {\n readonly drops: THREE_NS.InstancedMesh\n readonly bead: THREE_NS.Mesh\n setProfile(profile: FilmProfile): void\n /** Writes the film's drops in flight and the drip's bead into the meshes; answers how many drops are drawn. */\n sync(film: Film): number\n dispose(): void\n}\n\nexport function createFilmDrops(THREE: ThreeLib, capacity: number, profile: FilmProfile): FilmDrops {\n const material = new THREE.MeshStandardMaterial({ color: profile.dropColor, roughness: 0.12, metalness: 0.15, transparent: true, opacity: profile.dropOpacity })\n const geometry = new THREE.IcosahedronGeometry(1, 1)\n const drops = new THREE.InstancedMesh(geometry, material, capacity)\n drops.frustumCulled = false\n drops.instanceMatrix.setUsage(THREE.DynamicDrawUsage)\n drops.name = 'film_drops'\n const beadGeometry = new THREE.SphereGeometry(1, 10, 8)\n const bead = new THREE.Mesh(beadGeometry, material)\n bead.visible = false\n bead.name = 'film_bead'\n const hidden = new THREE.Matrix4().makeScale(0, 0, 0)\n for (let i = 0; i < capacity; i++) drops.setMatrixAt(i, hidden)\n const up = new THREE.Vector3(0, 1, 0)\n const direction = new THREE.Vector3()\n const location = new THREE.Vector3()\n const scale = new THREE.Vector3()\n const rotation = new THREE.Quaternion()\n const matrix = new THREE.Matrix4()\n return {\n drops,\n bead,\n setProfile(next) {\n material.color.setHex(next.dropColor)\n material.opacity = next.dropOpacity\n },\n sync(film) {\n const { active, position, velocity, radius } = film.drops\n let drawn = 0\n for (let i = 0; i < capacity; i++) {\n if (!active[i]) {\n drops.setMatrixAt(i, hidden)\n continue\n }\n const o = i * 3\n const speed = Math.hypot(velocity[o]!, velocity[o + 1]!, velocity[o + 2]!)\n direction.set(velocity[o]!, velocity[o + 1]!, velocity[o + 2]!).normalize()\n rotation.setFromUnitVectors(up, direction)\n const size = radius[i]! * METRES_PER_MM\n matrix.compose(location.set(position[o]!, position[o + 1]!, position[o + 2]!), rotation, scale.set(size, size * (1 + Math.min(MAX_STRETCH, speed * STRETCH_PER_SPEED)), size))\n drops.setMatrixAt(i, matrix)\n drawn++\n }\n drops.instanceMatrix.needsUpdate = true\n const state = film.bead\n bead.visible = state.visible\n if (state.visible) {\n const r = state.radius\n bead.position.set(state.position[0], state.position[1] - r * BEAD_HANG, state.position[2])\n bead.scale.set(r * BEAD_SCALE, r * BEAD_SCALE * (1 + state.swell * 0.7), r * BEAD_SCALE)\n }\n return drawn\n },\n dispose() {\n geometry.dispose()\n beadGeometry.dispose()\n material.dispose()\n },\n }\n}\n","/**\n * The film shader of the Fluid Standard page (F131-209), line by line. Where the page painted the whole surface itself (`baseCol`: wall, sidewalk, curb,\n * road), the film is drawn over the host's own surface, so `baseCol` is the flat colour `uBase` and the alpha is the film's coverage: the wet part and,\n * for fuel, the ground it darkened. Three more differences: the grid size and span are uniforms (`uGrid`, `uSpan`; the page hard-codes 256 x 396 and 4.95),\n * the strip tangent comes from the geometry (`aT`; the page's was the x axis), and the output goes through the r160 colour-space chunk after turning the page's\n * display-space colours linear. The two lines that mirror the page's own wall in the reflection (`R.z<-.02 ... env=vec3(.30,.32,.35)`, `R.y<-.02 ...`) are left out:\n * they describe the page's scene, not a surface of the host.\n */\nexport const FILM_VERTEX = `attribute vec3 aB;attribute vec3 aT;varying vec2 vUv;varying vec3 vPos;varying vec3 vN;varying vec3 vB;varying vec3 vT;\nvoid main(){vUv=uv;vPos=position;vN=normal;vB=aB;vT=aT;gl_Position=projectionMatrix*viewMatrix*vec4(position,1.);}`\n\nexport const FILM_FRAGMENT = `precision highp float;\nuniform sampler2D tF;uniform vec2 tx;uniform vec2 uGrid;uniform float uSpan;uniform float uT;uniform int uFl;uniform vec3 cThin,cThick,cDry,cDryThin,uBase;uniform float opK,F0,nrm,dbg;\nuniform vec3 camP,lampP,flashP,flashD;varying vec2 vUv;varying vec3 vPos;varying vec3 vN;varying vec3 vB;varying vec3 vT;\nfloat hsh(vec2 p){p=fract(p*vec2(123.34,456.21));p+=dot(p,p+45.32);return fract(p.x*p.y);}\nvec2 hsh2(vec2 p){return vec2(hsh(p),hsh(p+17.7));}\nfloat nz(vec2 p){vec2 i=floor(p),f=fract(p);f=f*f*(3.-2.*f);return mix(mix(hsh(i),hsh(i+vec2(1,0)),f.x),mix(hsh(i+vec2(0,1)),hsh(i+vec2(1,1)),f.x),f.y);}\nfloat vor(vec2 p){vec2 i=floor(p),f=fract(p);float d1=8.,d2=8.;for(int y=-1;y<=1;y++)for(int x=-1;x<=1;x++){vec2 o=vec2(float(x),float(y));vec2 r=o+hsh2(i+o)-f;float d=dot(r,r);if(d<d1){d2=d1;d1=d;}else if(d<d2)d2=d;}return sqrt(d2)-sqrt(d1);}\nfloat hs(vec2 u){float r=texture2D(tF,u).r;return r*r*6.;}\nvoid main(){\n vec4 S=texture2D(tF,vUv);vec2 q=tx*.5;float h=.25*(hs(vUv+q)+hs(vUv-q)+hs(vUv+vec2(q.x,-q.y))+hs(vUv+vec2(-q.x,q.y)));float dry=S.g;float wg=S.b;\n vec2 o1=vec2(tx.x*1.5,0.),o2=vec2(0.,tx.y*1.5);\n float hl=hs(vUv-o1),hr=hs(vUv+o1),hd=hs(vUv-o2),hu=hs(vUv+o2);\n vec2 g=vec2(hr-hl,hu-hd)/37.5;\n vec2 cellp=vUv*uGrid;\n float n1=nz(cellp*2.3),n2=nz(cellp*7.1);\n float hn=h+(n1-.5)*.045+(n2-.5)*.02;\n float m=smoothstep(.035,.08,hn);\n if(uFl==4){float wa=.25*(texture2D(tF,vUv+q).b+texture2D(tF,vUv-q).b+texture2D(tF,vUv+vec2(q.x,-q.y)).b+texture2D(tF,vUv+vec2(-q.x,q.y)).b);m=smoothstep(.35,.75,wa+(n1-.5)*.25)*smoothstep(.0,.012,h);wg=smoothstep(.2,.8,wa+(n1-.5)*.2);}\n vec2 p=vec2(dot(vPos,normalize(vT)),vUv.y*uSpan);\n vec3 base=uBase;\n vec3 T=normalize(vT),Bv=normalize(vB),N0=normalize(vN);\n vec3 nt=vec3(-g*nrm*m,1.);\n float dryM=smoothstep(.5,.95,dry);\n float wet=(1.-dryM)*m;\n // bile bubbles\n float bub=0.;\n if(uFl==2){vec2 bp=cellp/2.6;vec2 bi=floor(bp);vec2 bf=fract(bp)-.5;float rnd=hsh(bi);\n if(rnd>.5&&h>1.1){float t=fract(uT*(.18+.25*hsh(bi+3.1))+rnd*7.);float rad=.42*smoothstep(0.,.8,t);float d=length(bf-(hsh2(bi+9.)-.5)*.2);\n if(t<.86&&d<rad){float z=sqrt(max(0.,rad*rad-d*d));nt.xy+=(bf/max(rad,.01))*2.2*(1.-dryM);bub=z/rad;}\n else if(t>=.86){float rr=.42*1.15;float ring=1.-smoothstep(0.,.07,abs(d-rr*(.9+(t-.86)*.8)));nt.xy-=normalize(bf+.0001)*ring*.8*(1.-dryM);bub=-ring;}}}\n if(uFl==4){vec2 wp=p;nt.xy+=.05*m*vec2(nz(wp*9.+vec2(uT*1.7,0.))-.5,nz(wp*9.+vec2(0.,uT*1.4)+7.)-.5)*2.;}\n vec3 Nf=normalize(T*nt.x+Bv*nt.y+N0*nt.z);\n // albedo\n float th=smoothstep(.1,1.7,h);\n vec3 fc=mix(cThin,cThick,th);\n float op=clamp(1.-exp(-h*opK),0.,1.);\n vec3 alb;\n if(uFl==4){alb=base*mix(vec3(1.),cThin*1.15,clamp(h*.9,0.,1.));}\n else{alb=mix(base*mix(vec3(1.),fc*1.6,clamp(h*2.,0.,1.)),fc,op);}\n if(uFl==2&&bub>0.)alb=mix(alb,cThin*1.25,bub*.5);\n // dried look: matte, darker rim, cracks where it was thick\n float rim=smoothstep(.01,.05,length(g))*(1.-smoothstep(.4,1.,h));\n vec3 dc=mix(cDryThin,cDry,smoothstep(.06,.8,h))*(1.-.42*rim);\n float crk=(1.-smoothstep(.015,.05,vor(p*(uFl==2?34.:52.))))*smoothstep(.9,1.6,h);\n dc=mix(dc,base*.55,crk*.75);\n if(uFl!=3&&uFl!=4)alb=mix(alb,mix(base,dc,clamp(.4+h*1.4,0.,1.)),dryM);\n // gasoline darkens the ground while wet\n float gw=0.;if(uFl==4){gw=wg*(1.-m*.0);base*=mix(1.,.45,gw);alb=mix(base,alb,m);}\n vec3 col0=mix(base,alb*(uFl<2?mix(.62,1.,dryM):1.),m);\n // lighting\n vec3 V=normalize(camP-vPos);\n vec3 Lv=lampP-vPos;float dl=length(Lv);vec3 L=Lv/dl;float att=2.2/(1.+dl*dl*.12);\n vec3 lampC=vec3(1.,.88,.7);vec3 moonD=normalize(vec3(-3.,5.,4.));vec3 moonC=vec3(.11,.14,.19);vec3 amb=vec3(.14,.15,.17);\n vec3 Fv=flashP-vPos;float fd=length(Fv);vec3 FL=Fv/fd;float cone=smoothstep(.86,.97,dot(-FL,flashD));float fatt=cone*1.1/(1.+fd*fd*.6);\n vec3 dif=col0*(amb+lampC*att*max(dot(Nf,L),0.)+moonC*max(dot(Nf,moonD),0.)+vec3(.95,.97,1.)*fatt*max(dot(Nf,FL),0.));\n float wv=max(wet,gw*.7);\n float shin=mix(30.,320.,wv);\n vec3 Hh=normalize(L+V);vec3 Hf=normalize(FL+V);\n float sp=pow(max(dot(Nf,Hh),0.),shin)*shin*.011*att;float spf=pow(max(dot(Nf,Hf),0.),shin)*shin*.008*fatt;\n vec3 R=reflect(-V,Nf);\n vec3 env=mix(vec3(.03,.035,.04),vec3(.15,.17,.21),clamp(R.y*1.4,0.,1.));\n env+=lampC*(pow(max(dot(R,L),0.),500.)*7.+pow(max(dot(R,L),0.),40.)*.6)*att;\n float fr=F0+(1.-F0)*pow(1.-max(dot(Nf,V),0.),5.);\n vec3 col=dif*(1.-fr*wv*.7)+(env*min(1.,fr*(uFl==4?1.6:3.4))+min(lampC*sp+vec3(.9,.95,1.)*spf,vec3(.85)))*wv;\n if(uFl==3){float film=(1.-smoothstep(.04,.55,h))*m;vec3 ir=.5+.5*cos(6.283*(h*7.+n1*.5+uT*.03+vec3(0.,.33,.67)));col+=ir*film*(.10+.5*fr)*(.6+att*.4);}\n if(uFl==4){vec2 wp=p;float film=m*(.4+.6*(1.-smoothstep(.02,.2,h)));vec3 ir=.5+.5*cos(6.283*(nz(wp*6.)*1.4+uT*.02+vec3(0.,.33,.67)));col+=ir*film*.05*(.3+fr*3.);}\n if(dbg>.5){float k=clamp(h/3.,0.,1.);vec3 hc=mix(vec3(.05,.1,.5),vec3(1.,.9,.2),k);hc=mix(hc,vec3(1.,.2,.1),step(.66,k));col=mix(base*.25,mix(hc,vec3(.6),dryM*.7),step(.004,h));}\n float cover=max(m,gw*.55);\n gl_FragColor=vec4(pow(max(col,vec3(0.)),vec3(2.2)),clamp(cover,0.,1.));\n #include <colorspace_fragment>\n}`\n","import type * as THREE_NS from 'three'\nimport type { FilmLook, FilmProfile } from '@adatechnology/game-fluids'\nimport type { ThreeLib } from '../types'\nimport { FILM_FRAGMENT, FILM_VERTEX } from './shader'\n\n/** The page's `uFl` of each look (blood 0, bile 2, oil 3, fuel 4). */\nexport const LOOK_INDEX: Readonly<Record<FilmLook, number>> = { blood: 0, bile: 2, oil: 3, fuel: 4 }\nconst DEFAULT_SPAN = 4.95\n\nexport interface FilmMaterial {\n readonly material: THREE_NS.ShaderMaterial\n /** Sets the page's liquid uniforms from a profile (`setFluid`, F119-130). */\n setProfile(profile: FilmProfile): void\n dispose(): void\n}\n\nconst rgb = (THREE: ThreeLib, target: THREE_NS.Vector3, hex: number): void => {\n target.set(((hex >> 16) & 255) / 255, ((hex >> 8) & 255) / 255, (hex & 255) / 255)\n}\n\n/**\n * The film's material: the Fluid Standard's shader with the grid texture `tF` (r: thickness, g: dryness, b: dampness) and the liquid's colours.\n * It is transparent over the host's surface: the alpha is the film's coverage.\n */\nexport function createFilmMaterial(THREE: ThreeLib, texture: THREE_NS.Texture, width: number, height: number, span: number = DEFAULT_SPAN): FilmMaterial {\n const V3 = THREE.Vector3\n const uniforms = {\n tF: { value: texture },\n tx: { value: new THREE.Vector2(1 / width, 1 / height) },\n uGrid: { value: new THREE.Vector2(width, height) },\n uSpan: { value: span },\n uT: { value: 0 },\n uFl: { value: 0 },\n cThin: { value: new V3() },\n cThick: { value: new V3() },\n cDry: { value: new V3() },\n cDryThin: { value: new V3() },\n uBase: { value: new V3(0.4, 0.4, 0.38) },\n opK: { value: 3 },\n F0: { value: 0.03 },\n nrm: { value: 16 },\n camP: { value: new V3() },\n lampP: { value: new V3() },\n flashP: { value: new V3() },\n flashD: { value: new V3(0, 0, -1) },\n dbg: { value: 0 },\n }\n const material = new THREE.ShaderMaterial({\n uniforms,\n vertexShader: FILM_VERTEX,\n fragmentShader: FILM_FRAGMENT,\n transparent: true,\n depthWrite: false,\n // the layout's axes may be either handed (the page's strips were all built facing out)\n side: THREE.DoubleSide,\n polygonOffset: true,\n polygonOffsetFactor: -2,\n polygonOffsetUnits: -2,\n })\n return {\n material,\n setProfile(profile) {\n uniforms.uFl.value = LOOK_INDEX[profile.look]\n rgb(THREE, uniforms.cThin.value, profile.thin)\n rgb(THREE, uniforms.cThick.value, profile.thick)\n rgb(THREE, uniforms.cDry.value, profile.dry)\n rgb(THREE, uniforms.cDryThin.value, profile.dryThin)\n uniforms.opK.value = profile.opacityRate\n uniforms.F0.value = profile.reflectance\n uniforms.nrm.value = profile.normalScale\n },\n dispose() {\n material.dispose()\n },\n }\n}\n","import type * as THREE_NS from 'three'\nimport type { FilmLayout, FilmRegion } from '@adatechnology/game-fluids'\nimport type { ThreeLib } from '../types'\n\nconst SEGMENTS_ACROSS = 8\nconst SEGMENTS_ALONG = 8\n\n/**\n * One strip of the folded grid as a plane of the surface: `uv` runs over the region's rows and the grid's columns, so the shader reads its own cells.\n * `aB` is the way the film runs down the strip (rows) and `aT` its columns.\n */\nexport function createStripGeometry(THREE: ThreeLib, layout: FilmLayout, region: FilmRegion): THREE_NS.BufferGeometry {\n const { width, height, cellSize } = layout\n const alongSegments = region.rowCount <= 2 ? 1 : SEGMENTS_ALONG\n const position: number[] = []\n const normal: number[] = []\n const uv: number[] = []\n const down: number[] = []\n const across: number[] = []\n const index: number[] = []\n for (let j = 0; j <= alongSegments; j++) {\n const t = j / alongSegments\n for (let i = 0; i <= SEGMENTS_ACROSS; i++) {\n const u = i / SEGMENTS_ACROSS\n for (let k = 0; k < 3; k++) position.push(region.origin[k] + region.columnAxis[k] * u * width * cellSize + region.rowAxis[k] * t * region.rowCount * cellSize)\n normal.push(region.normal[0], region.normal[1], region.normal[2])\n down.push(region.rowAxis[0], region.rowAxis[1], region.rowAxis[2])\n across.push(region.columnAxis[0], region.columnAxis[1], region.columnAxis[2])\n uv.push(u, (region.firstRow + t * region.rowCount) / height)\n }\n }\n for (let j = 0; j < alongSegments; j++) {\n for (let i = 0; i < SEGMENTS_ACROSS; i++) {\n const a = j * (SEGMENTS_ACROSS + 1) + i\n const b = a + 1\n const c = a + SEGMENTS_ACROSS + 1\n index.push(a, c, b, b, c, c + 1)\n }\n }\n const geometry = new THREE.BufferGeometry()\n geometry.setAttribute('position', new THREE.Float32BufferAttribute(position, 3))\n geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normal, 3))\n geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uv, 2))\n geometry.setAttribute('aB', new THREE.Float32BufferAttribute(down, 3))\n geometry.setAttribute('aT', new THREE.Float32BufferAttribute(across, 3))\n geometry.setIndex(index)\n return geometry\n}\n","import type * as THREE_NS from 'three'\nimport { createFilm } from '@adatechnology/game-fluids'\nimport type { Film, FilmBounds, FilmLayout, FilmProfile, FilmRandom, StainTint, Vec3 } from '@adatechnology/game-fluids'\nimport type { ThreeLib } from '../types'\nimport { createFilmDrops } from './drops'\nimport type { FilmDrops } from './drops'\nimport { createFilmMaterial } from './material'\nimport type { FilmMaterial } from './material'\nimport { createStripGeometry } from './strips'\n\nconst MAX_THICKNESS = 6\nconst CHANNEL = 255\nconst VISIBLE_THICKNESS = 0.035\nconst DEFAULT_BASE = 0x666661\nconst MIN_CHANGE = 2\nconst CHANGE_FRACTION = 0.005\nconst LAMP_OFFSET: Vec3 = [0.9, 2.9, 0.4]\n\nexport interface FilmGridOptions {\n layout: FilmLayout\n profile: FilmProfile\n random: FilmRandom\n /** The host's surface colour under the film, `0xRRGGBB` (the shader blends over it). Default the page's sidewalk grey. */\n base?: number\n /** World point the grid is about: the distance to the player is measured from it. */\n center: Vec3\n /** The splash colours the profile was tinted with, kept to tell a grid of another tint apart and to bake with them. */\n tint?: StainTint\n stepRate?: number\n maxSteps?: number\n}\n\n/** A rectangle of cells, inclusive. */\nexport interface CellRect {\n firstRow: number\n lastRow: number\n firstColumn: number\n lastColumn: number\n}\n\n/**\n * One film grid on screen: the running film, its texture (thickness, dryness, dampness per cell as the page's `upload` writes them), the strips of the\n * layout drawn with the film shader, and the drops in flight.\n */\nexport class FilmGrid {\n readonly film: Film\n readonly group: THREE_NS.Group\n readonly texture: THREE_NS.DataTexture\n readonly center: Vec3\n readonly tint: StainTint | undefined\n readonly data: Uint8Array<ArrayBuffer>\n /** Seconds the grid has been without anything landing, flowing or drying. */\n idle = 0\n /** Set once the whole texture went to the GPU (the partial uploads need it there first). */\n uploaded = false\n private readonly strips: THREE_NS.Mesh[] = []\n private readonly material: FilmMaterial\n private readonly dropsView: FilmDrops\n private previous: CellRect | null = null\n private pending: CellRect | null = null\n private launched = 0\n private lastWet = 0\n\n constructor(\n private readonly THREE: ThreeLib,\n options: FilmGridOptions,\n ) {\n const { layout, profile } = options\n this.center = options.center\n this.tint = options.tint\n this.film = createFilm({ layout, profile, random: options.random, ...(options.stepRate ? { stepRate: options.stepRate } : {}), ...(options.maxSteps ? { maxSteps: options.maxSteps } : {}) })\n this.data = new Uint8Array(new ArrayBuffer(layout.width * layout.height * 4))\n this.texture = new THREE.DataTexture(this.data, layout.width, layout.height, THREE.RGBAFormat)\n this.texture.magFilter = THREE.LinearFilter\n this.texture.minFilter = THREE.LinearFilter\n this.texture.needsUpdate = true\n // three calls this after the texture reached the GPU: from then on only the dirty rectangle has to go up\n this.texture.onUpdate = () => {\n this.uploaded = true\n }\n this.material = createFilmMaterial(THREE, this.texture, layout.width, layout.height, layout.height * layout.cellSize)\n this.material.setProfile(profile)\n this.group = new THREE.Group()\n this.group.name = 'film_grid'\n for (const region of layout.regions) {\n const mesh = new THREE.Mesh(createStripGeometry(THREE, layout, region), this.material.material)\n mesh.frustumCulled = false\n mesh.renderOrder = 3\n mesh.name = `film_${region.id}`\n this.strips.push(mesh)\n this.group.add(mesh)\n }\n this.dropsView = createFilmDrops(THREE, layout.rules.dropCapacity, profile)\n this.group.add(this.dropsView.drops, this.dropsView.bead)\n const uniforms = this.material.material.uniforms\n const base = options.base ?? DEFAULT_BASE\n uniforms.uBase!.value.set(((base >> 16) & 255) / 255, ((base >> 8) & 255) / 255, (base & 255) / 255)\n uniforms.lampP!.value.set(this.center[0] + LAMP_OFFSET[0], this.center[1] + LAMP_OFFSET[1], this.center[2] + LAMP_OFFSET[2])\n this.launched = this.film.drops.launched\n }\n\n get profile(): FilmProfile {\n return this.film.profile\n }\n\n setProfile(profile: FilmProfile): void {\n this.film.profile = profile\n this.material.setProfile(profile)\n this.dropsView.setProfile(profile)\n }\n\n /** Where the film's light and eye are (the page's `camP`, `flashP` and `flashD`). */\n setView(camera: Vec3, forward: Vec3, time: number): void {\n const uniforms = this.material.material.uniforms\n uniforms.camP!.value.set(camera[0], camera[1], camera[2])\n uniforms.flashP!.value.set(camera[0] + 0.12, camera[1] - 0.15, camera[2])\n uniforms.flashD!.value.set(forward[0], forward[1], forward[2])\n uniforms.uT!.value = time\n }\n\n /** Steps the film one frame and refreshes the texture cells that may have changed. Returns the rectangle to upload, or null. */\n update(dt: number, dryingSeconds: number): CellRect | null {\n this.film.update({ dt, dryingSeconds })\n const bounds: FilmBounds = this.film.simulator.bounds\n const launched = this.film.drops.launched\n const busy = launched !== this.launched || this.film.drip !== undefined || this.film.sources.length > 0 || this.film.drags.some((drag) => !drag.done) || this.dropsInFlight()\n this.launched = launched\n const current: CellRect | null = bounds.lastRow < 0 ? null : { firstRow: bounds.firstRow, lastRow: bounds.lastRow, firstColumn: bounds.firstColumn, lastColumn: bounds.lastColumn }\n const rect = this.union(this.previous, current)\n this.previous = current\n const wet = current ? this.film.wetCells() : 0\n // still = nothing arriving and the wet area changing by less than half a percent (a slow creep does not keep a grid alive)\n const moved = Math.abs(wet - this.lastWet) > Math.max(MIN_CHANGE, wet * CHANGE_FRACTION)\n this.idle = busy || moved ? 0 : this.idle + dt\n if (moved || busy) this.lastWet = wet\n if (!rect) return this.takePending()\n this.encode(rect)\n this.pending = this.union(this.pending, rect)\n return this.takePending()\n }\n\n private takePending(): CellRect | null {\n const rect = this.pending\n this.pending = null\n return rect\n }\n\n /** The mean dryness of the cells that are wet (1 when nothing is). */\n dryness(): number {\n const { thickness, dryness } = this.film.field\n let wet = 0\n let dry = 0\n for (let i = 0; i < thickness.length; i++) {\n if (thickness[i]! > VISIBLE_THICKNESS) {\n wet++\n dry += dryness[i]!\n }\n }\n return wet === 0 ? 1 : dry / wet\n }\n\n /** Draws the drops and the bead; answers how many drops are in flight. */\n syncDrops(): number {\n return this.dropsView.sync(this.film)\n }\n\n dropsInFlight(): boolean {\n const { active } = this.film.drops\n for (let i = 0; i < active.length; i++) if (active[i]) return true\n return false\n }\n\n /** Writes the cells of the texture from the field, as the page's `upload` does (F360). */\n encode(rect: CellRect): void {\n const { thickness, dryness, dampness, width } = this.film.field\n const data = this.data\n for (let row = rect.firstRow; row <= rect.lastRow; row++) {\n for (let column = rect.firstColumn; column <= rect.lastColumn; column++) {\n const cell = row * width + column\n const j = cell * 4\n const h = thickness[cell]!\n data[j] = h > 0 ? Math.min(CHANNEL, Math.sqrt(Math.min(MAX_THICKNESS, h) / MAX_THICKNESS) * CHANNEL + 0.5) : 0\n data[j + 1] = dryness[cell]! * CHANNEL\n data[j + 2] = dampness[cell]! * CHANNEL\n data[j + 3] = CHANNEL\n }\n }\n }\n\n /** Encodes the whole field (a grid made from a state, or after `clear`). */\n encodeAll(): void {\n this.encode({ firstRow: 0, lastRow: this.film.field.height - 1, firstColumn: 0, lastColumn: this.film.field.width - 1 })\n this.texture.needsUpdate = true\n this.uploaded = false\n }\n\n private union(a: CellRect | null, b: CellRect | null): CellRect | null {\n if (!a) return b ? { ...b } : null\n if (!b) return { ...a }\n return { firstRow: Math.min(a.firstRow, b.firstRow), lastRow: Math.max(a.lastRow, b.lastRow), firstColumn: Math.min(a.firstColumn, b.firstColumn), lastColumn: Math.max(a.lastColumn, b.lastColumn) }\n }\n\n dispose(): void {\n for (const mesh of this.strips) mesh.geometry.dispose()\n this.material.dispose()\n this.dropsView.dispose()\n this.texture.dispose()\n }\n}\n","import type * as THREE_NS from 'three'\nimport { FILM_PROFILES, filmProfileOf, layoutFromSurfaces } from '@adatechnology/game-fluids'\nimport type { FilmLayout, FilmProfile, FilmRandom, FluidWorld, LiquidId, StainTint, SurfaceSpec, Vec3 } from '@adatechnology/game-fluids'\nimport type { ThreeLib } from '../types'\nimport { FilmGrid } from './grid'\nimport type { CellRect } from './grid'\n\n/** What the manager keeps from the page's budget text: up to 6 grids, only within 30 m, about 0.3 ms of simulation each (W711, W text \"a mais de 30 m não anima\"). */\nconst DEFAULT_MAX_GRIDS = 6\nconst DEFAULT_RANGE = 30\nconst DEFAULT_BUDGET_MS = 1.2\nconst DEFAULT_SETTLE_SECONDS = 20\nconst DEFAULT_MAX_STAGING = 16\nconst DEFAULT_GRID_METRES = 2\nconst DEFAULT_CELL = 0.0125\nconst BAKE_RANGE_FACTOR = 1.25\nconst EDGE_MARGIN = 0.35\nconst FLOOR_REACH = 0.3\n/** A drop thrown from higher than this above a strip is not counted as over it. */\nconst DROP_HEIGHT = 2.5\nconst DEFAULT_DROP_RADIUS = 3\nconst FALL_SPEED = 2\nconst VISIBLE_THICKNESS = 0.035\nconst DEFAULT_HALF = 50\nconst FREE_AFTER_EMPTY = 2\n/** How much darker the thick middle of a tinted film is than its thin edge. */\nconst THICK_FACTOR = 0.55\n\n/** The surfaces a grid is laid over: the floor, and optionally a wall the film folds up and a curb it folds down. */\nexport interface FilmSurfaces {\n floor: SurfaceSpec\n wall?: SurfaceSpec\n curb?: SurfaceSpec\n}\n\n/** The part of the renderer the manager uses to upload only the dirty rectangle of a texture (three 0.160 `copyTextureToTexture`). */\nexport interface FilmGpu {\n copyTextureToTexture(position: THREE_NS.Vector2, source: THREE_NS.Texture, destination: THREE_NS.Texture, level?: number): void\n}\n\nexport interface FilmViewer {\n position: Vec3\n forward: Vec3\n}\n\n/** What a baked grid leaves behind in the host's stain decals. */\nexport interface FilmBake {\n fluid: LiquidId\n /** Where the film was thickest (world), litres of it, and the colours of its wet and dry look. */\n point: Vec3\n liters: number\n tint: StainTint\n /** Radius in metres of the wet cells around `point`. */\n radius: number\n reason: 'range' | 'settled' | 'budget' | 'clear'\n}\n\nexport interface FilmManagerOptions {\n /** Where the grids are added (one group). */\n scene: THREE_NS.Object3D\n /** The host's random stream: the film never calls `Math.random`. */\n random: FilmRandom\n /** The player's eye: grids are simulated near it and drawn lit from it. A camera works; or give `viewer`. */\n camera?: THREE_NS.Camera\n viewer?: () => FilmViewer\n /** The surfaces under a world point (floor, wall, curb), or null for none. Default: the ground plane at `groundAt`. */\n surfaceAt?: (point: Vec3) => FilmSurfaces | null\n groundAt?: (x: number, z: number) => number\n /** The world whose stain decals take the film when a grid is baked (`fluids.splash`). Without it a bake only calls `onBake`. */\n fluids?: FluidWorld\n onBake?: (bake: FilmBake) => void\n /** Profiles by liquid id (default the film profiles of `game-fluids`); override one to tune it. */\n profiles?: Readonly<Record<string, FilmProfile>>\n /** Colour of the surface under the film (default the page's grey). */\n base?: number\n maxGrids?: number\n /** Metres from the player past which a grid is not simulated and is baked. */\n range?: number\n /** Milliseconds of film simulation per frame; the grids left out wait for the next frame. */\n budgetMs?: number\n /** Seconds without change after which a grid is baked and freed. */\n settleSeconds?: number\n /** Side of a grid in metres (cells are 1.25 cm) and the cell size. */\n gridMetres?: number\n cellSize?: number\n /** Seconds of drying clock that pass in one second of real time (the host scales it; default 1). */\n dryingSpeed?: number\n /** The renderer, for the partial texture uploads. Without it the whole texture goes up when it changes. */\n gpu?: FilmGpu\n /** A clock in milliseconds (default `performance.now`). */\n now?: () => number\n}\n\nexport interface FilmStats {\n grids: number\n drops: number\n wetCells: number\n /** Milliseconds the last update spent simulating. */\n simMs: number\n /** Grids left out of the last update by the budget. */\n starved: number\n baked: number\n /** Texels written by partial uploads in the last update. */\n uploadedTexels: number\n}\n\nexport interface FilmLaunch {\n fluid: LiquidId\n point: Vec3\n /** Default straight down at 2 m/s. */\n velocity?: Vec3\n /** Millimetres (default 3). */\n radius?: number\n surfaces?: FilmSurfaces\n /** The colours of this splash (an infection age: fresh red, dry brown): the film is drawn with them instead of the liquid's own. */\n tint?: StainTint\n}\n\nexport interface FilmDripSpec {\n fluid: LiquidId\n point: Vec3\n /** Average seconds between drops. */\n every: number\n radius?: number\n surfaces?: FilmSurfaces\n tint?: StainTint\n}\n\nconst sameTint = (a: StainTint | undefined, b: StainTint | undefined): boolean => a?.fresh === b?.fresh && a?.dry === b?.dry\n\nconst scaleColor = (hex: number, factor: number): number =>\n (Math.round(((hex >> 16) & 255) * factor) << 16) | (Math.round(((hex >> 8) & 255) * factor) << 8) | Math.round((hex & 255) * factor)\n\n/** A profile drawn in the colours of a splash: the thin film and the drops take the fresh colour, the thick middle a darker one, and the dried film the dry colour. */\nexport function tinted(profile: FilmProfile, tint: StainTint): FilmProfile {\n return { ...profile, thin: tint.fresh, thick: scaleColor(tint.fresh, THICK_FACTOR), dry: tint.dry, dryThin: tint.dry, dropColor: tint.fresh }\n}\n\nconst distance = (a: Vec3, b: Vec3): number => Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2])\n\n/**\n * Keeps the running films near the player: at most `maxGrids` grids of 2 x 2 m, only within `range`, simulated inside a millisecond budget and uploaded by their\n * dirty rectangle. A grid that is out of range, still, or squeezed out by a newer one is baked: its film becomes a stain of the host's world with the same\n * colours and amount, and the grid is freed.\n */\nexport class FilmManager {\n readonly group: THREE_NS.Group\n private readonly grids: FilmGrid[] = []\n private readonly profiles: Readonly<Record<string, FilmProfile>>\n private readonly staging = new Map<string, THREE_NS.DataTexture>()\n private readonly now: () => number\n private rain = 0\n private time = 0\n private bakedCount = 0\n private last: FilmStats = { grids: 0, drops: 0, wetCells: 0, simMs: 0, starved: 0, baked: 0, uploadedTexels: 0 }\n private empties = new Map<FilmGrid, number>()\n\n constructor(\n private readonly THREE: ThreeLib,\n private readonly options: FilmManagerOptions,\n ) {\n this.group = new THREE.Group()\n this.group.name = 'film_manager'\n options.scene.add(this.group)\n this.profiles = options.profiles ?? Object.fromEntries(FILM_PROFILES.map((profile) => [profile.id, profile]))\n this.now = options.now ?? (() => (typeof performance === 'undefined' ? Date.now() : performance.now()))\n }\n\n get gridCount(): number {\n return this.grids.length\n }\n\n /** The grids, for a host that wants to look at them (a bench, a test). */\n list(): readonly FilmGrid[] {\n return this.grids\n }\n\n setWeather(weather: { rain: number }): void {\n this.rain = Math.min(1, Math.max(0, weather.rain))\n for (const grid of this.grids) grid.film.setWeather({ rain: this.rain })\n }\n\n /** Throws a drop of a liquid: it lands in the grid under it (made on the spot) and leaves the impact splash. Null when it is out of range or has no surface. */\n launch(request: FilmLaunch): FilmGrid | null {\n const grid = this.gridFor(request.fluid, request.point, request.surfaces, request.tint)\n if (!grid) return null\n const velocity = request.velocity ?? [0, -FALL_SPEED, 0]\n grid.film.launch(request.point, velocity, request.radius ?? DEFAULT_DROP_RADIUS)\n return grid\n }\n\n /** A drip: a bead swells at `point`, detaches and falls every `every` seconds on average. Replaces the grid's drip. */\n drip(spec: FilmDripSpec): FilmGrid | null {\n const grid = this.gridFor(spec.fluid, spec.point, spec.surfaces, spec.tint)\n if (!grid) return null\n grid.film.drip = { position: [spec.point[0], spec.point[1], spec.point[2]], every: spec.every, base: spec.every, elapsed: 0, radius: spec.radius ?? DEFAULT_DROP_RADIUS }\n return grid\n }\n\n /** The grid of a liquid that covers `point`, made when there is none. Null when `point` is out of range or has no surface under it. */\n gridFor(fluid: LiquidId, point: Vec3, surfaces?: FilmSurfaces, tint?: StainTint): FilmGrid | null {\n const viewer = this.viewer()\n if (viewer && distance(viewer.position, point) > (this.options.range ?? DEFAULT_RANGE)) return null\n const existing = this.grids.find((grid) => grid.profile.id === fluid && sameTint(grid.tint, tint) && this.covers(grid, point))\n if (existing) return existing\n const base = this.profiles[fluid] ?? filmProfileOf(fluid)\n if (!base) return null\n const profile = tint ? tinted(base, tint) : base\n const found = surfaces ?? this.surfacesAt(point)\n if (!found) return null\n const layout = layoutFromSurfaces({ ...found, center: point, columns: this.columns(), rows: this.columns(), cellSize: this.options.cellSize ?? DEFAULT_CELL })\n if (this.grids.length >= (this.options.maxGrids ?? DEFAULT_MAX_GRIDS)) this.evict(point)\n const grid = new FilmGrid(this.THREE, { layout, profile, random: this.options.random, center: point, ...(tint ? { tint } : {}), ...(this.options.base === undefined ? {} : { base: this.options.base }) })\n grid.film.setWeather({ rain: this.rain })\n this.grids.push(grid)\n this.group.add(grid.group)\n return grid\n }\n\n /** One frame: simulates the grids near the player inside the budget, uploads what changed, bakes the ones that are far or still. */\n update(dt: number): FilmStats {\n this.time += dt\n const viewer = this.viewer()\n const range = this.options.range ?? DEFAULT_RANGE\n const budget = this.options.budgetMs ?? DEFAULT_BUDGET_MS\n const drying = dt * (this.options.dryingSpeed ?? 1)\n const order = [...this.grids].sort((a, b) => (viewer ? distance(viewer.position, a.center) - distance(viewer.position, b.center) : 0))\n const start = this.now()\n let starved = 0\n let uploaded = 0\n for (const grid of order) {\n if (viewer && distance(viewer.position, grid.center) > range * BAKE_RANGE_FACTOR) {\n this.release(grid, 'range')\n continue\n }\n if (this.now() - start > budget) {\n starved++\n continue\n }\n if (viewer) grid.setView(viewer.position, viewer.forward, this.time)\n const rect = grid.update(dt, drying)\n grid.syncDrops()\n if (rect) uploaded += this.upload(grid, rect)\n if (this.emptyFor(grid, dt) || grid.idle > (this.options.settleSeconds ?? DEFAULT_SETTLE_SECONDS)) this.release(grid, 'settled')\n }\n let drops = 0\n let wet = 0\n for (const grid of this.grids) {\n const { active } = grid.film.drops\n for (let i = 0; i < active.length; i++) drops += active[i]!\n wet += grid.film.wetCells()\n }\n this.last = { grids: this.grids.length, drops, wetCells: wet, simMs: this.now() - start, starved, baked: this.bakedCount, uploadedTexels: uploaded }\n return this.last\n }\n\n stats(): FilmStats {\n return this.last\n }\n\n /** Frees every grid. `bake` leaves each one's film as a stain first. */\n clear(bake = false): void {\n for (const grid of [...this.grids]) {\n if (bake) this.release(grid, 'clear')\n else this.free(grid)\n }\n }\n\n dispose(): void {\n this.clear()\n for (const texture of this.staging.values()) texture.dispose()\n this.staging.clear()\n this.options.scene.remove(this.group)\n }\n\n // ---- internals ----\n\n private columns(): number {\n return Math.round((this.options.gridMetres ?? DEFAULT_GRID_METRES) / (this.options.cellSize ?? DEFAULT_CELL))\n }\n\n private viewer(): FilmViewer | null {\n if (this.options.viewer) return this.options.viewer()\n const camera = this.options.camera\n if (!camera) return null\n const forward = new this.THREE.Vector3()\n camera.getWorldDirection(forward)\n return { position: [camera.position.x, camera.position.y, camera.position.z], forward: [forward.x, forward.y, forward.z] }\n }\n\n private surfacesAt(point: Vec3): FilmSurfaces | null {\n if (this.options.surfaceAt) return this.options.surfaceAt(point)\n const y = this.options.groundAt?.(point[0], point[2]) ?? 0\n return { floor: { id: 'film_ground', point: [point[0], y, point[2]], normal: [0, 1, 0], halfSize: [DEFAULT_HALF, DEFAULT_HALF], porosity: 0 } }\n }\n\n /** Whether `point` is over the grid, away from its edge, so a splash there does not fall off the strip. */\n private covers(grid: FilmGrid, point: Vec3): boolean {\n const layout: FilmLayout = grid.film.layout\n const wide = layout.width * layout.cellSize\n for (const region of layout.regions) {\n const local: Vec3 = [point[0] - region.origin[0], point[1] - region.origin[1], point[2] - region.origin[2]]\n const above = local[0] * region.normal[0] + local[1] * region.normal[1] + local[2] * region.normal[2]\n if (above < -FLOOR_REACH || above > DROP_HEIGHT) continue\n const sideways = local[0] * region.columnAxis[0] + local[1] * region.columnAxis[1] + local[2] * region.columnAxis[2]\n const down = local[0] * region.rowAxis[0] + local[1] * region.rowAxis[1] + local[2] * region.rowAxis[2]\n const length = region.rowCount * layout.cellSize\n if (sideways > EDGE_MARGIN && sideways < wide - EDGE_MARGIN && down > EDGE_MARGIN && down < length - EDGE_MARGIN) return true\n }\n return false\n }\n\n /** The grid squeezed out for a new one at `point`: the one that has been still the longest, then the farthest from the player. */\n private evict(point: Vec3): void {\n const viewer = this.viewer()\n const score = (grid: FilmGrid): number => grid.idle * 10 + (viewer ? distance(viewer.position, grid.center) : distance(point, grid.center))\n const victim = [...this.grids].sort((a, b) => score(b) - score(a))[0]\n if (victim) this.release(victim, 'budget')\n }\n\n /** A grid that has nothing wet for two seconds goes without a trace. */\n private emptyFor(grid: FilmGrid, dt: number): boolean {\n if (grid.film.wetCells() > 0 || grid.dropsInFlight() || grid.film.drip || grid.film.sources.length > 0) {\n this.empties.delete(grid)\n return false\n }\n const seconds = (this.empties.get(grid) ?? 0) + dt\n this.empties.set(grid, seconds)\n return seconds > FREE_AFTER_EMPTY\n }\n\n private free(grid: FilmGrid): void {\n const at = this.grids.indexOf(grid)\n if (at < 0) return\n this.grids.splice(at, 1)\n this.empties.delete(grid)\n this.group.remove(grid.group)\n grid.dispose()\n }\n\n /** Bakes the film of a grid into the host's stain decals and frees the grid. */\n private release(grid: FilmGrid, reason: FilmBake['reason']): void {\n const bake = this.measure(grid, reason)\n this.free(grid)\n if (!bake) return\n this.bakedCount++\n this.options.fluids?.splash(bake.fluid, bake.point, bake.liters, undefined, { tint: bake.tint })\n this.options.onBake?.(bake)\n }\n\n /** The film's centre of volume, its litres (millimetres of film times cell area) and the radius of the wet cells. */\n private measure(grid: FilmGrid, reason: FilmBake['reason']): FilmBake | null {\n const { thickness } = grid.film.field\n const { width, cellSize } = grid.film.layout\n let volume = 0\n let x = 0\n let y = 0\n let z = 0\n const wet: number[] = []\n for (let i = 0; i < thickness.length; i++) {\n const h = thickness[i]!\n if (h <= VISIBLE_THICKNESS) continue\n const at = grid.film.layout.cellToWorld(Math.floor(i / width) + 0.5, (i % width) + 0.5)\n volume += h\n x += at[0] * h\n y += at[1] * h\n z += at[2] * h\n wet.push(i)\n }\n if (volume === 0) return null\n const point: Vec3 = [x / volume, y / volume, z / volume]\n let radius = 0\n for (const i of wet) radius = Math.max(radius, distance(grid.film.layout.cellToWorld(Math.floor(i / width) + 0.5, (i % width) + 0.5), point))\n const profile = grid.profile\n return { fluid: profile.id, point, liters: volume * cellSize * cellSize, tint: grid.tint ?? { fresh: profile.thin, dry: profile.dry }, radius, reason }\n }\n\n /** Copies the dirty rectangle to the GPU texture; answers the texels written. */\n private upload(grid: FilmGrid, rect: CellRect): number {\n const gpu = this.options.gpu\n const width = rect.lastColumn - rect.firstColumn + 1\n const height = rect.lastRow - rect.firstRow + 1\n const whole = grid.film.layout.width * grid.film.layout.height\n if (!gpu || !grid.uploaded || width * height >= whole * 0.6) {\n grid.texture.needsUpdate = true\n return whole\n }\n const staging = this.stagingFor(width, height)\n const target = staging.image.data as unknown as Uint8Array\n const stride = grid.film.layout.width * 4\n for (let row = 0; row < height; row++) {\n const from = (rect.firstRow + row) * stride + rect.firstColumn * 4\n target.set(grid.data.subarray(from, from + width * 4), row * width * 4)\n }\n staging.needsUpdate = true\n gpu.copyTextureToTexture(new this.THREE.Vector2(rect.firstColumn, rect.firstRow), staging, grid.texture)\n return width * height\n }\n\n private stagingFor(width: number, height: number): THREE_NS.DataTexture {\n const key = `${width}x${height}`\n let texture = this.staging.get(key)\n if (!texture) {\n if (this.staging.size >= DEFAULT_MAX_STAGING) {\n const oldest = this.staging.keys().next().value as string\n this.staging.get(oldest)?.dispose()\n this.staging.delete(oldest)\n }\n texture = new this.THREE.DataTexture(new Uint8Array(new ArrayBuffer(width * height * 4)), width, height, this.THREE.RGBAFormat)\n texture.magFilter = this.THREE.LinearFilter\n texture.minFilter = this.THREE.LinearFilter\n this.staging.set(key, texture)\n }\n return texture\n }\n}\n\nexport function createFilmManager(THREE: ThreeLib, options: FilmManagerOptions): FilmManager {\n return new FilmManager(THREE, options)\n}\n","import type { FilmProfile, FluidEvent, FluidWorld, LiquidId, StainTint, Vec3 } from '@adatechnology/game-fluids'\nimport { FILM_PROFILES } from '@adatechnology/game-fluids'\nimport type { FilmSurfaces, FilmManager } from './manager'\n\nconst DEFAULT_DROP_LITERS = 0.00015\nconst DEFAULT_DROP_RADIUS = 3.2\nconst MAX_DROPS_PER_FRAME = 6\nconst RADIUS_SPREAD = 0.3\nconst RADIUS_BASE = 0.85\nconst DEFAULT_SPLAT_SPEED = 2.5\n\n/** A splash on the ground or a wall (a wound, a body falling): the film draws it as a drop landing, in the colours of `tint` when given. */\nexport interface FilmSplat {\n fluid: LiquidId\n point: Vec3\n /** The way the liquid travels (default straight down). */\n direction?: Vec3\n /** Metres per second (default 2.5). */\n speed?: number\n /** Drop radius in millimetres (default 3.2). */\n radius?: number\n /** Drops thrown (default 1). */\n count?: number\n surfaces?: FilmSurfaces\n tint?: StainTint\n}\n\nexport interface FilmBridgeOptions {\n fluids: FluidWorld\n manager: FilmManager\n /** The seeded stream that spreads the drops (the film never calls `Math.random`). */\n random: () => number\n /** Which film profile a world liquid runs as: by default the liquids that have one (`blood`, `old_blood`, `bile`, `motor_oil`, `fuel`). Map `diesel` to `fuel`, say. */\n liquids?: Readonly<Record<string, LiquidId>>\n /** Litres of one drop of a leak (default 0.00015). */\n dropLiters?: number\n surfaceAt?: (point: Vec3) => FilmSurfaces | null\n}\n\nexport interface FilmBridge {\n /** Throws drops of a splash into the film. Returns how many found a grid. */\n splat(splat: FilmSplat): number\n /** Weather of the film (the Phase 6 hook): while it rains the film does not dry and spreads 1.5 times faster. */\n setWeather(weather: { rain: number }): void\n /** Streams of the world that feed the film right now. */\n readonly leaks: number\n /** Feeds the leaks: call it each frame with the frame's seconds, before or after `manager.update`. */\n update(dt: number): void\n dispose(): void\n}\n\nconst filmLiquids = (): Record<string, LiquidId> => Object.fromEntries(FILM_PROFILES.map((profile: FilmProfile) => [profile.id, profile.id]))\n\n/**\n * Joins a fluid world to the film: a `leak` of a liquid that has a film (blood, fuel, oil) drips onto the ground or wall below as drops thrown from the\n * hole, at the rate the world gives, until the stream ends. `splat` is what the characters use for a wound (with the colours of the infection age as\n * `tint`), and `setWeather` is the rain hook.\n */\nexport function createFilmBridge(options: FilmBridgeOptions): FilmBridge {\n const { fluids, manager, random } = options\n const liquids = options.liquids ?? filmLiquids()\n const dropLiters = options.dropLiters ?? DEFAULT_DROP_LITERS\n const leaks = new Map<string, { fluid: LiquidId; owed: number }>()\n\n const offLeak = fluids.on('leak', (event: FluidEvent) => {\n if (event.type !== 'leak') return\n const fluid = liquids[event.liquid]\n if (fluid) leaks.set(event.stream, { fluid, owed: 0 })\n })\n const offEnd = fluids.on('stream_end', (event: FluidEvent) => {\n if (event.type === 'stream_end') leaks.delete(event.stream)\n })\n\n const surfaces = (point: Vec3): FilmSurfaces | undefined => options.surfaceAt?.(point) ?? undefined\n\n return {\n get leaks() {\n return leaks.size\n },\n splat(splat) {\n const count = splat.count ?? 1\n const speed = splat.speed ?? DEFAULT_SPLAT_SPEED\n const direction = splat.direction ?? [0, -1, 0]\n const length = Math.hypot(direction[0], direction[1], direction[2]) || 1\n const velocity: Vec3 = [(direction[0] / length) * speed, (direction[1] / length) * speed, (direction[2] / length) * speed]\n let landed = 0\n for (let i = 0; i < count; i++) {\n const grid = manager.launch({\n fluid: splat.fluid,\n point: splat.point,\n velocity,\n radius: (splat.radius ?? DEFAULT_DROP_RADIUS) * (RADIUS_BASE + random() * RADIUS_SPREAD),\n ...(splat.surfaces ? { surfaces: splat.surfaces } : {}),\n ...(splat.tint ? { tint: splat.tint } : {}),\n })\n if (grid) landed++\n }\n return landed\n },\n setWeather: (weather) => manager.setWeather(weather),\n update(dt) {\n if (leaks.size === 0) return\n const streams = new Map(fluids.streams().map((stream) => [stream.id, stream]))\n for (const [id, leak] of leaks) {\n const stream = streams.get(id)\n if (!stream) continue\n leak.owed += (stream.rate * dt) / dropLiters\n let thrown = 0\n while (leak.owed >= 1 && thrown < MAX_DROPS_PER_FRAME) {\n leak.owed -= 1\n thrown++\n manager.launch({\n fluid: leak.fluid,\n point: stream.origin,\n velocity: stream.velocity,\n radius: DEFAULT_DROP_RADIUS * (RADIUS_BASE + random() * RADIUS_SPREAD),\n ...(surfaces(stream.origin) ? { surfaces: surfaces(stream.origin)! } : {}),\n })\n }\n if (thrown === MAX_DROPS_PER_FRAME) leak.owed = 0\n }\n },\n dispose() {\n offLeak()\n offEnd()\n leaks.clear()\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;;;;ACGO,MAAM,gBAAyB,EAAE,QAAQ,IAAI,QAAQ,GAAG,OAAO,GAAG,aAAa,IAAI;AAcnF,WAAS,cAAc,KAA8B;AAC1D,UAAM,SAAmB,CAAC;AAC1B,UAAM,QAAkB,CAAC;AACzB,UAAM,gBAAgB,IAAI,SAAS,IAAI;AACvC,WAAO;MACL,SAAS,MAAM,GAAG,GAAG;AACnB,YAAI,OAAO;AACX,mBAAWA,UAAS,QAAQ;AAC1B,cAAI,OAAOA,OAAM,OAAO,IAAI,OAAQ,QAAO,MAAM,IAAIA;cAChD,OAAM,KAAKA,MAAK;QACvB;AACA,eAAO,SAAS;AAChB,YAAI,OAAO;AACX,mBAAWA,UAAS,OAAQ,MAAKA,OAAM,IAAI,MAAM,KAAKA,OAAM,IAAI,MAAM,IAAI,cAAe;AACzF,cAAM,QAAQ,MAAM,IAAI,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,GAAG,EAAE;AACnD,cAAM,OAAO;AACb,cAAM,IAAI;AACV,cAAM,IAAI;AACV,eAAO,KAAK,KAAK;AACjB,eAAO,QAAQ,IAAI,QAAQ,IAAI,cAAc;MAC/C;MACA,QAAQ;AACN,mBAAW,SAAS,OAAQ,OAAM,KAAK,KAAK;AAC5C,eAAO,SAAS;MAClB;IACF;EACF;ACrCO,MAAM,WAAyD;IACpE,MAAM,EAAE,OAAO,MAAM,QAAQ,MAAM,OAAO,KAAK,QAAQ,KAAK,SAAS,EAAE,KAAK,KAAM,QAAQ,IAAK,GAAG,SAAS,KAAM,QAAQ,IAAI;IAC7H,QAAQ,EAAE,OAAO,MAAM,QAAQ,MAAM,OAAO,KAAK,QAAQ,KAAK,SAAS,EAAE,KAAK,MAAM,QAAQ,IAAI,GAAG,SAAS,KAAM,QAAQ,IAAI;IAC9H,KAAK,EAAE,OAAO,KAAK,QAAQ,KAAK,OAAO,IAAI,QAAQ,KAAK,SAAS,EAAE,KAAK,KAAK,QAAQ,IAAI,GAAG,SAAS,KAAK,QAAQ,GAAG;EACvH;AAGO,MAAM,eAAe,EAAE,WAAW,KAAK,QAAQ,IAAI,aAAa,GAAG,YAAY,EAAE;ACbxF,MAAA,4BAAA;IACE,kBAAoB;IACpB,QAAU;EACZ;ACHA,MAAA,qBAAA;IACE,KAAO;MACL,IAAM;MACN,QAAU;MACV,QAAU;MACV,cAAgB;MAChB,WAAa;MACb,MAAQ;MACR,OAAS;QACP,OAAS;QACT,OAAS;QACT,MAAQ;MACV;MACA,MAAQ;QACN,OAAS;QACT,OAAS;MACX;MACA,UAAY;QACV,OAAS;QACT,MAAQ;QACR,OAAS;MACX;MACA,QAAU;QACR,OAAS;QACT,QAAU;UACR;UACA;UACA;QACF;QACA,KAAO;MACT;MACA,MAAQ;QACN,QAAU;QACV,QAAU;QACV,MAAQ;MACV;MACA,OAAS;QACP,OAAS;QACT,MAAQ;QACR,OAAS;QACT,WAAa;MACf;MACA,OAAS;QACP,UAAY;MACd;MACA,OAAS;MACT,MAAQ;MACR,SAAW;QACT;QACA;MACF;IACF;IACA,SAAW;MACT,IAAM;MACN,QAAU;MACV,QAAU;MACV,cAAgB;MAChB,WAAa;MACb,MAAQ;MACR,MAAQ;MACR,eAAiB;MACjB,OAAS;QACP,OAAS;QACT,OAAS;QACT,MAAQ;MACV;MACA,MAAQ;QACN,OAAS;QACT,OAAS;MACX;MACA,UAAY;QACV,OAAS;QACT,MAAQ;QACR,OAAS;MACX;MACA,QAAU;QACR,OAAS;QACT,QAAU;UACR;UACA;UACA;QACF;QACA,KAAO;MACT;MACA,MAAQ;QACN,QAAU;MACZ;MACA,OAAS;QACP,OAAS;QACT,MAAQ;QACR,OAAS;QACT,WAAa;MACf;MACA,OAAS;MACT,OAAS;MACT,MAAQ;MACR,QAAU;MACV,SAAW;QACT;QACA;MACF;IACF;IACA,WAAa;MACX,IAAM;MACN,QAAU;MACV,QAAU;MACV,cAAgB;MAChB,WAAa;MACb,MAAQ;MACR,OAAS;QACP,OAAS;QACT,OAAS;QACT,MAAQ;QACR,MAAQ;MACV;MACA,MAAQ;QACN,OAAS;QACT,OAAS;MACX;MACA,UAAY;QACV,OAAS;QACT,MAAQ;QACR,OAAS;QACT,OAAS;MACX;MACA,QAAU;QACR,OAAS;QACT,QAAU;UACR;UACA;QACF;QACA,OAAS;QACT,KAAO;MACT;MACA,MAAQ;QACN,QAAU;QACV,QAAU;QACV,MAAQ;MACV;MACA,OAAS;QACP,OAAS;QACT,MAAQ;QACR,OAAS;QACT,WAAa;MACf;MACA,OAAS;QACP,UAAY;MACd;MACA,OAAS;MACT,MAAQ;MACR,SAAW;QACT;QACA;MACF;IACF;IACA,aAAe;MACb,IAAM;MACN,QAAU;MACV,QAAU;MACV,cAAgB;MAChB,WAAa;MACb,MAAQ;MACR,MAAQ;MACR,OAAS;QACP,OAAS;QACT,OAAS;QACT,MAAQ;MACV;MACA,MAAQ;QACN,OAAS;QACT,OAAS;MACX;MACA,UAAY;QACV,OAAS;QACT,MAAQ;QACR,OAAS;QACT,QAAU;MACZ;MACA,QAAU;QACR,OAAS;QACT,QAAU;UACR;UACA;QACF;QACA,OAAS;QACT,KAAO;MACT;MACA,MAAQ;QACN,QAAU;MACZ;MACA,OAAS;QACP,OAAS;QACT,MAAQ;QACR,OAAS;QACT,WAAa;MACf;MACA,OAAS;QACP,UAAY;MACd;MACA,OAAS;MACT,MAAQ;MACR,UAAY;QACV,QAAU;QACV,QAAU;QACV,OAAS;QACT,MAAQ;QACR,MAAQ;MACV;MACA,eAAiB;QACf;UACE,QAAU;UACV,MAAQ;UACR,MAAQ;QACV;MACF;MACA,QAAU;MACV,UAAY;MACZ,SAAW;QACT;QACA;MACF;IACF;IACA,UAAY;MACV,IAAM;MACN,QAAU;MACV,QAAU;MACV,cAAgB;MAChB,WAAa;MACb,MAAQ;MACR,OAAS;QACP,OAAS;QACT,OAAS;QACT,MAAQ;MACV;MACA,MAAQ;QACN,OAAS;QACT,OAAS;MACX;MACA,UAAY;QACV,OAAS;QACT,MAAQ;QACR,OAAS;MACX;MACA,QAAU;QACR,OAAS;QACT,QAAU;UACR;UACA;UACA;QACF;QACA,KAAO;QACP,OAAS;MACX;MACA,MAAQ;QACN,QAAU;QACV,QAAU;QACV,MAAQ;MACV;MACA,OAAS;QACP,OAAS;QACT,MAAQ;QACR,OAAS;QACT,WAAa;MACf;MACA,OAAS;QACP,UAAY;MACd;MACA,OAAS;MACT,MAAQ;MACR,SAAW;QACT;QACA;MACF;IACF;IACA,IAAM;MACJ,IAAM;MACN,QAAU;MACV,QAAU;MACV,cAAgB;MAChB,WAAa;MACb,MAAQ;MACR,OAAS;QACP,OAAS;QACT,OAAS;QACT,MAAQ;MACV;MACA,MAAQ;QACN,OAAS;QACT,OAAS;MACX;MACA,UAAY;QACV,OAAS;QACT,MAAQ;QACR,OAAS;MACX;MACA,QAAU;QACR,OAAS;QACT,QAAU;UACR;UACA;UACA;QACF;QACA,KAAO;QACP,OAAS;MACX;MACA,MAAQ;QACN,QAAU;QACV,QAAU;QACV,MAAQ;MACV;MACA,OAAS;QACP,OAAS;QACT,MAAQ;QACR,OAAS;QACT,WAAa;MACf;MACA,OAAS;QACP,UAAY;MACd;MACA,OAAS;MACT,MAAQ;MACR,SAAW;QACT;QACA;MACF;IACF;IACA,UAAY;MACV,IAAM;MACN,QAAU;MACV,QAAU;MACV,cAAgB;MAChB,WAAa;MACb,MAAQ;MACR,OAAS;QACP,OAAS;QACT,OAAS;QACT,MAAQ;MACV;MACA,MAAQ;QACN,OAAS;QACT,OAAS;MACX;MACA,UAAY;QACV,OAAS;QACT,MAAQ;QACR,OAAS;QACT,OAAS;MACX;MACA,QAAU;QACR,OAAS;QACT,QAAU;UACR;UACA;UACA;QACF;QACA,KAAO;MACT;MACA,MAAQ;QACN,QAAU;MACZ;MACA,OAAS;QACP,OAAS;QACT,MAAQ;QACR,OAAS;QACT,WAAa;MACf;MACA,OAAS;QACP,UAAY;MACd;MACA,OAAS;MACT,MAAQ;MACR,MAAQ;QACN,OAAS;QACT,MAAQ;QACR,QAAU;MACZ;MACA,eAAiB;MACjB,SAAW;QACT;QACA;MACF;IACF;IACA,WAAa;MACX,IAAM;MACN,QAAU;MACV,QAAU;MACV,cAAgB;MAChB,WAAa;MACb,MAAQ;MACR,OAAS;QACP,OAAS;QACT,OAAS;QACT,MAAQ;QACR,MAAQ;MACV;MACA,MAAQ;QACN,OAAS;QACT,OAAS;MACX;MACA,UAAY;QACV,OAAS;QACT,MAAQ;QACR,OAAS;MACX;MACA,QAAU;QACR,OAAS;QACT,QAAU;UACR;UACA;UACA;QACF;QACA,KAAO;MACT;MACA,MAAQ;QACN,QAAU;MACZ;MACA,OAAS;QACP,OAAS;QACT,MAAQ;QACR,OAAS;QACT,WAAa;QACb,UAAY;MACd;MACA,OAAS;MACT,OAAS;MACT,MAAQ;MACR,SAAW;QACT;QACA;MACF;IACF;IACA,UAAY;MACV,IAAM;MACN,QAAU;MACV,QAAU;MACV,cAAgB;MAChB,WAAa;MACb,MAAQ;MACR,OAAS;QACP,OAAS;QACT,OAAS;QACT,MAAQ;MACV;MACA,MAAQ;QACN,OAAS;QACT,OAAS;MACX;MACA,UAAY;QACV,OAAS;QACT,MAAQ;QACR,OAAS;MACX;MACA,QAAU;QACR,OAAS;QACT,QAAU;UACR;UACA;UACA;QACF;QACA,KAAO;MACT;MACA,MAAQ;QACN,QAAU;QACV,QAAU;QACV,MAAQ;MACV;MACA,OAAS;QACP,OAAS;QACT,MAAQ;QACR,OAAS;QACT,WAAa;MACf;MACA,OAAS;QACP,UAAY;MACd;MACA,OAAS;MACT,MAAQ;MACR,SAAW;QACT;QACA;MACF;IACF;IACA,UAAY;MACV,IAAM;MACN,QAAU;MACV,QAAU;MACV,cAAgB;MAChB,WAAa;MACb,MAAQ;MACR,OAAS;QACP,OAAS;QACT,OAAS;QACT,MAAQ;MACV;MACA,MAAQ;QACN,OAAS;QACT,OAAS;MACX;MACA,UAAY;QACV,OAAS;QACT,MAAQ;QACR,OAAS;MACX;MACA,QAAU;QACR,OAAS;QACT,QAAU;UACR;UACA;QACF;QACA,KAAO;MACT;MACA,MAAQ;QACN,QAAU;MACZ;MACA,OAAS;QACP,OAAS;QACT,MAAQ;QACR,OAAS;QACT,WAAa;MACf;MACA,OAAS;QACP,UAAY;MACd;MACA,OAAS;MACT,MAAQ;MACR,eAAiB;MACjB,SAAW;QACT;QACA;MACF;IACF;IACA,UAAY;MACV,IAAM;MACN,QAAU;MACV,QAAU;MACV,cAAgB;MAChB,WAAa;MACb,MAAQ;MACR,OAAS;QACP,OAAS;QACT,OAAS;QACT,MAAQ;MACV;MACA,MAAQ;QACN,OAAS;QACT,OAAS;MACX;MACA,UAAY;QACV,OAAS;QACT,MAAQ;QACR,OAAS;MACX;MACA,QAAU;QACR,OAAS;QACT,QAAU;UACR;UACA;QACF;QACA,KAAO;MACT;MACA,MAAQ;QACN,QAAU;QACV,QAAU;QACV,MAAQ;MACV;MACA,OAAS;QACP,OAAS;QACT,MAAQ;QACR,OAAS;QACT,WAAa;MACf;MACA,OAAS;QACP,UAAY;MACd;MACA,OAAS;MACT,MAAQ;MACR,eAAiB;MACjB,SAAW;QACT;QACA;MACF;IACF;IACA,MAAQ;MACN,IAAM;MACN,QAAU;MACV,QAAU;MACV,cAAgB;MAChB,WAAa;MACb,MAAQ;MACR,OAAS;QACP,OAAS;QACT,OAAS;QACT,MAAQ;MACV;MACA,MAAQ;QACN,OAAS;QACT,OAAS;MACX;MACA,UAAY;QACV,OAAS;QACT,MAAQ;QACR,OAAS;QACT,QAAU;MACZ;MACA,QAAU;QACR,OAAS;QACT,QAAU;UACR;UACA;QACF;QACA,KAAO;MACT;MACA,MAAQ;QACN,QAAU;MACZ;MACA,OAAS;QACP,OAAS;QACT,MAAQ;QACR,OAAS;QACT,WAAa;MACf;MACA,OAAS;QACP,UAAY;MACd;MACA,OAAS;MACT,MAAQ;MACR,eAAiB;MACjB,SAAW;QACT;QACA;MACF;IACF;IACA,aAAe;MACb,IAAM;MACN,QAAU;MACV,QAAU;MACV,cAAgB;MAChB,WAAa;MACb,MAAQ;MACR,MAAQ;MACR,OAAS;QACP,OAAS;QACT,OAAS;QACT,MAAQ;MACV;MACA,MAAQ;QACN,OAAS;QACT,OAAS;MACX;MACA,UAAY;QACV,OAAS;QACT,MAAQ;QACR,OAAS;QACT,QAAU;MACZ;MACA,QAAU;QACR,OAAS;QACT,QAAU;UACR;UACA;QACF;QACA,OAAS;QACT,KAAO;MACT;MACA,MAAQ;QACN,QAAU;MACZ;MACA,OAAS;QACP,OAAS;QACT,MAAQ;QACR,OAAS;QACT,WAAa;MACf;MACA,OAAS;QACP,UAAY;MACd;MACA,OAAS;MACT,MAAQ;MACR,UAAY;QACV,QAAU;QACV,QAAU;QACV,OAAS;QACT,MAAQ;QACR,MAAQ;MACV;MACA,eAAiB;QACf;UACE,IAAM;UACN,IAAM;UACN,QAAU;UACV,MAAQ;QACV;QACA;UACE,IAAM;UACN,IAAM;UACN,QAAU;UACV,MAAQ;QACV;QACA;UACE,IAAM;UACN,IAAM;UACN,QAAU;UACV,MAAQ;QACV;MACF;MACA,QAAU;MACV,UAAY;MACZ,SAAW;QACT;QACA;MACF;IACF;IACA,eAAiB;MACf,IAAM;MACN,QAAU;MACV,QAAU;MACV,cAAgB;MAChB,WAAa;MACb,MAAQ;MACR,MAAQ;MACR,OAAS;QACP,OAAS;QACT,OAAS;QACT,MAAQ;MACV;MACA,MAAQ;QACN,OAAS;QACT,OAAS;MACX;MACA,UAAY;QACV,OAAS;QACT,MAAQ;QACR,OAAS;QACT,QAAU;MACZ;MACA,QAAU;QACR,OAAS;QACT,QAAU;UACR;UACA;UACA;QACF;QACA,OAAS;QACT,KAAO;MACT;MACA,MAAQ;QACN,QAAU;MACZ;MACA,OAAS;QACP,OAAS;QACT,MAAQ;QACR,OAAS;QACT,WAAa;MACf;MACA,OAAS;QACP,UAAY;MACd;MACA,OAAS;MACT,MAAQ;MACR,UAAY;QACV,QAAU;QACV,QAAU;QACV,OAAS;QACT,MAAQ;QACR,MAAQ;MACV;MACA,QAAU;MACV,UAAY;MACZ,SAAW;QACT;QACA;MACF;IACF;IACA,WAAa;MACX,IAAM;MACN,QAAU;MACV,QAAU;MACV,cAAgB;MAChB,WAAa;MACb,MAAQ;MACR,MAAQ;MACR,OAAS;QACP,OAAS;QACT,OAAS;QACT,MAAQ;MACV;MACA,MAAQ;QACN,OAAS;QACT,OAAS;MACX;MACA,UAAY;QACV,OAAS;QACT,MAAQ;QACR,OAAS;QACT,QAAU;MACZ;MACA,QAAU;QACR,OAAS;QACT,QAAU;UACR;UACA;UACA;QACF;QACA,OAAS;QACT,KAAO;MACT;MACA,MAAQ;QACN,QAAU;MACZ;MACA,OAAS;QACP,OAAS;QACT,MAAQ;QACR,OAAS;QACT,WAAa;MACf;MACA,OAAS;QACP,UAAY;MACd;MACA,OAAS;MACT,MAAQ;MACR,UAAY;QACV,QAAU;QACV,QAAU;QACV,OAAS;QACT,MAAQ;QACR,MAAQ;MACV;MACA,eAAiB;QACf;UACE,QAAU;UACV,MAAQ;UACR,MAAQ;QACV;MACF;MACA,QAAU;MACV,UAAY;MACZ,SAAW;QACT;QACA;MACF;IACF;IACA,mBAAqB;MACnB,IAAM;MACN,QAAU;MACV,QAAU;MACV,cAAgB;MAChB,WAAa;MACb,MAAQ;MACR,MAAQ;MACR,OAAS;QACP,OAAS;QACT,OAAS;QACT,MAAQ;MACV;MACA,MAAQ;QACN,OAAS;QACT,OAAS;MACX;MACA,UAAY;QACV,OAAS;QACT,MAAQ;QACR,OAAS;QACT,QAAU;MACZ;MACA,QAAU;QACR,OAAS;QACT,QAAU;UACR;UACA;UACA;QACF;QACA,KAAO;MACT;MACA,MAAQ;QACN,QAAU;QACV,QAAU;QACV,MAAQ;MACV;MACA,OAAS;QACP,OAAS;QACT,MAAQ;QACR,OAAS;QACT,WAAa;MACf;MACA,OAAS;QACP,UAAY;MACd;MACA,OAAS;MACT,MAAQ;MACR,UAAY;QACV,QAAU;QACV,QAAU;QACV,OAAS;QACT,MAAQ;QACR,MAAQ;QACR,UAAY;MACd;MACA,eAAiB;QACf;UACE,QAAU;UACV,MAAQ;UACR,MAAQ;UACR,OAAS;QACX;QACA;UACE,IAAM;UACN,IAAM;UACN,QAAU;UACV,MAAQ;QACV;QACA;UACE,IAAM;UACN,IAAM;UACN,QAAU;UACV,MAAQ;QACV;MACF;MACA,QAAU;MACV,UAAY;MACZ,SAAW;QACT;QACA;MACF;IACF;IACA,SAAW;MACT,IAAM;MACN,QAAU;MACV,QAAU;MACV,cAAgB;MAChB,WAAa;MACb,MAAQ;MACR,OAAS;QACP,OAAS;QACT,OAAS;QACT,MAAQ;MACV;MACA,MAAQ;QACN,OAAS;QACT,OAAS;MACX;MACA,UAAY;QACV,OAAS;QACT,MAAQ;QACR,OAAS;MACX;MACA,QAAU;QACR,OAAS;QACT,QAAU;UACR;UACA;UACA;QACF;QACA,KAAO;MACT;MACA,MAAQ;QACN,QAAU;QACV,QAAU;QACV,MAAQ;MACV;MACA,OAAS;QACP,OAAS;QACT,MAAQ;QACR,OAAS;QACT,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","names":["entry","__publicField","scale","length","DEFAULT_COLOR","min","puff","ageOrKeep","ring","floor","distance","lerp","behavior","spawn","call","UP","FRESH_BLOOD","local","BLOOD","SPEED","FORWARD","BODY_Z","TIP","PORT","WALL_Z","FACING","CENTRE","FALL_SECONDS","BODIES","SPLINTER_DIRECTION","at","SCRAPE_DIRECTION","CAR_ORIGIN","CAR_ROTATION","SPARK_DIRECTION","HITS","HIT_X","HIT_Y","blow","min","max","ring","scale","ORIGIN","OBJECT_ID_DRAWS","FLOOR","clamp","distance","x","y","random","length","SPOT","QUARTER_TURN","lerp","vx","vy","vz","ease","axis","UP","range","scale","FULL_TURN_DRAWN","length","__publicField","behavior","local","param","screen","ORIGIN","positionOf","distance","ageOrKeep","ring","puff","pick","flash","screen","scale","vy","vx","vz","call","QUARTER_TURN","floor","clamp","lerp","__publicField","__publicField","at","rnd","length","UP","range","ring","GRAVITY","QUARTER_TURN","DEFAULT_COLOR","FULL_TURN_DRAWN","scale","CAPACITY","DEFAULT_LIFE","decal","BYTE_MAX","FLOATS_PER_VECTOR","distance","cos","sin","RING_CAPACITY","camera","layer","call","local","puff","scale","length","distance","cos","sin","ease","clamp","min","max","flash","ring","RING_CAPACITY","FADE_SECONDS","decal","layer","GROUND_COLOR","GROUND_COLOR","camera","layer","sub","cross","ring","clamp","clamp","PROFILES","clamp","add","sub","dot","cross","normalize","COS","SIN","normalize","sub","dot","cross","length","distance","GRAVITY","impact","profileOf","sideX","sideZ","local","axis","distance","add","sub","dot","normalize","cross","after","ring","clamp","scale","vx","vy","vz","clamp","scale","rgb","VISIBLE_THICKNESS","camera","DEFAULT_CELL","VISIBLE_THICKNESS","grid","range","camera","local","above","length","at","DEFAULT_DROP_RADIUS","length"]}
|