@adatechnology/game-destruction-three 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +135 -2
- package/dist/index.d.ts +152 -0
- package/dist/index.global.js +3622 -0
- package/dist/index.global.js.map +1 -0
- package/dist/index.js +2551 -0
- package/dist/index.js.map +1 -0
- package/package.json +58 -3
|
@@ -0,0 +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/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/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 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 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.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.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 const c0 = this.colorHex[i] >= 0 ? this.colorHex[i] : def.c0\n const c1 = this.colorHex[i] >= 0 ? this.colorHex[i] : def.c1\n const a = hexToRgb(c0)\n const b = hexToRgb(c1)\n this.col[i3] = lerp(a[0], b[0], t)\n this.col[i3 + 1] = lerp(a[1], b[1], t)\n this.col[i3 + 2] = lerp(a[2], b[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 { 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 { 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\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 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}\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)\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 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 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 let guard = 0\n while (blasts.length && guard++ < 64) {\n const b = blasts.shift()!\n blastWorld(b.point, b.energy, b.radius, b.source)\n }\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,\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),\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 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) o.collapsed.add(p.spec.id)\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 /** 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 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 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 const scale = mat.damageScale?.ballistic ?? 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, 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 && 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) {\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, (stress - 1) * p.st.maxHp * 0.5 * 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 /** 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 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 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', 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', 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', 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', 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, 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 /** 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 }\n\n makeMeshVis(mesh: Mesh, home: string): MeshVis {\n return {\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 }\n }\n\n vertexCount(mv: MeshVis): number {\n return mv.mesh.geometry.getAttribute(POS_ATTR)?.count ?? 0\n }\n\n private readonly tints = new WeakMap<THREE_NS.Mesh, 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.mesh)) return this.tints.get(mv.mesh) ?? 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 let r = 0, g = 0, b = 0\n for (let i = 0; i < attr.count; i++) {\n r += attr.getX(i)\n g += attr.getY(i)\n b += attr.getZ(i)\n }\n tint = new this.THREE.Color(r / attr.count, g / attr.count, b / attr.count)\n }\n this.tints.set(mv.mesh, 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.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 // ------------------------------------------------------------------------------------------------------------------------------------\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 if (!geo.boundingBox) geo.computeBoundingBox()\n if (geo.boundingBox && geo.boundingBox.distanceToPoint(lp) > r) return false\n if (!this.ownGeometry(mv)) return false\n const 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 for (let i = 0; i < work.length; 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 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 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 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 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.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) mv.mesh.visible = false\n }\n\n /** Bring one mesh back to how it was when it was bound. */\n restoreMesh(mv: MeshVis): void {\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, 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, 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\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, Mesh[]>()\n const taken = new Set<Mesh>()\n const add = (pid: string, m: Mesh): void => {\n if (taken.has(m)) return\n taken.add(m)\n const l = byPart.get(pid)\n if (l) l.push(m)\n else byPart.set(pid, [m])\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]) for (const m of meshesIn(o)) add(pid, m)\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 for (const pid of partIds) bound.parts.set(pid, this.makePartVis(objectId, pid, state?.parts.find((p) => p.id === pid)?.material ?? '', byPart.get(pid) ?? []))\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))\n }\n this.bounds.set(objectId, bound)\n }\n\n private makePartVis(home: string, id: string, material: string, meshes: Mesh[]): PartVis {\n return { id, home, material, meshes: meshes.map((m) => this.visuals.makeMeshVis(m, home)), 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: Mesh[]): { 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 for (const m of meshes) {\n const g = m.geometry\n if (!g.boundingBox) g.computeBoundingBox()\n if (!g.boundingBox || g.boundingBox.isEmpty()) continue\n total.union(g.boundingBox.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): Mesh[] {\n const out: Mesh[] = []\n for (const pv of b.parts.values()) if (pv.gone === 'none') for (const mv of pv.meshes) out.push(mv.mesh)\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 '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 '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 (!mv.mesh.visible) continue\n const d = this.box.setFromObject(mv.mesh).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 (!mv.mesh.visible) 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 if (!this.autoSplit) return\n const b = this.bounds.get(ev.object)\n if (!b) return\n const st = this.world.state(ev.object)\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(ev.object, 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(ev.object))\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 group.attach(mv.mesh)\n mv.moved = true\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.box.setFromObject(mv.mesh))\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 (mv.mesh.visible) bb.union(tmp.setFromObject(mv.mesh))\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 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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AAY;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;MACb,UAAY;MACZ,cAAgB;QACf;QACA;MACD;MACA,aAAe;QACd,OAAS;MACV;MACA,WAAa;MACb,aAAe;MACf,S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","names":["geo","quat","materialsOf"]}
|