@adatechnology/game-destruction-three 0.3.3 → 0.5.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 +6 -3
- package/dist/index.d.ts +30 -7
- package/dist/index.global.js +8190 -538
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +284 -454
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
package/dist/index.global.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/math.ts","../src/fragments.ts","../src/ground.ts","../src/particles.ts","../src/pieces.ts","../../game-destruction/src/chain.ts","../../game-destruction/src/math.ts","../../game-destruction/src/fracture.ts","../../game-destruction/src/data/materials.json","../../game-destruction/src/data/material-hints.json","../../game-destruction/src/materials.ts","../../game-destruction/src/world.ts","../../game-destruction/src/frame.ts","../../game-destruction/src/size.ts","../../game-destruction/src/locale.ts","../../game-destruction/src/contracts.ts","../src/spec.ts","../src/heat.ts","../src/visual.ts","../src/renderer.ts"],"sourcesContent":["export { createDestructionRenderer } from './renderer'\nexport { specFromObject3D } from './spec'\nexport type { PartHint } from './spec'\nexport { heatColor } from './heat'\nexport type { ParticleKind } from './particles'\nexport type {\n BindOptions,\n DestructionRenderer,\n Mesh,\n MeshRange,\n Object3D,\n Piece,\n Quality,\n Quat,\n RendererOptions,\n SpecFromObjectOptions,\n SpecFromObjectResult,\n ThreeNamespace,\n Vec3,\n} from './types'\n","import type { Vec3 } from '@adatechnology/game-destruction'\n\n/** Small deterministic PRNG (mulberry32): returns a function giving numbers in [0, 1). */\nexport function createRng(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/** Hash of a few numbers into an unsigned 32-bit seed. */\nexport function hashNumbers(...values: number[]): number {\n let h = 2166136261\n for (const v of values) {\n const n = Math.round(v * 1000) | 0\n h = Math.imul(h ^ n, 16777619)\n h ^= h >>> 13\n }\n return h >>> 0\n}\n\nexport function hashString(s: string): number {\n let h = 2166136261\n for (let i = 0; i < s.length; i++) h = Math.imul(h ^ s.charCodeAt(i), 16777619)\n return h >>> 0\n}\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 function vec3Length(v: Vec3): number {\n return Math.hypot(v[0], v[1], v[2])\n}\n\nexport function normalize3(v: Vec3): Vec3 {\n const l = Math.hypot(v[0], v[1], v[2])\n return l > 1e-9 ? [v[0] / l, v[1] / l, v[2] / l] : [0, 1, 0]\n}\n\n/** `0xRRGGBB` -> `[r, g, b]` in 0..1. */\nexport function hexToRgb(hex: number): [number, number, number] {\n return [((hex >> 16) & 255) / 255, ((hex >> 8) & 255) / 255, (hex & 255) / 255]\n}\n\nexport function mixHex(a: number, b: number, t: number): number {\n const ar = (a >> 16) & 255\n const ag = (a >> 8) & 255\n const ab = a & 255\n const r = Math.round(ar + (((b >> 16) & 255) - ar) * t)\n const g = Math.round(ag + (((b >> 8) & 255) - ag) * t)\n const bl = Math.round(ab + ((b & 255) - ab) * t)\n return (r << 16) | (g << 8) | bl\n}\n","import type * as THREE_NS from 'three'\nimport type { Fragment } from '@adatechnology/game-destruction'\nimport { clamp, createRng, hashString } from './math'\n\ninterface Live {\n mesh: THREE_NS.Mesh\n home: string\n vel: THREE_NS.Vector3\n ang: THREE_NS.Vector3\n half: THREE_NS.Vector3\n baseScale: THREE_NS.Vector3\n still: number\n asleep: boolean\n /** Seconds at rest. */\n rest: number\n /** 1 while alive; counts down to 0 while shrinking away. */\n fade: number\n dying: boolean\n}\n\nexport interface FragmentSystemOptions {\n scene: THREE_NS.Scene\n groundAt: (x: number, z: number) => number\n gravity: number\n max: number\n life: number\n}\n\n/** Light physics for fracture debris: gravity, ground bounce and friction, spin damping, settling flat, sleep, shrink-away and removal. */\nexport class FragmentSystem {\n private readonly list: Live[] = []\n private readonly box: THREE_NS.BoxGeometry\n private shardGeos: THREE_NS.BufferGeometry[] = []\n private readonly materials = new Map<string, THREE_NS.Material>()\n private readonly q: THREE_NS.Quaternion\n private readonly dq: THREE_NS.Quaternion\n private readonly m: THREE_NS.Matrix4\n private readonly v: THREE_NS.Vector3\n private readonly w: THREE_NS.Vector3\n private readonly rng = createRng(0x51ed270b)\n\n constructor(\n private readonly THREE: typeof THREE_NS,\n private readonly o: FragmentSystemOptions,\n ) {\n this.box = new THREE.BoxGeometry(1, 1, 1)\n this.q = new THREE.Quaternion()\n this.dq = new THREE.Quaternion()\n this.m = new THREE.Matrix4()\n this.v = new THREE.Vector3()\n this.w = new THREE.Vector3()\n }\n\n get count(): number {\n return this.list.length\n }\n\n /** Meshes of every live fragment (tests and picking). */\n meshes(): THREE_NS.Mesh[] {\n return this.list.map((f) => f.mesh)\n }\n\n /** One shared material per source material id + colour (+ transparency). */\n materialFor(materialId: string, color: number, transparent: boolean, opacity = 0.5): THREE_NS.Material {\n const key = `${materialId}:${color}:${transparent ? 1 : 0}`\n let m = this.materials.get(key)\n if (!m) {\n m = transparent\n ? new this.THREE.MeshStandardMaterial({ color, roughness: 0.15, metalness: 0.1, transparent: true, opacity, side: this.THREE.DoubleSide, depthWrite: false })\n : new this.THREE.MeshLambertMaterial({ color })\n this.materials.set(key, m)\n }\n return m\n }\n\n private shardGeometry(seed: number): THREE_NS.BufferGeometry {\n const idx = seed % 6\n let g = this.shardGeos[idx]\n if (!g) {\n const r = createRng(1000 + idx)\n const n = 3 + Math.floor(r() * 3) // triangle, quad or pentagon, irregular\n const shape = new this.THREE.Shape()\n const pts: Array<[number, number]> = []\n for (let i = 0; i < n; i++) {\n const a = (i / n) * Math.PI * 2 + (r() - 0.5) * 0.7\n const rad = 0.35 + r() * 0.15\n pts.push([Math.cos(a) * rad, Math.sin(a) * rad])\n }\n shape.moveTo(pts[0][0], pts[0][1])\n for (let i = 1; i < n; i++) shape.lineTo(pts[i][0], pts[i][1])\n shape.closePath()\n g = new this.THREE.ExtrudeGeometry(shape, { depth: 1, bevelEnabled: false })\n g.translate(0, 0, -0.5)\n this.shardGeos[idx] = g\n }\n return g\n }\n\n /** Create the mesh of a fragment and start its motion. `maxAllowed` is the live budget. */\n spawn(f: Fragment, material: THREE_NS.Material, home: string, maxAllowed: number): void {\n const geo = f.shape === 'shard' ? this.shardGeometry(hashString(f.id)) : this.box\n const mesh = new this.THREE.Mesh(geo, material)\n mesh.position.set(f.center[0], f.center[1], f.center[2])\n mesh.quaternion.set(f.rotation[0], f.rotation[1], f.rotation[2], f.rotation[3]).normalize()\n const sz = f.size\n const sc = new this.THREE.Vector3(Math.max(0.005, sz[0]), Math.max(0.005, sz[1]), Math.max(0.003, sz[2]))\n mesh.scale.copy(sc)\n mesh.castShadow = true\n this.o.scene.add(mesh)\n const live: Live = {\n mesh,\n home,\n vel: new this.THREE.Vector3(f.velocity[0], f.velocity[1], f.velocity[2]),\n ang: new this.THREE.Vector3(f.angularVelocity[0], f.angularVelocity[1], f.angularVelocity[2]),\n // shards are thin: the contact extent uses the real (small) thickness, the chunk uses half of each side\n half: sc.clone().multiplyScalar(0.5),\n baseScale: sc,\n still: 0,\n asleep: false,\n rest: 0,\n fade: 1,\n dying: false,\n }\n this.list.push(live)\n let alive = this.list.reduce((n, l) => n + (l.dying ? 0 : 1), 0)\n for (let i = 0; i < this.list.length && alive > maxAllowed; i++) {\n if (!this.list[i].dying) {\n this.list[i].dying = true\n alive--\n }\n }\n }\n\n /** Add velocity to every fragment within `radius` of a point (blasts). */\n blast(point: [number, number, number], radius: number, speed: number): void {\n for (const f of this.list) {\n const d = this.v.copy(f.mesh.position).sub(this.w.set(point[0], point[1], point[2]))\n const dist = d.length()\n if (dist > radius || f.dying) continue\n const k = (1 - dist / radius) ** 2 * speed\n d.normalize().multiplyScalar(k)\n d.y += k * 0.4\n f.vel.add(d)\n f.ang.add(this.w.set(this.rng() - 0.5, this.rng() - 0.5, this.rng() - 0.5).multiplyScalar(k))\n f.asleep = false\n f.rest = 0\n }\n }\n\n update(dt: number): void {\n const g = this.o.gravity\n for (let i = this.list.length - 1; i >= 0; i--) {\n const f = this.list[i]\n if (f.dying) {\n f.fade -= dt / 0.6\n if (f.fade <= 0) {\n this.o.scene.remove(f.mesh)\n this.list.splice(i, 1)\n continue\n }\n f.mesh.scale.copy(f.baseScale).multiplyScalar(f.fade)\n }\n if (f.asleep) {\n f.rest += dt\n if (this.o.life > 0 && f.rest > this.o.life) f.dying = true\n continue\n }\n this.step(f, dt, g)\n }\n }\n\n private step(f: Live, dt: number, g: number): void {\n const mesh = f.mesh\n f.vel.y -= g * dt\n mesh.position.addScaledVector(f.vel, dt)\n const w = f.ang.length()\n if (w > 1e-5) {\n this.dq.setFromAxisAngle(this.v.copy(f.ang).multiplyScalar(1 / w), w * dt)\n mesh.quaternion.premultiply(this.dq).normalize()\n }\n // ground contact: lowest point of the oriented box\n this.m.makeRotationFromQuaternion(mesh.quaternion)\n const e = this.m.elements\n const h = f.half\n const sc = f.dying ? f.fade : 1\n const ext = (Math.abs(e[1]) * h.x + Math.abs(e[5]) * h.y + Math.abs(e[9]) * h.z) * sc\n const gy = this.o.groundAt(mesh.position.x, mesh.position.z)\n let contact = false\n if (mesh.position.y - ext < gy) {\n contact = true\n mesh.position.y = gy + ext\n if (f.vel.y < -0.6) {\n f.vel.y = -f.vel.y * 0.25\n f.vel.x *= 0.7\n f.vel.z *= 0.7\n f.ang.multiplyScalar(0.55)\n // a hit on a corner makes it tumble a little\n f.ang.x += (this.rng() - 0.5) * 2\n f.ang.z += (this.rng() - 0.5) * 2\n } else {\n f.vel.y = 0\n const fr = Math.exp(-7 * dt)\n f.vel.x *= fr\n f.vel.z *= fr\n f.ang.multiplyScalar(Math.exp(-6 * dt))\n }\n } else {\n f.ang.multiplyScalar(Math.exp(-0.15 * dt))\n }\n if (contact && f.vel.lengthSq() < 4) this.settle(f, dt)\n const slow = f.vel.lengthSq() < 0.012 && f.ang.lengthSq() < 0.2\n if (contact && slow) {\n f.still += dt\n if (f.still > 0.4) {\n f.asleep = true\n f.vel.set(0, 0, 0)\n f.ang.set(0, 0, 0)\n }\n } else {\n f.still = 0\n }\n }\n\n /** Tip the fragment towards lying on its flattest face. */\n private settle(f: Live, dt: number): void {\n const mesh = f.mesh\n this.m.makeRotationFromQuaternion(mesh.quaternion)\n const e = this.m.elements\n const up = [e[1], e[5], e[9]] // world-y component of each local axis\n const half = [f.half.x, f.half.y, f.half.z]\n let small = 0\n for (let i = 1; i < 3; i++) if (half[i] < half[small]) small = i\n let axis = small\n if (Math.abs(up[small]) < 0.35) {\n axis = 0\n for (let i = 1; i < 3; i++) if (Math.abs(up[i]) > Math.abs(up[axis])) axis = i\n }\n const sign = up[axis] >= 0 ? 1 : -1\n this.v.set(e[axis * 4], e[axis * 4 + 1], e[axis * 4 + 2]).multiplyScalar(sign)\n this.w.set(0, 1, 0)\n this.dq.setFromUnitVectors(this.v, this.w)\n this.q.copy(this.dq).multiply(mesh.quaternion)\n mesh.quaternion.slerp(this.q, clamp(dt * 7, 0, 1))\n }\n\n /** Remove the fragments of one object (after `restore`). */\n removeHome(home: string): void {\n for (let i = this.list.length - 1; i >= 0; i--) {\n if (this.list[i].home === home) {\n this.o.scene.remove(this.list[i].mesh)\n this.list.splice(i, 1)\n }\n }\n }\n\n clear(): void {\n for (const f of this.list) this.o.scene.remove(f.mesh)\n this.list.length = 0\n }\n\n dispose(): void {\n this.clear()\n this.box.dispose()\n for (const g of this.shardGeos) g?.dispose()\n this.shardGeos = []\n for (const m of this.materials.values()) m.dispose()\n this.materials.clear()\n }\n}\n","import type * as THREE_NS from 'three'\nimport { lerp } from './math'\n\ninterface Spot {\n mesh: THREE_NS.Mesh\n kind: 'puddle' | 'ash'\n home: string\n x: number\n z: number\n radius: number\n target: number\n}\n\nconst MAX_RADIUS = 1.2\n\n/** Flat discs on the ground: puddles of melted material and piles of ash. Nearby ones merge; the oldest go when the cap is reached. */\nexport class GroundSpots {\n private readonly spots: Spot[] = []\n private readonly geometry: THREE_NS.CircleGeometry\n private readonly materials = new Map<string, THREE_NS.MeshBasicMaterial>()\n\n constructor(\n private readonly THREE: typeof THREE_NS,\n private readonly scene: THREE_NS.Scene,\n private readonly groundAt: (x: number, z: number) => number,\n private readonly cap: number,\n ) {\n this.geometry = new THREE.CircleGeometry(1, 20)\n this.geometry.rotateX(-Math.PI / 2)\n }\n\n get count(): number {\n return this.spots.length\n }\n\n private material(kind: 'puddle' | 'ash', color: number): THREE_NS.MeshBasicMaterial {\n const key = `${kind}:${color}`\n let m = this.materials.get(key)\n if (!m) {\n m = new this.THREE.MeshBasicMaterial({\n color,\n transparent: true,\n opacity: kind === 'puddle' ? 0.88 : 0.95,\n depthWrite: false,\n polygonOffset: true,\n polygonOffsetFactor: -2,\n polygonOffsetUnits: -2,\n })\n this.materials.set(key, m)\n }\n return m\n }\n\n /** Add (or grow) a spot at (x, z). `radius` is the radius this addition contributes. */\n add(kind: 'puddle' | 'ash', x: number, z: number, radius: number, color: number, home: string): void {\n for (const s of this.spots) {\n if (s.kind !== kind) continue\n const d = Math.hypot(s.x - x, s.z - z)\n if (d < Math.max(s.target, radius) * 0.9) {\n s.target = Math.min(MAX_RADIUS, Math.sqrt(s.target * s.target + radius * radius * 0.5))\n return\n }\n }\n while (this.spots.length >= this.cap) this.remove(this.spots[0])\n const mesh = new this.THREE.Mesh(this.geometry, this.material(kind, color))\n const y = this.groundAt(x, z) + 0.004 + (this.spots.length % 8) * 0.0004\n mesh.position.set(x, y, z)\n mesh.scale.setScalar(0.001)\n mesh.renderOrder = 1\n this.scene.add(mesh)\n this.spots.push({ mesh, kind, home, x, z, radius: 0.001, target: Math.min(MAX_RADIUS, radius) })\n }\n\n private remove(s: Spot): void {\n const i = this.spots.indexOf(s)\n if (i >= 0) this.spots.splice(i, 1)\n this.scene.remove(s.mesh)\n }\n\n update(dt: number): void {\n const k = Math.min(1, dt * 3)\n for (const s of this.spots) {\n if (Math.abs(s.radius - s.target) < 1e-4) continue\n s.radius = lerp(s.radius, s.target, k)\n s.mesh.scale.setScalar(s.radius)\n }\n }\n\n /** Remove the spots made for one object (after its `restore`). */\n removeHome(home: string): void {\n for (const s of [...this.spots]) if (s.home === home) this.remove(s)\n }\n\n clear(): void {\n for (const s of [...this.spots]) this.remove(s)\n }\n\n dispose(): void {\n this.clear()\n this.geometry.dispose()\n for (const m of this.materials.values()) m.dispose()\n this.materials.clear()\n }\n}\n","import type * as THREE_NS from 'three'\nimport type { DebrisKind, Vec3 } from '@adatechnology/game-destruction'\nimport { clamp, createRng, hexToRgb, lerp } from './math'\n\n/** Every particle kind the renderer can emit. */\nexport type ParticleKind = DebrisKind | 'fire' | 'flash' | 'foam'\n\ninterface KindDef {\n additive: boolean\n /** Life range in seconds. */\n life: [number, number]\n /** Start size range in metres (billboard diameter). */\n size: [number, number]\n /** Size multiplier reached at the end of the life. */\n grow: number\n /** Gravity multiplier (1 = full gravity). */\n gravity: number\n /** Constant upward acceleration in m/s² (heat, steam). */\n rise: number\n /** Velocity damping per second. */\n drag: number\n c0: number\n c1: number\n alpha: number\n /** Fraction of the life spent fading in. */\n fadeIn: number\n /** How much of the emission direction follows the surface normal (1) versus a random direction (0). */\n cone: number\n /** What happens at ground height. */\n ground: 'none' | 'bounce' | 'stop' | 'land'\n flutter?: number\n flicker?: number\n}\n\nconst KINDS: Record<ParticleKind, KindDef> = {\n fire: { additive: true, life: [0.45, 0.9], size: [0.1, 0.2], grow: 0.3, gravity: 0, rise: 2.4, drag: 1.2, c0: 0xffc040, c1: 0xc02000, alpha: 0.9, fadeIn: 0.12, cone: 0.7, ground: 'none', flicker: 0.35 },\n smoke: { additive: false, life: [1.8, 3.2], size: [0.14, 0.24], grow: 3.2, gravity: 0, rise: 0.7, drag: 0.8, c0: 0x666666, c1: 0x9a9a9a, alpha: 0.35, fadeIn: 0.15, cone: 0.6, ground: 'none' },\n sparks: { additive: true, life: [0.25, 0.6], size: [0.018, 0.03], grow: 0.5, gravity: 1, rise: 0, drag: 0.3, c0: 0xfff2a0, c1: 0xff6a10, alpha: 1, fadeIn: 0.02, cone: 0.55, ground: 'bounce' },\n dust: { additive: false, life: [1, 2], size: [0.08, 0.14], grow: 2.5, gravity: 0.05, rise: 0, drag: 2.5, c0: 0x8d8579, c1: 0x8d8579, alpha: 0.4, fadeIn: 0.1, cone: 0.4, ground: 'none' },\n shards: { additive: true, life: [0.8, 1.4], size: [0.02, 0.035], grow: 0.8, gravity: 1, rise: 0, drag: 0.2, c0: 0xdff4ff, c1: 0x9fd0ff, alpha: 0.9, fadeIn: 0.03, cone: 0.5, ground: 'bounce', flicker: 0.5 },\n splinters: { additive: false, life: [0.8, 1.5], size: [0.02, 0.035], grow: 1, gravity: 1, rise: 0, drag: 0.3, c0: 0xa0764a, c1: 0x7a5530, alpha: 1, fadeIn: 0.02, cone: 0.5, ground: 'bounce' },\n chips: { additive: false, life: [0.8, 1.5], size: [0.025, 0.04], grow: 1, gravity: 1, rise: 0, drag: 0.3, c0: 0x8a8378, c1: 0x6e685f, alpha: 1, fadeIn: 0.02, cone: 0.5, ground: 'bounce' },\n fibers: { additive: false, life: [1.5, 2.5], size: [0.02, 0.03], grow: 1, gravity: 0.15, rise: 0, drag: 2, c0: 0xb8a888, c1: 0xb8a888, alpha: 0.9, fadeIn: 0.05, cone: 0.4, ground: 'stop', flutter: 2.5 },\n leaves: { additive: false, life: [1.5, 2.8], size: [0.03, 0.05], grow: 1, gravity: 0.15, rise: 0, drag: 2, c0: 0x5b9a3c, c1: 0x4a7f30, alpha: 0.95, fadeIn: 0.05, cone: 0.4, ground: 'stop', flutter: 2.8 },\n drips: { additive: false, life: [2, 3], size: [0.025, 0.035], grow: 1, gravity: 1, rise: 0, drag: 0, c0: 0x6a6a6a, c1: 0x6a6a6a, alpha: 1, fadeIn: 0.01, cone: 0.2, ground: 'land' },\n ash: { additive: false, life: [2, 4], size: [0.02, 0.04], grow: 1.2, gravity: 0.08, rise: 0, drag: 1.5, c0: 0x2a2623, c1: 0x1a1816, alpha: 0.8, fadeIn: 0.1, cone: 0.5, ground: 'stop', flutter: 0.6 },\n steam: { additive: false, life: [1, 2], size: [0.12, 0.2], grow: 3, gravity: 0, rise: 0.9, drag: 1, c0: 0xffffff, c1: 0xe0e8ee, alpha: 0.35, fadeIn: 0.15, cone: 0.6, ground: 'none' },\n flash: { additive: true, life: [0.12, 0.2], size: [1.2, 1.8], grow: 1.6, gravity: 0, rise: 0, drag: 0, c0: 0xfff3c0, c1: 0xff9030, alpha: 1, fadeIn: 0.1, cone: 0, ground: 'none' },\n foam: { additive: false, life: [1, 1.8], size: [0.06, 0.1], grow: 1.5, gravity: 0.6, rise: 0, drag: 1.5, c0: 0xf4f8ff, c1: 0xdde6f0, alpha: 0.8, fadeIn: 0.05, cone: 0.5, ground: 'stop' },\n}\n\nconst KIND_IDS = Object.keys(KINDS) as ParticleKind[]\nconst KIND_INDEX: Record<string, number> = {}\nKIND_IDS.forEach((k, i) => (KIND_INDEX[k] = i))\nconst KIND_LIST: KindDef[] = KIND_IDS.map((k) => KINDS[k])\n\n/** Per-spawn overrides. */\nexport interface SpawnOptions {\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 `drips` particle that reaches the ground calls `onLand` with this tag. */\n tag?: number\n}\n\nconst VERT = `\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\nconst FRAG = `\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\ninterface PoolHost {\n groundAt: (x: number, z: number) => number\n gravity: number\n onLand: (x: number, y: number, z: number, color: number, tag: number) => void\n}\n\n/** One fixed-size ring buffer of particles drawn as a single `THREE.Points` with a soft round sprite computed in the shader. */\nclass Pool {\n readonly points: THREE_NS.Points\n readonly geometry: THREE_NS.BufferGeometry\n readonly material: THREE_NS.ShaderMaterial\n live = 0\n private next = 0\n private high = 0\n private readonly pos: Float32Array\n private readonly col: Float32Array\n private readonly alp: Float32Array\n private readonly siz: Float32Array\n private readonly vel: 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 colorHex: Int32Array\n private readonly rgbStart: Float32Array\n private readonly rgbEnd: Float32Array\n private readonly bounced: Uint8Array\n\n constructor(\n THREE: typeof THREE_NS,\n readonly capacity: number,\n additive: boolean,\n pointScale: number,\n ) {\n const n = Math.max(1, capacity)\n this.pos = new Float32Array(n * 3)\n this.col = new Float32Array(n * 3)\n this.alp = new Float32Array(n)\n this.siz = new Float32Array(n)\n this.vel = 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.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.geometry = new THREE.BufferGeometry()\n const attr = (a: Float32Array, size: number) => {\n const at = new THREE.BufferAttribute(a, size)\n at.setUsage(THREE.DynamicDrawUsage)\n return at\n }\n this.geometry.setAttribute('position', attr(this.pos, 3))\n this.geometry.setAttribute('pcolor', attr(this.col, 3))\n this.geometry.setAttribute('alpha', attr(this.alp, 1))\n this.geometry.setAttribute('size', attr(this.siz, 1))\n this.geometry.setDrawRange(0, 0)\n this.material = new THREE.ShaderMaterial({\n uniforms: { uScale: { value: pointScale } },\n vertexShader: VERT,\n fragmentShader: FRAG,\n transparent: true,\n depthWrite: false,\n blending: additive ? THREE.AdditiveBlending : THREE.NormalBlending,\n })\n this.points = new THREE.Points(this.geometry, this.material)\n this.points.frustumCulled = false\n this.points.name = additive ? 'destruction-particles-additive' : 'destruction-particles'\n this.points.renderOrder = additive ? 11 : 10\n }\n\n spawn(kind: number, x: number, y: number, z: number, vx: number, vy: number, vz: number, def: KindDef, rnd: () => number, o?: SpawnOptions): void {\n if (this.capacity <= 0) return\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.pos[i * 3] = x\n this.pos[i * 3 + 1] = y\n this.pos[i * 3 + 2] = z\n this.vel[i * 3] = vx\n this.vel[i * 3 + 1] = vy\n this.vel[i * 3 + 2] = vz\n this.age[i] = 0\n this.life[i] = lerp(def.life[0], def.life[1], rnd()) * (o?.life ?? 1)\n this.size0[i] = lerp(def.size[0], def.size[1], rnd()) * (o?.size ?? 1)\n this.seed[i] = rnd()\n this.alphaMul[i] = o?.alpha ?? 1\n this.tag[i] = o?.tag ?? 0\n this.colorHex[i] = o?.color ?? -1\n this.rgbStart.set(hexToRgb(o?.color ?? def.c0), i * 3)\n this.rgbEnd.set(hexToRgb(o?.color ?? def.c1), i * 3)\n this.bounced[i] = 0\n this.siz[i] = this.size0[i]\n this.alp[i] = 0\n }\n\n private kill(i: number): void {\n if (!this.alive[i]) return\n this.alive[i] = 0\n this.live--\n this.alp[i] = 0\n this.siz[i] = 0\n }\n\n clear(): void {\n this.alive.fill(0)\n this.alp.fill(0)\n this.siz.fill(0)\n this.live = 0\n this.high = 0\n this.next = 0\n this.geometry.setDrawRange(0, 0)\n }\n\n /** Move a live particle (used by external pushes such as blasts). */\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.pos[i * 3] - cx\n const dy = this.pos[i * 3 + 1] - cy\n const dz = this.pos[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.vel[i * 3] += (dx / d) * f\n this.vel[i * 3 + 1] += (dy / d) * f\n this.vel[i * 3 + 2] += (dz / d) * f\n }\n }\n\n update(dt: number, host: PoolHost): void {\n let top = 0\n for (let i = 0; i < this.high; i++) {\n if (!this.alive[i]) continue\n const def = KIND_LIST[this.kind[i]]\n const age = (this.age[i] += dt)\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.vel[i3]\n let vy = this.vel[i3 + 1]\n let vz = this.vel[i3 + 2]\n vy += (def.rise - def.gravity * host.gravity) * dt\n if (def.flutter) {\n const ph = age * 7 + this.seed[i] * 40\n vx += Math.sin(ph) * def.flutter * dt\n vz += Math.cos(ph * 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.pos[i3] + vx * dt\n let y = this.pos[i3 + 1] + vy * dt\n const z = this.pos[i3 + 2] + vz * dt\n if (def.ground !== 'none') {\n const gy = host.groundAt(x, z) + 0.005\n if (y < gy) {\n if (def.ground === 'land') {\n const tag = this.tag[i]\n if (tag !== 0) host.onLand(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.vel[i3] = vx\n this.vel[i3 + 1] = vy\n this.vel[i3 + 2] = vz\n this.pos[i3] = x\n this.pos[i3 + 1] = y\n this.pos[i3 + 2] = z\n this.siz[i] = this.size0[i] * lerp(1, def.grow, t)\n this.col[i3] = lerp(this.rgbStart[i3], this.rgbEnd[i3], t)\n this.col[i3 + 1] = lerp(this.rgbStart[i3 + 1], this.rgbEnd[i3 + 1], t)\n this.col[i3 + 2] = lerp(this.rgbStart[i3 + 2], this.rgbEnd[i3 + 2], t)\n let env = t < def.fadeIn ? t / def.fadeIn : 1 - (t - def.fadeIn) / (1 - def.fadeIn)\n if (def.flicker) env *= 1 - def.flicker * (0.5 + 0.5 * Math.sin(age * 38 + this.seed[i] * 50))\n this.alp[i] = clamp(env, 0, 1) * def.alpha * this.alphaMul[i]\n top = i + 1\n }\n this.high = top\n this.geometry.setDrawRange(0, top)\n for (const name of ['position', 'pcolor', 'alpha', 'size']) {\n ;(this.geometry.getAttribute(name) as THREE_NS.BufferAttribute).needsUpdate = true\n }\n }\n\n dispose(): void {\n this.geometry.dispose()\n this.material.dispose()\n }\n}\n\nexport interface ParticleSystemOptions {\n capacity: number\n scene: THREE_NS.Scene\n /** Count multiplier for bursts (0.6 on low quality). */\n density: number\n /** Pixels of point size per metre at a distance of one metre; roughly `viewportHeight / (2 * tan(fov / 2))`. */\n pointScale: number\n groundAt: (x: number, z: number) => number\n gravity: number\n onLand: (x: number, y: number, z: number, color: number, tag: number) => void\n}\n\n/**\n * Two pooled particle systems (additive and normal blending), each a single `THREE.Points` with fixed-size buffers used as a ring. The Points are\n * added to the scene on first use and removed again by `detach`, so an idle renderer leaves nothing in the scene.\n */\nexport class ParticleSystem {\n private readonly add: Pool\n private readonly normal: Pool\n private readonly rng = createRng(0x9e3779b1)\n private attached = false\n private readonly host: PoolHost\n\n constructor(\n THREE: typeof THREE_NS,\n private readonly o: ParticleSystemOptions,\n ) {\n const addCap = Math.floor(o.capacity * 0.4)\n this.add = new Pool(THREE, addCap, true, o.pointScale)\n this.normal = new Pool(THREE, o.capacity - addCap, false, o.pointScale)\n this.host = { groundAt: o.groundAt, gravity: o.gravity, onLand: o.onLand }\n }\n\n get capacity(): number {\n return this.add.capacity + this.normal.capacity\n }\n\n get live(): number {\n return this.add.live + this.normal.live\n }\n\n private ensureAttached(): void {\n if (this.attached) return\n this.attached = true\n this.o.scene.add(this.add.points, this.normal.points)\n }\n\n /** Remove the Points from the scene (they come back on the next emit). */\n detach(): void {\n if (!this.attached) return\n this.attached = false\n this.o.scene.remove(this.add.points, this.normal.points)\n }\n\n /** Spawn one particle with an explicit velocity. */\n spawn(kind: ParticleKind, p: Vec3, v: Vec3, opts?: SpawnOptions): void {\n const def = KINDS[kind]\n this.ensureAttached()\n ;(def.additive ? this.add : this.normal).spawn(KIND_INDEX[kind], p[0], p[1], p[2], v[0], v[1], v[2], def, this.rng, opts)\n }\n\n /** Spawn `count` particles leaving `point` around `normal` at about `speed` m/s. */\n burst(kind: ParticleKind, point: Vec3, normal: Vec3, count: number, speed = 1, opts?: SpawnOptions): void {\n const def = KINDS[kind]\n const n = Math.max(0, Math.round(count * this.o.density))\n const rnd = this.rng\n const nl = Math.hypot(normal[0], normal[1], normal[2]) || 1\n const nx = normal[0] / nl\n const ny = normal[1] / nl\n const nz = normal[2] / nl\n for (let k = 0; k < n; k++) {\n // random unit vector\n const u = rnd() * 2 - 1\n const ph = rnd() * Math.PI * 2\n const s = Math.sqrt(1 - u * u)\n const rx = s * Math.cos(ph)\n const ry = u\n const rz = s * Math.sin(ph)\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 kind,\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 opts,\n )\n }\n }\n\n /** Blast wave: shove live particles away from a point. */\n push(point: Vec3, radius: number, speed: number): void {\n this.add.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 if (this.add.live > 0 || this.add.points.geometry.drawRange.count > 0) this.add.update(dt, this.host)\n if (this.normal.live > 0 || this.normal.points.geometry.drawRange.count > 0) this.normal.update(dt, this.host)\n }\n\n clear(): void {\n this.add.clear()\n this.normal.clear()\n this.detach()\n }\n\n dispose(): void {\n this.detach()\n this.add.dispose()\n this.normal.dispose()\n }\n}\n","import type * as THREE_NS from 'three'\nimport type { Vec3 } from '@adatechnology/game-destruction'\nimport type { PieceBody } from './internal'\nimport { clamp } from './math'\n\nexport interface PieceSystemOptions {\n groundAt: (x: number, z: number) => number\n gravity: number\n /** A piece hit the ground faster than 3 m/s (throttled per piece). */\n onHardLanding: (piece: PieceBody, point: Vec3, speed: number) => void\n /** Notified when a piece is about to be removed. */\n scene: THREE_NS.Scene\n}\n\nconst SUB_DT = 1 / 120\nconst MU = 0.7\nconst SLEEP_AFTER = 0.5\nconst HARD_LANDING = 3\n/** Contact tolerance in metres: corners this close to the ground count as touching, which keeps the contact set (and a resting box) stable. */\nconst SLOP = 0.002\n/** A resting support narrower than this fraction of the centre-of-mass height is a balancing act, not a rest: the piece topples. */\nconst NARROW = 0.3\n/** Angular acceleration (rad/s^2 per metre of height) that pushes a balancing piece off its edge, toward the side its centre of mass leans to. */\nconst TOPPLE = 3\n\n/**\n * Light rigid-body physics for loose pieces: gravity, the 8 corners of the local box against the ground with an impulse (restitution, friction,\n * angular response), damping, sleeping, and a gentle horizontal separation between pieces. Not a general solver: no piece-vs-scenery contact.\n */\nexport class PieceSystem {\n readonly list: PieceBody[] = []\n private time = 0\n private readonly p: THREE_NS.Vector3\n private readonly q: THREE_NS.Quaternion\n private readonly dq: THREE_NS.Quaternion\n private readonly m: THREE_NS.Matrix4\n private readonly ax: THREE_NS.Vector3\n\n constructor(\n private readonly THREE: typeof THREE_NS,\n private readonly o: PieceSystemOptions,\n ) {\n this.p = new THREE.Vector3()\n this.q = new THREE.Quaternion()\n this.dq = new THREE.Quaternion()\n this.m = new THREE.Matrix4()\n this.ax = new THREE.Vector3()\n }\n\n get(objectId: string): PieceBody | null {\n return this.list.find((b) => b.objectId === objectId) ?? null\n }\n\n create(objectId: string, root: THREE_NS.Object3D, halfExtents: Vec3, localCenter: THREE_NS.Vector3, mass: number, velocity?: Vec3, angular?: Vec3): PieceBody {\n const existing = this.get(objectId)\n if (existing) {\n existing.root = root\n existing.halfExtents = halfExtents\n existing.mass = mass\n this.setCenter(existing, localCenter)\n if (velocity) existing.velocity = [...velocity]\n existing.sleeping = false\n return existing\n }\n const body: PieceBody = {\n objectId,\n root,\n velocity: velocity ? [...velocity] : [0, 0, 0],\n angularVelocity: angular ? [...angular] : [0, 0, 0],\n halfExtents: [Math.max(0.01, halfExtents[0]), Math.max(0.01, halfExtents[1]), Math.max(0.01, halfExtents[2])],\n mass: Math.max(0.05, mass),\n sleeping: false,\n held: false,\n localCenter: [localCenter.x, localCenter.y, localCenter.z],\n centerV: localCenter.clone(),\n still: 0,\n lastHit: -1,\n onGround: false,\n }\n this.list.push(body)\n return body\n }\n\n /** Move the centre of mass (after a part broke off). */\n setCenter(b: PieceBody, c: THREE_NS.Vector3): void {\n b.centerV.copy(c)\n b.localCenter = [c.x, c.y, c.z]\n }\n\n remove(objectId: string): void {\n const i = this.list.findIndex((b) => b.objectId === objectId)\n if (i >= 0) this.list.splice(i, 1)\n }\n\n clear(): void {\n this.list.length = 0\n }\n\n /** World position of the centre of mass. */\n centerOf(b: PieceBody, out: THREE_NS.Vector3): THREE_NS.Vector3 {\n return out.copy(b.centerV).applyQuaternion(b.root.quaternion).add(b.root.position)\n }\n\n impulse(b: PieceBody, impulse: Vec3, point?: Vec3): void {\n const inv = 1 / b.mass\n b.velocity[0] += impulse[0] * inv\n b.velocity[1] += impulse[1] * inv\n b.velocity[2] += impulse[2] * inv\n if (point) {\n const c = this.centerOf(b, this.p)\n const rx = point[0] - c.x\n const ry = point[1] - c.y\n const rz = point[2] - c.z\n // torque = r x J, angular velocity change = I^-1 torque\n const tx = ry * impulse[2] - rz * impulse[1]\n const ty = rz * impulse[0] - rx * impulse[2]\n const tz = rx * impulse[1] - ry * impulse[0]\n const w = this.mulInvInertia(b, tx, ty, tz)\n b.angularVelocity[0] += w[0]\n b.angularVelocity[1] += w[1]\n b.angularVelocity[2] += w[2]\n }\n b.sleeping = false\n b.still = 0\n }\n\n /** I^-1 * (x, y, z) in world space for a box with the body's half extents and mass (needs `this.m` to hold the body's rotation). */\n private mulInvInertia(b: PieceBody, x: number, y: number, z: number): Vec3 {\n this.m.makeRotationFromQuaternion(b.root.quaternion)\n return this.mulInvInertiaWith(b, this.m.elements, x, y, z)\n }\n\n private mulInvInertiaWith(b: PieceBody, e: ArrayLike<number>, x: number, y: number, z: number): Vec3 {\n const [hx, hy, hz] = b.halfExtents\n const k = 3 / b.mass\n const ix = k / (hy * hy + hz * hz)\n const iy = k / (hx * hx + hz * hz)\n const iz = k / (hx * hx + hy * hy)\n // local = R^T v (columns of R are the local axes in world space)\n const lx = (e[0] * x + e[1] * y + e[2] * z) * ix\n const ly = (e[4] * x + e[5] * y + e[6] * z) * iy\n const lz = (e[8] * x + e[9] * y + e[10] * z) * iz\n return [e[0] * lx + e[4] * ly + e[8] * lz, e[1] * lx + e[5] * ly + e[9] * lz, e[2] * lx + e[6] * ly + e[10] * lz]\n }\n\n update(dt: number): void {\n this.time += dt\n for (const b of this.list) {\n if (b.held) {\n b.velocity = [0, 0, 0]\n b.angularVelocity = [0, 0, 0]\n b.sleeping = false\n b.still = 0\n continue\n }\n if (b.sleeping) continue\n this.integrate(b, dt)\n }\n this.separate(dt)\n }\n\n private integrate(b: PieceBody, dt: number): void {\n const c = this.centerOf(b, this.p)\n const pos: Vec3 = [c.x, c.y, c.z]\n const q = this.q.copy(b.root.quaternion)\n const v = b.velocity\n const w = b.angularVelocity\n const [hx, hy, hz] = b.halfExtents\n const invM = 1 / b.mass\n const steps = clamp(Math.ceil(dt / SUB_DT), 1, 8)\n const h = dt / steps\n let landing = 0\n let landPoint: Vec3 = [pos[0], pos[1], pos[2]]\n let contactedLast = false\n let touched = false\n let unstable = false\n\n for (let s = 0; s < steps; s++) {\n v[1] -= this.o.gravity * h\n pos[0] += v[0] * h\n pos[1] += v[1] * h\n pos[2] += v[2] * h\n const wl = Math.hypot(w[0], w[1], w[2])\n if (wl > 1e-6) {\n this.dq.setFromAxisAngle(this.ax.set(w[0] / wl, w[1] / wl, w[2] / wl), wl * h)\n q.premultiply(this.dq).normalize()\n }\n const damp = Math.exp(-0.15 * h)\n w[0] *= damp\n w[1] *= damp\n w[2] *= damp\n\n this.m.makeRotationFromQuaternion(q)\n const e = this.m.elements\n const rs: Vec3[] = []\n let maxPen = 0\n for (let i = 0; i < 8; i++) {\n const sx = i & 1 ? hx : -hx\n const sy = i & 2 ? hy : -hy\n const sz = i & 4 ? hz : -hz\n const r: Vec3 = [e[0] * sx + e[4] * sy + e[8] * sz, e[1] * sx + e[5] * sy + e[9] * sz, e[2] * sx + e[6] * sy + e[10] * sz]\n const gy = this.o.groundAt(pos[0] + r[0], pos[2] + r[2])\n const pen = gy - (pos[1] + r[1])\n if (pen > -SLOP) {\n rs.push(r)\n if (pen > maxPen) maxPen = pen\n }\n }\n contactedLast = rs.length > 0\n if (contactedLast) touched = true\n if (!contactedLast) continue\n pos[1] += Math.max(0, maxPen - SLOP * 0.5)\n let impact = 0\n for (const r of rs) impact = Math.max(impact, -(v[1] + w[2] * r[0] - w[0] * r[2]))\n const restitution = impact > 2 ? 0.25 : 0\n if (impact > landing) {\n landing = impact\n let sx = 0\n let sz = 0\n for (const r of rs) {\n sx += r[0]\n sz += r[2]\n }\n const lx = pos[0] + sx / rs.length\n const lz = pos[2] + sz / rs.length\n landPoint = [lx, this.o.groundAt(lx, lz), lz]\n }\n for (let it = 0; it < 4; it++) {\n for (const r of rs) {\n // contact point velocity\n let vx = v[0] + (w[1] * r[2] - w[2] * r[1])\n let vy = v[1] + (w[2] * r[0] - w[0] * r[2])\n let vz = v[2] + (w[0] * r[1] - w[1] * r[0])\n const vn = vy\n if (vn >= 0) continue\n // normal impulse (n = +y): r x n = (-rz, 0, rx)\n const a = this.mulInvInertiaWith(b, e, -r[2], 0, r[0])\n const denom = invM + (a[2] * r[0] - a[0] * r[2])\n const j = (-(1 + (it === 0 ? restitution : 0)) * vn) / denom\n v[1] += j * invM\n const dw = this.mulInvInertiaWith(b, e, -r[2] * j, 0, r[0] * j)\n w[0] += dw[0]\n w[1] += dw[1]\n w[2] += dw[2]\n // friction in the contact plane\n vx = v[0] + (w[1] * r[2] - w[2] * r[1])\n vy = v[1] + (w[2] * r[0] - w[0] * r[2])\n vz = v[2] + (w[0] * r[1] - w[1] * r[0])\n const vt = Math.hypot(vx, vz)\n if (vt > 1e-5) {\n const tx = vx / vt\n const tz = vz / vt\n // r x t with t = (tx, 0, tz) = (0*tz - rz*0 ... ) -> (ry*tz, rz*tx - rx*tz, -ry*tx)\n const rx = r[1] * tz\n const ry = r[2] * tx - r[0] * tz\n const rz = -r[1] * tx\n const at = this.mulInvInertiaWith(b, e, rx, ry, rz)\n const cx = at[1] * r[2] - at[2] * r[1]\n const cz = at[0] * r[1] - at[1] * r[0]\n const denomT = invM + cx * tx + cz * tz\n const jt = Math.min(vt / denomT, MU * j)\n v[0] -= tx * jt * invM\n v[2] -= tz * jt * invM\n const dwt = this.mulInvInertiaWith(b, e, rx * jt, ry * jt, rz * jt)\n w[0] -= dwt[0]\n w[1] -= dwt[1]\n w[2] -= dwt[2]\n }\n }\n }\n // Support check. The corner contacts above are relative to the centre of mass, so they already give a torque when the centre of mass is\n // outside the support. What they cannot do is end a balancing act: a tall thin piece on its edge (or a single corner) is in equilibrium\n // with the centre of mass above a very small support, so it must not rest there. It gets a gentle topple push toward the side it leans\n // to (or toward the wider side when it leans nowhere), and it may not sleep.\n let x0 = Infinity, x1 = -Infinity, z0 = Infinity, z1 = -Infinity, low = 0\n for (const r of rs) {\n x0 = Math.min(x0, r[0]); x1 = Math.max(x1, r[0])\n z0 = Math.min(z0, r[2]); z1 = Math.max(z1, r[2])\n low = Math.min(low, r[1])\n }\n const height = -low\n const wx = x1 - x0\n const wz = z1 - z0\n unstable = Math.min(wx, wz) < NARROW * height || x0 > 0 || x1 < 0 || z0 > 0 || z1 < 0\n if (unstable) {\n // the support centre relative to the centre of mass: the piece falls away from it (toward +axis when it is centred)\n const dirX = x0 + x1 > 0 ? -1 : 1\n const dirZ = z0 + z1 > 0 ? -1 : 1\n const k = TOPPLE * Math.min(height, 1) * h\n // rotating about -z moves the top toward +x; rotating about +x moves it toward +z\n if (wx <= wz) w[2] -= dirX * k\n else w[0] += dirZ * k\n }\n // rolling / spinning resistance while touching the ground\n const rd = Math.exp((rs.length >= 3 ? -3 : -1.5) * h)\n w[0] *= rd\n w[1] *= rd\n w[2] *= rd\n const fd = Math.exp(-0.3 * h)\n v[0] *= fd\n v[2] *= fd\n }\n\n // write back\n b.root.quaternion.copy(q)\n const off = this.ax.copy(b.centerV).applyQuaternion(q)\n b.root.position.set(pos[0] - off.x, pos[1] - off.y, pos[2] - off.z)\n b.root.updateMatrixWorld(true)\n b.onGround = contactedLast\n\n if (landing > HARD_LANDING && this.time - b.lastHit > 0.15) {\n b.lastHit = this.time\n this.o.onHardLanding(b, landPoint, landing)\n }\n\n const still = touched && !unstable && Math.hypot(v[0], v[1], v[2]) < 0.12 && Math.hypot(w[0], w[1], w[2]) < 0.45\n if (still) {\n b.still += dt\n if (b.still >= SLEEP_AFTER) {\n b.sleeping = true\n v[0] = v[1] = v[2] = 0\n w[0] = w[1] = w[2] = 0\n }\n } else {\n b.still = 0\n }\n }\n\n /** Push overlapping pieces apart (horizontal bounding circles), gently, so they do not interpenetrate forever. */\n private separate(dt: number): void {\n const n = this.list.length\n if (n < 2) return\n const maxMove = 1.5 * dt\n const ca = new this.THREE.Vector3()\n const cb = new this.THREE.Vector3()\n for (let i = 0; i < n; i++) {\n const a = this.list[i]\n for (let j = i + 1; j < n; j++) {\n const b = this.list[j]\n if (a.held && b.held) continue\n if (a.sleeping && b.sleeping) continue\n const ra = Math.hypot(a.halfExtents[0], a.halfExtents[1], a.halfExtents[2])\n const rb = Math.hypot(b.halfExtents[0], b.halfExtents[1], b.halfExtents[2])\n this.centerOf(a, ca)\n this.centerOf(b, cb)\n if (Math.abs(ca.y - cb.y) > ra + rb) continue\n const rh = (Math.max(a.halfExtents[0], a.halfExtents[2]) + Math.max(b.halfExtents[0], b.halfExtents[2])) * 0.8\n let dx = cb.x - ca.x\n let dz = cb.z - ca.z\n let d = Math.hypot(dx, dz)\n if (d >= rh) continue\n if (d < 1e-5) {\n dx = 1\n dz = 0\n d = 1\n }\n const push = Math.min(maxMove, (rh - d) * 0.5)\n const wa = a.held ? 0 : b.held ? 1 : b.mass / (a.mass + b.mass)\n const wb = b.held ? 0 : a.held ? 1 : a.mass / (a.mass + b.mass)\n const ux = dx / (d || 1)\n const uz = dz / (d || 1)\n if (!a.held) {\n a.root.position.x -= ux * push * 2 * wa\n a.root.position.z -= uz * push * 2 * wa\n a.sleeping = false\n }\n if (!b.held) {\n b.root.position.x += ux * push * 2 * wb\n b.root.position.z += uz * push * 2 * wb\n b.sleeping = false\n }\n }\n }\n }\n}\n","import type { ChainDelay, ExplosionChain, PartSpec } from './types'\n\n/** What can set an explosive part off from outside, in the order a hit can carry it. */\nexport type ChainTrigger = 'shot' | 'fire' | 'explosion'\n\n/** Temperature in °C that primes a part with `chain.fire` when its `explodes.atC` is not set. */\nexport const FIRE_PRIME_C = 300\n\n/** Seconds from a trigger to the blast: a number is fixed, `[min, max]` is drawn from the world seed (one draw). */\nexport const drawChainDelay = (delay: ChainDelay, random: () => number): number => {\n if (typeof delay === 'number') return delay\n const [min, max] = delay\n return min + random() * (max - min)\n}\n\n/** The delay `spec` gives for `trigger`, or undefined when the part does not answer to it. */\nexport const chainDelayOf = (spec: PartSpec, trigger: ChainTrigger): ChainDelay | undefined => spec.explodes?.chain?.[trigger]\n\n/** Fraction of the hit points a shot must have taken off before it primes the part. */\nexport const shotDamageFractionOf = (spec: PartSpec): number => spec.explodes?.chain?.shotDamageFraction ?? 0\n\n/** The numbers of an explosive that sits in the world, as `game-weapons` gives them (`ExplosiveDef` fits). */\nexport interface WorldExplosive {\n readonly energy: number\n /** Metres within which the blast reaches other parts (and primes theirs). */\n readonly injuryRadius: number\n readonly chain?: ExplosionChain\n}\n\nexport interface ExplosivePartOptions {\n /** Temperature in °C at which fire primes (or sets off) the part; default `FIRE_PRIME_C` when the explosive answers to fire. */\n readonly atC?: number\n}\n\n/**\n * A part that explodes with the energy and reach of `explosive` and answers to what `explosive.chain` says: a bullet, fire or a blast\n * nearby primes it and it goes off after the delay. `part` is everything else about the part (shape, material, position, fluid inside).\n */\nexport const explosivePart = (explosive: WorldExplosive, part: Omit<PartSpec, 'explodes'>, options: ExplosivePartOptions = {}): PartSpec => ({\n ...part,\n explodes: {\n energy: explosive.energy,\n radius: explosive.injuryRadius,\n ...(options.atC !== undefined ? { atC: options.atC } : {}),\n ...(explosive.chain ? { chain: { ...explosive.chain } } : {}),\n },\n})\n","import type { PartShape, Quat, Vec3 } from './types'\n\nconst EPS = 1e-9\n\n/** Small vector helpers (arrays of three numbers). Pure functions; they allocate a new array. */\nexport const vec = {\n add: (a: Vec3, b: Vec3): Vec3 => [a[0] + b[0], a[1] + b[1], a[2] + b[2]],\n sub: (a: Vec3, b: Vec3): Vec3 => [a[0] - b[0], a[1] - b[1], a[2] - b[2]],\n scale: (a: Vec3, s: number): Vec3 => [a[0] * s, a[1] * s, a[2] * s],\n dot: (a: Vec3, b: Vec3): number => a[0] * b[0] + a[1] * b[1] + a[2] * b[2],\n 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]],\n length: (a: Vec3): number => Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]),\n distance: (a: Vec3, b: Vec3): number => Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]),\n normalize: (a: Vec3, fallback: Vec3 = [0, 1, 0]): Vec3 => {\n const l = Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2])\n return l < EPS ? [fallback[0], fallback[1], fallback[2]] : [a[0] / l, a[1] / l, a[2] / l]\n },\n lerp: (a: Vec3, b: Vec3, t: number): Vec3 => [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t, a[2] + (b[2] - a[2]) * t],\n /** `a + b * s` */\n madd: (a: Vec3, b: Vec3, s: number): Vec3 => [a[0] + b[0] * s, a[1] + b[1] * s, a[2] + b[2] * s],\n}\n\nconst IDENTITY: Quat = [0, 0, 0, 1]\n\n/** Quaternion helpers (`[x, y, z, w]`). */\nexport const quat = {\n identity: (): Quat => [IDENTITY[0], IDENTITY[1], IDENTITY[2], IDENTITY[3]],\n /** Hamilton product `a * b` (apply `b` first, then `a`). */\n mul: (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 ],\n conjugate: (q: Quat): Quat => [-q[0], -q[1], -q[2], q[3]],\n normalize: (q: Quat): Quat => {\n const l = Math.hypot(q[0], q[1], q[2], q[3])\n return l < EPS ? [0, 0, 0, 1] : [q[0] / l, q[1] / l, q[2] / l, q[3] / l]\n },\n /** Rotation of `angle` radians around a unit `axis`. */\n fromAxisAngle: (axis: Vec3, angle: number): Quat => {\n const s = Math.sin(angle / 2)\n return [axis[0] * s, axis[1] * s, axis[2] * s, Math.cos(angle / 2)]\n },\n /** Rotation matrix, row-major 3x3. */\n toMatrix: (q: Quat): number[] => {\n const [x, y, z, w] = q\n const xx = x * x, yy = y * y, zz = z * z, xy = x * y, xz = x * z, yz = y * z, wx = w * x, wy = w * y, wz = w * z\n return [1 - 2 * (yy + zz), 2 * (xy - wz), 2 * (xz + wy), 2 * (xy + wz), 1 - 2 * (xx + zz), 2 * (yz - wx), 2 * (xz - wy), 2 * (yz + wx), 1 - 2 * (xx + yy)]\n },\n /** Quaternion from three orthonormal columns (the images of the x, y and z axes). */\n fromColumns: (cx: Vec3, cy: Vec3, cz: Vec3): Quat => {\n const m00 = cx[0], m10 = cx[1], m20 = cx[2]\n const m01 = cy[0], m11 = cy[1], m21 = cy[2]\n const m02 = cz[0], m12 = cz[1], m22 = cz[2]\n const tr = m00 + m11 + m22\n let q: Quat\n if (tr > 0) {\n const s = Math.sqrt(tr + 1) * 2\n q = [(m21 - m12) / s, (m02 - m20) / s, (m10 - m01) / s, 0.25 * s]\n } else if (m00 > m11 && m00 > m22) {\n const s = Math.sqrt(1 + m00 - m11 - m22) * 2\n q = [0.25 * s, (m01 + m10) / s, (m02 + m20) / s, (m21 - m12) / s]\n } else if (m11 > m22) {\n const s = Math.sqrt(1 + m11 - m00 - m22) * 2\n q = [(m01 + m10) / s, 0.25 * s, (m12 + m21) / s, (m02 - m20) / s]\n } else {\n const s = Math.sqrt(1 + m22 - m00 - m11) * 2\n q = [(m02 + m20) / s, (m12 + m21) / s, 0.25 * s, (m10 - m01) / s]\n }\n return quat.normalize(q)\n },\n}\n\n/** Quaternion from Euler angles in radians, order XYZ (the three.js default). */\nexport const quatFromEuler = (x: number, y: number, z: number): Quat => {\n const c1 = Math.cos(x / 2), c2 = Math.cos(y / 2), c3 = Math.cos(z / 2)\n const s1 = Math.sin(x / 2), s2 = Math.sin(y / 2), 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/** Rotate a vector by a quaternion. */\nexport const rotateVec = (q: Quat, v: Vec3): Vec3 => {\n const qx = q[0], qy = q[1], qz = q[2], qw = q[3]\n const tx = 2 * (qy * v[2] - qz * v[1])\n const ty = 2 * (qz * v[0] - qx * v[2])\n const tz = 2 * (qx * v[1] - qy * v[0])\n return [v[0] + qw * tx + (qy * tz - qz * ty), v[1] + qw * ty + (qz * tx - qx * tz), v[2] + qw * tz + (qx * ty - qy * tx)]\n}\n\n/** World point to the local space of a transform (`position`, `rotation`). */\nexport const toLocal = (position: Vec3, rotation: Quat, p: Vec3): Vec3 =>\n rotateVec([-rotation[0], -rotation[1], -rotation[2], rotation[3]], [p[0] - position[0], p[1] - position[1], p[2] - position[2]])\n\n/** Local point of a transform (`position`, `rotation`) to world space. */\nexport const toWorld = (position: Vec3, rotation: Quat, p: Vec3): Vec3 => {\n const r = rotateVec(rotation, p)\n return [r[0] + position[0], r[1] + position[1], r[2] + position[2]]\n}\n\n// ---------------------------------------------------------------------------------------------------------------------------------------------\n// Shapes (part space: centred on the origin, cylinders along +Y)\n// ---------------------------------------------------------------------------------------------------------------------------------------------\n\n/** Dimensions of the bounding box of a shape, in the shape's own axes. */\nexport const shapeDims = (s: PartShape): Vec3 => {\n if (s.kind === 'box') return [s.size[0], s.size[1], s.size[2]]\n if (s.kind === 'cylinder') return [s.radius * 2, s.height, s.radius * 2]\n return [s.radius * 2, s.radius * 2, s.radius * 2]\n}\n\n/** Radius of the sphere that encloses the shape. */\nexport const shapeBound = (s: PartShape): number => {\n if (s.kind === 'box') return 0.5 * Math.hypot(s.size[0], s.size[1], s.size[2])\n if (s.kind === 'cylinder') return Math.hypot(s.radius, s.height / 2)\n return s.radius\n}\n\nexport const shapeArea = (s: PartShape): number => {\n if (s.kind === 'box') return 2 * (s.size[0] * s.size[1] + s.size[1] * s.size[2] + s.size[0] * s.size[2])\n if (s.kind === 'cylinder') return 2 * Math.PI * s.radius * s.height + 2 * Math.PI * s.radius * s.radius\n return 4 * Math.PI * s.radius * s.radius\n}\n\nexport interface ShapeRay {\n /** Entry distance along the ray (negative when the origin is inside). */\n t0: number\n /** Normal at the entry, outward. */\n n0: Vec3\n /** Exit distance. */\n t1: number\n /** Normal at the exit, outward. */\n n1: Vec3\n}\n\n/** Exact ray intersection with a shape in part space. `d` need not be normalised but must be non-zero. */\nexport const rayShape = (s: PartShape, o: Vec3, d: Vec3): ShapeRay | null => {\n if (s.kind === 'box') {\n let tmin = -Infinity, tmax = Infinity\n let a0 = 0, a1 = 0, s0 = 1, s1 = 1\n for (let i = 0; i < 3; i++) {\n const h = s.size[i] / 2\n if (Math.abs(d[i]) < EPS) {\n if (Math.abs(o[i]) > h) return null\n continue\n }\n let ta = (-h - o[i]) / d[i]\n let tb = (h - o[i]) / d[i]\n let sa = -1, sb = 1\n if (ta > tb) {\n const t = ta\n ta = tb\n tb = t\n sa = 1\n sb = -1\n }\n if (ta > tmin) {\n tmin = ta\n a0 = i\n s0 = sa\n }\n if (tb < tmax) {\n tmax = tb\n a1 = i\n s1 = sb\n }\n if (tmin > tmax) return null\n }\n if (tmax < 0 || !Number.isFinite(tmin) || !Number.isFinite(tmax)) return null\n const n0: Vec3 = [0, 0, 0]\n const n1: Vec3 = [0, 0, 0]\n n0[a0] = s0\n n1[a1] = s1\n return { t0: tmin, n0, t1: tmax, n1 }\n }\n if (s.kind === 'sphere') {\n const b = vec.dot(o, d)\n const a = vec.dot(d, d)\n const c = vec.dot(o, o) - s.radius * s.radius\n const disc = b * b - a * c\n if (disc < 0) return null\n const sq = Math.sqrt(disc)\n const t0 = (-b - sq) / a\n const t1 = (-b + sq) / a\n if (t1 < 0) return null\n const p0 = vec.madd(o, d, t0)\n const p1 = vec.madd(o, d, t1)\n return { t0, n0: vec.normalize(p0), t1, n1: vec.normalize(p1) }\n }\n // cylinder along Y: intersection of the infinite cylinder interval and the y slab\n const r = s.radius\n const hh = s.height / 2\n const a = d[0] * d[0] + d[2] * d[2]\n const c = o[0] * o[0] + o[2] * o[2] - r * r\n let tA = -Infinity, tB = Infinity\n if (a > EPS) {\n const b = o[0] * d[0] + o[2] * d[2]\n const disc = b * b - a * c\n if (disc < 0) return null\n const sq = Math.sqrt(disc)\n tA = (-b - sq) / a\n tB = (-b + sq) / a\n } else if (c > 0) return null\n let yA = -Infinity, yB = Infinity\n if (Math.abs(d[1]) > EPS) {\n yA = (-hh - o[1]) / d[1]\n yB = (hh - o[1]) / d[1]\n if (yA > yB) {\n const t = yA\n yA = yB\n yB = t\n }\n } else if (Math.abs(o[1]) > hh) return null\n const t0 = Math.max(tA, yA)\n const t1 = Math.min(tB, yB)\n if (t0 > t1 || t1 < 0 || !Number.isFinite(t0) || !Number.isFinite(t1)) return null\n const normalAt = (t: number, fromSide: boolean, sign: number): Vec3 => {\n if (fromSide) {\n const p = vec.madd(o, d, t)\n return vec.normalize([p[0], 0, p[2]], [1, 0, 0])\n }\n return [0, sign, 0]\n }\n const n0 = normalAt(t0, tA >= yA, d[1] > 0 ? -1 : 1)\n const n1 = normalAt(t1, tB <= yB, d[1] > 0 ? 1 : -1)\n return { t0, n0, t1, n1 }\n}\n\n/** Distance from a part-space point to the shape (0 inside). */\nexport const distanceToShape = (s: PartShape, p: Vec3): number => {\n if (s.kind === 'box') {\n const dx = Math.max(Math.abs(p[0]) - s.size[0] / 2, 0)\n const dy = Math.max(Math.abs(p[1]) - s.size[1] / 2, 0)\n const dz = Math.max(Math.abs(p[2]) - s.size[2] / 2, 0)\n return Math.hypot(dx, dy, dz)\n }\n if (s.kind === 'sphere') return Math.max(0, vec.length(p) - s.radius)\n const dr = Math.max(0, Math.hypot(p[0], p[2]) - s.radius)\n const dy = Math.max(0, Math.abs(p[1]) - s.height / 2)\n return Math.hypot(dr, dy)\n}\n\n/** Nearest point on the surface of a shape to a part-space point, with the outward normal. */\nexport const surfacePoint = (s: PartShape, p: Vec3): { point: Vec3; normal: Vec3 } => {\n if (s.kind === 'box') {\n const h: Vec3 = [s.size[0] / 2, s.size[1] / 2, s.size[2] / 2]\n const inside = Math.abs(p[0]) <= h[0] && Math.abs(p[1]) <= h[1] && Math.abs(p[2]) <= h[2]\n if (!inside) {\n const c: Vec3 = [Math.max(-h[0], Math.min(h[0], p[0])), Math.max(-h[1], Math.min(h[1], p[1])), Math.max(-h[2], Math.min(h[2], p[2]))]\n return { point: c, normal: vec.normalize(vec.sub(p, c)) }\n }\n let axis = 0\n let best = Infinity\n for (let i = 0; i < 3; i++) {\n const g = h[i] - Math.abs(p[i])\n if (g < best) {\n best = g\n axis = i\n }\n }\n const sign = p[axis] >= 0 ? 1 : -1\n const point: Vec3 = [p[0], p[1], p[2]]\n point[axis] = sign * h[axis]\n const normal: Vec3 = [0, 0, 0]\n normal[axis] = sign\n return { point, normal }\n }\n if (s.kind === 'sphere') {\n const n = vec.normalize(p)\n return { point: vec.scale(n, s.radius), normal: n }\n }\n const rho = Math.hypot(p[0], p[2])\n const hh = s.height / 2\n const inside = rho <= s.radius && Math.abs(p[1]) <= hh\n if (!inside) {\n const k = rho > s.radius ? s.radius / rho : 1\n const c: Vec3 = [p[0] * k, Math.max(-hh, Math.min(hh, p[1])), p[2] * k]\n return { point: c, normal: vec.normalize(vec.sub(p, c)) }\n }\n if (s.radius - rho < hh - Math.abs(p[1])) {\n const k = rho < EPS ? 0 : s.radius / rho\n const point: Vec3 = rho < EPS ? [s.radius, p[1], 0] : [p[0] * k, p[1], p[2] * k]\n return { point, normal: vec.normalize([point[0], 0, point[2]], [1, 0, 0]) }\n }\n const sign = p[1] >= 0 ? 1 : -1\n return { point: [p[0], sign * hh, p[2]], normal: [0, sign, 0] }\n}\n\n/** Axis-aligned bounds of a shape with a given centre and orientation. */\nexport const aabbOf = (s: PartShape, center: Vec3, q: Quat): { min: Vec3; max: Vec3 } => {\n let hx: number, hy: number, hz: number\n if (s.kind === 'sphere') {\n hx = hy = hz = s.radius\n } else {\n const m = quat.toMatrix(q)\n if (s.kind === 'box') {\n const a = s.size[0] / 2, b = s.size[1] / 2, c = s.size[2] / 2\n hx = Math.abs(m[0]) * a + Math.abs(m[1]) * b + Math.abs(m[2]) * c\n hy = Math.abs(m[3]) * a + Math.abs(m[4]) * b + Math.abs(m[5]) * c\n hz = Math.abs(m[6]) * a + Math.abs(m[7]) * b + Math.abs(m[8]) * c\n } else {\n const ax = [m[1], m[4], m[7]]\n const hh = s.height / 2\n const e = (i: number) => s.radius * Math.sqrt(Math.max(0, 1 - ax[i] * ax[i])) + hh * Math.abs(ax[i])\n hx = e(0)\n hy = e(1)\n hz = e(2)\n }\n }\n return { min: [center[0] - hx, center[1] - hy, center[2] - hz], max: [center[0] + hx, center[1] + hy, center[2] + hz] }\n}\n\n/** Seeded generator (mulberry32). The state is a single int32, so it round-trips through JSON. */\nexport interface Rng {\n next(): number\n state(): number\n set(state: number): void\n}\n\nexport const createRng = (seed: number): Rng => {\n let s = seed | 0\n return {\n next() {\n s = (s + 0x6d2b79f5) | 0\n let t = Math.imul(s ^ (s >>> 15), 1 | s)\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296\n },\n state: () => s,\n set(v) {\n s = v | 0\n },\n }\n}\n","import { quat } from './math'\nimport type { Fragment, FractureStyle, Quat, Vec3 } from './types'\n\n/** A fragment in the part's local space (centre, size in the fragment's own axes, orientation relative to the part). */\nexport interface PlannedFragment {\n center: Vec3\n size: Vec3\n q: Quat\n shape: Fragment['shape']\n /** Share of the part's mass (unnormalised). */\n weight: number\n}\n\nexport interface PlanInput {\n style: FractureStyle\n /** Bounding box dimensions in part axes. */\n dims: Vec3\n thickness: number\n /** Index 0..2 of the grain axis, if any. */\n grain?: number\n /** Impact point in part space. */\n hit: Vec3\n fragmentSize: [number, number]\n maxFragments: number\n rnd: () => number\n}\n\ninterface Box {\n c: Vec3\n s: Vec3\n}\n\n/** Split a box into `count` pieces by cutting the largest piece along its longest allowed axis. */\nconst bsp = (root: Box, count: number, axes: number[], rnd: () => number): Box[] => {\n const pieces: Box[] = [root]\n const vol = (b: Box) => axes.reduce((v, a) => v * b.s[a], 1)\n while (pieces.length < count) {\n let bi = 0\n for (let i = 1; i < pieces.length; i++) if (vol(pieces[i]) > vol(pieces[bi])) bi = i\n const b = pieces[bi]\n let ax = axes[0]\n for (const a of axes) if (b.s[a] > b.s[ax]) ax = a\n if (b.s[ax] < 1e-4) break\n const r = 0.3 + 0.4 * rnd()\n const l1 = b.s[ax] * r\n const l2 = b.s[ax] - l1\n const c1: Vec3 = [b.c[0], b.c[1], b.c[2]]\n const c2: Vec3 = [b.c[0], b.c[1], b.c[2]]\n c1[ax] = b.c[ax] - b.s[ax] / 2 + l1 / 2\n c2[ax] = b.c[ax] + b.s[ax] / 2 - l2 / 2\n const s1: Vec3 = [b.s[0], b.s[1], b.s[2]]\n const s2: Vec3 = [b.s[0], b.s[1], b.s[2]]\n s1[ax] = l1\n s2[ax] = l2\n pieces[bi] = { c: c1, s: s1 }\n pieces.push({ c: c2, s: s2 })\n }\n return pieces\n}\n\nconst parity = (p: [number, number, number]): number => {\n let inv = 0\n for (let i = 0; i < 3; i++) for (let j = i + 1; j < 3; j++) if (p[i] > p[j]) inv++\n return inv % 2 === 0 ? 1 : -1\n}\n\n/** Orientation whose x, y, z axes are the part axes `ax`, `ay`, `az`, then rotated by `angle` around its own z. */\nconst frame = (ax: number, ay: number, az: number, angle = 0): Quat => {\n const e = (i: number): Vec3 => [i === 0 ? 1 : 0, i === 1 ? 1 : 0, i === 2 ? 1 : 0]\n const z = e(az)\n const sg = parity([ax, ay, az])\n const q = quat.fromColumns(e(ax), e(ay), [z[0] * sg, z[1] * sg, z[2] * sg])\n return angle === 0 ? q : quat.mul(q, quat.fromAxisAngle([0, 0, 1], angle))\n}\n\nconst clamp = (v: number, a: number, b: number) => Math.max(a, Math.min(b, v))\n\n/** Deterministic fragment layout for a failed part, in part space. */\nexport const planFragments = (inp: PlanInput): PlannedFragment[] => {\n const { style, dims, rnd, maxFragments } = inp\n const [fmin, fmax] = inp.fragmentSize\n const mean = (fmin + fmax) / 2\n const root: Box = { c: [0, 0, 0], s: [dims[0], dims[1], dims[2]] }\n const idq = quat.identity()\n const out: PlannedFragment[] = []\n const sorted = [0, 1, 2].sort((a, b) => dims[a] - dims[b])\n const t = sorted[0]\n const [u, v] = [sorted[1], sorted[2]].sort((a, b) => a - b)\n\n if (style === 'snap') {\n const n = Math.min(maxFragments, 2 + Math.floor(rnd() * 3))\n for (const b of bsp(root, n, [0, 1, 2], rnd)) out.push({ center: b.c, size: b.s, q: idq, shape: 'slab', weight: b.s[0] * b.s[1] * b.s[2] })\n return out\n }\n if (style === 'crumble' || style === 'none') {\n const n = clamp(Math.ceil((dims[0] * dims[1] * dims[2]) / (mean * mean * mean)), 4, maxFragments)\n for (const b of bsp(root, n, [0, 1, 2], rnd)) out.push({ center: b.c, size: b.s, q: idq, shape: 'chunk', weight: b.s[0] * b.s[1] * b.s[2] })\n return out\n }\n if (style === 'splinter') {\n const g = inp.grain ?? [0, 1, 2].sort((a, b) => dims[b] - dims[a])[0]\n const [a, b] = [0, 1, 2].filter((i) => i !== g)\n const w = Math.max(fmin * 1.5, 0.01)\n const c = clamp(Math.round((dims[a] * dims[b]) / (w * w)), 3, Math.max(3, Math.floor(maxFragments / 2)))\n const maxLen = fmax * 4\n const cells = bsp(root, c, [a, b], rnd)\n const pieces: Box[] = []\n for (const cell of cells) {\n let list = [cell]\n const extra = rnd() < 0.4 ? 1 : 0\n const k = Math.max(1, Math.ceil(dims[g] / maxLen)) + extra\n list = bsp(cell, k, [g], rnd)\n for (const p of list) pieces.push(p)\n }\n for (const p of pieces.slice(0, maxFragments)) {\n out.push({ center: p.c, size: [p.s[g], p.s[a], p.s[b]], q: frame(g, a, b), shape: 'splinter', weight: p.s[0] * p.s[1] * p.s[2] })\n }\n return out\n }\n if (style === 'tear') {\n const n = clamp(Math.round((dims[u] * dims[v]) / (mean * mean)), 3, maxFragments)\n const th = Math.min(inp.thickness, dims[t])\n for (const b of bsp(root, n, [u, v], rnd)) {\n out.push({ center: [b.c[0], b.c[1], b.c[2]], size: [b.s[u], b.s[v], th], q: frame(u, v, t), shape: 'scrap', weight: b.s[u] * b.s[v] })\n }\n return out\n }\n // shatter: thin shards radiating from the impact\n const th = Math.min(inp.thickness, dims[t])\n const Hu = dims[u] / 2, Hv = dims[v] / 2\n const hu = clamp(inp.hit[u], -Hu * 0.95, Hu * 0.95)\n const hv = clamp(inp.hit[v], -Hv * 0.95, Hv * 0.95)\n const R = Math.max(Math.hypot(Hu + Math.abs(hu), Hv + Math.abs(hv)), 0.02)\n const n = clamp(Math.round((dims[u] * dims[v]) / (mean * mean)), 6, maxFragments)\n const sectors = clamp(Math.round(Math.sqrt(n * 1.6)), 5, 10)\n const rings = Math.max(1, Math.ceil(n / sectors))\n const radii: number[] = []\n for (let k = 0; k <= rings; k++) radii.push(R * Math.pow(k / rings, 1.3))\n const cap = fmax * 1.5\n for (let j = 0; j < sectors && out.length < maxFragments; j++) {\n const theta = (j + (rnd() - 0.5) * 0.4) * ((Math.PI * 2) / sectors)\n for (let k = 0; k < rings && out.length < maxFragments; k++) {\n const rm = (radii[k] + radii[k + 1]) / 2\n const cu = hu + rm * Math.cos(theta)\n const cv = hv + rm * Math.sin(theta)\n if (Math.abs(cu) > Hu + 0.01 || Math.abs(cv) > Hv + 0.01) continue\n const radial = clamp(radii[k + 1] - radii[k], 0.01, cap)\n const tang = clamp(radii[k + 1] * ((Math.PI * 2) / sectors) * 0.9, 0.01, cap)\n const c: Vec3 = [0, 0, 0]\n c[u] = clamp(cu, -Hu * 0.98, Hu * 0.98)\n c[v] = clamp(cv, -Hv * 0.98, Hv * 0.98)\n c[t] = 0\n out.push({ center: c, size: [radial, tang, th], q: frame(u, v, t, theta), shape: 'shard', weight: radial * tang })\n }\n }\n if (out.length === 0) out.push({ center: [0, 0, 0], size: [dims[u], dims[v], th], q: frame(u, v, t), shape: 'shard', weight: 1 })\n return out\n}\n","[\n {\n \"id\": \"steel\",\n \"density\": 7850,\n \"hpPerKg\": 400,\n \"penetration\": 110,\n \"ductility\": 0.9,\n \"fracture\": \"none\",\n \"damageScale\": {\n \"slash\": 0.1,\n \"blunt\": 0.8\n },\n \"hitDebris\": \"sparks\",\n \"breakDebris\": \"sparks\",\n \"ricochetDeg\": 20,\n \"thermal\": {\n \"meltC\": 1450,\n \"meltRate\": 0.05,\n \"conductivity\": 0.5,\n \"heatCapacity\": 3\n },\n \"visual\": {\n \"glows\": true\n },\n \"tags\": [\n \"metal\",\n \"conductive\"\n ],\n \"compressive\": 2000000\n },\n {\n \"id\": \"cast_iron\",\n \"density\": 7200,\n \"hpPerKg\": 150,\n \"penetration\": 160,\n \"ductility\": 0.2,\n \"fracture\": \"snap\",\n \"fragmentSize\": [\n 0.08,\n 0.3\n ],\n \"damageScale\": {\n \"slash\": 0.1,\n \"blunt\": 1.2\n },\n \"hitDebris\": \"sparks\",\n \"breakDebris\": \"chips\",\n \"ricochetDeg\": 15,\n \"thermal\": {\n \"meltC\": 1200,\n \"meltRate\": 0.05,\n \"conductivity\": 0.45,\n \"heatCapacity\": 3\n },\n \"visual\": {\n \"glows\": true\n },\n \"tags\": [\n \"metal\",\n \"conductive\"\n ],\n \"compressive\": 1500000\n },\n {\n \"id\": \"aluminium\",\n \"density\": 2700,\n \"hpPerKg\": 250,\n \"penetration\": 60,\n \"ductility\": 0.85,\n \"fracture\": \"none\",\n \"damageScale\": {\n \"slash\": 0.2\n },\n \"hitDebris\": \"sparks\",\n \"breakDebris\": \"sparks\",\n \"ricochetDeg\": 15,\n \"thermal\": {\n \"meltC\": 660,\n \"meltRate\": 0.06,\n \"conductivity\": 0.8,\n \"heatCapacity\": 1.5\n },\n \"visual\": {\n \"glows\": true\n },\n \"tags\": [\n \"metal\",\n \"conductive\"\n ],\n \"compressive\": 800000\n },\n {\n \"id\": \"copper\",\n \"density\": 8960,\n \"hpPerKg\": 300,\n \"penetration\": 70,\n \"ductility\": 0.95,\n \"fracture\": \"none\",\n \"damageScale\": {\n \"slash\": 0.2\n },\n \"hitDebris\": \"sparks\",\n \"breakDebris\": \"sparks\",\n \"ricochetDeg\": 15,\n \"thermal\": {\n \"meltC\": 1085,\n \"meltRate\": 0.05,\n \"conductivity\": 1,\n \"heatCapacity\": 2.5\n },\n \"visual\": {\n \"glows\": true\n },\n \"tags\": [\n \"metal\",\n \"conductive\"\n ],\n \"compressive\": 1000000\n },\n {\n \"id\": \"wood_soft\",\n \"density\": 500,\n \"hpPerKg\": 90,\n \"penetration\": 9,\n \"ductility\": 0.35,\n \"fracture\": \"splinter\",\n \"fragmentSize\": [\n 0.03,\n 0.15\n ],\n \"damageScale\": {\n \"blunt\": 1.2,\n \"slash\": 1.5\n },\n \"hitDebris\": \"splinters\",\n \"breakDebris\": \"splinters\",\n \"thermal\": {\n \"ignitionC\": 300,\n \"flameC\": 800,\n \"fuelFraction\": 0.9,\n \"burnRate\": 0.012,\n \"conductivity\": 0.05,\n \"heatCapacity\": 1.2,\n \"smoke\": \"grey\"\n },\n \"visual\": {\n \"charColor\": 1709072\n },\n \"tags\": [\n \"wood\",\n \"organic\",\n \"flammable\"\n ],\n \"compressive\": 100000\n },\n {\n \"id\": \"wood_hard\",\n \"density\": 750,\n \"hpPerKg\": 140,\n \"penetration\": 14,\n \"ductility\": 0.3,\n \"fracture\": \"splinter\",\n \"fragmentSize\": [\n 0.03,\n 0.2\n ],\n \"damageScale\": {\n \"slash\": 1.2\n },\n \"hitDebris\": \"splinters\",\n \"breakDebris\": \"splinters\",\n \"thermal\": {\n \"ignitionC\": 330,\n \"flameC\": 800,\n \"fuelFraction\": 0.9,\n \"burnRate\": 0.01,\n \"conductivity\": 0.06,\n \"heatCapacity\": 1.4,\n \"smoke\": \"grey\"\n },\n \"visual\": {\n \"charColor\": 1709072\n },\n \"tags\": [\n \"wood\",\n \"organic\",\n \"flammable\"\n ],\n \"compressive\": 150000\n },\n {\n \"id\": \"plywood\",\n \"density\": 600,\n \"hpPerKg\": 70,\n \"penetration\": 7,\n \"ductility\": 0.3,\n \"fracture\": \"snap\",\n \"fragmentSize\": [\n 0.1,\n 0.4\n ],\n \"damageScale\": {\n \"slash\": 1.3\n },\n \"hitDebris\": \"splinters\",\n \"breakDebris\": \"splinters\",\n \"thermal\": {\n \"ignitionC\": 300,\n \"flameC\": 800,\n \"fuelFraction\": 0.85,\n \"burnRate\": 0.013,\n \"conductivity\": 0.05,\n \"heatCapacity\": 1.2,\n \"smoke\": \"grey\"\n },\n \"visual\": {\n \"charColor\": 1709072\n },\n \"tags\": [\n \"wood\",\n \"organic\",\n \"flammable\"\n ],\n \"compressive\": 60000\n },\n {\n \"id\": \"mdf\",\n \"density\": 750,\n \"hpPerKg\": 45,\n \"penetration\": 6,\n \"ductility\": 0.15,\n \"fracture\": \"snap\",\n \"fragmentSize\": [\n 0.08,\n 0.35\n ],\n \"hitDebris\": \"chips\",\n \"breakDebris\": \"dust\",\n \"thermal\": {\n \"ignitionC\": 290,\n \"flameC\": 750,\n \"fuelFraction\": 0.85,\n \"burnRate\": 0.014,\n \"conductivity\": 0.05,\n \"heatCapacity\": 1.2,\n \"smoke\": \"grey\"\n },\n \"visual\": {\n \"charColor\": 1709072\n },\n \"tags\": [\n \"wood\",\n \"organic\",\n \"flammable\"\n ],\n \"compressive\": 30000\n },\n {\n \"id\": \"glass\",\n \"density\": 2500,\n \"hpPerKg\": 12,\n \"penetration\": 6,\n \"ductility\": 0.02,\n \"fracture\": \"shatter\",\n \"fragmentSize\": [\n 0.05,\n 0.25\n ],\n \"damageScale\": {\n \"blunt\": 1.5\n },\n \"hitDebris\": \"shards\",\n \"breakDebris\": \"shards\",\n \"ricochetDeg\": 8,\n \"thermal\": {\n \"meltC\": 1400,\n \"meltRate\": 0.03,\n \"conductivity\": 0.1,\n \"heatCapacity\": 1.5,\n \"thermalShockC\": 150\n },\n \"visual\": {\n \"glows\": true,\n \"transparent\": true\n },\n \"tags\": [\n \"glass\",\n \"brittle\"\n ],\n \"compressive\": 100000\n },\n {\n \"id\": \"tempered_glass\",\n \"density\": 2500,\n \"hpPerKg\": 30,\n \"penetration\": 9,\n \"ductility\": 0.02,\n \"fracture\": \"crumble\",\n \"fragmentSize\": [\n 0.006,\n 0.02\n ],\n \"hitDebris\": \"shards\",\n \"breakDebris\": \"shards\",\n \"ricochetDeg\": 8,\n \"thermal\": {\n \"meltC\": 1400,\n \"meltRate\": 0.03,\n \"conductivity\": 0.1,\n \"heatCapacity\": 1.5,\n \"thermalShockC\": 250\n },\n \"visual\": {\n \"glows\": true,\n \"transparent\": true\n },\n \"tags\": [\n \"glass\",\n \"brittle\"\n ],\n \"compressive\": 200000\n },\n {\n \"id\": \"ceramic\",\n \"density\": 2400,\n \"hpPerKg\": 25,\n \"penetration\": 12,\n \"ductility\": 0.02,\n \"fracture\": \"shatter\",\n \"fragmentSize\": [\n 0.03,\n 0.12\n ],\n \"damageScale\": {\n \"blunt\": 1.3\n },\n \"hitDebris\": \"chips\",\n \"breakDebris\": \"shards\",\n \"thermal\": {\n \"conductivity\": 0.1,\n \"heatCapacity\": 1.5,\n \"thermalShockC\": 200\n },\n \"tags\": [\n \"ceramic\",\n \"brittle\"\n ],\n \"compressive\": 150000\n },\n {\n \"id\": \"concrete\",\n \"density\": 2300,\n \"hpPerKg\": 14,\n \"penetration\": 18,\n \"ductility\": 0.03,\n \"fracture\": \"crumble\",\n \"fragmentSize\": [\n 0.05,\n 0.25\n ],\n \"damageScale\": {\n \"explosive\": 1.2\n },\n \"hitDebris\": \"chips\",\n \"breakDebris\": \"dust\",\n \"thermal\": {\n \"conductivity\": 0.15,\n \"heatCapacity\": 2.5\n },\n \"tags\": [\n \"masonry\",\n \"brittle\"\n ],\n \"compressive\": 444000\n },\n {\n \"id\": \"brick\",\n \"density\": 1900,\n \"hpPerKg\": 12,\n \"penetration\": 14,\n \"ductility\": 0.05,\n \"fracture\": \"crumble\",\n \"fragmentSize\": [\n 0.04,\n 0.12\n ],\n \"hitDebris\": \"chips\",\n \"breakDebris\": \"dust\",\n \"thermal\": {\n \"conductivity\": 0.15,\n \"heatCapacity\": 2.5\n },\n \"tags\": [\n \"masonry\",\n \"brittle\"\n ],\n \"compressive\": 40000\n },\n {\n \"id\": \"plaster\",\n \"density\": 850,\n \"hpPerKg\": 18,\n \"penetration\": 3,\n \"ductility\": 0.05,\n \"fracture\": \"crumble\",\n \"fragmentSize\": [\n 0.05,\n 0.3\n ],\n \"hitDebris\": \"dust\",\n \"breakDebris\": \"dust\",\n \"thermal\": {\n \"conductivity\": 0.1,\n \"heatCapacity\": 1.5\n },\n \"tags\": [\n \"masonry\",\n \"brittle\"\n ],\n \"compressive\": 8000\n },\n {\n \"id\": \"stone\",\n \"density\": 2700,\n \"hpPerKg\": 25,\n \"penetration\": 25,\n \"ductility\": 0.03,\n \"fracture\": \"crumble\",\n \"fragmentSize\": [\n 0.06,\n 0.3\n ],\n \"hitDebris\": \"chips\",\n \"breakDebris\": \"dust\",\n \"thermal\": {\n \"conductivity\": 0.2,\n \"heatCapacity\": 2.5\n },\n \"tags\": [\n \"masonry\",\n \"brittle\"\n ],\n \"compressive\": 300000\n },\n {\n \"id\": \"plastic\",\n \"density\": 1100,\n \"hpPerKg\": 60,\n \"penetration\": 8,\n \"ductility\": 0.5,\n \"fracture\": \"snap\",\n \"fragmentSize\": [\n 0.05,\n 0.3\n ],\n \"hitDebris\": \"chips\",\n \"breakDebris\": \"chips\",\n \"thermal\": {\n \"ignitionC\": 400,\n \"flameC\": 700,\n \"fuelFraction\": 0.8,\n \"burnRate\": 0.02,\n \"meltC\": 200,\n \"meltRate\": 0.06,\n \"conductivity\": 0.05,\n \"heatCapacity\": 1,\n \"smoke\": \"black\"\n },\n \"visual\": {\n \"charColor\": 855309\n },\n \"tags\": [\n \"plastic\",\n \"flammable\"\n ],\n \"compressive\": 40000\n },\n {\n \"id\": \"rubber\",\n \"density\": 1100,\n \"hpPerKg\": 80,\n \"penetration\": 12,\n \"ductility\": 0.9,\n \"fracture\": \"tear\",\n \"fragmentSize\": [\n 0.05,\n 0.2\n ],\n \"hitDebris\": \"chips\",\n \"breakDebris\": \"fibers\",\n \"thermal\": {\n \"ignitionC\": 350,\n \"flameC\": 750,\n \"fuelFraction\": 0.6,\n \"burnRate\": 0.015,\n \"conductivity\": 0.05,\n \"heatCapacity\": 1.2,\n \"smoke\": \"black\"\n },\n \"visual\": {\n \"charColor\": 855309\n },\n \"tags\": [\n \"rubber\",\n \"flammable\"\n ],\n \"compressive\": 20000\n },\n {\n \"id\": \"fabric\",\n \"density\": 400,\n \"hpPerKg\": 20,\n \"penetration\": 0.8,\n \"ductility\": 0.8,\n \"fracture\": \"tear\",\n \"fragmentSize\": [\n 0.05,\n 0.25\n ],\n \"damageScale\": {\n \"slash\": 4,\n \"ballistic\": 0.5\n },\n \"hitDebris\": \"fibers\",\n \"breakDebris\": \"fibers\",\n \"thermal\": {\n \"ignitionC\": 230,\n \"flameC\": 600,\n \"fuelFraction\": 1,\n \"burnRate\": 0.05,\n \"conductivity\": 0.03,\n \"heatCapacity\": 0.6,\n \"smoke\": \"grey\"\n },\n \"visual\": {\n \"charColor\": 1709072\n },\n \"tags\": [\n \"fabric\",\n \"organic\",\n \"flammable\"\n ],\n \"compressive\": 500\n },\n {\n \"id\": \"foam\",\n \"density\": 30,\n \"hpPerKg\": 15,\n \"penetration\": 0.5,\n \"ductility\": 0.7,\n \"fracture\": \"tear\",\n \"fragmentSize\": [\n 0.03,\n 0.15\n ],\n \"damageScale\": {\n \"slash\": 2,\n \"ballistic\": 0.4\n },\n \"hitDebris\": \"fibers\",\n \"breakDebris\": \"fibers\",\n \"thermal\": {\n \"ignitionC\": 250,\n \"flameC\": 900,\n \"fuelFraction\": 0.95,\n \"burnRate\": 0.09,\n \"conductivity\": 0.02,\n \"heatCapacity\": 0.5,\n \"smoke\": \"black\"\n },\n \"visual\": {\n \"charColor\": 855309\n },\n \"tags\": [\n \"fabric\",\n \"flammable\"\n ],\n \"compressive\": 100\n },\n {\n \"id\": \"leather\",\n \"density\": 900,\n \"hpPerKg\": 50,\n \"penetration\": 4,\n \"ductility\": 0.8,\n \"fracture\": \"tear\",\n \"fragmentSize\": [\n 0.05,\n 0.25\n ],\n \"damageScale\": {\n \"slash\": 2\n },\n \"hitDebris\": \"fibers\",\n \"breakDebris\": \"fibers\",\n \"thermal\": {\n \"ignitionC\": 320,\n \"flameC\": 650,\n \"fuelFraction\": 0.7,\n \"burnRate\": 0.01,\n \"conductivity\": 0.05,\n \"heatCapacity\": 1,\n \"smoke\": \"grey\"\n },\n \"visual\": {\n \"charColor\": 1709072\n },\n \"tags\": [\n \"fabric\",\n \"organic\",\n \"flammable\"\n ],\n \"compressive\": 2000\n },\n {\n \"id\": \"paper\",\n \"density\": 700,\n \"hpPerKg\": 8,\n \"penetration\": 0.3,\n \"ductility\": 0.5,\n \"fracture\": \"tear\",\n \"fragmentSize\": [\n 0.03,\n 0.15\n ],\n \"damageScale\": {\n \"slash\": 3\n },\n \"hitDebris\": \"fibers\",\n \"breakDebris\": \"fibers\",\n \"thermal\": {\n \"ignitionC\": 230,\n \"flameC\": 600,\n \"fuelFraction\": 1,\n \"burnRate\": 0.06,\n \"conductivity\": 0.03,\n \"heatCapacity\": 0.5,\n \"smoke\": \"grey\"\n },\n \"visual\": {\n \"charColor\": 1709072\n },\n \"tags\": [\n \"fabric\",\n \"organic\",\n \"flammable\"\n ],\n \"compressive\": 500\n },\n {\n \"id\": \"foliage\",\n \"density\": 150,\n \"hpPerKg\": 20,\n \"penetration\": 0.5,\n \"ductility\": 0.6,\n \"fracture\": \"tear\",\n \"fragmentSize\": [\n 0.03,\n 0.1\n ],\n \"damageScale\": {\n \"slash\": 4\n },\n \"hitDebris\": \"leaves\",\n \"breakDebris\": \"leaves\",\n \"thermal\": {\n \"ignitionC\": 300,\n \"flameC\": 700,\n \"fuelFraction\": 0.8,\n \"burnRate\": 0.08,\n \"conductivity\": 0.03,\n \"heatCapacity\": 0.8,\n \"smoke\": \"white\"\n },\n \"visual\": {\n \"charColor\": 1709072\n },\n \"tags\": [\n \"plant\",\n \"organic\",\n \"flammable\"\n ],\n \"compressive\": 500\n },\n {\n \"id\": \"green_wood\",\n \"density\": 650,\n \"hpPerKg\": 5,\n \"penetration\": 10,\n \"ductility\": 0.5,\n \"fracture\": \"splinter\",\n \"fragmentSize\": [\n 0.03,\n 0.2\n ],\n \"damageScale\": {\n \"slash\": 1.3,\n \"ballistic\": 0.1\n },\n \"hitDebris\": \"splinters\",\n \"breakDebris\": \"leaves\",\n \"thermal\": {\n \"ignitionC\": 400,\n \"flameC\": 700,\n \"fuelFraction\": 0.8,\n \"burnRate\": 0.006,\n \"conductivity\": 0.06,\n \"heatCapacity\": 2,\n \"smoke\": \"white\"\n },\n \"visual\": {\n \"charColor\": 1709072\n },\n \"tags\": [\n \"plant\",\n \"wood\",\n \"organic\",\n \"flammable\"\n ],\n \"compressive\": 60000\n },\n {\n \"id\": \"cactus\",\n \"density\": 900,\n \"hpPerKg\": 30,\n \"penetration\": 2,\n \"ductility\": 0.6,\n \"fracture\": \"tear\",\n \"fragmentSize\": [\n 0.04,\n 0.15\n ],\n \"damageScale\": {\n \"slash\": 3\n },\n \"hitDebris\": \"fibers\",\n \"breakDebris\": \"fibers\",\n \"thermal\": {\n \"ignitionC\": 450,\n \"flameC\": 650,\n \"fuelFraction\": 0.3,\n \"burnRate\": 0.01,\n \"conductivity\": 0.05,\n \"heatCapacity\": 3,\n \"smoke\": \"white\"\n },\n \"visual\": {\n \"charColor\": 1709072\n },\n \"tags\": [\n \"plant\",\n \"organic\",\n \"flammable\"\n ],\n \"compressive\": 5000\n },\n {\n \"id\": \"flesh_fruit\",\n \"density\": 950,\n \"hpPerKg\": 15,\n \"penetration\": 1.5,\n \"ductility\": 0.7,\n \"fracture\": \"tear\",\n \"fragmentSize\": [\n 0.02,\n 0.08\n ],\n \"damageScale\": {\n \"slash\": 2\n },\n \"hitDebris\": \"drips\",\n \"breakDebris\": \"drips\",\n \"thermal\": {\n \"conductivity\": 0.05,\n \"heatCapacity\": 3\n },\n \"tags\": [\n \"plant\",\n \"organic\"\n ],\n \"compressive\": 1000\n },\n {\n \"id\": \"wax\",\n \"density\": 900,\n \"hpPerKg\": 30,\n \"penetration\": 3,\n \"ductility\": 0.5,\n \"fracture\": \"snap\",\n \"fragmentSize\": [\n 0.05,\n 0.2\n ],\n \"hitDebris\": \"chips\",\n \"breakDebris\": \"chips\",\n \"thermal\": {\n \"ignitionC\": 230,\n \"flameC\": 700,\n \"fuelFraction\": 0.9,\n \"burnRate\": 0.02,\n \"meltC\": 60,\n \"meltRate\": 0.2,\n \"conductivity\": 0.05,\n \"heatCapacity\": 0.8,\n \"smoke\": \"grey\"\n },\n \"tags\": [\n \"wax\",\n \"flammable\"\n ],\n \"compressive\": 5000\n },\n {\n \"id\": \"soil\",\n \"density\": 1300,\n \"hpPerKg\": 3,\n \"penetration\": 5,\n \"ductility\": 0.3,\n \"fracture\": \"crumble\",\n \"fragmentSize\": [\n 0.03,\n 0.12\n ],\n \"hitDebris\": \"dust\",\n \"breakDebris\": \"dust\",\n \"thermal\": {\n \"conductivity\": 0.1,\n \"heatCapacity\": 2.5\n },\n \"tags\": [\n \"soil\",\n \"masonry\"\n ],\n \"compressive\": 20000\n }\n]","{\n \"cast iron\": \"cast_iron\",\n \"cast_iron\": \"cast_iron\",\n \"castiron\": \"cast_iron\",\n \"metal\": \"steel\",\n \"steel\": \"steel\",\n \"iron\": \"steel\",\n \"chrome\": \"steel\",\n \"alumin\": \"aluminium\",\n \"alu_\": \"aluminium\",\n \"alu-\": \"aluminium\",\n \"alu \": \"aluminium\",\n \"copper\": \"copper\",\n \"brass\": \"copper\",\n \"bronze\": \"copper\",\n \"wood\": \"wood_soft\",\n \"timber\": \"wood_soft\",\n \"plank\": \"wood_soft\",\n \"lumber\": \"wood_soft\",\n \"pine\": \"wood_soft\",\n \"hardwood\": \"wood_hard\",\n \"oak\": \"wood_hard\",\n \"plywood\": \"plywood\",\n \"mdf\": \"mdf\",\n \"chipboard\": \"mdf\",\n \"glass\": \"glass\",\n \"window\": \"glass\",\n \"mirror\": \"glass\",\n \"tempered\": \"tempered_glass\",\n \"windshield\": \"tempered_glass\",\n \"fabric\": \"fabric\",\n \"cloth\": \"fabric\",\n \"cushion\": \"fabric\",\n \"sofa\": \"fabric\",\n \"curtain\": \"fabric\",\n \"carpet\": \"fabric\",\n \"textile\": \"fabric\",\n \"canvas\": \"fabric\",\n \"foam\": \"foam\",\n \"sponge\": \"foam\",\n \"mattress\": \"foam\",\n \"leather\": \"leather\",\n \"paper\": \"paper\",\n \"cardboard\": \"paper\",\n \"leaf\": \"foliage\",\n \"leaves\": \"foliage\",\n \"foliage\": \"foliage\",\n \"bush\": \"foliage\",\n \"grass\": \"foliage\",\n \"trunk\": \"green_wood\",\n \"branch\": \"green_wood\",\n \"twig\": \"green_wood\",\n \"cactus\": \"cactus\",\n \"fruit\": \"flesh_fruit\",\n \"apple\": \"flesh_fruit\",\n \"melon\": \"flesh_fruit\",\n \"wax\": \"wax\",\n \"candle\": \"wax\",\n \"concrete\": \"concrete\",\n \"cement\": \"concrete\",\n \"brick\": \"brick\",\n \"tile\": \"ceramic\",\n \"ceramic\": \"ceramic\",\n \"porcelain\": \"ceramic\",\n \"stone\": \"stone\",\n \"rock\": \"stone\",\n \"marble\": \"stone\",\n \"plaster\": \"plaster\",\n \"drywall\": \"plaster\",\n \"gypsum\": \"plaster\",\n \"plastic\": \"plastic\",\n \"pvc\": \"plastic\",\n \"acrylic\": \"plastic\",\n \"rubber\": \"rubber\",\n \"tire\": \"rubber\",\n \"tyre\": \"rubber\",\n \"soil\": \"soil\",\n \"dirt\": \"soil\",\n \"potting\": \"soil\",\n \"mud\": \"soil\"\n}","import materialsJson from './data/materials.json'\nimport hintsJson from './data/material-hints.json'\nimport { aabbOf, quatFromEuler, shapeDims } from './math'\nimport type { JointKind, JointSpec, MaterialDef, MaterialId, PartShape, PartSpec, ToolKind } from './types'\n\n/** Built-in materials (data in `src/data/materials.json`). Override or extend with the `materials` option of `createDestruction`. */\nexport const MATERIALS: MaterialDef[] = materialsJson as unknown as MaterialDef[]\n\n/** Default joint strength (joint hit points), tools that undo it and seconds of tool work. */\nexport const JOINT_DEFAULTS: Record<JointKind, { strength: number; tools: ToolKind[]; seconds: number }> = {\n screw: { strength: 80, tools: ['screwdriver'], seconds: 2.5 },\n bolt: { strength: 200, tools: ['wrench'], seconds: 4 },\n nail: { strength: 60, tools: ['pry_bar', 'hammer'], seconds: 2 },\n weld: { strength: 600, tools: ['cutting_torch', 'saw'], seconds: 8 },\n glue: { strength: 50, tools: ['pry_bar', 'knife'], seconds: 3 },\n hinge: { strength: 120, tools: ['screwdriver'], seconds: 3 },\n clip: { strength: 20, tools: ['hand'], seconds: 0.6 },\n tie: { strength: 15, tools: ['knife', 'hand'], seconds: 1 },\n mortar: { strength: 400, tools: ['hammer'], seconds: 6 },\n fused: { strength: 1000, tools: [], seconds: 0 },\n stem: { strength: 40, tools: ['knife', 'saw', 'hand'], seconds: 1.5 },\n rebar: { strength: 1500, tools: ['cutting_torch', 'saw'], seconds: 20 },\n resting: { strength: 30, tools: ['hand'], seconds: 1 },\n}\n\nconst has = (m: MaterialDef, tag: string) => !!m.tags && m.tags.includes(tag)\n\n/**\n * The joint kind that fits two materials: glass is glued (clipped to plastic), soft things are tied, plants share a stem, masonry gets mortar,\n * metal is welded, wood is screwed, plastic clips. `inferJoints` refines weld to bolt for thick metal and screw to nail for thin wood.\n */\nexport const JOINT_BY_MATERIALS = (a: MaterialDef, b: MaterialDef): JointKind => {\n const any = (tag: string) => has(a, tag) || has(b, tag)\n const both = (tag: string) => has(a, tag) && has(b, tag)\n if (any('glass')) return any('plastic') ? 'clip' : 'glue'\n if (any('fabric')) return 'tie'\n if (both('plant')) return 'stem'\n if (any('plant')) return 'tie'\n if (both('masonry')) return 'mortar'\n if (any('masonry') && any('metal')) return 'bolt'\n if (both('metal')) return 'weld'\n if (any('plastic')) return 'clip'\n if (any('rubber') || any('wax')) return 'glue'\n return 'screw'\n}\n\nconst MATERIAL_MAP: ReadonlyMap<MaterialId, MaterialDef> = new Map(MATERIALS.map((m) => [m.id, m]))\n\nconst HINTS = Object.entries(hintsJson as Record<string, string>).sort((x, y) => y[0].length - x[0].length)\n\n/** Guess a material id from a mesh or node name (case-insensitive substring; the longest keyword wins). */\nexport const guessMaterial = (name: string): MaterialId | undefined => {\n const n = name.toLowerCase()\n for (const [key, id] of HINTS) if (n.includes(key)) return id\n return undefined\n}\n\n/** Volume of a shape in m³. */\nexport const partVolume = (shape: PartShape): number => {\n if (shape.kind === 'box') return shape.size[0] * shape.size[1] * shape.size[2]\n if (shape.kind === 'cylinder') return Math.PI * shape.radius * shape.radius * shape.height\n return (4 / 3) * Math.PI * shape.radius ** 3\n}\n\n/** Mass of a part in kg: its `mass`, or density × volume × fill. */\nexport const partMass = (part: PartSpec, material: MaterialDef): number => part.mass ?? material.density * partVolume(part.shape) * (part.fill ?? 1)\n\n/** Wall thickness of a part in metres (see `PartSpec.thickness`). */\nexport const partThickness = (part: PartSpec): number => {\n const dims = shapeDims(part.shape)\n const min = Math.min(dims[0], dims[1], dims[2])\n return Math.max(1e-4, Math.min(part.thickness ?? min * (part.fill ?? 1), min))\n}\n\n/**\n * Joints for touching parts. Two parts touch when their axis-aligned bounds (after rotation), grown by `tolerance` metres, overlap. The kind comes\n * from `JOINT_BY_MATERIALS`; thick metal pairs (smallest dimension of both >= 2 cm) get a `bolt` instead of a `weld`, and thin wood pairs (either\n * under 2 cm) a `nail` instead of a `screw`.\n */\nexport const inferJoints = (parts: PartSpec[], tolerance = 0.01, materials: ReadonlyMap<string, MaterialDef> = MATERIAL_MAP): JointSpec[] => {\n const boxes = parts.map((p) => aabbOf(p.shape, p.position, quatFromEuler(...(p.rotation ?? [0, 0, 0]))))\n const out: JointSpec[] = []\n for (let i = 0; i < parts.length; i++) {\n for (let j = i + 1; j < parts.length; j++) {\n const a = boxes[i], b = boxes[j]\n let touch = true\n for (let k = 0; k < 3 && touch; k++) if (a.min[k] - tolerance > b.max[k] || b.min[k] - tolerance > a.max[k]) touch = false\n if (!touch) continue\n const ma = materials.get(parts[i].material)\n const mb = materials.get(parts[j].material)\n let kind: JointKind = ma && mb ? JOINT_BY_MATERIALS(ma, mb) : 'screw'\n const thin = Math.min(partThickness(parts[i]), partThickness(parts[j]))\n if (kind === 'weld' && thin >= 0.02) kind = 'bolt'\n if (kind === 'screw' && ma && mb && has(ma, 'wood') && has(mb, 'wood') && thin < 0.02) kind = 'nail'\n out.push({ id: `${parts[i].id}~${parts[j].id}`, a: parts[i].id, b: parts[j].id, kind })\n }\n }\n return out\n}\n\n/**\n * Load in kg a structural part carries before it fails: `structure.capacity`, else `material.compressive` (default 20 000 kg/m²) times the\n * section. The section is the product of the two smallest dimensions (thickness times longest dimension for slabs; thickness times horizontal length for walls); foundations get x10.\n */\nexport const partCapacity = (part: PartSpec, material: MaterialDef): number => {\n if (part.structure?.capacity !== undefined) return part.structure.capacity\n const d = shapeDims(part.shape).sort((a, b) => a - b)\n const role = part.structure?.role\n const sh = shapeDims(part.shape)\n const section = role === 'wall' || role === 'partition' ? Math.min(sh[0], sh[2]) * Math.max(sh[0], sh[2]) : role === 'slab' ? d[0] * d[2] : d[0] * d[1]\n return Math.max(1, section * (material.compressive ?? 20000) * (role === 'foundation' ? 10 : 1))\n}\n\n\n/**\n * Multiplier of blunt damage on brittle parts (ductility < 0.3): up to x4.7 for fully brittle materials (glass, ceramic, stone), times a thinness\n * term that follows bending stress (1 / thickness squared, x1 at 2 cm or more, at most x25). A 3 mm glass bottle breaks from a 1.5 m drop; a\n * concrete block (thick) is only ever x4.4.\n */\nexport const fragility = (ductility: number, thickness: number): number => {\n if (ductility >= 0.3) return 1\n return (1 + (4 * (0.3 - ductility)) / 0.3) * Math.min(25, Math.max(1, (0.02 / Math.max(thickness, 1e-4)) ** 2))\n}\n","import { chainDelayOf, drawChainDelay, FIRE_PRIME_C, shotDamageFractionOf } from './chain'\nimport type { ChainTrigger } from './chain'\nimport { planFragments } from './fracture'\nimport { JOINT_DEFAULTS, inferJoints, MATERIALS, partCapacity, partMass, partThickness, partVolume, fragility } from './materials'\nimport { aabbOf, createRng, distanceToShape, quat, quatFromEuler, rayShape, rotateVec, shapeArea, shapeBound, shapeDims, surfacePoint, toLocal, toWorld, vec } from './math'\nimport type { DestructibleImpl } from './contracts'\nimport type {\n AddOptions, Crack, DamageType, DebrisKind, DestructibleSpec, Dent, DestructionEvent, DestructionEventType, DestructionOptions, DestructionSnapshot,\n DestructionWorld, Fragment, Hit, HitResult, Hole, JointSpec, JointState, MaterialDef, MaterialId, ObjectRuntime, ObjectState, PartRuntime, PartSpec,\n PartState, Quat, RayHit, ToolKind, Vec3,\n} from './types'\n\n/** Hit points removed per joule (before the material's `damageScale`). */\nconst DMG_PER_J = 0.02\n/** Fraction of a blast's energy that reaches a part at distance 0. */\nconst BLAST_COUPLING = 0.1\nconst FLAMMABLE_FLUIDS = ['gas', 'fuel']\n\n/** A cut only goes through a slice of the part, so a part longer than `CUT_SLICE` costs no more to cut than a 1 m piece of the same section. */\nconst CUT_SLICE = 1\nconst cutFactorOf = (shape: PartSpec['shape']): number => clamp(Math.max(...shapeDims(shape)) / CUT_SLICE, 1, 50)\n\ninterface PartRT {\n spec: PartSpec\n mat: MaterialDef\n st: PartState\n q: Quat\n /** Current object-local position (the spec's, unless moved by `setPartPose`). */\n pos: Vec3\n mass0: number\n fuel0: number\n area: number\n thickness: number\n /** Slash damage multiplier: longest dimension / 1 m (1..50). */\n cutFactor: number\n dims: Vec3\n bound: number\n rt: PartRuntime\n /** Structural role other than `none`. */\n structural: boolean\n anchored: boolean\n cap: number\n}\n\ninterface JointRT {\n spec: Required<JointSpec>\n st: JointState\n ia: number\n ib: number\n bears: boolean | 'a_on_b' | 'b_on_a'\n /** Object-local position. */\n pos: Vec3\n step: number\n}\n\ninterface ObjRT {\n id: string\n spec: DestructibleSpec\n st: ObjectState\n parts: PartRT[]\n byId: Map<string, PartRT>\n idx: Map<string, number>\n joints: JointRT[]\n jointMap: Map<string, JointRT>\n byPart: Map<string, JointRT[]>\n /** Local bounding sphere. */\n bc: Vec3\n br: number\n active: boolean\n tmp: number[]\n hasStructure: boolean\n dirty: boolean\n maxStress: number\n collapsed: Set<string>\n /** Structural parts with no support path to an anchored part (they fall). */\n unsup: boolean[]\n anyAnchor: boolean\n /** Creation order of the object in this world: breaks ties between parts that go off at the same time. */\n order: number\n}\n\ninterface Carry {\n parts: Map<string, { st: PartState; rt: PartRuntime }>\n joints: Map<string, { st: JointState; step: number }>\n collapsed?: string[]\n}\n\ninterface Loc {\n p: PartRT\n point: Vec3\n normal: Vec3\n dir: Vec3\n pathLen: number\n}\n\ninterface ImplRT {\n impl: DestructibleImpl\n position: Vec3\n rotation: Quat\n /** Part pose overrides set by `setPartPose` (object-local). */\n poses: Map<string, { pos: Vec3; q: Quat }>\n}\n\ninterface Blast {\n point: Vec3\n energy: number\n radius: number\n source?: string\n}\n\nconst clamp = (v: number, a: number, b: number) => Math.max(a, Math.min(b, v))\nconst isLost = (p: PartRT) => p.st.status === 'broken' || p.st.status === 'melted' || p.st.status === 'ashes'\nconst clone = <T>(v: T): T => structuredClone(v)\n/**\n * Fraction of a structural part's hit points lost per second while its stress is above 1. A floor of 0.12 makes even a barely overloaded part\n * give way in about 8 s (a rate proportional to the excess alone never finishes: stress 1.02 would take two minutes), then it grows with the\n * excess and tops out at 0.6: stress 1.1 fails in ~4.5 s, 1.5 in ~1.9 s and anything above 1.7 in ~1.7 s. Deterministic: depends only on stress.\n */\nconst overloadRate = (stress: number) => Math.min(0.6, 0.12 + 0.8 * (stress - 1))\nconst updateBound = (o: ObjRT) => {\n if (!o.parts.length) return\n const c: Vec3 = [0, 0, 0]\n for (const p of o.parts) for (let k = 0; k < 3; k++) c[k] += p.pos[k] / o.parts.length\n let r = 0\n for (const p of o.parts) r = Math.max(r, vec.distance(c, p.pos) + p.bound)\n o.bc = c\n o.br = r\n}\nconst AXIS = { x: 0, y: 1, z: 2 } as const\n\nexport const createDestruction = (options: DestructionOptions = {}): DestructionWorld => {\n const materials = new Map<MaterialId, MaterialDef>()\n for (const m of MATERIALS) materials.set(m.id, m)\n for (const m of options.materials ?? []) materials.set(m.id, m)\n const ambient = options.ambientC ?? 20\n const maxFragments = options.maxFragments ?? 24\n const maxDents = options.maxDents ?? 24\n const maxHoles = options.maxHoles ?? 32\n const fireSpread = options.fireSpread ?? 0.6\n const rng = createRng(options.seed ?? 1)\n const rnd = () => rng.next()\n\n let time = 0\n let counter = 0\n const objects = new Map<string, ObjRT>()\n const listeners = new Map<string, Set<(e: DestructionEvent) => void>>()\n let cur: DestructionEvent[] = []\n const blasts: Blast[] = []\n /** Explosive parts that were primed and wait for their delay. */\n const primed = new Map<PartRT, ObjRT>()\n let orderCounter = 0\n const impls = new Map<string, ImplRT>()\n const taken = (id: string) => objects.has(id) || impls.has(id)\n\n // -------------------------------------------------------------------------------------------------------------------------------------------\n // Events\n // -------------------------------------------------------------------------------------------------------------------------------------------\n\n const emit = (e: DestructionEvent) => {\n cur.push(e)\n options.onEvent?.(e)\n const all = listeners.get('*')\n if (all) for (const fn of [...all]) fn(e)\n const typed = listeners.get(e.type)\n if (typed) for (const fn of [...typed]) fn(e)\n }\n const base = (o: ObjRT, source?: string): { object: string; time: number; source?: string } =>\n source === undefined ? { object: o.id, time } : { object: o.id, time, source }\n\n const pendingCollapse = new Map<ObjRT, string | undefined>()\n let runDepth = 0\n\n /** Blasts queued by parts that exploded; each can prime or set off more parts. It ends: a part goes off once and is primed once, so the queue only grows by parts not yet lost. */\n const drainBlasts = () => {\n for (let blast = blasts.shift(); blast; blast = blasts.shift()) blastWorld(blast.point, blast.energy, blast.radius, blast.source)\n }\n\n const run = <T>(fn: () => T): { value: T; events: DestructionEvent[] } => {\n const prev = cur\n const mine: DestructionEvent[] = []\n cur = mine\n runDepth++\n try {\n const value = fn()\n drainBlasts()\n if (runDepth === 1) flushCollapse()\n return { value, events: mine }\n } finally {\n runDepth--\n cur = prev\n }\n }\n\n // -------------------------------------------------------------------------------------------------------------------------------------------\n // Building objects\n // -------------------------------------------------------------------------------------------------------------------------------------------\n\n const nextId = () => {\n let id: string\n do id = `obj_${++counter}`\n while (taken(id))\n return id\n }\n\n const freshRuntime = (): PartRuntime => ({ meltEmitted: 0, dripT: 0, prevTemp: ambient, shockT: 0, jet: false })\n\n const resolveSpec = (spec: DestructibleSpec): DestructibleSpec => {\n const out = clone(spec)\n const joints = out.joints ?? inferJoints(out.parts, 0.01, materials)\n const structural = (id: string) => {\n const role = out.parts.find((p) => p.id === id)?.structure?.role\n return role !== undefined && role !== 'none'\n }\n out.joints = joints.map((j) => {\n const def = JOINT_DEFAULTS[j.kind]\n return {\n id: j.id ?? `${j.a}~${j.b}`, a: j.a, b: j.b, kind: j.kind, strength: j.strength ?? def.strength, tools: j.tools ?? [...def.tools], seconds: j.seconds ?? def.seconds,\n bears: j.bears ?? (structural(j.a) && structural(j.b)),\n }\n })\n return out\n }\n\n const build = (spec: DestructibleSpec, id: string, pos: Vec3, rot: Quat, carry?: Carry): ObjRT => {\n const st: ObjectState = { id, position: [pos[0], pos[1], pos[2]], rotation: [rot[0], rot[1], rot[2], rot[3]], parts: [], joints: [], groups: [], destroyed: false }\n if (spec.name !== undefined) st.name = spec.name\n const o: ObjRT = {\n id, spec, st, parts: [], byId: new Map(), idx: new Map(), joints: [], jointMap: new Map(), byPart: new Map(), bc: [0, 0, 0], br: 0, active: false, tmp: [], hasStructure: false, dirty: false, maxStress: 0, collapsed: new Set(carry?.collapsed ?? []), unsup: [], anyAnchor: false, order: ++orderCounter,\n }\n for (const ps of spec.parts) {\n if (o.byId.has(ps.id)) throw new Error(`duplicate part id \"${ps.id}\"`)\n const mat = materials.get(ps.material)\n if (!mat) throw new Error(`unknown material \"${ps.material}\" on part \"${ps.id}\"`)\n const mass0 = partMass(ps, mat)\n const maxHp = Math.max(1, ps.hp ?? mass0 * mat.hpPerKg)\n const fuel0 = mat.thermal.ignitionC !== undefined ? mass0 * (mat.thermal.fuelFraction ?? 0) : 0\n const c = carry?.parts.get(ps.id)\n const pst: PartState = c?.st ?? {\n id: ps.id, material: ps.material, status: 'intact', hp: maxHp, maxHp, mass: mass0, temperature: ambient, burning: false, fuel: fuel0, char: 0, melt: 0,\n dents: [], holes: [], cracks: [], group: 0,\n }\n if (!c && ps.contains) pst.liters = ps.contains.liters\n const p: PartRT = {\n spec: ps, mat, st: pst, q: quatFromEuler(...(ps.rotation ?? [0, 0, 0])), pos: [ps.position[0], ps.position[1], ps.position[2]], mass0, fuel0, area: shapeArea(ps.shape), thickness: partThickness(ps), cutFactor: cutFactorOf(ps.shape),\n dims: shapeDims(ps.shape), bound: shapeBound(ps.shape), rt: c?.rt ?? { ...freshRuntime(), prevTemp: pst.temperature },\n structural: !!ps.structure && ps.structure.role !== 'none', anchored: !!ps.anchored || ps.structure?.role === 'foundation', cap: ps.structure ? partCapacity(ps, mat) : 0,\n }\n if (p.structural) o.hasStructure = true\n if (p.rt.chainDue !== undefined && !isLost(p)) primed.set(p, o)\n o.idx.set(ps.id, o.parts.length)\n o.parts.push(p)\n o.byId.set(ps.id, p)\n o.st.parts.push(pst)\n o.byPart.set(ps.id, [])\n }\n for (const js of spec.joints ?? []) {\n const r = js as Required<JointSpec>\n const ia = o.idx.get(r.a), ib = o.idx.get(r.b)\n if (ia === undefined || ib === undefined) throw new Error(`joint \"${r.id}\" references an unknown part`)\n if (o.jointMap.has(r.id)) throw new Error(`duplicate joint id \"${r.id}\"`)\n const pa = o.parts[ia], pb = o.parts[ib]\n const A = aabbOf(pa.spec.shape, pa.spec.position, pa.q)\n const B = aabbOf(pb.spec.shape, pb.spec.position, pb.q)\n const pos: Vec3 = [0, 0, 0]\n for (let k = 0; k < 3; k++) pos[k] = (Math.max(A.min[k], B.min[k]) + Math.min(A.max[k], B.max[k])) / 2\n const bears: JointRT['bears'] = r.bears ?? (pa.structural && pb.structural)\n const c = carry?.joints.get(r.id)\n const jst: JointState = c?.st ?? { id: r.id, a: r.a, b: r.b, kind: r.kind, hp: r.strength, maxHp: r.strength, progress: 0, open: false }\n const j: JointRT = { spec: r, st: jst, ia, ib, bears, pos, step: c?.step ?? 0 }\n o.joints.push(j)\n o.jointMap.set(r.id, j)\n o.byPart.get(r.a)!.push(j)\n o.byPart.get(r.b)!.push(j)\n o.st.joints.push(jst)\n }\n // open joints whose parts are no longer both here (left behind by `splitGroup`) stay listed, inert, so callers still see them open\n for (const [jid, c] of carry?.joints ?? []) if (c.st.open && !o.jointMap.has(jid)) o.st.joints.push(c.st)\n updateBound(o)\n o.anyAnchor = o.parts.some((p) => p.anchored)\n o.unsup = o.parts.map(() => false)\n solveStructure(o)\n recomputeGroups(o)\n o.st.destroyed = o.parts.length > 0 && o.parts.every(isLost)\n o.active = o.maxStress > 0.8 || o.parts.some((p) => p.st.burning || p.st.temperature > ambient + 0.5 || (p.st.liters ?? 0) > 0)\n return o\n }\n\n const recomputeGroups = (o: ObjRT) => {\n const n = o.parts.length\n const parent = Array.from({ length: n }, (_, i) => i)\n const find = (i: number): number => {\n while (parent[i] !== i) {\n parent[i] = parent[parent[i]]\n i = parent[i]\n }\n return i\n }\n const union = (a: number, b: number) => {\n parent[find(a)] = find(b)\n }\n const live = (j: JointRT) => !j.st.open && !isLost(o.parts[j.ia]) && !isLost(o.parts[j.ib])\n if (o.unsup.some(Boolean)) {\n // parts that lost every support fall: cut them from the supported pieces; loose non-structural parts follow the part they hang on\n for (const j of o.joints) if (live(j) && !o.unsup[j.ia] && !o.unsup[j.ib]) union(j.ia, j.ib)\n const supported = new Set<number>()\n for (let i = 0; i < n; i++) if (!isLost(o.parts[i]) && !o.unsup[i] && (o.parts[i].structural || o.parts[i].anchored)) supported.add(find(i))\n for (const j of o.joints) {\n if (!live(j) || !(o.unsup[j.ia] || o.unsup[j.ib])) continue\n if (o.unsup[j.ia] && o.unsup[j.ib]) union(j.ia, j.ib)\n else {\n const u = o.unsup[j.ia] ? j.ia : j.ib\n const other = u === j.ia ? j.ib : j.ia\n if (!o.parts[other].structural && !supported.has(find(other))) union(u, other)\n }\n }\n } else for (const j of o.joints) if (live(j)) union(j.ia, j.ib)\n const comps = new Map<number, number[]>()\n for (let i = 0; i < n; i++) {\n if (isLost(o.parts[i])) continue\n const r = find(i)\n const l = comps.get(r)\n if (l) l.push(i)\n else comps.set(r, [i])\n }\n const list = [...comps.values()].map((ix) => ({\n ix,\n anchored: ix.some((i) => o.parts[i].anchored),\n mass: ix.reduce((s, i) => s + o.parts[i].st.mass, 0),\n }))\n list.sort((a, b) => (a.anchored === b.anchored ? (b.mass - a.mass) || (a.ix[0] - b.ix[0]) : a.anchored ? -1 : 1))\n o.st.groups = list.map((g) => g.ix.map((i) => o.parts[i].spec.id))\n for (const p of o.parts) if (isLost(p)) p.st.group = -1\n list.forEach((g, gi) => g.ix.forEach((i) => (o.parts[i].st.group = gi)))\n }\n\n /** Which part `r` rests on over joint `j` (index), or -1 when the joint does not let `r` rest on its other end. */\n const restsOn = (j: JointRT, r: number): number => {\n const b = j.bears\n if (!b) return -1\n if (b === true) return j.ia === r ? j.ib : j.ia\n if (b === 'a_on_b') return j.ia === r ? j.ib : -1\n return j.ib === r ? j.ia : -1\n }\n\n /** Distribute own mass plus carried load down the bearing graph to the anchored parts; mark parts without any support path. */\n const solveStructure = (o: ObjRT) => {\n o.maxStress = 0\n if (!o.hasStructure) return\n const parts = o.parts\n const n = parts.length\n const dist = new Array<number>(n).fill(-1)\n const queue: number[] = []\n const ok = (i: number) => parts[i].structural && !isLost(parts[i])\n const isPartition = (i: number) => parts[i].spec.structure?.role === 'partition'\n for (let i = 0; i < n; i++) if (ok(i) && parts[i].anchored) {\n dist[i] = 0\n queue.push(i)\n }\n for (let h = 0; h < queue.length; h++) {\n const s = queue[h]\n if (isPartition(s)) continue\n for (const j of o.byPart.get(parts[s].spec.id)!) {\n if (j.st.open || !j.bears) continue\n const r = j.ia === s ? j.ib : j.ia\n if (dist[r] >= 0 || !ok(r) || restsOn(j, r) !== s) continue\n dist[r] = dist[s] + 1\n queue.push(r)\n }\n }\n const incoming = new Array<number>(n).fill(0)\n for (let h = queue.length - 1; h >= 0; h--) {\n const i = queue[h]\n const total = parts[i].st.mass + incoming[i]\n parts[i].st.load = total\n const lower: number[] = []\n for (const j of o.byPart.get(parts[i].spec.id)!) {\n if (j.st.open || !j.bears) continue\n const k = restsOn(j, i)\n if (k >= 0 && dist[k] >= 0 && dist[k] < dist[i] && !isPartition(k)) lower.push(k)\n }\n for (const k of lower) incoming[k] += total / lower.length\n }\n for (let i = 0; i < n; i++) {\n const p = parts[i]\n o.unsup[i] = false\n if (!p.structural) continue\n if (dist[i] < 0 || isLost(p)) {\n p.st.load = p.st.mass\n p.st.stress = 0\n o.unsup[i] = o.anyAnchor && !isLost(p)\n continue\n }\n p.st.stress = p.st.load! / p.cap\n if (p.st.stress > o.maxStress) o.maxStress = p.st.stress\n }\n if (o.maxStress > 0.8) o.active = true\n }\n\n /**\n * Ask for a collapse report. It is not computed right away: a blow can break several parts in one go (a column and the roof's supports, or a\n * column whose joint opens just before the column itself fractures), so the groups are examined once, after every loss of the operation.\n */\n const collapseCheck = (o: ObjRT, src?: string) => {\n if (!o.hasStructure || !o.parts.some((p) => p.anchored)) return\n if (!pendingCollapse.has(o)) pendingCollapse.set(o, src)\n }\n\n /** Report the groups of remaining structural parts that lost every anchor (each part is reported once). */\n const flushCollapse = () => {\n for (const [o, src] of [...pendingCollapse]) {\n pendingCollapse.delete(o)\n if (!objects.has(o.id) || objects.get(o.id) !== o) continue\n for (const g of o.st.groups) {\n const ps = g.map((id) => o.byId.get(id)!).filter((p) => !isLost(p) && !o.collapsed.has(p.spec.id))\n if (ps.length === 0 || g.some((id) => o.byId.get(id)!.anchored) || !ps.some((p) => p.structural)) continue\n for (const p of ps) {\n o.collapsed.add(p.spec.id)\n p.st.status = 'collapsed'\n }\n emit({ ...base(o, src), type: 'collapse', parts: ps.map((p) => p.spec.id), mass: ps.reduce((sum, p) => sum + p.st.mass, 0) })\n }\n }\n }\n\n const getObj = (id: string): ObjRT => {\n const o = objects.get(id)\n if (!o) throw new Error(`unknown object \"${id}\"`)\n return o\n }\n const getPart = (o: ObjRT, id: string): PartRT => {\n const p = o.byId.get(id)\n if (!p) throw new Error(`unknown part \"${id}\" in object \"${o.id}\"`)\n return p\n }\n\n // -------------------------------------------------------------------------------------------------------------------------------------------\n // Geometry in world space\n // -------------------------------------------------------------------------------------------------------------------------------------------\n\n const partXf = (o: ObjRT, p: PartRT): { c: Vec3; q: Quat } => ({ c: toWorld(o.st.position, o.st.rotation, p.pos), q: quat.mul(o.st.rotation, p.q) })\n const objLocal = (o: ObjRT, w: Vec3) => toLocal(o.st.position, o.st.rotation, w)\n const objLocalDir = (o: ObjRT, w: Vec3) => rotateVec(quat.conjugate(o.st.rotation), w)\n const jointWorld = (o: ObjRT, j: JointRT) => toWorld(o.st.position, o.st.rotation, j.pos)\n const distToPart = (o: ObjRT, p: PartRT, w: Vec3) => {\n const { c, q } = partXf(o, p)\n return distanceToShape(p.spec.shape, toLocal(c, q, w))\n }\n const partWorldAabb = (o: ObjRT, p: PartRT) => {\n const { c, q } = partXf(o, p)\n return aabbOf(p.spec.shape, c, q)\n }\n\n interface PartRay { t0: number; t1: number; n: Vec3 }\n const rayPartRaw = (pos: Vec3, rot: Quat, spec: PartSpec, pq: Quat, origin: Vec3, d: Vec3, lp: Vec3 = spec.position): PartRay | null => {\n const c = toWorld(pos, rot, lp)\n const q = quat.mul(rot, pq)\n const qi = quat.conjugate(q)\n const r = rayShape(spec.shape, rotateVec(qi, vec.sub(origin, c)), rotateVec(qi, d))\n return r ? { t0: r.t0, t1: r.t1, n: rotateVec(q, r.n0) } : null\n }\n const rayPart = (o: ObjRT, p: PartRT, origin: Vec3, d: Vec3): PartRay | null => rayPartRaw(o.st.position, o.st.rotation, p.spec, p.q, origin, d, p.pos)\n\n const raySphereMiss = (origin: Vec3, d: Vec3, c: Vec3, r: number, maxDist: number): boolean => {\n const oc = vec.sub(origin, c)\n const b = vec.dot(oc, d)\n const cc = vec.dot(oc, oc) - r * r\n if (cc > 0 && b > 0) return true\n const disc = b * b - cc\n if (disc < 0) return true\n return -b - Math.sqrt(disc) > maxDist\n }\n\n const raycastInternal = (origin: Vec3, dir: Vec3, maxDist: number, ignore?: Set<string>): (RayHit & { pathLen: number }) | null => {\n const d = vec.normalize(dir)\n let best: (RayHit & { pathLen: number }) | null = null\n for (const o of objects.values()) {\n if (raySphereMiss(origin, d, toWorld(o.st.position, o.st.rotation, o.bc), o.br, best ? Math.min(maxDist, best.distance) : maxDist)) continue\n for (const p of o.parts) {\n if (isLost(p) || ignore?.has(`${o.id}\\0${p.spec.id}`)) continue\n const r = rayPart(o, p, origin, d)\n if (!r || r.t0 < 0 || r.t0 > maxDist) continue\n if (!best || r.t0 < best.distance) best = { object: o.id, part: p.spec.id, point: vec.madd(origin, d, r.t0), normal: r.n, distance: r.t0, pathLen: r.t1 - r.t0 }\n }\n }\n for (const im of impls.values()) {\n for (const ps of im.impl.describe().parts) {\n if (ignore?.has(`${im.impl.id}\\0${ps.id}`)) continue\n const ov = im.poses.get(ps.id)\n const r = rayPartRaw(im.position, im.rotation, ps, ov?.q ?? quatFromEuler(...(ps.rotation ?? [0, 0, 0])), origin, d, ov?.pos)\n if (!r || r.t0 < 0 || r.t0 > maxDist) continue\n if (!best || r.t0 < best.distance) best = { object: im.impl.id, part: ps.id, point: vec.madd(origin, d, r.t0), normal: r.n, distance: r.t0, pathLen: r.t1 - r.t0 }\n }\n }\n return best\n }\n\n // impl hosting\n const implHit = (im: ImplRT, hit: Hit): HitResult => {\n let h = hit\n if (hit.space === 'local') {\n h = { ...hit, space: 'world', point: toWorld(im.position, im.rotation, hit.point) }\n if (hit.direction) h.direction = rotateVec(im.rotation, hit.direction)\n }\n const start = cur.length\n for (const e of im.impl.hit(h) ?? []) emit(e.time === undefined ? { ...e, time } : e)\n const mine = cur.slice(start)\n const res: HitResult = { object: im.impl.id, damage: 0, penetrated: false, exitEnergy: 0, events: mine, point: hit.point }\n for (const e of mine) {\n if (e.type === 'damage') res.damage += e.amount\n if (e.type === 'hole' && e.hole.through) res.penetrated = true\n if (res.part === undefined && 'part' in e) res.part = (e as { part: string }).part\n }\n return res\n }\n\n const implJointPos = (im: ImplRT, a: string, b: string): Vec3 | null => {\n const parts = im.impl.describe().parts\n const pa = parts.find((p) => p.id === a), pb = parts.find((p) => p.id === b)\n if (!pa || !pb) return null\n const A = aabbOf(pa.shape, pa.position, quatFromEuler(...(pa.rotation ?? [0, 0, 0])))\n const B = aabbOf(pb.shape, pb.position, quatFromEuler(...(pb.rotation ?? [0, 0, 0])))\n const pos: Vec3 = [0, 0, 0]\n for (let k = 0; k < 3; k++) pos[k] = (Math.max(A.min[k], B.min[k]) + Math.min(A.max[k], B.max[k])) / 2\n return toWorld(im.position, im.rotation, pos)\n }\n\n /** Find the part a hit lands on: a short ray along `dir`, else the nearest part within `tol`. */\n const locate = (o: ObjRT, point: Vec3, dir: Vec3 | undefined, tol: number, only?: PartRT): Loc | null => {\n const cands = only ? [only] : o.parts\n if (dir && vec.length(dir) > 1e-9) {\n const d = vec.normalize(dir)\n const origin = vec.madd(point, d, -0.05)\n let best: Loc | null = null\n let bestT = Infinity\n let inside: Loc | null = null\n for (const p of cands) {\n if (isLost(p)) continue\n const r = rayPart(o, p, origin, d)\n if (!r) continue\n if (r.t0 >= 0 && r.t0 <= 0.15) {\n if (r.t0 < bestT) {\n bestT = r.t0\n best = { p, point: vec.madd(origin, d, r.t0), normal: r.n, dir: d, pathLen: Math.max(r.t1 - r.t0, 1e-4) }\n }\n } else if (r.t0 < 0 && r.t1 > 0 && !inside) {\n inside = { p, point, normal: vec.scale(d, -1), dir: d, pathLen: Math.max(r.t1 - 0.05, p.thickness) }\n }\n }\n if (best) return best\n if (inside) return inside\n }\n let np: PartRT | null = null\n let nd = Infinity\n for (const p of cands) {\n if (isLost(p)) continue\n const dd = distToPart(o, p, point)\n if (dd < nd) {\n nd = dd\n np = p\n }\n }\n if (!np || nd > tol) return null\n const { c, q } = partXf(o, np)\n const sp = surfacePoint(np.spec.shape, toLocal(c, q, point))\n const normal = rotateVec(q, sp.normal)\n return { p: np, point: toWorld(c, q, sp.point), normal, dir: dir && vec.length(dir) > 1e-9 ? vec.normalize(dir) : vec.scale(normal, -1), pathLen: np.thickness }\n }\n\n // -------------------------------------------------------------------------------------------------------------------------------------------\n // Marks on parts\n // -------------------------------------------------------------------------------------------------------------------------------------------\n\n const markDamaged = (p: PartRT) => {\n if (p.st.status === 'intact') p.st.status = 'damaged'\n }\n\n const addDent = (o: ObjRT, p: PartRT, wp: Vec3, wn: Vec3, radius: number, depth: number, src?: string) => {\n const dent: Dent = { point: objLocal(o, wp), normal: objLocalDir(o, wn), radius, depth }\n const arr = p.st.dents\n let ni = -1, nd = Infinity\n for (let i = 0; i < arr.length; i++) {\n const dd = vec.distance(arr[i].point, dent.point)\n if (dd < nd) {\n nd = dd\n ni = i\n }\n }\n if (ni >= 0 && (arr.length >= maxDents || nd < arr[ni].radius * 0.6)) {\n arr[ni].depth = Math.min(0.1, Math.max(arr[ni].depth, depth) + depth * 0.3)\n arr[ni].radius = Math.max(arr[ni].radius, radius)\n } else arr.push(dent)\n markDamaged(p)\n emit({ ...base(o, src), type: 'dent', part: p.spec.id, dent, worldPoint: wp })\n }\n\n const addHole = (o: ObjRT, p: PartRT, wp: Vec3, wn: Vec3, diameter: number, through: boolean, exit?: Vec3, src?: string) => {\n const hole: Hole = { point: objLocal(o, wp), normal: objLocalDir(o, wn), diameter, through }\n p.st.holes.push(hole)\n if (p.st.holes.length > maxHoles) p.st.holes.shift()\n markDamaged(p)\n emit({ ...base(o, src), type: 'hole', part: p.spec.id, hole, worldPoint: wp, ...(exit ? { exitPoint: exit } : {}) })\n return hole\n }\n\n const addCrack = (o: ObjRT, p: PartRT, wp: Vec3, wn: Vec3, length: number, src?: string) => {\n const crack: Crack = { point: objLocal(o, wp), normal: objLocalDir(o, wn), length: Math.min(length, Math.max(p.bound, 0.05)), rays: 5 + Math.floor(rnd() * 5) }\n p.st.cracks.push(crack)\n if (p.st.cracks.length > 16) p.st.cracks.shift()\n markDamaged(p)\n emit({ ...base(o, src), type: 'crack', part: p.spec.id, crack, worldPoint: wp })\n }\n\n const debris = (o: ObjRT, p: PartRT, kind: DebrisKind, point: Vec3, normal: Vec3, count: number, speed: number, src?: string) =>\n emit({ ...base(o, src), type: 'debris', part: p.spec.id, kind, point, normal, count, speed })\n\n const holeRate = (hole: Hole, pressurized: boolean) => (pressurized ? 0.5 : 0.1) * (hole.diameter / 0.009)\n\n // -------------------------------------------------------------------------------------------------------------------------------------------\n // Failure, joints\n // -------------------------------------------------------------------------------------------------------------------------------------------\n\n const detachEvents = (o: ObjRT, opened: JointRT[], by: 'break' | 'tool' | 'part_lost', src?: string) => {\n for (const j of opened) emit({ ...base(o, src), type: 'detach', joint: j.spec.id, parts: [j.spec.a, j.spec.b], by, groups: o.st.groups.map((g) => [...g]) })\n }\n\n const checkDestroyed = (o: ObjRT, src?: string) => {\n if (!o.st.destroyed && o.parts.length > 0 && o.parts.every(isLost)) {\n o.st.destroyed = true\n emit({ ...base(o, src), type: 'destroyed' })\n }\n }\n\n const partLost = (o: ObjRT, p: PartRT, src?: string) => {\n const opened: JointRT[] = []\n for (const j of o.byPart.get(p.spec.id) ?? []) {\n if (j.st.open) continue\n j.st.open = true\n j.st.openedBy = 'part_lost'\n opened.push(j)\n }\n solveStructure(o)\n recomputeGroups(o)\n detachEvents(o, opened, 'part_lost', src)\n collapseCheck(o, src)\n checkDestroyed(o, src)\n }\n\n /**\n * Distance from a world point to the joint's contact region: the overlap box of the two parts' bounds (or, when they only nearly touch, the box\n * of the gap between them). Measuring to the joint centre would make a thick part untouchable: a blow on a trunk's surface is half the trunk's\n * width away from a joint that sits on its axis.\n */\n const jointReach = (o: ObjRT, j: JointRT, point: Vec3): number => {\n const A = partWorldAabb(o, o.parts[j.ia]), B = partWorldAabb(o, o.parts[j.ib])\n let d2 = 0\n for (let k = 0; k < 3; k++) {\n const lo = Math.min(Math.max(A.min[k], B.min[k]), Math.min(A.max[k], B.max[k]))\n const hi = Math.max(Math.max(A.min[k], B.min[k]), Math.min(A.max[k], B.max[k]))\n const d = point[k] < lo ? lo - point[k] : point[k] > hi ? point[k] - hi : 0\n d2 += d * d\n }\n return Math.sqrt(d2)\n }\n /** How far from the contact region a blow still counts: half the smallest dimension of the smaller part, at least 6 cm. */\n const jointReachLimit = (o: ObjRT, j: JointRT): number => {\n const pa = o.parts[j.ia], pb = o.parts[j.ib]\n const small = pa.dims[0] * pa.dims[1] * pa.dims[2] <= pb.dims[0] * pb.dims[1] * pb.dims[2] ? pa : pb\n return Math.max(0.06, 0.5 * Math.min(small.dims[0], small.dims[1], small.dims[2]))\n }\n\n const wearJoints = (o: ObjRT, p: PartRT, amount: number, near?: Vec3, radius = 0.06, src?: string, split = false) => {\n if (amount <= 0) return\n const mine = (o.byPart.get(p.spec.id) ?? []).filter((j) => !j.st.open)\n if (split && mine.length) amount /= mine.length\n for (const j of mine) {\n if (j.st.open) continue\n if (near && jointReach(o, j, near) > Math.max(radius, jointReachLimit(o, j))) continue\n j.st.hp -= amount\n if (j.st.hp <= 0) {\n j.st.hp = 0\n j.st.open = true\n j.st.openedBy = 'break'\n solveStructure(o)\n recomputeGroups(o)\n detachEvents(o, [j], 'break', src)\n collapseCheck(o, src)\n }\n }\n }\n\n /** Part hit points reached 0 (or it was forced to explode): fragments, debris, joints open. */\n const failPart = (o: ObjRT, p: PartRT, origin: Vec3 | null, dir: Vec3 | null, fragEnergy: number, cap: number, src?: string) => {\n if (isLost(p)) return\n const ex = p.spec.explodes\n const style = p.mat.fracture === 'none' ? 'snap' : p.mat.fracture\n const { c, q } = partXf(o, p)\n const hitW = origin ?? c\n const grain = p.spec.grain ? AXIS[p.spec.grain] : undefined\n const plan = planFragments({\n style, dims: p.dims, thickness: p.thickness, grain, hit: toLocal(c, q, hitW), fragmentSize: p.mat.fragmentSize ?? [0.04, 0.2], maxFragments, rnd,\n })\n const mass = p.st.mass\n const totW = plan.reduce((s, f) => s + f.weight, 0) || 1\n const energy = ex ? ex.energy * 0.2 : fragEnergy\n const speed = clamp(Math.sqrt((2 * energy) / Math.max(mass, 0.05)), 0.3, ex ? 40 : cap)\n const fragments: Fragment[] = plan.map((f, i) => {\n const cw = toWorld(c, q, f.center)\n const out = vec.normalize(vec.sub(cw, hitW), [0, 1, 0])\n const dv = dir ? vec.normalize(vec.madd(out, dir, 0.6)) : out\n const k = speed * (0.6 + 0.8 * rnd())\n return {\n id: `${p.spec.id}#${i}`, part: p.spec.id, material: p.spec.material, shape: f.shape, center: cw, size: f.size, rotation: quat.mul(q, f.q),\n velocity: [dv[0] * k, dv[1] * k + 0.5 * rnd(), dv[2] * k],\n angularVelocity: [(rnd() - 0.5) * 12, (rnd() - 0.5) * 12, (rnd() - 0.5) * 12],\n mass: (mass * f.weight) / totW,\n }\n })\n p.st.status = 'broken'\n p.st.hp = 0\n p.st.mass = 0\n p.st.burning = false\n p.rt.jet = false\n emit({ ...base(o, src), type: 'fracture', part: p.spec.id, style, fragments, point: hitW })\n debris(o, p, p.mat.breakDebris, c, [0, 1, 0], clamp(Math.round(Math.cbrt(partVolume(p.spec.shape)) * 30), 3, 20), 2, src)\n if (ex) {\n emit({ ...base(o, src), type: 'explode', part: p.spec.id, point: c, energy: ex.energy, radius: ex.radius })\n blasts.push({ point: c, energy: ex.energy, radius: ex.radius, source: src })\n }\n partLost(o, p, src)\n }\n\n /**\n * Sets an explosive part off from outside (`explodes.chain`): it will explode after the delay of `trigger`, once. The delay is one draw\n * from the world seed, so the same inputs give the same order of blasts.\n */\n const prime = (o: ObjRT, p: PartRT, trigger: ChainTrigger, src?: string) => {\n const spec = chainDelayOf(p.spec, trigger)\n if (spec === undefined || isLost(p) || p.rt.chainDue !== undefined) return\n const delay = drawChainDelay(spec, rnd)\n p.rt.chainDue = time + delay\n primed.set(p, o)\n o.active = true\n emit({ ...base(o, src), type: 'primed', part: p.spec.id, point: partXf(o, p).c, trigger, delay })\n }\n\n const CHAIN_TRIGGER_OF: Partial<Record<DamageType, ChainTrigger>> = { ballistic: 'shot', explosive: 'explosion' }\n\n /** Remove hit points. Returns the amount actually removed. */\n const applyDamage = (o: ObjRT, p: PartRT, amount: number, type: DamageType, origin: Vec3 | null, dir: Vec3 | null, fragEnergy: number, cap: number, src?: string): number => {\n if (isLost(p) || amount <= 0) return 0\n const prev = p.st.hp\n p.st.hp -= amount\n markDamaged(p)\n const trigger = CHAIN_TRIGGER_OF[type]\n const chained = trigger !== undefined && chainDelayOf(p.spec, trigger) !== undefined\n // a bullet or a blast sets a chained part off after its delay instead of tearing it apart on the spot\n if (chained && p.st.hp <= 0 && p.rt.chainDue === undefined) p.st.hp = Math.min(prev, 1)\n if (p.st.hp <= 0 && p.mat.fracture === 'none' && !p.spec.explodes) p.st.hp = Math.min(prev, 1)\n const done = prev - p.st.hp\n if (done > 0) emit({ ...base(o, src), type: 'damage', part: p.spec.id, amount: done, hp: Math.max(0, p.st.hp), damageType: type })\n if (p.st.hp <= 0) failPart(o, p, origin, dir, fragEnergy, cap, src)\n else if (chained && (trigger === 'explosion' || 1 - p.st.hp / p.st.maxHp >= shotDamageFractionOf(p.spec))) prime(o, p, trigger, src)\n return done\n }\n\n // -------------------------------------------------------------------------------------------------------------------------------------------\n // Hits\n // -------------------------------------------------------------------------------------------------------------------------------------------\n\n interface BallisticOut { penetrated: boolean; exitEnergy: number; exitPoint?: Vec3; damage: number }\n\n const ballistic = (o: ObjRT, p: PartRT, entry: Vec3, n: Vec3, d: Vec3, E: number, caliber: number, pathLen: number, src?: string): BallisticOut => {\n const mat = p.mat\n const sin = Math.min(1, Math.abs(vec.dot(d, n)))\n const angle = (Math.asin(sin) * 180) / Math.PI\n const effM = p.thickness / Math.max(sin, 0.3)\n const thr = mat.penetration * effM * 1000\n const penetrated = E > thr\n // a thin brittle shell that shatters (a pane, a bottle) is lost to one bullet that a thick block of the same glass shrugs off: the same\n // fragility factor that melee uses for blunt hits (1 for ductile materials and for shells 2 cm or thicker, up to x118 for 3 mm glass)\n const scale = (mat.damageScale?.ballistic ?? 1) * (mat.fracture === 'shatter' ? fragility(mat.ductility, p.thickness) : 1)\n const id = p.spec.id\n if (!penetrated && mat.ricochetDeg && angle < mat.ricochetDeg) {\n const r = vec.sub(d, vec.scale(n, 2 * vec.dot(d, n)))\n emit({ ...base(o, src), type: 'ricochet', part: id, point: entry, direction: vec.normalize(r), energy: E * 0.4 })\n debris(o, p, mat.hitDebris, entry, n, 3, 3, src)\n const dmg = applyDamage(o, p, E * DMG_PER_J * scale * 0.2, 'ballistic', entry, d, E * 0.1, 12, src)\n return { penetrated: false, exitEnergy: 0, damage: dmg }\n }\n const brittle = mat.ductility < 0.3\n const ratio = E / Math.max(thr, 1e-6)\n const crackLen = clamp(0.03 + ratio * 0.2, 0.03, 0.8)\n let exitPoint: Vec3 | undefined\n if (penetrated) {\n exitPoint = vec.madd(entry, d, Math.min(pathLen, effM))\n const hole = addHole(o, p, entry, n, (caliber / 1000) * (mat.ductility >= 0.3 ? 1 : 1.5), true, exitPoint, src)\n if (brittle && mat.fracture === 'shatter') addCrack(o, p, entry, n, crackLen, src)\n if (p.spec.contains && (p.st.liters ?? 0) > 0) {\n emit({\n ...base(o, src), type: 'leak', part: id, fluid: p.spec.contains.fluid, point: entry, direction: n, rate: holeRate(hole, !!p.spec.contains.pressurized),\n pressurized: !!p.spec.contains.pressurized,\n })\n }\n } else if (mat.ductility >= 0.5) {\n addDent(o, p, entry, n, Math.max((caliber * 3) / 1000, 0.012), clamp(Math.min(Math.max(p.thickness * 3, 0.003), ratio * 0.01), 0.001, 0.05), src)\n } else if (brittle) {\n addCrack(o, p, entry, n, crackLen, src)\n if (mat.fracture !== 'shatter') addHole(o, p, entry, n, caliber / 1000, false, undefined, src)\n } else addHole(o, p, entry, n, caliber / 1000, false, undefined, src)\n debris(o, p, mat.hitDebris, entry, n, clamp(Math.round(E / 60) + 2, 2, 14), clamp(Math.sqrt(E) / 6, 1, 8), src)\n // a bullet only wears the joints of the part it hits, by the energy the part absorbed (a through-and-through shot transfers little)\n wearJoints(o, p, (penetrated ? thr : E) * 0.02, undefined, 0, src, true)\n const dmg = applyDamage(o, p, E * DMG_PER_J * scale, 'ballistic', entry, d, E * 0.1, 12, src)\n return { penetrated, exitEnergy: penetrated ? E - thr : 0, exitPoint, damage: dmg }\n }\n\n const melee = (o: ObjRT, p: PartRT, kind: 'blunt' | 'slash', E: number, point: Vec3, n: Vec3, d: Vec3, contactR: number, src?: string): number => {\n const mat = p.mat\n const thrMm = mat.penetration * p.thickness * 1000\n // brittle thin shells (a bottle, a mug, a pane) break from a knock that a thick block shrugs off\n const fragile = kind === 'blunt' ? fragility(mat.ductility, p.thickness) : 1\n const Ec = E * fragile\n if (E > 0) {\n if (kind === 'blunt') {\n if (mat.ductility >= 0.5) addDent(o, p, point, n, Math.max(contactR, 0.015), clamp(E * 0.0001, 0.001, 0.06), src)\n else if (mat.ductility < 0.3) {\n if (Ec >= 5) addCrack(o, p, point, n, clamp(0.04 + Math.min(Ec, E * 6) * 0.0008, 0.04, 0.6), src)\n } else if (E >= thrMm * 0.2) addDent(o, p, point, n, Math.max(contactR, 0.015), clamp(E * 0.00006, 0.001, 0.04), src)\n } else if (mat.fracture === 'tear') {\n addHole(o, p, point, n, clamp(0.02 + E * 0.0005, 0.02, 0.3), true, undefined, src)\n } else if (mat.ductility >= 0.5) {\n addDent(o, p, point, n, Math.max(contactR, 0.01), clamp(E * 0.00004, 0.0005, 0.02), src)\n }\n debris(o, p, mat.hitDebris, point, n, clamp(Math.round(E / 25) + 1, 1, 10), clamp(Math.sqrt(E) / 8, 1, 6), src)\n }\n if (kind === 'blunt') wearJoints(o, p, E * 0.3, undefined, 0, src)\n else wearJoints(o, p, E * 0.3, point, 0.06, src)\n const scale = mat.damageScale?.[kind] ?? 1\n return applyDamage(o, p, Ec * DMG_PER_J * scale * (kind === 'slash' ? p.cutFactor : 1), kind, point, d, E * 0.1, 12, src)\n }\n\n const blastObject = (o: ObjRT, point: Vec3, energy: number, radius: number, src?: string): HitResult => {\n const start = cur.length\n const res: HitResult = { object: o.id, damage: 0, penetrated: false, exitEnergy: 0, events: [] }\n for (const p of o.parts) {\n if (isLost(p)) continue\n const { c, q } = partXf(o, p)\n const lp = toLocal(c, q, point)\n const dist = distanceToShape(p.spec.shape, lp)\n if (dist >= radius) continue\n const f = (1 - dist / radius) ** 2\n const Ein = energy * f * BLAST_COUPLING\n const sp = surfacePoint(p.spec.shape, lp)\n const wp = toWorld(c, q, sp.point)\n const wn = rotateVec(q, sp.normal)\n if (!res.part) {\n res.part = p.spec.id\n res.point = wp\n }\n const mat = p.mat\n if (Ein > 20) {\n if (mat.ductility >= 0.5) addDent(o, p, wp, wn, clamp(0.05 + Math.sqrt(Ein) * 0.002, 0.05, 0.4), clamp(0.004 + Ein * 0.00002, 0.004, 0.08), src)\n else if (mat.ductility < 0.3 && Ein > 60) addCrack(o, p, wp, wn, clamp(0.05 + Ein * 0.0005, 0.05, 1), src)\n debris(o, p, mat.hitDebris, wp, wn, clamp(Math.round(Ein / 80) + 1, 1, 12), clamp(Math.sqrt(Ein) / 4, 2, 15), src)\n }\n const th = mat.thermal\n if (th.ignitionC !== undefined && p.st.fuel > 0) {\n p.st.temperature += 300 * f\n o.active = true\n if (f >= 0.25 && !p.st.burning) {\n p.st.temperature = Math.max(p.st.temperature, th.ignitionC + 1)\n checkIgnite(o, p, src)\n }\n }\n wearJoints(o, p, Ein * 0.5, undefined, 0, src)\n res.damage += applyDamage(o, p, Ein * DMG_PER_J * (mat.damageScale?.explosive ?? 1), 'explosive', point, null, Ein * 0.3, 40, src)\n }\n res.events = cur.slice(start)\n return res\n }\n\n const blastWorld = (point: Vec3, energy: number, radius: number, src?: string): HitResult[] => {\n const out: HitResult[] = []\n for (const o of [...objects.values()]) {\n if (vec.distance(toWorld(o.st.position, o.st.rotation, o.bc), point) - o.br >= radius) continue\n const r = blastObject(o, point, energy, radius, src)\n if (r.part) out.push(r)\n }\n for (const im of [...impls.values()]) {\n const inRange = im.impl.describe().parts.some((ps) => {\n const ov = im.poses.get(ps.id)\n const c = toWorld(im.position, im.rotation, ov?.pos ?? ps.position)\n const q = quat.mul(im.rotation, ov?.q ?? quatFromEuler(...(ps.rotation ?? [0, 0, 0])))\n return distanceToShape(ps.shape, toLocal(c, q, point)) < radius\n })\n if (inRange) out.push(implHit(im, { type: 'explosive', point, energy, radius, ...(src !== undefined ? { source: src } : {}) }))\n }\n return out\n }\n\n const heatObject = (o: ObjRT, point: Vec3, T: number, radius: number, duration: number, src?: string): HitResult => {\n const start = cur.length\n const res: HitResult = { object: o.id, damage: 0, penetrated: false, exitEnergy: 0, events: [] }\n for (const p of o.parts) {\n if (isLost(p)) continue\n const dist = distToPart(o, p, point)\n if (dist >= radius) continue\n const f = clamp(1 - dist / radius, 0.25, 1)\n if (!res.part) {\n res.part = p.spec.id\n res.point = point\n }\n if (T > p.st.temperature) {\n const k = (0.8 * f) / (p.mat.thermal.heatCapacity ?? 1)\n p.st.temperature += (T - p.st.temperature) * (1 - Math.exp(-k * duration))\n }\n o.active = true\n checkIgnite(o, p, src)\n }\n res.events = cur.slice(start)\n return res\n }\n\n const doHit = (o: ObjRT, hit: Hit, only?: PartRT): HitResult => {\n let point = hit.point\n let dir = hit.direction\n if (hit.space === 'local') {\n point = toWorld(o.st.position, o.st.rotation, point)\n if (dir) dir = rotateVec(o.st.rotation, dir)\n }\n const src = hit.source\n const start = cur.length\n let res: HitResult = { object: o.id, damage: 0, penetrated: false, exitEnergy: 0, events: [] }\n const E = hit.energy ?? 0\n if (hit.type === 'ballistic') {\n const loc = locate(o, point, dir, 0.05, only)\n if (loc) {\n const out = ballistic(o, loc.p, loc.point, loc.normal, loc.dir, E, hit.caliber ?? 9, loc.pathLen, src)\n res = { ...res, part: loc.p.spec.id, point: loc.point, damage: out.damage, penetrated: out.penetrated, exitEnergy: out.exitEnergy }\n }\n } else if (hit.type === 'blunt' || hit.type === 'slash') {\n const radius = hit.radius ?? 0\n const loc = locate(o, point, dir, Math.max(0.05, radius), only)\n if (loc) {\n res.part = loc.p.spec.id\n res.point = loc.point\n res.damage += melee(o, loc.p, hit.type, E, loc.point, loc.normal, loc.dir, radius || 0.03, src)\n if (radius > 0.1 && !only) {\n for (const p of o.parts) {\n if (p === loc.p || isLost(p)) continue\n const dd = distToPart(o, p, point)\n if (dd >= radius) continue\n const { c, q } = partXf(o, p)\n const sp = surfacePoint(p.spec.shape, toLocal(c, q, point))\n res.damage += melee(o, p, hit.type, E * (1 - dd / radius) ** 2, toWorld(c, q, sp.point), rotateVec(q, sp.normal), loc.dir, radius, src)\n }\n }\n }\n } else if (hit.type === 'explosive') {\n res = blastObject(o, point, E, hit.radius ?? 2, src)\n } else {\n res = heatObject(o, point, hit.temperature ?? 800, hit.radius ?? 0.25, hit.duration ?? 1, src)\n }\n res.events = cur.slice(start)\n return res\n }\n\n // -------------------------------------------------------------------------------------------------------------------------------------------\n // Fire, heat, melting, leaks\n // -------------------------------------------------------------------------------------------------------------------------------------------\n\n const checkIgnite = (o: ObjRT, p: PartRT, src?: string) => {\n const th = p.mat.thermal\n if (isLost(p) || p.st.burning || th.ignitionC === undefined || p.st.fuel <= 0 || p.st.temperature < th.ignitionC) return\n p.st.burning = true\n p.st.temperature = Math.max(p.st.temperature, th.flameC ?? 800)\n o.active = true\n markDamaged(p)\n emit({ ...base(o, src), type: 'ignite', part: p.spec.id, point: partXf(o, p).c })\n }\n\n const syncMass = (p: PartRT) => {\n p.st.mass = Math.max(0, p.mass0 * (1 - p.st.melt) - (p.fuel0 - p.st.fuel))\n }\n\n const burnStep = (o: ObjRT, p: PartRT, dt: number) => {\n const th = p.mat.thermal\n const use = Math.min((th.burnRate ?? 0.01) * p.area * dt, p.st.fuel)\n p.st.fuel -= use\n syncMass(p)\n if (p.structural) o.dirty = true\n p.st.temperature = th.flameC ?? 800\n markDamaged(p)\n if (p.fuel0 > 0) {\n p.st.char = clamp(1 - p.st.fuel / p.fuel0, 0, 1)\n p.st.hp = Math.min(p.st.hp, Math.max(1, p.st.maxHp * (p.st.fuel / p.fuel0)))\n }\n if (p.st.fuel <= 1e-9) {\n p.st.fuel = 0\n p.st.burning = false\n p.st.char = 1\n const c = partXf(o, p).c\n if ((th.fuelFraction ?? 0) >= 0.8) {\n p.st.status = 'ashes'\n p.st.hp = 0\n p.st.mass = p.mass0 * (1 - (th.fuelFraction ?? 0))\n emit({ ...base(o), type: 'burnout', part: p.spec.id, point: c })\n debris(o, p, 'ash', c, [0, 1, 0], clamp(Math.round(Math.cbrt(partVolume(p.spec.shape)) * 30), 3, 20), 1)\n partLost(o, p)\n } else {\n emit({ ...base(o), type: 'burnout', part: p.spec.id, point: c })\n }\n }\n }\n\n const meltStep = (o: ObjRT, p: PartRT, dt: number) => {\n const th = p.mat.thermal\n if (th.meltC === undefined || p.st.temperature <= th.meltC) return\n const rate = (th.meltRate ?? 0.05) * ((p.st.temperature - th.meltC) / 100)\n p.st.melt = Math.min(1, p.st.melt + rate * dt)\n syncMass(p)\n if (p.structural) o.dirty = true\n markDamaged(p)\n p.rt.dripT += dt\n if (p.rt.dripT >= 0.25) {\n p.rt.dripT -= 0.25\n const b = partWorldAabb(o, p)\n const pt: Vec3 = [b.min[0] + (b.max[0] - b.min[0]) * (0.2 + 0.6 * rnd()), b.min[1], b.min[2] + (b.max[2] - b.min[2]) * (0.2 + 0.6 * rnd())]\n emit({ ...base(o), type: 'drip', part: p.spec.id, point: pt, material: p.mat.id })\n }\n if (p.st.melt - p.rt.meltEmitted >= 0.05 || p.st.melt >= 1) {\n p.rt.meltEmitted = p.st.melt\n emit({ ...base(o), type: 'melt', part: p.spec.id, melt: p.st.melt })\n }\n if (p.st.melt >= 1) {\n p.st.status = 'melted'\n p.st.hp = 0\n p.st.mass = 0\n p.st.burning = false\n p.rt.jet = false\n partLost(o, p)\n }\n }\n\n const stepObject = (o: ObjRT, dt: number, sources: Array<{ o: ObjRT; p: PartRT; flame: number }>) => {\n const parts = o.parts\n const T0 = o.tmp\n T0.length = parts.length\n for (let i = 0; i < parts.length; i++) T0[i] = parts[i].st.temperature\n let hot = false\n for (let i = 0; i < parts.length; i++) {\n const p = parts[i]\n if (isLost(p)) continue\n const th = p.mat.thermal\n const hc = th.heatCapacity ?? 1\n const s = p.st\n if (p.rt.jet) {\n s.temperature = Math.max(s.temperature, 900)\n } else if (s.burning) {\n burnStep(o, p, dt)\n if (isLost(p)) continue\n }\n if (!s.burning && !p.rt.jet) {\n // conduction from hotter neighbours through closed joints, then cooling\n let dT = 0\n let maxN = s.temperature\n for (const j of o.byPart.get(p.spec.id)!) {\n if (j.st.open) continue\n const k = j.ia === i ? j.ib : j.ia\n if (isLost(parts[k]) || T0[k] <= T0[i]) continue\n const c = (th.conductivity + parts[k].mat.thermal.conductivity) / 2\n dT += (T0[k] - T0[i]) * (1 - Math.exp((-1.2 * c * dt) / hc))\n if (T0[k] > maxN) maxN = T0[k]\n }\n if (dT > 0) s.temperature = Math.min(s.temperature + dT, maxN)\n s.temperature += (ambient - s.temperature) * (1 - Math.exp(-dt / (20 * hc)))\n if (Math.abs(s.temperature - ambient) < 0.05) s.temperature = ambient\n }\n meltStep(o, p, dt)\n if (isLost(p)) continue\n checkIgnite(o, p)\n const ex = p.spec.explodes\n if (ex && chainDelayOf(p.spec, 'fire') !== undefined) {\n if (s.temperature >= (ex.atC ?? FIRE_PRIME_C)) prime(o, p, 'fire')\n } else if (ex && ex.atC !== undefined && s.temperature >= ex.atC) {\n failPart(o, p, null, null, 0, 40)\n continue\n }\n // leaks\n const cont = p.spec.contains\n if (cont && (s.liters ?? 0) > 0) {\n let rate = 0\n for (const h of s.holes) if (h.through) rate += holeRate(h, !!cont.pressurized)\n if (rate > 0) {\n s.liters = Math.max(0, (s.liters ?? 0) - rate * dt)\n hot = true\n if (FLAMMABLE_FLUIDS.includes(cont.fluid) && s.temperature >= 350 && !p.rt.jet) {\n if (ex && chainDelayOf(p.spec, 'fire') !== undefined) {\n prime(o, p, 'fire')\n continue\n }\n if (ex) {\n failPart(o, p, null, null, 0, 40)\n continue\n }\n p.rt.jet = true\n s.burning = true\n emit({ ...base(o), type: 'ignite', part: p.spec.id, point: partXf(o, p).c })\n }\n }\n }\n if (p.rt.jet && (s.liters ?? 0) <= 0) {\n p.rt.jet = false\n s.burning = false\n emit({ ...base(o), type: 'extinguish', part: p.spec.id })\n }\n // thermal shock\n const sc = th.thermalShockC\n if (sc !== undefined) {\n p.rt.shockT = Math.max(0, p.rt.shockT - dt)\n const rate = Math.abs(s.temperature - p.rt.prevTemp) / dt\n p.rt.prevTemp = s.temperature\n if (rate > sc && p.rt.shockT <= 0) {\n p.rt.shockT = 1\n const { c, q } = partXf(o, p)\n const lp: Vec3 = [(rnd() - 0.5) * p.dims[0], (rnd() - 0.5) * p.dims[1], (rnd() - 0.5) * p.dims[2]]\n const sp = surfacePoint(p.spec.shape, lp)\n addCrack(o, p, toWorld(c, q, sp.point), rotateVec(q, sp.normal), 0.1 + (rate / sc) * 0.1)\n applyDamage(o, p, p.st.maxHp * 0.25, 'heat', null, null, 5, 3)\n if (isLost(p)) continue\n }\n }\n if (s.burning || p.rt.jet) sources.push({ o, p, flame: p.rt.jet ? 900 : th.flameC ?? 800 })\n if (s.burning || p.rt.jet || s.temperature > ambient + 0.5) hot = true\n }\n if (o.hasStructure) {\n if (o.dirty) {\n o.dirty = false\n solveStructure(o)\n }\n for (const p of parts) {\n if (!p.structural || isLost(p)) continue\n const stress = p.st.stress ?? 0\n p.rt.stressT = Math.max(0, (p.rt.stressT ?? 0) - dt)\n if (stress > 1) {\n if (p.rt.stressT <= 0) {\n p.rt.stressT = 0.5\n emit({ ...base(o), type: 'stress', part: p.spec.id, stress, load: p.st.load ?? 0 })\n }\n applyDamage(o, p, overloadRate(stress) * p.st.maxHp * dt, 'blunt', null, null, 5, 3)\n } else if (stress > 0.8 && p.mat.ductility < 0.3 && p.rt.stressT <= 0 && p.st.cracks.length < 6) {\n p.rt.stressT = 0.5\n const { c, q } = partXf(o, p)\n const sp = surfacePoint(p.spec.shape, [(rnd() - 0.5) * p.dims[0], (rnd() - 0.5) * p.dims[1], (rnd() - 0.5) * p.dims[2]])\n addCrack(o, p, toWorld(c, q, sp.point), rotateVec(q, sp.normal), 0.1 + (stress - 0.8) * 0.5)\n }\n }\n if (o.maxStress > 0.8) hot = true\n }\n o.active = hot\n }\n\n const spread = (sources: Array<{ o: ObjRT; p: PartRT; flame: number }>, dt: number) => {\n for (const src of sources) {\n if (!src.p.st.burning && !src.p.rt.jet) continue\n const sc = toWorld(src.o.st.position, src.o.st.rotation, src.p.pos)\n const A = partWorldAabb(src.o, src.p)\n for (const o of objects.values()) {\n if (o === src.o) continue\n if (vec.distance(toWorld(o.st.position, o.st.rotation, o.bc), sc) > o.br + src.p.bound + fireSpread) continue\n for (const q of o.parts) {\n if (isLost(q) || q.st.burning) continue\n const B = partWorldAabb(o, q)\n const gap = Math.hypot(\n Math.max(0, A.min[0] - B.max[0], B.min[0] - A.max[0]),\n Math.max(0, A.min[1] - B.max[1], B.min[1] - A.max[1]),\n Math.max(0, A.min[2] - B.max[2], B.min[2] - A.max[2]),\n )\n if (gap >= fireSpread || src.flame <= q.st.temperature) continue\n const f = 1 - gap / fireSpread\n q.st.temperature += (src.flame - q.st.temperature) * (1 - Math.exp((-0.6 * f * dt) / (q.mat.thermal.heatCapacity ?? 1)))\n o.active = true\n }\n }\n }\n }\n\n // -------------------------------------------------------------------------------------------------------------------------------------------\n // World API\n // -------------------------------------------------------------------------------------------------------------------------------------------\n\n /** The primed part that goes off first: earliest due time, then the older object, then the part listed first. */\n const nextPrimed = (until: number): { o: ObjRT; p: PartRT } | null => {\n let best: { o: ObjRT; p: PartRT } | null = null\n for (const [p, o] of primed) {\n if (objects.get(o.id) !== o || isLost(p)) {\n primed.delete(p)\n continue\n }\n const due = p.rt.chainDue as number\n if (due > until) continue\n if (!best) best = { o, p }\n else {\n const bestDue = best.p.rt.chainDue as number\n if (due < bestDue || (due === bestDue && (o.order < best.o.order || (o.order === best.o.order && (o.idx.get(p.spec.id) ?? 0) < (best.o.idx.get(best.p.spec.id) ?? 0))))) best = { o, p }\n }\n }\n return best\n }\n\n /**\n * Explodes the primed parts whose delay ran out by `until`, one at a time in order of time; each blast is resolved before the next part goes off,\n * so a part it primes counts its delay from that moment, not from the end of the frame.\n */\n const detonatePrimed = (until: number) => {\n const end = time\n for (let next = nextPrimed(until); next; next = nextPrimed(until)) {\n primed.delete(next.p)\n time = Math.max(0, Math.min(end, next.p.rt.chainDue as number))\n next.p.rt.chainDue = undefined\n failPart(next.o, next.p, null, null, 0, 40)\n drainBlasts()\n }\n time = end\n }\n\n /** Collapse events are produced after the whole operation: make sure the result of the operation carries them. */\n const withCollapses = (r: { value: HitResult[]; events: DestructionEvent[] }): HitResult[] => {\n const last = r.value[r.value.length - 1]\n if (!last) return r.value\n for (const e of r.events) if (e.type === 'collapse' && !r.value.some((h) => h.events.includes(e))) last.events.push(e)\n return r.value\n }\n\n const whole = (fn: () => HitResult): HitResult => {\n const r = run(fn)\n r.value.events = r.events\n return r.value\n }\n\n const world: DestructionWorld = {\n materials,\n get time() {\n return time\n },\n add(spec, opts: AddOptions = {}) {\n const id = opts.id ?? nextId()\n if (taken(id)) throw new Error(`object \"${id}\" already exists`)\n objects.set(id, build(resolveSpec(spec), id, opts.position ?? [0, 0, 0], opts.rotation ?? [0, 0, 0, 1]))\n return id\n },\n register(impl, opts: AddOptions = {}) {\n const id = opts.id ?? impl.id\n if (taken(id)) throw new Error(`object \"${id}\" already exists`)\n impls.set(id, { impl, position: opts.position ?? [0, 0, 0], rotation: opts.rotation ?? [0, 0, 0, 1], poses: new Map() })\n return id\n },\n setPartPose(objectId, partId, position, rotation) {\n const q = quatFromEuler(...(rotation ?? [0, 0, 0]))\n const im = impls.get(objectId)\n if (im) {\n im.impl.setPartPose?.(partId, position, rotation)\n im.poses.set(partId, { pos: [position[0], position[1], position[2]], q })\n return\n }\n const o = getObj(objectId)\n const p = getPart(o, partId)\n p.pos = [position[0], position[1], position[2]]\n p.q = q\n updateBound(o)\n },\n remove(id) {\n objects.delete(id)\n impls.delete(id)\n },\n has: (id) => taken(id),\n ids: () => [...objects.keys(), ...impls.keys()],\n setTransform(id, position, rotation) {\n const im = impls.get(id)\n if (im) {\n im.position = [position[0], position[1], position[2]]\n im.rotation = [rotation[0], rotation[1], rotation[2], rotation[3]]\n return\n }\n const o = getObj(id)\n o.st.position = [position[0], position[1], position[2]]\n o.st.rotation = [rotation[0], rotation[1], rotation[2], rotation[3]]\n },\n splitGroup(id, group, newId) {\n if (impls.has(id)) throw new Error(`object \"${id}\" is a registered DestructibleImpl and cannot be split; its module owns its pieces`)\n const o = getObj(id)\n if (o.st.groups.length < 2) return id\n const names = o.st.groups[group]\n if (!names) throw new Error(`object \"${id}\" has no group ${group}`)\n const moved = new Set(names)\n const nid = newId ?? nextId()\n if (objects.has(nid)) throw new Error(`object \"${nid}\" already exists`)\n // an open joint between the two objects stays with the original\n const carryFor = (keep: (pid: string) => boolean, keepOpen: (a: string, b: string) => boolean = () => false): Carry => ({\n parts: new Map(o.parts.filter((p) => keep(p.spec.id)).map((p) => [p.spec.id, { st: p.st, rt: p.rt }])),\n joints: new Map([\n ...o.joints.filter((j) => (keep(j.spec.a) && keep(j.spec.b)) || (j.st.open && keepOpen(j.spec.a, j.spec.b))).map((j): [string, { st: JointState; step: number }] => [j.spec.id, { st: j.st, step: j.step }]),\n ...o.st.joints.filter((st) => st.open && !o.jointMap.has(st.id) && keepOpen(st.a, st.b)).map((st): [string, { st: JointState; step: number }] => [st.id, { st, step: 0 }]),\n ]),\n collapsed: [...o.collapsed].filter(keep),\n })\n const sub = (keep: (pid: string) => boolean): DestructibleSpec => {\n const s: DestructibleSpec = { parts: o.spec.parts.filter((p) => keep(p.id)), joints: (o.spec.joints ?? []).filter((j) => keep(j.a) && keep(j.b)) }\n if (o.spec.name !== undefined) s.name = o.spec.name\n if (o.spec.tags) s.tags = [...o.spec.tags]\n return clone(s)\n }\n const inNew = (pid: string) => moved.has(pid)\n const inOld = (pid: string) => !moved.has(pid)\n const pos = o.st.position, rot = o.st.rotation\n const fresh = build(sub(inNew), nid, pos, rot, carryFor(inNew, (a, b) => inNew(a) && inNew(b)))\n const rest = build(sub(inOld), id, pos, rot, carryFor(inOld, (a, b) => !(inNew(a) && inNew(b))))\n objects.set(id, rest)\n objects.set(nid, fresh)\n return nid\n },\n hit(objectId, h) {\n const im = impls.get(objectId)\n if (im) return whole(() => implHit(im, h))\n const o = getObj(objectId)\n return whole(() => doHit(o, h))\n },\n hitPart(objectId, partId, h) {\n const im = impls.get(objectId)\n if (im) return whole(() => implHit(im, h))\n const o = getObj(objectId)\n const p = getPart(o, partId)\n return whole(() => doHit(o, h, p))\n },\n explode(point, energy, radius, source) {\n return withCollapses(run(() => blastWorld(point, energy, radius, source)))\n },\n raycast(origin, direction, maxDistance = Infinity) {\n const r = raycastInternal(origin, direction, maxDistance)\n return r ? { object: r.object, part: r.part, point: r.point, normal: r.normal, distance: r.distance } : null\n },\n shoot(origin, direction, energy, caliber, source) {\n return withCollapses(run(() => {\n const results: HitResult[] = []\n const d = vec.normalize(direction)\n const ignore = new Set<string>()\n let org = origin\n let E = energy\n for (let i = 0; i < 4 && E > 0; i++) {\n const rh = raycastInternal(org, d, Infinity, ignore)\n if (!rh) break\n const im = impls.get(rh.object)\n if (im) {\n const r = implHit(im, { type: 'ballistic', point: rh.point, direction: d, energy: E, caliber, ...(source !== undefined ? { source } : {}) })\n r.part = rh.part\n r.point = rh.point\n r.exitEnergy = r.penetrated ? E * 0.5 : 0\n results.push(r)\n ignore.add(`${im.impl.id}\\0${rh.part}`)\n if (!r.penetrated) break\n org = vec.madd(rh.point, d, Math.min(rh.pathLen, 0.05) + 0.001)\n E = r.exitEnergy\n continue\n }\n const o = getObj(rh.object)\n const p = getPart(o, rh.part)\n const start = cur.length\n const out = ballistic(o, p, rh.point, rh.normal, d, E, caliber, rh.pathLen, source)\n results.push({ object: o.id, part: p.spec.id, point: rh.point, damage: out.damage, penetrated: out.penetrated, exitEnergy: out.exitEnergy, events: cur.slice(start) })\n ignore.add(`${o.id}\\0${p.spec.id}`)\n if (!out.penetrated || !out.exitPoint) break\n org = vec.madd(out.exitPoint, d, 0.001)\n E = out.exitEnergy\n }\n return results\n }))\n },\n disassemble(objectId, jointId, tool: ToolKind, dt) {\n const im = impls.get(objectId)\n if (im) return im.impl.disassemble ? im.impl.disassemble(jointId, tool, dt) : -1\n const o = getObj(objectId)\n const j = o.jointMap.get(jointId)\n if (!j || !j.spec.tools.includes(tool)) return -1\n if (j.st.open) return 1\n return run(() => {\n j.st.progress = Math.min(1, j.st.progress + (j.spec.seconds > 0 ? dt / j.spec.seconds : 1))\n if (j.st.progress >= 1 - 1e-9) j.st.progress = 1\n const step = Math.floor(j.st.progress * 10 + 1e-9)\n if (step > j.step || j.st.progress >= 1) {\n j.step = step\n emit({ ...base(o), type: 'disassemble_progress', joint: j.spec.id, tool, progress: j.st.progress })\n }\n if (j.st.progress >= 1) {\n j.st.open = true\n j.st.openedBy = 'tool'\n solveStructure(o)\n recomputeGroups(o)\n detachEvents(o, [j], 'tool')\n collapseCheck(o)\n }\n return j.st.progress\n }).value\n },\n workableJoints(objectId, tool) {\n const im = impls.get(objectId)\n if (im) {\n const spec = im.impl.describe()\n return im.impl.state().joints.filter((js) => {\n const j = (spec.joints ?? []).find((x) => (x.id ?? `${x.a}~${x.b}`) === js.id)\n return !js.open && !!j && (j.tools ?? JOINT_DEFAULTS[j.kind].tools).includes(tool)\n })\n }\n return getObj(objectId).joints.filter((j) => !j.st.open && j.spec.tools.includes(tool)).map((j) => j.st)\n },\n nearestJoint(objectId, point, maxDistance = 0.3, tool) {\n const im = impls.get(objectId)\n // Candidates: closed joints with their world position and whether `tool` can undo them.\n const cands: { st: JointState; d: number; ok: boolean }[] = []\n if (im) {\n const spec = im.impl.describe()\n for (const js of im.impl.state().joints) {\n if (js.open) continue\n const pos = implJointPos(im, js.a, js.b)\n const j = (spec.joints ?? []).find((x) => (x.id ?? `${x.a}~${x.b}`) === js.id)\n cands.push({ st: js, d: pos ? vec.distance(pos, point) : Infinity, ok: !!j && (j.tools ?? JOINT_DEFAULTS[j.kind].tools).includes(tool as ToolKind) })\n }\n } else {\n const o = getObj(objectId)\n for (const j of o.joints) {\n if (j.st.open) continue\n cands.push({ st: j.st, d: vec.distance(jointWorld(o, j), point), ok: tool ? j.spec.tools.includes(tool) : true })\n }\n }\n const pick = (only: boolean): JointState | null => {\n let best: JointState | null = null\n let bd = maxDistance\n for (const c of cands) {\n if (only && !c.ok) continue\n if (c.d <= bd) {\n bd = c.d\n best = c.st\n }\n }\n return best\n }\n return (tool ? pick(true) : null) ?? pick(false)\n },\n extinguish(objectId, partId, coolC = 200) {\n const im = impls.get(objectId)\n if (im) {\n run(() => im.impl.extinguish?.(partId))\n return\n }\n const o = getObj(objectId)\n run(() => {\n for (const p of partId ? [getPart(o, partId)] : o.parts) {\n if (isLost(p)) continue\n if (p.st.burning) {\n p.st.burning = false\n p.rt.jet = false\n emit({ ...base(o), type: 'extinguish', part: p.spec.id })\n }\n p.st.temperature = Math.max(ambient, p.st.temperature - coolC)\n }\n o.active = true\n })\n },\n setLiters(objectId, partId, liters) {\n if (impls.has(objectId)) return\n const p = getPart(getObj(objectId), partId)\n if (!p.spec.contains) return\n p.st.liters = clamp(liters, 0, p.spec.contains.liters)\n getObj(objectId).active = true\n },\n update(dt) {\n if (!(dt > 0)) return []\n return run(() => {\n time += dt\n const sources: Array<{ o: ObjRT; p: PartRT; flame: number }> = []\n for (const o of [...objects.values()]) if (o.active) stepObject(o, dt, sources)\n if (sources.length) spread(sources, dt)\n detonatePrimed(time)\n for (const im of [...impls.values()]) {\n if (!im.impl.update) continue\n for (const e of im.impl.update(dt) ?? []) emit(e.time === undefined ? { ...e, time } : e)\n }\n }).events\n },\n state: (id) => objects.get(id)?.st ?? impls.get(id)?.impl.state() ?? null,\n snapshot: (id) => {\n const st = objects.get(id)?.st ?? impls.get(id)?.impl.state()\n return st ? clone(st) : null\n },\n part: (id, partId) => objects.get(id)?.byId.get(partId)?.st ?? impls.get(id)?.impl.state().parts.find((p) => p.id === partId) ?? null,\n reset(id) {\n const im = impls.get(id)\n if (im) {\n im.impl.reset?.()\n return\n }\n const o = getObj(id)\n objects.set(id, build(o.spec, id, o.st.position, o.st.rotation))\n },\n on(type: DestructionEventType | '*', 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 serialize(): DestructionSnapshot {\n return {\n version: 1,\n time,\n rng: rng.state(),\n counter,\n objects: [...objects.values()].map((o) => {\n const runtime: ObjectRuntime = { parts: {}, joints: {}, collapsed: [...o.collapsed] }\n for (const p of o.parts) runtime.parts[p.spec.id] = { ...p.rt }\n for (const j of o.joints) runtime.joints[j.spec.id] = j.step\n return { id: o.id, spec: clone(o.spec), state: clone(o.st), runtime }\n }),\n ...(impls.size\n ? { impls: [...impls.values()].map((im) => ({ id: im.impl.id, position: [...im.position] as Vec3, rotation: [...im.rotation] as Quat, data: im.impl.serialize?.() })) }\n : {}),\n }\n },\n load(snap) {\n if (snap.version !== 1) throw new Error(`unsupported snapshot version ${String(snap.version)}`)\n objects.clear()\n time = snap.time\n rng.set(snap.rng)\n counter = snap.counter ?? 0\n for (const e of snap.objects) {\n const state = clone(e.state)\n const carry: Carry = {\n parts: new Map(\n state.parts.map((st) => [st.id, { st, rt: e.runtime?.parts[st.id] ? { ...e.runtime.parts[st.id] } : { ...freshRuntime(), prevTemp: st.temperature } }]),\n ),\n joints: new Map(state.joints.map((st) => [st.id, { st, step: e.runtime?.joints[st.id] ?? 0 }])),\n collapsed: e.runtime?.collapsed,\n }\n objects.set(e.id, build(clone(e.spec), e.id, state.position, state.rotation, carry))\n }\n for (const e of snap.impls ?? []) {\n const im = impls.get(e.id)\n if (!im) continue\n im.position = [e.position[0], e.position[1], e.position[2]]\n im.rotation = [e.rotation[0], e.rotation[1], e.rotation[2], e.rotation[3]]\n if (e.data !== undefined) im.impl.load?.(e.data)\n }\n },\n }\n return world\n}\n","import type { DestructibleSpec, JointSpec, MaterialId, PartSpec } from './types'\n\nexport interface FrameOptions {\n /** Total length along x between the outer column centres, metres (default 8). */\n spanX?: number\n /** Total length along z (default 8). */\n spanZ?: number\n /** Columns along x (at least 2, default 3). */\n columnsX?: number\n /** Columns along z (at least 2, default 3). */\n columnsZ?: number\n /** Height of one floor from slab top to the underside of the next slab, metres (default 3). */\n height?: number\n /** Number of floors (default 1). */\n floors?: number\n /** Brick walls on every perimeter bay of every floor; the first bay of the ground floor on the -z side gets a door opening (default false). */\n walls?: boolean\n /** A thin roof slab resting on the top beams (default true); without it the top floor is open. */\n roof?: boolean\n columnSize?: number\n beamHeight?: number\n slabThickness?: number\n wallThickness?: number\n doorWidth?: number\n doorHeight?: number\n /** Material of foundations, columns, beams, slabs and roof (default `concrete`). */\n material?: MaterialId\n wallMaterial?: MaterialId\n /** Override the load capacity of every column, kg (see `PartState.load`). */\n columnCapacity?: number\n}\n\n/**\n * A parametric test structure: a grid of columns on foundations, beams between the column tops, one slab per floor (the last one a roof), and\n * optional brick walls with a door. Joints are explicit and directional: columns rest on foundations (or on the slab below) with `rebar`, beams on\n * columns, slabs on beams, the roof rests on the top beams, walls hang on their columns with `mortar`. Part ids: `foundation_i_j`, `column_f_i_j`,\n * `beam_x_f_i_j`, `beam_z_f_i_j`, `slab_f`, `roof`, `wall_*`, `door`. Ground is y = 0 (the top of the foundations).\n */\nexport const buildFrame = (o: FrameOptions = {}): DestructibleSpec => {\n const spanX = o.spanX ?? 8, spanZ = o.spanZ ?? 8\n const nx = Math.max(2, o.columnsX ?? 3), nz = Math.max(2, o.columnsZ ?? 3)\n const H = o.height ?? 3, floors = Math.max(1, o.floors ?? 1)\n const cs = o.columnSize ?? 0.3, bh = o.beamHeight ?? 0.3, bw = 0.2, T = o.slabThickness ?? 0.2, wt = o.wallThickness ?? 0.2\n const dw = o.doorWidth ?? 1, dh = o.doorHeight ?? 2.1\n const mat = o.material ?? 'concrete', wmat = o.wallMaterial ?? 'brick'\n const roof = o.roof ?? true\n const xs = Array.from({ length: nx }, (_, i) => -spanX / 2 + (i * spanX) / (nx - 1))\n const zs = Array.from({ length: nz }, (_, j) => -spanZ / 2 + (j * spanZ) / (nz - 1))\n const parts: PartSpec[] = []\n const joints: JointSpec[] = []\n const on = (a: string, b: string, kind: JointSpec['kind']) => joints.push({ id: `${a}~${b}`, a, b, kind, bears: 'a_on_b' })\n\n for (let i = 0; i < nx; i++) {\n for (let j = 0; j < nz; j++) {\n parts.push({\n id: `foundation_${i}_${j}`, material: mat, shape: { kind: 'box', size: [cs * 2.2, 0.3, cs * 2.2] }, position: [xs[i], -0.15, zs[j]],\n structure: { role: 'foundation' }, anchored: true,\n })\n }\n }\n for (let f = 0; f < floors; f++) {\n const yb = f * (H + T)\n const top = yb + H\n const last = f === floors - 1\n const slabId = last && roof ? 'roof' : `slab_${f}`\n for (let i = 0; i < nx; i++) {\n for (let j = 0; j < nz; j++) {\n const id = `column_${f}_${i}_${j}`\n parts.push({\n id, material: mat, shape: { kind: 'box', size: [cs, H, cs] }, position: [xs[i], yb + H / 2, zs[j]],\n structure: o.columnCapacity === undefined ? { role: 'column' } : { role: 'column', capacity: o.columnCapacity },\n })\n on(id, f === 0 ? `foundation_${i}_${j}` : `slab_${f - 1}`, 'rebar')\n }\n }\n const beams: string[] = []\n const beam = (id: string, size: [number, number, number], pos: [number, number, number], c1: string, c2: string) => {\n parts.push({ id, material: mat, shape: { kind: 'box', size }, position: pos, structure: { role: 'beam' } })\n on(id, c1, 'rebar')\n on(id, c2, 'rebar')\n beams.push(id)\n }\n for (let j = 0; j < nz; j++) {\n for (let i = 0; i < nx - 1; i++) {\n beam(`beam_x_${f}_${i}_${j}`, [xs[i + 1] - xs[i] - cs, bh, bw], [(xs[i] + xs[i + 1]) / 2, top - bh / 2, zs[j]], `column_${f}_${i}_${j}`, `column_${f}_${i + 1}_${j}`)\n }\n }\n for (let i = 0; i < nx; i++) {\n for (let j = 0; j < nz - 1; j++) {\n beam(`beam_z_${f}_${i}_${j}`, [bw, bh, zs[j + 1] - zs[j] - cs], [xs[i], top - bh / 2, (zs[j] + zs[j + 1]) / 2], `column_${f}_${i}_${j}`, `column_${f}_${i}_${j + 1}`)\n }\n }\n const thick = last && roof ? T * 0.75 : T\n parts.push({\n id: slabId, material: mat, shape: { kind: 'box', size: [spanX + cs + 0.2, thick, spanZ + cs + 0.2] }, position: [0, top + thick / 2, 0],\n structure: { role: last && roof ? 'roof' : 'slab' },\n })\n for (const b of beams) on(slabId, b, last && roof ? 'resting' : 'rebar')\n\n if (o.walls) {\n const wh = H - bh\n const wy = yb + wh / 2\n const wall = (id: string, size: [number, number, number], pos: [number, number, number], supports: string[]) => {\n parts.push({ id, material: wmat, shape: { kind: 'box', size }, position: pos, structure: { role: 'wall' } })\n for (const s of supports) on(id, s, 'mortar')\n }\n for (const j of [0, nz - 1]) {\n for (let i = 0; i < nx - 1; i++) {\n const c1 = `column_${f}_${i}_${j}`, c2 = `column_${f}_${i + 1}_${j}`\n const avail = xs[i + 1] - xs[i] - cs\n if (f === 0 && j === 0 && i === 0 && avail > dw + 0.4) {\n const side = (avail - dw) / 2\n const lx = xs[i] + cs / 2 + side / 2, rx = xs[i + 1] - cs / 2 - side / 2\n wall(`wall_door_left`, [side, wh, wt], [lx, wy, zs[j]], [c1])\n wall(`wall_door_right`, [side, wh, wt], [rx, wy, zs[j]], [c2])\n const lh = wh - dh\n wall(`wall_door_lintel`, [dw, lh, wt], [xs[i] + cs / 2 + side + dw / 2, yb + dh + lh / 2, zs[j]], ['wall_door_left', 'wall_door_right'])\n parts.push({ id: 'door', material: 'wood_hard', shape: { kind: 'box', size: [dw - 0.04, dh, 0.05] }, position: [xs[i] + cs / 2 + side + dw / 2, yb + dh / 2, zs[j]], tags: ['door'] })\n joints.push({ id: 'door~wall_door_left', a: 'door', b: 'wall_door_left', kind: 'hinge', bears: false })\n } else wall(`wall_x_${f}_${i}_${j}`, [avail, wh, wt], [(xs[i] + xs[i + 1]) / 2, wy, zs[j]], [c1, c2])\n }\n }\n for (const i of [0, nx - 1]) {\n for (let j = 0; j < nz - 1; j++) {\n wall(`wall_z_${f}_${i}_${j}`, [wt, wh, zs[j + 1] - zs[j] - cs], [xs[i], wy, (zs[j] + zs[j + 1]) / 2], [`column_${f}_${i}_${j}`, `column_${f}_${i}_${j + 1}`])\n }\n }\n }\n }\n return { name: 'frame', parts, joints, tags: ['structure'] }\n}\n","import { createDestruction } from './world'\nimport type { DestructibleSpec, SizeStructureOptions } from './types'\n\n/**\n * Sizes the load capacity of a structure the way an engineer would, from the loads the world itself computes. The intact spec goes through the\n * same load solver `createDestruction` uses, and each structural part (`structure.role` other than `none`) gets `structure.capacity = load / safety`,\n * so its at-rest stress is exactly `safety`. With `redundancy` set, every `column` is also removed in turn and the capacity of the parts that\n * would carry more is raised so they stay at or below `redundancy`. Parts without `structure`, with role `none`, or with no support path to an\n * anchored part are left as they are. The spec is not modified; a copy is returned.\n */\nexport const sizeStructure = (spec: DestructibleSpec, options: SizeStructureOptions = {}): DestructibleSpec => {\n const safety = options.safety ?? 0.65\n const redundancy = options.redundancy\n const loads = (s: DestructibleSpec): Map<string, { load: number; stress: number }> => {\n const w = createDestruction(options.materials ? { materials: options.materials } : {})\n const id = w.add(s)\n return new Map(w.state(id)!.parts.map((p) => [p.id, { load: p.load ?? 0, stress: p.stress ?? 0 }]))\n }\n const out = structuredClone(spec)\n const base = loads(out)\n const need = new Map<string, number>()\n for (const p of out.parts) {\n if (!p.structure || p.structure.role === 'none') continue\n const b = base.get(p.id)\n if (!b || b.stress <= 0) continue\n need.set(p.id, b.load / safety)\n }\n if (redundancy !== undefined) {\n for (const col of out.parts) {\n if (col.structure?.role !== 'column') continue\n const reduced: DestructibleSpec = {\n ...out,\n parts: out.parts.filter((p) => p.id !== col.id),\n joints: (out.joints ?? []).filter((j) => j.a !== col.id && j.b !== col.id),\n }\n // joints are inferred when the spec has none; removing a part then removes its joints with it\n if (!out.joints) delete reduced.joints\n const l = loads(reduced)\n for (const [id, n] of need) {\n const r = l.get(id)\n if (r && r.stress > 0) need.set(id, Math.max(n, r.load / redundancy))\n }\n }\n }\n for (const p of out.parts) {\n const n = need.get(p.id)\n if (n !== undefined) p.structure = { ...p.structure!, capacity: n }\n }\n return out\n}\n","import type { DamageType, DestructionEventType, FractureStyle, JointKind, PartStatus, StructureRole, ToolKind } from './types'\n\n/** Locales shipped with the package. Unknown locales fall back to `en`. */\nexport type DestructionLocale = 'en' | 'pt-BR'\n\nexport interface DestructionStrings {\n /** Display name per material id (see `MATERIALS`). Unknown ids are not listed. */\n materialNames: Record<string, string>\n partStatus: Record<PartStatus, string>\n jointKinds: Record<JointKind, string>\n toolKinds: Record<ToolKind, string>\n damageTypes: Record<DamageType, string>\n /** Short label per event type, for logs and kill-feed style text. */\n eventLabels: Record<DestructionEventType, string>\n fractureStyles: Record<FractureStyle, string>\n structureRoles: Record<StructureRole, string>\n}\n\nexport const locales: Record<DestructionLocale, DestructionStrings> = {\n en: {\n materialNames: {\n steel: 'steel', cast_iron: 'cast iron', aluminium: 'aluminium', copper: 'copper', wood_soft: 'softwood', wood_hard: 'hardwood', plywood: 'plywood',\n mdf: 'MDF', glass: 'glass', tempered_glass: 'tempered glass', ceramic: 'ceramic', concrete: 'concrete', brick: 'brick', plaster: 'plaster',\n stone: 'stone', plastic: 'plastic', rubber: 'rubber', fabric: 'fabric', foam: 'foam', leather: 'leather', paper: 'paper', foliage: 'foliage',\n green_wood: 'green wood', cactus: 'cactus', flesh_fruit: 'fruit', wax: 'wax', soil: 'soil',\n },\n partStatus: { intact: 'intact', damaged: 'damaged', collapsed: 'collapsed', broken: 'broken', melted: 'melted', ashes: 'ashes' },\n jointKinds: {\n screw: 'screw', bolt: 'nut and bolt', nail: 'nail', weld: 'weld', glue: 'glue', hinge: 'hinge', clip: 'clip', tie: 'tie', mortar: 'mortar',\n fused: 'fused', stem: 'stem', rebar: 'rebar', resting: 'resting',\n },\n toolKinds: {\n hand: 'hand', screwdriver: 'screwdriver', wrench: 'wrench', pry_bar: 'pry bar', saw: 'saw', cutting_torch: 'cutting torch', knife: 'knife', hammer: 'hammer',\n },\n damageTypes: { ballistic: 'ballistic', blunt: 'blunt', slash: 'slash', explosive: 'explosive', fire: 'fire', heat: 'heat' },\n eventLabels: {\n damage: 'damage', dent: 'dent', hole: 'hole', crack: 'crack', ricochet: 'ricochet', debris: 'debris', fracture: 'broke', detach: 'came loose',\n disassemble_progress: 'taking apart', ignite: 'caught fire', extinguish: 'put out', burnout: 'burned to ash', melt: 'melting', drip: 'dripping',\n leak: 'leak', explode: 'exploded', primed: 'about to explode', stress: 'overload', collapse: 'collapsed', destroyed: 'destroyed',\n },\n fractureStyles: { shatter: 'shatter', crumble: 'crumble', splinter: 'splinter', tear: 'tear', snap: 'snap', none: 'none' },\n structureRoles: { foundation: 'foundation', column: 'column', beam: 'beam', slab: 'slab', wall: 'wall', partition: 'partition', roof: 'roof', stair: 'stairs', none: 'none' },\n },\n 'pt-BR': {\n materialNames: {\n steel: 'aço', cast_iron: 'ferro fundido', aluminium: 'alumínio', copper: 'cobre', wood_soft: 'madeira macia', wood_hard: 'madeira de lei',\n plywood: 'compensado', mdf: 'MDF', glass: 'vidro', tempered_glass: 'vidro temperado', ceramic: 'cerâmica', concrete: 'concreto', brick: 'tijolo',\n plaster: 'gesso', stone: 'pedra', plastic: 'plástico', rubber: 'borracha', fabric: 'tecido', foam: 'espuma', leather: 'couro', paper: 'papel',\n foliage: 'folhagem', green_wood: 'madeira verde', cactus: 'cacto', flesh_fruit: 'fruta', wax: 'cera', soil: 'terra',\n },\n partStatus: { intact: 'intacto', damaged: 'danificado', collapsed: 'desabado', broken: 'quebrado', melted: 'derretido', ashes: 'cinzas' },\n jointKinds: {\n screw: 'parafuso', bolt: 'porca e parafuso', nail: 'prego', weld: 'solda', glue: 'cola', hinge: 'dobradiça', clip: 'encaixe', tie: 'amarração',\n mortar: 'argamassa', fused: 'fundido', stem: 'caule', rebar: 'vergalhão', resting: 'apoiado',\n },\n toolKinds: {\n hand: 'mão', screwdriver: 'chave de fenda', wrench: 'chave inglesa', pry_bar: 'pé de cabra', saw: 'serra', cutting_torch: 'maçarico', knife: 'faca', hammer: 'martelo',\n },\n damageTypes: { ballistic: 'tiro', blunt: 'impacto', slash: 'corte', explosive: 'explosão', fire: 'fogo', heat: 'calor' },\n eventLabels: {\n damage: 'dano', dent: 'amassado', hole: 'furo', crack: 'trinca', ricochet: 'ricochete', debris: 'estilhaços', fracture: 'quebrou', detach: 'soltou',\n disassemble_progress: 'desmontando', ignite: 'pegou fogo', extinguish: 'apagou', burnout: 'virou cinza', melt: 'derretendo', drip: 'pingando',\n leak: 'vazamento', explode: 'explodiu', primed: 'prestes a explodir', stress: 'sobrecarga', collapse: 'desabou', destroyed: 'destruído',\n },\n fractureStyles: { shatter: 'estilhaçar', crumble: 'esfarelar', splinter: 'lascar', tear: 'rasgar', snap: 'partir', none: 'nenhuma' },\n structureRoles: { foundation: 'fundação', column: 'pilar', beam: 'viga', slab: 'laje', wall: 'parede', partition: 'divisória', roof: 'telhado', stair: 'escada', none: 'nenhum' },\n },\n}\n\nexport const resolveStrings = (locale: string | undefined): DestructionStrings => locales[(locale as DestructionLocale) ?? 'en'] ?? locales.en\n","import { checkDestructible as check } from '@adatechnology/game-contracts'\nimport type { CheckOptions as ContractCheckOptions, DestructibleImpl } from '@adatechnology/game-contracts'\nimport { MATERIALS } from './materials'\n\n// The contract itself lives in @adatechnology/game-contracts (a module depends only on that); re-exported here so that no import breaks.\nexport { IMPACT_CONTRACT, checkHit } from '@adatechnology/game-contracts'\nexport type { DestructibleImpl, ImpactContract } from '@adatechnology/game-contracts'\n\nexport interface CheckOptions {\n /** Ids of custom materials the impl uses besides the built-in `MATERIALS`. */\n materials?: string[]\n}\n\n/**\n * `checkDestructible` of `@adatechnology/game-contracts`, with the built-in `MATERIALS` (plus `options.materials`) as the known material ids.\n * It hits the impl (and calls `reset()` afterwards when there is one).\n */\nexport const checkDestructible = (impl: DestructibleImpl, options: CheckOptions = {}): { ok: boolean; problems: string[] } => {\n const contractOptions: ContractCheckOptions = { materials: [...MATERIALS.map((m) => m.id), ...(options.materials ?? [])] }\n return check(impl, contractOptions)\n}\n","import type * as THREE_NS from 'three'\nimport { guessMaterial, inferJoints } from '@adatechnology/game-destruction'\nimport type { JointKind, JointSpec, MaterialId, PartSpec } from '@adatechnology/game-destruction'\nimport type { Mesh, Object3D, SpecFromObjectOptions, SpecFromObjectResult, ThreeNamespace } from './types'\n\ninterface Entry {\n key: string\n meshes: Mesh[]\n box: THREE_NS.Box3\n material: MaterialId\n explicitId: string | null\n fill?: number\n hollow: boolean\n tags?: string[]\n /** Part id wished by the host (`userData.destructionPart`). */\n fixedId: boolean\n name: string\n /** `parent` / `kind` of an object-valued `destructionPart`: the part is attached to `parent` by a joint of that kind. */\n parent?: string\n jointKind?: JointKind\n}\n\n/** What `userData.destructionPart` says: a string (the part id) or an object `{ id, material?, parent?, kind? }`. */\nexport interface PartHint {\n id: string\n material?: string\n parent?: string\n kind?: string\n}\n\n/** Read `userData.destructionPart` (string or object with an `id`); null when it names no part. */\nexport function partHintOf(value: unknown): PartHint | null {\n if (typeof value === 'string') return value ? { id: value } : null\n if (!value || typeof value !== 'object') return null\n const v = value as Record<string, unknown>\n if (typeof v.id !== 'string' || !v.id) return null\n const str = (x: unknown): string | undefined => (typeof x === 'string' && x ? x : undefined)\n return { id: v.id, material: str(v.material), parent: str(v.parent), kind: str(v.kind) }\n}\n\nconst METAL = /steel|iron|metal|alumin|copper|brass|bronze|tin|titan|chrome|zinc|lead/i\n\nfunction sanitizeId(name: string): string {\n let id = name\n .trim()\n .replace(/([a-z0-9])([A-Z])/g, '$1_$2')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '_')\n .replace(/^_+|_+$/g, '')\n if (id && /^[0-9]/.test(id)) id = `p_${id}`\n return id\n}\n\nfunction isMesh(o: Object3D): o is Mesh {\n return (o as Mesh).isMesh === true\n}\n\nfunction sizeOf(box: THREE_NS.Box3): [number, number, number] {\n return [box.max.x - box.min.x, box.max.y - box.min.y, box.max.z - box.min.z]\n}\n\n/**\n * Build a `DestructibleSpec` from a three.js object: one part per mesh (meshes sharing `userData.destructionPart` become one part), each an\n * axis-aligned box in the root's space (rotation omitted), with the material taken from `options.materialOf`, `userData.destructionMaterial`\n * or a guess from the names. Tiny meshes are merged into the part they overlap most; joints are inferred from touching parts.\n *\n * Meshes that are invisible or have `userData.destructionIgnore` (or sit under such an object) are skipped. The result also tells which meshes\n * became which part, ready to be passed to `bind`.\n */\nexport function specFromObject3D(THREE: ThreeNamespace, root: Object3D, options: SpecFromObjectOptions = {}): SpecFromObjectResult {\n const fallback = options.fallback ?? 'plastic'\n const minSize = options.minSize ?? 0.015\n root.updateMatrixWorld(true)\n const inv = root.matrixWorld.clone().invert()\n\n // 1. collect meshes -> entries (grouped by an explicit part name)\n const entries: Entry[] = []\n const byKey = new Map<string, Entry>()\n const visit = (o: Object3D): void => {\n if (!o.visible || o.userData?.destructionIgnore) return\n if (isMesh(o) && o.geometry) {\n const geom = o.geometry\n if (!geom.boundingBox) geom.computeBoundingBox()\n if (geom.boundingBox && !geom.boundingBox.isEmpty()) {\n const box = geom.boundingBox.clone().applyMatrix4(new THREE.Matrix4().multiplyMatrices(inv, o.matrixWorld))\n const mats = Array.isArray(o.material) ? o.material : [o.material]\n const matName = mats[0]?.name ?? ''\n const ud = o.userData ?? {}\n const hint = partHintOf(ud.destructionPart)\n const material =\n options.materialOf?.(o) ??\n (typeof ud.destructionMaterial === 'string' ? (ud.destructionMaterial as string) : undefined) ??\n hint?.material ??\n guessMaterial(`${o.name} ${matName} ${typeof ud.material === 'string' ? ud.material : ''}`) ??\n fallback\n const explicit = hint?.id ?? null\n const hollow = ud.hollow === true || /hollow/i.test(o.name)\n const fill = options.fillOf?.(o, material)\n const tags = Array.isArray(ud.destructionTags) ? (ud.destructionTags as string[]) : undefined\n const existing = explicit ? byKey.get(explicit) : undefined\n if (existing) {\n const before = sizeOf(existing.box)\n const bv = before[0] * before[1] * before[2]\n const ns = sizeOf(box)\n existing.box.union(box)\n existing.meshes.push(o)\n if (ns[0] * ns[1] * ns[2] > bv) existing.material = material\n existing.hollow = existing.hollow || hollow\n if (fill !== undefined) existing.fill = fill\n } else {\n const e: Entry = { key: explicit ?? `m${entries.length}`, meshes: [o], box, material, explicitId: explicit, fill, hollow, tags, fixedId: !!explicit, name: o.name, parent: hint?.parent, jointKind: hint?.kind as JointKind | undefined }\n entries.push(e)\n if (explicit) byKey.set(explicit, e)\n }\n }\n }\n for (const c of o.children) visit(c)\n }\n visit(root)\n\n // 2. merge tiny meshes into the touching part they overlap most\n const eps = 0.005\n const overlapVolume = (a: THREE_NS.Box3, b: THREE_NS.Box3): number => {\n const x = Math.min(a.max.x, b.max.x) - Math.max(a.min.x, b.min.x) + eps\n const y = Math.min(a.max.y, b.max.y) - Math.max(a.min.y, b.min.y) + eps\n const z = Math.min(a.max.z, b.max.z) - Math.max(a.min.z, b.min.z) + eps\n return x > 0 && y > 0 && z > 0 ? x * y * z : 0\n }\n const largest = (e: Entry) => Math.max(...sizeOf(e.box))\n const kept: Entry[] = []\n const tiny: Entry[] = []\n for (const e of entries) (!e.fixedId && largest(e) < minSize ? tiny : kept).push(e)\n for (const t of tiny) {\n let best: Entry | null = null\n let bestV = 0\n for (const k of kept) {\n const v = overlapVolume(t.box, k.box)\n if (v > bestV) {\n bestV = v\n best = k\n }\n }\n if (!best) {\n // not touching anything: join the nearest part within a small distance\n let bd = 0.05\n for (const k of kept) {\n const d = k.box.distanceToPoint(t.box.getCenter(new THREE.Vector3()))\n if (d < bd) {\n bd = d\n best = k\n }\n }\n }\n if (best) {\n best.meshes.push(...t.meshes) // the box stays: a tiny mesh sits inside or on the surface of its part\n } else {\n kept.push(t) // isolated crumb: keep it as its own part\n }\n }\n\n // 3. ids, parts\n const used = new Set<string>()\n const parts: PartSpec[] = []\n const meshesOf: Record<string, Object3D[]> = {}\n let counter = 0\n for (const e of kept) {\n counter++\n let base = (e.explicitId ? sanitizeId(e.explicitId) : '') || sanitizeId(e.name) || `part_${counter}`\n let id = base\n for (let n = 2; used.has(id); n++) id = `${base}_${n}`\n used.add(id)\n base = id\n const size = sizeOf(e.box)\n const center = e.box.getCenter(new THREE.Vector3())\n const part: PartSpec = {\n id,\n material: e.material,\n shape: { kind: 'box', size: [Math.max(size[0], 0.002), Math.max(size[1], 0.002), Math.max(size[2], 0.002)] },\n position: [center.x, center.y, center.z],\n }\n const minSide = Math.min(...size)\n if (e.fill !== undefined) part.fill = e.fill\n else if (e.hollow) part.fill = 0.05\n if (minSide < 0.01 && METAL.test(e.material)) {\n part.fill = e.fill ?? 1\n part.thickness = Math.max(minSide, 0.0005)\n }\n if (e.tags) part.tags = [...e.tags]\n const ao = options.anchorBelow ?? -1\n if (ao >= 0 && e.box.min.y <= ao) part.anchored = true\n parts.push(part)\n meshesOf[id] = e.meshes\n }\n\n const spec: SpecFromObjectResult['spec'] = { parts }\n if (options.name !== undefined) spec.name = options.name\n else if (root.name) spec.name = root.name\n if (options.tags) spec.tags = [...options.tags]\n spec.joints = options.joints === false ? [] : inferJoints(parts)\n // explicit attachments from `destructionPart: { parent, kind }` win over the inferred joint between the same two parts\n if (options.joints !== false) {\n const finalId = new Map<Entry, string>()\n kept.forEach((e, i) => finalId.set(e, parts[i].id))\n const idOf = new Map<string, string>()\n for (const e of kept) if (e.explicitId) idOf.set(e.explicitId, finalId.get(e)!)\n const joints: JointSpec[] = spec.joints\n for (const e of kept) {\n if (!e.parent) continue\n const id = finalId.get(e)!\n const parent = idOf.get(e.parent) ?? (used.has(sanitizeId(e.parent)) ? sanitizeId(e.parent) : null)\n if (!parent || parent === id) continue\n const at = joints.findIndex((j) => (j.a === parent && j.b === id) || (j.a === id && j.b === parent))\n const kind = e.jointKind ?? (at >= 0 ? joints[at].kind : 'resting')\n const joint: JointSpec = { id: `${parent}~${id}`, a: parent, b: id, kind }\n if (at >= 0) joints[at] = joint\n else joints.push(joint)\n }\n }\n return { spec, parts: meshesOf }\n}\n","import { mixHex } from './math'\n\n/** Blackbody-ish stops: [temperature °C, colour]. Luminance grows with the temperature. */\nconst STOPS: Array<[number, number]> = [\n [450, 0x000000],\n [500, 0x2a0400],\n [650, 0x8a1500],\n [800, 0xd84a08],\n [950, 0xff8c14],\n [1100, 0xffc23a],\n [1250, 0xffe8a0],\n [1400, 0xffffff],\n]\n\n/**\n * Glow colour of a hot surface as `0xRRGGBB`: black (no glow) below about 450 °C, dark red around 500, orange around 800, yellow around 1100 and\n * white from 1400 °C. Brightness never decreases as the temperature rises. Use it as an emissive colour.\n */\nexport function heatColor(temperatureC: number): number {\n if (!(temperatureC > STOPS[0][0])) return 0\n const last = STOPS[STOPS.length - 1]\n if (temperatureC >= last[0]) return last[1]\n for (let i = 1; i < STOPS.length; i++) {\n const [t1, c1] = STOPS[i]\n if (temperatureC <= t1) {\n const [t0, c0] = STOPS[i - 1]\n return mixHex(c0, c1, (temperatureC - t0) / (t1 - t0))\n }\n }\n return last[1]\n}\n","import type * as THREE_NS from 'three'\nimport type { Dent } from '@adatechnology/game-destruction'\nimport { heatColor } from './heat'\nimport type { Decal, HostVis, MeshVis, PartVis } from './internal'\nimport { clamp, createRng, lerp } from './math'\n\ntype Mesh = THREE_NS.Mesh\n\nconst POS_ATTR = 'position'\n\nfunction positionAttr(geo: THREE_NS.BufferGeometry): THREE_NS.BufferAttribute | null {\n const a = geo.getAttribute(POS_ATTR) as THREE_NS.BufferAttribute | THREE_NS.InterleavedBufferAttribute | undefined\n return a && !(a as THREE_NS.InterleavedBufferAttribute).isInterleavedBufferAttribute ? (a as THREE_NS.BufferAttribute) : null\n}\n\nfunction materialsOf(m: THREE_NS.Material | THREE_NS.Material[]): THREE_NS.Material[] {\n return Array.isArray(m) ? m : [m]\n}\n\nfunction colorOf(m: THREE_NS.Material): THREE_NS.Color | null {\n const c = (m as THREE_NS.MeshStandardMaterial).color\n return c && (c as THREE_NS.Color).isColor ? c : null\n}\n\n/** Shared resources for decals (one geometry, a few materials) created lazily and disposed together. */\nexport class DecalKit {\n private disc: THREE_NS.CircleGeometry | null = null\n private discMat: THREE_NS.MeshBasicMaterial | null = null\n private lineMats = new Map<number, THREE_NS.LineBasicMaterial>()\n constructor(private readonly THREE: typeof THREE_NS) {}\n\n discGeometry(): THREE_NS.CircleGeometry {\n return (this.disc ??= new this.THREE.CircleGeometry(1, 14))\n }\n\n discMaterial(): THREE_NS.MeshBasicMaterial {\n return (this.discMat ??= new this.THREE.MeshBasicMaterial({\n color: 0x050505,\n transparent: true,\n opacity: 0.92,\n depthWrite: false,\n polygonOffset: true,\n polygonOffsetFactor: -4,\n polygonOffsetUnits: -4,\n }))\n }\n\n lineMaterial(color: number): THREE_NS.LineBasicMaterial {\n let m = this.lineMats.get(color)\n if (!m) {\n m = new this.THREE.LineBasicMaterial({ color, transparent: true, opacity: 0.9, depthWrite: false })\n this.lineMats.set(color, m)\n }\n return m\n }\n\n dispose(): void {\n this.disc?.dispose()\n this.discMat?.dispose()\n for (const m of this.lineMats.values()) m.dispose()\n this.disc = null\n this.discMat = null\n this.lineMats.clear()\n }\n}\n\nconst tmpV = (THREE: typeof THREE_NS) => new THREE.Vector3()\n\n/** Mesh-level visual changes: copy-on-write geometry and materials, dents, decals, char, glow, melt, hiding, restoring. */\nexport class Visuals {\n readonly kit: DecalKit\n private readonly a: THREE_NS.Vector3\n private readonly b: THREE_NS.Vector3\n private readonly m4: THREE_NS.Matrix4\n private readonly box: THREE_NS.Box3\n private readonly t: THREE_NS.Vector3\n private readonly lbox: THREE_NS.Box3\n /** Total live decals across meshes (kept in sync by add/remove). */\n decalCount = 0\n\n constructor(\n private readonly THREE: typeof THREE_NS,\n private readonly lowQuality: boolean,\n private readonly decalCap: number,\n ) {\n this.kit = new DecalKit(THREE)\n this.a = tmpV(THREE)\n this.b = tmpV(THREE)\n this.m4 = new THREE.Matrix4()\n this.box = new THREE.Box3()\n this.t = tmpV(THREE)\n this.lbox = new THREE.Box3()\n }\n\n /**\n * @param range vertex range `[v0, v1)` of a merged mesh this part is drawn by (see `MeshRange`); `hosts` caches the shared state per mesh for one `bind`.\n */\n makeMeshVis(mesh: Mesh, home: string, range?: [number, number], hosts?: Map<Mesh, HostVis>): MeshVis {\n let host: HostVis | null = null\n if (range) {\n host = hosts?.get(mesh) ?? null\n if (!host) {\n host = { mesh, origGeometry: mesh.geometry, origMaterial: mesh.material, ownGeo: false, work: null, ownMat: false, sharers: [] }\n hosts?.set(mesh, host)\n }\n }\n const mv: MeshVis = {\n mesh,\n home,\n origGeometry: mesh.geometry,\n origMaterial: mesh.material,\n origVisible: mesh.visible,\n origParent: mesh.parent,\n origPosition: mesh.position.clone(),\n origQuaternion: mesh.quaternion.clone(),\n origScale: mesh.scale.clone(),\n ownMat: false,\n ownGeo: false,\n work: null,\n baseColors: null,\n decals: [],\n meltApplied: 0,\n bottomY: 0,\n moved: false,\n host,\n range: range ? [range[0], range[1]] : null,\n merged: host && range ? { host, range: [range[0], range[1]] } : null,\n loose: false,\n collapsed: false,\n hidden: false,\n touched: false,\n charK: 0,\n charHex: 0,\n glowHex: 0,\n glowK: 0,\n }\n host?.sharers.push(mv)\n return mv\n }\n\n vertexCount(mv: MeshVis): number {\n if (mv.range) return mv.range[1] - mv.range[0]\n return mv.mesh.geometry.getAttribute(POS_ATTR)?.count ?? 0\n }\n\n /** Is the part drawn? (A ranged part shares its mesh, so `mesh.visible` says nothing about it.) */\n shown(mv: MeshVis): boolean {\n return mv.host ? !mv.hidden : mv.mesh.visible\n }\n\n /** Box of the part's vertices in the space of its mesh (the whole geometry for a plain mesh). */\n localBox(mv: MeshVis, out: THREE_NS.Box3): THREE_NS.Box3 {\n out.makeEmpty()\n if (!mv.host || !mv.range) {\n const geo = mv.mesh.geometry\n if (!geo.boundingBox) geo.computeBoundingBox()\n return geo.boundingBox ? out.copy(geo.boundingBox) : out\n }\n const arr = mv.host.work ?? (positionAttr(mv.mesh.geometry)?.array as unknown as ArrayLike<number> | undefined)\n if (!arr) return out\n for (let i = mv.range[0] * 3; i < mv.range[1] * 3; i += 3) out.expandByPoint(this.t.set(arr[i], arr[i + 1], arr[i + 2]))\n return out\n }\n\n /** Box of the part in world space. */\n worldBox(mv: MeshVis, out: THREE_NS.Box3): THREE_NS.Box3 {\n if (!mv.host) return out.setFromObject(mv.mesh)\n this.localBox(mv, out)\n mv.mesh.updateWorldMatrix(true, false)\n return out.isEmpty() ? out : out.applyMatrix4(mv.mesh.matrixWorld)\n }\n\n private readonly tints = new WeakMap<MeshVis, THREE_NS.Color | null>()\n\n /**\n * Average vertex colour of a mesh whose material uses `vertexColors` (computed once per mesh, from its original geometry), else null. Fragments\n * and debris of such a mesh multiply it by `material.color`; without it they come out white.\n */\n vertexTint(mv: MeshVis): THREE_NS.Color | null {\n if (this.tints.has(mv)) return this.tints.get(mv) ?? null\n let tint: THREE_NS.Color | null = null\n const used = materialsOf(mv.origMaterial).some((m) => (m as THREE_NS.MeshStandardMaterial).vertexColors === true)\n const attr = used ? (mv.origGeometry.getAttribute('color') as THREE_NS.BufferAttribute | undefined) : undefined\n if (attr && attr.count > 0 && attr.itemSize >= 3) {\n // a ranged part averages its own vertices only\n const from = mv.merged ? mv.merged.range[0] : 0\n const to = mv.merged ? Math.min(attr.count, mv.merged.range[1]) : attr.count\n let r = 0, g = 0, b = 0\n for (let i = from; i < to; i++) {\n r += attr.getX(i)\n g += attr.getY(i)\n b += attr.getZ(i)\n }\n const n = Math.max(1, to - from)\n tint = new this.THREE.Color(r / n, g / n, b / n)\n }\n this.tints.set(mv, tint)\n return tint\n }\n\n /** Colour of the original material (before any char) times the vertex tint, `0xRRGGBB`, or -1. */\n baseColorHex(mv: MeshVis): number {\n const c = mv.baseColors?.[0] ?? colorOf(materialsOf(mv.origMaterial)[0])\n if (!c) return -1\n const tint = this.vertexTint(mv)\n return tint ? c.clone().multiply(tint).getHex() : c.getHex()\n }\n\n /** Is the original material transparent? */\n isTransparent(mv: MeshVis): boolean {\n return materialsOf(mv.origMaterial).some((m) => m.transparent && m.opacity < 1)\n }\n\n ownMaterial(mv: MeshVis): void {\n if (mv.ownMat) return\n mv.ownMat = true\n const src = materialsOf(mv.origMaterial)\n const cloned = src.map((m) => m.clone())\n mv.baseColors = src.map((m) => colorOf(m)?.clone() ?? new this.THREE.Color(0x888888))\n mv.mesh.material = Array.isArray(mv.origMaterial) ? cloned : cloned[0]\n }\n\n /** Clone the geometry (vertex edits); false when its positions cannot be edited. */\n ownGeometry(mv: MeshVis): boolean {\n if (mv.host) return this.ownHostGeometry(mv.host)\n if (mv.ownGeo) return true\n const pos = positionAttr(mv.mesh.geometry)\n if (!pos) return false\n mv.ownGeo = true\n const g = mv.mesh.geometry.clone()\n mv.mesh.geometry = g\n mv.work = new Float32Array((positionAttr(g) as THREE_NS.BufferAttribute).array as ArrayLike<number>)\n return true\n }\n\n private ownHostGeometry(host: HostVis): boolean {\n if (host.ownGeo) return true\n const pos = positionAttr(host.mesh.geometry)\n if (!pos) return false\n host.ownGeo = true\n const g = host.mesh.geometry.clone()\n host.mesh.geometry = g\n host.work = new Float32Array((positionAttr(g) as THREE_NS.BufferAttribute).array as ArrayLike<number>)\n return true\n }\n\n // ------------------------------------------------------------------------------------------------------------------------------------\n // Dents\n // ------------------------------------------------------------------------------------------------------------------------------------\n\n /**\n * Push the vertices of the mesh within `dent.radius` of `worldPoint` inwards along `worldNormal` by `depth * (1 - (d/r)^2)^2`.\n * Returns false when nothing was changed (too far, low quality on a heavy mesh, or no editable positions).\n */\n applyDent(mv: MeshVis, worldPoint: THREE_NS.Vector3, worldNormal: THREE_NS.Vector3, dent: Dent): boolean {\n const mesh = mv.mesh\n if (this.lowQuality && this.vertexCount(mv) > 20000) return false\n mesh.updateWorldMatrix(true, false)\n const s = mesh.matrixWorld.getMaxScaleOnAxis() || 1\n const lp = mesh.worldToLocal(this.a.copy(worldPoint))\n const ln = this.b.copy(worldNormal).transformDirection(this.m4.copy(mesh.matrixWorld).invert()).normalize()\n const r = dent.radius / s\n const depth = dent.depth / s\n // quick reject: the point must be near the mesh bounds\n const geo = mesh.geometry\n let bb: THREE_NS.Box3 | null\n if (mv.host) bb = this.localBox(mv, this.lbox)\n else {\n if (!geo.boundingBox) geo.computeBoundingBox()\n bb = geo.boundingBox\n }\n if (bb && !bb.isEmpty() && bb.distanceToPoint(lp) > r) return false\n if (!this.ownGeometry(mv)) return false\n const work = (mv.host ? mv.host.work : mv.work) as Float32Array\n const pos = positionAttr(mesh.geometry) as THREE_NS.BufferAttribute\n const r2 = r * r\n let touched = false\n const px = lp.x\n const py = lp.y\n const pz = lp.z\n const nx = ln.x\n const ny = ln.y\n const nz = ln.z\n const from = mv.range ? mv.range[0] * 3 : 0\n const to = mv.range ? mv.range[1] * 3 : work.length\n for (let i = from; i < to; i += 3) {\n const dx = work[i] - px\n const dy = work[i + 1] - py\n const dz = work[i + 2] - pz\n const d2 = dx * dx + dy * dy + dz * dz\n if (d2 >= r2) continue\n const f = 1 - d2 / r2\n const k = depth * f * f\n work[i] -= nx * k\n work[i + 1] -= ny * k\n work[i + 2] -= nz * k\n touched = true\n }\n if (!touched) return false\n mv.touched = true\n this.writePositions(mv, pos)\n return true\n }\n\n /** Copy `work` (dents) into the geometry, adding the melt sag when the mesh is melting. */\n private writePositions(mv: MeshVis, pos: THREE_NS.BufferAttribute): void {\n if (mv.host) {\n this.rebuildHost(mv.host)\n return\n }\n const work = mv.work as Float32Array\n const arr = pos.array as unknown as Float32Array\n const melt = mv.meltApplied\n if (melt <= 0 || (this.lowQuality && work.length / 3 > 20000)) {\n arr.set(work)\n } else {\n const geo = mv.mesh.geometry\n if (!geo.boundingBox) geo.computeBoundingBox()\n const bb = geo.boundingBox as THREE_NS.Box3\n const h = Math.max(1e-4, bb.max.y - bb.min.y)\n const minY = bb.min.y\n for (let i = 0, v = 0; i < work.length; i += 3, v++) {\n const n = ((Math.imul(v + 1, 2654435761) >>> 0) % 1000) / 1000\n const n2 = ((Math.imul(v + 7, 40503) >>> 0) % 1000) / 1000\n const lowness = 1 - (work[i + 1] - minY) / h // 1 at the bottom, 0 at the top\n const spread = 1 + 0.12 * melt * (0.4 + n2) * (0.3 + lowness)\n arr[i] = work[i] * spread\n arr[i + 2] = work[i + 2] * spread\n arr[i + 1] = Math.max(minY, work[i + 1] - n * melt * h * 0.08)\n }\n }\n pos.needsUpdate = true\n const geo = mv.mesh.geometry\n geo.computeVertexNormals()\n geo.boundingBox = null\n geo.boundingSphere = null\n }\n\n // ------------------------------------------------------------------------------------------------------------------------------------\n // Decals\n // ------------------------------------------------------------------------------------------------------------------------------------\n\n /** Decal pose in mesh space: position and a rotation taking +Z to the surface normal. */\n private decalPose(mesh: Mesh, worldPoint: THREE_NS.Vector3, worldNormal: THREE_NS.Vector3, lift: number): { s: number; pos: THREE_NS.Vector3; quat: THREE_NS.Quaternion } {\n mesh.updateWorldMatrix(true, false)\n const s = mesh.matrixWorld.getMaxScaleOnAxis() || 1\n const ln = worldNormal.clone().transformDirection(this.m4.copy(mesh.matrixWorld).invert()).normalize()\n const pos = mesh.worldToLocal(worldPoint.clone()).addScaledVector(ln, lift / s)\n const quat = new this.THREE.Quaternion().setFromUnitVectors(new this.THREE.Vector3(0, 0, 1), ln)\n return { s, pos, quat }\n }\n\n private addDecal(mv: MeshVis, decal: Decal): void {\n mv.mesh.add(decal.obj)\n mv.decals.push(decal)\n this.decalCount++\n while (mv.decals.length > 48 || this.decalCount > this.decalCap) {\n const old = mv.decals.shift()\n if (!old) break\n this.disposeDecal(old)\n }\n }\n\n private disposeDecal(d: Decal): void {\n d.obj.parent?.remove(d.obj)\n d.geometry?.dispose()\n this.decalCount = Math.max(0, this.decalCount - 1)\n }\n\n addHole(mv: MeshVis, worldPoint: THREE_NS.Vector3, worldNormal: THREE_NS.Vector3, diameter: number): void {\n const { s, pos, quat } = this.decalPose(mv.mesh, worldPoint, worldNormal, 0.0025)\n const disc = new this.THREE.Mesh(this.kit.discGeometry(), this.kit.discMaterial())\n disc.position.copy(pos)\n disc.quaternion.copy(quat)\n disc.scale.setScalar((Math.max(0.004, diameter) * 0.5 * 1.25) / s)\n disc.renderOrder = 2\n this.addDecal(mv, { obj: disc })\n }\n\n /** A star of jagged lines: `rays` rays up to `length` metres, seeded so the same event always draws the same star. */\n addCrack(mv: MeshVis, worldPoint: THREE_NS.Vector3, worldNormal: THREE_NS.Vector3, rays: number, length: number, color: number, seed: number): void {\n const { s, pos, quat } = this.decalPose(mv.mesh, worldPoint, worldNormal, 0.002)\n const rnd = createRng(seed)\n const verts: number[] = []\n const n = clamp(Math.round(rays), 3, 16)\n const L = Math.max(0.02, length) / s\n for (let r = 0; r < n; r++) {\n let ang = (r / n) * Math.PI * 2 + (rnd() - 0.5) * 0.5\n let x = 0\n let y = 0\n const segs = 3\n const total = L * (0.6 + 0.4 * rnd())\n for (let k = 0; k < segs; k++) {\n const step = (total / segs) * (0.7 + 0.6 * rnd())\n ang += (rnd() - 0.5) * 0.45\n const nx = x + Math.cos(ang) * step\n const ny = y + Math.sin(ang) * step\n verts.push(x, y, 0, nx, ny, 0)\n if (k > 0 && rnd() < 0.5) {\n const ba = ang + (rnd() < 0.5 ? -1 : 1) * (0.5 + rnd() * 0.5)\n const bl = step * (0.6 + 0.6 * rnd())\n verts.push(nx, ny, 0, nx + Math.cos(ba) * bl, ny + Math.sin(ba) * bl, 0)\n }\n x = nx\n y = ny\n }\n }\n // a faint ring around the impact\n const ringR = L * 0.12\n for (let k = 0; k < 8; k++) {\n const a0 = (k / 8) * Math.PI * 2\n const a1 = ((k + 1) / 8) * Math.PI * 2\n if (rnd() < 0.75) verts.push(Math.cos(a0) * ringR, Math.sin(a0) * ringR, 0, Math.cos(a1) * ringR, Math.sin(a1) * ringR, 0)\n }\n const geo = new this.THREE.BufferGeometry()\n geo.setAttribute('position', new this.THREE.Float32BufferAttribute(verts, 3))\n const lines = new this.THREE.LineSegments(geo, this.kit.lineMaterial(color))\n lines.position.copy(pos)\n lines.quaternion.copy(quat)\n lines.renderOrder = 2\n this.addDecal(mv, { obj: lines, geometry: geo })\n }\n\n // ------------------------------------------------------------------------------------------------------------------------------------\n // Char and heat glow\n // ------------------------------------------------------------------------------------------------------------------------------------\n\n setChar(pv: PartVis, char: number, charColor: number): void {\n if (Math.abs(char - pv.charApplied) < 0.004) return\n pv.charApplied = char\n const target = new this.THREE.Color(charColor)\n for (const mv of pv.meshes) {\n if (mv.host) {\n mv.charK = clamp(char, 0, 1)\n mv.charHex = charColor\n this.paint(mv)\n continue\n }\n this.ownMaterial(mv)\n const mats = materialsOf(mv.mesh.material)\n mats.forEach((m, i) => {\n const c = colorOf(m)\n const base = mv.baseColors?.[i]\n if (c && base) c.copy(base).lerp(target, clamp(char, 0, 1))\n })\n }\n }\n\n /** Emissive glow of a hot part. `temperature` below the glow threshold switches it off. */\n setGlow(pv: PartVis, temperatureC: number): void {\n const hex = heatColor(temperatureC)\n const key = hex === 0 ? 0 : Math.round(temperatureC / 5) * 5\n if (key === pv.glowApplied) return\n pv.glowApplied = key\n const intensity = hex === 0 ? 0 : clamp(0.35 + (temperatureC - 450) / 900, 0, 1.4)\n for (const mv of pv.meshes) {\n if (mv.host) {\n mv.glowHex = hex\n mv.glowK = intensity\n this.paint(mv)\n continue\n }\n this.ownMaterial(mv)\n for (const m of materialsOf(mv.mesh.material)) {\n const e = (m as THREE_NS.MeshStandardMaterial).emissive\n if (e && (e as THREE_NS.Color).isColor) {\n e.setHex(hex)\n ;(m as THREE_NS.MeshStandardMaterial).emissiveIntensity = intensity\n }\n }\n }\n }\n\n // ------------------------------------------------------------------------------------------------------------------------------------\n // Melt\n // ------------------------------------------------------------------------------------------------------------------------------------\n\n /** Sag the part: shorter in Y (down to 25 %), a bit wider, vertices pushed down with noise, the bottom stays put. */\n setMelt(pv: PartVis, melt: number): void {\n const m = clamp(melt, 0, 1)\n if (Math.abs(m - pv.meltApplied) < 0.01) return\n pv.meltApplied = m\n for (const mv of pv.meshes) {\n const mesh = mv.mesh\n if (mv.host) {\n // a range of a shared mesh cannot scale the mesh: squash and widen its own vertices\n mv.meltApplied = m\n mv.touched = true\n if (!(this.lowQuality && this.vertexCount(mv) > 20000) && this.ownGeometry(mv)) this.rebuildHost(mv.host)\n continue\n }\n if (mv.meltApplied === 0 && m > 0) {\n mesh.updateWorldMatrix(true, false)\n mv.bottomY = this.box.setFromObject(mesh).min.y\n }\n mv.meltApplied = m\n mv.moved = true\n const sy = lerp(1, 0.25, m)\n const sxz = 1 + 0.18 * m\n mesh.scale.set(mv.origScale.x * sxz, mv.origScale.y * sy, mv.origScale.z * sxz)\n mesh.position.copy(mv.origPosition)\n mesh.updateWorldMatrix(true, false)\n const bottom = this.box.setFromObject(mesh).min.y\n const dy = mv.bottomY - bottom\n if (Math.abs(dy) > 1e-6 && mesh.parent) {\n mesh.parent.updateWorldMatrix(true, false)\n const inv = this.m4.copy(mesh.parent.matrixWorld).invert()\n this.a.set(0, dy, 0).applyMatrix4(inv)\n this.b.set(0, 0, 0).applyMatrix4(inv)\n mesh.position.copy(mv.origPosition).add(this.a.sub(this.b))\n }\n if (!(this.lowQuality && this.vertexCount(mv) > 20000) && this.ownGeometry(mv)) {\n this.writePositions(mv, positionAttr(mesh.geometry) as THREE_NS.BufferAttribute)\n }\n }\n }\n\n // ------------------------------------------------------------------------------------------------------------------------------------\n // Visibility and restore\n // ------------------------------------------------------------------------------------------------------------------------------------\n\n hide(pv: PartVis, reason: PartVis['gone']): void {\n pv.gone = reason\n for (const mv of pv.meshes) {\n if (!mv.host || mv.loose) {\n mv.mesh.visible = false\n mv.hidden = true\n continue\n }\n // a range of a shared mesh: collapse its vertices to a point and hide what was drawn on it\n mv.hidden = true\n for (const d of mv.decals) d.obj.visible = false\n this.collapse(mv)\n }\n }\n\n // ------------------------------------------------------------------------------------------------------------------------------------\n // Parts drawn by a vertex range of a merged mesh\n // ------------------------------------------------------------------------------------------------------------------------------------\n\n /** Collapse the part's vertices in the merged mesh to one point (its triangles become degenerate and vanish). */\n private collapse(mv: MeshVis): void {\n if (!mv.merged || !this.ownHostGeometry(mv.merged.host)) return\n mv.collapsed = true\n mv.touched = true\n this.rebuildHost(mv.merged.host)\n }\n\n /** Write `work` into the geometry again with the melt of ranged parts and the collapsed ranges applied. */\n private rebuildHost(host: HostVis): void {\n const geo = host.mesh.geometry\n const pos = positionAttr(geo)\n const work = host.work\n if (!pos || !work) return\n const arr = pos.array as unknown as Float32Array\n arr.set(work)\n for (const s of host.sharers) {\n if (s.merged && s.merged.host === host) {\n if (s.collapsed) this.collapseRange(arr, s.merged.range)\n else if (s.meltApplied > 0 && !s.loose) this.meltRange(arr, work, s.merged.range, s.meltApplied)\n } else if (s.host === host && s.range && s.meltApplied > 0) this.meltRange(arr, work, s.range, s.meltApplied)\n }\n pos.needsUpdate = true\n geo.computeVertexNormals()\n geo.boundingBox = null\n geo.boundingSphere = null\n }\n\n private collapseRange(arr: Float32Array, range: [number, number]): void {\n const x = arr[range[0] * 3]\n const y = arr[range[0] * 3 + 1]\n const z = arr[range[0] * 3 + 2]\n for (let i = range[0] * 3; i < range[1] * 3; i += 3) {\n arr[i] = x\n arr[i + 1] = y\n arr[i + 2] = z\n }\n }\n\n /** Melt of one range: shorter in Y (down to 25 %) with the bottom fixed, a bit wider around its centre, with the same noise as a whole mesh. */\n private meltRange(arr: Float32Array, work: Float32Array, range: [number, number], melt: number): void {\n let minY = Infinity\n let maxY = -Infinity\n let cx = 0\n let cz = 0\n const n = Math.max(1, range[1] - range[0])\n for (let i = range[0] * 3; i < range[1] * 3; i += 3) {\n minY = Math.min(minY, work[i + 1])\n maxY = Math.max(maxY, work[i + 1])\n cx += work[i]\n cz += work[i + 2]\n }\n cx /= n\n cz /= n\n const h = Math.max(1e-4, maxY - minY)\n const sy = lerp(1, 0.25, melt)\n const sxz = 1 + 0.18 * melt\n for (let i = range[0] * 3, v = 0; i < range[1] * 3; i += 3, v++) {\n const noise = ((Math.imul(v + 1, 2654435761) >>> 0) % 1000) / 1000\n arr[i] = cx + (work[i] - cx) * sxz\n arr[i + 2] = cz + (work[i + 2] - cz) * sxz\n arr[i + 1] = Math.max(minY, minY + (work[i + 1] - minY) * sy - noise * melt * h * 0.04)\n }\n }\n\n /** The colour attribute of the host's current geometry (created white when there is none) with vertex colours switched on in its material. */\n private ensureColors(host: HostVis): THREE_NS.BufferAttribute | null {\n if (!this.ownHostGeometry(host)) return null\n const geo = host.mesh.geometry\n let attr = geo.getAttribute('color') as THREE_NS.BufferAttribute | undefined\n if (!attr) {\n const n = geo.getAttribute(POS_ATTR).count\n attr = new this.THREE.BufferAttribute(new Float32Array(n * 3).fill(1), 3)\n geo.setAttribute('color', attr)\n }\n if (!host.ownMat) {\n host.ownMat = true\n const src = materialsOf(host.mesh.material)\n const cloned = src.map((m) => m.clone())\n host.mesh.material = Array.isArray(host.mesh.material) ? cloned : cloned[0]\n }\n for (const m of materialsOf(host.mesh.material)) {\n if ((m as THREE_NS.MeshStandardMaterial).vertexColors !== true) {\n ;(m as THREE_NS.MeshStandardMaterial).vertexColors = true\n m.needsUpdate = true\n }\n }\n return attr\n }\n\n /** Colour of the part's vertices from the original colours, its char and its heat. Only the part's range is written. */\n private paint(mv: MeshVis): void {\n if (!mv.host || !mv.range || !mv.merged) return\n const idle = mv.charK <= 0 && mv.glowK <= 0\n if (idle && !mv.touched) return\n const attr = this.ensureColors(mv.host)\n if (!attr) return\n const size = attr.itemSize\n const arr = attr.array as unknown as Float32Array\n const orig = mv.merged.host.origGeometry.getAttribute('color') as THREE_NS.BufferAttribute | undefined\n const oSize = orig?.itemSize ?? 3\n const oArr = orig?.array as unknown as ArrayLike<number> | undefined\n const shift = mv.merged.range[0] - mv.range[0]\n const ch = new this.THREE.Color(mv.charHex)\n const heat = new this.THREE.Color(mv.glowHex)\n const k = clamp(mv.charK, 0, 1)\n const gk = clamp(mv.glowK, 0, 1)\n const boost = 1 + Math.max(0, mv.glowK - 1)\n for (let i = mv.range[0]; i < mv.range[1]; i++) {\n const o = (i + shift) * oSize\n let r = oArr ? oArr[o] : 1\n let g = oArr ? oArr[o + 1] : 1\n let b = oArr ? oArr[o + 2] : 1\n if (k > 0) {\n r = lerp(r, ch.r, k)\n g = lerp(g, ch.g, k)\n b = lerp(b, ch.b, k)\n }\n if (gk > 0) {\n r = lerp(r, heat.r * boost, gk)\n g = lerp(g, heat.g * boost, gk)\n b = lerp(b, heat.b * boost, gk)\n }\n arr[i * size] = r\n arr[i * size + 1] = g\n arr[i * size + 2] = b\n }\n mv.touched = true\n attr.needsUpdate = true\n }\n\n /**\n * Move a ranged part to a mesh of its own: the faces whose vertices all lie in its range are copied (with the current dents and colours) into a\n * new mesh that takes the merged mesh's transform and material, then attached to `group`; the range is collapsed in the merged mesh. Decals\n * go with it.\n */\n extract(mv: MeshVis, group: THREE_NS.Object3D): void {\n if (!mv.host || !mv.range || !mv.merged || mv.loose) return\n const host = mv.host\n const [v0, v1] = mv.range\n const src = host.mesh.geometry\n const geo = new this.THREE.BufferGeometry()\n const count = v1 - v0\n for (const name of Object.keys(src.attributes)) {\n const a = src.getAttribute(name) as THREE_NS.BufferAttribute\n if ((a as unknown as { isInterleavedBufferAttribute?: boolean }).isInterleavedBufferAttribute) continue\n const size = a.itemSize\n const from = (name === POS_ATTR ? host.work : null) ?? (a.array as unknown as Float32Array)\n const slice = (from as unknown as { slice(a: number, b: number): ArrayLike<number> }).slice(v0 * size, v1 * size)\n const Ctor = (a.array as unknown as { constructor: new (x: ArrayLike<number>) => ArrayLike<number> }).constructor\n geo.setAttribute(name, new this.THREE.BufferAttribute(new Ctor(slice) as never, size, a.normalized))\n }\n const index = src.getIndex()\n if (index) {\n const out: number[] = []\n for (let i = 0; i + 2 < index.count; i += 3) {\n const a = index.getX(i)\n const b = index.getX(i + 1)\n const c = index.getX(i + 2)\n if (a >= v0 && a < v1 && b >= v0 && b < v1 && c >= v0 && c < v1) out.push(a - v0, b - v0, c - v0)\n }\n geo.setIndex(count > 65535 ? new this.THREE.Uint32BufferAttribute(out, 1) : new this.THREE.Uint16BufferAttribute(out, 1))\n }\n const mats = materialsOf(host.mesh.material).map((m) => m.clone())\n const loose = new this.THREE.Mesh(geo, mats[0])\n loose.name = `${host.mesh.name || 'mesh'}:${v0}-${v1}`\n loose.castShadow = host.mesh.castShadow\n loose.receiveShadow = host.mesh.receiveShadow\n loose.renderOrder = host.mesh.renderOrder\n loose.frustumCulled = host.mesh.frustumCulled\n loose.layers.mask = host.mesh.layers.mask\n loose.position.copy(host.mesh.position)\n loose.quaternion.copy(host.mesh.quaternion)\n loose.scale.copy(host.mesh.scale)\n const parent = host.mesh.parent\n if (parent) parent.add(loose)\n loose.updateMatrixWorld(true)\n // the merged mesh loses the range; the decals and the part's state follow the new mesh\n this.collapse(mv)\n for (const d of mv.decals) {\n d.obj.visible = true\n loose.add(d.obj)\n }\n const lp = positionAttr(geo) as THREE_NS.BufferAttribute\n const lh: HostVis = {\n mesh: loose,\n origGeometry: geo,\n origMaterial: loose.material,\n ownGeo: true,\n work: new Float32Array(lp.array as ArrayLike<number>),\n ownMat: true,\n sharers: [mv],\n }\n mv.host = lh\n mv.range = [0, count]\n mv.mesh = loose\n mv.loose = true\n mv.moved = true\n group.attach(loose)\n for (const m of materialsOf(loose.material)) m.needsUpdate = true\n }\n\n private restoreRanged(mv: MeshVis): void {\n const merged = mv.merged as NonNullable<MeshVis['merged']>\n const host = merged.host\n for (const d of mv.decals) this.disposeDecal(d)\n mv.decals.length = 0\n if (mv.loose) {\n const loose = mv.mesh\n loose.parent?.remove(loose)\n loose.geometry.dispose()\n for (const m of materialsOf(loose.material)) m.dispose()\n mv.host = host\n mv.range = [merged.range[0], merged.range[1]]\n mv.mesh = host.mesh\n mv.loose = false\n mv.moved = false\n }\n mv.collapsed = false\n mv.hidden = false\n mv.meltApplied = 0\n mv.charK = 0\n mv.glowK = 0\n mv.touched = false\n if (!host.ownGeo) return\n // positions and colours of the range go back to the original ones\n const work = host.work as Float32Array\n const op = positionAttr(host.origGeometry)\n const [v0, v1] = merged.range\n if (op) work.set((op.array as unknown as Float32Array).subarray(v0 * 3, v1 * 3), v0 * 3)\n const attr = host.mesh.geometry.getAttribute('color') as THREE_NS.BufferAttribute | undefined\n if (attr) {\n const orig = host.origGeometry.getAttribute('color') as THREE_NS.BufferAttribute | undefined\n const size = attr.itemSize\n const a = attr.array as unknown as Float32Array\n for (let i = v0; i < v1; i++) {\n for (let c = 0; c < 3; c++) a[i * size + c] = orig ? orig.array[i * orig.itemSize + c] : 1\n }\n attr.needsUpdate = true\n }\n if (host.sharers.every((s) => !s.touched)) {\n host.mesh.geometry.dispose()\n host.mesh.geometry = host.origGeometry\n host.work = null\n host.ownGeo = false\n if (host.ownMat) {\n for (const m of materialsOf(host.mesh.material)) m.dispose()\n host.mesh.material = host.origMaterial\n host.ownMat = false\n }\n } else this.rebuildHost(host)\n }\n\n /** Bring one mesh back to how it was when it was bound. */\n restoreMesh(mv: MeshVis): void {\n if (mv.merged) {\n this.restoreRanged(mv)\n return\n }\n const mesh = mv.mesh\n for (const d of mv.decals) this.disposeDecal(d)\n mv.decals.length = 0\n if (mv.ownGeo) {\n mesh.geometry.dispose()\n mesh.geometry = mv.origGeometry\n mv.ownGeo = false\n mv.work = null\n }\n if (mv.ownMat) {\n for (const m of materialsOf(mesh.material)) m.dispose()\n mesh.material = mv.origMaterial\n mv.ownMat = false\n mv.baseColors = null\n }\n mesh.visible = mv.origVisible\n if (mv.origParent && mesh.parent !== mv.origParent) {\n mv.origParent.add(mesh)\n mv.moved = true\n }\n if (mv.moved) {\n mesh.position.copy(mv.origPosition)\n mesh.quaternion.copy(mv.origQuaternion)\n mesh.scale.copy(mv.origScale)\n mv.moved = false\n }\n mv.meltApplied = 0\n }\n\n restorePart(pv: PartVis): void {\n for (const mv of pv.meshes) this.restoreMesh(mv)\n pv.charApplied = 0\n pv.glowApplied = 0\n pv.meltApplied = 0\n pv.gone = 'none'\n }\n\n dispose(): void {\n this.kit.dispose()\n }\n}\n","import type * as THREE_NS from 'three'\nimport type { DebrisKind, DestructionEvent, MaterialDef, PartSpec, Vec3 } from '@adatechnology/game-destruction'\nimport { FragmentSystem } from './fragments'\nimport { GroundSpots } from './ground'\nimport type { Bound, HostVis, MeshVis, PartVis, PieceBody } from './internal'\nimport { clamp, createRng, hashNumbers, hashString, mixHex } from './math'\nimport { ParticleSystem } from './particles'\nimport type { ParticleKind } from './particles'\nimport { PieceSystem } from './pieces'\nimport { partHintOf } from './spec'\nimport type { DestructionRenderer, BindOptions, Mesh, MeshRange, Object3D, Piece, RendererOptions, ThreeNamespace } from './types'\nimport { Visuals } from './visual'\n\ntype Ev<T extends DestructionEvent['type']> = Extract<DestructionEvent, { type: T }>\n\nconst GLOW_FROM_C = 430\nconst QUEUE_CAP = 4000\n\ninterface FireEmitter {\n object: string\n part: string\n point: Vec3\n accFire: number\n accSmoke: number\n}\n\ninterface Leak {\n object: string\n part: string\n fluid: string\n point: Vec3\n direction: Vec3\n rate: number\n pressurized: boolean\n acc: number\n}\n\nconst FLUID_COLORS: Record<string, number> = {\n water: 0x8cc8ff,\n fuel: 0xd9a032,\n oil: 0x2a2018,\n gas: 0xf2f2f2,\n sap: 0xb89b3a,\n acid: 0x9bd23c,\n blood: 0x8a0a0a,\n}\nconst SMOKE_TONES: Record<string, number> = { white: 0xe6e6e6, grey: 0x8a8a8a, black: 0x2a2a2a }\n\nconst DEFAULT_SPEED: Record<string, number> = {\n sparks: 3,\n splinters: 2.5,\n dust: 1,\n shards: 2.5,\n fibers: 1.2,\n leaves: 1.2,\n drips: 0.5,\n ash: 0.8,\n chips: 2.5,\n smoke: 0.8,\n steam: 1,\n fire: 1.2,\n flash: 0,\n foam: 2.5,\n}\n\n/** Sound key for an impact on a material. */\nfunction impactKey(def: MaterialDef | undefined): string {\n if (!def) return 'impact_wood'\n const tags = def.tags ?? []\n if (tags.includes('metal')) return 'impact_metal'\n if (def.visual?.transparent || def.fracture === 'shatter') return 'impact_glass'\n if (def.fracture === 'crumble') return 'impact_masonry'\n if (def.fracture === 'tear') return 'impact_soft'\n return def.fracture === 'none' ? 'impact_metal' : 'impact_wood'\n}\n\nfunction fractureKey(def: MaterialDef | undefined, style: string): string {\n switch (style) {\n case 'shatter':\n return 'shatter_glass'\n case 'crumble':\n return 'crumble'\n case 'splinter':\n return 'splinter'\n default:\n return impactKey(def)\n }\n}\n\nfunction fallbackColor(id: string, def: MaterialDef | undefined): number {\n if (def?.tags?.includes('metal')) return 0x8a8f96\n if (def?.visual?.transparent) return 0x9fd0e0\n const h = hashString(id)\n return mixHex(0x6b5a48, 0x9a9488, (h % 100) / 100)\n}\n\nfunction materialsOf(m: THREE_NS.Material | THREE_NS.Material[]): THREE_NS.Material[] {\n return Array.isArray(m) ? m : [m]\n}\n\nfunction isMeshRange(v: Object3D | MeshRange): v is MeshRange {\n const r = v as Partial<MeshRange>\n return !!r.mesh && Array.isArray(r.range)\n}\n\n/** Create the renderer. See `DestructionRenderer`. */\nexport function createDestructionRenderer(THREE: ThreeNamespace, options: RendererOptions): DestructionRenderer {\n return new Renderer(THREE, options)\n}\n\nclass Renderer implements DestructionRenderer {\n private readonly world: RendererOptions['world']\n private readonly scene: THREE_NS.Scene\n private readonly quality: 'low' | 'medium' | 'high'\n private readonly low: boolean\n private readonly groundAt: (x: number, z: number) => number\n private readonly gravity: number\n private readonly autoSplit: boolean\n private readonly maxFragments: number\n private readonly bounds = new Map<string, Bound>()\n private readonly queue: DestructionEvent[] = []\n private readonly unsubscribe: () => void\n private readonly visuals: Visuals\n private readonly particles: ParticleSystem\n private readonly fragments: FragmentSystem\n private readonly ground: GroundSpots\n private readonly pieceSys: PieceSystem\n private readonly active = new Map<string, { object: string; part: string }>()\n private readonly fires = new Map<string, FireEmitter>()\n private readonly leaks: Leak[] = []\n private readonly homeTags: string[] = []\n private readonly throttles = new Map<string, number>()\n private readonly rng = createRng(0xc0ffee)\n private light: THREE_NS.PointLight | null = null\n private lightT = 0\n private lightDur = 0.3\n private lightPeak = 0\n private time = 0\n private scanTimer = 0\n private disposed = false\n private readonly box: THREE_NS.Box3\n private readonly tmpQ: THREE_NS.Quaternion\n private readonly v1: THREE_NS.Vector3\n private readonly v2: THREE_NS.Vector3\n\n constructor(\n private readonly THREE: ThreeNamespace,\n private readonly o: RendererOptions,\n ) {\n this.world = o.world\n this.scene = o.scene\n this.quality = o.quality ?? 'medium'\n this.low = this.quality === 'low'\n this.groundAt = o.groundAt ?? (() => 0)\n this.gravity = o.gravity ?? 9.81\n this.autoSplit = o.autoSplit !== false\n this.maxFragments = o.maxFragments ?? 160\n this.box = new THREE.Box3()\n this.tmpQ = new THREE.Quaternion()\n this.v1 = new THREE.Vector3()\n this.v2 = new THREE.Vector3()\n const maxParticles = Math.max(8, Math.floor((o.maxParticles ?? 2500) * (this.low ? 0.5 : 1)))\n this.visuals = new Visuals(THREE, this.low, 600)\n this.ground = new GroundSpots(THREE, o.scene, this.groundAt, 48)\n this.particles = new ParticleSystem(THREE, {\n capacity: maxParticles,\n scene: o.scene,\n density: this.low ? 0.6 : 1,\n pointScale: o.pointScale ?? 700,\n groundAt: this.groundAt,\n gravity: this.gravity,\n onLand: (x, _y, z, color, tag) => {\n const home = this.homeTags[tag - 1] ?? ''\n this.ground.add('puddle', x, z, 0.05 + this.rng() * 0.04, color, home)\n },\n })\n this.fragments = new FragmentSystem(THREE, {\n scene: o.scene,\n groundAt: this.groundAt,\n gravity: this.gravity,\n max: this.maxFragments,\n life: o.fragmentLife ?? 12,\n })\n this.pieceSys = new PieceSystem(THREE, {\n groundAt: this.groundAt,\n gravity: this.gravity,\n scene: o.scene,\n onHardLanding: (p, point, speed) => this.hardLanding(p, point, speed),\n })\n this.unsubscribe = this.world.on('*', (ev) => {\n if (this.queue.length < QUEUE_CAP) this.queue.push(ev)\n })\n }\n\n // ----------------------------------------------------------------------------------------------------------------------------------\n // Binding\n // ----------------------------------------------------------------------------------------------------------------------------------\n\n bind(objectId: string, root: Object3D, options: BindOptions = {}): void {\n if (this.bounds.has(objectId)) this.unbind(objectId)\n const state = this.world.state(objectId)\n const partIds = new Set((state?.parts ?? []).map((p) => p.id))\n const byPart = new Map<string, Array<{ mesh: Mesh; range?: [number, number] }>>()\n const taken = new Set<Mesh>()\n const put = (pid: string, e: { mesh: Mesh; range?: [number, number] }): void => {\n const l = byPart.get(pid)\n if (l) l.push(e)\n else byPart.set(pid, [e])\n }\n const add = (pid: string, m: Mesh): void => {\n if (taken.has(m)) return\n taken.add(m)\n put(pid, { mesh: m })\n }\n const addRange = (pid: string, r: MeshRange): void => {\n const n = (r.mesh.geometry.getAttribute('position')?.count ?? 0) as number\n const [v0, v1] = r.range\n if (!Number.isInteger(v0) || !Number.isInteger(v1) || v0 < 0 || v1 <= v0 || v1 > n) {\n throw new Error(`game-destruction-three: invalid vertex range [${v0}, ${v1}) for part \"${pid}\" (the mesh has ${n} vertices)`)\n }\n put(pid, { mesh: r.mesh, range: [v0, v1] })\n }\n const meshesIn = (o: Object3D): Mesh[] => {\n const out: Mesh[] = []\n o.traverse((c) => {\n if ((c as Mesh).isMesh) out.push(c as Mesh)\n })\n return out\n }\n if (options.parts) {\n for (const [pid, v] of Object.entries(options.parts)) {\n for (const o of Array.isArray(v) ? v : [v]) {\n if (isMeshRange(o)) addRange(pid, o)\n else for (const m of meshesIn(o)) add(pid, m)\n }\n }\n } else {\n const leftovers: Mesh[] = []\n for (const m of meshesIn(root)) {\n const ud = partHintOf(m.userData?.destructionPart)?.id\n if (ud !== undefined && partIds.has(ud)) add(ud, m)\n else if (partIds.has(m.name)) add(m.name, m)\n else leftovers.push(m)\n }\n if (leftovers.length) this.matchByBox(objectId, root, leftovers, add)\n }\n const bound: Bound = { id: objectId, root, sync: options.syncTransform !== false, parts: new Map(), lastPos: null, lastQuat: null, piece: null, isSplit: false }\n const hosts = new Map<Mesh, HostVis>()\n for (const pid of partIds) bound.parts.set(pid, this.makePartVis(objectId, pid, state?.parts.find((p) => p.id === pid)?.material ?? '', byPart.get(pid) ?? [], hosts))\n for (const [pid, meshes] of byPart) {\n if (!bound.parts.has(pid)) bound.parts.set(pid, this.makePartVis(objectId, pid, this.world.part(objectId, pid)?.material ?? '', meshes, hosts))\n }\n this.bounds.set(objectId, bound)\n }\n\n private makePartVis(home: string, id: string, material: string, meshes: Array<{ mesh: Mesh; range?: [number, number] }>, hosts: Map<Mesh, HostVis>): PartVis {\n return { id, home, material, meshes: meshes.map((m) => this.visuals.makeMeshVis(m.mesh, home, m.range, hosts)), charApplied: 0, glowApplied: 0, meltApplied: 0, gone: 'none' }\n }\n\n /** Match unlabelled meshes to the part whose shape contains the centre of the mesh's bounding box. */\n private matchByBox(objectId: string, root: Object3D, meshes: Mesh[], add: (pid: string, m: Mesh) => void): void {\n let specParts: PartSpec[] | undefined\n try {\n specParts = this.world.serialize?.().objects.find((o) => o.id === objectId)?.spec.parts\n } catch {\n specParts = undefined\n }\n if (!specParts) return\n root.updateMatrixWorld(true)\n const e = new this.THREE.Euler()\n const q = new this.THREE.Quaternion()\n for (const m of meshes) {\n this.box.setFromObject(m)\n if (this.box.isEmpty()) continue\n const c = root.worldToLocal(this.box.getCenter(this.v1))\n let best: PartSpec | null = null\n let bestVol = Infinity\n for (const p of specParts) {\n const l = this.v2.set(c.x - p.position[0], c.y - p.position[1], c.z - p.position[2])\n if (p.rotation) l.applyQuaternion(q.setFromEuler(e.set(p.rotation[0], p.rotation[1], p.rotation[2], 'XYZ')).invert())\n let inside = false\n let vol = 0\n const s = p.shape\n if (s.kind === 'box') {\n inside = Math.abs(l.x) <= s.size[0] / 2 && Math.abs(l.y) <= s.size[1] / 2 && Math.abs(l.z) <= s.size[2] / 2\n vol = s.size[0] * s.size[1] * s.size[2]\n } else if (s.kind === 'cylinder') {\n inside = Math.hypot(l.x, l.z) <= s.radius && Math.abs(l.y) <= s.height / 2\n vol = Math.PI * s.radius * s.radius * s.height\n } else {\n inside = l.length() <= s.radius\n vol = (4 / 3) * Math.PI * s.radius ** 3\n }\n if (inside && vol < bestVol) {\n best = p\n bestVol = vol\n }\n }\n if (best) add(best.id, m)\n }\n }\n\n unbind(objectId: string): void {\n const b = this.bounds.get(objectId)\n if (!b) return\n this.restore(objectId)\n this.pieceSys.remove(objectId)\n this.bounds.delete(objectId)\n }\n\n // ----------------------------------------------------------------------------------------------------------------------------------\n // Pieces\n // ----------------------------------------------------------------------------------------------------------------------------------\n\n /** Half extents and centre of the box around some meshes, in the local space of `root`. */\n private measure(root: Object3D, meshes: MeshVis[]): { half: Vec3; center: THREE_NS.Vector3 } {\n root.updateMatrixWorld(true)\n const inv = root.matrixWorld.clone().invert()\n const total = new this.THREE.Box3()\n const m4 = new this.THREE.Matrix4()\n const lb = new this.THREE.Box3()\n for (const mv of meshes) {\n const m = mv.mesh\n m.updateWorldMatrix(true, false)\n this.visuals.localBox(mv, lb)\n if (lb.isEmpty()) continue\n total.union(lb.clone().applyMatrix4(m4.multiplyMatrices(inv, m.matrixWorld)))\n }\n if (total.isEmpty()) return { half: [0.25, 0.25, 0.25], center: new this.THREE.Vector3() }\n const size = total.getSize(new this.THREE.Vector3())\n return { half: [Math.max(0.02, size.x / 2), Math.max(0.02, size.y / 2), Math.max(0.02, size.z / 2)], center: total.getCenter(new this.THREE.Vector3()) }\n }\n\n private meshesOf(b: Bound): MeshVis[] {\n const out: MeshVis[] = []\n for (const pv of b.parts.values()) if (pv.gone === 'none') for (const mv of pv.meshes) out.push(mv)\n return out\n }\n\n private massOf(objectId: string): number {\n const st = this.world.state(objectId)\n if (!st) return 5\n let m = 0\n for (const p of st.parts) if (p.status !== 'broken' && p.status !== 'melted' && p.status !== 'ashes') m += p.mass\n return m > 0 ? m : 5\n }\n\n makePiece(objectId: string, options: { mass?: number; velocity?: Vec3 } = {}): Piece {\n const b = this.bounds.get(objectId)\n if (!b) throw new Error(`game-destruction-three: object \"${objectId}\" is not bound`)\n if (b.root.parent && b.root.parent !== this.scene) this.scene.attach(b.root)\n const { half, center } = this.measure(b.root, this.meshesOf(b))\n const piece = this.pieceSys.create(objectId, b.root, half, center, options.mass ?? this.massOf(objectId), options.velocity)\n b.piece = piece\n return piece\n }\n\n pieces(): Piece[] {\n return [...this.pieceSys.list]\n }\n\n piece(objectId: string): Piece | null {\n return this.pieceSys.get(objectId)\n }\n\n impulse(objectId: string, impulse: Vec3, point?: Vec3): void {\n const p = this.pieceSys.get(objectId)\n if (p) this.pieceSys.impulse(p, impulse, point)\n }\n\n private hardLanding(p: PieceBody, point: Vec3, speed: number): void {\n const key = this.impactSoundOf(p.objectId)\n this.sound(key, point, clamp(speed / 10, 0.2, 1))\n // the piece just moved this frame: the world must know where it is before the hit is resolved against its parts\n this.syncBound(p.objectId)\n try {\n this.world.hit(p.objectId, { type: 'blunt', point, energy: 0.5 * p.mass * speed * speed, radius: 0.08, direction: [0, -1, 0], source: 'fall' })\n } catch {\n // the object may have been removed from the world\n }\n }\n\n private impactSoundOf(objectId: string): string {\n const st = this.world.state(objectId)\n let best: MaterialDef | undefined\n let mass = -1\n for (const p of st?.parts ?? []) {\n if (p.mass > mass) {\n mass = p.mass\n best = this.world.materials.get(p.material)\n }\n }\n return impactKey(best)\n }\n\n // ----------------------------------------------------------------------------------------------------------------------------------\n // Update\n // ----------------------------------------------------------------------------------------------------------------------------------\n\n update(dt: number): void {\n if (this.disposed) return\n dt = clamp(dt, 0, 0.1)\n this.time += dt\n this.drainQueue()\n this.updateActive(dt)\n this.updateLeaks(dt)\n this.pieceSys.update(dt)\n this.fragments.update(dt)\n this.ground.update(dt)\n this.updateLight(dt)\n this.particles.update(dt)\n this.syncTransforms()\n }\n\n private drainQueue(): void {\n // events produced while applying (a hard landing calls `hit`) wait for the next frame\n const batch = this.queue.splice(0, this.queue.length)\n for (const ev of batch) {\n try {\n this.apply(ev)\n } catch (err) {\n if (typeof console !== 'undefined') console.error('game-destruction-three: failed to apply', ev.type, err)\n }\n }\n }\n\n private syncTransforms(): void {\n for (const b of this.bounds.values()) this.syncOne(b)\n }\n\n /** Push the current transform of one bound object to the world right now (used before a landing hit). */\n private syncBound(objectId: string): void {\n const b = this.bounds.get(objectId)\n if (b) this.syncOne(b)\n }\n\n private syncOne(b: Bound): void {\n if (!b.sync) return\n const p = this.v1\n const q = this.tmpQ\n b.root.updateWorldMatrix(true, false)\n b.root.matrixWorld.decompose(p, q, this.v2)\n const last = b.lastPos\n const lq = b.lastQuat\n const moved = !last || !lq || Math.abs(p.x - last[0]) + Math.abs(p.y - last[1]) + Math.abs(p.z - last[2]) > 1e-5 || Math.abs(q.x - lq[0]) + Math.abs(q.y - lq[1]) + Math.abs(q.z - lq[2]) + Math.abs(q.w - lq[3]) > 1e-6\n if (!moved) return\n b.lastPos = [p.x, p.y, p.z]\n b.lastQuat = [q.x, q.y, q.z, q.w]\n if (this.world.has(b.id)) this.world.setTransform(b.id, [p.x, p.y, p.z], [q.x, q.y, q.z, q.w])\n }\n\n // ----------------------------------------------------------------------------------------------------------------------------------\n // Events\n // ----------------------------------------------------------------------------------------------------------------------------------\n\n private apply(ev: DestructionEvent): void {\n switch (ev.type) {\n case 'damage':\n if (ev.damageType === 'heat' || ev.damageType === 'fire' || ev.damageType === 'explosive') this.activate(ev.object, ev.part)\n break\n case 'dent':\n this.onDent(ev)\n break\n case 'hole':\n this.onHole(ev)\n break\n case 'crack':\n this.onCrack(ev)\n break\n case 'ricochet': {\n const d = ev.direction\n this.particles.burst('sparks', ev.point, d, 4 + Math.min(10, ev.energy / 150), 4)\n this.sound('ricochet', ev.point, 0.7)\n break\n }\n case 'debris':\n this.particles.burst(ev.kind, ev.point, ev.normal, ev.count, ev.speed > 0 ? ev.speed : DEFAULT_SPEED[ev.kind] ?? 1, this.debrisOptions(ev.kind, ev.object, ev.part))\n break\n case 'fracture':\n this.onFracture(ev)\n break\n case 'detach':\n this.onDetach(ev)\n break\n case 'collapse':\n this.splitLoose(ev.object)\n break\n case 'disassemble_progress': {\n const k = `unscrew:${ev.joint}`\n if (this.time - (this.throttles.get(k) ?? -1) > 0.25) {\n this.throttles.set(k, this.time)\n this.sound('unscrew', this.rootPoint(ev.object), clamp(0.3 + ev.progress * 0.5, 0, 1))\n }\n break\n }\n case 'ignite':\n this.fires.set(`${ev.object}|${ev.part}`, { object: ev.object, part: ev.part, point: ev.point, accFire: 0, accSmoke: 0 })\n this.activate(ev.object, ev.part)\n this.particles.burst('fire', ev.point, [0, 1, 0], 6, 1)\n this.sound('ignite', ev.point, 0.7)\n this.sound('burn_loop', ev.point, 0.5)\n break\n case 'primed':\n // a bullet or fire sets the part burning until its delay runs out; a blast nearby only waits for the explosion\n if (ev.trigger !== 'explosion') {\n this.fires.set(`${ev.object}|${ev.part}`, { object: ev.object, part: ev.part, point: ev.point, accFire: 0, accSmoke: 0 })\n this.activate(ev.object, ev.part)\n this.particles.burst('fire', ev.point, [0, 1, 0], 6, 1)\n this.sound('ignite', ev.point, 0.7)\n }\n break\n case 'extinguish': {\n const f = this.fires.get(`${ev.object}|${ev.part}`)\n if (f) {\n this.fires.delete(`${ev.object}|${ev.part}`)\n this.particles.burst('steam', f.point, [0, 1, 0], 14, 1.2)\n }\n this.activate(ev.object, ev.part)\n this.sound('extinguish', f?.point ?? this.rootPoint(ev.object), 0.6)\n break\n }\n case 'burnout': {\n this.fires.delete(`${ev.object}|${ev.part}`)\n this.particles.burst('ash', ev.point, [0, 1, 0], 16, 1)\n this.particles.burst('smoke', ev.point, [0, 1, 0], 6, 0.8, { color: SMOKE_TONES.grey })\n const pv = this.partVis(ev.object, ev.part)\n if (pv) {\n const def = this.world.materials.get(pv.material)\n this.visuals.setChar(pv, 1, def?.visual?.charColor ?? 0x1a1410)\n }\n this.activate(ev.object, ev.part)\n break\n }\n case 'melt':\n this.activate(ev.object, ev.part)\n if (this.time - (this.throttles.get(`melt:${ev.object}|${ev.part}`) ?? -9) > 1.5) {\n this.throttles.set(`melt:${ev.object}|${ev.part}`, this.time)\n this.sound('melt_sizzle', this.rootPoint(ev.object), 0.4)\n }\n break\n case 'drip': {\n const b = this.bounds.get(ev.object)\n const pv = b?.parts.get(ev.part)\n const def = this.world.materials.get(ev.material)\n const color = (pv && this.partColor(pv, def)) ?? fallbackColor(ev.material, def)\n let tag = this.homeTags.indexOf(ev.object) + 1\n if (tag === 0) tag = this.homeTags.push(ev.object)\n this.particles.spawn('drips', ev.point, [(this.rng() - 0.5) * 0.1, -0.1, (this.rng() - 0.5) * 0.1], { color, tag })\n break\n }\n case 'leak': {\n const key = `${ev.object}|${ev.part}`\n const same = this.leaks.find((l) => l.object === ev.object && l.part === ev.part && Math.hypot(l.point[0] - ev.point[0], l.point[1] - ev.point[1], l.point[2] - ev.point[2]) < 0.03)\n if (same) {\n same.rate = ev.rate\n same.direction = ev.direction\n } else {\n if (this.leaks.length >= 24) this.leaks.shift()\n this.leaks.push({ object: ev.object, part: ev.part, fluid: ev.fluid, point: ev.point, direction: ev.direction, rate: ev.rate, pressurized: ev.pressurized, acc: 0 })\n }\n void key\n break\n }\n case 'explode':\n this.onExplode(ev)\n break\n case 'destroyed':\n break\n }\n }\n\n private rootPoint(objectId: string): Vec3 {\n const b = this.bounds.get(objectId)\n return b ? [b.root.position.x, b.root.position.y, b.root.position.z] : [0, 0, 0]\n }\n\n private sound(key: string, point: Vec3, volume: number): void {\n this.o.onSound?.(key, point, volume)\n }\n\n private partVis(objectId: string, partId: string): PartVis | null {\n return this.bounds.get(objectId)?.parts.get(partId) ?? null\n }\n\n private activate(objectId: string, partId: string): void {\n this.active.set(`${objectId}|${partId}`, { object: objectId, part: partId })\n }\n\n /** Current colour of the part's first mesh (char included), or null. */\n private partColor(pv: PartVis, def: MaterialDef | undefined): number | null {\n const mv = pv.meshes[0]\n if (!mv) return null\n const mat = materialsOf(mv.mesh.material)[0] as THREE_NS.MeshStandardMaterial | undefined\n const c = mat?.color\n if (c && (c as THREE_NS.Color).isColor) {\n const tint = this.visuals.vertexTint(mv)\n return tint ? c.clone().multiply(tint).getHex() : c.getHex()\n }\n return def ? fallbackColor(def.id, def) : null\n }\n\n private debrisOptions(kind: DebrisKind, objectId: string, partId: string): { color?: number } | undefined {\n const pv = this.partVis(objectId, partId)\n const def = pv ? this.world.materials.get(pv.material) : undefined\n if (kind === 'smoke') return { color: SMOKE_TONES[def?.thermal.smoke ?? 'grey'] }\n if (kind === 'fibers' || kind === 'leaves') {\n const c = pv ? this.visuals.baseColorHex(pv.meshes[0] ?? ({} as never)) : -1\n return c >= 0 ? { color: c } : undefined\n }\n if (kind === 'dust' || kind === 'chips') {\n const c = pv && pv.meshes[0] ? this.visuals.baseColorHex(pv.meshes[0]) : -1\n return c >= 0 && kind === 'chips' ? { color: c } : undefined\n }\n return undefined\n }\n\n /** Mesh of the part closest to a world point. */\n private nearestMesh(pv: PartVis, p: THREE_NS.Vector3): PartVis['meshes'][number] | null {\n let best: PartVis['meshes'][number] | null = null\n let bd = Infinity\n for (const mv of pv.meshes) {\n if (!this.visuals.shown(mv)) continue\n const d = this.visuals.worldBox(mv, this.box).distanceToPoint(p)\n if (d < bd) {\n bd = d\n best = mv\n }\n }\n return best\n }\n\n private worldDir(b: Bound, v: Vec3): THREE_NS.Vector3 {\n b.root.updateWorldMatrix(true, false)\n return new this.THREE.Vector3(v[0], v[1], v[2]).applyQuaternion(b.root.getWorldQuaternion(this.tmpQ)).normalize()\n }\n\n private onDent(ev: Ev<'dent'>): void {\n const b = this.bounds.get(ev.object)\n const pv = b?.parts.get(ev.part)\n if (!b || !pv) return\n const def = this.world.materials.get(pv.material)\n const wp = new this.THREE.Vector3(ev.worldPoint[0], ev.worldPoint[1], ev.worldPoint[2])\n const wn = this.worldDir(b, ev.dent.normal)\n for (const mv of pv.meshes) {\n if (!this.visuals.shown(mv)) continue\n this.visuals.applyDent(mv, wp, wn, ev.dent)\n }\n if (def?.tags?.includes('metal')) this.particles.burst('sparks', ev.worldPoint, [wn.x, wn.y, wn.z], 4 + Math.min(8, ev.dent.depth * 150), 3.2)\n this.sound(impactKey(def), ev.worldPoint, clamp(0.3 + ev.dent.depth * 12, 0.3, 1))\n }\n\n private onHole(ev: Ev<'hole'>): void {\n const b = this.bounds.get(ev.object)\n const pv = b?.parts.get(ev.part)\n if (!b || !pv) return\n const def = this.world.materials.get(pv.material)\n const n = this.worldDir(b, ev.hole.normal)\n const draw = (point: Vec3, normal: THREE_NS.Vector3): void => {\n const wp = new this.THREE.Vector3(point[0], point[1], point[2])\n const mv = this.nearestMesh(pv, wp)\n if (!mv) return\n if (def?.visual?.transparent) {\n const seed = hashNumbers(ev.time, point[0], point[1], point[2])\n this.visuals.addCrack(mv, wp, normal, 7, Math.max(0.05, ev.hole.diameter * 5), 0xe8f4ff, seed)\n } else {\n this.visuals.addHole(mv, wp, normal, ev.hole.diameter)\n }\n }\n draw(ev.worldPoint, n)\n if (ev.hole.through && ev.exitPoint) draw(ev.exitPoint, n.clone().negate())\n if (def?.tags?.includes('metal')) this.particles.burst('sparks', ev.worldPoint, [n.x, n.y, n.z], 6, 3.5)\n this.sound(impactKey(def), ev.worldPoint, 0.6)\n }\n\n private onCrack(ev: Ev<'crack'>): void {\n const b = this.bounds.get(ev.object)\n const pv = b?.parts.get(ev.part)\n if (!b || !pv) return\n const def = this.world.materials.get(pv.material)\n const wp = new this.THREE.Vector3(ev.worldPoint[0], ev.worldPoint[1], ev.worldPoint[2])\n const mv = this.nearestMesh(pv, wp)\n if (mv) {\n const color = def?.visual?.transparent ? 0xe8f4ff : 0x2a2724\n this.visuals.addCrack(mv, wp, this.worldDir(b, ev.crack.normal), ev.crack.rays, ev.crack.length, color, hashNumbers(ev.time, ev.worldPoint[0], ev.worldPoint[1], ev.worldPoint[2]))\n }\n this.sound(impactKey(def), ev.worldPoint, 0.5)\n }\n\n private onFracture(ev: Ev<'fracture'>): void {\n const b = this.bounds.get(ev.object)\n const pv = b?.parts.get(ev.part)\n const matId = pv?.material || (ev.fragments[0]?.material ?? '')\n const def = this.world.materials.get(matId)\n let color = fallbackColor(matId, def)\n let opacity = 0.5\n const transparent = def?.visual?.transparent === true\n if (pv) {\n const c = this.partColor(pv, def)\n if (c !== null) color = c\n const m = pv.meshes[0] ? (materialsOf(pv.meshes[0].origMaterial)[0] as THREE_NS.Material | undefined) : undefined\n if (m && m.transparent) opacity = clamp(m.opacity, 0.2, 0.8)\n this.visuals.hide(pv, 'broken')\n }\n const maxAlive = this.low ? Math.floor(this.maxFragments / 2) : this.maxFragments\n const step = this.low ? 2 : 1\n for (let i = 0; i < ev.fragments.length; i += step) {\n const fr = ev.fragments[i]\n const fdef = this.world.materials.get(fr.material)\n const mat = this.fragments.materialFor(fr.material, fdef === def || !fdef ? color : fallbackColor(fr.material, fdef), transparent || fdef?.visual?.transparent === true, opacity)\n this.fragments.spawn(fr, mat, ev.object, maxAlive)\n }\n const up: Vec3 = [0, 1, 0]\n const sizeK = clamp(ev.fragments.length / 10, 0.5, 2)\n switch (ev.style) {\n case 'shatter':\n this.particles.burst('shards', ev.point, up, 18 * sizeK, 3)\n break\n case 'crumble':\n this.particles.burst('dust', ev.point, up, 16 * sizeK, 1.2, { color: pv ? (this.partColor(pv, def) ?? undefined) : undefined })\n this.particles.burst('chips', ev.point, up, 6, 3)\n break\n case 'splinter':\n this.particles.burst('splinters', ev.point, up, 14 * sizeK, 3)\n this.particles.burst('dust', ev.point, up, 5, 0.8, { color: 0xb59a74 })\n break\n case 'tear':\n this.particles.burst('fibers', ev.point, up, 12 * sizeK, 1.6, { color: color })\n break\n case 'snap':\n this.particles.burst('chips', ev.point, up, 8 * sizeK, 3)\n break\n default:\n this.particles.burst('sparks', ev.point, up, 10, 4)\n }\n this.sound(fractureKey(def, ev.style), ev.point, clamp(0.4 + ev.fragments.length * 0.03, 0.4, 1))\n }\n\n private onDetach(ev: Ev<'detach'>): void {\n this.sound('detach', this.rootPoint(ev.object), 0.5)\n this.splitLoose(ev.object)\n }\n\n /** Turn every loose group of the object (a detach, a collapse) into a piece of its own. */\n private splitLoose(objectId: string): void {\n if (!this.autoSplit) return\n const b = this.bounds.get(objectId)\n if (!b) return\n const st = this.world.state(objectId)\n if (!st || st.groups.length < 2) return\n const loose = st.groups.length - 1\n for (let i = 0; i < loose; i++) {\n // the loose groups are always read from index 1: each split renumbers the rest\n let newId: string\n try {\n newId = this.world.splitGroup(objectId, 1)\n } catch {\n break\n }\n this.adoptSplit(b, newId)\n }\n const old = b.piece\n if (old) {\n const { half, center } = this.measure(b.root, this.meshesOf(b))\n old.halfExtents = half\n this.pieceSys.setCenter(old, center)\n old.mass = Math.max(0.05, this.massOf(objectId))\n old.sleeping = false\n }\n }\n\n /** The renderer side of `splitGroup`: a new group holding the meshes of the new object, made a piece. */\n private adoptSplit(from: Bound, newId: string): void {\n const state = this.world.state(newId)\n const root = from.root\n root.updateWorldMatrix(true, false)\n const group = new this.THREE.Group()\n group.name = `destruction-piece-${newId}`\n root.getWorldPosition(group.position)\n root.getWorldQuaternion(group.quaternion)\n this.scene.add(group)\n group.updateMatrixWorld(true)\n const nb: Bound = { id: newId, root: group, sync: true, parts: new Map(), lastPos: null, lastQuat: null, piece: null, isSplit: true }\n for (const p of state?.parts ?? []) {\n const pv = from.parts.get(p.id)\n if (!pv) continue\n from.parts.delete(p.id)\n nb.parts.set(p.id, pv)\n for (const mv of pv.meshes) {\n // a part drawn by a range of a merged mesh leaves its faces behind: it gets a mesh of its own\n if (mv.host && !mv.loose) this.visuals.extract(mv, group)\n else {\n group.attach(mv.mesh)\n mv.moved = true\n }\n }\n }\n this.bounds.set(newId, nb)\n const meshes = this.meshesOf(nb)\n if (meshes.length === 0) return\n const { half, center } = this.measure(group, meshes)\n let vel: Vec3 = [0, 0, 0]\n let ang: Vec3 = [0, 0, 0]\n const op = from.piece\n if (op) {\n vel = [...op.velocity]\n ang = [...op.angularVelocity]\n // linear velocity at the loose piece's centre\n const c = this.pieceSys.centerOf(op, new this.THREE.Vector3())\n const nc = center.clone().applyQuaternion(group.quaternion).add(group.position)\n vel[0] += ang[1] * (nc.z - c.z) - ang[2] * (nc.y - c.y)\n vel[1] += ang[2] * (nc.x - c.x) - ang[0] * (nc.z - c.z)\n vel[2] += ang[0] * (nc.y - c.y) - ang[1] * (nc.x - c.x)\n }\n const piece = this.pieceSys.create(newId, group, half, center, this.massOf(newId), vel, ang)\n nb.piece = piece\n this.o.onPiece?.(piece)\n }\n\n private onExplode(ev: Ev<'explode'>): void {\n const p = ev.point\n const k = clamp(ev.radius / 3, 0.4, 2.5)\n this.particles.burst('flash', p, [0, 1, 0], 1, 0, { size: k })\n this.particles.burst('fire', p, [0, 1, 0], 34 * k, 4 * k, { size: 1 + k * 0.5 })\n this.particles.burst('smoke', p, [0, 1, 0], 24 * k, 2.2, { color: SMOKE_TONES.black, size: 1 + k * 0.6 })\n this.particles.burst('dust', p, [0, 1, 0], 22 * k, 3.5 * k)\n this.particles.burst('sparks', p, [0, 1, 0], 16 * k, 8)\n this.particles.push(p, ev.radius, 4)\n if (this.o.explosionLight !== false) {\n if (!this.light) {\n this.light = new this.THREE.PointLight(0xffa860, 0, 10, 2)\n this.light.name = 'destruction-explosion-light'\n }\n if (!this.light.parent) this.scene.add(this.light)\n this.light.position.set(p[0], p[1] + 0.5, p[2])\n this.light.distance = Math.max(6, ev.radius * 3)\n this.lightPeak = clamp(Math.sqrt(Math.max(1, ev.energy)) * 0.6, 10, 400)\n this.lightDur = 0.35\n this.lightT = this.lightDur\n }\n // shove pieces and fragments\n const c = this.v1\n for (const pc of this.pieceSys.list) {\n this.pieceSys.centerOf(pc, c)\n const dx = c.x - p[0]\n const dy = c.y - p[1]\n const dz = c.z - p[2]\n const d = Math.hypot(dx, dy, dz)\n if (d > ev.radius) continue\n const fall = (1 - d / ev.radius) ** 2\n const speed = Math.min(30, Math.sqrt((2 * ev.energy * 0.02) / pc.mass)) * fall\n const inv = d > 1e-4 ? 1 / d : 0\n const dir: Vec3 = d > 1e-4 ? [dx * inv, dy * inv + 0.3, dz * inv] : [0, 1, 0]\n const spread = pc.halfExtents[0]\n this.pieceSys.impulse(pc, [dir[0] * speed * pc.mass, dir[1] * speed * pc.mass, dir[2] * speed * pc.mass], [c.x + (this.rng() - 0.5) * spread, c.y + spread * 0.5, c.z + (this.rng() - 0.5) * spread])\n }\n this.fragments.blast(p, ev.radius, Math.min(20, Math.sqrt(ev.energy) * 0.08))\n this.sound('explode', p, clamp(0.5 + ev.radius / 10, 0.5, 1))\n }\n\n private updateLight(dt: number): void {\n if (!this.light || this.lightT <= 0) return\n this.lightT = Math.max(0, this.lightT - dt)\n const t = this.lightT / this.lightDur\n this.light.intensity = this.lightPeak * t * t\n }\n\n // ----------------------------------------------------------------------------------------------------------------------------------\n // Fire, heat, char, melt, leaks\n // ----------------------------------------------------------------------------------------------------------------------------------\n\n private updateActive(dt: number): void {\n this.scanTimer += dt\n if (this.scanTimer >= 0.4) {\n this.scanTimer = 0\n for (const b of this.bounds.values()) {\n for (const pv of b.parts.values()) {\n if (pv.gone !== 'none' && pv.gone !== 'broken') continue\n const ps = this.world.part(b.id, pv.id)\n if (!ps) continue\n const def = this.world.materials.get(pv.material)\n if (\n ps.burning ||\n (def?.visual?.glows && ps.temperature > GLOW_FROM_C) ||\n Math.abs(ps.char - pv.charApplied) > 0.01 ||\n Math.abs(ps.melt - pv.meltApplied) > 0.01 ||\n (pv.gone === 'none' && (ps.status === 'melted' || ps.status === 'ashes'))\n ) {\n this.activate(b.id, pv.id)\n }\n }\n }\n }\n for (const [key, a] of this.active) {\n const pv = this.partVis(a.object, a.part)\n const ps = pv ? this.world.part(a.object, a.part) : null\n if (!pv || !ps) {\n this.active.delete(key)\n continue\n }\n const def = this.world.materials.get(pv.material)\n if (pv.gone === 'none') {\n this.visuals.setChar(pv, ps.char, def?.visual?.charColor ?? 0x1a1410)\n if (def?.visual?.glows) this.visuals.setGlow(pv, ps.temperature)\n this.visuals.setMelt(pv, ps.melt)\n if (ps.status === 'melted') this.onGone(a.object, pv, 'melted', def)\n else if (ps.status === 'ashes') this.onGone(a.object, pv, 'ashes', def)\n }\n const fire = this.fires.get(key)\n if (fire) {\n if (ps.burning && ps.status !== 'broken' && ps.status !== 'ashes' && ps.status !== 'melted') this.emitFire(fire, pv, def, dt)\n else this.fires.delete(key)\n }\n const hot = def?.visual?.glows === true && ps.temperature > GLOW_FROM_C\n if (!ps.burning && !hot && Math.abs(ps.char - pv.charApplied) < 0.01 && Math.abs(ps.melt - pv.meltApplied) < 0.01) {\n if (def?.visual?.glows && pv.gone === 'none') this.visuals.setGlow(pv, 0)\n this.active.delete(key)\n }\n }\n }\n\n private onGone(objectId: string, pv: PartVis, reason: 'melted' | 'ashes', def: MaterialDef | undefined): void {\n const bb = new this.THREE.Box3()\n for (const mv of pv.meshes) bb.union(this.visuals.worldBox(mv, this.box))\n this.visuals.hide(pv, reason)\n if (bb.isEmpty()) return\n const c = bb.getCenter(new this.THREE.Vector3())\n const s = bb.getSize(new this.THREE.Vector3())\n const footprint = Math.sqrt(Math.max(s.x * s.z, 1e-4))\n if (reason === 'melted') {\n this.ground.add('puddle', c.x, c.z, clamp(footprint * 0.55, 0.05, 1), this.partColor(pv, def) ?? fallbackColor(pv.material, def), objectId)\n } else {\n this.ground.add('ash', c.x, c.z, clamp(footprint * 0.35, 0.04, 0.5), 0x1d1a18, objectId)\n this.particles.burst('ash', [c.x, bb.max.y, c.z], [0, 1, 0], 10, 0.8)\n }\n }\n\n private emitFire(f: FireEmitter, pv: PartVis, def: MaterialDef | undefined, dt: number): void {\n const bb = this.box\n bb.makeEmpty()\n const tmp = new this.THREE.Box3()\n for (const mv of pv.meshes) if (this.visuals.shown(mv)) bb.union(this.visuals.worldBox(mv, tmp))\n let cx = f.point[0]\n let cy = f.point[1]\n let cz = f.point[2]\n let sx = 0.2\n let sy = 0.2\n let sz = 0.2\n if (!bb.isEmpty()) {\n const c = bb.getCenter(this.v1)\n const s = bb.getSize(this.v2)\n cx = c.x\n cy = bb.min.y\n cz = c.z\n sx = s.x\n sy = s.y\n sz = s.z\n }\n const vol = clamp(Math.cbrt(Math.max(sx * sy * sz, 1e-5)), 0.05, 1.5)\n f.accFire += (18 + 70 * vol) * dt\n f.accSmoke += (6 + 24 * vol) * dt\n const tone = SMOKE_TONES[def?.thermal.smoke ?? 'grey']\n const rnd = this.rng\n while (f.accFire >= 1) {\n f.accFire -= 1\n this.particles.spawn('fire', [cx + (rnd() - 0.5) * sx, cy + sy * (0.3 + 0.7 * rnd()), cz + (rnd() - 0.5) * sz], [(rnd() - 0.5) * 0.4, 0.5 + rnd() * 0.9, (rnd() - 0.5) * 0.4], { size: 0.6 + vol * 0.8 })\n }\n while (f.accSmoke >= 1) {\n f.accSmoke -= 1\n this.particles.spawn('smoke', [cx + (rnd() - 0.5) * sx, cy + sy * (0.8 + 0.4 * rnd()), cz + (rnd() - 0.5) * sz], [(rnd() - 0.5) * 0.3 + 0.1, 0.4 + rnd() * 0.5, (rnd() - 0.5) * 0.3], { color: tone, size: 0.6 + vol })\n }\n }\n\n private updateLeaks(dt: number): void {\n for (let i = this.leaks.length - 1; i >= 0; i--) {\n const l = this.leaks[i]\n const ps = this.world.part(l.object, l.part)\n if (!ps || ps.liters === undefined || ps.liters <= 0) {\n this.leaks.splice(i, 1)\n continue\n }\n l.acc += clamp(20 + l.rate * 400, 15, 120) * dt\n const color = FLUID_COLORS[l.fluid] ?? 0xaaaaaa\n const d = l.direction\n const dl = Math.hypot(d[0], d[1], d[2]) || 1\n const sp = l.pressurized ? 2.5 + this.rng() * 1.5 : 0.3 + this.rng() * 0.3\n while (l.acc >= 1) {\n l.acc -= 1\n const vel: Vec3 = [(d[0] / dl) * sp + (this.rng() - 0.5) * 0.25, (d[1] / dl) * sp + (this.rng() - 0.5) * 0.25, (d[2] / dl) * sp + (this.rng() - 0.5) * 0.25]\n if (l.fluid === 'gas') this.particles.spawn('steam', l.point, vel, { color, size: 0.5, alpha: 0.5 })\n else this.particles.spawn('drips', l.point, vel, { color, tag: this.rng() < 0.03 ? this.tagOf(l.object) : 0 })\n }\n }\n }\n\n private tagOf(object: string): number {\n let tag = this.homeTags.indexOf(object) + 1\n if (tag === 0) tag = this.homeTags.push(object)\n return tag\n }\n\n // ----------------------------------------------------------------------------------------------------------------------------------\n // Emit, clear, restore, stats, dispose\n // ----------------------------------------------------------------------------------------------------------------------------------\n\n emit(kind: DebrisKind | 'fire' | 'flash' | 'foam', point: Vec3, normal: Vec3, count: number, speed?: number): void {\n this.particles.burst(kind as ParticleKind, point, normal, count, speed ?? DEFAULT_SPEED[kind] ?? 1, kind === 'smoke' ? { color: SMOKE_TONES.grey } : undefined)\n }\n\n explode(point: Vec3, energy: number, radius: number): void {\n this.onExplode({ type: 'explode', object: '', part: '', time: this.time, point, energy, radius })\n }\n\n restore(objectId: string): void {\n const homes = new Set<string>([objectId])\n const self = this.bounds.get(objectId)\n if (self?.isSplit) for (const pv of self.parts.values()) homes.add(pv.home)\n for (const home of homes) this.restoreHome(home)\n for (const [key, a] of [...this.active]) if (homes.has(a.object)) this.active.delete(key)\n for (const [key, f] of [...this.fires]) if (homes.has(f.object)) this.fires.delete(key)\n for (let i = this.leaks.length - 1; i >= 0; i--) if (homes.has(this.leaks[i].object)) this.leaks.splice(i, 1)\n for (const home of homes) {\n this.fragments.removeHome(home)\n this.ground.removeHome(home)\n }\n }\n\n private restoreHome(home: string): void {\n const homeBound = this.bounds.get(home)\n for (const b of [...this.bounds.values()]) {\n for (const [pid, pv] of [...b.parts]) {\n if (pv.home !== home) continue\n this.visuals.restorePart(pv)\n if (b !== homeBound && homeBound) {\n b.parts.delete(pid)\n homeBound.parts.set(pid, pv)\n }\n }\n if (b.isSplit && b.parts.size === 0) {\n this.scene.remove(b.root)\n this.pieceSys.remove(b.id)\n this.bounds.delete(b.id)\n }\n }\n }\n\n clear(): void {\n this.queue.length = 0\n this.active.clear()\n this.fires.clear()\n this.leaks.length = 0\n for (const id of [...this.bounds.keys()]) if (this.bounds.has(id)) this.restore(id)\n // anything left of a split object whose home is gone\n for (const b of [...this.bounds.values()]) {\n if (b.isSplit) {\n for (const pv of b.parts.values()) this.visuals.restorePart(pv)\n this.scene.remove(b.root)\n this.pieceSys.remove(b.id)\n this.bounds.delete(b.id)\n }\n }\n this.fragments.clear()\n this.ground.clear()\n this.particles.clear()\n if (this.light) {\n this.light.intensity = 0\n this.lightT = 0\n this.scene.remove(this.light)\n }\n }\n\n stats(): { fragments: number; particles: number; pieces: number; decals: number } {\n return { fragments: this.fragments.count, particles: this.particles.live, pieces: this.pieceSys.list.length, decals: this.visuals.decalCount + this.ground.count }\n }\n\n dispose(): void {\n if (this.disposed) return\n this.unsubscribe()\n this.clear()\n for (const id of [...this.bounds.keys()]) this.unbind(id)\n this.fragments.dispose()\n this.ground.dispose()\n this.particles.dispose()\n this.visuals.dispose()\n this.light?.dispose?.()\n this.light = null\n this.disposed = true\n }\n}\n"],"mappings":"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ACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,aAAe;QACd,OAAS;MACV;MACA,WAAa;MACb,aAAe;MACf,aAAe;MACf,SAAW;QACV,OAAS;QACT,UAAY;QACZ,cAAgB;QAChB,cAAgB;MACjB;MACA,QAAU;QACT,OAAS;MACV;MACA,MAAQ;QACP;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,aAAe;QACd,OAAS;MACV;MACA,WAAa;MACb,aAAe;MACf,aAAe;MACf,SAAW;QACV,OAAS;QACT,UAAY;QACZ,cAAgB;QAChB,cAAgB;MACjB;MACA,QAAU;QACT,OAAS;MACV;MACA,MAAQ;QACP;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;QACT,OAAS;MACV;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;MACV;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;MACV;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;MACV;MACA,WAAa;MACb,aAAe;MACf,aAAe;MACf,SAAW;QACV,OAAS;QACT,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,eAAiB;MAClB;MACA,QAAU;QACT,OAAS;QACT,aAAe;MAChB;MACA,MAAQ;QACP;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,WAAa;MACb,aAAe;MACf,aAAe;MACf,SAAW;QACV,OAAS;QACT,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,eAAiB;MAClB;MACA,QAAU;QACT,OAAS;QACT,aAAe;MAChB;MACA,MAAQ;QACP;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;MACV;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,cAAgB;QAChB,cAAgB;QAChB,eAAiB;MAClB;MACA,MAAQ;QACP;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,WAAa;MACd;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,cAAgB;QAChB,cAAgB;MACjB;MACA,MAAQ;QACP;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,cAAgB;QAChB,cAAgB;MACjB;MACA,MAAQ;QACP;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,cAAgB;QAChB,cAAgB;MACjB;MACA,MAAQ;QACP;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,cAAgB;QAChB,cAAgB;MACjB;MACA,MAAQ;QACP;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,OAAS;QACT,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;QACT,WAAa;MACd;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;QACT,WAAa;MACd;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;MACV;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;MACV;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;MACV;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;QACT,WAAa;MACd;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;QACA;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;MACV;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;M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","names":["geo","quat","materialsOf"]}
|
|
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/math.ts","../src/fragments.ts","../src/ground.ts","../src/pieces.ts","../src/rebar.ts","../../game-destruction/src/chain.ts","../../game-destruction/src/math.ts","../../game-destruction/src/fracture.ts","../../game-destruction/src/breach.ts","../../game-destruction/src/layers.ts","../../game-destruction/src/rebar.ts","../../game-destruction/src/data/materials.json","../../game-destruction/src/data/material-hints.json","../../game-destruction/src/materials.ts","../../game-destruction/src/world.ts","../../game-destruction/src/data/assemblies.json","../../game-destruction/src/assemblies.ts","../../game-destruction/src/frame.ts","../../game-destruction/src/size.ts","../../game-destruction/src/locale.ts","../../game-destruction/src/contracts.ts","../src/spec.ts","../src/heat.ts","../src/visual.ts","../src/renderer.ts"],"sourcesContent":["export { createDestructionRenderer } from './renderer'\nexport { specFromObject3D } from './spec'\nexport type { PartHint } from './spec'\nexport { heatColor } from './heat'\nimport type { SpriteKind } from '@adatechnology/game-vfx'\n\n/** Every particle kind the renderer can emit (the game's sprite kinds, see `@adatechnology/game-vfx`). */\nexport type ParticleKind = SpriteKind\nexport type {\n BindOptions,\n DestructionRenderer,\n ExplodeOptions,\n Mesh,\n MeshRange,\n Object3D,\n Piece,\n Quality,\n Quat,\n RendererOptions,\n SpecFromObjectOptions,\n SpecFromObjectResult,\n ThreeNamespace,\n Vec3,\n} from './types'\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","import type { Vec3 } from '@adatechnology/game-destruction'\n\n/** Small deterministic PRNG (mulberry32): returns a function giving numbers in [0, 1). */\nexport function createRng(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/** Hash of a few numbers into an unsigned 32-bit seed. */\nexport function hashNumbers(...values: number[]): number {\n let h = 2166136261\n for (const v of values) {\n const n = Math.round(v * 1000) | 0\n h = Math.imul(h ^ n, 16777619)\n h ^= h >>> 13\n }\n return h >>> 0\n}\n\nexport function hashString(s: string): number {\n let h = 2166136261\n for (let i = 0; i < s.length; i++) h = Math.imul(h ^ s.charCodeAt(i), 16777619)\n return h >>> 0\n}\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 function vec3Length(v: Vec3): number {\n return Math.hypot(v[0], v[1], v[2])\n}\n\nexport function normalize3(v: Vec3): Vec3 {\n const l = Math.hypot(v[0], v[1], v[2])\n return l > 1e-9 ? [v[0] / l, v[1] / l, v[2] / l] : [0, 1, 0]\n}\n\n/** `0xRRGGBB` -> `[r, g, b]` in 0..1. */\nexport function hexToRgb(hex: number): [number, number, number] {\n return [((hex >> 16) & 255) / 255, ((hex >> 8) & 255) / 255, (hex & 255) / 255]\n}\n\nexport function mixHex(a: number, b: number, t: number): number {\n const ar = (a >> 16) & 255\n const ag = (a >> 8) & 255\n const ab = a & 255\n const r = Math.round(ar + (((b >> 16) & 255) - ar) * t)\n const g = Math.round(ag + (((b >> 8) & 255) - ag) * t)\n const bl = Math.round(ab + ((b & 255) - ab) * t)\n return (r << 16) | (g << 8) | bl\n}\n","import type * as THREE_NS from 'three'\nimport type { Fragment } from '@adatechnology/game-destruction'\nimport { clamp, createRng, hashString } from './math'\n\ninterface Live {\n mesh: THREE_NS.Mesh\n home: string\n vel: THREE_NS.Vector3\n ang: THREE_NS.Vector3\n half: THREE_NS.Vector3\n baseScale: THREE_NS.Vector3\n still: number\n asleep: boolean\n /** Seconds at rest. */\n rest: number\n /** 1 while alive; counts down to 0 while shrinking away. */\n fade: number\n dying: boolean\n}\n\nexport interface FragmentSystemOptions {\n scene: THREE_NS.Scene\n groundAt: (x: number, z: number) => number\n gravity: number\n max: number\n life: number\n}\n\n/** Light physics for fracture debris: gravity, ground bounce and friction, spin damping, settling flat, sleep, shrink-away and removal. */\nexport class FragmentSystem {\n private readonly list: Live[] = []\n private readonly box: THREE_NS.BoxGeometry\n private shardGeos: THREE_NS.BufferGeometry[] = []\n private readonly materials = new Map<string, THREE_NS.Material>()\n private readonly q: THREE_NS.Quaternion\n private readonly dq: THREE_NS.Quaternion\n private readonly m: THREE_NS.Matrix4\n private readonly v: THREE_NS.Vector3\n private readonly w: THREE_NS.Vector3\n private readonly rng = createRng(0x51ed270b)\n\n constructor(\n private readonly THREE: typeof THREE_NS,\n private readonly o: FragmentSystemOptions,\n ) {\n this.box = new THREE.BoxGeometry(1, 1, 1)\n this.q = new THREE.Quaternion()\n this.dq = new THREE.Quaternion()\n this.m = new THREE.Matrix4()\n this.v = new THREE.Vector3()\n this.w = new THREE.Vector3()\n }\n\n get count(): number {\n return this.list.length\n }\n\n /** Meshes of every live fragment (tests and picking). */\n meshes(): THREE_NS.Mesh[] {\n return this.list.map((f) => f.mesh)\n }\n\n /** One shared material per source material id + colour (+ transparency). */\n materialFor(materialId: string, color: number, transparent: boolean, opacity = 0.5): THREE_NS.Material {\n const key = `${materialId}:${color}:${transparent ? 1 : 0}`\n let m = this.materials.get(key)\n if (!m) {\n m = transparent\n ? new this.THREE.MeshStandardMaterial({ color, roughness: 0.15, metalness: 0.1, transparent: true, opacity, side: this.THREE.DoubleSide, depthWrite: false })\n : new this.THREE.MeshLambertMaterial({ color })\n this.materials.set(key, m)\n }\n return m\n }\n\n private shardGeometry(seed: number): THREE_NS.BufferGeometry {\n const idx = seed % 6\n let g = this.shardGeos[idx]\n if (!g) {\n const r = createRng(1000 + idx)\n const n = 3 + Math.floor(r() * 3) // triangle, quad or pentagon, irregular\n const shape = new this.THREE.Shape()\n const pts: Array<[number, number]> = []\n for (let i = 0; i < n; i++) {\n const a = (i / n) * Math.PI * 2 + (r() - 0.5) * 0.7\n const rad = 0.35 + r() * 0.15\n pts.push([Math.cos(a) * rad, Math.sin(a) * rad])\n }\n shape.moveTo(pts[0][0], pts[0][1])\n for (let i = 1; i < n; i++) shape.lineTo(pts[i][0], pts[i][1])\n shape.closePath()\n g = new this.THREE.ExtrudeGeometry(shape, { depth: 1, bevelEnabled: false })\n g.translate(0, 0, -0.5)\n this.shardGeos[idx] = g\n }\n return g\n }\n\n private markGeometry: THREE_NS.CircleGeometry | null = null\n private markMaterial: THREE_NS.MeshBasicMaterial | null = null\n\n /**\n * Bullet holes that stay on a fragment (`Fragment.marks`): dark discs that are children of its mesh, so they fall, spin and settle with it. The mesh is\n * a unit box scaled to the fragment, so each disc is scaled back; exact for a mark on a face, which is where a bullet hole is.\n */\n addMarks(mesh: THREE_NS.Mesh, f: Fragment): void {\n if (!f.marks?.length) return\n this.markGeometry ??= new this.THREE.CircleGeometry(1, 10)\n this.markMaterial ??= new this.THREE.MeshBasicMaterial({ color: 0x0b0a09, depthWrite: false, polygonOffset: true, polygonOffsetFactor: -4, polygonOffsetUnits: -4 })\n const sc = mesh.scale\n for (const mark of f.marks) {\n const disc = new this.THREE.Mesh(this.markGeometry, this.markMaterial)\n const radius = Math.max(0.004, mark.diameter) * 0.6\n const n = this.v.set(mark.normal[0], mark.normal[1], mark.normal[2]).normalize()\n disc.position.set(mark.point[0] / sc.x + (n.x * 0.002) / sc.x, mark.point[1] / sc.y + (n.y * 0.002) / sc.y, mark.point[2] / sc.z + (n.z * 0.002) / sc.z)\n disc.quaternion.setFromUnitVectors(this.w.set(0, 0, 1), n)\n disc.scale.set(radius / sc.x, radius / sc.y, radius / sc.z)\n mesh.add(disc)\n }\n }\n\n /** Create the mesh of a fragment and start its motion. `maxAllowed` is the live budget. */\n spawn(f: Fragment, material: THREE_NS.Material, home: string, maxAllowed: number): THREE_NS.Mesh {\n const geo = f.shape === 'shard' ? this.shardGeometry(hashString(f.id)) : this.box\n const mesh = new this.THREE.Mesh(geo, material)\n mesh.position.set(f.center[0], f.center[1], f.center[2])\n mesh.quaternion.set(f.rotation[0], f.rotation[1], f.rotation[2], f.rotation[3]).normalize()\n const sz = f.size\n const sc = new this.THREE.Vector3(Math.max(0.005, sz[0]), Math.max(0.005, sz[1]), Math.max(0.003, sz[2]))\n mesh.scale.copy(sc)\n mesh.castShadow = true\n this.o.scene.add(mesh)\n const live: Live = {\n mesh,\n home,\n vel: new this.THREE.Vector3(f.velocity[0], f.velocity[1], f.velocity[2]),\n ang: new this.THREE.Vector3(f.angularVelocity[0], f.angularVelocity[1], f.angularVelocity[2]),\n // shards are thin: the contact extent uses the real (small) thickness, the chunk uses half of each side\n half: sc.clone().multiplyScalar(0.5),\n baseScale: sc,\n still: 0,\n asleep: false,\n rest: 0,\n fade: 1,\n dying: false,\n }\n this.list.push(live)\n let alive = this.list.reduce((n, l) => n + (l.dying ? 0 : 1), 0)\n for (let i = 0; i < this.list.length && alive > maxAllowed; i++) {\n if (!this.list[i].dying) {\n this.list[i].dying = true\n alive--\n }\n }\n this.addMarks(mesh, f)\n return mesh\n }\n\n /** Add velocity to every fragment within `radius` of a point (blasts). */\n blast(point: [number, number, number], radius: number, speed: number): void {\n for (const f of this.list) {\n const d = this.v.copy(f.mesh.position).sub(this.w.set(point[0], point[1], point[2]))\n const dist = d.length()\n if (dist > radius || f.dying) continue\n const k = (1 - dist / radius) ** 2 * speed\n d.normalize().multiplyScalar(k)\n d.y += k * 0.4\n f.vel.add(d)\n f.ang.add(this.w.set(this.rng() - 0.5, this.rng() - 0.5, this.rng() - 0.5).multiplyScalar(k))\n f.asleep = false\n f.rest = 0\n }\n }\n\n update(dt: number): void {\n const g = this.o.gravity\n for (let i = this.list.length - 1; i >= 0; i--) {\n const f = this.list[i]\n if (f.dying) {\n f.fade -= dt / 0.6\n if (f.fade <= 0) {\n this.o.scene.remove(f.mesh)\n this.list.splice(i, 1)\n continue\n }\n f.mesh.scale.copy(f.baseScale).multiplyScalar(f.fade)\n }\n if (f.asleep) {\n f.rest += dt\n if (this.o.life > 0 && f.rest > this.o.life) f.dying = true\n continue\n }\n this.step(f, dt, g)\n }\n }\n\n private step(f: Live, dt: number, g: number): void {\n const mesh = f.mesh\n f.vel.y -= g * dt\n mesh.position.addScaledVector(f.vel, dt)\n const w = f.ang.length()\n if (w > 1e-5) {\n this.dq.setFromAxisAngle(this.v.copy(f.ang).multiplyScalar(1 / w), w * dt)\n mesh.quaternion.premultiply(this.dq).normalize()\n }\n // ground contact: lowest point of the oriented box\n this.m.makeRotationFromQuaternion(mesh.quaternion)\n const e = this.m.elements\n const h = f.half\n const sc = f.dying ? f.fade : 1\n const ext = (Math.abs(e[1]) * h.x + Math.abs(e[5]) * h.y + Math.abs(e[9]) * h.z) * sc\n const gy = this.o.groundAt(mesh.position.x, mesh.position.z)\n let contact = false\n if (mesh.position.y - ext < gy) {\n contact = true\n mesh.position.y = gy + ext\n if (f.vel.y < -0.6) {\n f.vel.y = -f.vel.y * 0.25\n f.vel.x *= 0.7\n f.vel.z *= 0.7\n f.ang.multiplyScalar(0.55)\n // a hit on a corner makes it tumble a little\n f.ang.x += (this.rng() - 0.5) * 2\n f.ang.z += (this.rng() - 0.5) * 2\n } else {\n f.vel.y = 0\n const fr = Math.exp(-7 * dt)\n f.vel.x *= fr\n f.vel.z *= fr\n f.ang.multiplyScalar(Math.exp(-6 * dt))\n }\n } else {\n f.ang.multiplyScalar(Math.exp(-0.15 * dt))\n }\n if (contact && f.vel.lengthSq() < 4) this.settle(f, dt)\n const slow = f.vel.lengthSq() < 0.012 && f.ang.lengthSq() < 0.2\n if (contact && slow) {\n f.still += dt\n if (f.still > 0.4) {\n f.asleep = true\n f.vel.set(0, 0, 0)\n f.ang.set(0, 0, 0)\n }\n } else {\n f.still = 0\n }\n }\n\n /** Tip the fragment towards lying on its flattest face. */\n private settle(f: Live, dt: number): void {\n const mesh = f.mesh\n this.m.makeRotationFromQuaternion(mesh.quaternion)\n const e = this.m.elements\n const up = [e[1], e[5], e[9]] // world-y component of each local axis\n const half = [f.half.x, f.half.y, f.half.z]\n let small = 0\n for (let i = 1; i < 3; i++) if (half[i] < half[small]) small = i\n let axis = small\n if (Math.abs(up[small]) < 0.35) {\n axis = 0\n for (let i = 1; i < 3; i++) if (Math.abs(up[i]) > Math.abs(up[axis])) axis = i\n }\n const sign = up[axis] >= 0 ? 1 : -1\n this.v.set(e[axis * 4], e[axis * 4 + 1], e[axis * 4 + 2]).multiplyScalar(sign)\n this.w.set(0, 1, 0)\n this.dq.setFromUnitVectors(this.v, this.w)\n this.q.copy(this.dq).multiply(mesh.quaternion)\n mesh.quaternion.slerp(this.q, clamp(dt * 7, 0, 1))\n }\n\n /** Remove the fragments of one object (after `restore`). */\n removeHome(home: string): void {\n for (let i = this.list.length - 1; i >= 0; i--) {\n if (this.list[i].home === home) {\n this.o.scene.remove(this.list[i].mesh)\n this.list.splice(i, 1)\n }\n }\n }\n\n clear(): void {\n for (const f of this.list) this.o.scene.remove(f.mesh)\n this.list.length = 0\n }\n\n dispose(): void {\n this.clear()\n this.box.dispose()\n this.markGeometry?.dispose()\n this.markMaterial?.dispose()\n for (const g of this.shardGeos) g?.dispose()\n this.shardGeos = []\n for (const m of this.materials.values()) m.dispose()\n this.materials.clear()\n }\n}\n","import type * as THREE_NS from 'three'\nimport { lerp } from './math'\n\ninterface Spot {\n mesh: THREE_NS.Mesh\n kind: 'puddle' | 'ash'\n home: string\n x: number\n z: number\n radius: number\n target: number\n}\n\nconst MAX_RADIUS = 1.2\n\n/** Flat discs on the ground: puddles of melted material and piles of ash. Nearby ones merge; the oldest go when the cap is reached. */\nexport class GroundSpots {\n private readonly spots: Spot[] = []\n private readonly geometry: THREE_NS.CircleGeometry\n private readonly materials = new Map<string, THREE_NS.MeshBasicMaterial>()\n\n constructor(\n private readonly THREE: typeof THREE_NS,\n private readonly scene: THREE_NS.Scene,\n private readonly groundAt: (x: number, z: number) => number,\n private readonly cap: number,\n ) {\n this.geometry = new THREE.CircleGeometry(1, 20)\n this.geometry.rotateX(-Math.PI / 2)\n }\n\n get count(): number {\n return this.spots.length\n }\n\n private material(kind: 'puddle' | 'ash', color: number): THREE_NS.MeshBasicMaterial {\n const key = `${kind}:${color}`\n let m = this.materials.get(key)\n if (!m) {\n m = new this.THREE.MeshBasicMaterial({\n color,\n transparent: true,\n opacity: kind === 'puddle' ? 0.88 : 0.95,\n depthWrite: false,\n polygonOffset: true,\n polygonOffsetFactor: -2,\n polygonOffsetUnits: -2,\n })\n this.materials.set(key, m)\n }\n return m\n }\n\n /** Add (or grow) a spot at (x, z). `radius` is the radius this addition contributes. */\n add(kind: 'puddle' | 'ash', x: number, z: number, radius: number, color: number, home: string): void {\n for (const s of this.spots) {\n if (s.kind !== kind) continue\n const d = Math.hypot(s.x - x, s.z - z)\n if (d < Math.max(s.target, radius) * 0.9) {\n s.target = Math.min(MAX_RADIUS, Math.sqrt(s.target * s.target + radius * radius * 0.5))\n return\n }\n }\n while (this.spots.length >= this.cap) this.remove(this.spots[0])\n const mesh = new this.THREE.Mesh(this.geometry, this.material(kind, color))\n const y = this.groundAt(x, z) + 0.004 + (this.spots.length % 8) * 0.0004\n mesh.position.set(x, y, z)\n mesh.scale.setScalar(0.001)\n mesh.renderOrder = 1\n this.scene.add(mesh)\n this.spots.push({ mesh, kind, home, x, z, radius: 0.001, target: Math.min(MAX_RADIUS, radius) })\n }\n\n private remove(s: Spot): void {\n const i = this.spots.indexOf(s)\n if (i >= 0) this.spots.splice(i, 1)\n this.scene.remove(s.mesh)\n }\n\n update(dt: number): void {\n const k = Math.min(1, dt * 3)\n for (const s of this.spots) {\n if (Math.abs(s.radius - s.target) < 1e-4) continue\n s.radius = lerp(s.radius, s.target, k)\n s.mesh.scale.setScalar(s.radius)\n }\n }\n\n /** Remove the spots made for one object (after its `restore`). */\n removeHome(home: string): void {\n for (const s of [...this.spots]) if (s.home === home) this.remove(s)\n }\n\n clear(): void {\n for (const s of [...this.spots]) this.remove(s)\n }\n\n dispose(): void {\n this.clear()\n this.geometry.dispose()\n for (const m of this.materials.values()) m.dispose()\n this.materials.clear()\n }\n}\n","import type * as THREE_NS from 'three'\nimport type { Vec3 } from '@adatechnology/game-destruction'\nimport type { PieceBody } from './internal'\nimport { clamp } from './math'\n\nexport interface PieceSystemOptions {\n groundAt: (x: number, z: number) => number\n gravity: number\n /** A piece hit the ground faster than 3 m/s (throttled per piece). */\n onHardLanding: (piece: PieceBody, point: Vec3, speed: number) => void\n /** Notified when a piece is about to be removed. */\n scene: THREE_NS.Scene\n}\n\nconst SUB_DT = 1 / 120\nconst MU = 0.7\nconst SLEEP_AFTER = 0.5\nconst HARD_LANDING = 3\n/** Contact tolerance in metres: corners this close to the ground count as touching, which keeps the contact set (and a resting box) stable. */\nconst SLOP = 0.002\n/** A resting support narrower than this fraction of the centre-of-mass height is a balancing act, not a rest: the piece topples. */\nconst NARROW = 0.3\n/** Angular acceleration (rad/s^2 per metre of height) that pushes a balancing piece off its edge, toward the side its centre of mass leans to. */\nconst TOPPLE = 3\n\n/**\n * Light rigid-body physics for loose pieces: gravity, the 8 corners of the local box against the ground with an impulse (restitution, friction,\n * angular response), damping, sleeping, and a gentle horizontal separation between pieces. Not a general solver: no piece-vs-scenery contact.\n */\nexport class PieceSystem {\n readonly list: PieceBody[] = []\n private time = 0\n private readonly p: THREE_NS.Vector3\n private readonly q: THREE_NS.Quaternion\n private readonly dq: THREE_NS.Quaternion\n private readonly m: THREE_NS.Matrix4\n private readonly ax: THREE_NS.Vector3\n\n constructor(\n private readonly THREE: typeof THREE_NS,\n private readonly o: PieceSystemOptions,\n ) {\n this.p = new THREE.Vector3()\n this.q = new THREE.Quaternion()\n this.dq = new THREE.Quaternion()\n this.m = new THREE.Matrix4()\n this.ax = new THREE.Vector3()\n }\n\n get(objectId: string): PieceBody | null {\n return this.list.find((b) => b.objectId === objectId) ?? null\n }\n\n create(objectId: string, root: THREE_NS.Object3D, halfExtents: Vec3, localCenter: THREE_NS.Vector3, mass: number, velocity?: Vec3, angular?: Vec3): PieceBody {\n const existing = this.get(objectId)\n if (existing) {\n existing.root = root\n existing.halfExtents = halfExtents\n existing.mass = mass\n this.setCenter(existing, localCenter)\n if (velocity) existing.velocity = [...velocity]\n existing.sleeping = false\n return existing\n }\n const body: PieceBody = {\n objectId,\n root,\n velocity: velocity ? [...velocity] : [0, 0, 0],\n angularVelocity: angular ? [...angular] : [0, 0, 0],\n halfExtents: [Math.max(0.01, halfExtents[0]), Math.max(0.01, halfExtents[1]), Math.max(0.01, halfExtents[2])],\n mass: Math.max(0.05, mass),\n sleeping: false,\n held: false,\n localCenter: [localCenter.x, localCenter.y, localCenter.z],\n centerV: localCenter.clone(),\n still: 0,\n lastHit: -1,\n onGround: false,\n }\n this.list.push(body)\n return body\n }\n\n /** Move the centre of mass (after a part broke off). */\n setCenter(b: PieceBody, c: THREE_NS.Vector3): void {\n b.centerV.copy(c)\n b.localCenter = [c.x, c.y, c.z]\n }\n\n remove(objectId: string): void {\n const i = this.list.findIndex((b) => b.objectId === objectId)\n if (i >= 0) this.list.splice(i, 1)\n }\n\n clear(): void {\n this.list.length = 0\n }\n\n /** World position of the centre of mass. */\n centerOf(b: PieceBody, out: THREE_NS.Vector3): THREE_NS.Vector3 {\n return out.copy(b.centerV).applyQuaternion(b.root.quaternion).add(b.root.position)\n }\n\n impulse(b: PieceBody, impulse: Vec3, point?: Vec3): void {\n const inv = 1 / b.mass\n b.velocity[0] += impulse[0] * inv\n b.velocity[1] += impulse[1] * inv\n b.velocity[2] += impulse[2] * inv\n if (point) {\n const c = this.centerOf(b, this.p)\n const rx = point[0] - c.x\n const ry = point[1] - c.y\n const rz = point[2] - c.z\n // torque = r x J, angular velocity change = I^-1 torque\n const tx = ry * impulse[2] - rz * impulse[1]\n const ty = rz * impulse[0] - rx * impulse[2]\n const tz = rx * impulse[1] - ry * impulse[0]\n const w = this.mulInvInertia(b, tx, ty, tz)\n b.angularVelocity[0] += w[0]\n b.angularVelocity[1] += w[1]\n b.angularVelocity[2] += w[2]\n }\n b.sleeping = false\n b.still = 0\n }\n\n /** I^-1 * (x, y, z) in world space for a box with the body's half extents and mass (needs `this.m` to hold the body's rotation). */\n private mulInvInertia(b: PieceBody, x: number, y: number, z: number): Vec3 {\n this.m.makeRotationFromQuaternion(b.root.quaternion)\n return this.mulInvInertiaWith(b, this.m.elements, x, y, z)\n }\n\n private mulInvInertiaWith(b: PieceBody, e: ArrayLike<number>, x: number, y: number, z: number): Vec3 {\n const [hx, hy, hz] = b.halfExtents\n const k = 3 / b.mass\n const ix = k / (hy * hy + hz * hz)\n const iy = k / (hx * hx + hz * hz)\n const iz = k / (hx * hx + hy * hy)\n // local = R^T v (columns of R are the local axes in world space)\n const lx = (e[0] * x + e[1] * y + e[2] * z) * ix\n const ly = (e[4] * x + e[5] * y + e[6] * z) * iy\n const lz = (e[8] * x + e[9] * y + e[10] * z) * iz\n return [e[0] * lx + e[4] * ly + e[8] * lz, e[1] * lx + e[5] * ly + e[9] * lz, e[2] * lx + e[6] * ly + e[10] * lz]\n }\n\n update(dt: number): void {\n this.time += dt\n for (const b of this.list) {\n if (b.held) {\n b.velocity = [0, 0, 0]\n b.angularVelocity = [0, 0, 0]\n b.sleeping = false\n b.still = 0\n continue\n }\n if (b.sleeping) continue\n this.integrate(b, dt)\n }\n this.separate(dt)\n }\n\n private integrate(b: PieceBody, dt: number): void {\n const c = this.centerOf(b, this.p)\n const pos: Vec3 = [c.x, c.y, c.z]\n const q = this.q.copy(b.root.quaternion)\n const v = b.velocity\n const w = b.angularVelocity\n const [hx, hy, hz] = b.halfExtents\n const invM = 1 / b.mass\n const steps = clamp(Math.ceil(dt / SUB_DT), 1, 8)\n const h = dt / steps\n let landing = 0\n let landPoint: Vec3 = [pos[0], pos[1], pos[2]]\n let contactedLast = false\n let touched = false\n let unstable = false\n\n for (let s = 0; s < steps; s++) {\n v[1] -= this.o.gravity * h\n pos[0] += v[0] * h\n pos[1] += v[1] * h\n pos[2] += v[2] * h\n const wl = Math.hypot(w[0], w[1], w[2])\n if (wl > 1e-6) {\n this.dq.setFromAxisAngle(this.ax.set(w[0] / wl, w[1] / wl, w[2] / wl), wl * h)\n q.premultiply(this.dq).normalize()\n }\n const damp = Math.exp(-0.15 * h)\n w[0] *= damp\n w[1] *= damp\n w[2] *= damp\n\n this.m.makeRotationFromQuaternion(q)\n const e = this.m.elements\n const rs: Vec3[] = []\n let maxPen = 0\n for (let i = 0; i < 8; i++) {\n const sx = i & 1 ? hx : -hx\n const sy = i & 2 ? hy : -hy\n const sz = i & 4 ? hz : -hz\n const r: Vec3 = [e[0] * sx + e[4] * sy + e[8] * sz, e[1] * sx + e[5] * sy + e[9] * sz, e[2] * sx + e[6] * sy + e[10] * sz]\n const gy = this.o.groundAt(pos[0] + r[0], pos[2] + r[2])\n const pen = gy - (pos[1] + r[1])\n if (pen > -SLOP) {\n rs.push(r)\n if (pen > maxPen) maxPen = pen\n }\n }\n contactedLast = rs.length > 0\n if (contactedLast) touched = true\n if (!contactedLast) continue\n pos[1] += Math.max(0, maxPen - SLOP * 0.5)\n let impact = 0\n for (const r of rs) impact = Math.max(impact, -(v[1] + w[2] * r[0] - w[0] * r[2]))\n const restitution = impact > 2 ? 0.25 : 0\n if (impact > landing) {\n landing = impact\n let sx = 0\n let sz = 0\n for (const r of rs) {\n sx += r[0]\n sz += r[2]\n }\n const lx = pos[0] + sx / rs.length\n const lz = pos[2] + sz / rs.length\n landPoint = [lx, this.o.groundAt(lx, lz), lz]\n }\n for (let it = 0; it < 4; it++) {\n for (const r of rs) {\n // contact point velocity\n let vx = v[0] + (w[1] * r[2] - w[2] * r[1])\n let vy = v[1] + (w[2] * r[0] - w[0] * r[2])\n let vz = v[2] + (w[0] * r[1] - w[1] * r[0])\n const vn = vy\n if (vn >= 0) continue\n // normal impulse (n = +y): r x n = (-rz, 0, rx)\n const a = this.mulInvInertiaWith(b, e, -r[2], 0, r[0])\n const denom = invM + (a[2] * r[0] - a[0] * r[2])\n const j = (-(1 + (it === 0 ? restitution : 0)) * vn) / denom\n v[1] += j * invM\n const dw = this.mulInvInertiaWith(b, e, -r[2] * j, 0, r[0] * j)\n w[0] += dw[0]\n w[1] += dw[1]\n w[2] += dw[2]\n // friction in the contact plane\n vx = v[0] + (w[1] * r[2] - w[2] * r[1])\n vy = v[1] + (w[2] * r[0] - w[0] * r[2])\n vz = v[2] + (w[0] * r[1] - w[1] * r[0])\n const vt = Math.hypot(vx, vz)\n if (vt > 1e-5) {\n const tx = vx / vt\n const tz = vz / vt\n // r x t with t = (tx, 0, tz) = (0*tz - rz*0 ... ) -> (ry*tz, rz*tx - rx*tz, -ry*tx)\n const rx = r[1] * tz\n const ry = r[2] * tx - r[0] * tz\n const rz = -r[1] * tx\n const at = this.mulInvInertiaWith(b, e, rx, ry, rz)\n const cx = at[1] * r[2] - at[2] * r[1]\n const cz = at[0] * r[1] - at[1] * r[0]\n const denomT = invM + cx * tx + cz * tz\n const jt = Math.min(vt / denomT, MU * j)\n v[0] -= tx * jt * invM\n v[2] -= tz * jt * invM\n const dwt = this.mulInvInertiaWith(b, e, rx * jt, ry * jt, rz * jt)\n w[0] -= dwt[0]\n w[1] -= dwt[1]\n w[2] -= dwt[2]\n }\n }\n }\n // Support check. The corner contacts above are relative to the centre of mass, so they already give a torque when the centre of mass is\n // outside the support. What they cannot do is end a balancing act: a tall thin piece on its edge (or a single corner) is in equilibrium\n // with the centre of mass above a very small support, so it must not rest there. It gets a gentle topple push toward the side it leans\n // to (or toward the wider side when it leans nowhere), and it may not sleep.\n let x0 = Infinity, x1 = -Infinity, z0 = Infinity, z1 = -Infinity, low = 0\n for (const r of rs) {\n x0 = Math.min(x0, r[0]); x1 = Math.max(x1, r[0])\n z0 = Math.min(z0, r[2]); z1 = Math.max(z1, r[2])\n low = Math.min(low, r[1])\n }\n const height = -low\n const wx = x1 - x0\n const wz = z1 - z0\n unstable = Math.min(wx, wz) < NARROW * height || x0 > 0 || x1 < 0 || z0 > 0 || z1 < 0\n if (unstable) {\n // the support centre relative to the centre of mass: the piece falls away from it (toward +axis when it is centred)\n const dirX = x0 + x1 > 0 ? -1 : 1\n const dirZ = z0 + z1 > 0 ? -1 : 1\n const k = TOPPLE * Math.min(height, 1) * h\n // rotating about -z moves the top toward +x; rotating about +x moves it toward +z\n if (wx <= wz) w[2] -= dirX * k\n else w[0] += dirZ * k\n }\n // rolling / spinning resistance while touching the ground\n const rd = Math.exp((rs.length >= 3 ? -3 : -1.5) * h)\n w[0] *= rd\n w[1] *= rd\n w[2] *= rd\n const fd = Math.exp(-0.3 * h)\n v[0] *= fd\n v[2] *= fd\n }\n\n // write back\n b.root.quaternion.copy(q)\n const off = this.ax.copy(b.centerV).applyQuaternion(q)\n b.root.position.set(pos[0] - off.x, pos[1] - off.y, pos[2] - off.z)\n b.root.updateMatrixWorld(true)\n b.onGround = contactedLast\n\n if (landing > HARD_LANDING && this.time - b.lastHit > 0.15) {\n b.lastHit = this.time\n this.o.onHardLanding(b, landPoint, landing)\n }\n\n const still = touched && !unstable && Math.hypot(v[0], v[1], v[2]) < 0.12 && Math.hypot(w[0], w[1], w[2]) < 0.45\n if (still) {\n b.still += dt\n if (b.still >= SLEEP_AFTER) {\n b.sleeping = true\n v[0] = v[1] = v[2] = 0\n w[0] = w[1] = w[2] = 0\n }\n } else {\n b.still = 0\n }\n }\n\n /** Push overlapping pieces apart (horizontal bounding circles), gently, so they do not interpenetrate forever. */\n private separate(dt: number): void {\n const n = this.list.length\n if (n < 2) return\n const maxMove = 1.5 * dt\n const ca = new this.THREE.Vector3()\n const cb = new this.THREE.Vector3()\n for (let i = 0; i < n; i++) {\n const a = this.list[i]\n for (let j = i + 1; j < n; j++) {\n const b = this.list[j]\n if (a.held && b.held) continue\n if (a.sleeping && b.sleeping) continue\n const ra = Math.hypot(a.halfExtents[0], a.halfExtents[1], a.halfExtents[2])\n const rb = Math.hypot(b.halfExtents[0], b.halfExtents[1], b.halfExtents[2])\n this.centerOf(a, ca)\n this.centerOf(b, cb)\n if (Math.abs(ca.y - cb.y) > ra + rb) continue\n const rh = (Math.max(a.halfExtents[0], a.halfExtents[2]) + Math.max(b.halfExtents[0], b.halfExtents[2])) * 0.8\n let dx = cb.x - ca.x\n let dz = cb.z - ca.z\n let d = Math.hypot(dx, dz)\n if (d >= rh) continue\n if (d < 1e-5) {\n dx = 1\n dz = 0\n d = 1\n }\n const push = Math.min(maxMove, (rh - d) * 0.5)\n const wa = a.held ? 0 : b.held ? 1 : b.mass / (a.mass + b.mass)\n const wb = b.held ? 0 : a.held ? 1 : a.mass / (a.mass + b.mass)\n const ux = dx / (d || 1)\n const uz = dz / (d || 1)\n if (!a.held) {\n a.root.position.x -= ux * push * 2 * wa\n a.root.position.z -= uz * push * 2 * wa\n a.sleeping = false\n }\n if (!b.held) {\n b.root.position.x += ux * push * 2 * wb\n b.root.position.z += uz * push * 2 * wb\n b.sleeping = false\n }\n }\n }\n }\n}\n","import type * as THREE_NS from 'three'\nimport type { Vec3 } from '@adatechnology/game-destruction'\n\n/** Bare rebar of reinforced parts: one instanced cylinder mesh per part, replaced whenever the part exposes more (or less) of its cage. */\nexport class RebarCages {\n private readonly cages = new Map<string, THREE_NS.InstancedMesh>()\n private readonly geometry: THREE_NS.CylinderGeometry\n private readonly material: THREE_NS.MeshStandardMaterial\n private readonly up: THREE_NS.Vector3\n private readonly dir: THREE_NS.Vector3\n private readonly mid: THREE_NS.Vector3\n private readonly q: THREE_NS.Quaternion\n private readonly scale: THREE_NS.Vector3\n private readonly m: THREE_NS.Matrix4\n\n constructor(\n private readonly THREE: typeof THREE_NS,\n private readonly scene: THREE_NS.Scene,\n radius = 0.004,\n ) {\n this.geometry = new THREE.CylinderGeometry(radius, radius, 1, 6)\n this.material = new THREE.MeshStandardMaterial({ color: 0x74665c, roughness: 0.55, metalness: 0.6 })\n this.up = new THREE.Vector3(0, 1, 0)\n this.dir = new THREE.Vector3()\n this.mid = new THREE.Vector3()\n this.q = new THREE.Quaternion()\n this.scale = new THREE.Vector3()\n this.m = new THREE.Matrix4()\n }\n\n /** Bars drawn now, over every part. */\n get count(): number {\n let n = 0\n for (const mesh of this.cages.values()) n += mesh.count\n return n\n }\n\n /** Meshes of the live cages (tests and picking). */\n meshes(): THREE_NS.InstancedMesh[] {\n return [...this.cages.values()]\n }\n\n /** Draw `bars` (world space) as the cage of `key`; an empty list removes it. */\n set(key: string, bars: ReadonlyArray<readonly [Vec3, Vec3]>): void {\n this.remove(key)\n if (bars.length === 0) return\n const mesh = new this.THREE.InstancedMesh(this.geometry, this.material, bars.length)\n bars.forEach(([a, b], i) => {\n this.dir.set(b[0] - a[0], b[1] - a[1], b[2] - a[2])\n const length = this.dir.length()\n this.mid.set((a[0] + b[0]) / 2, (a[1] + b[1]) / 2, (a[2] + b[2]) / 2)\n this.q.setFromUnitVectors(this.up, length > 1e-9 ? this.dir.divideScalar(length) : this.up)\n this.m.compose(this.mid, this.q, this.scale.set(1, Math.max(length, 1e-4), 1))\n mesh.setMatrixAt(i, this.m)\n })\n mesh.instanceMatrix.needsUpdate = true\n mesh.castShadow = true\n this.scene.add(mesh)\n this.cages.set(key, mesh)\n }\n\n remove(key: string): void {\n const mesh = this.cages.get(key)\n if (!mesh) return\n this.scene.remove(mesh)\n mesh.dispose()\n this.cages.delete(key)\n }\n\n /** Remove the cages of every part of `object` (keys are `object|part`). */\n removeObject(object: string): void {\n for (const key of [...this.cages.keys()]) if (key.startsWith(`${object}|`)) this.remove(key)\n }\n\n clear(): void {\n for (const key of [...this.cages.keys()]) this.remove(key)\n }\n\n dispose(): void {\n this.clear()\n this.geometry.dispose()\n this.material.dispose()\n }\n}\n","import type { ChainDelay, ExplosionChain, PartSpec } from './types'\n\n/** What can set an explosive part off from outside, in the order a hit can carry it. */\nexport type ChainTrigger = 'shot' | 'fire' | 'explosion'\n\n/** Temperature in °C that primes a part with `chain.fire` when its `explodes.atC` is not set. */\nexport const FIRE_PRIME_C = 300\n\n/** Seconds from a trigger to the blast: a number is fixed, `[min, max]` is drawn from the world seed (one draw). */\nexport const drawChainDelay = (delay: ChainDelay, random: () => number): number => {\n if (typeof delay === 'number') return delay\n const [min, max] = delay\n return min + random() * (max - min)\n}\n\n/** The delay `spec` gives for `trigger`, or undefined when the part does not answer to it. */\nexport const chainDelayOf = (spec: PartSpec, trigger: ChainTrigger): ChainDelay | undefined => spec.explodes?.chain?.[trigger]\n\n/** Fraction of the hit points a shot must have taken off before it primes the part. */\nexport const shotDamageFractionOf = (spec: PartSpec): number => spec.explodes?.chain?.shotDamageFraction ?? 0\n\n/** The numbers of an explosive that sits in the world, as `game-weapons` gives them (`ExplosiveDef` fits). */\nexport interface WorldExplosive {\n readonly energy: number\n /** Metres within which the blast reaches other parts (and primes theirs). */\n readonly injuryRadius: number\n readonly chain?: ExplosionChain\n}\n\nexport interface ExplosivePartOptions {\n /** Temperature in °C at which fire primes (or sets off) the part; default `FIRE_PRIME_C` when the explosive answers to fire. */\n readonly atC?: number\n}\n\n/**\n * A part that explodes with the energy and reach of `explosive` and answers to what `explosive.chain` says: a bullet, fire or a blast\n * nearby primes it and it goes off after the delay. `part` is everything else about the part (shape, material, position, fluid inside).\n */\nexport const explosivePart = (explosive: WorldExplosive, part: Omit<PartSpec, 'explodes'>, options: ExplosivePartOptions = {}): PartSpec => ({\n ...part,\n explodes: {\n energy: explosive.energy,\n radius: explosive.injuryRadius,\n ...(options.atC !== undefined ? { atC: options.atC } : {}),\n ...(explosive.chain ? { chain: { ...explosive.chain } } : {}),\n },\n})\n","import type { PartShape, Quat, Vec3 } from './types'\n\nconst EPS = 1e-9\n\n/** Small vector helpers (arrays of three numbers). Pure functions; they allocate a new array. */\nexport const vec = {\n add: (a: Vec3, b: Vec3): Vec3 => [a[0] + b[0], a[1] + b[1], a[2] + b[2]],\n sub: (a: Vec3, b: Vec3): Vec3 => [a[0] - b[0], a[1] - b[1], a[2] - b[2]],\n scale: (a: Vec3, s: number): Vec3 => [a[0] * s, a[1] * s, a[2] * s],\n dot: (a: Vec3, b: Vec3): number => a[0] * b[0] + a[1] * b[1] + a[2] * b[2],\n 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]],\n length: (a: Vec3): number => Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]),\n distance: (a: Vec3, b: Vec3): number => Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]),\n normalize: (a: Vec3, fallback: Vec3 = [0, 1, 0]): Vec3 => {\n const l = Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2])\n return l < EPS ? [fallback[0], fallback[1], fallback[2]] : [a[0] / l, a[1] / l, a[2] / l]\n },\n lerp: (a: Vec3, b: Vec3, t: number): Vec3 => [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t, a[2] + (b[2] - a[2]) * t],\n /** `a + b * s` */\n madd: (a: Vec3, b: Vec3, s: number): Vec3 => [a[0] + b[0] * s, a[1] + b[1] * s, a[2] + b[2] * s],\n}\n\nconst IDENTITY: Quat = [0, 0, 0, 1]\n\n/** Quaternion helpers (`[x, y, z, w]`). */\nexport const quat = {\n identity: (): Quat => [IDENTITY[0], IDENTITY[1], IDENTITY[2], IDENTITY[3]],\n /** Hamilton product `a * b` (apply `b` first, then `a`). */\n mul: (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 ],\n conjugate: (q: Quat): Quat => [-q[0], -q[1], -q[2], q[3]],\n normalize: (q: Quat): Quat => {\n const l = Math.hypot(q[0], q[1], q[2], q[3])\n return l < EPS ? [0, 0, 0, 1] : [q[0] / l, q[1] / l, q[2] / l, q[3] / l]\n },\n /** Rotation of `angle` radians around a unit `axis`. */\n fromAxisAngle: (axis: Vec3, angle: number): Quat => {\n const s = Math.sin(angle / 2)\n return [axis[0] * s, axis[1] * s, axis[2] * s, Math.cos(angle / 2)]\n },\n /** Rotation matrix, row-major 3x3. */\n toMatrix: (q: Quat): number[] => {\n const [x, y, z, w] = q\n const xx = x * x, yy = y * y, zz = z * z, xy = x * y, xz = x * z, yz = y * z, wx = w * x, wy = w * y, wz = w * z\n return [1 - 2 * (yy + zz), 2 * (xy - wz), 2 * (xz + wy), 2 * (xy + wz), 1 - 2 * (xx + zz), 2 * (yz - wx), 2 * (xz - wy), 2 * (yz + wx), 1 - 2 * (xx + yy)]\n },\n /** Quaternion from three orthonormal columns (the images of the x, y and z axes). */\n fromColumns: (cx: Vec3, cy: Vec3, cz: Vec3): Quat => {\n const m00 = cx[0], m10 = cx[1], m20 = cx[2]\n const m01 = cy[0], m11 = cy[1], m21 = cy[2]\n const m02 = cz[0], m12 = cz[1], m22 = cz[2]\n const tr = m00 + m11 + m22\n let q: Quat\n if (tr > 0) {\n const s = Math.sqrt(tr + 1) * 2\n q = [(m21 - m12) / s, (m02 - m20) / s, (m10 - m01) / s, 0.25 * s]\n } else if (m00 > m11 && m00 > m22) {\n const s = Math.sqrt(1 + m00 - m11 - m22) * 2\n q = [0.25 * s, (m01 + m10) / s, (m02 + m20) / s, (m21 - m12) / s]\n } else if (m11 > m22) {\n const s = Math.sqrt(1 + m11 - m00 - m22) * 2\n q = [(m01 + m10) / s, 0.25 * s, (m12 + m21) / s, (m02 - m20) / s]\n } else {\n const s = Math.sqrt(1 + m22 - m00 - m11) * 2\n q = [(m02 + m20) / s, (m12 + m21) / s, 0.25 * s, (m10 - m01) / s]\n }\n return quat.normalize(q)\n },\n}\n\n/** Quaternion from Euler angles in radians, order XYZ (the three.js default). */\nexport const quatFromEuler = (x: number, y: number, z: number): Quat => {\n const c1 = Math.cos(x / 2), c2 = Math.cos(y / 2), c3 = Math.cos(z / 2)\n const s1 = Math.sin(x / 2), s2 = Math.sin(y / 2), 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/** Rotate a vector by a quaternion. */\nexport const rotateVec = (q: Quat, v: Vec3): Vec3 => {\n const qx = q[0], qy = q[1], qz = q[2], qw = q[3]\n const tx = 2 * (qy * v[2] - qz * v[1])\n const ty = 2 * (qz * v[0] - qx * v[2])\n const tz = 2 * (qx * v[1] - qy * v[0])\n return [v[0] + qw * tx + (qy * tz - qz * ty), v[1] + qw * ty + (qz * tx - qx * tz), v[2] + qw * tz + (qx * ty - qy * tx)]\n}\n\n/** World point to the local space of a transform (`position`, `rotation`). */\nexport const toLocal = (position: Vec3, rotation: Quat, p: Vec3): Vec3 =>\n rotateVec([-rotation[0], -rotation[1], -rotation[2], rotation[3]], [p[0] - position[0], p[1] - position[1], p[2] - position[2]])\n\n/** Local point of a transform (`position`, `rotation`) to world space. */\nexport const toWorld = (position: Vec3, rotation: Quat, p: Vec3): Vec3 => {\n const r = rotateVec(rotation, p)\n return [r[0] + position[0], r[1] + position[1], r[2] + position[2]]\n}\n\n// ---------------------------------------------------------------------------------------------------------------------------------------------\n// Shapes (part space: centred on the origin, cylinders along +Y)\n// ---------------------------------------------------------------------------------------------------------------------------------------------\n\n/** Dimensions of the bounding box of a shape, in the shape's own axes. */\nexport const shapeDims = (s: PartShape): Vec3 => {\n if (s.kind === 'box') return [s.size[0], s.size[1], s.size[2]]\n if (s.kind === 'cylinder') return [s.radius * 2, s.height, s.radius * 2]\n return [s.radius * 2, s.radius * 2, s.radius * 2]\n}\n\n/** Radius of the sphere that encloses the shape. */\nexport const shapeBound = (s: PartShape): number => {\n if (s.kind === 'box') return 0.5 * Math.hypot(s.size[0], s.size[1], s.size[2])\n if (s.kind === 'cylinder') return Math.hypot(s.radius, s.height / 2)\n return s.radius\n}\n\nexport const shapeArea = (s: PartShape): number => {\n if (s.kind === 'box') return 2 * (s.size[0] * s.size[1] + s.size[1] * s.size[2] + s.size[0] * s.size[2])\n if (s.kind === 'cylinder') return 2 * Math.PI * s.radius * s.height + 2 * Math.PI * s.radius * s.radius\n return 4 * Math.PI * s.radius * s.radius\n}\n\nexport interface ShapeRay {\n /** Entry distance along the ray (negative when the origin is inside). */\n t0: number\n /** Normal at the entry, outward. */\n n0: Vec3\n /** Exit distance. */\n t1: number\n /** Normal at the exit, outward. */\n n1: Vec3\n}\n\n/** Exact ray intersection with a shape in part space. `d` need not be normalised but must be non-zero. */\nexport const rayShape = (s: PartShape, o: Vec3, d: Vec3): ShapeRay | null => {\n if (s.kind === 'box') {\n let tmin = -Infinity, tmax = Infinity\n let a0 = 0, a1 = 0, s0 = 1, s1 = 1\n for (let i = 0; i < 3; i++) {\n const h = s.size[i] / 2\n if (Math.abs(d[i]) < EPS) {\n if (Math.abs(o[i]) > h) return null\n continue\n }\n let ta = (-h - o[i]) / d[i]\n let tb = (h - o[i]) / d[i]\n let sa = -1, sb = 1\n if (ta > tb) {\n const t = ta\n ta = tb\n tb = t\n sa = 1\n sb = -1\n }\n if (ta > tmin) {\n tmin = ta\n a0 = i\n s0 = sa\n }\n if (tb < tmax) {\n tmax = tb\n a1 = i\n s1 = sb\n }\n if (tmin > tmax) return null\n }\n if (tmax < 0 || !Number.isFinite(tmin) || !Number.isFinite(tmax)) return null\n const n0: Vec3 = [0, 0, 0]\n const n1: Vec3 = [0, 0, 0]\n n0[a0] = s0\n n1[a1] = s1\n return { t0: tmin, n0, t1: tmax, n1 }\n }\n if (s.kind === 'sphere') {\n const b = vec.dot(o, d)\n const a = vec.dot(d, d)\n const c = vec.dot(o, o) - s.radius * s.radius\n const disc = b * b - a * c\n if (disc < 0) return null\n const sq = Math.sqrt(disc)\n const t0 = (-b - sq) / a\n const t1 = (-b + sq) / a\n if (t1 < 0) return null\n const p0 = vec.madd(o, d, t0)\n const p1 = vec.madd(o, d, t1)\n return { t0, n0: vec.normalize(p0), t1, n1: vec.normalize(p1) }\n }\n // cylinder along Y: intersection of the infinite cylinder interval and the y slab\n const r = s.radius\n const hh = s.height / 2\n const a = d[0] * d[0] + d[2] * d[2]\n const c = o[0] * o[0] + o[2] * o[2] - r * r\n let tA = -Infinity, tB = Infinity\n if (a > EPS) {\n const b = o[0] * d[0] + o[2] * d[2]\n const disc = b * b - a * c\n if (disc < 0) return null\n const sq = Math.sqrt(disc)\n tA = (-b - sq) / a\n tB = (-b + sq) / a\n } else if (c > 0) return null\n let yA = -Infinity, yB = Infinity\n if (Math.abs(d[1]) > EPS) {\n yA = (-hh - o[1]) / d[1]\n yB = (hh - o[1]) / d[1]\n if (yA > yB) {\n const t = yA\n yA = yB\n yB = t\n }\n } else if (Math.abs(o[1]) > hh) return null\n const t0 = Math.max(tA, yA)\n const t1 = Math.min(tB, yB)\n if (t0 > t1 || t1 < 0 || !Number.isFinite(t0) || !Number.isFinite(t1)) return null\n const normalAt = (t: number, fromSide: boolean, sign: number): Vec3 => {\n if (fromSide) {\n const p = vec.madd(o, d, t)\n return vec.normalize([p[0], 0, p[2]], [1, 0, 0])\n }\n return [0, sign, 0]\n }\n const n0 = normalAt(t0, tA >= yA, d[1] > 0 ? -1 : 1)\n const n1 = normalAt(t1, tB <= yB, d[1] > 0 ? 1 : -1)\n return { t0, n0, t1, n1 }\n}\n\n/** Distance from a part-space point to the shape (0 inside). */\nexport const distanceToShape = (s: PartShape, p: Vec3): number => {\n if (s.kind === 'box') {\n const dx = Math.max(Math.abs(p[0]) - s.size[0] / 2, 0)\n const dy = Math.max(Math.abs(p[1]) - s.size[1] / 2, 0)\n const dz = Math.max(Math.abs(p[2]) - s.size[2] / 2, 0)\n return Math.hypot(dx, dy, dz)\n }\n if (s.kind === 'sphere') return Math.max(0, vec.length(p) - s.radius)\n const dr = Math.max(0, Math.hypot(p[0], p[2]) - s.radius)\n const dy = Math.max(0, Math.abs(p[1]) - s.height / 2)\n return Math.hypot(dr, dy)\n}\n\n/** Nearest point on the surface of a shape to a part-space point, with the outward normal. */\nexport const surfacePoint = (s: PartShape, p: Vec3): { point: Vec3; normal: Vec3 } => {\n if (s.kind === 'box') {\n const h: Vec3 = [s.size[0] / 2, s.size[1] / 2, s.size[2] / 2]\n const inside = Math.abs(p[0]) <= h[0] && Math.abs(p[1]) <= h[1] && Math.abs(p[2]) <= h[2]\n if (!inside) {\n const c: Vec3 = [Math.max(-h[0], Math.min(h[0], p[0])), Math.max(-h[1], Math.min(h[1], p[1])), Math.max(-h[2], Math.min(h[2], p[2]))]\n return { point: c, normal: vec.normalize(vec.sub(p, c)) }\n }\n let axis = 0\n let best = Infinity\n for (let i = 0; i < 3; i++) {\n const g = h[i] - Math.abs(p[i])\n if (g < best) {\n best = g\n axis = i\n }\n }\n const sign = p[axis] >= 0 ? 1 : -1\n const point: Vec3 = [p[0], p[1], p[2]]\n point[axis] = sign * h[axis]\n const normal: Vec3 = [0, 0, 0]\n normal[axis] = sign\n return { point, normal }\n }\n if (s.kind === 'sphere') {\n const n = vec.normalize(p)\n return { point: vec.scale(n, s.radius), normal: n }\n }\n const rho = Math.hypot(p[0], p[2])\n const hh = s.height / 2\n const inside = rho <= s.radius && Math.abs(p[1]) <= hh\n if (!inside) {\n const k = rho > s.radius ? s.radius / rho : 1\n const c: Vec3 = [p[0] * k, Math.max(-hh, Math.min(hh, p[1])), p[2] * k]\n return { point: c, normal: vec.normalize(vec.sub(p, c)) }\n }\n if (s.radius - rho < hh - Math.abs(p[1])) {\n const k = rho < EPS ? 0 : s.radius / rho\n const point: Vec3 = rho < EPS ? [s.radius, p[1], 0] : [p[0] * k, p[1], p[2] * k]\n return { point, normal: vec.normalize([point[0], 0, point[2]], [1, 0, 0]) }\n }\n const sign = p[1] >= 0 ? 1 : -1\n return { point: [p[0], sign * hh, p[2]], normal: [0, sign, 0] }\n}\n\n/** Axis-aligned bounds of a shape with a given centre and orientation. */\nexport const aabbOf = (s: PartShape, center: Vec3, q: Quat): { min: Vec3; max: Vec3 } => {\n let hx: number, hy: number, hz: number\n if (s.kind === 'sphere') {\n hx = hy = hz = s.radius\n } else {\n const m = quat.toMatrix(q)\n if (s.kind === 'box') {\n const a = s.size[0] / 2, b = s.size[1] / 2, c = s.size[2] / 2\n hx = Math.abs(m[0]) * a + Math.abs(m[1]) * b + Math.abs(m[2]) * c\n hy = Math.abs(m[3]) * a + Math.abs(m[4]) * b + Math.abs(m[5]) * c\n hz = Math.abs(m[6]) * a + Math.abs(m[7]) * b + Math.abs(m[8]) * c\n } else {\n const ax = [m[1], m[4], m[7]]\n const hh = s.height / 2\n const e = (i: number) => s.radius * Math.sqrt(Math.max(0, 1 - ax[i] * ax[i])) + hh * Math.abs(ax[i])\n hx = e(0)\n hy = e(1)\n hz = e(2)\n }\n }\n return { min: [center[0] - hx, center[1] - hy, center[2] - hz], max: [center[0] + hx, center[1] + hy, center[2] + hz] }\n}\n\n/** Seeded generator (mulberry32). The state is a single int32, so it round-trips through JSON. */\nexport interface Rng {\n next(): number\n state(): number\n set(state: number): void\n}\n\nexport const createRng = (seed: number): Rng => {\n let s = seed | 0\n return {\n next() {\n s = (s + 0x6d2b79f5) | 0\n let t = Math.imul(s ^ (s >>> 15), 1 | s)\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296\n },\n state: () => s,\n set(v) {\n s = v | 0\n },\n }\n}\n","import { quat } from './math'\nimport type { Fragment, FractureStyle, Quat, Vec3 } from './types'\n\n/** A fragment in the part's local space (centre, size in the fragment's own axes, orientation relative to the part). */\nexport interface PlannedFragment {\n center: Vec3\n size: Vec3\n q: Quat\n shape: Fragment['shape']\n /** Share of the part's mass (unnormalised). */\n weight: number\n}\n\nexport interface PlanInput {\n style: FractureStyle\n /** Bounding box dimensions in part axes. */\n dims: Vec3\n thickness: number\n /** Index 0..2 of the grain axis, if any. */\n grain?: number\n /** Impact point in part space. */\n hit: Vec3\n fragmentSize: [number, number]\n maxFragments: number\n rnd: () => number\n}\n\ninterface Box {\n c: Vec3\n s: Vec3\n}\n\n/** Split a box into `count` pieces by cutting the largest piece along its longest allowed axis. */\nconst bsp = (root: Box, count: number, axes: number[], rnd: () => number): Box[] => {\n const pieces: Box[] = [root]\n const vol = (b: Box) => axes.reduce((v, a) => v * b.s[a], 1)\n while (pieces.length < count) {\n let bi = 0\n for (let i = 1; i < pieces.length; i++) if (vol(pieces[i]) > vol(pieces[bi])) bi = i\n const b = pieces[bi]\n let ax = axes[0]\n for (const a of axes) if (b.s[a] > b.s[ax]) ax = a\n if (b.s[ax] < 1e-4) break\n const r = 0.3 + 0.4 * rnd()\n const l1 = b.s[ax] * r\n const l2 = b.s[ax] - l1\n const c1: Vec3 = [b.c[0], b.c[1], b.c[2]]\n const c2: Vec3 = [b.c[0], b.c[1], b.c[2]]\n c1[ax] = b.c[ax] - b.s[ax] / 2 + l1 / 2\n c2[ax] = b.c[ax] + b.s[ax] / 2 - l2 / 2\n const s1: Vec3 = [b.s[0], b.s[1], b.s[2]]\n const s2: Vec3 = [b.s[0], b.s[1], b.s[2]]\n s1[ax] = l1\n s2[ax] = l2\n pieces[bi] = { c: c1, s: s1 }\n pieces.push({ c: c2, s: s2 })\n }\n return pieces\n}\n\nconst parity = (p: [number, number, number]): number => {\n let inv = 0\n for (let i = 0; i < 3; i++) for (let j = i + 1; j < 3; j++) if (p[i] > p[j]) inv++\n return inv % 2 === 0 ? 1 : -1\n}\n\n/** Orientation whose x, y, z axes are the part axes `ax`, `ay`, `az`, then rotated by `angle` around its own z. */\nconst frame = (ax: number, ay: number, az: number, angle = 0): Quat => {\n const e = (i: number): Vec3 => [i === 0 ? 1 : 0, i === 1 ? 1 : 0, i === 2 ? 1 : 0]\n const z = e(az)\n const sg = parity([ax, ay, az])\n const q = quat.fromColumns(e(ax), e(ay), [z[0] * sg, z[1] * sg, z[2] * sg])\n return angle === 0 ? q : quat.mul(q, quat.fromAxisAngle([0, 0, 1], angle))\n}\n\nconst clamp = (v: number, a: number, b: number) => Math.max(a, Math.min(b, v))\n\n/** Deterministic fragment layout for a failed part, in part space. */\nexport const planFragments = (inp: PlanInput): PlannedFragment[] => {\n const { style, dims, rnd, maxFragments } = inp\n const [fmin, fmax] = inp.fragmentSize\n const mean = (fmin + fmax) / 2\n const root: Box = { c: [0, 0, 0], s: [dims[0], dims[1], dims[2]] }\n const idq = quat.identity()\n const out: PlannedFragment[] = []\n const sorted = [0, 1, 2].sort((a, b) => dims[a] - dims[b])\n const t = sorted[0]\n const [u, v] = [sorted[1], sorted[2]].sort((a, b) => a - b)\n\n if (style === 'snap') {\n const n = Math.min(maxFragments, 2 + Math.floor(rnd() * 3))\n for (const b of bsp(root, n, [0, 1, 2], rnd)) out.push({ center: b.c, size: b.s, q: idq, shape: 'slab', weight: b.s[0] * b.s[1] * b.s[2] })\n return out\n }\n if (style === 'crumble' || style === 'none') {\n const n = clamp(Math.ceil((dims[0] * dims[1] * dims[2]) / (mean * mean * mean)), 4, maxFragments)\n for (const b of bsp(root, n, [0, 1, 2], rnd)) out.push({ center: b.c, size: b.s, q: idq, shape: 'chunk', weight: b.s[0] * b.s[1] * b.s[2] })\n return out\n }\n if (style === 'splinter') {\n const g = inp.grain ?? [0, 1, 2].sort((a, b) => dims[b] - dims[a])[0]\n const [a, b] = [0, 1, 2].filter((i) => i !== g)\n const w = Math.max(fmin * 1.5, 0.01)\n const c = clamp(Math.round((dims[a] * dims[b]) / (w * w)), 3, Math.max(3, Math.floor(maxFragments / 2)))\n const maxLen = fmax * 4\n const cells = bsp(root, c, [a, b], rnd)\n const pieces: Box[] = []\n for (const cell of cells) {\n let list = [cell]\n const extra = rnd() < 0.4 ? 1 : 0\n const k = Math.max(1, Math.ceil(dims[g] / maxLen)) + extra\n list = bsp(cell, k, [g], rnd)\n for (const p of list) pieces.push(p)\n }\n for (const p of pieces.slice(0, maxFragments)) {\n out.push({ center: p.c, size: [p.s[g], p.s[a], p.s[b]], q: frame(g, a, b), shape: 'splinter', weight: p.s[0] * p.s[1] * p.s[2] })\n }\n return out\n }\n if (style === 'tear') {\n const n = clamp(Math.round((dims[u] * dims[v]) / (mean * mean)), 3, maxFragments)\n const th = Math.min(inp.thickness, dims[t])\n for (const b of bsp(root, n, [u, v], rnd)) {\n out.push({ center: [b.c[0], b.c[1], b.c[2]], size: [b.s[u], b.s[v], th], q: frame(u, v, t), shape: 'scrap', weight: b.s[u] * b.s[v] })\n }\n return out\n }\n // shatter: thin shards radiating from the impact\n const th = Math.min(inp.thickness, dims[t])\n const Hu = dims[u] / 2, Hv = dims[v] / 2\n const hu = clamp(inp.hit[u], -Hu * 0.95, Hu * 0.95)\n const hv = clamp(inp.hit[v], -Hv * 0.95, Hv * 0.95)\n const R = Math.max(Math.hypot(Hu + Math.abs(hu), Hv + Math.abs(hv)), 0.02)\n const n = clamp(Math.round((dims[u] * dims[v]) / (mean * mean)), 6, maxFragments)\n const sectors = clamp(Math.round(Math.sqrt(n * 1.6)), 5, 10)\n const rings = Math.max(1, Math.ceil(n / sectors))\n const radii: number[] = []\n for (let k = 0; k <= rings; k++) radii.push(R * Math.pow(k / rings, 1.3))\n const cap = fmax * 1.5\n for (let j = 0; j < sectors && out.length < maxFragments; j++) {\n const theta = (j + (rnd() - 0.5) * 0.4) * ((Math.PI * 2) / sectors)\n for (let k = 0; k < rings && out.length < maxFragments; k++) {\n const rm = (radii[k] + radii[k + 1]) / 2\n const cu = hu + rm * Math.cos(theta)\n const cv = hv + rm * Math.sin(theta)\n if (Math.abs(cu) > Hu + 0.01 || Math.abs(cv) > Hv + 0.01) continue\n const radial = clamp(radii[k + 1] - radii[k], 0.01, cap)\n const tang = clamp(radii[k + 1] * ((Math.PI * 2) / sectors) * 0.9, 0.01, cap)\n const c: Vec3 = [0, 0, 0]\n c[u] = clamp(cu, -Hu * 0.98, Hu * 0.98)\n c[v] = clamp(cv, -Hv * 0.98, Hv * 0.98)\n c[t] = 0\n out.push({ center: c, size: [radial, tang, th], q: frame(u, v, t, theta), shape: 'shard', weight: radial * tang })\n }\n }\n if (out.length === 0) out.push({ center: [0, 0, 0], size: [dims[u], dims[v], th], q: frame(u, v, t), shape: 'shard', weight: 1 })\n return out\n}\n","import { quat, rotateVec } from './math'\nimport type { FragmentMark, PartUnits, Quat, Vec3 } from './types'\n\n/** One masonry unit (or panel) cut out of a wall, in the part's own axes. */\nexport interface UnitCell {\n center: Vec3\n size: Vec3\n}\n\n/** The two axes a wall spreads along: height is local y unless the part is thin along y (a slab), then x and z. */\nexport const planeAxes = (thin: number): [number, number] => (thin === 1 ? [0, 2] : [thin === 0 ? 2 : 0, 1])\n\n/**\n * Every unit of the wall laid in `bond`: rows along the plane's second axis, running bond shifts odd rows by half a unit, edge units are cut to\n * the wall. More than `cap` units are merged into bigger ones (the same bond) so a whole wall stays a drawable number of fragments.\n */\nexport const unitGrid = (dims: Vec3, thin: number, units: Pick<PartUnits, 'size' | 'bond'>, cap: number): UnitCell[] => {\n const [u, v] = planeAxes(thin)\n let unitU = Math.min(units.size[0], dims[u]), unitV = Math.min(units.size[1], dims[v])\n const rowsFor = (h: number): number => Math.ceil(dims[v] / h - 1e-9)\n const colsFor = (w: number): number => Math.ceil(dims[u] / w - 1e-9) + (units.bond === 'running' ? 1 : 0)\n const merge = Math.ceil(Math.sqrt((rowsFor(unitV) * colsFor(unitU)) / cap))\n if (merge > 1) {\n unitU = Math.min(unitU * merge, dims[u])\n unitV = Math.min(unitV * merge, dims[v])\n }\n const cells: UnitCell[] = []\n for (let row = 0; row < rowsFor(unitV); row++) {\n const v0 = -dims[v] / 2 + row * unitV, v1 = Math.min(v0 + unitV, dims[v] / 2)\n const shift = units.bond === 'running' && row % 2 === 1 ? unitU / 2 : 0\n for (let col = 0; col < colsFor(unitU); col++) {\n const u0 = Math.max(-dims[u] / 2 + col * unitU - shift, -dims[u] / 2), u1 = Math.min(-dims[u] / 2 + (col + 1) * unitU - shift, dims[u] / 2)\n if (u1 - u0 < 0.01) continue\n const center: Vec3 = [0, 0, 0], size: Vec3 = [0, 0, 0]\n center[u] = (u0 + u1) / 2\n center[v] = (v0 + v1) / 2\n size[u] = u1 - u0\n size[v] = v1 - v0\n size[thin] = dims[thin]\n cells.push({ center, size })\n }\n }\n return cells\n}\n\nconst planeDistance = (cell: UnitCell, thin: number, point: Vec3): number => {\n const [u, v] = planeAxes(thin)\n const du = cell.center[u] - point[u], dv = cell.center[v] - point[v]\n return Math.sqrt(du * du + dv * dv)\n}\n\n/** Units whose centre is within `radius` of `point` (part-local) in the wall plane; at least the nearest one. */\nexport const cellsWithin = (cells: readonly UnitCell[], thin: number, point: Vec3, radius: number): UnitCell[] => {\n const inside = cells.filter((c) => planeDistance(c, thin, point) <= radius)\n if (inside.length > 0 || cells.length === 0) return inside\n return [cells.reduce((best, c) => (planeDistance(c, thin, point) < planeDistance(best, thin, point) ? c : best))]\n}\n\nexport const cellsOutside = (cells: readonly UnitCell[], thin: number, point: Vec3, radius: number): UnitCell[] => cells.filter((c) => planeDistance(c, thin, point) > radius)\n\n/** A hole in the part's own axes. */\nexport interface LocalHole {\n point: Vec3\n normal: Vec3\n diameter: number\n}\n\nexport const MAX_MARKS_PER_FRAGMENT = 12\n\n/** The holes that fall inside a fragment box (`rotation` is the fragment's orientation in the part), in the fragment's own axes. */\nexport const marksInBox = (center: Vec3, size: Vec3, rotation: Quat | undefined, holes: readonly LocalHole[]): FragmentMark[] => {\n const inverse = rotation ? quat.conjugate(rotation) : undefined\n const marks: FragmentMark[] = []\n for (const hole of holes) {\n const offset: Vec3 = [hole.point[0] - center[0], hole.point[1] - center[1], hole.point[2] - center[2]]\n const point = inverse ? rotateVec(inverse, offset) : offset\n const inside = point.every((value, k) => Math.abs(value) <= size[k]! / 2 + hole.diameter / 2)\n if (!inside) continue\n marks.push({ point, normal: inverse ? rotateVec(inverse, hole.normal) : hole.normal, diameter: hole.diameter })\n if (marks.length >= MAX_MARKS_PER_FRAGMENT) break\n }\n return marks\n}\n","import type { MaterialDef, SurfaceLayer, Vec3 } from './types'\n\n/** A surface layer with its material resolved; `energy` is what a bullet spends crossing one face's worth of it, in joules. */\nexport interface LayerRT {\n index: number\n def: SurfaceLayer\n mat: MaterialDef\n energy: number\n}\n\n/** `[layer, face (+1 or -1), x, y, z, radius]`: a patch of a layer that came off, in object-local space. Saved with the part. */\nexport type Spall = readonly [number, number, number, number, number, number]\n\nexport const MAX_SPALLS = 64\n\n/** Index of the smallest dimension (the first one on a tie): the axis a wall is thin along. */\nexport const thinAxisOf = (dims: Vec3): number => (dims[0] <= dims[1] && dims[0] <= dims[2] ? 0 : dims[1] <= dims[2] ? 1 : 2)\n\nexport const resolveLayer = (def: SurfaceLayer, index: number, mat: MaterialDef): LayerRT => ({ index, def, mat, energy: mat.penetration * def.thickness * 1000 })\n\nconst onFace = (layer: LayerRT, face: number): boolean => layer.def.faces === 'both' || layer.def.faces === (face > 0 ? '+' : '-')\n\nconst isSpalled = (spalls: readonly Spall[] | undefined, layer: number, face: number, point: Vec3): boolean => {\n for (const s of spalls ?? []) {\n if (s[0] !== layer || s[1] !== face) continue\n const dx = s[2] - point[0], dy = s[3] - point[1], dz = s[4] - point[2]\n if (dx * dx + dy * dy + dz * dz <= s[5] * s[5]) return true\n }\n return false\n}\n\n/** The outermost layer still on `face` at `point` (layers are listed outermost first). */\nexport const outerLayer = (layers: readonly LayerRT[], spalls: readonly Spall[] | undefined, face: number, point: Vec3): LayerRT | undefined =>\n layers.find((l) => onFace(l, face) && !isSpalled(spalls, l.index, face, point))\n\n/** Joules a bullet spends crossing every layer still on `face` at `point`. */\nexport const layersEnergy = (layers: readonly LayerRT[], spalls: readonly Spall[] | undefined, face: number, point: Vec3): number => {\n let sum = 0\n for (const l of layers) if (onFace(l, face) && !isSpalled(spalls, l.index, face, point)) sum += l.energy\n return sum\n}\n\n/** Metres of layer a ray crosses on one face (full thickness, before obliquity). */\nexport const layersThickness = (layers: readonly LayerRT[], face: number): number => {\n let sum = 0\n for (const l of layers) if (onFace(l, face)) sum += l.def.thickness\n return sum\n}\n","import type { Vec3 } from './types'\n\n/** A bar as two end points. */\nexport type Bar = [Vec3, Vec3]\n\nconst COVER = 0.03\nconst MAX_BARS_PER_DIRECTION = 24\nconst MAX_MEMBER_BARS_PER_SIDE = 6\nconst STEEL_DENSITY = 7850\n\n/** Positions centred on 0 with exact `spacing`, as many as fit inside `length - 2 * cover` (at least two, at most `cap`). */\nconst spread = (length: number, cover: number, spacing: number, cap: number): number[] => {\n const room = Math.max(0, length - 2 * cover)\n const count = Math.min(cap, Math.max(2, Math.floor(room / spacing) + 1))\n const step = count > 1 && room / (count - 1) < spacing ? room / (count - 1) : spacing\n return Array.from({ length: count }, (_, i) => (i - (count - 1) / 2) * step)\n}\n\nconst bar = (axis: number, along: number, fixed: Array<[number, number]>): Bar => {\n const a: Vec3 = [0, 0, 0], b: Vec3 = [0, 0, 0]\n for (const [index, value] of fixed) {\n a[index] = value\n b[index] = value\n }\n a[axis] = -along\n b[axis] = along\n return [a, b]\n}\n\n/** Long members (columns, beams): two smaller dimensions close to each other and a much longer third one. */\nconst isMember = (dims: Vec3): boolean => {\n const [a = 0, b = 0, c = 0] = [...dims].sort((x, y) => x - y)\n return b / a < 3 && c / b >= 3\n}\n\n/**\n * Rebar of a box in the part's own axes. Plates (walls, slabs) get a grid along each of the two large axes at mid thickness; long members get\n * bars along their length at the perimeter of the section. `thin` is the axis the part is thinnest along.\n */\nexport const rebarBars = (dims: Vec3, thin: number, spacing: number): Bar[] => {\n const out: Bar[] = []\n if (isMember(dims)) {\n const long = dims.indexOf(Math.max(...dims))\n const [u = 0, v = 0] = [0, 1, 2].filter((i) => i !== long)\n const us = spread(dims[u], COVER, spacing, MAX_MEMBER_BARS_PER_SIDE)\n const vs = spread(dims[v], COVER, spacing, MAX_MEMBER_BARS_PER_SIDE)\n us.forEach((x, i) => vs.forEach((y, j) => {\n if (i === 0 || j === 0 || i === us.length - 1 || j === vs.length - 1) out.push(bar(long, dims[long] / 2 - 0.02, [[u, x], [v, y]]))\n }))\n return out\n }\n const [u = 0, v = 0] = [0, 1, 2].filter((i) => i !== thin)\n for (const y of spread(dims[v], COVER, spacing, MAX_BARS_PER_DIRECTION)) out.push(bar(u, dims[u] / 2 - 0.02, [[v, y], [thin, 0]]))\n for (const x of spread(dims[u], COVER, spacing, MAX_BARS_PER_DIRECTION)) out.push(bar(v, dims[v] / 2 - 0.02, [[u, x], [thin, 0]]))\n return out\n}\n\n/** The part of each bar that lies within `radius` of `center` in the plane of axes `u` and `v` (bars that miss the disc are dropped). */\nexport const clipBarsToDisc = (bars: readonly Bar[], u: number, v: number, center: [number, number], radius: number): Bar[] => {\n const out: Bar[] = []\n for (const [a, b] of bars) {\n const axis = a[u] !== b[u] ? u : v\n const other = axis === u ? v : u\n const off = a[other] - center[other === u ? 0 : 1]\n if (Math.abs(off) >= radius) continue\n const half = Math.sqrt(radius * radius - off * off)\n const mid = center[axis === u ? 0 : 1]\n const lo = Math.max(Math.min(a[axis], b[axis]), mid - half), hi = Math.min(Math.max(a[axis], b[axis]), mid + half)\n if (hi - lo < 1e-6) continue\n const p: Vec3 = [a[0], a[1], a[2]], q: Vec3 = [b[0], b[1], b[2]]\n p[axis] = lo\n q[axis] = hi\n out.push([p, q])\n }\n return out\n}\n\n/** Steel mass in kg of bars of `diameter` metres. */\nexport const rebarMass = (bars: readonly Bar[], diameter: number, density = STEEL_DENSITY): number => {\n let length = 0\n for (const [a, b] of bars) length += Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]) + Math.abs(a[2] - b[2])\n return Math.PI * (diameter / 2) * (diameter / 2) * length * density\n}\n","[\n {\n \"id\": \"steel\",\n \"density\": 7850,\n \"hpPerKg\": 400,\n \"penetration\": 110,\n \"ductility\": 0.9,\n \"fracture\": \"none\",\n \"damageScale\": {\n \"slash\": 0.1,\n \"blunt\": 0.8\n },\n \"hitDebris\": \"sparks\",\n \"breakDebris\": \"sparks\",\n \"ricochetDeg\": 20,\n \"thermal\": {\n \"meltC\": 1450,\n \"meltRate\": 0.05,\n \"conductivity\": 0.5,\n \"heatCapacity\": 3\n },\n \"visual\": {\n \"glows\": true\n },\n \"tags\": [\n \"metal\",\n \"conductive\"\n ],\n \"compressive\": 2000000\n },\n {\n \"id\": \"cast_iron\",\n \"density\": 7200,\n \"hpPerKg\": 150,\n \"penetration\": 160,\n \"ductility\": 0.2,\n \"fracture\": \"snap\",\n \"fragmentSize\": [\n 0.08,\n 0.3\n ],\n \"damageScale\": {\n \"slash\": 0.1,\n \"blunt\": 1.2\n },\n \"hitDebris\": \"sparks\",\n \"breakDebris\": \"chips\",\n \"ricochetDeg\": 15,\n \"thermal\": {\n \"meltC\": 1200,\n \"meltRate\": 0.05,\n \"conductivity\": 0.45,\n \"heatCapacity\": 3\n },\n \"visual\": {\n \"glows\": true\n },\n \"tags\": [\n \"metal\",\n \"conductive\"\n ],\n \"compressive\": 1500000\n },\n {\n \"id\": \"aluminium\",\n \"density\": 2700,\n \"hpPerKg\": 250,\n \"penetration\": 60,\n \"ductility\": 0.85,\n \"fracture\": \"none\",\n \"damageScale\": {\n \"slash\": 0.2\n },\n \"hitDebris\": \"sparks\",\n \"breakDebris\": \"sparks\",\n \"ricochetDeg\": 15,\n \"thermal\": {\n \"meltC\": 660,\n \"meltRate\": 0.06,\n \"conductivity\": 0.8,\n \"heatCapacity\": 1.5\n },\n \"visual\": {\n \"glows\": true\n },\n \"tags\": [\n \"metal\",\n \"conductive\"\n ],\n \"compressive\": 800000\n },\n {\n \"id\": \"copper\",\n \"density\": 8960,\n \"hpPerKg\": 300,\n \"penetration\": 70,\n \"ductility\": 0.95,\n \"fracture\": \"none\",\n \"damageScale\": {\n \"slash\": 0.2\n },\n \"hitDebris\": \"sparks\",\n \"breakDebris\": \"sparks\",\n \"ricochetDeg\": 15,\n \"thermal\": {\n \"meltC\": 1085,\n \"meltRate\": 0.05,\n \"conductivity\": 1,\n \"heatCapacity\": 2.5\n },\n \"visual\": {\n \"glows\": true\n },\n \"tags\": [\n \"metal\",\n \"conductive\"\n ],\n \"compressive\": 1000000\n },\n {\n \"id\": \"wood_soft\",\n \"density\": 500,\n \"hpPerKg\": 90,\n \"penetration\": 9,\n \"ductility\": 0.35,\n \"fracture\": \"splinter\",\n \"fragmentSize\": [\n 0.03,\n 0.15\n ],\n \"damageScale\": {\n \"blunt\": 1.2,\n \"slash\": 1.5\n },\n \"hitDebris\": \"splinters\",\n \"breakDebris\": \"splinters\",\n \"thermal\": {\n \"ignitionC\": 300,\n \"flameC\": 800,\n \"fuelFraction\": 0.9,\n \"burnRate\": 0.012,\n \"conductivity\": 0.05,\n \"heatCapacity\": 1.2,\n \"smoke\": \"grey\"\n },\n \"visual\": {\n \"charColor\": 1709072\n },\n \"tags\": [\n \"wood\",\n \"organic\",\n \"flammable\"\n ],\n \"compressive\": 100000\n },\n {\n \"id\": \"wood_hard\",\n \"density\": 750,\n \"hpPerKg\": 140,\n \"penetration\": 14,\n \"ductility\": 0.3,\n \"fracture\": \"splinter\",\n \"fragmentSize\": [\n 0.03,\n 0.2\n ],\n \"damageScale\": {\n \"slash\": 1.2\n },\n \"hitDebris\": \"splinters\",\n \"breakDebris\": \"splinters\",\n \"thermal\": {\n \"ignitionC\": 330,\n \"flameC\": 800,\n \"fuelFraction\": 0.9,\n \"burnRate\": 0.01,\n \"conductivity\": 0.06,\n \"heatCapacity\": 1.4,\n \"smoke\": \"grey\"\n },\n \"visual\": {\n \"charColor\": 1709072\n },\n \"tags\": [\n \"wood\",\n \"organic\",\n \"flammable\"\n ],\n \"compressive\": 150000\n },\n {\n \"id\": \"plywood\",\n \"density\": 600,\n \"hpPerKg\": 70,\n \"penetration\": 7,\n \"ductility\": 0.3,\n \"fracture\": \"snap\",\n \"fragmentSize\": [\n 0.1,\n 0.4\n ],\n \"damageScale\": {\n \"slash\": 1.3\n },\n \"hitDebris\": \"splinters\",\n \"breakDebris\": \"splinters\",\n \"thermal\": {\n \"ignitionC\": 300,\n \"flameC\": 800,\n \"fuelFraction\": 0.85,\n \"burnRate\": 0.013,\n \"conductivity\": 0.05,\n \"heatCapacity\": 1.2,\n \"smoke\": \"grey\"\n },\n \"visual\": {\n \"charColor\": 1709072\n },\n \"tags\": [\n \"wood\",\n \"organic\",\n \"flammable\"\n ],\n \"compressive\": 60000\n },\n {\n \"id\": \"mdf\",\n \"density\": 750,\n \"hpPerKg\": 45,\n \"penetration\": 6,\n \"ductility\": 0.15,\n \"fracture\": \"snap\",\n \"fragmentSize\": [\n 0.08,\n 0.35\n ],\n \"hitDebris\": \"chips\",\n \"breakDebris\": \"dust\",\n \"thermal\": {\n \"ignitionC\": 290,\n \"flameC\": 750,\n \"fuelFraction\": 0.85,\n \"burnRate\": 0.014,\n \"conductivity\": 0.05,\n \"heatCapacity\": 1.2,\n \"smoke\": \"grey\"\n },\n \"visual\": {\n \"charColor\": 1709072\n },\n \"tags\": [\n \"wood\",\n \"organic\",\n \"flammable\"\n ],\n \"compressive\": 30000\n },\n {\n \"id\": \"glass\",\n \"density\": 2500,\n \"hpPerKg\": 12,\n \"penetration\": 6,\n \"ductility\": 0.02,\n \"fracture\": \"shatter\",\n \"fragmentSize\": [\n 0.05,\n 0.25\n ],\n \"damageScale\": {\n \"blunt\": 1.5\n },\n \"hitDebris\": \"shards\",\n \"breakDebris\": \"shards\",\n \"ricochetDeg\": 8,\n \"thermal\": {\n \"meltC\": 1400,\n \"meltRate\": 0.03,\n \"conductivity\": 0.1,\n \"heatCapacity\": 1.5,\n \"thermalShockC\": 150\n },\n \"visual\": {\n \"glows\": true,\n \"transparent\": true\n },\n \"tags\": [\n \"glass\",\n \"brittle\"\n ],\n \"compressive\": 100000\n },\n {\n \"id\": \"tempered_glass\",\n \"density\": 2500,\n \"hpPerKg\": 30,\n \"penetration\": 9,\n \"ductility\": 0.02,\n \"fracture\": \"crumble\",\n \"fragmentSize\": [\n 0.006,\n 0.02\n ],\n \"hitDebris\": \"shards\",\n \"breakDebris\": \"shards\",\n \"ricochetDeg\": 8,\n \"thermal\": {\n \"meltC\": 1400,\n \"meltRate\": 0.03,\n \"conductivity\": 0.1,\n \"heatCapacity\": 1.5,\n \"thermalShockC\": 250\n },\n \"visual\": {\n \"glows\": true,\n \"transparent\": true\n },\n \"tags\": [\n \"glass\",\n \"brittle\"\n ],\n \"compressive\": 200000\n },\n {\n \"id\": \"ceramic\",\n \"density\": 2400,\n \"hpPerKg\": 25,\n \"penetration\": 12,\n \"ductility\": 0.02,\n \"fracture\": \"shatter\",\n \"fragmentSize\": [\n 0.03,\n 0.12\n ],\n \"damageScale\": {\n \"blunt\": 1.3\n },\n \"hitDebris\": \"chips\",\n \"breakDebris\": \"shards\",\n \"thermal\": {\n \"conductivity\": 0.1,\n \"heatCapacity\": 1.5,\n \"thermalShockC\": 200\n },\n \"tags\": [\n \"ceramic\",\n \"brittle\"\n ],\n \"compressive\": 150000\n },\n {\n \"id\": \"concrete\",\n \"density\": 2300,\n \"hpPerKg\": 14,\n \"penetration\": 18,\n \"ductility\": 0.03,\n \"fracture\": \"crumble\",\n \"fragmentSize\": [\n 0.05,\n 0.25\n ],\n \"damageScale\": {\n \"explosive\": 1.2\n },\n \"hitDebris\": \"chips\",\n \"breakDebris\": \"dust\",\n \"thermal\": {\n \"conductivity\": 0.15,\n \"heatCapacity\": 2.5\n },\n \"tags\": [\n \"masonry\",\n \"brittle\"\n ],\n \"compressive\": 444000\n },\n {\n \"id\": \"brick\",\n \"density\": 1900,\n \"hpPerKg\": 12,\n \"penetration\": 14,\n \"ductility\": 0.05,\n \"fracture\": \"crumble\",\n \"fragmentSize\": [\n 0.04,\n 0.12\n ],\n \"hitDebris\": \"chips\",\n \"breakDebris\": \"dust\",\n \"thermal\": {\n \"conductivity\": 0.15,\n \"heatCapacity\": 2.5\n },\n \"tags\": [\n \"masonry\",\n \"brittle\"\n ],\n \"compressive\": 40000\n },\n {\n \"id\": \"plaster\",\n \"density\": 850,\n \"hpPerKg\": 18,\n \"penetration\": 3,\n \"ductility\": 0.05,\n \"fracture\": \"crumble\",\n \"fragmentSize\": [\n 0.05,\n 0.3\n ],\n \"hitDebris\": \"dust\",\n \"breakDebris\": \"dust\",\n \"thermal\": {\n \"conductivity\": 0.1,\n \"heatCapacity\": 1.5\n },\n \"tags\": [\n \"masonry\",\n \"brittle\"\n ],\n \"compressive\": 8000\n },\n {\n \"id\": \"stone\",\n \"density\": 2700,\n \"hpPerKg\": 25,\n \"penetration\": 25,\n \"ductility\": 0.03,\n \"fracture\": \"crumble\",\n \"fragmentSize\": [\n 0.06,\n 0.3\n ],\n \"hitDebris\": \"chips\",\n \"breakDebris\": \"dust\",\n \"thermal\": {\n \"conductivity\": 0.2,\n \"heatCapacity\": 2.5\n },\n \"tags\": [\n \"masonry\",\n \"brittle\"\n ],\n \"compressive\": 300000\n },\n {\n \"id\": \"plastic\",\n \"density\": 1100,\n \"hpPerKg\": 60,\n \"penetration\": 8,\n \"ductility\": 0.5,\n \"fracture\": \"snap\",\n \"fragmentSize\": [\n 0.05,\n 0.3\n ],\n \"hitDebris\": \"chips\",\n \"breakDebris\": \"chips\",\n \"thermal\": {\n \"ignitionC\": 400,\n \"flameC\": 700,\n \"fuelFraction\": 0.8,\n \"burnRate\": 0.02,\n \"meltC\": 200,\n \"meltRate\": 0.06,\n \"conductivity\": 0.05,\n \"heatCapacity\": 1,\n \"smoke\": \"black\"\n },\n \"visual\": {\n \"charColor\": 855309\n },\n \"tags\": [\n \"plastic\",\n \"flammable\"\n ],\n \"compressive\": 40000\n },\n {\n \"id\": \"rubber\",\n \"density\": 1100,\n \"hpPerKg\": 80,\n \"penetration\": 12,\n \"ductility\": 0.9,\n \"fracture\": \"tear\",\n \"fragmentSize\": [\n 0.05,\n 0.2\n ],\n \"hitDebris\": \"chips\",\n \"breakDebris\": \"fibers\",\n \"thermal\": {\n \"ignitionC\": 350,\n \"flameC\": 750,\n \"fuelFraction\": 0.6,\n \"burnRate\": 0.015,\n \"conductivity\": 0.05,\n \"heatCapacity\": 1.2,\n \"smoke\": \"black\"\n },\n \"visual\": {\n \"charColor\": 855309\n },\n \"tags\": [\n \"rubber\",\n \"flammable\"\n ],\n \"compressive\": 20000\n },\n {\n \"id\": \"fabric\",\n \"density\": 400,\n \"hpPerKg\": 20,\n \"penetration\": 0.8,\n \"ductility\": 0.8,\n \"fracture\": \"tear\",\n \"fragmentSize\": [\n 0.05,\n 0.25\n ],\n \"damageScale\": {\n \"slash\": 4,\n \"ballistic\": 0.5\n },\n \"hitDebris\": \"fibers\",\n \"breakDebris\": \"fibers\",\n \"thermal\": {\n \"ignitionC\": 230,\n \"flameC\": 600,\n \"fuelFraction\": 1,\n \"burnRate\": 0.05,\n \"conductivity\": 0.03,\n \"heatCapacity\": 0.6,\n \"smoke\": \"grey\"\n },\n \"visual\": {\n \"charColor\": 1709072\n },\n \"tags\": [\n \"fabric\",\n \"organic\",\n \"flammable\"\n ],\n \"compressive\": 500\n },\n {\n \"id\": \"foam\",\n \"density\": 30,\n \"hpPerKg\": 15,\n \"penetration\": 0.5,\n \"ductility\": 0.7,\n \"fracture\": \"tear\",\n \"fragmentSize\": [\n 0.03,\n 0.15\n ],\n \"damageScale\": {\n \"slash\": 2,\n \"ballistic\": 0.4\n },\n \"hitDebris\": \"fibers\",\n \"breakDebris\": \"fibers\",\n \"thermal\": {\n \"ignitionC\": 250,\n \"flameC\": 900,\n \"fuelFraction\": 0.95,\n \"burnRate\": 0.09,\n \"conductivity\": 0.02,\n \"heatCapacity\": 0.5,\n \"smoke\": \"black\"\n },\n \"visual\": {\n \"charColor\": 855309\n },\n \"tags\": [\n \"fabric\",\n \"flammable\"\n ],\n \"compressive\": 100\n },\n {\n \"id\": \"leather\",\n \"density\": 900,\n \"hpPerKg\": 50,\n \"penetration\": 4,\n \"ductility\": 0.8,\n \"fracture\": \"tear\",\n \"fragmentSize\": [\n 0.05,\n 0.25\n ],\n \"damageScale\": {\n \"slash\": 2\n },\n \"hitDebris\": \"fibers\",\n \"breakDebris\": \"fibers\",\n \"thermal\": {\n \"ignitionC\": 320,\n \"flameC\": 650,\n \"fuelFraction\": 0.7,\n \"burnRate\": 0.01,\n \"conductivity\": 0.05,\n \"heatCapacity\": 1,\n \"smoke\": \"grey\"\n },\n \"visual\": {\n \"charColor\": 1709072\n },\n \"tags\": [\n \"fabric\",\n \"organic\",\n \"flammable\"\n ],\n \"compressive\": 2000\n },\n {\n \"id\": \"paper\",\n \"density\": 700,\n \"hpPerKg\": 8,\n \"penetration\": 0.3,\n \"ductility\": 0.5,\n \"fracture\": \"tear\",\n \"fragmentSize\": [\n 0.03,\n 0.15\n ],\n \"damageScale\": {\n \"slash\": 3\n },\n \"hitDebris\": \"fibers\",\n \"breakDebris\": \"fibers\",\n \"thermal\": {\n \"ignitionC\": 230,\n \"flameC\": 600,\n \"fuelFraction\": 1,\n \"burnRate\": 0.06,\n \"conductivity\": 0.03,\n \"heatCapacity\": 0.5,\n \"smoke\": \"grey\"\n },\n \"visual\": {\n \"charColor\": 1709072\n },\n \"tags\": [\n \"fabric\",\n \"organic\",\n \"flammable\"\n ],\n \"compressive\": 500\n },\n {\n \"id\": \"foliage\",\n \"density\": 150,\n \"hpPerKg\": 20,\n \"penetration\": 0.5,\n \"ductility\": 0.6,\n \"fracture\": \"tear\",\n \"fragmentSize\": [\n 0.03,\n 0.1\n ],\n \"damageScale\": {\n \"slash\": 4\n },\n \"hitDebris\": \"leaves\",\n \"breakDebris\": \"leaves\",\n \"thermal\": {\n \"ignitionC\": 300,\n \"flameC\": 700,\n \"fuelFraction\": 0.8,\n \"burnRate\": 0.08,\n \"conductivity\": 0.03,\n \"heatCapacity\": 0.8,\n \"smoke\": \"white\"\n },\n \"visual\": {\n \"charColor\": 1709072\n },\n \"tags\": [\n \"plant\",\n \"organic\",\n \"flammable\"\n ],\n \"compressive\": 500\n },\n {\n \"id\": \"green_wood\",\n \"density\": 650,\n \"hpPerKg\": 5,\n \"penetration\": 10,\n \"ductility\": 0.5,\n \"fracture\": \"splinter\",\n \"fragmentSize\": [\n 0.03,\n 0.2\n ],\n \"damageScale\": {\n \"slash\": 1.3,\n \"ballistic\": 0.1\n },\n \"hitDebris\": \"splinters\",\n \"breakDebris\": \"leaves\",\n \"thermal\": {\n \"ignitionC\": 400,\n \"flameC\": 700,\n \"fuelFraction\": 0.8,\n \"burnRate\": 0.006,\n \"conductivity\": 0.06,\n \"heatCapacity\": 2,\n \"smoke\": \"white\"\n },\n \"visual\": {\n \"charColor\": 1709072\n },\n \"tags\": [\n \"plant\",\n \"wood\",\n \"organic\",\n \"flammable\"\n ],\n \"compressive\": 60000\n },\n {\n \"id\": \"cactus\",\n \"density\": 900,\n \"hpPerKg\": 30,\n \"penetration\": 2,\n \"ductility\": 0.6,\n \"fracture\": \"tear\",\n \"fragmentSize\": [\n 0.04,\n 0.15\n ],\n \"damageScale\": {\n \"slash\": 3\n },\n \"hitDebris\": \"fibers\",\n \"breakDebris\": \"fibers\",\n \"thermal\": {\n \"ignitionC\": 450,\n \"flameC\": 650,\n \"fuelFraction\": 0.3,\n \"burnRate\": 0.01,\n \"conductivity\": 0.05,\n \"heatCapacity\": 3,\n \"smoke\": \"white\"\n },\n \"visual\": {\n \"charColor\": 1709072\n },\n \"tags\": [\n \"plant\",\n \"organic\",\n \"flammable\"\n ],\n \"compressive\": 5000\n },\n {\n \"id\": \"flesh_fruit\",\n \"density\": 950,\n \"hpPerKg\": 15,\n \"penetration\": 1.5,\n \"ductility\": 0.7,\n \"fracture\": \"tear\",\n \"fragmentSize\": [\n 0.02,\n 0.08\n ],\n \"damageScale\": {\n \"slash\": 2\n },\n \"hitDebris\": \"drips\",\n \"breakDebris\": \"drips\",\n \"thermal\": {\n \"conductivity\": 0.05,\n \"heatCapacity\": 3\n },\n \"tags\": [\n \"plant\",\n \"organic\"\n ],\n \"compressive\": 1000\n },\n {\n \"id\": \"wax\",\n \"density\": 900,\n \"hpPerKg\": 30,\n \"penetration\": 3,\n \"ductility\": 0.5,\n \"fracture\": \"snap\",\n \"fragmentSize\": [\n 0.05,\n 0.2\n ],\n \"hitDebris\": \"chips\",\n \"breakDebris\": \"chips\",\n \"thermal\": {\n \"ignitionC\": 230,\n \"flameC\": 700,\n \"fuelFraction\": 0.9,\n \"burnRate\": 0.02,\n \"meltC\": 60,\n \"meltRate\": 0.2,\n \"conductivity\": 0.05,\n \"heatCapacity\": 0.8,\n \"smoke\": \"grey\"\n },\n \"tags\": [\n \"wax\",\n \"flammable\"\n ],\n \"compressive\": 5000\n },\n {\n \"id\": \"soil\",\n \"density\": 1300,\n \"hpPerKg\": 3,\n \"penetration\": 5,\n \"ductility\": 0.3,\n \"fracture\": \"crumble\",\n \"fragmentSize\": [\n 0.03,\n 0.12\n ],\n \"hitDebris\": \"dust\",\n \"breakDebris\": \"dust\",\n \"thermal\": {\n \"conductivity\": 0.1,\n \"heatCapacity\": 2.5\n },\n \"tags\": [\n \"soil\",\n \"masonry\"\n ],\n \"compressive\": 20000\n }\n]","{\n \"cast iron\": \"cast_iron\",\n \"cast_iron\": \"cast_iron\",\n \"castiron\": \"cast_iron\",\n \"metal\": \"steel\",\n \"steel\": \"steel\",\n \"iron\": \"steel\",\n \"chrome\": \"steel\",\n \"alumin\": \"aluminium\",\n \"alu_\": \"aluminium\",\n \"alu-\": \"aluminium\",\n \"alu \": \"aluminium\",\n \"copper\": \"copper\",\n \"brass\": \"copper\",\n \"bronze\": \"copper\",\n \"wood\": \"wood_soft\",\n \"timber\": \"wood_soft\",\n \"plank\": \"wood_soft\",\n \"lumber\": \"wood_soft\",\n \"pine\": \"wood_soft\",\n \"hardwood\": \"wood_hard\",\n \"oak\": \"wood_hard\",\n \"plywood\": \"plywood\",\n \"mdf\": \"mdf\",\n \"chipboard\": \"mdf\",\n \"glass\": \"glass\",\n \"window\": \"glass\",\n \"mirror\": \"glass\",\n \"tempered\": \"tempered_glass\",\n \"windshield\": \"tempered_glass\",\n \"fabric\": \"fabric\",\n \"cloth\": \"fabric\",\n \"cushion\": \"fabric\",\n \"sofa\": \"fabric\",\n \"curtain\": \"fabric\",\n \"carpet\": \"fabric\",\n \"textile\": \"fabric\",\n \"canvas\": \"fabric\",\n \"foam\": \"foam\",\n \"sponge\": \"foam\",\n \"mattress\": \"foam\",\n \"leather\": \"leather\",\n \"paper\": \"paper\",\n \"cardboard\": \"paper\",\n \"leaf\": \"foliage\",\n \"leaves\": \"foliage\",\n \"foliage\": \"foliage\",\n \"bush\": \"foliage\",\n \"grass\": \"foliage\",\n \"trunk\": \"green_wood\",\n \"branch\": \"green_wood\",\n \"twig\": \"green_wood\",\n \"cactus\": \"cactus\",\n \"fruit\": \"flesh_fruit\",\n \"apple\": \"flesh_fruit\",\n \"melon\": \"flesh_fruit\",\n \"wax\": \"wax\",\n \"candle\": \"wax\",\n \"concrete\": \"concrete\",\n \"cement\": \"concrete\",\n \"brick\": \"brick\",\n \"tile\": \"ceramic\",\n \"ceramic\": \"ceramic\",\n \"porcelain\": \"ceramic\",\n \"stone\": \"stone\",\n \"rock\": \"stone\",\n \"marble\": \"stone\",\n \"plaster\": \"plaster\",\n \"drywall\": \"plaster\",\n \"gypsum\": \"plaster\",\n \"plastic\": \"plastic\",\n \"pvc\": \"plastic\",\n \"acrylic\": \"plastic\",\n \"rubber\": \"rubber\",\n \"tire\": \"rubber\",\n \"tyre\": \"rubber\",\n \"soil\": \"soil\",\n \"dirt\": \"soil\",\n \"potting\": \"soil\",\n \"mud\": \"soil\"\n}","import materialsJson from './data/materials.json'\nimport hintsJson from './data/material-hints.json'\nimport { aabbOf, quatFromEuler, shapeDims } from './math'\nimport type { JointKind, JointSpec, MaterialDef, MaterialId, PartShape, PartSpec, ToolKind } from './types'\n\n/** Built-in materials (data in `src/data/materials.json`). Override or extend with the `materials` option of `createDestruction`. */\nexport const MATERIALS: MaterialDef[] = materialsJson as unknown as MaterialDef[]\n\n/** Default joint strength (joint hit points), tools that undo it and seconds of tool work. */\nexport const JOINT_DEFAULTS: Record<JointKind, { strength: number; tools: ToolKind[]; seconds: number }> = {\n screw: { strength: 80, tools: ['screwdriver'], seconds: 2.5 },\n bolt: { strength: 200, tools: ['wrench'], seconds: 4 },\n nail: { strength: 60, tools: ['pry_bar', 'hammer'], seconds: 2 },\n weld: { strength: 600, tools: ['cutting_torch', 'saw'], seconds: 8 },\n glue: { strength: 50, tools: ['pry_bar', 'knife'], seconds: 3 },\n hinge: { strength: 120, tools: ['screwdriver'], seconds: 3 },\n clip: { strength: 20, tools: ['hand'], seconds: 0.6 },\n tie: { strength: 15, tools: ['knife', 'hand'], seconds: 1 },\n mortar: { strength: 400, tools: ['hammer'], seconds: 6 },\n fused: { strength: 1000, tools: [], seconds: 0 },\n stem: { strength: 40, tools: ['knife', 'saw', 'hand'], seconds: 1.5 },\n rebar: { strength: 1500, tools: ['cutting_torch', 'saw'], seconds: 20 },\n resting: { strength: 30, tools: ['hand'], seconds: 1 },\n}\n\nconst has = (m: MaterialDef, tag: string) => !!m.tags && m.tags.includes(tag)\n\n/**\n * The joint kind that fits two materials: glass is glued (clipped to plastic), soft things are tied, plants share a stem, masonry gets mortar,\n * metal is welded, wood is screwed, plastic clips. `inferJoints` refines weld to bolt for thick metal and screw to nail for thin wood.\n */\nexport const JOINT_BY_MATERIALS = (a: MaterialDef, b: MaterialDef): JointKind => {\n const any = (tag: string) => has(a, tag) || has(b, tag)\n const both = (tag: string) => has(a, tag) && has(b, tag)\n if (any('glass')) return any('plastic') ? 'clip' : 'glue'\n if (any('fabric')) return 'tie'\n if (both('plant')) return 'stem'\n if (any('plant')) return 'tie'\n if (both('masonry')) return 'mortar'\n if (any('masonry') && any('metal')) return 'bolt'\n if (both('metal')) return 'weld'\n if (any('plastic')) return 'clip'\n if (any('rubber') || any('wax')) return 'glue'\n return 'screw'\n}\n\nconst MATERIAL_MAP: ReadonlyMap<MaterialId, MaterialDef> = new Map(MATERIALS.map((m) => [m.id, m]))\n\nconst HINTS = Object.entries(hintsJson as Record<string, string>).sort((x, y) => y[0].length - x[0].length)\n\n/** Guess a material id from a mesh or node name (case-insensitive substring; the longest keyword wins). */\nexport const guessMaterial = (name: string): MaterialId | undefined => {\n const n = name.toLowerCase()\n for (const [key, id] of HINTS) if (n.includes(key)) return id\n return undefined\n}\n\n/** Volume of a shape in m³. */\nexport const partVolume = (shape: PartShape): number => {\n if (shape.kind === 'box') return shape.size[0] * shape.size[1] * shape.size[2]\n if (shape.kind === 'cylinder') return Math.PI * shape.radius * shape.radius * shape.height\n return (4 / 3) * Math.PI * shape.radius ** 3\n}\n\n/** Mass of a part in kg: its `mass`, or density × volume × fill. */\nexport const partMass = (part: PartSpec, material: MaterialDef): number => part.mass ?? material.density * partVolume(part.shape) * (part.fill ?? 1)\n\n/** Wall thickness of a part in metres (see `PartSpec.thickness`). */\nexport const partThickness = (part: PartSpec): number => {\n const dims = shapeDims(part.shape)\n const min = Math.min(dims[0], dims[1], dims[2])\n return Math.max(1e-4, Math.min(part.thickness ?? min * (part.fill ?? 1), min))\n}\n\n/**\n * Joints for touching parts. Two parts touch when their axis-aligned bounds (after rotation), grown by `tolerance` metres, overlap. The kind comes\n * from `JOINT_BY_MATERIALS`; thick metal pairs (smallest dimension of both >= 2 cm) get a `bolt` instead of a `weld`, and thin wood pairs (either\n * under 2 cm) a `nail` instead of a `screw`.\n */\nexport const inferJoints = (parts: PartSpec[], tolerance = 0.01, materials: ReadonlyMap<string, MaterialDef> = MATERIAL_MAP): JointSpec[] => {\n const boxes = parts.map((p) => aabbOf(p.shape, p.position, quatFromEuler(...(p.rotation ?? [0, 0, 0]))))\n const out: JointSpec[] = []\n for (let i = 0; i < parts.length; i++) {\n for (let j = i + 1; j < parts.length; j++) {\n const a = boxes[i], b = boxes[j]\n let touch = true\n for (let k = 0; k < 3 && touch; k++) if (a.min[k] - tolerance > b.max[k] || b.min[k] - tolerance > a.max[k]) touch = false\n if (!touch) continue\n const ma = materials.get(parts[i].material)\n const mb = materials.get(parts[j].material)\n let kind: JointKind = ma && mb ? JOINT_BY_MATERIALS(ma, mb) : 'screw'\n const thin = Math.min(partThickness(parts[i]), partThickness(parts[j]))\n if (kind === 'weld' && thin >= 0.02) kind = 'bolt'\n if (kind === 'screw' && ma && mb && has(ma, 'wood') && has(mb, 'wood') && thin < 0.02) kind = 'nail'\n out.push({ id: `${parts[i].id}~${parts[j].id}`, a: parts[i].id, b: parts[j].id, kind })\n }\n }\n return out\n}\n\n/**\n * Load in kg a structural part carries before it fails: `structure.capacity`, else `material.compressive` (default 20 000 kg/m²) times the\n * section. The section is the product of the two smallest dimensions (thickness times longest dimension for slabs; thickness times horizontal length for walls); foundations get x10.\n */\nexport const partCapacity = (part: PartSpec, material: MaterialDef): number => {\n if (part.structure?.capacity !== undefined) return part.structure.capacity\n const d = shapeDims(part.shape).sort((a, b) => a - b)\n const role = part.structure?.role\n const sh = shapeDims(part.shape)\n const section = role === 'wall' || role === 'partition' ? Math.min(sh[0], sh[2]) * Math.max(sh[0], sh[2]) : role === 'slab' ? d[0] * d[2] : d[0] * d[1]\n return Math.max(1, section * (material.compressive ?? 20000) * (role === 'foundation' ? 10 : 1))\n}\n\n\n/**\n * Multiplier of blunt damage on brittle parts (ductility < 0.3): up to x4.7 for fully brittle materials (glass, ceramic, stone), times a thinness\n * term that follows bending stress (1 / thickness squared, x1 at 2 cm or more, at most x25). A 3 mm glass bottle breaks from a 1.5 m drop; a\n * concrete block (thick) is only ever x4.4.\n */\nexport const fragility = (ductility: number, thickness: number): number => {\n if (ductility >= 0.3) return 1\n return (1 + (4 * (0.3 - ductility)) / 0.3) * Math.min(25, Math.max(1, (0.02 / Math.max(thickness, 1e-4)) ** 2))\n}\n","import { chainDelayOf, drawChainDelay, FIRE_PRIME_C, shotDamageFractionOf } from './chain'\nimport type { ChainTrigger } from './chain'\nimport { planFragments } from './fracture'\nimport type { PlannedFragment } from './fracture'\nimport { cellsOutside, cellsWithin, marksInBox, planeAxes, unitGrid } from './breach'\nimport type { LocalHole, UnitCell } from './breach'\nimport { layersEnergy, layersThickness, MAX_SPALLS, outerLayer, resolveLayer, thinAxisOf } from './layers'\nimport type { LayerRT } from './layers'\nimport { clipBarsToDisc, rebarBars, rebarMass } from './rebar'\nimport { JOINT_DEFAULTS, inferJoints, MATERIALS, partCapacity, partMass, partThickness, partVolume, fragility } from './materials'\nimport { aabbOf, createRng, distanceToShape, quat, quatFromEuler, rayShape, rotateVec, shapeArea, shapeBound, shapeDims, surfacePoint, toLocal, toWorld, vec } from './math'\nimport type { DestructibleImpl } from './contracts'\nimport type {\n AddOptions, Crack, DamageType, DebrisKind, DestructibleSpec, Dent, DestructionEvent, DestructionEventType, DestructionOptions, DestructionSnapshot,\n DamageStage, DestructionWorld, Fragment, Hit, HitResult, Hole, JointSpec, JointState, MaterialDef, MaterialId, ObjectRuntime, ObjectState, PartRuntime, PartSpec,\n PartState, Quat, RayHit, ToolKind, Vec3,\n} from './types'\n\n/** Hit points removed per joule (before the material's `damageScale`). */\nconst DMG_PER_J = 0.02\n/** Fraction of a blast's energy that reaches a part at distance 0. */\nconst BLAST_COUPLING = 0.1\nconst FLAMMABLE_FLUIDS = ['gas', 'fuel']\n/** Staged parts: default breach radius (m), blast multiplier on the breach area, area a part must lose to open (m²), and the share of capacity the bare cage keeps. */\nconst BREACH_RADIUS = 0.55\nconst BREACH_BLAST_FACTOR = 6\nconst BREACH_AREA = 0.25\nconst RESIDUAL_CAPACITY = 0.15\n/** Unit-sized fragments of a whole wall: at most this many times `maxFragments`. A breach picks its units from a grid of up to `BREACH_GRID_CAP`. */\nconst UNIT_FRAGMENT_FACTOR = 4\nconst BREACH_GRID_CAP = 4096\n\n/** A cut only goes through a slice of the part, so a part longer than `CUT_SLICE` costs no more to cut than a 1 m piece of the same section. */\nconst CUT_SLICE = 1\nconst cutFactorOf = (shape: PartSpec['shape']): number => clamp(Math.max(...shapeDims(shape)) / CUT_SLICE, 1, 50)\n\ninterface PartRT {\n spec: PartSpec\n mat: MaterialDef\n st: PartState\n q: Quat\n /** Current object-local position (the spec's, unless moved by `setPartPose`). */\n pos: Vec3\n mass0: number\n fuel0: number\n area: number\n thickness: number\n /** Slash damage multiplier: longest dimension / 1 m (1..50). */\n cutFactor: number\n dims: Vec3\n bound: number\n rt: PartRuntime\n /** Structural role other than `none`. */\n structural: boolean\n anchored: boolean\n cap: number\n /** The part has layers, units, reinforcement or a breach: it goes through damage stages. */\n staged: boolean\n /** Axis the part is thinnest along (the wall's depth). */\n thin: number\n layers: LayerRT[]\n}\n\ninterface JointRT {\n spec: Required<JointSpec>\n st: JointState\n ia: number\n ib: number\n bears: boolean | 'a_on_b' | 'b_on_a'\n /** Object-local position. */\n pos: Vec3\n step: number\n}\n\ninterface ObjRT {\n id: string\n spec: DestructibleSpec\n st: ObjectState\n parts: PartRT[]\n byId: Map<string, PartRT>\n idx: Map<string, number>\n joints: JointRT[]\n jointMap: Map<string, JointRT>\n byPart: Map<string, JointRT[]>\n /** Local bounding sphere. */\n bc: Vec3\n br: number\n active: boolean\n tmp: number[]\n hasStructure: boolean\n dirty: boolean\n maxStress: number\n collapsed: Set<string>\n /** Structural parts with no support path to an anchored part (they fall). */\n unsup: boolean[]\n anyAnchor: boolean\n /** Creation order of the object in this world: breaks ties between parts that go off at the same time. */\n order: number\n}\n\ninterface Carry {\n parts: Map<string, { st: PartState; rt: PartRuntime }>\n joints: Map<string, { st: JointState; step: number }>\n collapsed?: string[]\n}\n\ninterface Loc {\n p: PartRT\n point: Vec3\n normal: Vec3\n dir: Vec3\n pathLen: number\n}\n\ninterface ImplRT {\n impl: DestructibleImpl\n position: Vec3\n rotation: Quat\n /** Part pose overrides set by `setPartPose` (object-local). */\n poses: Map<string, { pos: Vec3; q: Quat }>\n}\n\ninterface Blast {\n point: Vec3\n energy: number\n radius: number\n source?: string\n}\n\nconst clamp = (v: number, a: number, b: number) => Math.max(a, Math.min(b, v))\n/** Share of its capacity a structural part still has: a bare rebar cage keeps the reinforcement's residual, a breached part loses what left it. */\nconst capacityScale = (p: PartRT): number => {\n if (p.st.stripped) return p.spec.reinforcement?.residualCapacity ?? RESIDUAL_CAPACITY\n return p.rt.removed ? Math.max(0.05, 1 - p.rt.removed / p.mass0) : 1\n}\nconst isLost = (p: PartRT) => p.st.status === 'broken' || p.st.status === 'melted' || p.st.status === 'ashes'\nconst clone = <T>(v: T): T => structuredClone(v)\n/**\n * Fraction of a structural part's hit points lost per second while its stress is above 1. A floor of 0.12 makes even a barely overloaded part\n * give way in about 8 s (a rate proportional to the excess alone never finishes: stress 1.02 would take two minutes), then it grows with the\n * excess and tops out at 0.6: stress 1.1 fails in ~4.5 s, 1.5 in ~1.9 s and anything above 1.7 in ~1.7 s. Deterministic: depends only on stress.\n */\nconst overloadRate = (stress: number) => Math.min(0.6, 0.12 + 0.8 * (stress - 1))\nconst updateBound = (o: ObjRT) => {\n if (!o.parts.length) return\n const c: Vec3 = [0, 0, 0]\n for (const p of o.parts) for (let k = 0; k < 3; k++) c[k] += p.pos[k] / o.parts.length\n let r = 0\n for (const p of o.parts) r = Math.max(r, vec.distance(c, p.pos) + p.bound)\n o.bc = c\n o.br = r\n}\nconst AXIS = { x: 0, y: 1, z: 2 } as const\n\nexport const createDestruction = (options: DestructionOptions = {}): DestructionWorld => {\n const materials = new Map<MaterialId, MaterialDef>()\n for (const m of MATERIALS) materials.set(m.id, m)\n for (const m of options.materials ?? []) materials.set(m.id, m)\n const ambient = options.ambientC ?? 20\n const maxFragments = options.maxFragments ?? 24\n const maxDents = options.maxDents ?? 24\n const maxHoles = options.maxHoles ?? 32\n const fireSpread = options.fireSpread ?? 0.6\n const breachRadius = options.breach?.radius ?? BREACH_RADIUS\n const breachBlastFactor = options.breach?.blastFactor ?? BREACH_BLAST_FACTOR\n const rng = createRng(options.seed ?? 1)\n const rnd = () => rng.next()\n\n let time = 0\n let counter = 0\n const objects = new Map<string, ObjRT>()\n const listeners = new Map<string, Set<(e: DestructionEvent) => void>>()\n let cur: DestructionEvent[] = []\n const blasts: Blast[] = []\n /** Glass the blast front has not reached yet: the damage lands when it does (`blastFrontSpeed`). */\n const frontQueue: Array<{ o: ObjRT; p: PartRT; due: number; amount: number; fragEnergy: number; point: Vec3; src?: string }> = []\n const blastFrontSpeed = options.blastFrontSpeed ?? 0\n /** Explosive parts that were primed and wait for their delay. */\n const primed = new Map<PartRT, ObjRT>()\n let orderCounter = 0\n const impls = new Map<string, ImplRT>()\n const taken = (id: string) => objects.has(id) || impls.has(id)\n\n // -------------------------------------------------------------------------------------------------------------------------------------------\n // Events\n // -------------------------------------------------------------------------------------------------------------------------------------------\n\n const emit = (e: DestructionEvent) => {\n cur.push(e)\n options.onEvent?.(e)\n const all = listeners.get('*')\n if (all) for (const fn of [...all]) fn(e)\n const typed = listeners.get(e.type)\n if (typed) for (const fn of [...typed]) fn(e)\n }\n const base = (o: ObjRT, source?: string): { object: string; time: number; source?: string } =>\n source === undefined ? { object: o.id, time } : { object: o.id, time, source }\n\n const pendingCollapse = new Map<ObjRT, string | undefined>()\n let runDepth = 0\n\n /** Blasts queued by parts that exploded; each can prime or set off more parts. It ends: a part goes off once and is primed once, so the queue only grows by parts not yet lost. */\n const drainBlasts = () => {\n for (let blast = blasts.shift(); blast; blast = blasts.shift()) blastWorld(blast.point, blast.energy, blast.radius, blast.source)\n }\n\n const run = <T>(fn: () => T): { value: T; events: DestructionEvent[] } => {\n const prev = cur\n const mine: DestructionEvent[] = []\n cur = mine\n runDepth++\n try {\n const value = fn()\n drainBlasts()\n if (runDepth === 1) flushCollapse()\n return { value, events: mine }\n } finally {\n runDepth--\n cur = prev\n }\n }\n\n // -------------------------------------------------------------------------------------------------------------------------------------------\n // Building objects\n // -------------------------------------------------------------------------------------------------------------------------------------------\n\n const nextId = () => {\n let id: string\n do id = `obj_${++counter}`\n while (taken(id))\n return id\n }\n\n const freshRuntime = (): PartRuntime => ({ meltEmitted: 0, dripT: 0, prevTemp: ambient, shockT: 0, jet: false })\n\n const resolveSpec = (spec: DestructibleSpec): DestructibleSpec => {\n const out = clone(spec)\n const joints = out.joints ?? inferJoints(out.parts, 0.01, materials)\n const structural = (id: string) => {\n const role = out.parts.find((p) => p.id === id)?.structure?.role\n return role !== undefined && role !== 'none'\n }\n out.joints = joints.map((j) => {\n const def = JOINT_DEFAULTS[j.kind]\n return {\n id: j.id ?? `${j.a}~${j.b}`, a: j.a, b: j.b, kind: j.kind, strength: j.strength ?? def.strength, tools: j.tools ?? [...def.tools], seconds: j.seconds ?? def.seconds,\n bears: j.bears ?? (structural(j.a) && structural(j.b)),\n }\n })\n return out\n }\n\n const build = (spec: DestructibleSpec, id: string, pos: Vec3, rot: Quat, carry?: Carry): ObjRT => {\n const st: ObjectState = { id, position: [pos[0], pos[1], pos[2]], rotation: [rot[0], rot[1], rot[2], rot[3]], parts: [], joints: [], groups: [], destroyed: false }\n if (spec.name !== undefined) st.name = spec.name\n const o: ObjRT = {\n id, spec, st, parts: [], byId: new Map(), idx: new Map(), joints: [], jointMap: new Map(), byPart: new Map(), bc: [0, 0, 0], br: 0, active: false, tmp: [], hasStructure: false, dirty: false, maxStress: 0, collapsed: new Set(carry?.collapsed ?? []), unsup: [], anyAnchor: false, order: ++orderCounter,\n }\n for (const ps of spec.parts) {\n if (o.byId.has(ps.id)) throw new Error(`duplicate part id \"${ps.id}\"`)\n const mat = materials.get(ps.material)\n if (!mat) throw new Error(`unknown material \"${ps.material}\" on part \"${ps.id}\"`)\n const mass0 = partMass(ps, mat)\n const maxHp = Math.max(1, ps.hp ?? mass0 * mat.hpPerKg)\n const fuel0 = mat.thermal.ignitionC !== undefined ? mass0 * (mat.thermal.fuelFraction ?? 0) : 0\n const c = carry?.parts.get(ps.id)\n const staged = !!(ps.layers?.length || ps.reinforcement || ps.units || ps.breach)\n const layers = (ps.layers ?? []).map((l, i) => {\n const lm = materials.get(l.material)\n if (!lm) throw new Error(`unknown material \"${l.material}\" on layer ${i} of part \"${ps.id}\"`)\n return resolveLayer(l, i, lm)\n })\n if (ps.reinforcement && !materials.has(ps.reinforcement.material)) throw new Error(`unknown material \"${ps.reinforcement.material}\" on the reinforcement of part \"${ps.id}\"`)\n const pst: PartState = c?.st ?? {\n id: ps.id, material: ps.material, status: 'intact', hp: maxHp, maxHp, mass: mass0, temperature: ambient, burning: false, fuel: fuel0, char: 0, melt: 0,\n dents: [], holes: [], cracks: [], group: 0, ...(staged ? { stage: 0 as DamageStage } : {}),\n }\n if (!c && ps.contains) pst.liters = ps.contains.liters\n const p: PartRT = {\n spec: ps, mat, st: pst, q: quatFromEuler(...(ps.rotation ?? [0, 0, 0])), pos: [ps.position[0], ps.position[1], ps.position[2]], mass0, fuel0, area: shapeArea(ps.shape), thickness: partThickness(ps), cutFactor: cutFactorOf(ps.shape),\n dims: shapeDims(ps.shape), bound: shapeBound(ps.shape), rt: c?.rt ?? { ...freshRuntime(), prevTemp: pst.temperature },\n structural: !!ps.structure && ps.structure.role !== 'none', anchored: !!ps.anchored || ps.structure?.role === 'foundation', cap: ps.structure ? partCapacity(ps, mat) : 0,\n staged, thin: thinAxisOf(shapeDims(ps.shape)), layers,\n }\n if (p.structural) o.hasStructure = true\n if (p.rt.chainDue !== undefined && !isLost(p)) primed.set(p, o)\n o.idx.set(ps.id, o.parts.length)\n o.parts.push(p)\n o.byId.set(ps.id, p)\n o.st.parts.push(pst)\n o.byPart.set(ps.id, [])\n }\n for (const js of spec.joints ?? []) {\n const r = js as Required<JointSpec>\n const ia = o.idx.get(r.a), ib = o.idx.get(r.b)\n if (ia === undefined || ib === undefined) throw new Error(`joint \"${r.id}\" references an unknown part`)\n if (o.jointMap.has(r.id)) throw new Error(`duplicate joint id \"${r.id}\"`)\n const pa = o.parts[ia], pb = o.parts[ib]\n const A = aabbOf(pa.spec.shape, pa.spec.position, pa.q)\n const B = aabbOf(pb.spec.shape, pb.spec.position, pb.q)\n const pos: Vec3 = [0, 0, 0]\n for (let k = 0; k < 3; k++) pos[k] = (Math.max(A.min[k], B.min[k]) + Math.min(A.max[k], B.max[k])) / 2\n const bears: JointRT['bears'] = r.bears ?? (pa.structural && pb.structural)\n const c = carry?.joints.get(r.id)\n const jst: JointState = c?.st ?? { id: r.id, a: r.a, b: r.b, kind: r.kind, hp: r.strength, maxHp: r.strength, progress: 0, open: false }\n const j: JointRT = { spec: r, st: jst, ia, ib, bears, pos, step: c?.step ?? 0 }\n o.joints.push(j)\n o.jointMap.set(r.id, j)\n o.byPart.get(r.a)!.push(j)\n o.byPart.get(r.b)!.push(j)\n o.st.joints.push(jst)\n }\n // open joints whose parts are no longer both here (left behind by `splitGroup`) stay listed, inert, so callers still see them open\n for (const [jid, c] of carry?.joints ?? []) if (c.st.open && !o.jointMap.has(jid)) o.st.joints.push(c.st)\n updateBound(o)\n o.anyAnchor = o.parts.some((p) => p.anchored)\n o.unsup = o.parts.map(() => false)\n solveStructure(o)\n recomputeGroups(o)\n o.st.destroyed = o.parts.length > 0 && o.parts.every(isLost)\n o.active = o.maxStress > 0.8 || o.parts.some((p) => p.st.burning || p.st.temperature > ambient + 0.5 || (p.st.liters ?? 0) > 0)\n return o\n }\n\n const recomputeGroups = (o: ObjRT) => {\n const n = o.parts.length\n const parent = Array.from({ length: n }, (_, i) => i)\n const find = (i: number): number => {\n while (parent[i] !== i) {\n parent[i] = parent[parent[i]]\n i = parent[i]\n }\n return i\n }\n const union = (a: number, b: number) => {\n parent[find(a)] = find(b)\n }\n const live = (j: JointRT) => !j.st.open && !isLost(o.parts[j.ia]) && !isLost(o.parts[j.ib])\n if (o.unsup.some(Boolean)) {\n // parts that lost every support fall: cut them from the supported pieces; loose non-structural parts follow the part they hang on\n for (const j of o.joints) if (live(j) && !o.unsup[j.ia] && !o.unsup[j.ib]) union(j.ia, j.ib)\n const supported = new Set<number>()\n for (let i = 0; i < n; i++) if (!isLost(o.parts[i]) && !o.unsup[i] && (o.parts[i].structural || o.parts[i].anchored)) supported.add(find(i))\n for (const j of o.joints) {\n if (!live(j) || !(o.unsup[j.ia] || o.unsup[j.ib])) continue\n if (o.unsup[j.ia] && o.unsup[j.ib]) union(j.ia, j.ib)\n else {\n const u = o.unsup[j.ia] ? j.ia : j.ib\n const other = u === j.ia ? j.ib : j.ia\n if (!o.parts[other].structural && !supported.has(find(other))) union(u, other)\n }\n }\n } else for (const j of o.joints) if (live(j)) union(j.ia, j.ib)\n const comps = new Map<number, number[]>()\n for (let i = 0; i < n; i++) {\n if (isLost(o.parts[i])) continue\n const r = find(i)\n const l = comps.get(r)\n if (l) l.push(i)\n else comps.set(r, [i])\n }\n const list = [...comps.values()].map((ix) => ({\n ix,\n anchored: ix.some((i) => o.parts[i].anchored),\n mass: ix.reduce((s, i) => s + o.parts[i].st.mass, 0),\n }))\n list.sort((a, b) => (a.anchored === b.anchored ? (b.mass - a.mass) || (a.ix[0] - b.ix[0]) : a.anchored ? -1 : 1))\n o.st.groups = list.map((g) => g.ix.map((i) => o.parts[i].spec.id))\n for (const p of o.parts) if (isLost(p)) p.st.group = -1\n list.forEach((g, gi) => g.ix.forEach((i) => (o.parts[i].st.group = gi)))\n }\n\n /** Which part `r` rests on over joint `j` (index), or -1 when the joint does not let `r` rest on its other end. */\n const restsOn = (j: JointRT, r: number): number => {\n const b = j.bears\n if (!b) return -1\n if (b === true) return j.ia === r ? j.ib : j.ia\n if (b === 'a_on_b') return j.ia === r ? j.ib : -1\n return j.ib === r ? j.ia : -1\n }\n\n /** Distribute own mass plus carried load down the bearing graph to the anchored parts; mark parts without any support path. */\n const solveStructure = (o: ObjRT) => {\n o.maxStress = 0\n if (!o.hasStructure) return\n const parts = o.parts\n const n = parts.length\n const dist = new Array<number>(n).fill(-1)\n const queue: number[] = []\n const ok = (i: number) => parts[i].structural && !isLost(parts[i])\n const isPartition = (i: number) => parts[i].spec.structure?.role === 'partition'\n for (let i = 0; i < n; i++) if (ok(i) && parts[i].anchored) {\n dist[i] = 0\n queue.push(i)\n }\n for (let h = 0; h < queue.length; h++) {\n const s = queue[h]\n if (isPartition(s)) continue\n for (const j of o.byPart.get(parts[s].spec.id)!) {\n if (j.st.open || !j.bears) continue\n const r = j.ia === s ? j.ib : j.ia\n if (dist[r] >= 0 || !ok(r) || restsOn(j, r) !== s) continue\n dist[r] = dist[s] + 1\n queue.push(r)\n }\n }\n const incoming = new Array<number>(n).fill(0)\n for (let h = queue.length - 1; h >= 0; h--) {\n const i = queue[h]\n const total = parts[i].st.mass + incoming[i]\n parts[i].st.load = total\n const lower: number[] = []\n for (const j of o.byPart.get(parts[i].spec.id)!) {\n if (j.st.open || !j.bears) continue\n const k = restsOn(j, i)\n if (k >= 0 && dist[k] >= 0 && dist[k] < dist[i] && !isPartition(k)) lower.push(k)\n }\n for (const k of lower) incoming[k] += total / lower.length\n }\n for (let i = 0; i < n; i++) {\n const p = parts[i]\n o.unsup[i] = false\n if (!p.structural) continue\n if (dist[i] < 0 || isLost(p)) {\n p.st.load = p.st.mass\n p.st.stress = 0\n o.unsup[i] = o.anyAnchor && !isLost(p)\n continue\n }\n p.st.stress = p.st.load! / (p.cap * capacityScale(p))\n if (p.st.stress > o.maxStress) o.maxStress = p.st.stress\n }\n if (o.maxStress > 0.8) o.active = true\n }\n\n /**\n * Ask for a collapse report. It is not computed right away: a blow can break several parts in one go (a column and the roof's supports, or a\n * column whose joint opens just before the column itself fractures), so the groups are examined once, after every loss of the operation.\n */\n const collapseCheck = (o: ObjRT, src?: string) => {\n if (!o.hasStructure || !o.parts.some((p) => p.anchored)) return\n if (!pendingCollapse.has(o)) pendingCollapse.set(o, src)\n }\n\n /** Report the groups of remaining structural parts that lost every anchor (each part is reported once). */\n const flushCollapse = () => {\n for (const [o, src] of [...pendingCollapse]) {\n pendingCollapse.delete(o)\n if (!objects.has(o.id) || objects.get(o.id) !== o) continue\n for (const g of o.st.groups) {\n const ps = g.map((id) => o.byId.get(id)!).filter((p) => !isLost(p) && !o.collapsed.has(p.spec.id))\n if (ps.length === 0 || g.some((id) => o.byId.get(id)!.anchored) || !ps.some((p) => p.structural)) continue\n for (const p of ps) {\n o.collapsed.add(p.spec.id)\n p.st.status = 'collapsed'\n setStage(o, p, 3, {}, src)\n }\n emit({ ...base(o, src), type: 'collapse', parts: ps.map((p) => p.spec.id), mass: ps.reduce((sum, p) => sum + p.st.mass, 0) })\n }\n }\n }\n\n const getObj = (id: string): ObjRT => {\n const o = objects.get(id)\n if (!o) throw new Error(`unknown object \"${id}\"`)\n return o\n }\n const getPart = (o: ObjRT, id: string): PartRT => {\n const p = o.byId.get(id)\n if (!p) throw new Error(`unknown part \"${id}\" in object \"${o.id}\"`)\n return p\n }\n\n // -------------------------------------------------------------------------------------------------------------------------------------------\n // Geometry in world space\n // -------------------------------------------------------------------------------------------------------------------------------------------\n\n const partXf = (o: ObjRT, p: PartRT): { c: Vec3; q: Quat } => ({ c: toWorld(o.st.position, o.st.rotation, p.pos), q: quat.mul(o.st.rotation, p.q) })\n const objLocal = (o: ObjRT, w: Vec3) => toLocal(o.st.position, o.st.rotation, w)\n const objLocalDir = (o: ObjRT, w: Vec3) => rotateVec(quat.conjugate(o.st.rotation), w)\n const jointWorld = (o: ObjRT, j: JointRT) => toWorld(o.st.position, o.st.rotation, j.pos)\n const distToPart = (o: ObjRT, p: PartRT, w: Vec3) => {\n const { c, q } = partXf(o, p)\n return distanceToShape(p.spec.shape, toLocal(c, q, w))\n }\n const partWorldAabb = (o: ObjRT, p: PartRT) => {\n const { c, q } = partXf(o, p)\n return aabbOf(p.spec.shape, c, q)\n }\n\n interface PartRay { t0: number; t1: number; n: Vec3 }\n const rayPartRaw = (pos: Vec3, rot: Quat, spec: PartSpec, pq: Quat, origin: Vec3, d: Vec3, lp: Vec3 = spec.position): PartRay | null => {\n const c = toWorld(pos, rot, lp)\n const q = quat.mul(rot, pq)\n const qi = quat.conjugate(q)\n const r = rayShape(spec.shape, rotateVec(qi, vec.sub(origin, c)), rotateVec(qi, d))\n return r ? { t0: r.t0, t1: r.t1, n: rotateVec(q, r.n0) } : null\n }\n const rayPart = (o: ObjRT, p: PartRT, origin: Vec3, d: Vec3): PartRay | null => rayPartRaw(o.st.position, o.st.rotation, p.spec, p.q, origin, d, p.pos)\n\n const raySphereMiss = (origin: Vec3, d: Vec3, c: Vec3, r: number, maxDist: number): boolean => {\n const oc = vec.sub(origin, c)\n const b = vec.dot(oc, d)\n const cc = vec.dot(oc, oc) - r * r\n if (cc > 0 && b > 0) return true\n const disc = b * b - cc\n if (disc < 0) return true\n return -b - Math.sqrt(disc) > maxDist\n }\n\n const raycastInternal = (origin: Vec3, dir: Vec3, maxDist: number, ignore?: Set<string>): (RayHit & { pathLen: number }) | null => {\n const d = vec.normalize(dir)\n let best: (RayHit & { pathLen: number }) | null = null\n for (const o of objects.values()) {\n if (raySphereMiss(origin, d, toWorld(o.st.position, o.st.rotation, o.bc), o.br, best ? Math.min(maxDist, best.distance) : maxDist)) continue\n for (const p of o.parts) {\n if (isLost(p) || ignore?.has(`${o.id}\\0${p.spec.id}`)) continue\n const r = rayPart(o, p, origin, d)\n if (!r || r.t0 < 0 || r.t0 > maxDist) continue\n if (p.st.opening && insideOpening(o, p, vec.madd(origin, d, r.t0))) continue\n if (!best || r.t0 < best.distance) best = { object: o.id, part: p.spec.id, point: vec.madd(origin, d, r.t0), normal: r.n, distance: r.t0, pathLen: r.t1 - r.t0 }\n }\n }\n for (const im of impls.values()) {\n for (const ps of im.impl.describe().parts) {\n if (ignore?.has(`${im.impl.id}\\0${ps.id}`)) continue\n const ov = im.poses.get(ps.id)\n const r = rayPartRaw(im.position, im.rotation, ps, ov?.q ?? quatFromEuler(...(ps.rotation ?? [0, 0, 0])), origin, d, ov?.pos)\n if (!r || r.t0 < 0 || r.t0 > maxDist) continue\n if (!best || r.t0 < best.distance) best = { object: im.impl.id, part: ps.id, point: vec.madd(origin, d, r.t0), normal: r.n, distance: r.t0, pathLen: r.t1 - r.t0 }\n }\n }\n return best\n }\n\n // impl hosting\n const implHit = (im: ImplRT, hit: Hit): HitResult => {\n let h = hit\n if (hit.space === 'local') {\n h = { ...hit, space: 'world', point: toWorld(im.position, im.rotation, hit.point) }\n if (hit.direction) h.direction = rotateVec(im.rotation, hit.direction)\n }\n const start = cur.length\n for (const e of im.impl.hit(h) ?? []) emit(e.time === undefined ? { ...e, time } : e)\n const mine = cur.slice(start)\n const res: HitResult = { object: im.impl.id, damage: 0, penetrated: false, exitEnergy: 0, events: mine, point: hit.point }\n for (const e of mine) {\n if (e.type === 'damage') res.damage += e.amount\n if (e.type === 'hole' && e.hole.through) res.penetrated = true\n if (res.part === undefined && 'part' in e) res.part = (e as { part: string }).part\n }\n return res\n }\n\n const implJointPos = (im: ImplRT, a: string, b: string): Vec3 | null => {\n const parts = im.impl.describe().parts\n const pa = parts.find((p) => p.id === a), pb = parts.find((p) => p.id === b)\n if (!pa || !pb) return null\n const A = aabbOf(pa.shape, pa.position, quatFromEuler(...(pa.rotation ?? [0, 0, 0])))\n const B = aabbOf(pb.shape, pb.position, quatFromEuler(...(pb.rotation ?? [0, 0, 0])))\n const pos: Vec3 = [0, 0, 0]\n for (let k = 0; k < 3; k++) pos[k] = (Math.max(A.min[k], B.min[k]) + Math.min(A.max[k], B.max[k])) / 2\n return toWorld(im.position, im.rotation, pos)\n }\n\n /** Find the part a hit lands on: a short ray along `dir`, else the nearest part within `tol`. */\n const locate = (o: ObjRT, point: Vec3, dir: Vec3 | undefined, tol: number, only?: PartRT): Loc | null => {\n const cands = only ? [only] : o.parts\n if (dir && vec.length(dir) > 1e-9) {\n const d = vec.normalize(dir)\n const origin = vec.madd(point, d, -0.05)\n let best: Loc | null = null\n let bestT = Infinity\n let inside: Loc | null = null\n for (const p of cands) {\n if (isLost(p)) continue\n const r = rayPart(o, p, origin, d)\n if (!r) continue\n if (r.t0 >= 0 && r.t0 <= 0.15) {\n if (r.t0 < bestT) {\n bestT = r.t0\n best = { p, point: vec.madd(origin, d, r.t0), normal: r.n, dir: d, pathLen: Math.max(r.t1 - r.t0, 1e-4) }\n }\n } else if (r.t0 < 0 && r.t1 > 0 && !inside) {\n inside = { p, point, normal: vec.scale(d, -1), dir: d, pathLen: Math.max(r.t1 - 0.05, p.thickness) }\n }\n }\n if (best) return best\n if (inside) return inside\n }\n let np: PartRT | null = null\n let nd = Infinity\n for (const p of cands) {\n if (isLost(p)) continue\n const dd = distToPart(o, p, point)\n if (dd < nd) {\n nd = dd\n np = p\n }\n }\n if (!np || nd > tol) return null\n const { c, q } = partXf(o, np)\n const sp = surfacePoint(np.spec.shape, toLocal(c, q, point))\n const normal = rotateVec(q, sp.normal)\n return { p: np, point: toWorld(c, q, sp.point), normal, dir: dir && vec.length(dir) > 1e-9 ? vec.normalize(dir) : vec.scale(normal, -1), pathLen: np.thickness }\n }\n\n // -------------------------------------------------------------------------------------------------------------------------------------------\n // Marks on parts\n // -------------------------------------------------------------------------------------------------------------------------------------------\n\n const markDamaged = (p: PartRT) => {\n if (p.st.status === 'intact') p.st.status = 'damaged'\n }\n\n const addDent = (o: ObjRT, p: PartRT, wp: Vec3, wn: Vec3, radius: number, depth: number, src?: string) => {\n const dent: Dent = { point: objLocal(o, wp), normal: objLocalDir(o, wn), radius, depth }\n const arr = p.st.dents\n let ni = -1, nd = Infinity\n for (let i = 0; i < arr.length; i++) {\n const dd = vec.distance(arr[i].point, dent.point)\n if (dd < nd) {\n nd = dd\n ni = i\n }\n }\n if (ni >= 0 && (arr.length >= maxDents || nd < arr[ni].radius * 0.6)) {\n arr[ni].depth = Math.min(0.1, Math.max(arr[ni].depth, depth) + depth * 0.3)\n arr[ni].radius = Math.max(arr[ni].radius, radius)\n } else arr.push(dent)\n markDamaged(p)\n emit({ ...base(o, src), type: 'dent', part: p.spec.id, dent, worldPoint: wp })\n }\n\n const addHole = (o: ObjRT, p: PartRT, wp: Vec3, wn: Vec3, diameter: number, through: boolean, exit?: Vec3, src?: string) => {\n const hole: Hole = { point: objLocal(o, wp), normal: objLocalDir(o, wn), diameter, through }\n p.st.holes.push(hole)\n if (p.st.holes.length > maxHoles) p.st.holes.shift()\n markDamaged(p)\n emit({ ...base(o, src), type: 'hole', part: p.spec.id, hole, worldPoint: wp, ...(exit ? { exitPoint: exit } : {}) })\n setStage(o, p, 1, {}, src)\n return hole\n }\n\n const addCrack = (o: ObjRT, p: PartRT, wp: Vec3, wn: Vec3, length: number, src?: string) => {\n const crack: Crack = { point: objLocal(o, wp), normal: objLocalDir(o, wn), length: Math.min(length, Math.max(p.bound, 0.05)), rays: 5 + Math.floor(rnd() * 5) }\n p.st.cracks.push(crack)\n if (p.st.cracks.length > 16) p.st.cracks.shift()\n markDamaged(p)\n emit({ ...base(o, src), type: 'crack', part: p.spec.id, crack, worldPoint: wp })\n setStage(o, p, 1, {}, src)\n }\n\n const debris = (o: ObjRT, p: PartRT, kind: DebrisKind, point: Vec3, normal: Vec3, count: number, speed: number, src?: string) =>\n emit({ ...base(o, src), type: 'debris', part: p.spec.id, kind, point, normal, count, speed })\n\n const holeRate = (hole: Hole, pressurized: boolean) => (pressurized ? 0.5 : 0.1) * (hole.diameter / 0.009)\n\n // -------------------------------------------------------------------------------------------------------------------------------------------\n // Failure, joints\n // -------------------------------------------------------------------------------------------------------------------------------------------\n\n const detachEvents = (o: ObjRT, opened: JointRT[], by: 'break' | 'tool' | 'part_lost', src?: string) => {\n for (const j of opened) emit({ ...base(o, src), type: 'detach', joint: j.spec.id, parts: [j.spec.a, j.spec.b], by, groups: o.st.groups.map((g) => [...g]) })\n }\n\n const checkDestroyed = (o: ObjRT, src?: string) => {\n if (!o.st.destroyed && o.parts.length > 0 && o.parts.every(isLost)) {\n o.st.destroyed = true\n emit({ ...base(o, src), type: 'destroyed' })\n }\n }\n\n const partLost = (o: ObjRT, p: PartRT, src?: string) => {\n const opened: JointRT[] = []\n for (const j of o.byPart.get(p.spec.id) ?? []) {\n if (j.st.open) continue\n j.st.open = true\n j.st.openedBy = 'part_lost'\n opened.push(j)\n }\n solveStructure(o)\n recomputeGroups(o)\n detachEvents(o, opened, 'part_lost', src)\n collapseCheck(o, src)\n checkDestroyed(o, src)\n }\n\n /**\n * Distance from a world point to the joint's contact region: the overlap box of the two parts' bounds (or, when they only nearly touch, the box\n * of the gap between them). Measuring to the joint centre would make a thick part untouchable: a blow on a trunk's surface is half the trunk's\n * width away from a joint that sits on its axis.\n */\n const jointReach = (o: ObjRT, j: JointRT, point: Vec3): number => {\n const A = partWorldAabb(o, o.parts[j.ia]), B = partWorldAabb(o, o.parts[j.ib])\n let d2 = 0\n for (let k = 0; k < 3; k++) {\n const lo = Math.min(Math.max(A.min[k], B.min[k]), Math.min(A.max[k], B.max[k]))\n const hi = Math.max(Math.max(A.min[k], B.min[k]), Math.min(A.max[k], B.max[k]))\n const d = point[k] < lo ? lo - point[k] : point[k] > hi ? point[k] - hi : 0\n d2 += d * d\n }\n return Math.sqrt(d2)\n }\n /** How far from the contact region a blow still counts: half the smallest dimension of the smaller part, at least 6 cm. */\n const jointReachLimit = (o: ObjRT, j: JointRT): number => {\n const pa = o.parts[j.ia], pb = o.parts[j.ib]\n const small = pa.dims[0] * pa.dims[1] * pa.dims[2] <= pb.dims[0] * pb.dims[1] * pb.dims[2] ? pa : pb\n return Math.max(0.06, 0.5 * Math.min(small.dims[0], small.dims[1], small.dims[2]))\n }\n\n const wearJoints = (o: ObjRT, p: PartRT, amount: number, near?: Vec3, radius = 0.06, src?: string, split = false) => {\n if (amount <= 0) return\n const mine = (o.byPart.get(p.spec.id) ?? []).filter((j) => !j.st.open)\n if (split && mine.length) amount /= mine.length\n for (const j of mine) {\n if (j.st.open) continue\n // the rebar of a reinforced part is its cage: blows do not wear it, only a torch or a saw cuts it (or the cage itself giving way)\n if (j.spec.kind === 'rebar' && (o.parts[j.ia].spec.reinforcement || o.parts[j.ib].spec.reinforcement)) continue\n if (near && jointReach(o, j, near) > Math.max(radius, jointReachLimit(o, j))) continue\n j.st.hp -= amount\n if (j.st.hp <= 0) {\n j.st.hp = 0\n j.st.open = true\n j.st.openedBy = 'break'\n solveStructure(o)\n recomputeGroups(o)\n detachEvents(o, [j], 'break', src)\n collapseCheck(o, src)\n }\n }\n }\n\n // -------------------------------------------------------------------------------------------------------------------------------------------\n // Damage stages: spall, breach, reinforcement cage\n // -------------------------------------------------------------------------------------------------------------------------------------------\n\n const setStage = (o: ObjRT, p: PartRT, stage: DamageStage, extra: { opening?: { center: Vec3; radius: number }; fragments?: Fragment[] } = {}, src?: string) => {\n if (!p.staged || (p.st.stage ?? 0) >= stage) return\n p.st.stage = stage\n emit({ ...base(o, src), type: 'stage', part: p.spec.id, stage, ...extra })\n }\n\n /** `1` when the outward normal points along the part's local +thin axis, else `-1`. */\n const faceOf = (o: ObjRT, p: PartRT, worldNormal: Vec3): number => (rotateVec(quat.conjugate(partXf(o, p).q), worldNormal)[p.thin] >= 0 ? 1 : -1)\n\n const spallOuter = (o: ObjRT, p: PartRT, wp: Vec3, face: number, normal: Vec3, radius: number, src?: string) => {\n if (p.layers.length === 0) return\n const lp = objLocal(o, wp)\n const layer = outerLayer(p.layers, p.rt.spalls, face, lp)\n if (!layer) return\n const spalls = (p.rt.spalls ??= [])\n spalls.push([layer.index, face, lp[0], lp[1], lp[2], radius])\n if (spalls.length > MAX_SPALLS) spalls.shift()\n markDamaged(p)\n emit({ ...base(o, src), type: 'spall', part: p.spec.id, layer: layer.index, point: wp, area: Math.PI * radius * radius })\n debris(o, p, layer.mat.hitDebris, wp, normal, 5, 2, src)\n setStage(o, p, 1, {}, src)\n }\n\n const insideOpening = (o: ObjRT, p: PartRT, objectPoint: Vec3): boolean => {\n const op = p.st.opening\n if (!op) return false\n const [u, v] = planeAxes(p.thin)\n const lp = toLocal(p.pos, p.q, objectPoint)\n const lc = toLocal(p.pos, p.q, op.center)\n return Math.hypot(lp[u] - lc[u], lp[v] - lc[v]) <= op.radius\n }\n\n /** Square metres of wall a blow removes: the volume its damage takes out, over the thickness it crosses. */\n const breachAreaOf = (p: PartRT, damage: number, blast: boolean): number =>\n (damage / (p.mat.hpPerKg * p.mat.density) / Math.max(p.thickness, 0.005)) * (blast ? breachBlastFactor : 1)\n\n const localHolesOf = (p: PartRT): LocalHole[] =>\n p.st.holes.map((h) => ({ point: toLocal(p.pos, p.q, h.point), normal: rotateVec(quat.conjugate(p.q), h.normal), diameter: h.diameter }))\n\n const unitsOf = (p: PartRT) => p.spec.units ?? { size: [p.mat.fragmentSize?.[1] ?? 0.2, p.mat.fragmentSize?.[1] ?? 0.2, p.thickness] as Vec3, bond: 'stack' as const }\n\n const FRAGMENT_SHAPE = { shatter: 'shard', splinter: 'splinter', tear: 'scrap' } as const\n\n /** Unit-sized fragments of `cells` (part-local), thrown from `hitW`; the holes that fell on a unit stay on it as `marks`. */\n const cellFragments = (o: ObjRT, p: PartRT, cells: readonly UnitCell[], hitW: Vec3, dir: Vec3 | null, speed: number, mass: number, tag: string): Fragment[] => {\n const { c, q } = partXf(o, p)\n const holes = p.st.holes.length > 0 ? localHolesOf(p) : []\n const [u, v] = planeAxes(p.thin)\n const faceArea = (cell: UnitCell) => cell.size[u] * cell.size[v]\n const total = cells.reduce((sum, cell) => sum + faceArea(cell), 0) || 1\n const shape = p.spec.units ? 'chunk' : FRAGMENT_SHAPE[p.mat.fracture as keyof typeof FRAGMENT_SHAPE] ?? 'chunk'\n return cells.map((cell, i) => {\n const cw = toWorld(c, q, cell.center)\n const out = vec.normalize(vec.sub(cw, hitW), [0, 1, 0])\n const dv = dir ? vec.normalize(vec.madd(out, dir, 0.6)) : out\n const k = speed * (0.6 + 0.8 * rnd())\n const marks = marksInBox(cell.center, cell.size, undefined, holes)\n return {\n id: `${p.spec.id}#${tag}${i}`, part: p.spec.id, material: p.spec.material, shape, center: cw, size: [cell.size[0], cell.size[1], cell.size[2]], rotation: q,\n velocity: [dv[0] * k, dv[1] * k + 0.5 * rnd(), dv[2] * k], angularVelocity: [(rnd() - 0.5) * 8, (rnd() - 0.5) * 8, (rnd() - 0.5) * 8],\n mass: (mass * faceArea(cell)) / total, ...(marks.length > 0 ? { marks } : {}),\n }\n })\n }\n\n const worldBars = (o: ObjRT, p: PartRT, bars: ReadonlyArray<readonly [Vec3, Vec3]>): Array<[Vec3, Vec3]> => {\n const { c, q } = partXf(o, p)\n return bars.map(([a, b]) => [toWorld(c, q, a), toWorld(c, q, b)])\n }\n\n /** A blow that made through damage of `area` m² at `wp` (world): adds up with the earlier ones within the breach radius, and opens the part past its threshold. */\n const addBreach = (o: ObjRT, p: PartRT, wp: Vec3, area: number, energy: number, dir: Vec3 | null, src?: string) => {\n if (!p.staged || area <= 0 || (p.st.stage ?? 0) >= 2 || p.st.stripped || isLost(p)) return\n const lp = objLocal(o, wp)\n const list = (p.rt.breach ??= [])\n list.push([lp[0], lp[1], lp[2], area])\n if (list.length > MAX_SPALLS) list.shift()\n let total = 0\n const sum: Vec3 = [0, 0, 0]\n for (const [x, y, z, a] of list) {\n if (vec.distance([x, y, z], lp) > breachRadius) continue\n total += a\n sum[0] += x * a\n sum[1] += y * a\n sum[2] += z * a\n }\n if (total < (p.spec.breach?.area ?? BREACH_AREA)) return\n openBreach(o, p, [sum[0] / total, sum[1] / total, sum[2] / total], total, energy, dir, src)\n }\n\n /**\n * Stage 2: the units around `center` (object-local) come out as fragments the size of one unit, the part keeps an opening and its mass drops. An\n * opening as wide as the part cuts it in two (a column, a lintel): that is a fracture instead.\n */\n const openBreach = (o: ObjRT, p: PartRT, center: Vec3, total: number, energy: number, dir: Vec3 | null, src?: string) => {\n const [u, v] = planeAxes(p.thin)\n const radius = clamp(Math.sqrt(total / Math.PI), Math.sqrt((p.spec.breach?.area ?? BREACH_AREA) / Math.PI), breachRadius)\n const lc = toLocal(p.pos, p.q, center)\n lc[p.thin] = 0\n const { c, q } = partXf(o, p)\n const worldCenter = toWorld(c, q, lc)\n if (2 * radius >= Math.min(p.dims[u], p.dims[v])) {\n failPart(o, p, worldCenter, dir, energy, 12, src)\n return\n }\n const cells = cellsWithin(unitGrid(p.dims, p.thin, unitsOf(p), BREACH_GRID_CAP), p.thin, lc, radius)\n const removed = (p.st.mass * cells.reduce((sum, cell) => sum + cell.size[u] * cell.size[v], 0)) / (p.dims[u] * p.dims[v])\n const speed = clamp(Math.sqrt((2 * energy * 0.1) / Math.max(removed, 0.05)), 0.5, 8)\n const fragments = cellFragments(o, p, cells, worldCenter, dir, speed, removed, 'b')\n const holes = localHolesOf(p)\n p.st.holes = p.st.holes.filter((_, i) => !cells.some((cell) => marksInBox(cell.center, cell.size, undefined, [holes[i]!]).length > 0))\n p.st.mass = Math.max(0, p.st.mass - removed)\n p.rt.removed = (p.rt.removed ?? 0) + removed\n p.st.opening = { center: objLocal(o, worldCenter), radius }\n markDamaged(p)\n setStage(o, p, 1, {}, src)\n setStage(o, p, 2, { opening: { center: worldCenter, radius }, fragments }, src)\n debris(o, p, p.mat.breakDebris, worldCenter, vec.scale(dir ?? [0, 1, 0], -1), 14, 3, src)\n const reinforcement = p.spec.reinforcement\n if (reinforcement) {\n const bars = worldBars(o, p, clipBarsToDisc(rebarBars(p.dims, p.thin, reinforcement.spacing), u, v, [lc[u], lc[v]], radius + 0.05))\n if (bars.length > 0) emit({ ...base(o, src), type: 'expose', part: p.spec.id, bars })\n }\n if (p.structural) solveStructure(o)\n }\n\n /** The concrete of a reinforced part is gone: the part stays as its rebar cage, with the capacity the reinforcement leaves it. */\n const stripPart = (o: ObjRT, p: PartRT, reinforcement: NonNullable<PartSpec['reinforcement']>, src?: string) => {\n const bars = rebarBars(p.dims, p.thin, reinforcement.spacing)\n p.st.stripped = true\n p.st.hp = p.st.maxHp\n p.st.mass = rebarMass(bars, reinforcement.diameter, materials.get(reinforcement.material)?.density)\n p.st.holes = []\n p.st.burning = false\n p.rt.jet = false\n markDamaged(p)\n emit({ ...base(o, src), type: 'expose', part: p.spec.id, bars: worldBars(o, p, bars) })\n solveStructure(o)\n collapseCheck(o, src)\n }\n\n /** The units of the part that are still there (the opening of a breach is already gone), as a fragment plan. */\n const unitFragmentPlan = (p: PartRT, cap: number): PlannedFragment[] => {\n const grid = unitGrid(p.dims, p.thin, unitsOf(p), cap)\n const op = p.st.opening\n const cells = op ? cellsOutside(grid, p.thin, toLocal(p.pos, p.q, op.center), op.radius) : grid\n const [u, v] = planeAxes(p.thin)\n return cells.map((cell) => ({ center: cell.center, size: cell.size, q: quat.identity(), shape: 'chunk' as const, weight: cell.size[u] * cell.size[v] }))\n }\n\n /** The bare cage gives way (overload, a blast, a cut that finishes): the part is lost for good. */\n const loseCage = (o: ObjRT, p: PartRT, src?: string) => {\n p.st.status = 'broken'\n p.st.hp = 0\n p.st.mass = 0\n emit({ ...base(o, src), type: 'expose', part: p.spec.id, bars: [] })\n debris(o, p, 'sparks', partXf(o, p).c, [0, 1, 0], 6, 3, src)\n partLost(o, p, src)\n }\n\n /** Part hit points reached 0 (or it was forced to explode): fragments, debris, joints open. */\n const failPart = (o: ObjRT, p: PartRT, origin: Vec3 | null, dir: Vec3 | null, fragEnergy: number, cap: number, src?: string) => {\n if (isLost(p)) return\n if (p.st.stripped) {\n loseCage(o, p, src)\n return\n }\n const ex = p.spec.explodes\n const style = p.mat.fracture === 'none' ? 'snap' : p.mat.fracture\n const { c, q } = partXf(o, p)\n const hitW = origin ?? c\n const grain = p.spec.grain ? AXIS[p.spec.grain] : undefined\n const hitLocal = toLocal(c, q, hitW)\n const unitPlan = p.spec.units ? unitFragmentPlan(p, maxFragments * UNIT_FRAGMENT_FACTOR) : []\n const plan = unitPlan.length > 0 ? unitPlan : planFragments({\n style, dims: p.dims, thickness: p.thickness, grain, hit: hitLocal, fragmentSize: p.mat.fragmentSize ?? [0.04, 0.2], maxFragments, rnd,\n })\n const holes = p.st.holes.length > 0 ? localHolesOf(p) : []\n const mass = p.st.mass\n const totW = plan.reduce((s, f) => s + f.weight, 0) || 1\n const energy = ex ? ex.energy * 0.2 : fragEnergy\n const speed = clamp(Math.sqrt((2 * energy) / Math.max(mass, 0.05)), 0.3, ex ? 40 : cap)\n const fragments: Fragment[] = plan.map((f, i) => {\n const cw = toWorld(c, q, f.center)\n const out = vec.normalize(vec.sub(cw, hitW), [0, 1, 0])\n const dv = dir ? vec.normalize(vec.madd(out, dir, 0.6)) : out\n const k = speed * (0.6 + 0.8 * rnd())\n const marks = marksInBox(f.center, f.size, f.q, holes)\n return {\n id: `${p.spec.id}#${i}`, part: p.spec.id, material: p.spec.material, shape: f.shape, center: cw, size: f.size, rotation: quat.mul(q, f.q),\n velocity: [dv[0] * k, dv[1] * k + 0.5 * rnd(), dv[2] * k],\n angularVelocity: [(rnd() - 0.5) * 12, (rnd() - 0.5) * 12, (rnd() - 0.5) * 12],\n mass: (mass * f.weight) / totW, ...(marks.length > 0 ? { marks } : {}),\n }\n })\n const reinforcement = p.spec.reinforcement\n if (!reinforcement) {\n p.st.status = 'broken'\n p.st.hp = 0\n p.st.mass = 0\n p.st.burning = false\n p.rt.jet = false\n }\n emit({ ...base(o, src), type: 'fracture', part: p.spec.id, style, fragments, point: hitW })\n debris(o, p, p.mat.breakDebris, c, [0, 1, 0], clamp(Math.round(Math.cbrt(partVolume(p.spec.shape)) * 30), 3, 20), 2, src)\n setStage(o, p, 3, {}, src)\n if (reinforcement) {\n stripPart(o, p, reinforcement, src)\n return\n }\n if (ex) {\n emit({ ...base(o, src), type: 'explode', part: p.spec.id, point: c, energy: ex.energy, radius: ex.radius, ...(ex.effect !== undefined ? { effect: ex.effect } : {}) })\n blasts.push({ point: c, energy: ex.energy, radius: ex.radius, source: src })\n }\n partLost(o, p, src)\n }\n\n /**\n * Sets an explosive part off from outside (`explodes.chain`): it will explode after the delay of `trigger`, once. The delay is one draw\n * from the world seed, so the same inputs give the same order of blasts.\n */\n const prime = (o: ObjRT, p: PartRT, trigger: ChainTrigger, src?: string) => {\n const spec = chainDelayOf(p.spec, trigger)\n if (spec === undefined || isLost(p) || p.rt.chainDue !== undefined) return\n const delay = drawChainDelay(spec, rnd)\n p.rt.chainDue = time + delay\n primed.set(p, o)\n o.active = true\n emit({ ...base(o, src), type: 'primed', part: p.spec.id, point: partXf(o, p).c, trigger, delay })\n }\n\n const CHAIN_TRIGGER_OF: Partial<Record<DamageType, ChainTrigger>> = { ballistic: 'shot', explosive: 'explosion' }\n\n /** Remove hit points. Returns the amount actually removed. */\n const applyDamage = (o: ObjRT, p: PartRT, amount: number, type: DamageType, origin: Vec3 | null, dir: Vec3 | null, fragEnergy: number, cap: number, src?: string): number => {\n if (isLost(p) || amount <= 0) return 0\n const prev = p.st.hp\n p.st.hp -= amount\n markDamaged(p)\n const trigger = CHAIN_TRIGGER_OF[type]\n const chained = trigger !== undefined && chainDelayOf(p.spec, trigger) !== undefined\n // a bullet or a blast sets a chained part off after its delay instead of tearing it apart on the spot\n if (chained && p.st.hp <= 0 && p.rt.chainDue === undefined) p.st.hp = Math.min(prev, 1)\n if (p.st.hp <= 0 && p.mat.fracture === 'none' && !p.spec.explodes) p.st.hp = Math.min(prev, 1)\n const done = prev - p.st.hp\n if (done > 0) emit({ ...base(o, src), type: 'damage', part: p.spec.id, amount: done, hp: Math.max(0, p.st.hp), damageType: type })\n if (p.st.hp <= 0) failPart(o, p, origin, dir, fragEnergy, cap, src)\n else if (chained && (trigger === 'explosion' || 1 - p.st.hp / p.st.maxHp >= shotDamageFractionOf(p.spec))) prime(o, p, trigger, src)\n return done\n }\n\n // -------------------------------------------------------------------------------------------------------------------------------------------\n // Hits\n // -------------------------------------------------------------------------------------------------------------------------------------------\n\n interface BallisticOut { penetrated: boolean; exitEnergy: number; exitPoint?: Vec3; damage: number }\n\n const ballistic = (o: ObjRT, p: PartRT, entry: Vec3, n: Vec3, d: Vec3, E: number, caliber: number, pathLen: number, src?: string): BallisticOut => {\n const mat = p.mat\n // a bare cage and an opening do not stop a bullet\n if (p.st.stripped || (p.st.opening && insideOpening(o, p, objLocal(o, entry)))) {\n return { penetrated: true, exitEnergy: E, exitPoint: vec.madd(entry, d, Math.min(pathLen, p.thickness)), damage: 0 }\n }\n const sin = Math.min(1, Math.abs(vec.dot(d, n)))\n const angle = (Math.asin(sin) * 180) / Math.PI\n const slant = Math.max(sin, 0.3)\n const effM = p.thickness / slant\n const face = p.layers.length > 0 ? faceOf(o, p, n) : 1\n const exitLocal = objLocal(o, vec.madd(entry, d, Math.min(pathLen, effM)))\n const coats = p.layers.length > 0 ? layersEnergy(p.layers, p.rt.spalls, face, objLocal(o, entry)) + layersEnergy(p.layers, p.rt.spalls, -face, exitLocal) : 0\n // each coat adds penetration x thickness to the core's; a part without layers keeps the plain formula\n const thr = coats > 0 ? (mat.penetration * p.thickness * 1000 + coats) / slant : mat.penetration * effM * 1000\n const penetrated = E > thr\n // a thin brittle shell that shatters (a pane, a bottle) is lost to one bullet that a thick block of the same glass shrugs off: the same\n // fragility factor that melee uses for blunt hits (1 for ductile materials and for shells 2 cm or thicker, up to x118 for 3 mm glass)\n const scale = (mat.damageScale?.ballistic ?? 1) * (mat.fracture === 'shatter' ? fragility(mat.ductility, p.thickness) : 1)\n const id = p.spec.id\n if (!penetrated && mat.ricochetDeg && angle < mat.ricochetDeg) {\n const r = vec.sub(d, vec.scale(n, 2 * vec.dot(d, n)))\n emit({ ...base(o, src), type: 'ricochet', part: id, point: entry, direction: vec.normalize(r), energy: E * 0.4 })\n debris(o, p, mat.hitDebris, entry, n, 3, 3, src)\n const dmg = applyDamage(o, p, E * DMG_PER_J * scale * 0.2, 'ballistic', entry, d, E * 0.1, 12, src)\n return { penetrated: false, exitEnergy: 0, damage: dmg }\n }\n if (p.layers.length > 0) spallOuter(o, p, entry, face, n, Math.max(0.03, (caliber / 1000) * 4), src)\n const brittle = mat.ductility < 0.3\n const ratio = E / Math.max(thr, 1e-6)\n const crackLen = clamp(0.03 + ratio * 0.2, 0.03, 0.8)\n let exitPoint: Vec3 | undefined\n if (penetrated) {\n exitPoint = vec.madd(entry, d, Math.min(pathLen, effM + layersThickness(p.layers, face) / slant + layersThickness(p.layers, -face) / slant))\n const hole = addHole(o, p, entry, n, (caliber / 1000) * (mat.ductility >= 0.3 ? 1 : 1.5), true, exitPoint, src)\n if (p.layers.length > 0) spallOuter(o, p, exitPoint, -face, vec.scale(n, -1), Math.max(0.03, (caliber / 1000) * 4), src)\n addBreach(o, p, entry, Math.PI * (hole.diameter / 2) * (hole.diameter / 2), E, d, src)\n if (brittle && mat.fracture === 'shatter') addCrack(o, p, entry, n, crackLen, src)\n if (p.spec.contains && (p.st.liters ?? 0) > 0) {\n emit({\n ...base(o, src), type: 'leak', part: id, fluid: p.spec.contains.fluid, point: entry, direction: n, rate: holeRate(hole, !!p.spec.contains.pressurized),\n pressurized: !!p.spec.contains.pressurized,\n })\n }\n } else if (mat.ductility >= 0.5) {\n addDent(o, p, entry, n, Math.max((caliber * 3) / 1000, 0.012), clamp(Math.min(Math.max(p.thickness * 3, 0.003), ratio * 0.01), 0.001, 0.05), src)\n } else if (brittle) {\n addCrack(o, p, entry, n, crackLen, src)\n if (mat.fracture !== 'shatter') addHole(o, p, entry, n, caliber / 1000, false, undefined, src)\n } else addHole(o, p, entry, n, caliber / 1000, false, undefined, src)\n debris(o, p, mat.hitDebris, entry, n, clamp(Math.round(E / 60) + 2, 2, 14), clamp(Math.sqrt(E) / 6, 1, 8), src)\n // a bullet only wears the joints of the part it hits, by the energy the part absorbed (a through-and-through shot transfers little)\n wearJoints(o, p, (penetrated ? thr : E) * 0.02, undefined, 0, src, true)\n const dmg = applyDamage(o, p, E * DMG_PER_J * scale, 'ballistic', entry, d, E * 0.1, 12, src)\n return { penetrated, exitEnergy: penetrated ? E - thr : 0, exitPoint, damage: dmg }\n }\n\n const melee = (o: ObjRT, p: PartRT, kind: 'blunt' | 'slash', E: number, point: Vec3, n: Vec3, d: Vec3, contactR: number, src?: string): number => {\n const mat = p.mat\n const thrMm = mat.penetration * p.thickness * 1000\n // brittle thin shells (a bottle, a mug, a pane) break from a knock that a thick block shrugs off\n const fragile = kind === 'blunt' ? fragility(mat.ductility, p.thickness) : 1\n const Ec = E * fragile\n if (E > 0) {\n if (kind === 'blunt') {\n if (mat.ductility >= 0.5) addDent(o, p, point, n, Math.max(contactR, 0.015), clamp(E * 0.0001, 0.001, 0.06), src)\n else if (mat.ductility < 0.3) {\n if (Ec >= 5) addCrack(o, p, point, n, clamp(0.04 + Math.min(Ec, E * 6) * 0.0008, 0.04, 0.6), src)\n } else if (E >= thrMm * 0.2) addDent(o, p, point, n, Math.max(contactR, 0.015), clamp(E * 0.00006, 0.001, 0.04), src)\n } else if (mat.fracture === 'tear') {\n addHole(o, p, point, n, clamp(0.02 + E * 0.0005, 0.02, 0.3), true, undefined, src)\n } else if (mat.ductility >= 0.5) {\n addDent(o, p, point, n, Math.max(contactR, 0.01), clamp(E * 0.00004, 0.0005, 0.02), src)\n }\n debris(o, p, mat.hitDebris, point, n, clamp(Math.round(E / 25) + 1, 1, 10), clamp(Math.sqrt(E) / 8, 1, 6), src)\n }\n if (kind === 'blunt') wearJoints(o, p, E * 0.3, undefined, 0, src)\n else wearJoints(o, p, E * 0.3, point, 0.06, src)\n if (kind === 'blunt' && E > 0 && p.layers.length > 0) spallOuter(o, p, point, faceOf(o, p, n), n, Math.max(contactR, 0.05), src)\n const scale = mat.damageScale?.[kind] ?? 1\n const done = applyDamage(o, p, Ec * DMG_PER_J * scale * (kind === 'slash' ? p.cutFactor : 1), kind, point, d, E * 0.1, 12, src)\n addBreach(o, p, point, breachAreaOf(p, done, false), E, d, src)\n return done\n }\n\n const blastObject = (o: ObjRT, point: Vec3, energy: number, radius: number, src?: string): HitResult => {\n const start = cur.length\n const res: HitResult = { object: o.id, damage: 0, penetrated: false, exitEnergy: 0, events: [] }\n for (const p of o.parts) {\n if (isLost(p)) continue\n const { c, q } = partXf(o, p)\n const lp = toLocal(c, q, point)\n const dist = distanceToShape(p.spec.shape, lp)\n if (dist >= radius) continue\n const f = (1 - dist / radius) ** 2\n const Ein = energy * f * BLAST_COUPLING\n const sp = surfacePoint(p.spec.shape, lp)\n const wp = toWorld(c, q, sp.point)\n const wn = rotateVec(q, sp.normal)\n if (!res.part) {\n res.part = p.spec.id\n res.point = wp\n }\n const mat = p.mat\n if (Ein > 20) {\n if (mat.ductility >= 0.5) addDent(o, p, wp, wn, clamp(0.05 + Math.sqrt(Ein) * 0.002, 0.05, 0.4), clamp(0.004 + Ein * 0.00002, 0.004, 0.08), src)\n else if (mat.ductility < 0.3 && Ein > 60) addCrack(o, p, wp, wn, clamp(0.05 + Ein * 0.0005, 0.05, 1), src)\n debris(o, p, mat.hitDebris, wp, wn, clamp(Math.round(Ein / 80) + 1, 1, 12), clamp(Math.sqrt(Ein) / 4, 2, 15), src)\n }\n const th = mat.thermal\n if (th.ignitionC !== undefined && p.st.fuel > 0) {\n p.st.temperature += 300 * f\n o.active = true\n if (f >= 0.25 && !p.st.burning) {\n p.st.temperature = Math.max(p.st.temperature, th.ignitionC + 1)\n checkIgnite(o, p, src)\n }\n }\n wearJoints(o, p, Ein * 0.5, undefined, 0, src)\n const amount = Ein * DMG_PER_J * (mat.damageScale?.explosive ?? 1)\n if (blastFrontSpeed > 0 && dist > 0 && mat.tags?.includes('glass')) {\n frontQueue.push({ o, p, due: time + dist / blastFrontSpeed, amount, fragEnergy: Ein * 0.3, point, ...(src !== undefined ? { src } : {}) })\n continue\n }\n if (Ein > 20 && p.layers.length > 0) spallOuter(o, p, wp, faceOf(o, p, wn), wn, clamp(0.05 + Math.sqrt(Ein) * 0.002, 0.05, 0.4), src)\n const dealt = applyDamage(o, p, amount, 'explosive', point, null, Ein * 0.3, 40, src)\n res.damage += dealt\n addBreach(o, p, wp, breachAreaOf(p, dealt, true), Ein, null, src)\n }\n res.events = cur.slice(start)\n return res\n }\n\n const blastWorld = (point: Vec3, energy: number, radius: number, src?: string): HitResult[] => {\n const out: HitResult[] = []\n for (const o of [...objects.values()]) {\n if (vec.distance(toWorld(o.st.position, o.st.rotation, o.bc), point) - o.br >= radius) continue\n const r = blastObject(o, point, energy, radius, src)\n if (r.part) out.push(r)\n }\n for (const im of [...impls.values()]) {\n const inRange = im.impl.describe().parts.some((ps) => {\n const ov = im.poses.get(ps.id)\n const c = toWorld(im.position, im.rotation, ov?.pos ?? ps.position)\n const q = quat.mul(im.rotation, ov?.q ?? quatFromEuler(...(ps.rotation ?? [0, 0, 0])))\n return distanceToShape(ps.shape, toLocal(c, q, point)) < radius\n })\n if (inRange) out.push(implHit(im, { type: 'explosive', point, energy, radius, ...(src !== undefined ? { source: src } : {}) }))\n }\n return out\n }\n\n const heatObject = (o: ObjRT, point: Vec3, T: number, radius: number, duration: number, src?: string): HitResult => {\n const start = cur.length\n const res: HitResult = { object: o.id, damage: 0, penetrated: false, exitEnergy: 0, events: [] }\n for (const p of o.parts) {\n if (isLost(p)) continue\n const dist = distToPart(o, p, point)\n if (dist >= radius) continue\n const f = clamp(1 - dist / radius, 0.25, 1)\n if (!res.part) {\n res.part = p.spec.id\n res.point = point\n }\n if (T > p.st.temperature) {\n const k = (0.8 * f) / (p.mat.thermal.heatCapacity ?? 1)\n p.st.temperature += (T - p.st.temperature) * (1 - Math.exp(-k * duration))\n }\n o.active = true\n checkIgnite(o, p, src)\n }\n res.events = cur.slice(start)\n return res\n }\n\n const doHit = (o: ObjRT, hit: Hit, only?: PartRT): HitResult => {\n let point = hit.point\n let dir = hit.direction\n if (hit.space === 'local') {\n point = toWorld(o.st.position, o.st.rotation, point)\n if (dir) dir = rotateVec(o.st.rotation, dir)\n }\n const src = hit.source\n const start = cur.length\n let res: HitResult = { object: o.id, damage: 0, penetrated: false, exitEnergy: 0, events: [] }\n const E = hit.energy ?? 0\n if (hit.type === 'ballistic') {\n const loc = locate(o, point, dir, 0.05, only)\n if (loc) {\n const out = ballistic(o, loc.p, loc.point, loc.normal, loc.dir, E, hit.caliber ?? 9, loc.pathLen, src)\n res = { ...res, part: loc.p.spec.id, point: loc.point, damage: out.damage, penetrated: out.penetrated, exitEnergy: out.exitEnergy }\n }\n } else if (hit.type === 'blunt' || hit.type === 'slash') {\n const radius = hit.radius ?? 0\n const loc = locate(o, point, dir, Math.max(0.05, radius), only)\n if (loc) {\n res.part = loc.p.spec.id\n res.point = loc.point\n res.damage += melee(o, loc.p, hit.type, E, loc.point, loc.normal, loc.dir, radius || 0.03, src)\n if (radius > 0.1 && !only) {\n for (const p of o.parts) {\n if (p === loc.p || isLost(p)) continue\n const dd = distToPart(o, p, point)\n if (dd >= radius) continue\n const { c, q } = partXf(o, p)\n const sp = surfacePoint(p.spec.shape, toLocal(c, q, point))\n res.damage += melee(o, p, hit.type, E * (1 - dd / radius) ** 2, toWorld(c, q, sp.point), rotateVec(q, sp.normal), loc.dir, radius, src)\n }\n }\n }\n } else if (hit.type === 'explosive') {\n res = blastObject(o, point, E, hit.radius ?? 2, src)\n } else {\n res = heatObject(o, point, hit.temperature ?? 800, hit.radius ?? 0.25, hit.duration ?? 1, src)\n }\n res.events = cur.slice(start)\n return res\n }\n\n // -------------------------------------------------------------------------------------------------------------------------------------------\n // Fire, heat, melting, leaks\n // -------------------------------------------------------------------------------------------------------------------------------------------\n\n const checkIgnite = (o: ObjRT, p: PartRT, src?: string) => {\n const th = p.mat.thermal\n if (isLost(p) || p.st.burning || th.ignitionC === undefined || p.st.fuel <= 0 || p.st.temperature < th.ignitionC) return\n p.st.burning = true\n p.st.temperature = Math.max(p.st.temperature, th.flameC ?? 800)\n o.active = true\n markDamaged(p)\n emit({ ...base(o, src), type: 'ignite', part: p.spec.id, point: partXf(o, p).c })\n }\n\n const syncMass = (p: PartRT) => {\n p.st.mass = Math.max(0, p.mass0 * (1 - p.st.melt) - (p.fuel0 - p.st.fuel))\n }\n\n const burnStep = (o: ObjRT, p: PartRT, dt: number) => {\n const th = p.mat.thermal\n const use = Math.min((th.burnRate ?? 0.01) * p.area * dt, p.st.fuel)\n p.st.fuel -= use\n syncMass(p)\n if (p.structural) o.dirty = true\n p.st.temperature = th.flameC ?? 800\n markDamaged(p)\n if (p.fuel0 > 0) {\n p.st.char = clamp(1 - p.st.fuel / p.fuel0, 0, 1)\n p.st.hp = Math.min(p.st.hp, Math.max(1, p.st.maxHp * (p.st.fuel / p.fuel0)))\n }\n if (p.st.fuel <= 1e-9) {\n p.st.fuel = 0\n p.st.burning = false\n p.st.char = 1\n const c = partXf(o, p).c\n if ((th.fuelFraction ?? 0) >= 0.8) {\n p.st.status = 'ashes'\n p.st.hp = 0\n p.st.mass = p.mass0 * (1 - (th.fuelFraction ?? 0))\n emit({ ...base(o), type: 'burnout', part: p.spec.id, point: c })\n debris(o, p, 'ash', c, [0, 1, 0], clamp(Math.round(Math.cbrt(partVolume(p.spec.shape)) * 30), 3, 20), 1)\n partLost(o, p)\n } else {\n emit({ ...base(o), type: 'burnout', part: p.spec.id, point: c })\n }\n }\n }\n\n const meltStep = (o: ObjRT, p: PartRT, dt: number) => {\n const th = p.mat.thermal\n if (th.meltC === undefined || p.st.temperature <= th.meltC) return\n const rate = (th.meltRate ?? 0.05) * ((p.st.temperature - th.meltC) / 100)\n p.st.melt = Math.min(1, p.st.melt + rate * dt)\n syncMass(p)\n if (p.structural) o.dirty = true\n markDamaged(p)\n p.rt.dripT += dt\n if (p.rt.dripT >= 0.25) {\n p.rt.dripT -= 0.25\n const b = partWorldAabb(o, p)\n const pt: Vec3 = [b.min[0] + (b.max[0] - b.min[0]) * (0.2 + 0.6 * rnd()), b.min[1], b.min[2] + (b.max[2] - b.min[2]) * (0.2 + 0.6 * rnd())]\n emit({ ...base(o), type: 'drip', part: p.spec.id, point: pt, material: p.mat.id })\n }\n if (p.st.melt - p.rt.meltEmitted >= 0.05 || p.st.melt >= 1) {\n p.rt.meltEmitted = p.st.melt\n emit({ ...base(o), type: 'melt', part: p.spec.id, melt: p.st.melt })\n }\n if (p.st.melt >= 1) {\n p.st.status = 'melted'\n p.st.hp = 0\n p.st.mass = 0\n p.st.burning = false\n p.rt.jet = false\n partLost(o, p)\n }\n }\n\n const stepObject = (o: ObjRT, dt: number, sources: Array<{ o: ObjRT; p: PartRT; flame: number }>) => {\n const parts = o.parts\n const T0 = o.tmp\n T0.length = parts.length\n for (let i = 0; i < parts.length; i++) T0[i] = parts[i].st.temperature\n let hot = false\n for (let i = 0; i < parts.length; i++) {\n const p = parts[i]\n if (isLost(p)) continue\n const th = p.mat.thermal\n const hc = th.heatCapacity ?? 1\n const s = p.st\n if (p.rt.jet) {\n s.temperature = Math.max(s.temperature, 900)\n } else if (s.burning) {\n burnStep(o, p, dt)\n if (isLost(p)) continue\n }\n if (!s.burning && !p.rt.jet) {\n // conduction from hotter neighbours through closed joints, then cooling\n let dT = 0\n let maxN = s.temperature\n for (const j of o.byPart.get(p.spec.id)!) {\n if (j.st.open) continue\n const k = j.ia === i ? j.ib : j.ia\n if (isLost(parts[k]) || T0[k] <= T0[i]) continue\n const c = (th.conductivity + parts[k].mat.thermal.conductivity) / 2\n dT += (T0[k] - T0[i]) * (1 - Math.exp((-1.2 * c * dt) / hc))\n if (T0[k] > maxN) maxN = T0[k]\n }\n if (dT > 0) s.temperature = Math.min(s.temperature + dT, maxN)\n s.temperature += (ambient - s.temperature) * (1 - Math.exp(-dt / (20 * hc)))\n if (Math.abs(s.temperature - ambient) < 0.05) s.temperature = ambient\n }\n meltStep(o, p, dt)\n if (isLost(p)) continue\n checkIgnite(o, p)\n const ex = p.spec.explodes\n if (ex && chainDelayOf(p.spec, 'fire') !== undefined) {\n if (s.temperature >= (ex.atC ?? FIRE_PRIME_C)) prime(o, p, 'fire')\n } else if (ex && ex.atC !== undefined && s.temperature >= ex.atC) {\n failPart(o, p, null, null, 0, 40)\n continue\n }\n // leaks\n const cont = p.spec.contains\n if (cont && (s.liters ?? 0) > 0) {\n let rate = 0\n for (const h of s.holes) if (h.through) rate += holeRate(h, !!cont.pressurized)\n if (rate > 0) {\n s.liters = Math.max(0, (s.liters ?? 0) - rate * dt)\n hot = true\n if (FLAMMABLE_FLUIDS.includes(cont.fluid) && s.temperature >= 350 && !p.rt.jet) {\n if (ex && chainDelayOf(p.spec, 'fire') !== undefined) {\n prime(o, p, 'fire')\n continue\n }\n if (ex) {\n failPart(o, p, null, null, 0, 40)\n continue\n }\n p.rt.jet = true\n s.burning = true\n emit({ ...base(o), type: 'ignite', part: p.spec.id, point: partXf(o, p).c })\n }\n }\n }\n if (p.rt.jet && (s.liters ?? 0) <= 0) {\n p.rt.jet = false\n s.burning = false\n emit({ ...base(o), type: 'extinguish', part: p.spec.id })\n }\n // thermal shock\n const sc = th.thermalShockC\n if (sc !== undefined) {\n p.rt.shockT = Math.max(0, p.rt.shockT - dt)\n const rate = Math.abs(s.temperature - p.rt.prevTemp) / dt\n p.rt.prevTemp = s.temperature\n if (rate > sc && p.rt.shockT <= 0) {\n p.rt.shockT = 1\n const { c, q } = partXf(o, p)\n const lp: Vec3 = [(rnd() - 0.5) * p.dims[0], (rnd() - 0.5) * p.dims[1], (rnd() - 0.5) * p.dims[2]]\n const sp = surfacePoint(p.spec.shape, lp)\n addCrack(o, p, toWorld(c, q, sp.point), rotateVec(q, sp.normal), 0.1 + (rate / sc) * 0.1)\n applyDamage(o, p, p.st.maxHp * 0.25, 'heat', null, null, 5, 3)\n if (isLost(p)) continue\n }\n }\n if (s.burning || p.rt.jet) sources.push({ o, p, flame: p.rt.jet ? 900 : th.flameC ?? 800 })\n if (s.burning || p.rt.jet || s.temperature > ambient + 0.5) hot = true\n }\n if (o.hasStructure) {\n if (o.dirty) {\n o.dirty = false\n solveStructure(o)\n }\n for (const p of parts) {\n if (!p.structural || isLost(p)) continue\n const stress = p.st.stress ?? 0\n p.rt.stressT = Math.max(0, (p.rt.stressT ?? 0) - dt)\n if (stress > 1) {\n if (p.rt.stressT <= 0) {\n p.rt.stressT = 0.5\n emit({ ...base(o), type: 'stress', part: p.spec.id, stress, load: p.st.load ?? 0 })\n }\n applyDamage(o, p, overloadRate(stress) * p.st.maxHp * dt, 'blunt', null, null, 5, 3)\n } else if (stress > 0.8 && p.mat.ductility < 0.3 && p.rt.stressT <= 0 && p.st.cracks.length < 6) {\n p.rt.stressT = 0.5\n const { c, q } = partXf(o, p)\n const sp = surfacePoint(p.spec.shape, [(rnd() - 0.5) * p.dims[0], (rnd() - 0.5) * p.dims[1], (rnd() - 0.5) * p.dims[2]])\n addCrack(o, p, toWorld(c, q, sp.point), rotateVec(q, sp.normal), 0.1 + (stress - 0.8) * 0.5)\n }\n }\n if (o.maxStress > 0.8) hot = true\n }\n o.active = hot\n }\n\n const spread = (sources: Array<{ o: ObjRT; p: PartRT; flame: number }>, dt: number) => {\n for (const src of sources) {\n if (!src.p.st.burning && !src.p.rt.jet) continue\n const sc = toWorld(src.o.st.position, src.o.st.rotation, src.p.pos)\n const A = partWorldAabb(src.o, src.p)\n for (const o of objects.values()) {\n if (o === src.o) continue\n if (vec.distance(toWorld(o.st.position, o.st.rotation, o.bc), sc) > o.br + src.p.bound + fireSpread) continue\n for (const q of o.parts) {\n if (isLost(q) || q.st.burning) continue\n const B = partWorldAabb(o, q)\n const gap = Math.hypot(\n Math.max(0, A.min[0] - B.max[0], B.min[0] - A.max[0]),\n Math.max(0, A.min[1] - B.max[1], B.min[1] - A.max[1]),\n Math.max(0, A.min[2] - B.max[2], B.min[2] - A.max[2]),\n )\n if (gap >= fireSpread || src.flame <= q.st.temperature) continue\n const f = 1 - gap / fireSpread\n q.st.temperature += (src.flame - q.st.temperature) * (1 - Math.exp((-0.6 * f * dt) / (q.mat.thermal.heatCapacity ?? 1)))\n o.active = true\n }\n }\n }\n }\n\n // -------------------------------------------------------------------------------------------------------------------------------------------\n // World API\n // -------------------------------------------------------------------------------------------------------------------------------------------\n\n /** The primed part that goes off first: earliest due time, then the older object, then the part listed first. */\n const nextPrimed = (until: number): { o: ObjRT; p: PartRT } | null => {\n let best: { o: ObjRT; p: PartRT } | null = null\n for (const [p, o] of primed) {\n if (objects.get(o.id) !== o || isLost(p)) {\n primed.delete(p)\n continue\n }\n const due = p.rt.chainDue as number\n if (due > until) continue\n if (!best) best = { o, p }\n else {\n const bestDue = best.p.rt.chainDue as number\n if (due < bestDue || (due === bestDue && (o.order < best.o.order || (o.order === best.o.order && (o.idx.get(p.spec.id) ?? 0) < (best.o.idx.get(best.p.spec.id) ?? 0))))) best = { o, p }\n }\n }\n return best\n }\n\n /**\n * Explodes the primed parts whose delay ran out by `until`, one at a time in order of time; each blast is resolved before the next part goes off,\n * so a part it primes counts its delay from that moment, not from the end of the frame.\n */\n const detonatePrimed = (until: number) => {\n const end = time\n for (let next = nextPrimed(until); next; next = nextPrimed(until)) {\n primed.delete(next.p)\n time = Math.max(0, Math.min(end, next.p.rt.chainDue as number))\n next.p.rt.chainDue = undefined\n failPart(next.o, next.p, null, null, 0, 40)\n drainBlasts()\n }\n time = end\n }\n\n /** Lands the damage of the glass the blast front has reached by `time`, the nearest first. */\n const releaseFront = () => {\n if (frontQueue.length === 0) return\n frontQueue.sort((a, b) => a.due - b.due)\n while (frontQueue.length > 0 && frontQueue[0]!.due <= time) {\n const hit = frontQueue.shift()!\n if (objects.get(hit.o.id) !== hit.o) continue\n applyDamage(hit.o, hit.p, hit.amount, 'explosive', hit.point, null, hit.fragEnergy, 40, hit.src)\n }\n }\n\n /** Collapse events are produced after the whole operation: make sure the result of the operation carries them. */\n const withCollapses = (r: { value: HitResult[]; events: DestructionEvent[] }): HitResult[] => {\n const last = r.value[r.value.length - 1]\n if (!last) return r.value\n for (const e of r.events) if (e.type === 'collapse' && !r.value.some((h) => h.events.includes(e))) last.events.push(e)\n return r.value\n }\n\n const whole = (fn: () => HitResult): HitResult => {\n const r = run(fn)\n r.value.events = r.events\n return r.value\n }\n\n const world: DestructionWorld = {\n materials,\n get time() {\n return time\n },\n add(spec, opts: AddOptions = {}) {\n const id = opts.id ?? nextId()\n if (taken(id)) throw new Error(`object \"${id}\" already exists`)\n objects.set(id, build(resolveSpec(spec), id, opts.position ?? [0, 0, 0], opts.rotation ?? [0, 0, 0, 1]))\n return id\n },\n register(impl, opts: AddOptions = {}) {\n const id = opts.id ?? impl.id\n if (taken(id)) throw new Error(`object \"${id}\" already exists`)\n impls.set(id, { impl, position: opts.position ?? [0, 0, 0], rotation: opts.rotation ?? [0, 0, 0, 1], poses: new Map() })\n return id\n },\n setPartPose(objectId, partId, position, rotation) {\n const q = quatFromEuler(...(rotation ?? [0, 0, 0]))\n const im = impls.get(objectId)\n if (im) {\n im.impl.setPartPose?.(partId, position, rotation)\n im.poses.set(partId, { pos: [position[0], position[1], position[2]], q })\n return\n }\n const o = getObj(objectId)\n const p = getPart(o, partId)\n p.pos = [position[0], position[1], position[2]]\n p.q = q\n updateBound(o)\n },\n remove(id) {\n objects.delete(id)\n impls.delete(id)\n },\n has: (id) => taken(id),\n ids: () => [...objects.keys(), ...impls.keys()],\n setTransform(id, position, rotation) {\n const im = impls.get(id)\n if (im) {\n im.position = [position[0], position[1], position[2]]\n im.rotation = [rotation[0], rotation[1], rotation[2], rotation[3]]\n return\n }\n const o = getObj(id)\n o.st.position = [position[0], position[1], position[2]]\n o.st.rotation = [rotation[0], rotation[1], rotation[2], rotation[3]]\n },\n splitGroup(id, group, newId) {\n if (impls.has(id)) throw new Error(`object \"${id}\" is a registered DestructibleImpl and cannot be split; its module owns its pieces`)\n const o = getObj(id)\n if (o.st.groups.length < 2) return id\n const names = o.st.groups[group]\n if (!names) throw new Error(`object \"${id}\" has no group ${group}`)\n const moved = new Set(names)\n const nid = newId ?? nextId()\n if (objects.has(nid)) throw new Error(`object \"${nid}\" already exists`)\n // an open joint between the two objects stays with the original\n const carryFor = (keep: (pid: string) => boolean, keepOpen: (a: string, b: string) => boolean = () => false): Carry => ({\n parts: new Map(o.parts.filter((p) => keep(p.spec.id)).map((p) => [p.spec.id, { st: p.st, rt: p.rt }])),\n joints: new Map([\n ...o.joints.filter((j) => (keep(j.spec.a) && keep(j.spec.b)) || (j.st.open && keepOpen(j.spec.a, j.spec.b))).map((j): [string, { st: JointState; step: number }] => [j.spec.id, { st: j.st, step: j.step }]),\n ...o.st.joints.filter((st) => st.open && !o.jointMap.has(st.id) && keepOpen(st.a, st.b)).map((st): [string, { st: JointState; step: number }] => [st.id, { st, step: 0 }]),\n ]),\n collapsed: [...o.collapsed].filter(keep),\n })\n const sub = (keep: (pid: string) => boolean): DestructibleSpec => {\n const s: DestructibleSpec = { parts: o.spec.parts.filter((p) => keep(p.id)), joints: (o.spec.joints ?? []).filter((j) => keep(j.a) && keep(j.b)) }\n if (o.spec.name !== undefined) s.name = o.spec.name\n if (o.spec.tags) s.tags = [...o.spec.tags]\n return clone(s)\n }\n const inNew = (pid: string) => moved.has(pid)\n const inOld = (pid: string) => !moved.has(pid)\n const pos = o.st.position, rot = o.st.rotation\n const fresh = build(sub(inNew), nid, pos, rot, carryFor(inNew, (a, b) => inNew(a) && inNew(b)))\n const rest = build(sub(inOld), id, pos, rot, carryFor(inOld, (a, b) => !(inNew(a) && inNew(b))))\n objects.set(id, rest)\n objects.set(nid, fresh)\n return nid\n },\n hit(objectId, h) {\n const im = impls.get(objectId)\n if (im) return whole(() => implHit(im, h))\n const o = getObj(objectId)\n return whole(() => doHit(o, h))\n },\n hitPart(objectId, partId, h) {\n const im = impls.get(objectId)\n if (im) return whole(() => implHit(im, h))\n const o = getObj(objectId)\n const p = getPart(o, partId)\n return whole(() => doHit(o, h, p))\n },\n explode(point, energy, radius, source) {\n return withCollapses(run(() => blastWorld(point, energy, radius, source)))\n },\n raycast(origin, direction, maxDistance = Infinity) {\n const r = raycastInternal(origin, direction, maxDistance)\n return r ? { object: r.object, part: r.part, point: r.point, normal: r.normal, distance: r.distance } : null\n },\n shoot(origin, direction, energy, caliber, source) {\n return withCollapses(run(() => {\n const results: HitResult[] = []\n const d = vec.normalize(direction)\n const ignore = new Set<string>()\n let org = origin\n let E = energy\n for (let i = 0; i < 4 && E > 0; i++) {\n const rh = raycastInternal(org, d, Infinity, ignore)\n if (!rh) break\n const im = impls.get(rh.object)\n if (im) {\n const r = implHit(im, { type: 'ballistic', point: rh.point, direction: d, energy: E, caliber, ...(source !== undefined ? { source } : {}) })\n r.part = rh.part\n r.point = rh.point\n r.exitEnergy = r.penetrated ? E * 0.5 : 0\n results.push(r)\n ignore.add(`${im.impl.id}\\0${rh.part}`)\n if (!r.penetrated) break\n org = vec.madd(rh.point, d, Math.min(rh.pathLen, 0.05) + 0.001)\n E = r.exitEnergy\n continue\n }\n const o = getObj(rh.object)\n const p = getPart(o, rh.part)\n const start = cur.length\n const out = ballistic(o, p, rh.point, rh.normal, d, E, caliber, rh.pathLen, source)\n results.push({ object: o.id, part: p.spec.id, point: rh.point, damage: out.damage, penetrated: out.penetrated, exitEnergy: out.exitEnergy, events: cur.slice(start) })\n ignore.add(`${o.id}\\0${p.spec.id}`)\n if (!out.penetrated || !out.exitPoint) break\n org = vec.madd(out.exitPoint, d, 0.001)\n E = out.exitEnergy\n }\n return results\n }))\n },\n disassemble(objectId, jointId, tool: ToolKind, dt) {\n const im = impls.get(objectId)\n if (im) return im.impl.disassemble ? im.impl.disassemble(jointId, tool, dt) : -1\n const o = getObj(objectId)\n const j = o.jointMap.get(jointId)\n if (!j || !j.spec.tools.includes(tool)) return -1\n if (j.st.open) return 1\n return run(() => {\n j.st.progress = Math.min(1, j.st.progress + (j.spec.seconds > 0 ? dt / j.spec.seconds : 1))\n if (j.st.progress >= 1 - 1e-9) j.st.progress = 1\n const step = Math.floor(j.st.progress * 10 + 1e-9)\n if (step > j.step || j.st.progress >= 1) {\n j.step = step\n emit({ ...base(o), type: 'disassemble_progress', joint: j.spec.id, tool, progress: j.st.progress })\n }\n if (j.st.progress >= 1) {\n j.st.open = true\n j.st.openedBy = 'tool'\n solveStructure(o)\n recomputeGroups(o)\n detachEvents(o, [j], 'tool')\n collapseCheck(o)\n }\n return j.st.progress\n }).value\n },\n workableJoints(objectId, tool) {\n const im = impls.get(objectId)\n if (im) {\n const spec = im.impl.describe()\n return im.impl.state().joints.filter((js) => {\n const j = (spec.joints ?? []).find((x) => (x.id ?? `${x.a}~${x.b}`) === js.id)\n return !js.open && !!j && (j.tools ?? JOINT_DEFAULTS[j.kind].tools).includes(tool)\n })\n }\n return getObj(objectId).joints.filter((j) => !j.st.open && j.spec.tools.includes(tool)).map((j) => j.st)\n },\n nearestJoint(objectId, point, maxDistance = 0.3, tool) {\n const im = impls.get(objectId)\n // Candidates: closed joints with their world position and whether `tool` can undo them.\n const cands: { st: JointState; d: number; ok: boolean }[] = []\n if (im) {\n const spec = im.impl.describe()\n for (const js of im.impl.state().joints) {\n if (js.open) continue\n const pos = implJointPos(im, js.a, js.b)\n const j = (spec.joints ?? []).find((x) => (x.id ?? `${x.a}~${x.b}`) === js.id)\n cands.push({ st: js, d: pos ? vec.distance(pos, point) : Infinity, ok: !!j && (j.tools ?? JOINT_DEFAULTS[j.kind].tools).includes(tool as ToolKind) })\n }\n } else {\n const o = getObj(objectId)\n for (const j of o.joints) {\n if (j.st.open) continue\n cands.push({ st: j.st, d: vec.distance(jointWorld(o, j), point), ok: tool ? j.spec.tools.includes(tool) : true })\n }\n }\n const pick = (only: boolean): JointState | null => {\n let best: JointState | null = null\n let bd = maxDistance\n for (const c of cands) {\n if (only && !c.ok) continue\n if (c.d <= bd) {\n bd = c.d\n best = c.st\n }\n }\n return best\n }\n return (tool ? pick(true) : null) ?? pick(false)\n },\n extinguish(objectId, partId, coolC = 200) {\n const im = impls.get(objectId)\n if (im) {\n run(() => im.impl.extinguish?.(partId))\n return\n }\n const o = getObj(objectId)\n run(() => {\n for (const p of partId ? [getPart(o, partId)] : o.parts) {\n if (isLost(p)) continue\n if (p.st.burning) {\n p.st.burning = false\n p.rt.jet = false\n emit({ ...base(o), type: 'extinguish', part: p.spec.id })\n }\n p.st.temperature = Math.max(ambient, p.st.temperature - coolC)\n }\n o.active = true\n })\n },\n setLiters(objectId, partId, liters) {\n if (impls.has(objectId)) return\n const p = getPart(getObj(objectId), partId)\n if (!p.spec.contains) return\n p.st.liters = clamp(liters, 0, p.spec.contains.liters)\n getObj(objectId).active = true\n },\n update(dt) {\n if (!(dt > 0)) return []\n return run(() => {\n time += dt\n const sources: Array<{ o: ObjRT; p: PartRT; flame: number }> = []\n for (const o of [...objects.values()]) if (o.active) stepObject(o, dt, sources)\n if (sources.length) spread(sources, dt)\n detonatePrimed(time)\n releaseFront()\n for (const im of [...impls.values()]) {\n if (!im.impl.update) continue\n for (const e of im.impl.update(dt) ?? []) emit(e.time === undefined ? { ...e, time } : e)\n }\n }).events\n },\n state: (id) => objects.get(id)?.st ?? impls.get(id)?.impl.state() ?? null,\n snapshot: (id) => {\n const st = objects.get(id)?.st ?? impls.get(id)?.impl.state()\n return st ? clone(st) : null\n },\n part: (id, partId) => objects.get(id)?.byId.get(partId)?.st ?? impls.get(id)?.impl.state().parts.find((p) => p.id === partId) ?? null,\n reset(id) {\n const im = impls.get(id)\n if (im) {\n im.impl.reset?.()\n return\n }\n const o = getObj(id)\n objects.set(id, build(o.spec, id, o.st.position, o.st.rotation))\n },\n on(type: DestructionEventType | '*', 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 serialize(): DestructionSnapshot {\n return {\n version: 1,\n time,\n rng: rng.state(),\n counter,\n objects: [...objects.values()].map((o) => {\n const runtime: ObjectRuntime = { parts: {}, joints: {}, collapsed: [...o.collapsed] }\n for (const p of o.parts) runtime.parts[p.spec.id] = clone(p.rt)\n for (const j of o.joints) runtime.joints[j.spec.id] = j.step\n return { id: o.id, spec: clone(o.spec), state: clone(o.st), runtime }\n }),\n ...(impls.size\n ? { impls: [...impls.values()].map((im) => ({ id: im.impl.id, position: [...im.position] as Vec3, rotation: [...im.rotation] as Quat, data: im.impl.serialize?.() })) }\n : {}),\n }\n },\n load(snap) {\n if (snap.version !== 1) throw new Error(`unsupported snapshot version ${String(snap.version)}`)\n objects.clear()\n time = snap.time\n rng.set(snap.rng)\n counter = snap.counter ?? 0\n for (const e of snap.objects) {\n const state = clone(e.state)\n const carry: Carry = {\n parts: new Map(\n state.parts.map((st) => [st.id, { st, rt: e.runtime?.parts[st.id] ? clone(e.runtime.parts[st.id]) : { ...freshRuntime(), prevTemp: st.temperature } }]),\n ),\n joints: new Map(state.joints.map((st) => [st.id, { st, step: e.runtime?.joints[st.id] ?? 0 }])),\n collapsed: e.runtime?.collapsed,\n }\n objects.set(e.id, build(clone(e.spec), e.id, state.position, state.rotation, carry))\n }\n for (const e of snap.impls ?? []) {\n const im = impls.get(e.id)\n if (!im) continue\n im.position = [e.position[0], e.position[1], e.position[2]]\n im.rotation = [e.rotation[0], e.rotation[1], e.rotation[2], e.rotation[3]]\n if (e.data !== undefined) im.impl.load?.(e.data)\n }\n },\n }\n return world\n}\n","[\n {\n \"id\": \"brick_wall_plastered\",\n \"name\": \"Plastered brick wall\",\n \"core\": { \"material\": \"brick\", \"thickness\": 0.09 },\n \"layers\": [{ \"material\": \"plaster\", \"thickness\": 0.02, \"faces\": \"both\" }],\n \"units\": { \"size\": [0.23, 0.075, 0.11], \"bond\": \"running\", \"mortar\": \"plaster\" },\n \"breach\": { \"area\": 0.25 }\n },\n {\n \"id\": \"concrete_block_wall\",\n \"name\": \"Concrete block wall\",\n \"core\": { \"material\": \"concrete\", \"thickness\": 0.14 },\n \"fill\": 0.55,\n \"units\": { \"size\": [0.39, 0.19, 0.14], \"bond\": \"running\", \"mortar\": \"plaster\" },\n \"breach\": { \"area\": 0.25 }\n },\n {\n \"id\": \"reinforced_concrete\",\n \"name\": \"Reinforced concrete wall\",\n \"core\": { \"material\": \"concrete\", \"thickness\": 0.2 },\n \"reinforcement\": { \"material\": \"steel\", \"spacing\": 0.18, \"diameter\": 0.008, \"residualCapacity\": 0.15 },\n \"units\": { \"size\": [0.6, 0.42, 0.2], \"bond\": \"stack\", \"mortar\": \"concrete\" },\n \"breach\": { \"area\": 0.25 }\n },\n {\n \"id\": \"drywall\",\n \"name\": \"Drywall partition on steel studs\",\n \"core\": { \"material\": \"plaster\", \"thickness\": 0.0125 },\n \"layers\": [{ \"material\": \"plaster\", \"thickness\": 0.0125, \"faces\": \"+\" }],\n \"depth\": 0.095,\n \"breach\": { \"area\": 0.25 }\n },\n {\n \"id\": \"wood_frame_wall\",\n \"name\": \"Wood frame wall\",\n \"core\": { \"material\": \"plywood\", \"thickness\": 0.012 },\n \"layers\": [{ \"material\": \"plaster\", \"thickness\": 0.0125, \"faces\": \"-\" }],\n \"depth\": 0.1135,\n \"breach\": { \"area\": 0.25 }\n },\n {\n \"id\": \"log_wall\",\n \"name\": \"Log wall\",\n \"core\": { \"material\": \"wood_soft\", \"thickness\": 0.2 },\n \"fill\": 0.785,\n \"breach\": { \"area\": 0.25 }\n },\n {\n \"id\": \"sheet_metal_shack\",\n \"name\": \"Corrugated sheet metal wall\",\n \"core\": { \"material\": \"steel\", \"thickness\": 0.0006 },\n \"depth\": 0.02,\n \"breach\": { \"area\": 0.25 }\n },\n {\n \"id\": \"glass_pane\",\n \"name\": \"Glass pane\",\n \"core\": { \"material\": \"glass\", \"thickness\": 0.006 },\n \"breach\": { \"area\": 0.1 }\n }\n]\n","import assembliesJson from './data/assemblies.json'\nimport { MATERIALS } from './materials'\nimport type { MaterialDef, MaterialId, PartBreach, PartSpec, PartUnits, Reinforcement, SurfaceLayer, Vec3 } from './types'\n\n/**\n * A wall as a builder builds it: a core with coats, the masonry unit it is laid in, its rebar and when it breaches. Dimensions are metres (data in\n * `src/data/assemblies.json`); `assemblyPart` turns one into a `PartSpec`. Extend or override with `registerAssembly`.\n */\nexport interface AssemblyDef {\n id: string\n name: string\n /** `thickness` is the core's, in metres: what a bullet has to get through besides the coats. */\n core: { material: MaterialId; thickness: number }\n /** Coats, outermost first. */\n layers?: readonly SurfaceLayer[]\n /** Physical depth of the wall when it is not core plus coats (a drywall partition is mostly air between the boards). */\n depth?: number\n /** Solid fraction of the core (hollow blocks, round logs): scales its mass and the thickness a bullet crosses. Default 1. */\n fill?: number\n units?: PartUnits\n reinforcement?: Reinforcement\n breach?: PartBreach\n}\n\nexport interface AssemblyPartOptions {\n id: string\n /** Along the part's x axis. */\n width: number\n /** Along the part's y axis. */\n height: number\n position: Vec3\n rotation?: Vec3\n structure?: PartSpec['structure']\n /** Other `PartSpec` fields (tags, anchored, hp...). */\n extra?: Partial<PartSpec>\n /** Materials to read densities from (default the built-in `MATERIALS`). */\n materials?: ReadonlyMap<MaterialId, MaterialDef>\n}\n\n/** Built-in assemblies; `registerAssembly` adds to this list. */\nexport const ASSEMBLIES: AssemblyDef[] = assembliesJson as unknown as AssemblyDef[]\n\nexport const registerAssembly = (def: AssemblyDef): void => {\n const at = ASSEMBLIES.findIndex((a) => a.id === def.id)\n if (at >= 0) ASSEMBLIES[at] = def\n else ASSEMBLIES.push(def)\n}\n\nexport const getAssembly = (id: string): AssemblyDef | undefined => ASSEMBLIES.find((a) => a.id === id)\n\nconst faceCount = (layer: SurfaceLayer): number => (layer.faces === 'both' ? 2 : 1)\n\n/** Physical depth of the wall in metres. */\nexport const assemblyDepth = (def: AssemblyDef): number => def.depth ?? def.core.thickness + (def.layers ?? []).reduce((sum, l) => sum + l.thickness * faceCount(l), 0)\n\n/**\n * The wall as a box `[width, height, depth]` (thin along local z) with the assembly's layers, units, rebar and breach. Mass is the core (times `fill`)\n * plus the coats; `thickness` is the core's, so each coat adds its own `penetration x thickness` to what a bullet must cross.\n */\nexport const assemblyPart = (assemblyId: string, options: AssemblyPartOptions): PartSpec => {\n const def = getAssembly(assemblyId)\n if (!def) throw new Error(`unknown assembly \"${assemblyId}\"`)\n const densityOf = (id: MaterialId): number => {\n const m = options.materials?.get(id) ?? MATERIALS.find((x) => x.id === id)\n if (!m) throw new Error(`unknown material \"${id}\" in assembly \"${assemblyId}\"`)\n return m.density\n }\n const fill = def.fill ?? 1\n const area = options.width * options.height\n const coreMass = area * def.core.thickness * fill * densityOf(def.core.material)\n const layerMass = (def.layers ?? []).reduce((sum, l) => sum + area * l.thickness * faceCount(l) * densityOf(l.material), 0)\n return {\n id: options.id,\n material: def.core.material,\n shape: { kind: 'box', size: [options.width, options.height, assemblyDepth(def)] },\n position: options.position,\n ...(options.rotation ? { rotation: options.rotation } : {}),\n mass: coreMass + layerMass,\n thickness: def.core.thickness * fill,\n ...(def.layers ? { layers: def.layers.map((l) => ({ ...l })) } : {}),\n ...(def.units ? { units: { ...def.units, size: [...def.units.size] as Vec3 } } : {}),\n ...(def.reinforcement ? { reinforcement: { ...def.reinforcement } } : {}),\n ...(def.breach ? { breach: { ...def.breach } } : {}),\n ...(options.structure ? { structure: options.structure } : {}),\n ...options.extra,\n }\n}\n","import type { DestructibleSpec, JointSpec, MaterialId, PartSpec } from './types'\n\nexport interface FrameOptions {\n /** Total length along x between the outer column centres, metres (default 8). */\n spanX?: number\n /** Total length along z (default 8). */\n spanZ?: number\n /** Columns along x (at least 2, default 3). */\n columnsX?: number\n /** Columns along z (at least 2, default 3). */\n columnsZ?: number\n /** Height of one floor from slab top to the underside of the next slab, metres (default 3). */\n height?: number\n /** Number of floors (default 1). */\n floors?: number\n /** Brick walls on every perimeter bay of every floor; the first bay of the ground floor on the -z side gets a door opening (default false). */\n walls?: boolean\n /** A thin roof slab resting on the top beams (default true); without it the top floor is open. */\n roof?: boolean\n columnSize?: number\n beamHeight?: number\n slabThickness?: number\n wallThickness?: number\n doorWidth?: number\n doorHeight?: number\n /** Material of foundations, columns, beams, slabs and roof (default `concrete`). */\n material?: MaterialId\n wallMaterial?: MaterialId\n /** Override the load capacity of every column, kg (see `PartState.load`). */\n columnCapacity?: number\n}\n\n/**\n * A parametric test structure: a grid of columns on foundations, beams between the column tops, one slab per floor (the last one a roof), and\n * optional brick walls with a door. Joints are explicit and directional: columns rest on foundations (or on the slab below) with `rebar`, beams on\n * columns, slabs on beams, the roof rests on the top beams, walls hang on their columns with `mortar`. Part ids: `foundation_i_j`, `column_f_i_j`,\n * `beam_x_f_i_j`, `beam_z_f_i_j`, `slab_f`, `roof`, `wall_*`, `door`. Ground is y = 0 (the top of the foundations).\n */\nexport const buildFrame = (o: FrameOptions = {}): DestructibleSpec => {\n const spanX = o.spanX ?? 8, spanZ = o.spanZ ?? 8\n const nx = Math.max(2, o.columnsX ?? 3), nz = Math.max(2, o.columnsZ ?? 3)\n const H = o.height ?? 3, floors = Math.max(1, o.floors ?? 1)\n const cs = o.columnSize ?? 0.3, bh = o.beamHeight ?? 0.3, bw = 0.2, T = o.slabThickness ?? 0.2, wt = o.wallThickness ?? 0.2\n const dw = o.doorWidth ?? 1, dh = o.doorHeight ?? 2.1\n const mat = o.material ?? 'concrete', wmat = o.wallMaterial ?? 'brick'\n const roof = o.roof ?? true\n const xs = Array.from({ length: nx }, (_, i) => -spanX / 2 + (i * spanX) / (nx - 1))\n const zs = Array.from({ length: nz }, (_, j) => -spanZ / 2 + (j * spanZ) / (nz - 1))\n const parts: PartSpec[] = []\n const joints: JointSpec[] = []\n const on = (a: string, b: string, kind: JointSpec['kind']) => joints.push({ id: `${a}~${b}`, a, b, kind, bears: 'a_on_b' })\n\n for (let i = 0; i < nx; i++) {\n for (let j = 0; j < nz; j++) {\n parts.push({\n id: `foundation_${i}_${j}`, material: mat, shape: { kind: 'box', size: [cs * 2.2, 0.3, cs * 2.2] }, position: [xs[i], -0.15, zs[j]],\n structure: { role: 'foundation' }, anchored: true,\n })\n }\n }\n for (let f = 0; f < floors; f++) {\n const yb = f * (H + T)\n const top = yb + H\n const last = f === floors - 1\n const slabId = last && roof ? 'roof' : `slab_${f}`\n for (let i = 0; i < nx; i++) {\n for (let j = 0; j < nz; j++) {\n const id = `column_${f}_${i}_${j}`\n parts.push({\n id, material: mat, shape: { kind: 'box', size: [cs, H, cs] }, position: [xs[i], yb + H / 2, zs[j]],\n structure: o.columnCapacity === undefined ? { role: 'column' } : { role: 'column', capacity: o.columnCapacity },\n })\n on(id, f === 0 ? `foundation_${i}_${j}` : `slab_${f - 1}`, 'rebar')\n }\n }\n const beams: string[] = []\n const beam = (id: string, size: [number, number, number], pos: [number, number, number], c1: string, c2: string) => {\n parts.push({ id, material: mat, shape: { kind: 'box', size }, position: pos, structure: { role: 'beam' } })\n on(id, c1, 'rebar')\n on(id, c2, 'rebar')\n beams.push(id)\n }\n for (let j = 0; j < nz; j++) {\n for (let i = 0; i < nx - 1; i++) {\n beam(`beam_x_${f}_${i}_${j}`, [xs[i + 1] - xs[i] - cs, bh, bw], [(xs[i] + xs[i + 1]) / 2, top - bh / 2, zs[j]], `column_${f}_${i}_${j}`, `column_${f}_${i + 1}_${j}`)\n }\n }\n for (let i = 0; i < nx; i++) {\n for (let j = 0; j < nz - 1; j++) {\n beam(`beam_z_${f}_${i}_${j}`, [bw, bh, zs[j + 1] - zs[j] - cs], [xs[i], top - bh / 2, (zs[j] + zs[j + 1]) / 2], `column_${f}_${i}_${j}`, `column_${f}_${i}_${j + 1}`)\n }\n }\n const thick = last && roof ? T * 0.75 : T\n parts.push({\n id: slabId, material: mat, shape: { kind: 'box', size: [spanX + cs + 0.2, thick, spanZ + cs + 0.2] }, position: [0, top + thick / 2, 0],\n structure: { role: last && roof ? 'roof' : 'slab' },\n })\n for (const b of beams) on(slabId, b, last && roof ? 'resting' : 'rebar')\n\n if (o.walls) {\n const wh = H - bh\n const wy = yb + wh / 2\n const wall = (id: string, size: [number, number, number], pos: [number, number, number], supports: string[]) => {\n parts.push({ id, material: wmat, shape: { kind: 'box', size }, position: pos, structure: { role: 'wall' } })\n for (const s of supports) on(id, s, 'mortar')\n }\n for (const j of [0, nz - 1]) {\n for (let i = 0; i < nx - 1; i++) {\n const c1 = `column_${f}_${i}_${j}`, c2 = `column_${f}_${i + 1}_${j}`\n const avail = xs[i + 1] - xs[i] - cs\n if (f === 0 && j === 0 && i === 0 && avail > dw + 0.4) {\n const side = (avail - dw) / 2\n const lx = xs[i] + cs / 2 + side / 2, rx = xs[i + 1] - cs / 2 - side / 2\n wall(`wall_door_left`, [side, wh, wt], [lx, wy, zs[j]], [c1])\n wall(`wall_door_right`, [side, wh, wt], [rx, wy, zs[j]], [c2])\n const lh = wh - dh\n wall(`wall_door_lintel`, [dw, lh, wt], [xs[i] + cs / 2 + side + dw / 2, yb + dh + lh / 2, zs[j]], ['wall_door_left', 'wall_door_right'])\n parts.push({ id: 'door', material: 'wood_hard', shape: { kind: 'box', size: [dw - 0.04, dh, 0.05] }, position: [xs[i] + cs / 2 + side + dw / 2, yb + dh / 2, zs[j]], tags: ['door'] })\n joints.push({ id: 'door~wall_door_left', a: 'door', b: 'wall_door_left', kind: 'hinge', bears: false })\n } else wall(`wall_x_${f}_${i}_${j}`, [avail, wh, wt], [(xs[i] + xs[i + 1]) / 2, wy, zs[j]], [c1, c2])\n }\n }\n for (const i of [0, nx - 1]) {\n for (let j = 0; j < nz - 1; j++) {\n wall(`wall_z_${f}_${i}_${j}`, [wt, wh, zs[j + 1] - zs[j] - cs], [xs[i], wy, (zs[j] + zs[j + 1]) / 2], [`column_${f}_${i}_${j}`, `column_${f}_${i}_${j + 1}`])\n }\n }\n }\n }\n return { name: 'frame', parts, joints, tags: ['structure'] }\n}\n","import { createDestruction } from './world'\nimport type { DestructibleSpec, SizeStructureOptions, StructureRole } from './types'\n\n/**\n * Sizes the load capacity of a structure the way an engineer would, from the loads the world itself computes. The intact spec goes through the\n * same load solver `createDestruction` uses, and each structural part (`structure.role` other than `none`) gets `structure.capacity = load / safety`,\n * so its at-rest stress is exactly `safety` (`safetyByRole` overrides it per role). With `redundancy` set, every part whose role is in\n * `redundancyRoles` (default `column`) is also removed in turn and the capacity of the parts that would carry more is raised so they stay at or below\n * `redundancy`. Parts without `structure`, with role `none`, or with no support path to an\n * anchored part are left as they are. The spec is not modified; a copy is returned.\n */\nexport const sizeStructure = (spec: DestructibleSpec, options: SizeStructureOptions = {}): DestructibleSpec => {\n const safety = options.safety ?? 0.65\n const redundancy = options.redundancy\n const redundancyRoles: readonly StructureRole[] = options.redundancyRoles ?? ['column']\n const safetyOf = (role: StructureRole): number => options.safetyByRole?.[role] ?? safety\n const loads = (s: DestructibleSpec): Map<string, { load: number; stress: number }> => {\n const w = createDestruction(options.materials ? { materials: options.materials } : {})\n const id = w.add(s)\n return new Map(w.state(id)!.parts.map((p) => [p.id, { load: p.load ?? 0, stress: p.stress ?? 0 }]))\n }\n const out = structuredClone(spec)\n const base = loads(out)\n const need = new Map<string, number>()\n for (const p of out.parts) {\n if (!p.structure || p.structure.role === 'none') continue\n const b = base.get(p.id)\n if (!b || b.stress <= 0) continue\n need.set(p.id, b.load / safetyOf(p.structure.role))\n }\n if (redundancy !== undefined) {\n for (const col of out.parts) {\n if (!col.structure || !redundancyRoles.includes(col.structure.role)) continue\n const reduced: DestructibleSpec = {\n ...out,\n parts: out.parts.filter((p) => p.id !== col.id),\n joints: (out.joints ?? []).filter((j) => j.a !== col.id && j.b !== col.id),\n }\n // joints are inferred when the spec has none; removing a part then removes its joints with it\n if (!out.joints) delete reduced.joints\n const l = loads(reduced)\n for (const [id, n] of need) {\n const r = l.get(id)\n if (r && r.stress > 0) need.set(id, Math.max(n, r.load / redundancy))\n }\n }\n }\n for (const p of out.parts) {\n const n = need.get(p.id)\n if (n !== undefined) p.structure = { ...p.structure!, capacity: n }\n }\n return out\n}\n","import type { DamageType, DestructionEventType, FractureStyle, JointKind, PartStatus, StructureRole, ToolKind } from './types'\n\n/** Locales shipped with the package. Unknown locales fall back to `en`. */\nexport type DestructionLocale = 'en' | 'pt-BR'\n\nexport interface DestructionStrings {\n /** Display name per material id (see `MATERIALS`). Unknown ids are not listed. */\n materialNames: Record<string, string>\n partStatus: Record<PartStatus, string>\n jointKinds: Record<JointKind, string>\n toolKinds: Record<ToolKind, string>\n damageTypes: Record<DamageType, string>\n /** Short label per event type, for logs and kill-feed style text. */\n eventLabels: Record<DestructionEventType, string>\n fractureStyles: Record<FractureStyle, string>\n structureRoles: Record<StructureRole, string>\n}\n\nexport const locales: Record<DestructionLocale, DestructionStrings> = {\n en: {\n materialNames: {\n steel: 'steel', cast_iron: 'cast iron', aluminium: 'aluminium', copper: 'copper', wood_soft: 'softwood', wood_hard: 'hardwood', plywood: 'plywood',\n mdf: 'MDF', glass: 'glass', tempered_glass: 'tempered glass', ceramic: 'ceramic', concrete: 'concrete', brick: 'brick', plaster: 'plaster',\n stone: 'stone', plastic: 'plastic', rubber: 'rubber', fabric: 'fabric', foam: 'foam', leather: 'leather', paper: 'paper', foliage: 'foliage',\n green_wood: 'green wood', cactus: 'cactus', flesh_fruit: 'fruit', wax: 'wax', soil: 'soil',\n },\n partStatus: { intact: 'intact', damaged: 'damaged', collapsed: 'collapsed', broken: 'broken', melted: 'melted', ashes: 'ashes' },\n jointKinds: {\n screw: 'screw', bolt: 'nut and bolt', nail: 'nail', weld: 'weld', glue: 'glue', hinge: 'hinge', clip: 'clip', tie: 'tie', mortar: 'mortar',\n fused: 'fused', stem: 'stem', rebar: 'rebar', resting: 'resting',\n },\n toolKinds: {\n hand: 'hand', screwdriver: 'screwdriver', wrench: 'wrench', pry_bar: 'pry bar', saw: 'saw', cutting_torch: 'cutting torch', knife: 'knife', hammer: 'hammer',\n },\n damageTypes: { ballistic: 'ballistic', blunt: 'blunt', slash: 'slash', explosive: 'explosive', fire: 'fire', heat: 'heat' },\n eventLabels: {\n damage: 'damage', dent: 'dent', hole: 'hole', crack: 'crack', ricochet: 'ricochet', debris: 'debris', fracture: 'broke', detach: 'came loose',\n disassemble_progress: 'taking apart', ignite: 'caught fire', extinguish: 'put out', burnout: 'burned to ash', melt: 'melting', drip: 'dripping',\n leak: 'leak', explode: 'exploded', primed: 'about to explode', stress: 'overload', collapse: 'collapsed', spall: 'plaster came off', stage: 'damage stage', expose: 'rebar bare', body: 'body', destroyed: 'destroyed',\n },\n fractureStyles: { shatter: 'shatter', crumble: 'crumble', splinter: 'splinter', tear: 'tear', snap: 'snap', none: 'none' },\n structureRoles: { foundation: 'foundation', column: 'column', beam: 'beam', slab: 'slab', wall: 'wall', partition: 'partition', roof: 'roof', stair: 'stairs', none: 'none' },\n },\n 'pt-BR': {\n materialNames: {\n steel: 'aço', cast_iron: 'ferro fundido', aluminium: 'alumínio', copper: 'cobre', wood_soft: 'madeira macia', wood_hard: 'madeira de lei',\n plywood: 'compensado', mdf: 'MDF', glass: 'vidro', tempered_glass: 'vidro temperado', ceramic: 'cerâmica', concrete: 'concreto', brick: 'tijolo',\n plaster: 'gesso', stone: 'pedra', plastic: 'plástico', rubber: 'borracha', fabric: 'tecido', foam: 'espuma', leather: 'couro', paper: 'papel',\n foliage: 'folhagem', green_wood: 'madeira verde', cactus: 'cacto', flesh_fruit: 'fruta', wax: 'cera', soil: 'terra',\n },\n partStatus: { intact: 'intacto', damaged: 'danificado', collapsed: 'desabado', broken: 'quebrado', melted: 'derretido', ashes: 'cinzas' },\n jointKinds: {\n screw: 'parafuso', bolt: 'porca e parafuso', nail: 'prego', weld: 'solda', glue: 'cola', hinge: 'dobradiça', clip: 'encaixe', tie: 'amarração',\n mortar: 'argamassa', fused: 'fundido', stem: 'caule', rebar: 'vergalhão', resting: 'apoiado',\n },\n toolKinds: {\n hand: 'mão', screwdriver: 'chave de fenda', wrench: 'chave inglesa', pry_bar: 'pé de cabra', saw: 'serra', cutting_torch: 'maçarico', knife: 'faca', hammer: 'martelo',\n },\n damageTypes: { ballistic: 'tiro', blunt: 'impacto', slash: 'corte', explosive: 'explosão', fire: 'fogo', heat: 'calor' },\n eventLabels: {\n damage: 'dano', dent: 'amassado', hole: 'furo', crack: 'trinca', ricochet: 'ricochete', debris: 'estilhaços', fracture: 'quebrou', detach: 'soltou',\n disassemble_progress: 'desmontando', ignite: 'pegou fogo', extinguish: 'apagou', burnout: 'virou cinza', melt: 'derretendo', drip: 'pingando',\n leak: 'vazamento', explode: 'explodiu', primed: 'prestes a explodir', stress: 'sobrecarga', collapse: 'desabou', spall: 'reboco soltou', stage: 'estágio de dano', expose: 'vergalhão à mostra', body: 'corpo', destroyed: 'destruído',\n },\n fractureStyles: { shatter: 'estilhaçar', crumble: 'esfarelar', splinter: 'lascar', tear: 'rasgar', snap: 'partir', none: 'nenhuma' },\n structureRoles: { foundation: 'fundação', column: 'pilar', beam: 'viga', slab: 'laje', wall: 'parede', partition: 'divisória', roof: 'telhado', stair: 'escada', none: 'nenhum' },\n },\n}\n\nexport const resolveStrings = (locale: string | undefined): DestructionStrings => locales[(locale as DestructionLocale) ?? 'en'] ?? locales.en\n","import { checkDestructible as check } from '@adatechnology/game-contracts'\nimport type { CheckOptions as ContractCheckOptions, DestructibleImpl } from '@adatechnology/game-contracts'\nimport { MATERIALS } from './materials'\n\n// The contract itself lives in @adatechnology/game-contracts (a module depends only on that); re-exported here so that no import breaks.\nexport { IMPACT_CONTRACT, checkHit } from '@adatechnology/game-contracts'\nexport type { DestructibleImpl, ImpactContract } from '@adatechnology/game-contracts'\n\nexport interface CheckOptions {\n /** Ids of custom materials the impl uses besides the built-in `MATERIALS`. */\n materials?: string[]\n}\n\n/**\n * `checkDestructible` of `@adatechnology/game-contracts`, with the built-in `MATERIALS` (plus `options.materials`) as the known material ids.\n * It hits the impl (and calls `reset()` afterwards when there is one).\n */\nexport const checkDestructible = (impl: DestructibleImpl, options: CheckOptions = {}): { ok: boolean; problems: string[] } => {\n const contractOptions: ContractCheckOptions = { materials: [...MATERIALS.map((m) => m.id), ...(options.materials ?? [])] }\n return check(impl, contractOptions)\n}\n","import type * as THREE_NS from 'three'\nimport { guessMaterial, inferJoints } from '@adatechnology/game-destruction'\nimport type { JointKind, JointSpec, MaterialId, PartSpec } from '@adatechnology/game-destruction'\nimport type { Mesh, Object3D, SpecFromObjectOptions, SpecFromObjectResult, ThreeNamespace } from './types'\n\ninterface Entry {\n key: string\n meshes: Mesh[]\n box: THREE_NS.Box3\n material: MaterialId\n explicitId: string | null\n fill?: number\n hollow: boolean\n tags?: string[]\n /** Part id wished by the host (`userData.destructionPart`). */\n fixedId: boolean\n name: string\n /** `parent` / `kind` of an object-valued `destructionPart`: the part is attached to `parent` by a joint of that kind. */\n parent?: string\n jointKind?: JointKind\n}\n\n/** What `userData.destructionPart` says: a string (the part id) or an object `{ id, material?, parent?, kind? }`. */\nexport interface PartHint {\n id: string\n material?: string\n parent?: string\n kind?: string\n}\n\n/** Read `userData.destructionPart` (string or object with an `id`); null when it names no part. */\nexport function partHintOf(value: unknown): PartHint | null {\n if (typeof value === 'string') return value ? { id: value } : null\n if (!value || typeof value !== 'object') return null\n const v = value as Record<string, unknown>\n if (typeof v.id !== 'string' || !v.id) return null\n const str = (x: unknown): string | undefined => (typeof x === 'string' && x ? x : undefined)\n return { id: v.id, material: str(v.material), parent: str(v.parent), kind: str(v.kind) }\n}\n\nconst METAL = /steel|iron|metal|alumin|copper|brass|bronze|tin|titan|chrome|zinc|lead/i\n\nfunction sanitizeId(name: string): string {\n let id = name\n .trim()\n .replace(/([a-z0-9])([A-Z])/g, '$1_$2')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '_')\n .replace(/^_+|_+$/g, '')\n if (id && /^[0-9]/.test(id)) id = `p_${id}`\n return id\n}\n\nfunction isMesh(o: Object3D): o is Mesh {\n return (o as Mesh).isMesh === true\n}\n\nfunction sizeOf(box: THREE_NS.Box3): [number, number, number] {\n return [box.max.x - box.min.x, box.max.y - box.min.y, box.max.z - box.min.z]\n}\n\n/**\n * Build a `DestructibleSpec` from a three.js object: one part per mesh (meshes sharing `userData.destructionPart` become one part), each an\n * axis-aligned box in the root's space (rotation omitted), with the material taken from `options.materialOf`, `userData.destructionMaterial`\n * or a guess from the names. Tiny meshes are merged into the part they overlap most; joints are inferred from touching parts.\n *\n * Meshes that are invisible or have `userData.destructionIgnore` (or sit under such an object) are skipped. The result also tells which meshes\n * became which part, ready to be passed to `bind`.\n */\nexport function specFromObject3D(THREE: ThreeNamespace, root: Object3D, options: SpecFromObjectOptions = {}): SpecFromObjectResult {\n const fallback = options.fallback ?? 'plastic'\n const minSize = options.minSize ?? 0.015\n root.updateMatrixWorld(true)\n const inv = root.matrixWorld.clone().invert()\n\n // 1. collect meshes -> entries (grouped by an explicit part name)\n const entries: Entry[] = []\n const byKey = new Map<string, Entry>()\n const visit = (o: Object3D): void => {\n if (!o.visible || o.userData?.destructionIgnore) return\n if (isMesh(o) && o.geometry) {\n const geom = o.geometry\n if (!geom.boundingBox) geom.computeBoundingBox()\n if (geom.boundingBox && !geom.boundingBox.isEmpty()) {\n const box = geom.boundingBox.clone().applyMatrix4(new THREE.Matrix4().multiplyMatrices(inv, o.matrixWorld))\n const mats = Array.isArray(o.material) ? o.material : [o.material]\n const matName = mats[0]?.name ?? ''\n const ud = o.userData ?? {}\n const hint = partHintOf(ud.destructionPart)\n const material =\n options.materialOf?.(o) ??\n (typeof ud.destructionMaterial === 'string' ? (ud.destructionMaterial as string) : undefined) ??\n hint?.material ??\n guessMaterial(`${o.name} ${matName} ${typeof ud.material === 'string' ? ud.material : ''}`) ??\n fallback\n const explicit = hint?.id ?? null\n const hollow = ud.hollow === true || /hollow/i.test(o.name)\n const fill = options.fillOf?.(o, material)\n const tags = Array.isArray(ud.destructionTags) ? (ud.destructionTags as string[]) : undefined\n const existing = explicit ? byKey.get(explicit) : undefined\n if (existing) {\n const before = sizeOf(existing.box)\n const bv = before[0] * before[1] * before[2]\n const ns = sizeOf(box)\n existing.box.union(box)\n existing.meshes.push(o)\n if (ns[0] * ns[1] * ns[2] > bv) existing.material = material\n existing.hollow = existing.hollow || hollow\n if (fill !== undefined) existing.fill = fill\n } else {\n const e: Entry = { key: explicit ?? `m${entries.length}`, meshes: [o], box, material, explicitId: explicit, fill, hollow, tags, fixedId: !!explicit, name: o.name, parent: hint?.parent, jointKind: hint?.kind as JointKind | undefined }\n entries.push(e)\n if (explicit) byKey.set(explicit, e)\n }\n }\n }\n for (const c of o.children) visit(c)\n }\n visit(root)\n\n // 2. merge tiny meshes into the touching part they overlap most\n const eps = 0.005\n const overlapVolume = (a: THREE_NS.Box3, b: THREE_NS.Box3): number => {\n const x = Math.min(a.max.x, b.max.x) - Math.max(a.min.x, b.min.x) + eps\n const y = Math.min(a.max.y, b.max.y) - Math.max(a.min.y, b.min.y) + eps\n const z = Math.min(a.max.z, b.max.z) - Math.max(a.min.z, b.min.z) + eps\n return x > 0 && y > 0 && z > 0 ? x * y * z : 0\n }\n const largest = (e: Entry) => Math.max(...sizeOf(e.box))\n const kept: Entry[] = []\n const tiny: Entry[] = []\n for (const e of entries) (!e.fixedId && largest(e) < minSize ? tiny : kept).push(e)\n for (const t of tiny) {\n let best: Entry | null = null\n let bestV = 0\n for (const k of kept) {\n const v = overlapVolume(t.box, k.box)\n if (v > bestV) {\n bestV = v\n best = k\n }\n }\n if (!best) {\n // not touching anything: join the nearest part within a small distance\n let bd = 0.05\n for (const k of kept) {\n const d = k.box.distanceToPoint(t.box.getCenter(new THREE.Vector3()))\n if (d < bd) {\n bd = d\n best = k\n }\n }\n }\n if (best) {\n best.meshes.push(...t.meshes) // the box stays: a tiny mesh sits inside or on the surface of its part\n } else {\n kept.push(t) // isolated crumb: keep it as its own part\n }\n }\n\n // 3. ids, parts\n const used = new Set<string>()\n const parts: PartSpec[] = []\n const meshesOf: Record<string, Object3D[]> = {}\n let counter = 0\n for (const e of kept) {\n counter++\n let base = (e.explicitId ? sanitizeId(e.explicitId) : '') || sanitizeId(e.name) || `part_${counter}`\n let id = base\n for (let n = 2; used.has(id); n++) id = `${base}_${n}`\n used.add(id)\n base = id\n const size = sizeOf(e.box)\n const center = e.box.getCenter(new THREE.Vector3())\n const part: PartSpec = {\n id,\n material: e.material,\n shape: { kind: 'box', size: [Math.max(size[0], 0.002), Math.max(size[1], 0.002), Math.max(size[2], 0.002)] },\n position: [center.x, center.y, center.z],\n }\n const minSide = Math.min(...size)\n if (e.fill !== undefined) part.fill = e.fill\n else if (e.hollow) part.fill = 0.05\n if (minSide < 0.01 && METAL.test(e.material)) {\n part.fill = e.fill ?? 1\n part.thickness = Math.max(minSide, 0.0005)\n }\n if (e.tags) part.tags = [...e.tags]\n const ao = options.anchorBelow ?? -1\n if (ao >= 0 && e.box.min.y <= ao) part.anchored = true\n parts.push(part)\n meshesOf[id] = e.meshes\n }\n\n const spec: SpecFromObjectResult['spec'] = { parts }\n if (options.name !== undefined) spec.name = options.name\n else if (root.name) spec.name = root.name\n if (options.tags) spec.tags = [...options.tags]\n spec.joints = options.joints === false ? [] : inferJoints(parts)\n // explicit attachments from `destructionPart: { parent, kind }` win over the inferred joint between the same two parts\n if (options.joints !== false) {\n const finalId = new Map<Entry, string>()\n kept.forEach((e, i) => finalId.set(e, parts[i].id))\n const idOf = new Map<string, string>()\n for (const e of kept) if (e.explicitId) idOf.set(e.explicitId, finalId.get(e)!)\n const joints: JointSpec[] = spec.joints\n for (const e of kept) {\n if (!e.parent) continue\n const id = finalId.get(e)!\n const parent = idOf.get(e.parent) ?? (used.has(sanitizeId(e.parent)) ? sanitizeId(e.parent) : null)\n if (!parent || parent === id) continue\n const at = joints.findIndex((j) => (j.a === parent && j.b === id) || (j.a === id && j.b === parent))\n const kind = e.jointKind ?? (at >= 0 ? joints[at].kind : 'resting')\n const joint: JointSpec = { id: `${parent}~${id}`, a: parent, b: id, kind }\n if (at >= 0) joints[at] = joint\n else joints.push(joint)\n }\n }\n return { spec, parts: meshesOf }\n}\n","import { mixHex } from './math'\n\n/** Blackbody-ish stops: [temperature °C, colour]. Luminance grows with the temperature. */\nconst STOPS: Array<[number, number]> = [\n [450, 0x000000],\n [500, 0x2a0400],\n [650, 0x8a1500],\n [800, 0xd84a08],\n [950, 0xff8c14],\n [1100, 0xffc23a],\n [1250, 0xffe8a0],\n [1400, 0xffffff],\n]\n\n/**\n * Glow colour of a hot surface as `0xRRGGBB`: black (no glow) below about 450 °C, dark red around 500, orange around 800, yellow around 1100 and\n * white from 1400 °C. Brightness never decreases as the temperature rises. Use it as an emissive colour.\n */\nexport function heatColor(temperatureC: number): number {\n if (!(temperatureC > STOPS[0][0])) return 0\n const last = STOPS[STOPS.length - 1]\n if (temperatureC >= last[0]) return last[1]\n for (let i = 1; i < STOPS.length; i++) {\n const [t1, c1] = STOPS[i]\n if (temperatureC <= t1) {\n const [t0, c0] = STOPS[i - 1]\n return mixHex(c0, c1, (temperatureC - t0) / (t1 - t0))\n }\n }\n return last[1]\n}\n","import type * as THREE_NS from 'three'\nimport type { Dent } from '@adatechnology/game-destruction'\nimport { heatColor } from './heat'\nimport type { Decal, HostVis, MeshVis, PartVis } from './internal'\nimport { clamp, createRng, lerp } from './math'\n\ntype Mesh = THREE_NS.Mesh\n\nconst POS_ATTR = 'position'\n\nfunction positionAttr(geo: THREE_NS.BufferGeometry): THREE_NS.BufferAttribute | null {\n const a = geo.getAttribute(POS_ATTR) as THREE_NS.BufferAttribute | THREE_NS.InterleavedBufferAttribute | undefined\n return a && !(a as THREE_NS.InterleavedBufferAttribute).isInterleavedBufferAttribute ? (a as THREE_NS.BufferAttribute) : null\n}\n\nfunction materialsOf(m: THREE_NS.Material | THREE_NS.Material[]): THREE_NS.Material[] {\n return Array.isArray(m) ? m : [m]\n}\n\nfunction colorOf(m: THREE_NS.Material): THREE_NS.Color | null {\n const c = (m as THREE_NS.MeshStandardMaterial).color\n return c && (c as THREE_NS.Color).isColor ? c : null\n}\n\n/** Shared resources for decals (one geometry, a few materials) created lazily and disposed together. */\nexport class DecalKit {\n private disc: THREE_NS.CircleGeometry | null = null\n private discMat: THREE_NS.MeshBasicMaterial | null = null\n private lineMats = new Map<number, THREE_NS.LineBasicMaterial>()\n private patchMats = new Map<number, THREE_NS.MeshBasicMaterial>()\n constructor(private readonly THREE: typeof THREE_NS) {}\n\n discGeometry(): THREE_NS.CircleGeometry {\n return (this.disc ??= new this.THREE.CircleGeometry(1, 14))\n }\n\n discMaterial(): THREE_NS.MeshBasicMaterial {\n return (this.discMat ??= new this.THREE.MeshBasicMaterial({\n color: 0x050505,\n transparent: true,\n opacity: 0.92,\n depthWrite: false,\n polygonOffset: true,\n polygonOffsetFactor: -4,\n polygonOffsetUnits: -4,\n }))\n }\n\n /** Opaque patch in the colour of what shows under a spalled coat. */\n patchMaterial(color: number): THREE_NS.MeshBasicMaterial {\n let m = this.patchMats.get(color)\n if (!m) {\n m = new this.THREE.MeshBasicMaterial({ color, depthWrite: false, polygonOffset: true, polygonOffsetFactor: -3, polygonOffsetUnits: -3 })\n this.patchMats.set(color, m)\n }\n return m\n }\n\n lineMaterial(color: number): THREE_NS.LineBasicMaterial {\n let m = this.lineMats.get(color)\n if (!m) {\n m = new this.THREE.LineBasicMaterial({ color, transparent: true, opacity: 0.9, depthWrite: false })\n this.lineMats.set(color, m)\n }\n return m\n }\n\n dispose(): void {\n this.disc?.dispose()\n this.discMat?.dispose()\n for (const m of this.lineMats.values()) m.dispose()\n for (const m of this.patchMats.values()) m.dispose()\n this.patchMats.clear()\n this.disc = null\n this.discMat = null\n this.lineMats.clear()\n }\n}\n\nconst tmpV = (THREE: typeof THREE_NS) => new THREE.Vector3()\n\n/** Mesh-level visual changes: copy-on-write geometry and materials, dents, decals, char, glow, melt, hiding, restoring. */\nexport class Visuals {\n readonly kit: DecalKit\n private readonly a: THREE_NS.Vector3\n private readonly b: THREE_NS.Vector3\n private readonly m4: THREE_NS.Matrix4\n private readonly box: THREE_NS.Box3\n private readonly t: THREE_NS.Vector3\n private readonly lbox: THREE_NS.Box3\n /** Total live decals across meshes (kept in sync by add/remove). */\n decalCount = 0\n\n constructor(\n private readonly THREE: typeof THREE_NS,\n private readonly lowQuality: boolean,\n private readonly decalCap: number,\n ) {\n this.kit = new DecalKit(THREE)\n this.a = tmpV(THREE)\n this.b = tmpV(THREE)\n this.m4 = new THREE.Matrix4()\n this.box = new THREE.Box3()\n this.t = tmpV(THREE)\n this.lbox = new THREE.Box3()\n }\n\n /**\n * @param range vertex range `[v0, v1)` of a merged mesh this part is drawn by (see `MeshRange`); `hosts` caches the shared state per mesh for one `bind`.\n */\n makeMeshVis(mesh: Mesh, home: string, range?: [number, number], hosts?: Map<Mesh, HostVis>): MeshVis {\n let host: HostVis | null = null\n if (range) {\n host = hosts?.get(mesh) ?? null\n if (!host) {\n host = { mesh, origGeometry: mesh.geometry, origMaterial: mesh.material, ownGeo: false, work: null, ownMat: false, sharers: [] }\n hosts?.set(mesh, host)\n }\n }\n const mv: MeshVis = {\n mesh,\n home,\n origGeometry: mesh.geometry,\n origMaterial: mesh.material,\n origVisible: mesh.visible,\n origParent: mesh.parent,\n origPosition: mesh.position.clone(),\n origQuaternion: mesh.quaternion.clone(),\n origScale: mesh.scale.clone(),\n ownMat: false,\n ownGeo: false,\n work: null,\n baseColors: null,\n decals: [],\n meltApplied: 0,\n bottomY: 0,\n moved: false,\n host,\n range: range ? [range[0], range[1]] : null,\n merged: host && range ? { host, range: [range[0], range[1]] } : null,\n loose: false,\n collapsed: false,\n hidden: false,\n touched: false,\n charK: 0,\n charHex: 0,\n glowHex: 0,\n glowK: 0,\n }\n host?.sharers.push(mv)\n return mv\n }\n\n vertexCount(mv: MeshVis): number {\n if (mv.range) return mv.range[1] - mv.range[0]\n return mv.mesh.geometry.getAttribute(POS_ATTR)?.count ?? 0\n }\n\n /** Is the part drawn? (A ranged part shares its mesh, so `mesh.visible` says nothing about it.) */\n shown(mv: MeshVis): boolean {\n return mv.host ? !mv.hidden : mv.mesh.visible\n }\n\n /** Box of the part's vertices in the space of its mesh (the whole geometry for a plain mesh). */\n localBox(mv: MeshVis, out: THREE_NS.Box3): THREE_NS.Box3 {\n out.makeEmpty()\n if (!mv.host || !mv.range) {\n const geo = mv.mesh.geometry\n if (!geo.boundingBox) geo.computeBoundingBox()\n return geo.boundingBox ? out.copy(geo.boundingBox) : out\n }\n const arr = mv.host.work ?? (positionAttr(mv.mesh.geometry)?.array as unknown as ArrayLike<number> | undefined)\n if (!arr) return out\n for (let i = mv.range[0] * 3; i < mv.range[1] * 3; i += 3) out.expandByPoint(this.t.set(arr[i], arr[i + 1], arr[i + 2]))\n return out\n }\n\n /** Box of the part in world space. */\n worldBox(mv: MeshVis, out: THREE_NS.Box3): THREE_NS.Box3 {\n if (!mv.host) return out.setFromObject(mv.mesh)\n this.localBox(mv, out)\n mv.mesh.updateWorldMatrix(true, false)\n return out.isEmpty() ? out : out.applyMatrix4(mv.mesh.matrixWorld)\n }\n\n private readonly tints = new WeakMap<MeshVis, THREE_NS.Color | null>()\n\n /**\n * Average vertex colour of a mesh whose material uses `vertexColors` (computed once per mesh, from its original geometry), else null. Fragments\n * and debris of such a mesh multiply it by `material.color`; without it they come out white.\n */\n vertexTint(mv: MeshVis): THREE_NS.Color | null {\n if (this.tints.has(mv)) return this.tints.get(mv) ?? null\n let tint: THREE_NS.Color | null = null\n const used = materialsOf(mv.origMaterial).some((m) => (m as THREE_NS.MeshStandardMaterial).vertexColors === true)\n const attr = used ? (mv.origGeometry.getAttribute('color') as THREE_NS.BufferAttribute | undefined) : undefined\n if (attr && attr.count > 0 && attr.itemSize >= 3) {\n // a ranged part averages its own vertices only\n const from = mv.merged ? mv.merged.range[0] : 0\n const to = mv.merged ? Math.min(attr.count, mv.merged.range[1]) : attr.count\n let r = 0, g = 0, b = 0\n for (let i = from; i < to; i++) {\n r += attr.getX(i)\n g += attr.getY(i)\n b += attr.getZ(i)\n }\n const n = Math.max(1, to - from)\n tint = new this.THREE.Color(r / n, g / n, b / n)\n }\n this.tints.set(mv, tint)\n return tint\n }\n\n /** Colour of the original material (before any char) times the vertex tint, `0xRRGGBB`, or -1. */\n baseColorHex(mv: MeshVis): number {\n const c = mv.baseColors?.[0] ?? colorOf(materialsOf(mv.origMaterial)[0])\n if (!c) return -1\n const tint = this.vertexTint(mv)\n return tint ? c.clone().multiply(tint).getHex() : c.getHex()\n }\n\n /** Is the original material transparent? */\n isTransparent(mv: MeshVis): boolean {\n return materialsOf(mv.origMaterial).some((m) => m.transparent && m.opacity < 1)\n }\n\n ownMaterial(mv: MeshVis): void {\n if (mv.ownMat) return\n mv.ownMat = true\n const src = materialsOf(mv.origMaterial)\n const cloned = src.map((m) => m.clone())\n mv.baseColors = src.map((m) => colorOf(m)?.clone() ?? new this.THREE.Color(0x888888))\n mv.mesh.material = Array.isArray(mv.origMaterial) ? cloned : cloned[0]\n }\n\n /** Clone the geometry (vertex edits); false when its positions cannot be edited. */\n ownGeometry(mv: MeshVis): boolean {\n if (mv.host) return this.ownHostGeometry(mv.host)\n if (mv.ownGeo) return true\n const pos = positionAttr(mv.mesh.geometry)\n if (!pos) return false\n mv.ownGeo = true\n const g = mv.mesh.geometry.clone()\n mv.mesh.geometry = g\n mv.work = new Float32Array((positionAttr(g) as THREE_NS.BufferAttribute).array as ArrayLike<number>)\n return true\n }\n\n private ownHostGeometry(host: HostVis): boolean {\n if (host.ownGeo) return true\n const pos = positionAttr(host.mesh.geometry)\n if (!pos) return false\n host.ownGeo = true\n const g = host.mesh.geometry.clone()\n host.mesh.geometry = g\n host.work = new Float32Array((positionAttr(g) as THREE_NS.BufferAttribute).array as ArrayLike<number>)\n return true\n }\n\n // ------------------------------------------------------------------------------------------------------------------------------------\n // Dents\n // ------------------------------------------------------------------------------------------------------------------------------------\n\n /**\n * Push the vertices of the mesh within `dent.radius` of `worldPoint` inwards along `worldNormal` by `depth * (1 - (d/r)^2)^2`.\n * Returns false when nothing was changed (too far, low quality on a heavy mesh, or no editable positions).\n */\n applyDent(mv: MeshVis, worldPoint: THREE_NS.Vector3, worldNormal: THREE_NS.Vector3, dent: Dent): boolean {\n const mesh = mv.mesh\n if (this.lowQuality && this.vertexCount(mv) > 20000) return false\n mesh.updateWorldMatrix(true, false)\n const s = mesh.matrixWorld.getMaxScaleOnAxis() || 1\n const lp = mesh.worldToLocal(this.a.copy(worldPoint))\n const ln = this.b.copy(worldNormal).transformDirection(this.m4.copy(mesh.matrixWorld).invert()).normalize()\n const r = dent.radius / s\n const depth = dent.depth / s\n // quick reject: the point must be near the mesh bounds\n const geo = mesh.geometry\n let bb: THREE_NS.Box3 | null\n if (mv.host) bb = this.localBox(mv, this.lbox)\n else {\n if (!geo.boundingBox) geo.computeBoundingBox()\n bb = geo.boundingBox\n }\n if (bb && !bb.isEmpty() && bb.distanceToPoint(lp) > r) return false\n if (!this.ownGeometry(mv)) return false\n const work = (mv.host ? mv.host.work : mv.work) as Float32Array\n const pos = positionAttr(mesh.geometry) as THREE_NS.BufferAttribute\n const r2 = r * r\n let touched = false\n const px = lp.x\n const py = lp.y\n const pz = lp.z\n const nx = ln.x\n const ny = ln.y\n const nz = ln.z\n const from = mv.range ? mv.range[0] * 3 : 0\n const to = mv.range ? mv.range[1] * 3 : work.length\n for (let i = from; i < to; i += 3) {\n const dx = work[i] - px\n const dy = work[i + 1] - py\n const dz = work[i + 2] - pz\n const d2 = dx * dx + dy * dy + dz * dz\n if (d2 >= r2) continue\n const f = 1 - d2 / r2\n const k = depth * f * f\n work[i] -= nx * k\n work[i + 1] -= ny * k\n work[i + 2] -= nz * k\n touched = true\n }\n if (!touched) return false\n mv.touched = true\n this.writePositions(mv, pos)\n return true\n }\n\n /** Copy `work` (dents) into the geometry, adding the melt sag when the mesh is melting. */\n private writePositions(mv: MeshVis, pos: THREE_NS.BufferAttribute): void {\n if (mv.host) {\n this.rebuildHost(mv.host)\n return\n }\n const work = mv.work as Float32Array\n const arr = pos.array as unknown as Float32Array\n const melt = mv.meltApplied\n if (melt <= 0 || (this.lowQuality && work.length / 3 > 20000)) {\n arr.set(work)\n } else {\n const geo = mv.mesh.geometry\n if (!geo.boundingBox) geo.computeBoundingBox()\n const bb = geo.boundingBox as THREE_NS.Box3\n const h = Math.max(1e-4, bb.max.y - bb.min.y)\n const minY = bb.min.y\n for (let i = 0, v = 0; i < work.length; i += 3, v++) {\n const n = ((Math.imul(v + 1, 2654435761) >>> 0) % 1000) / 1000\n const n2 = ((Math.imul(v + 7, 40503) >>> 0) % 1000) / 1000\n const lowness = 1 - (work[i + 1] - minY) / h // 1 at the bottom, 0 at the top\n const spread = 1 + 0.12 * melt * (0.4 + n2) * (0.3 + lowness)\n arr[i] = work[i] * spread\n arr[i + 2] = work[i + 2] * spread\n arr[i + 1] = Math.max(minY, work[i + 1] - n * melt * h * 0.08)\n }\n }\n pos.needsUpdate = true\n const geo = mv.mesh.geometry\n geo.computeVertexNormals()\n geo.boundingBox = null\n geo.boundingSphere = null\n }\n\n // ------------------------------------------------------------------------------------------------------------------------------------\n // Decals\n // ------------------------------------------------------------------------------------------------------------------------------------\n\n /** Decal pose in mesh space: position and a rotation taking +Z to the surface normal. */\n private decalPose(mesh: Mesh, worldPoint: THREE_NS.Vector3, worldNormal: THREE_NS.Vector3, lift: number): { s: number; pos: THREE_NS.Vector3; quat: THREE_NS.Quaternion } {\n mesh.updateWorldMatrix(true, false)\n const s = mesh.matrixWorld.getMaxScaleOnAxis() || 1\n const ln = worldNormal.clone().transformDirection(this.m4.copy(mesh.matrixWorld).invert()).normalize()\n const pos = mesh.worldToLocal(worldPoint.clone()).addScaledVector(ln, lift / s)\n const quat = new this.THREE.Quaternion().setFromUnitVectors(new this.THREE.Vector3(0, 0, 1), ln)\n return { s, pos, quat }\n }\n\n private addDecal(mv: MeshVis, decal: Decal): void {\n mv.mesh.add(decal.obj)\n mv.decals.push(decal)\n this.decalCount++\n while (mv.decals.length > 48 || this.decalCount > this.decalCap) {\n const old = mv.decals.shift()\n if (!old) break\n this.disposeDecal(old)\n }\n }\n\n private disposeDecal(d: Decal): void {\n d.obj.parent?.remove(d.obj)\n d.geometry?.dispose()\n this.decalCount = Math.max(0, this.decalCount - 1)\n }\n\n addHole(mv: MeshVis, worldPoint: THREE_NS.Vector3, worldNormal: THREE_NS.Vector3, diameter: number): void {\n const { s, pos, quat } = this.decalPose(mv.mesh, worldPoint, worldNormal, 0.0025)\n const disc = new this.THREE.Mesh(this.kit.discGeometry(), this.kit.discMaterial())\n disc.position.copy(pos)\n disc.quaternion.copy(quat)\n disc.scale.setScalar((Math.max(0.004, diameter) * 0.5 * 1.25) / s)\n disc.renderOrder = 2\n this.addDecal(mv, { obj: disc })\n }\n\n /** A patch where a surface coat came off, in the colour of the core under it; drawn under the bullet holes. */\n addSpall(mv: MeshVis, worldPoint: THREE_NS.Vector3, worldNormal: THREE_NS.Vector3, radius: number, color: number): void {\n const { s, pos, quat } = this.decalPose(mv.mesh, worldPoint, worldNormal, 0.0015)\n const disc = new this.THREE.Mesh(this.kit.discGeometry(), this.kit.patchMaterial(color))\n disc.position.copy(pos)\n disc.quaternion.copy(quat)\n disc.scale.setScalar(Math.max(0.01, radius) / s)\n disc.renderOrder = 1\n this.addDecal(mv, { obj: disc })\n }\n\n /** A star of jagged lines: `rays` rays up to `length` metres, seeded so the same event always draws the same star. */\n addCrack(mv: MeshVis, worldPoint: THREE_NS.Vector3, worldNormal: THREE_NS.Vector3, rays: number, length: number, color: number, seed: number): void {\n const { s, pos, quat } = this.decalPose(mv.mesh, worldPoint, worldNormal, 0.002)\n const rnd = createRng(seed)\n const verts: number[] = []\n const n = clamp(Math.round(rays), 3, 16)\n const L = Math.max(0.02, length) / s\n for (let r = 0; r < n; r++) {\n let ang = (r / n) * Math.PI * 2 + (rnd() - 0.5) * 0.5\n let x = 0\n let y = 0\n const segs = 3\n const total = L * (0.6 + 0.4 * rnd())\n for (let k = 0; k < segs; k++) {\n const step = (total / segs) * (0.7 + 0.6 * rnd())\n ang += (rnd() - 0.5) * 0.45\n const nx = x + Math.cos(ang) * step\n const ny = y + Math.sin(ang) * step\n verts.push(x, y, 0, nx, ny, 0)\n if (k > 0 && rnd() < 0.5) {\n const ba = ang + (rnd() < 0.5 ? -1 : 1) * (0.5 + rnd() * 0.5)\n const bl = step * (0.6 + 0.6 * rnd())\n verts.push(nx, ny, 0, nx + Math.cos(ba) * bl, ny + Math.sin(ba) * bl, 0)\n }\n x = nx\n y = ny\n }\n }\n // a faint ring around the impact\n const ringR = L * 0.12\n for (let k = 0; k < 8; k++) {\n const a0 = (k / 8) * Math.PI * 2\n const a1 = ((k + 1) / 8) * Math.PI * 2\n if (rnd() < 0.75) verts.push(Math.cos(a0) * ringR, Math.sin(a0) * ringR, 0, Math.cos(a1) * ringR, Math.sin(a1) * ringR, 0)\n }\n const geo = new this.THREE.BufferGeometry()\n geo.setAttribute('position', new this.THREE.Float32BufferAttribute(verts, 3))\n const lines = new this.THREE.LineSegments(geo, this.kit.lineMaterial(color))\n lines.position.copy(pos)\n lines.quaternion.copy(quat)\n lines.renderOrder = 2\n this.addDecal(mv, { obj: lines, geometry: geo })\n }\n\n // ------------------------------------------------------------------------------------------------------------------------------------\n // Char and heat glow\n // ------------------------------------------------------------------------------------------------------------------------------------\n\n setChar(pv: PartVis, char: number, charColor: number): void {\n if (Math.abs(char - pv.charApplied) < 0.004) return\n pv.charApplied = char\n const target = new this.THREE.Color(charColor)\n for (const mv of pv.meshes) {\n if (mv.host) {\n mv.charK = clamp(char, 0, 1)\n mv.charHex = charColor\n this.paint(mv)\n continue\n }\n this.ownMaterial(mv)\n const mats = materialsOf(mv.mesh.material)\n mats.forEach((m, i) => {\n const c = colorOf(m)\n const base = mv.baseColors?.[i]\n if (c && base) c.copy(base).lerp(target, clamp(char, 0, 1))\n })\n }\n }\n\n /** Emissive glow of a hot part. `temperature` below the glow threshold switches it off. */\n setGlow(pv: PartVis, temperatureC: number): void {\n const hex = heatColor(temperatureC)\n const key = hex === 0 ? 0 : Math.round(temperatureC / 5) * 5\n if (key === pv.glowApplied) return\n pv.glowApplied = key\n const intensity = hex === 0 ? 0 : clamp(0.35 + (temperatureC - 450) / 900, 0, 1.4)\n for (const mv of pv.meshes) {\n if (mv.host) {\n mv.glowHex = hex\n mv.glowK = intensity\n this.paint(mv)\n continue\n }\n this.ownMaterial(mv)\n for (const m of materialsOf(mv.mesh.material)) {\n const e = (m as THREE_NS.MeshStandardMaterial).emissive\n if (e && (e as THREE_NS.Color).isColor) {\n e.setHex(hex)\n ;(m as THREE_NS.MeshStandardMaterial).emissiveIntensity = intensity\n }\n }\n }\n }\n\n // ------------------------------------------------------------------------------------------------------------------------------------\n // Melt\n // ------------------------------------------------------------------------------------------------------------------------------------\n\n /** Sag the part: shorter in Y (down to 25 %), a bit wider, vertices pushed down with noise, the bottom stays put. */\n setMelt(pv: PartVis, melt: number): void {\n const m = clamp(melt, 0, 1)\n if (Math.abs(m - pv.meltApplied) < 0.01) return\n pv.meltApplied = m\n for (const mv of pv.meshes) {\n const mesh = mv.mesh\n if (mv.host) {\n // a range of a shared mesh cannot scale the mesh: squash and widen its own vertices\n mv.meltApplied = m\n mv.touched = true\n if (!(this.lowQuality && this.vertexCount(mv) > 20000) && this.ownGeometry(mv)) this.rebuildHost(mv.host)\n continue\n }\n if (mv.meltApplied === 0 && m > 0) {\n mesh.updateWorldMatrix(true, false)\n mv.bottomY = this.box.setFromObject(mesh).min.y\n }\n mv.meltApplied = m\n mv.moved = true\n const sy = lerp(1, 0.25, m)\n const sxz = 1 + 0.18 * m\n mesh.scale.set(mv.origScale.x * sxz, mv.origScale.y * sy, mv.origScale.z * sxz)\n mesh.position.copy(mv.origPosition)\n mesh.updateWorldMatrix(true, false)\n const bottom = this.box.setFromObject(mesh).min.y\n const dy = mv.bottomY - bottom\n if (Math.abs(dy) > 1e-6 && mesh.parent) {\n mesh.parent.updateWorldMatrix(true, false)\n const inv = this.m4.copy(mesh.parent.matrixWorld).invert()\n this.a.set(0, dy, 0).applyMatrix4(inv)\n this.b.set(0, 0, 0).applyMatrix4(inv)\n mesh.position.copy(mv.origPosition).add(this.a.sub(this.b))\n }\n if (!(this.lowQuality && this.vertexCount(mv) > 20000) && this.ownGeometry(mv)) {\n this.writePositions(mv, positionAttr(mesh.geometry) as THREE_NS.BufferAttribute)\n }\n }\n }\n\n // ------------------------------------------------------------------------------------------------------------------------------------\n // Visibility and restore\n // ------------------------------------------------------------------------------------------------------------------------------------\n\n hide(pv: PartVis, reason: PartVis['gone']): void {\n pv.gone = reason\n for (const mv of pv.meshes) {\n if (!mv.host || mv.loose) {\n mv.mesh.visible = false\n mv.hidden = true\n continue\n }\n // a range of a shared mesh: collapse its vertices to a point and hide what was drawn on it\n mv.hidden = true\n for (const d of mv.decals) d.obj.visible = false\n this.collapse(mv)\n }\n }\n\n // ------------------------------------------------------------------------------------------------------------------------------------\n // Parts drawn by a vertex range of a merged mesh\n // ------------------------------------------------------------------------------------------------------------------------------------\n\n /** Collapse the part's vertices in the merged mesh to one point (its triangles become degenerate and vanish). */\n private collapse(mv: MeshVis): void {\n if (!mv.merged || !this.ownHostGeometry(mv.merged.host)) return\n mv.collapsed = true\n mv.touched = true\n this.rebuildHost(mv.merged.host)\n }\n\n /** Write `work` into the geometry again with the melt of ranged parts and the collapsed ranges applied. */\n private rebuildHost(host: HostVis): void {\n const geo = host.mesh.geometry\n const pos = positionAttr(geo)\n const work = host.work\n if (!pos || !work) return\n const arr = pos.array as unknown as Float32Array\n arr.set(work)\n for (const s of host.sharers) {\n if (s.merged && s.merged.host === host) {\n if (s.collapsed) this.collapseRange(arr, s.merged.range)\n else if (s.meltApplied > 0 && !s.loose) this.meltRange(arr, work, s.merged.range, s.meltApplied)\n } else if (s.host === host && s.range && s.meltApplied > 0) this.meltRange(arr, work, s.range, s.meltApplied)\n }\n pos.needsUpdate = true\n geo.computeVertexNormals()\n geo.boundingBox = null\n geo.boundingSphere = null\n }\n\n private collapseRange(arr: Float32Array, range: [number, number]): void {\n const x = arr[range[0] * 3]\n const y = arr[range[0] * 3 + 1]\n const z = arr[range[0] * 3 + 2]\n for (let i = range[0] * 3; i < range[1] * 3; i += 3) {\n arr[i] = x\n arr[i + 1] = y\n arr[i + 2] = z\n }\n }\n\n /** Melt of one range: shorter in Y (down to 25 %) with the bottom fixed, a bit wider around its centre, with the same noise as a whole mesh. */\n private meltRange(arr: Float32Array, work: Float32Array, range: [number, number], melt: number): void {\n let minY = Infinity\n let maxY = -Infinity\n let cx = 0\n let cz = 0\n const n = Math.max(1, range[1] - range[0])\n for (let i = range[0] * 3; i < range[1] * 3; i += 3) {\n minY = Math.min(minY, work[i + 1])\n maxY = Math.max(maxY, work[i + 1])\n cx += work[i]\n cz += work[i + 2]\n }\n cx /= n\n cz /= n\n const h = Math.max(1e-4, maxY - minY)\n const sy = lerp(1, 0.25, melt)\n const sxz = 1 + 0.18 * melt\n for (let i = range[0] * 3, v = 0; i < range[1] * 3; i += 3, v++) {\n const noise = ((Math.imul(v + 1, 2654435761) >>> 0) % 1000) / 1000\n arr[i] = cx + (work[i] - cx) * sxz\n arr[i + 2] = cz + (work[i + 2] - cz) * sxz\n arr[i + 1] = Math.max(minY, minY + (work[i + 1] - minY) * sy - noise * melt * h * 0.04)\n }\n }\n\n /** The colour attribute of the host's current geometry (created white when there is none) with vertex colours switched on in its material. */\n private ensureColors(host: HostVis): THREE_NS.BufferAttribute | null {\n if (!this.ownHostGeometry(host)) return null\n const geo = host.mesh.geometry\n let attr = geo.getAttribute('color') as THREE_NS.BufferAttribute | undefined\n if (!attr) {\n const n = geo.getAttribute(POS_ATTR).count\n attr = new this.THREE.BufferAttribute(new Float32Array(n * 3).fill(1), 3)\n geo.setAttribute('color', attr)\n }\n if (!host.ownMat) {\n host.ownMat = true\n const src = materialsOf(host.mesh.material)\n const cloned = src.map((m) => m.clone())\n host.mesh.material = Array.isArray(host.mesh.material) ? cloned : cloned[0]\n }\n for (const m of materialsOf(host.mesh.material)) {\n if ((m as THREE_NS.MeshStandardMaterial).vertexColors !== true) {\n ;(m as THREE_NS.MeshStandardMaterial).vertexColors = true\n m.needsUpdate = true\n }\n }\n return attr\n }\n\n /** Colour of the part's vertices from the original colours, its char and its heat. Only the part's range is written. */\n private paint(mv: MeshVis): void {\n if (!mv.host || !mv.range || !mv.merged) return\n const idle = mv.charK <= 0 && mv.glowK <= 0\n if (idle && !mv.touched) return\n const attr = this.ensureColors(mv.host)\n if (!attr) return\n const size = attr.itemSize\n const arr = attr.array as unknown as Float32Array\n const orig = mv.merged.host.origGeometry.getAttribute('color') as THREE_NS.BufferAttribute | undefined\n const oSize = orig?.itemSize ?? 3\n const oArr = orig?.array as unknown as ArrayLike<number> | undefined\n const shift = mv.merged.range[0] - mv.range[0]\n const ch = new this.THREE.Color(mv.charHex)\n const heat = new this.THREE.Color(mv.glowHex)\n const k = clamp(mv.charK, 0, 1)\n const gk = clamp(mv.glowK, 0, 1)\n const boost = 1 + Math.max(0, mv.glowK - 1)\n for (let i = mv.range[0]; i < mv.range[1]; i++) {\n const o = (i + shift) * oSize\n let r = oArr ? oArr[o] : 1\n let g = oArr ? oArr[o + 1] : 1\n let b = oArr ? oArr[o + 2] : 1\n if (k > 0) {\n r = lerp(r, ch.r, k)\n g = lerp(g, ch.g, k)\n b = lerp(b, ch.b, k)\n }\n if (gk > 0) {\n r = lerp(r, heat.r * boost, gk)\n g = lerp(g, heat.g * boost, gk)\n b = lerp(b, heat.b * boost, gk)\n }\n arr[i * size] = r\n arr[i * size + 1] = g\n arr[i * size + 2] = b\n }\n mv.touched = true\n attr.needsUpdate = true\n }\n\n /**\n * Move a ranged part to a mesh of its own: the faces whose vertices all lie in its range are copied (with the current dents and colours) into a\n * new mesh that takes the merged mesh's transform and material, then attached to `group`; the range is collapsed in the merged mesh. Decals\n * go with it.\n */\n extract(mv: MeshVis, group: THREE_NS.Object3D): void {\n if (!mv.host || !mv.range || !mv.merged || mv.loose) return\n const host = mv.host\n const [v0, v1] = mv.range\n const src = host.mesh.geometry\n const geo = new this.THREE.BufferGeometry()\n const count = v1 - v0\n for (const name of Object.keys(src.attributes)) {\n const a = src.getAttribute(name) as THREE_NS.BufferAttribute\n if ((a as unknown as { isInterleavedBufferAttribute?: boolean }).isInterleavedBufferAttribute) continue\n const size = a.itemSize\n const from = (name === POS_ATTR ? host.work : null) ?? (a.array as unknown as Float32Array)\n const slice = (from as unknown as { slice(a: number, b: number): ArrayLike<number> }).slice(v0 * size, v1 * size)\n const Ctor = (a.array as unknown as { constructor: new (x: ArrayLike<number>) => ArrayLike<number> }).constructor\n geo.setAttribute(name, new this.THREE.BufferAttribute(new Ctor(slice) as never, size, a.normalized))\n }\n const index = src.getIndex()\n if (index) {\n const out: number[] = []\n for (let i = 0; i + 2 < index.count; i += 3) {\n const a = index.getX(i)\n const b = index.getX(i + 1)\n const c = index.getX(i + 2)\n if (a >= v0 && a < v1 && b >= v0 && b < v1 && c >= v0 && c < v1) out.push(a - v0, b - v0, c - v0)\n }\n geo.setIndex(count > 65535 ? new this.THREE.Uint32BufferAttribute(out, 1) : new this.THREE.Uint16BufferAttribute(out, 1))\n }\n const mats = materialsOf(host.mesh.material).map((m) => m.clone())\n const loose = new this.THREE.Mesh(geo, mats[0])\n loose.name = `${host.mesh.name || 'mesh'}:${v0}-${v1}`\n loose.castShadow = host.mesh.castShadow\n loose.receiveShadow = host.mesh.receiveShadow\n loose.renderOrder = host.mesh.renderOrder\n loose.frustumCulled = host.mesh.frustumCulled\n loose.layers.mask = host.mesh.layers.mask\n loose.position.copy(host.mesh.position)\n loose.quaternion.copy(host.mesh.quaternion)\n loose.scale.copy(host.mesh.scale)\n const parent = host.mesh.parent\n if (parent) parent.add(loose)\n loose.updateMatrixWorld(true)\n // the merged mesh loses the range; the decals and the part's state follow the new mesh\n this.collapse(mv)\n for (const d of mv.decals) {\n d.obj.visible = true\n loose.add(d.obj)\n }\n const lp = positionAttr(geo) as THREE_NS.BufferAttribute\n const lh: HostVis = {\n mesh: loose,\n origGeometry: geo,\n origMaterial: loose.material,\n ownGeo: true,\n work: new Float32Array(lp.array as ArrayLike<number>),\n ownMat: true,\n sharers: [mv],\n }\n mv.host = lh\n mv.range = [0, count]\n mv.mesh = loose\n mv.loose = true\n mv.moved = true\n group.attach(loose)\n for (const m of materialsOf(loose.material)) m.needsUpdate = true\n }\n\n private restoreRanged(mv: MeshVis): void {\n const merged = mv.merged as NonNullable<MeshVis['merged']>\n const host = merged.host\n for (const d of mv.decals) this.disposeDecal(d)\n mv.decals.length = 0\n if (mv.loose) {\n const loose = mv.mesh\n loose.parent?.remove(loose)\n loose.geometry.dispose()\n for (const m of materialsOf(loose.material)) m.dispose()\n mv.host = host\n mv.range = [merged.range[0], merged.range[1]]\n mv.mesh = host.mesh\n mv.loose = false\n mv.moved = false\n }\n mv.collapsed = false\n mv.hidden = false\n mv.meltApplied = 0\n mv.charK = 0\n mv.glowK = 0\n mv.touched = false\n if (!host.ownGeo) return\n // positions and colours of the range go back to the original ones\n const work = host.work as Float32Array\n const op = positionAttr(host.origGeometry)\n const [v0, v1] = merged.range\n if (op) work.set((op.array as unknown as Float32Array).subarray(v0 * 3, v1 * 3), v0 * 3)\n const attr = host.mesh.geometry.getAttribute('color') as THREE_NS.BufferAttribute | undefined\n if (attr) {\n const orig = host.origGeometry.getAttribute('color') as THREE_NS.BufferAttribute | undefined\n const size = attr.itemSize\n const a = attr.array as unknown as Float32Array\n for (let i = v0; i < v1; i++) {\n for (let c = 0; c < 3; c++) a[i * size + c] = orig ? orig.array[i * orig.itemSize + c] : 1\n }\n attr.needsUpdate = true\n }\n if (host.sharers.every((s) => !s.touched)) {\n host.mesh.geometry.dispose()\n host.mesh.geometry = host.origGeometry\n host.work = null\n host.ownGeo = false\n if (host.ownMat) {\n for (const m of materialsOf(host.mesh.material)) m.dispose()\n host.mesh.material = host.origMaterial\n host.ownMat = false\n }\n } else this.rebuildHost(host)\n }\n\n /** Bring one mesh back to how it was when it was bound. */\n restoreMesh(mv: MeshVis): void {\n if (mv.merged) {\n this.restoreRanged(mv)\n return\n }\n const mesh = mv.mesh\n for (const d of mv.decals) this.disposeDecal(d)\n mv.decals.length = 0\n if (mv.ownGeo) {\n mesh.geometry.dispose()\n mesh.geometry = mv.origGeometry\n mv.ownGeo = false\n mv.work = null\n }\n if (mv.ownMat) {\n for (const m of materialsOf(mesh.material)) m.dispose()\n mesh.material = mv.origMaterial\n mv.ownMat = false\n mv.baseColors = null\n }\n mesh.visible = mv.origVisible\n if (mv.origParent && mesh.parent !== mv.origParent) {\n mv.origParent.add(mesh)\n mv.moved = true\n }\n if (mv.moved) {\n mesh.position.copy(mv.origPosition)\n mesh.quaternion.copy(mv.origQuaternion)\n mesh.scale.copy(mv.origScale)\n mv.moved = false\n }\n mv.meltApplied = 0\n }\n\n restorePart(pv: PartVis): void {\n for (const mv of pv.meshes) this.restoreMesh(mv)\n pv.charApplied = 0\n pv.glowApplied = 0\n pv.meltApplied = 0\n pv.gone = 'none'\n }\n\n dispose(): void {\n this.kit.dispose()\n }\n}\n","import type * as THREE_NS from 'three'\nimport type { DebrisKind, DestructionEvent, Fragment, MaterialDef, PartSpec, Vec3 } from '@adatechnology/game-destruction'\nimport { createVfx, effectForEvent, EVENT_EFFECTS_MAP } from '@adatechnology/game-vfx'\nimport type { GameEvent } from '@adatechnology/game-vfx'\nimport type { VfxWorld } from '@adatechnology/game-vfx'\nimport { createVfxRenderer } from '@adatechnology/game-vfx-three'\nimport type { VfxRenderer } from '@adatechnology/game-vfx-three'\nimport { FragmentSystem } from './fragments'\nimport { GroundSpots } from './ground'\nimport type { Bound, HostVis, MeshVis, PartVis, PieceBody } from './internal'\nimport { clamp, createRng, hashNumbers, hashString, mixHex } from './math'\nimport { PieceSystem } from './pieces'\nimport { RebarCages } from './rebar'\nimport { partHintOf } from './spec'\nimport type { DestructionRenderer, BindOptions, ExplodeOptions, Mesh, MeshRange, Object3D, Piece, RendererOptions, ThreeNamespace } from './types'\nimport { Visuals } from './visual'\n\nlet rendererCount = 0\nconst IMPACT_TAGS = ['metal', 'glass', 'wood'] as const\n\ntype Ev<T extends DestructionEvent['type']> = Extract<DestructionEvent, { type: T }>\n\nconst GLOW_FROM_C = 430\nconst QUEUE_CAP = 4000\n/** A part's fire lasts until the part stops burning (`stopFire`); this is only the ceiling the effects world needs. */\nconst FIRE_CEILING_SECONDS = 3600\n/** The `generic` explosion is the M67's (15 m injury radius): a blast with no effect id of its own scales with its radius. */\nconst GENERIC_REFERENCE_RADIUS = 15\n/** The small flame of a part that a bullet or fire primed: it burns until the part goes off. */\nconst PRIMED_FIRE_RADIUS = 0.2\n\ninterface FireEmitter {\n object: string\n part: string\n point: Vec3\n /** The burning part's position, moved every frame; the effects world's fire follows it. */\n follow: { x: number; y: number; z: number }\n /** Handle of the fire in the effects world, once the part's box is known. */\n handle?: number\n}\n\ninterface Leak {\n object: string\n part: string\n fluid: string\n point: Vec3\n direction: Vec3\n rate: number\n pressurized: boolean\n acc: number\n}\n\nconst FLUID_COLORS: Record<string, number> = {\n water: 0x8cc8ff,\n fuel: 0xd9a032,\n oil: 0x2a2018,\n gas: 0xf2f2f2,\n sap: 0xb89b3a,\n acid: 0x9bd23c,\n blood: 0x8a0a0a,\n}\nconst SMOKE_TONES: Record<string, number> = { white: 0xe6e6e6, grey: 0x8a8a8a, black: 0x2a2a2a }\n\nconst DEFAULT_SPEED: Record<string, number> = {\n sparks: 3,\n splinters: 2.5,\n dust: 1,\n shards: 2.5,\n fibers: 1.2,\n leaves: 1.2,\n drips: 0.5,\n ash: 0.8,\n chips: 2.5,\n smoke: 0.8,\n steam: 1,\n fire: 1.2,\n flash: 0,\n foam: 2.5,\n}\n\n/** Sound key for an impact on a material. */\nfunction impactKey(def: MaterialDef | undefined): string {\n if (!def) return 'impact_wood'\n const tags = def.tags ?? []\n if (tags.includes('metal')) return 'impact_metal'\n if (def.visual?.transparent || def.fracture === 'shatter') return 'impact_glass'\n if (def.fracture === 'crumble') return 'impact_masonry'\n if (def.fracture === 'tear') return 'impact_soft'\n return def.fracture === 'none' ? 'impact_metal' : 'impact_wood'\n}\n\nfunction fractureKey(def: MaterialDef | undefined, style: string): string {\n switch (style) {\n case 'shatter':\n return 'shatter_glass'\n case 'crumble':\n return 'crumble'\n case 'splinter':\n return 'splinter'\n default:\n return impactKey(def)\n }\n}\n\nfunction fallbackColor(id: string, def: MaterialDef | undefined): number {\n if (def?.tags?.includes('metal')) return 0x8a8f96\n if (def?.visual?.transparent) return 0x9fd0e0\n const h = hashString(id)\n return mixHex(0x6b5a48, 0x9a9488, (h % 100) / 100)\n}\n\nfunction materialsOf(m: THREE_NS.Material | THREE_NS.Material[]): THREE_NS.Material[] {\n return Array.isArray(m) ? m : [m]\n}\n\nfunction isMeshRange(v: Object3D | MeshRange): v is MeshRange {\n const r = v as Partial<MeshRange>\n return !!r.mesh && Array.isArray(r.range)\n}\n\n/** Create the renderer. See `DestructionRenderer`. */\nexport function createDestructionRenderer(THREE: ThreeNamespace, options: RendererOptions): DestructionRenderer {\n return new Renderer(THREE, options)\n}\n\nclass Renderer implements DestructionRenderer {\n private readonly world: RendererOptions['world']\n private readonly scene: THREE_NS.Scene\n private readonly quality: 'low' | 'medium' | 'high'\n private readonly low: boolean\n private readonly groundAt: (x: number, z: number) => number\n private readonly gravity: number\n private readonly autoSplit: boolean\n private readonly maxFragments: number\n private readonly bounds = new Map<string, Bound>()\n private readonly queue: DestructionEvent[] = []\n private readonly unsubscribe: () => void\n private readonly visuals: Visuals\n private readonly fragments: FragmentSystem\n private readonly ground: GroundSpots\n private readonly pieceSys: PieceSystem\n private readonly cages: RebarCages\n private readonly active = new Map<string, { object: string; part: string }>()\n private readonly fires = new Map<string, FireEmitter>()\n private readonly leaks: Leak[] = []\n private readonly homeTags: string[] = []\n private readonly throttles = new Map<string, number>()\n private readonly rng = createRng(0xc0ffee)\n private readonly vfx: VfxWorld\n /** Names this renderer to the world's drips, so a shared world tells each renderer where its own landed. */\n private readonly owner = `destruction-${++rendererCount}`\n private readonly stopLanding: () => void\n /** The effects renderer made here when the host gave no shared world; it is stepped in `update`. */\n private readonly ownVfx: VfxRenderer | undefined\n private time = 0\n private scanTimer = 0\n private disposed = false\n private readonly box: THREE_NS.Box3\n private readonly tmpQ: THREE_NS.Quaternion\n private readonly v1: THREE_NS.Vector3\n private readonly v2: THREE_NS.Vector3\n\n constructor(\n private readonly THREE: ThreeNamespace,\n private readonly o: RendererOptions,\n ) {\n this.world = o.world\n this.scene = o.scene\n this.quality = o.quality ?? 'medium'\n this.low = this.quality === 'low'\n this.groundAt = o.groundAt ?? (() => 0)\n this.gravity = o.gravity ?? 9.81\n this.autoSplit = o.autoSplit !== false\n this.maxFragments = o.maxFragments ?? 160\n this.box = new THREE.Box3()\n this.tmpQ = new THREE.Quaternion()\n this.v1 = new THREE.Vector3()\n this.v2 = new THREE.Vector3()\n const maxParticles = Math.max(8, Math.floor((o.maxParticles ?? 2500) * (this.low ? 0.5 : 1)))\n this.visuals = new Visuals(THREE, this.low, 600)\n this.ground = new GroundSpots(THREE, o.scene, this.groundAt, 48)\n this.vfx = o.vfx ?? createVfx({ profile: this.quality, gravity: this.gravity, groundAt: this.groundAt, sprites: { capacity: maxParticles, density: this.low ? 0.6 : 1 } })\n this.ownVfx = o.vfx ? undefined : createVfxRenderer(THREE, { scene: o.scene, camera: o.camera ?? new THREE.PerspectiveCamera(), vfx: this.vfx, pointScale: o.pointScale ?? 700 })\n this.stopLanding = this.vfx.onLand(this.owner, (x, _y, z, color, tag) => {\n const home = this.homeTags[tag - 1] ?? ''\n this.ground.add('puddle', x, z, 0.05 + this.rng() * 0.04, color, home)\n })\n this.fragments = new FragmentSystem(THREE, {\n scene: o.scene,\n groundAt: this.groundAt,\n gravity: this.gravity,\n max: this.maxFragments,\n life: o.fragmentLife ?? 12,\n })\n this.cages = new RebarCages(THREE, o.scene)\n this.pieceSys = new PieceSystem(THREE, {\n groundAt: this.groundAt,\n gravity: this.gravity,\n scene: o.scene,\n onHardLanding: (p, point, speed) => this.hardLanding(p, point, speed),\n })\n this.unsubscribe = this.world.on('*', (ev) => {\n if (this.queue.length < QUEUE_CAP) this.queue.push(ev)\n })\n }\n\n // ----------------------------------------------------------------------------------------------------------------------------------\n // Binding\n // ----------------------------------------------------------------------------------------------------------------------------------\n\n bind(objectId: string, root: Object3D, options: BindOptions = {}): void {\n if (this.bounds.has(objectId)) this.unbind(objectId)\n const state = this.world.state(objectId)\n const partIds = new Set((state?.parts ?? []).map((p) => p.id))\n const byPart = new Map<string, Array<{ mesh: Mesh; range?: [number, number] }>>()\n const taken = new Set<Mesh>()\n const put = (pid: string, e: { mesh: Mesh; range?: [number, number] }): void => {\n const l = byPart.get(pid)\n if (l) l.push(e)\n else byPart.set(pid, [e])\n }\n const add = (pid: string, m: Mesh): void => {\n if (taken.has(m)) return\n taken.add(m)\n put(pid, { mesh: m })\n }\n const addRange = (pid: string, r: MeshRange): void => {\n const n = (r.mesh.geometry.getAttribute('position')?.count ?? 0) as number\n const [v0, v1] = r.range\n if (!Number.isInteger(v0) || !Number.isInteger(v1) || v0 < 0 || v1 <= v0 || v1 > n) {\n throw new Error(`game-destruction-three: invalid vertex range [${v0}, ${v1}) for part \"${pid}\" (the mesh has ${n} vertices)`)\n }\n put(pid, { mesh: r.mesh, range: [v0, v1] })\n }\n const meshesIn = (o: Object3D): Mesh[] => {\n const out: Mesh[] = []\n o.traverse((c) => {\n if ((c as Mesh).isMesh) out.push(c as Mesh)\n })\n return out\n }\n if (options.parts) {\n for (const [pid, v] of Object.entries(options.parts)) {\n for (const o of Array.isArray(v) ? v : [v]) {\n if (isMeshRange(o)) addRange(pid, o)\n else for (const m of meshesIn(o)) add(pid, m)\n }\n }\n } else {\n const leftovers: Mesh[] = []\n for (const m of meshesIn(root)) {\n const ud = partHintOf(m.userData?.destructionPart)?.id\n if (ud !== undefined && partIds.has(ud)) add(ud, m)\n else if (partIds.has(m.name)) add(m.name, m)\n else leftovers.push(m)\n }\n if (leftovers.length) this.matchByBox(objectId, root, leftovers, add)\n }\n const bound: Bound = { id: objectId, root, sync: options.syncTransform !== false, parts: new Map(), lastPos: null, lastQuat: null, piece: null, isSplit: false }\n const hosts = new Map<Mesh, HostVis>()\n for (const pid of partIds) bound.parts.set(pid, this.makePartVis(objectId, pid, state?.parts.find((p) => p.id === pid)?.material ?? '', byPart.get(pid) ?? [], hosts))\n for (const [pid, meshes] of byPart) {\n if (!bound.parts.has(pid)) bound.parts.set(pid, this.makePartVis(objectId, pid, this.world.part(objectId, pid)?.material ?? '', meshes, hosts))\n }\n this.bounds.set(objectId, bound)\n }\n\n private makePartVis(home: string, id: string, material: string, meshes: Array<{ mesh: Mesh; range?: [number, number] }>, hosts: Map<Mesh, HostVis>): PartVis {\n return { id, home, material, meshes: meshes.map((m) => this.visuals.makeMeshVis(m.mesh, home, m.range, hosts)), charApplied: 0, glowApplied: 0, meltApplied: 0, gone: 'none' }\n }\n\n /** Match unlabelled meshes to the part whose shape contains the centre of the mesh's bounding box. */\n private matchByBox(objectId: string, root: Object3D, meshes: Mesh[], add: (pid: string, m: Mesh) => void): void {\n let specParts: PartSpec[] | undefined\n try {\n specParts = this.world.serialize?.().objects.find((o) => o.id === objectId)?.spec.parts\n } catch {\n specParts = undefined\n }\n if (!specParts) return\n root.updateMatrixWorld(true)\n const e = new this.THREE.Euler()\n const q = new this.THREE.Quaternion()\n for (const m of meshes) {\n this.box.setFromObject(m)\n if (this.box.isEmpty()) continue\n const c = root.worldToLocal(this.box.getCenter(this.v1))\n let best: PartSpec | null = null\n let bestVol = Infinity\n for (const p of specParts) {\n const l = this.v2.set(c.x - p.position[0], c.y - p.position[1], c.z - p.position[2])\n if (p.rotation) l.applyQuaternion(q.setFromEuler(e.set(p.rotation[0], p.rotation[1], p.rotation[2], 'XYZ')).invert())\n let inside = false\n let vol = 0\n const s = p.shape\n if (s.kind === 'box') {\n inside = Math.abs(l.x) <= s.size[0] / 2 && Math.abs(l.y) <= s.size[1] / 2 && Math.abs(l.z) <= s.size[2] / 2\n vol = s.size[0] * s.size[1] * s.size[2]\n } else if (s.kind === 'cylinder') {\n inside = Math.hypot(l.x, l.z) <= s.radius && Math.abs(l.y) <= s.height / 2\n vol = Math.PI * s.radius * s.radius * s.height\n } else {\n inside = l.length() <= s.radius\n vol = (4 / 3) * Math.PI * s.radius ** 3\n }\n if (inside && vol < bestVol) {\n best = p\n bestVol = vol\n }\n }\n if (best) add(best.id, m)\n }\n }\n\n unbind(objectId: string): void {\n const b = this.bounds.get(objectId)\n if (!b) return\n this.restore(objectId)\n this.pieceSys.remove(objectId)\n this.bounds.delete(objectId)\n }\n\n // ----------------------------------------------------------------------------------------------------------------------------------\n // Pieces\n // ----------------------------------------------------------------------------------------------------------------------------------\n\n /** Half extents and centre of the box around some meshes, in the local space of `root`. */\n private measure(root: Object3D, meshes: MeshVis[]): { half: Vec3; center: THREE_NS.Vector3 } {\n root.updateMatrixWorld(true)\n const inv = root.matrixWorld.clone().invert()\n const total = new this.THREE.Box3()\n const m4 = new this.THREE.Matrix4()\n const lb = new this.THREE.Box3()\n for (const mv of meshes) {\n const m = mv.mesh\n m.updateWorldMatrix(true, false)\n this.visuals.localBox(mv, lb)\n if (lb.isEmpty()) continue\n total.union(lb.clone().applyMatrix4(m4.multiplyMatrices(inv, m.matrixWorld)))\n }\n if (total.isEmpty()) return { half: [0.25, 0.25, 0.25], center: new this.THREE.Vector3() }\n const size = total.getSize(new this.THREE.Vector3())\n return { half: [Math.max(0.02, size.x / 2), Math.max(0.02, size.y / 2), Math.max(0.02, size.z / 2)], center: total.getCenter(new this.THREE.Vector3()) }\n }\n\n private meshesOf(b: Bound): MeshVis[] {\n const out: MeshVis[] = []\n for (const pv of b.parts.values()) if (pv.gone === 'none') for (const mv of pv.meshes) out.push(mv)\n return out\n }\n\n private massOf(objectId: string): number {\n const st = this.world.state(objectId)\n if (!st) return 5\n let m = 0\n for (const p of st.parts) if (p.status !== 'broken' && p.status !== 'melted' && p.status !== 'ashes') m += p.mass\n return m > 0 ? m : 5\n }\n\n makePiece(objectId: string, options: { mass?: number; velocity?: Vec3 } = {}): Piece {\n const b = this.bounds.get(objectId)\n if (!b) throw new Error(`game-destruction-three: object \"${objectId}\" is not bound`)\n if (b.root.parent && b.root.parent !== this.scene) this.scene.attach(b.root)\n const { half, center } = this.measure(b.root, this.meshesOf(b))\n const piece = this.pieceSys.create(objectId, b.root, half, center, options.mass ?? this.massOf(objectId), options.velocity)\n b.piece = piece\n return piece\n }\n\n pieces(): Piece[] {\n return [...this.pieceSys.list]\n }\n\n piece(objectId: string): Piece | null {\n return this.pieceSys.get(objectId)\n }\n\n impulse(objectId: string, impulse: Vec3, point?: Vec3): void {\n const p = this.pieceSys.get(objectId)\n if (p) this.pieceSys.impulse(p, impulse, point)\n }\n\n private hardLanding(p: PieceBody, point: Vec3, speed: number): void {\n const key = this.impactSoundOf(p.objectId)\n this.sound(key, point, clamp(speed / 10, 0.2, 1))\n // the piece just moved this frame: the world must know where it is before the hit is resolved against its parts\n this.syncBound(p.objectId)\n try {\n this.world.hit(p.objectId, { type: 'blunt', point, energy: 0.5 * p.mass * speed * speed, radius: 0.08, direction: [0, -1, 0], source: 'fall' })\n } catch {\n // the object may have been removed from the world\n }\n }\n\n private impactSoundOf(objectId: string): string {\n const st = this.world.state(objectId)\n let best: MaterialDef | undefined\n let mass = -1\n for (const p of st?.parts ?? []) {\n if (p.mass > mass) {\n mass = p.mass\n best = this.world.materials.get(p.material)\n }\n }\n return impactKey(best)\n }\n\n // ----------------------------------------------------------------------------------------------------------------------------------\n // Update\n // ----------------------------------------------------------------------------------------------------------------------------------\n\n update(dt: number): void {\n if (this.disposed) return\n dt = clamp(dt, 0, 0.1)\n this.time += dt\n this.drainQueue()\n this.updateActive(dt)\n this.updateLeaks(dt)\n this.pieceSys.update(dt)\n this.fragments.update(dt)\n this.ground.update(dt)\n this.ownVfx?.update(dt)\n this.syncTransforms()\n }\n\n private drainQueue(): void {\n // events produced while applying (a hard landing calls `hit`) wait for the next frame\n const batch = this.queue.splice(0, this.queue.length)\n for (const ev of batch) {\n try {\n this.apply(ev)\n } catch (err) {\n if (typeof console !== 'undefined') console.error('game-destruction-three: failed to apply', ev.type, err)\n }\n }\n }\n\n private syncTransforms(): void {\n for (const b of this.bounds.values()) this.syncOne(b)\n }\n\n /** Push the current transform of one bound object to the world right now (used before a landing hit). */\n private syncBound(objectId: string): void {\n const b = this.bounds.get(objectId)\n if (b) this.syncOne(b)\n }\n\n private syncOne(b: Bound): void {\n if (!b.sync) return\n const p = this.v1\n const q = this.tmpQ\n b.root.updateWorldMatrix(true, false)\n b.root.matrixWorld.decompose(p, q, this.v2)\n const last = b.lastPos\n const lq = b.lastQuat\n const moved = !last || !lq || Math.abs(p.x - last[0]) + Math.abs(p.y - last[1]) + Math.abs(p.z - last[2]) > 1e-5 || Math.abs(q.x - lq[0]) + Math.abs(q.y - lq[1]) + Math.abs(q.z - lq[2]) + Math.abs(q.w - lq[3]) > 1e-6\n if (!moved) return\n b.lastPos = [p.x, p.y, p.z]\n b.lastQuat = [q.x, q.y, q.z, q.w]\n if (this.world.has(b.id)) this.world.setTransform(b.id, [p.x, p.y, p.z], [q.x, q.y, q.z, q.w])\n }\n\n // ----------------------------------------------------------------------------------------------------------------------------------\n // Events\n // ----------------------------------------------------------------------------------------------------------------------------------\n\n private apply(ev: DestructionEvent): void {\n switch (ev.type) {\n case 'damage':\n if (ev.damageType === 'heat' || ev.damageType === 'fire' || ev.damageType === 'explosive') this.activate(ev.object, ev.part)\n break\n case 'dent':\n this.onDent(ev)\n break\n case 'hole':\n this.onHole(ev)\n break\n case 'crack':\n this.onCrack(ev)\n break\n case 'ricochet': {\n const d = ev.direction\n this.vfx.burst('sparks', ev.point, d, 4 + Math.min(10, ev.energy / 150), 4)\n this.sound('ricochet', ev.point, 0.7)\n break\n }\n case 'debris':\n this.vfx.burst(ev.kind, ev.point, ev.normal, ev.count, ev.speed > 0 ? ev.speed : DEFAULT_SPEED[ev.kind] ?? 1, this.debrisOptions(ev.kind, ev.object, ev.part))\n break\n case 'fracture':\n this.onFracture(ev)\n break\n case 'detach':\n this.onDetach(ev)\n break\n case 'collapse':\n this.splitLoose(ev.object)\n this.playEvent({ kind: 'collapse' }, this.rootPoint(ev.object))\n break\n case 'disassemble_progress': {\n const k = `unscrew:${ev.joint}`\n if (this.time - (this.throttles.get(k) ?? -1) > 0.25) {\n this.throttles.set(k, this.time)\n this.sound('unscrew', this.rootPoint(ev.object), clamp(0.3 + ev.progress * 0.5, 0, 1))\n }\n break\n }\n case 'ignite':\n this.startFire(ev)\n this.activate(ev.object, ev.part)\n this.sound('ignite', ev.point, 0.7)\n this.sound('burn_loop', ev.point, 0.5)\n break\n case 'primed':\n // a bullet or fire sets the part burning until its delay runs out; a blast nearby only waits for the explosion\n if (ev.trigger !== 'explosion') {\n this.vfx.fire({ point: ev.point, radius: PRIMED_FIRE_RADIUS, life: ev.delay })\n this.activate(ev.object, ev.part)\n this.sound('ignite', ev.point, 0.7)\n }\n break\n case 'spall':\n this.onSpall(ev)\n break\n case 'stage':\n this.onStage(ev)\n break\n case 'expose':\n this.cages.set(`${ev.object}|${ev.part}`, ev.bars)\n if (ev.bars.length > 0) {\n const [a, b] = ev.bars[0] as [Vec3, Vec3]\n this.playEvent({ kind: 'expose' }, [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2, (a[2] + b[2]) / 2])\n }\n break\n case 'extinguish': {\n const f = this.fires.get(`${ev.object}|${ev.part}`)\n if (f) {\n this.stopFire(`${ev.object}|${ev.part}`)\n this.vfx.burst('steam', f.point, [0, 1, 0], 14, 1.2)\n }\n this.activate(ev.object, ev.part)\n this.sound('extinguish', f?.point ?? this.rootPoint(ev.object), 0.6)\n break\n }\n case 'burnout': {\n this.stopFire(`${ev.object}|${ev.part}`)\n this.vfx.burst('ash', ev.point, [0, 1, 0], 16, 1)\n this.vfx.burst('smoke', ev.point, [0, 1, 0], 6, 0.8, { color: SMOKE_TONES.grey })\n const pv = this.partVis(ev.object, ev.part)\n if (pv) {\n const def = this.world.materials.get(pv.material)\n this.visuals.setChar(pv, 1, def?.visual?.charColor ?? 0x1a1410)\n }\n this.activate(ev.object, ev.part)\n break\n }\n case 'melt':\n this.activate(ev.object, ev.part)\n if (this.time - (this.throttles.get(`melt:${ev.object}|${ev.part}`) ?? -9) > 1.5) {\n this.throttles.set(`melt:${ev.object}|${ev.part}`, this.time)\n this.sound('melt_sizzle', this.rootPoint(ev.object), 0.4)\n }\n break\n case 'drip': {\n const b = this.bounds.get(ev.object)\n const pv = b?.parts.get(ev.part)\n const def = this.world.materials.get(ev.material)\n const color = (pv && this.partColor(pv, def)) ?? fallbackColor(ev.material, def)\n let tag = this.homeTags.indexOf(ev.object) + 1\n if (tag === 0) tag = this.homeTags.push(ev.object)\n this.vfx.spawn('drips', ev.point, [(this.rng() - 0.5) * 0.1, -0.1, (this.rng() - 0.5) * 0.1], { color, tag, owner: this.owner })\n break\n }\n case 'leak': {\n const key = `${ev.object}|${ev.part}`\n const same = this.leaks.find((l) => l.object === ev.object && l.part === ev.part && Math.hypot(l.point[0] - ev.point[0], l.point[1] - ev.point[1], l.point[2] - ev.point[2]) < 0.03)\n if (same) {\n same.rate = ev.rate\n same.direction = ev.direction\n } else {\n if (this.leaks.length >= 24) this.leaks.shift()\n this.leaks.push({ object: ev.object, part: ev.part, fluid: ev.fluid, point: ev.point, direction: ev.direction, rate: ev.rate, pressurized: ev.pressurized, acc: 0 })\n }\n void key\n break\n }\n case 'explode':\n this.onExplode(ev)\n break\n case 'destroyed':\n break\n }\n }\n\n private rootPoint(objectId: string): Vec3 {\n const b = this.bounds.get(objectId)\n return b ? [b.root.position.x, b.root.position.y, b.root.position.z] : [0, 0, 0]\n }\n\n private sound(key: string, point: Vec3, volume: number): void {\n this.o.onSound?.(key, point, volume)\n }\n\n private partVis(objectId: string, partId: string): PartVis | null {\n return this.bounds.get(objectId)?.parts.get(partId) ?? null\n }\n\n private activate(objectId: string, partId: string): void {\n this.active.set(`${objectId}|${partId}`, { object: objectId, part: partId })\n }\n\n /** Current colour of the part's first mesh (char included), or null. */\n private partColor(pv: PartVis, def: MaterialDef | undefined): number | null {\n const mv = pv.meshes[0]\n if (!mv) return null\n const mat = materialsOf(mv.mesh.material)[0] as THREE_NS.MeshStandardMaterial | undefined\n const c = mat?.color\n if (c && (c as THREE_NS.Color).isColor) {\n const tint = this.visuals.vertexTint(mv)\n return tint ? c.clone().multiply(tint).getHex() : c.getHex()\n }\n return def ? fallbackColor(def.id, def) : null\n }\n\n private debrisOptions(kind: DebrisKind, objectId: string, partId: string): { color?: number } | undefined {\n const pv = this.partVis(objectId, partId)\n const def = pv ? this.world.materials.get(pv.material) : undefined\n if (kind === 'smoke') return { color: SMOKE_TONES[def?.thermal.smoke ?? 'grey'] }\n if (kind === 'fibers' || kind === 'leaves') {\n const c = pv ? this.visuals.baseColorHex(pv.meshes[0] ?? ({} as never)) : -1\n return c >= 0 ? { color: c } : undefined\n }\n if (kind === 'dust' || kind === 'chips') {\n const c = pv && pv.meshes[0] ? this.visuals.baseColorHex(pv.meshes[0]) : -1\n return c >= 0 && kind === 'chips' ? { color: c } : undefined\n }\n return undefined\n }\n\n /** Mesh of the part closest to a world point. */\n private nearestMesh(pv: PartVis, p: THREE_NS.Vector3): PartVis['meshes'][number] | null {\n let best: PartVis['meshes'][number] | null = null\n let bd = Infinity\n for (const mv of pv.meshes) {\n if (!this.visuals.shown(mv)) continue\n const d = this.visuals.worldBox(mv, this.box).distanceToPoint(p)\n if (d < bd) {\n bd = d\n best = mv\n }\n }\n return best\n }\n\n private worldDir(b: Bound, v: Vec3): THREE_NS.Vector3 {\n b.root.updateWorldMatrix(true, false)\n return new this.THREE.Vector3(v[0], v[1], v[2]).applyQuaternion(b.root.getWorldQuaternion(this.tmpQ)).normalize()\n }\n\n private onDent(ev: Ev<'dent'>): void {\n const b = this.bounds.get(ev.object)\n const pv = b?.parts.get(ev.part)\n if (!b || !pv) return\n const def = this.world.materials.get(pv.material)\n const wp = new this.THREE.Vector3(ev.worldPoint[0], ev.worldPoint[1], ev.worldPoint[2])\n const wn = this.worldDir(b, ev.dent.normal)\n for (const mv of pv.meshes) {\n if (!this.visuals.shown(mv)) continue\n this.visuals.applyDent(mv, wp, wn, ev.dent)\n }\n if (def?.tags?.includes('metal')) this.vfx.burst('sparks', ev.worldPoint, [wn.x, wn.y, wn.z], 4 + Math.min(8, ev.dent.depth * 150), 3.2)\n this.sound(impactKey(def), ev.worldPoint, clamp(0.3 + ev.dent.depth * 12, 0.3, 1))\n }\n\n private onHole(ev: Ev<'hole'>): void {\n const b = this.bounds.get(ev.object)\n const pv = b?.parts.get(ev.part)\n if (!b || !pv) return\n const def = this.world.materials.get(pv.material)\n const n = this.worldDir(b, ev.hole.normal)\n const draw = (point: Vec3, normal: THREE_NS.Vector3): void => {\n const wp = new this.THREE.Vector3(point[0], point[1], point[2])\n const mv = this.nearestMesh(pv, wp)\n if (!mv) return\n if (def?.visual?.transparent) {\n const seed = hashNumbers(ev.time, point[0], point[1], point[2])\n this.visuals.addCrack(mv, wp, normal, 7, Math.max(0.05, ev.hole.diameter * 5), 0xe8f4ff, seed)\n } else {\n this.visuals.addHole(mv, wp, normal, ev.hole.diameter)\n }\n }\n draw(ev.worldPoint, n)\n if (ev.hole.through && ev.exitPoint) draw(ev.exitPoint, n.clone().negate())\n if (def?.tags?.includes('metal')) this.vfx.burst('sparks', ev.worldPoint, [n.x, n.y, n.z], 6, 3.5)\n this.sound(impactKey(def), ev.worldPoint, 0.6)\n }\n\n private onCrack(ev: Ev<'crack'>): void {\n const b = this.bounds.get(ev.object)\n const pv = b?.parts.get(ev.part)\n if (!b || !pv) return\n const def = this.world.materials.get(pv.material)\n const wp = new this.THREE.Vector3(ev.worldPoint[0], ev.worldPoint[1], ev.worldPoint[2])\n const mv = this.nearestMesh(pv, wp)\n if (mv) {\n const color = def?.visual?.transparent ? 0xe8f4ff : 0x2a2724\n this.visuals.addCrack(mv, wp, this.worldDir(b, ev.crack.normal), ev.crack.rays, ev.crack.length, color, hashNumbers(ev.time, ev.worldPoint[0], ev.worldPoint[1], ev.worldPoint[2]))\n }\n this.sound(impactKey(def), ev.worldPoint, 0.5)\n }\n\n /** Outward normal of the face of `mv` nearest to a world point: the thin axis of its world box (right for a wall; a sloped face is left to the vertex-range binding). */\n private faceNormal(mv: PartVis['meshes'][number], point: THREE_NS.Vector3): THREE_NS.Vector3 {\n const box = this.visuals.worldBox(mv, this.box)\n const size = box.getSize(this.v1)\n const axis = size.x <= size.y && size.x <= size.z ? 'x' : size.y <= size.z ? 'y' : 'z'\n const side = point[axis] >= (box.min[axis] + box.max[axis]) / 2 ? 1 : -1\n return new this.THREE.Vector3(axis === 'x' ? side : 0, axis === 'y' ? side : 0, axis === 'z' ? side : 0)\n }\n\n /** The material an impact effect is chosen by: the tags of the material (`metal`, `glass`, `wood`), else stone and the like are `concrete`. */\n private impactMaterial(def: MaterialDef | undefined): string {\n for (const tag of IMPACT_TAGS) if (def?.tags?.includes(tag)) return tag\n return 'concrete'\n }\n\n /**\n * Plays the effect the event map names for a destruction event (`data/event-effects.json` of `game-vfx`, or `RendererOptions.eventEffects`) at a point.\n * Answers whether one played, so the caller can leave out the burst it replaces.\n */\n private playEvent(event: GameEvent, point: Vec3): boolean {\n const id = effectForEvent(event, this.o.eventEffects ?? EVENT_EFFECTS_MAP)\n if (!id) return false\n return this.vfx.play(id, point, { seed: hashNumbers(point[0], point[1], point[2], this.time) }) !== null\n }\n\n /** A coat came off: a patch in the colour of the core under it, below the bullet holes and cracks. */\n private onSpall(ev: Ev<'spall'>): void {\n const pv = this.partVis(ev.object, ev.part)\n if (!pv) return\n const wp = new this.THREE.Vector3(ev.point[0], ev.point[1], ev.point[2])\n const mv = this.nearestMesh(pv, wp)\n if (!mv) return\n const def = this.world.materials.get(pv.material)\n this.playEvent({ kind: 'spall', material: this.impactMaterial(def) }, ev.point)\n this.visuals.addSpall(mv, wp, this.faceNormal(mv, wp), Math.sqrt(ev.area / Math.PI), fallbackColor(pv.material, def))\n }\n\n /**\n * Stage 2 of a staged part: the unit-sized fragments come out and the part stays drawn (hiding it is what `fracture` does). The opening itself\n * (cutting the wall's faces) is not drawn yet: it needs the per-vertex binding of the house renderer.\n */\n private onStage(ev: Ev<'stage'>): void {\n const played = this.playEvent({ kind: 'stage', stage: ev.stage }, ev.opening?.center ?? this.rootPoint(ev.object))\n if (!ev.fragments || !ev.opening) return\n const pv = this.partVis(ev.object, ev.part)\n const def = pv ? this.world.materials.get(pv.material) : undefined\n const color = (pv ? this.partColor(pv, def) : null) ?? fallbackColor(pv?.material ?? '', def)\n const maxAlive = this.low ? Math.floor(this.maxFragments / 2) : this.maxFragments\n const step = this.low ? 2 : 1\n for (let i = 0; i < ev.fragments.length; i += step) {\n const fr = ev.fragments[i] as Fragment\n const fdef = this.world.materials.get(fr.material)\n this.fragments.spawn(fr, this.fragments.materialFor(fr.material, fdef === def || !fdef ? color : fallbackColor(fr.material, fdef), false), ev.object, maxAlive)\n }\n if (!played) this.vfx.burst('dust', ev.opening.center, [0, 1, 0], 14, 1.2, { color })\n this.sound(impactKey(def), ev.opening.center, 0.8)\n }\n\n private onFracture(ev: Ev<'fracture'>): void {\n const b = this.bounds.get(ev.object)\n const pv = b?.parts.get(ev.part)\n const matId = pv?.material || (ev.fragments[0]?.material ?? '')\n const def = this.world.materials.get(matId)\n let color = fallbackColor(matId, def)\n let opacity = 0.5\n const transparent = def?.visual?.transparent === true\n if (pv) {\n const c = this.partColor(pv, def)\n if (c !== null) color = c\n const m = pv.meshes[0] ? (materialsOf(pv.meshes[0].origMaterial)[0] as THREE_NS.Material | undefined) : undefined\n if (m && m.transparent) opacity = clamp(m.opacity, 0.2, 0.8)\n this.visuals.hide(pv, 'broken')\n }\n const maxAlive = this.low ? Math.floor(this.maxFragments / 2) : this.maxFragments\n const step = this.low ? 2 : 1\n for (let i = 0; i < ev.fragments.length; i += step) {\n const fr = ev.fragments[i]\n const fdef = this.world.materials.get(fr.material)\n const mat = this.fragments.materialFor(fr.material, fdef === def || !fdef ? color : fallbackColor(fr.material, fdef), transparent || fdef?.visual?.transparent === true, opacity)\n this.fragments.spawn(fr, mat, ev.object, maxAlive)\n }\n const up: Vec3 = [0, 1, 0]\n const sizeK = clamp(ev.fragments.length / 10, 0.5, 2)\n switch (ev.style) {\n case 'shatter':\n this.vfx.burst('shards', ev.point, up, 18 * sizeK, 3)\n break\n case 'crumble':\n this.vfx.burst('dust', ev.point, up, 16 * sizeK, 1.2, { color: pv ? (this.partColor(pv, def) ?? undefined) : undefined })\n this.vfx.burst('chips', ev.point, up, 6, 3)\n break\n case 'splinter':\n this.vfx.burst('splinters', ev.point, up, 14 * sizeK, 3)\n this.vfx.burst('dust', ev.point, up, 5, 0.8, { color: 0xb59a74 })\n break\n case 'tear':\n this.vfx.burst('fibers', ev.point, up, 12 * sizeK, 1.6, { color: color })\n break\n case 'snap':\n this.vfx.burst('chips', ev.point, up, 8 * sizeK, 3)\n break\n default:\n this.vfx.burst('sparks', ev.point, up, 10, 4)\n }\n this.sound(fractureKey(def, ev.style), ev.point, clamp(0.4 + ev.fragments.length * 0.03, 0.4, 1))\n }\n\n private onDetach(ev: Ev<'detach'>): void {\n this.sound('detach', this.rootPoint(ev.object), 0.5)\n this.splitLoose(ev.object)\n }\n\n /** Turn every loose group of the object (a detach, a collapse) into a piece of its own. */\n private splitLoose(objectId: string): void {\n if (!this.autoSplit) return\n const b = this.bounds.get(objectId)\n if (!b) return\n const st = this.world.state(objectId)\n if (!st || st.groups.length < 2) return\n const loose = st.groups.length - 1\n for (let i = 0; i < loose; i++) {\n // the loose groups are always read from index 1: each split renumbers the rest\n let newId: string\n try {\n newId = this.world.splitGroup(objectId, 1)\n } catch {\n break\n }\n this.adoptSplit(b, newId)\n }\n const old = b.piece\n if (old) {\n const { half, center } = this.measure(b.root, this.meshesOf(b))\n old.halfExtents = half\n this.pieceSys.setCenter(old, center)\n old.mass = Math.max(0.05, this.massOf(objectId))\n old.sleeping = false\n }\n }\n\n /** The renderer side of `splitGroup`: a new group holding the meshes of the new object, made a piece. */\n private adoptSplit(from: Bound, newId: string): void {\n const state = this.world.state(newId)\n const root = from.root\n root.updateWorldMatrix(true, false)\n const group = new this.THREE.Group()\n group.name = `destruction-piece-${newId}`\n root.getWorldPosition(group.position)\n root.getWorldQuaternion(group.quaternion)\n this.scene.add(group)\n group.updateMatrixWorld(true)\n const nb: Bound = { id: newId, root: group, sync: true, parts: new Map(), lastPos: null, lastQuat: null, piece: null, isSplit: true }\n for (const p of state?.parts ?? []) {\n const pv = from.parts.get(p.id)\n if (!pv) continue\n from.parts.delete(p.id)\n nb.parts.set(p.id, pv)\n for (const mv of pv.meshes) {\n // a part drawn by a range of a merged mesh leaves its faces behind: it gets a mesh of its own\n if (mv.host && !mv.loose) this.visuals.extract(mv, group)\n else {\n group.attach(mv.mesh)\n mv.moved = true\n }\n }\n }\n this.bounds.set(newId, nb)\n const meshes = this.meshesOf(nb)\n if (meshes.length === 0) return\n const { half, center } = this.measure(group, meshes)\n let vel: Vec3 = [0, 0, 0]\n let ang: Vec3 = [0, 0, 0]\n const op = from.piece\n if (op) {\n vel = [...op.velocity]\n ang = [...op.angularVelocity]\n // linear velocity at the loose piece's centre\n const c = this.pieceSys.centerOf(op, new this.THREE.Vector3())\n const nc = center.clone().applyQuaternion(group.quaternion).add(group.position)\n vel[0] += ang[1] * (nc.z - c.z) - ang[2] * (nc.y - c.y)\n vel[1] += ang[2] * (nc.x - c.x) - ang[0] * (nc.z - c.z)\n vel[2] += ang[0] * (nc.y - c.y) - ang[1] * (nc.x - c.x)\n }\n const piece = this.pieceSys.create(newId, group, half, center, this.massOf(newId), vel, ang)\n nb.piece = piece\n this.o.onPiece?.(piece)\n }\n\n private onExplode(ev: Ev<'explode'>, options: ExplodeOptions = {}): void {\n const p = ev.point\n if (options.visual !== false) this.playExplosion(ev, options)\n this.vfx.push(p, ev.radius, 4)\n // shove pieces and fragments\n const c = this.v1\n for (const pc of this.pieceSys.list) {\n this.pieceSys.centerOf(pc, c)\n const dx = c.x - p[0]\n const dy = c.y - p[1]\n const dz = c.z - p[2]\n const d = Math.hypot(dx, dy, dz)\n if (d > ev.radius) continue\n const fall = (1 - d / ev.radius) ** 2\n const speed = Math.min(30, Math.sqrt((2 * ev.energy * 0.02) / pc.mass)) * fall\n const inv = d > 1e-4 ? 1 / d : 0\n const dir: Vec3 = d > 1e-4 ? [dx * inv, dy * inv + 0.3, dz * inv] : [0, 1, 0]\n const spread = pc.halfExtents[0]\n this.pieceSys.impulse(pc, [dir[0] * speed * pc.mass, dir[1] * speed * pc.mass, dir[2] * speed * pc.mass], [c.x + (this.rng() - 0.5) * spread, c.y + spread * 0.5, c.z + (this.rng() - 0.5) * spread])\n }\n this.fragments.blast(p, ev.radius, Math.min(20, Math.sqrt(ev.energy) * 0.08))\n this.sound('explode', p, clamp(0.5 + ev.radius / 10, 0.5, 1))\n }\n\n private playExplosion(ev: Ev<'explode'>, options: ExplodeOptions): void {\n const named = options.effect ?? ev.effect\n const direction = options.direction\n const scale = clamp(ev.radius / GENERIC_REFERENCE_RADIUS, 0.2, 3)\n if (named !== undefined && this.vfx.explosion(named, ev.point, direction ? { direction } : {}) !== null) return\n this.vfx.explosion('generic', ev.point, { scale, ...(direction ? { direction } : {}) })\n }\n\n private startFire(ev: { object: string; part: string; point: Vec3 }): void {\n const key = `${ev.object}|${ev.part}`\n this.stopFire(key)\n this.fires.set(key, { object: ev.object, part: ev.part, point: ev.point, follow: { x: ev.point[0], y: ev.point[1], z: ev.point[2] } })\n }\n\n private stopFire(key: string): void {\n const fire = this.fires.get(key)\n if (!fire) return\n if (fire.handle !== undefined) this.vfx.stop(fire.handle)\n this.fires.delete(key)\n }\n\n // ----------------------------------------------------------------------------------------------------------------------------------\n // Fire, heat, char, melt, leaks\n // ----------------------------------------------------------------------------------------------------------------------------------\n\n private updateActive(dt: number): void {\n this.scanTimer += dt\n if (this.scanTimer >= 0.4) {\n this.scanTimer = 0\n for (const b of this.bounds.values()) {\n for (const pv of b.parts.values()) {\n if (pv.gone !== 'none' && pv.gone !== 'broken') continue\n const ps = this.world.part(b.id, pv.id)\n if (!ps) continue\n const def = this.world.materials.get(pv.material)\n if (\n ps.burning ||\n (def?.visual?.glows && ps.temperature > GLOW_FROM_C) ||\n Math.abs(ps.char - pv.charApplied) > 0.01 ||\n Math.abs(ps.melt - pv.meltApplied) > 0.01 ||\n (pv.gone === 'none' && (ps.status === 'melted' || ps.status === 'ashes'))\n ) {\n this.activate(b.id, pv.id)\n }\n }\n }\n }\n for (const [key, a] of this.active) {\n const pv = this.partVis(a.object, a.part)\n const ps = pv ? this.world.part(a.object, a.part) : null\n if (!pv || !ps) {\n this.active.delete(key)\n continue\n }\n const def = this.world.materials.get(pv.material)\n if (pv.gone === 'none') {\n this.visuals.setChar(pv, ps.char, def?.visual?.charColor ?? 0x1a1410)\n if (def?.visual?.glows) this.visuals.setGlow(pv, ps.temperature)\n this.visuals.setMelt(pv, ps.melt)\n if (ps.status === 'melted') this.onGone(a.object, pv, 'melted', def)\n else if (ps.status === 'ashes') this.onGone(a.object, pv, 'ashes', def)\n }\n const fire = this.fires.get(key)\n if (fire) {\n if (ps.burning && ps.status !== 'broken' && ps.status !== 'ashes' && ps.status !== 'melted') this.emitFire(fire, pv, def)\n else this.stopFire(key)\n }\n const hot = def?.visual?.glows === true && ps.temperature > GLOW_FROM_C\n if (!ps.burning && !hot && Math.abs(ps.char - pv.charApplied) < 0.01 && Math.abs(ps.melt - pv.meltApplied) < 0.01) {\n if (def?.visual?.glows && pv.gone === 'none') this.visuals.setGlow(pv, 0)\n this.active.delete(key)\n }\n }\n }\n\n private onGone(objectId: string, pv: PartVis, reason: 'melted' | 'ashes', def: MaterialDef | undefined): void {\n const bb = new this.THREE.Box3()\n for (const mv of pv.meshes) bb.union(this.visuals.worldBox(mv, this.box))\n this.visuals.hide(pv, reason)\n if (bb.isEmpty()) return\n const c = bb.getCenter(new this.THREE.Vector3())\n const s = bb.getSize(new this.THREE.Vector3())\n const footprint = Math.sqrt(Math.max(s.x * s.z, 1e-4))\n if (reason === 'melted') {\n this.ground.add('puddle', c.x, c.z, clamp(footprint * 0.55, 0.05, 1), this.partColor(pv, def) ?? fallbackColor(pv.material, def), objectId)\n } else {\n this.ground.add('ash', c.x, c.z, clamp(footprint * 0.35, 0.04, 0.5), 0x1d1a18, objectId)\n this.vfx.burst('ash', [c.x, bb.max.y, c.z], [0, 1, 0], 10, 0.8)\n }\n }\n\n private emitFire(f: FireEmitter, pv: PartVis, def: MaterialDef | undefined): void {\n const bb = this.box\n bb.makeEmpty()\n const tmp = new this.THREE.Box3()\n for (const mv of pv.meshes) if (this.visuals.shown(mv)) bb.union(this.visuals.worldBox(mv, tmp))\n if (!bb.isEmpty()) {\n const c = bb.getCenter(this.v1)\n f.follow.x = c.x\n f.follow.y = bb.min.y\n f.follow.z = c.z\n }\n if (f.handle !== undefined) return\n const size = bb.isEmpty() ? this.v2.set(0.2, 0.2, 0.2) : bb.getSize(this.v2)\n const radius = clamp(Math.max(size.x, size.z) * 0.5, 0.12, 1.2)\n const smokeColor = SMOKE_TONES[def?.thermal.smoke ?? 'grey']\n f.handle = this.vfx.fire({ point: [f.follow.x, f.follow.y, f.follow.z], radius, life: FIRE_CEILING_SECONDS, follow: f.follow, ...(smokeColor !== undefined ? { smokeColor } : {}) })\n }\n\n private updateLeaks(dt: number): void {\n for (let i = this.leaks.length - 1; i >= 0; i--) {\n const l = this.leaks[i]\n const ps = this.world.part(l.object, l.part)\n if (!ps || ps.liters === undefined || ps.liters <= 0) {\n this.leaks.splice(i, 1)\n continue\n }\n l.acc += clamp(20 + l.rate * 400, 15, 120) * dt\n const color = FLUID_COLORS[l.fluid] ?? 0xaaaaaa\n const d = l.direction\n const dl = Math.hypot(d[0], d[1], d[2]) || 1\n const sp = l.pressurized ? 2.5 + this.rng() * 1.5 : 0.3 + this.rng() * 0.3\n while (l.acc >= 1) {\n l.acc -= 1\n const vel: Vec3 = [(d[0] / dl) * sp + (this.rng() - 0.5) * 0.25, (d[1] / dl) * sp + (this.rng() - 0.5) * 0.25, (d[2] / dl) * sp + (this.rng() - 0.5) * 0.25]\n if (l.fluid === 'gas') this.vfx.spawn('steam', l.point, vel, { color, size: 0.5, alpha: 0.5 })\n else this.vfx.spawn('drips', l.point, vel, { color, tag: this.rng() < 0.03 ? this.tagOf(l.object) : 0, owner: this.owner })\n }\n }\n }\n\n private tagOf(object: string): number {\n let tag = this.homeTags.indexOf(object) + 1\n if (tag === 0) tag = this.homeTags.push(object)\n return tag\n }\n\n // ----------------------------------------------------------------------------------------------------------------------------------\n // Emit, clear, restore, stats, dispose\n // ----------------------------------------------------------------------------------------------------------------------------------\n\n emit(kind: DebrisKind | 'fire' | 'flash' | 'foam', point: Vec3, normal: Vec3, count: number, speed?: number): void {\n this.vfx.burst(kind, point, normal, count, speed ?? DEFAULT_SPEED[kind] ?? 1, kind === 'smoke' ? { color: SMOKE_TONES.grey } : undefined)\n }\n\n explode(point: Vec3, energy: number, radius: number, options?: ExplodeOptions): void {\n this.onExplode({ type: 'explode', object: '', part: '', time: this.time, point, energy, radius }, options)\n }\n\n restore(objectId: string): void {\n const homes = new Set<string>([objectId])\n const self = this.bounds.get(objectId)\n if (self?.isSplit) for (const pv of self.parts.values()) homes.add(pv.home)\n for (const home of homes) this.restoreHome(home)\n for (const [key, a] of [...this.active]) if (homes.has(a.object)) this.active.delete(key)\n for (const [key, f] of [...this.fires]) if (homes.has(f.object)) this.stopFire(key)\n for (let i = this.leaks.length - 1; i >= 0; i--) if (homes.has(this.leaks[i].object)) this.leaks.splice(i, 1)\n for (const home of homes) {\n this.fragments.removeHome(home)\n this.ground.removeHome(home)\n this.cages.removeObject(home)\n }\n }\n\n private restoreHome(home: string): void {\n const homeBound = this.bounds.get(home)\n for (const b of [...this.bounds.values()]) {\n for (const [pid, pv] of [...b.parts]) {\n if (pv.home !== home) continue\n this.visuals.restorePart(pv)\n if (b !== homeBound && homeBound) {\n b.parts.delete(pid)\n homeBound.parts.set(pid, pv)\n }\n }\n if (b.isSplit && b.parts.size === 0) {\n this.scene.remove(b.root)\n this.pieceSys.remove(b.id)\n this.bounds.delete(b.id)\n }\n }\n }\n\n clear(): void {\n this.queue.length = 0\n this.active.clear()\n for (const key of [...this.fires.keys()]) this.stopFire(key)\n this.leaks.length = 0\n for (const id of [...this.bounds.keys()]) if (this.bounds.has(id)) this.restore(id)\n // anything left of a split object whose home is gone\n for (const b of [...this.bounds.values()]) {\n if (b.isSplit) {\n for (const pv of b.parts.values()) this.visuals.restorePart(pv)\n this.scene.remove(b.root)\n this.pieceSys.remove(b.id)\n this.bounds.delete(b.id)\n }\n }\n this.fragments.clear()\n this.ground.clear()\n this.cages.clear()\n this.vfx.sprites().clear()\n this.ownVfx?.world.clear()\n }\n\n stats(): { fragments: number; particles: number; pieces: number; decals: number } {\n return { fragments: this.fragments.count, particles: this.vfx.sprites().live + this.ownedVfxParticles(), pieces: this.pieceSys.list.length, decals: this.visuals.decalCount + this.ground.count }\n }\n\n private ownedVfxParticles(): number {\n if (!this.ownVfx) return 0\n const { puffs, sparks, glows, chunks } = this.vfx.stats()\n return puffs + sparks + glows + chunks\n }\n\n rebarCount(): number {\n return this.cages.count\n }\n\n dispose(): void {\n if (this.disposed) return\n this.unsubscribe()\n this.clear()\n for (const id of [...this.bounds.keys()]) this.unbind(id)\n this.fragments.dispose()\n this.ground.dispose()\n this.cages.dispose()\n this.stopLanding()\n this.visuals.dispose()\n this.ownVfx?.dispose()\n this.disposed = true\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;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,WAAa;MACf;MACA,OAAS;QACP,UAAY;MACd;MACA,OAAS;MACT,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Cb,UAAY;MACZ,aAAe;QACd,OAAS;MACV;MACA,WAAa;MACb,aAAe;MACf,aAAe;MACf,SAAW;QACV,OAAS;QACT,UAAY;QACZ,cAAgB;QAChB,cAAgB;MACjB;MACA,QAAU;QACT,OAAS;MACV;MACA,MAAQ;QACP;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;QACT,OAAS;MACV;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;MACV;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;MACV;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;MACV;MACA,WAAa;MACb,aAAe;MACf,aAAe;MACf,SAAW;QACV,OAAS;QACT,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,eAAiB;MAClB;MACA,QAAU;QACT,OAAS;QACT,aAAe;MAChB;MACA,MAAQ;QACP;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,WAAa;MACb,aAAe;MACf,aAAe;MACf,SAAW;QACV,OAAS;QACT,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,eAAiB;MAClB;MACA,QAAU;QACT,OAAS;QACT,aAAe;MAChB;MACA,MAAQ;QACP;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;MACV;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,cAAgB;QAChB,cAAgB;QAChB,eAAiB;MAClB;MACA,MAAQ;QACP;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,WAAa;MACd;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,cAAgB;QAChB,cAAgB;MACjB;MACA,MAAQ;QACP;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,cAAgB;QAChB,cAAgB;MACjB;MACA,MAAQ;QACP;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,cAAgB;QAChB,cAAgB;MACjB;MACA,MAAQ;QACP;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,cAAgB;QAChB,cAAgB;MACjB;MACA,MAAQ;QACP;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,OAAS;QACT,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;QACT,WAAa;MACd;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;QACT,WAAa;MACd;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;MACV;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;MACV;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;MACV;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;QACT,WAAa;MACd;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;QACA;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;MACV;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ,cAAgB;QAChB,cAAgB;QAChB,OAAS;MACV;MACA,QAAU;QACT,WAAa;MACd;MACA,MAAQ;QACP;QACA;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;MACV;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,cAAgB;QAChB,cAAgB;MACjB;MACA,MAAQ;QACP;QACA;MACD;MACA,aAAe;IAChB;IACA;MACC,IAAM;MACN,SAAW;MACX,SAAW;MACX,aAAe;MACf,WAAa;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,WAAa;MACb,aAAe;MACf,SAAW;QACV,WAAa;QACb,QAAU;QACV,cAAgB;QAChB,UAAY;QACZ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","names":["entry","__publicField","DEFAULT_COLOR","min","puff","ageOrKeep","ring","floor","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","ORIGIN","OBJECT_ID_DRAWS","FLOOR","clamp","x","y","random","SPOT","QUARTER_TURN","lerp","vx","vy","vz","ease","axis","UP","FULL_TURN_DRAWN","__publicField","behavior","local","param","screen","ORIGIN","positionOf","ageOrKeep","ring","puff","pick","flash","screen","vy","vx","vz","call","QUARTER_TURN","floor","clamp","lerp","__publicField","__publicField","at","rnd","UP","ring","GRAVITY","QUARTER_TURN","DEFAULT_COLOR","FULL_TURN_DRAWN","CAPACITY","DEFAULT_LIFE","decal","BYTE_MAX","FLOATS_PER_VECTOR","cos","sin","RING_CAPACITY","camera","layer","call","local","puff","colorOf","cos","sin","ease","clamp","min","max","flash","ring","RING_CAPACITY","FADE_SECONDS","decal","layer","GROUND_COLOR","GROUND_COLOR","camera","layer","clamp","lerp","axis","clamp","lerp","clamp","impact","vx","vy","vz","at","axis","MATERIALS","MATERIALS","min","at","geo","quat","decal","rnd","clamp","lerp","materialsOf","add","clamp","axis"]}
|